16

I'm trying to develop extension that works only on specified pages - If page owner adds global variable into their code (for eg. ACCEPT_STATS = true;) I want to execute specified code.

I've already bind my function to the onload event, i've also found solution how to do that in Firefox:

var win = window.top.getBrowser().selectedBrowser.contentWindow;
if (typeof win.wrappedJSObject.ACCEPT_STATS !== 'undefined') {
    // code to run if global variable present
}

but I couldn't make this work under Chrome. Is there any possibility to access document's global variable throw Chrome Extension code?

My extension's code is injected as a content-script.

Tomasz Banasiak
  • 1,540
  • 2
  • 13
  • 19
  • It sounds like something a content script should do , no ? https://developer.chrome.com/extensions/content_scripts.html – Joel Blum Feb 12 '14 at 15:11
  • "However, content scripts have some limitations. They cannot: [...] Use variables or functions defined by web pages or by other content scripts" – kmoe Feb 12 '14 at 15:12
  • Content scripts are executed in an _isolated world_. You would have to inject the code onto the page. – Ross Joo Feb 12 '14 at 15:13
  • Yep, thats the problem. I search for websites with provided global JS variable and want to test if specified page contains it or not. – Tomasz Banasiak Feb 12 '14 at 15:13
  • Well but it's possible to communicate between content script and webpage with window.postMessage , as long as the webpage is listening. but without listening I don't think there's a chance since it violates security policies – Joel Blum Feb 12 '14 at 15:14
  • https://developer.mozilla.org/en-US/docs/Web/API/Window.postMessage – Joel Blum Feb 12 '14 at 15:16

2 Answers2

17

Yes, including script into the page does run in an isolated context from the pages runtime script.

However, it is possible to work around the isolated worlds issue by pushing inline script into the runtime context via a script tag appended to the document's html. That inline script can then throw a custom event.

The included script in the isolated context can listen for that event and respond to it accordingly.

So code in your included script would look something like this:

// inject code into "the other side" to talk back to this side;
var scr = document.createElement('script');
//appending text to a function to convert it's src to string only works in Chrome
scr.textContent = '(' + function () { 
  var check = [do your custom code here];
  var event = document.createEvent("CustomEvent");  
  event.initCustomEvent("MyCustomEvent", true, true, {"passback":check});
  window.dispatchEvent(event); } + ')();'
//cram that sucker in 
(document.head || document.documentElement).appendChild(scr);
//and then hide the evidence as much as possible.
scr.parentNode.removeChild(scr);
//now listen for the message
window.addEventListener("MyCustomEvent", function (e) {
  var check = e.detail.passback;
  // [do what you need to here].
});
PatAtCP
  • 577
  • 3
  • 10
  • 1
    This is an excellent solution, in my opinion. Please note, however, that "initCustomEvent" is deprecated. Instead, you can use the "CustomEvent" constructor. Source: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/initCustomEvent – Luoruize Aug 17 '15 at 12:18
  • I'm getting a `Uncaught TypeError: ")();" is not a function` error. Is this solution still supposed to work? – Adrien Joly Nov 09 '16 at 13:29
  • 1
    it actually works by passing a multi-line string: `scr.textContent = \` var event = document.createEvent("CustomEvent"); event.initCustomEvent("MyCustomEvent", true, true, {"passback": token}); window.dispatchEvent(event); \`;` – Adrien Joly Nov 09 '16 at 13:33
  • Seems it doesn't work now - `e.detail` is null in content script. – nikicat Aug 07 '22 at 19:04
3

The javascript running on the page is running in a different "isolated world" than the javascript that you inject using content scripts. Google Chrome keeps these two worlds separate for security reasons and therefore you can't just read window.XYZ on any window. More info on how isolated worlds work : http://www.youtube.com/watch?v=laLudeUmXHM

The correct way of implementing this is by communicating with the page is via window.postMessage API. Here're how I would go about it :

  1. Inject a content script into each tab
  2. Send a message to the tab via window.postMessage
  3. If the page understands this message, it responds correctly (again via window.postMessage)
  4. Content script executes the code that it needed to execute.

HTH

Trunal Bhanse
  • 1,651
  • 1
  • 17
  • 27