Last active
May 16, 2023 19:20
-
-
Save varenc/1fe600021346edcd562e7e715317ac19 to your computer and use it in GitHub Desktop.
Force energy saver mode or tab memory discarding on Google Chrome on macOS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#### Force energy saver mode or tab memory discarding on Google Chrome on macOS | |
# Works even when you're plugged into AC power and not on battery or when your battery or memory is not low. | |
# The script below works by just automating clicks on the chrome://discards page which allows you to force these behaviors | |
# | |
# Setup instructions: | |
# - Just add the functions below to your .zshrc, .bashrc, or any other shell config file | |
# - Or save them to a file and add `source <path-to-file>` to a shell rc | |
# - You must also have Chrome "Allow JavaScript from Apple Events" enabled. This is the Chrome menu: View -> Developer -> Allow JavaScript from Apple Events | |
# - You must also grant macOS accessibility permissions but you should be prompted for this on first run. | |
# | |
# After installed: | |
# - `chromeDiscardMemory` proctively discards ALL chrome tab memory | |
# - `chromeToggleEnergySaver` toggles Chrome energy saver mode | |
# - You can also setup automators like Alfred or Apple Shortcuts to run these functions | |
function chromeDiscardMemory() { | |
# click all of the enabled "Proactive Discard" buttons in Chrome to free up memory | |
__chromeDiscardHelper "deepQuerySelectorAll(document,'div[is=\"action-link\"]:not([disabled])').forEach((el,n) => { if (el.innerText.indexOf(\"Proactive Discard\") > -1) { el.click() } })" "All Chrome tabs have been discarded"; | |
} | |
function chromeToggleEnergySaver() { | |
# click the toggle battery saver mode button in Chrome | |
__chromeDiscardHelper "deepQuerySelectorAll(document,'div[is=\"action-link\"]').find(element => /toggle.*battery\\\s+saver/i.test(element.textContent)).click()" "Chrome energy saver mode toggled"; | |
# the selector 'deepQuerySelectorAll(document,"#discards > div:nth-child(2)")[0].click()' is cleaner and also works but it seems more brittle | |
} | |
# This is the shared helper function that does the actual work of navigating to the chrome://discards page, | |
# defining the deepQuerySelectorAll function, and running the passed in JS code which just clicks the appropriate buttons | |
function __chromeDiscardHelper() { | |
[ $#@ -ne 2 ] && echo "Usage: $0 <JS CODE> <success-message>" >&2 && return 1; | |
/usr/bin/osascript -l JavaScript <<EndOfScript | |
ObjC.import("stdlib"); | |
const chrome = Application("Google Chrome"); | |
const activeTab = chrome.windows[0].activeTab; | |
let navigatedToDiscards = false; | |
if (activeTab.url() !== "chrome://discards/") { | |
activeTab.url = "chrome://discards/"; | |
navigatedToDiscards = true; | |
delay(0.5); | |
} | |
// The below is a bit confusing due to multiple layers of embedding/escaping. | |
activeTab.execute({ javascript: \` | |
// deepQuerySelectorAll is just like querySelectorAll but it also searches shadow roots recursively | |
// This is necessary for the chrome://discards page which makes extensive use of shadow roots | |
// confession: GPT-4 wrote deepQuerySelectorAll for me... | |
function deepQuerySelectorAll(root = document, selector) { | |
const results = Array.from(root.querySelectorAll(selector)); | |
root.querySelectorAll("*").forEach(el => { | |
if (el.shadowRoot) { | |
results.push(...deepQuerySelectorAll(el.shadowRoot, selector)); | |
} | |
}); | |
return results; | |
} | |
$1 ; // this first argument, the JS code executed in Chrome | |
alert("${2}"); | |
\`}); | |
// return to the previous page iff we weren't already on the chrome://discards page | |
if (navigatedToDiscards) { | |
console.log("Pausing and then telling Chrome to go back to previous page"); | |
delay(1); | |
activeTab.goBack(); | |
} | |
\$.exit(0); | |
EndOfScript | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment