Skip to content

Instantly share code, notes, and snippets.

@phillipskevin
Last active July 7, 2022 18:02
Show Gist options
  • Save phillipskevin/89e59ad47a41f79f03b66354612cc25b to your computer and use it in GitHub Desktop.
Save phillipskevin/89e59ad47a41f79f03b66354612cc25b to your computer and use it in GitHub Desktop.
chrome-extension-accessing-dom-and-window

Description

This gist shows two techniques for (simultaneously) accessing the DOM and the window from a Chrome extension

solution 1: eval aka the easy way

You can run arbitrary code in the page using chrome.devtools.inspectedWindow.eval( ... )

This solution is shown in the files below prefixed with 1-.

This will work at any time - even when you're stopped at a breakpoint in the debugger.

The only limitations are

  • it has to be synchronous
  • it is bound to cross-origin policies. For example, you can't read something like return window.frames[0].my_global if frames[0] is cross-origin.
  • return value is serialized

solution 2: message passing

The more complicated option is to inject a script into the pages/frames you want to read data from and use message passing to pass that data back to your extension.

This is shown in the files below prefixed with 2-.

Limitations of this solution

  • it's complicated
  • the content-script and injected-script cannot run while at a breakpoint
<!doctype html>
<script src="index.js"></script>
chrome.devtools.inspectedWindow.eval(
// you can do more complicated things by stringifying IIFEs like "(function(){ /*do whatever you want here*/ }())"
"document.location.href",
function(result, isException) {
console.log("result", result);
}
);
{
"manifest_version": 2,
"name": "Simple Example Extension",
"version": "0.0.0",
"devtools_page": "index.html"
}
/*
* executed by: manifest - once
* has access to DOM:
* has access to `window`:
* has access to `$0`:
* an run while debugger is paused:
* communication:
* -
* -
*/
chrome.runtime.onConnect.addListener(function(port) {
port.onMessage.addListener(function(msg, port) {
chrome.runtime.sendMessage(msg);
});
});
/*
* executed by: manifest - once per frame
* has access to DOM:
* has access to `window`:
* has access to `$0`:
* an run while debugger is paused:
* communication:
* -
* -
*/
var port = chrome.runtime.connect();
document.addEventListener("__msg-from-injected-script__", function(response) {
port.postMessage(response.detail);
});
var s = document.createElement("script");
s.src = chrome.extension.getURL("injected-script.js");
s.onload = function() {
this.remove();
};
(document.head || document.documentElement).appendChild(s);
<!doctype html>
<script src="index.js"></script>
/*
* executed by:
* has access to DOM:
* has access to `window`:
* has access to `$0`:
* an run while debugger is paused:
* communication:
* -
* -
*/
chrome.runtime.onMessage.addListener(function(result, sender) {
console.log("result", result);
});
/*
* executed by:
* has access to DOM:
* has access to `window`:
* has access to `$0`:
* communication:
* -
* -
*/
var customEvent = new CustomEvent("__msg-from-injected-script__", {
detail: document.location.href
});
document.dispatchEvent(customEvent);
{
"manifest_version": 2,
"name": "Complex Example Extension",
"version": "0.0.0",
"devtools_page": "index.html",
"background": {
"scripts": [ "background.js" ]
},
"content_scripts": [ {
"all_frames": true,
"js": [ "content-script.js" ],
"matches": [ "*://*/*" ]
} ],
"web_accessible_resources": [
"injected-script.js"
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment