Your code works differently in the console and in the content script, because Chrome creates a separate, isolated JavaScript context for each extension's scripts.
There are many reasons for it, not least security: the page cannot affect a script running in the higher-privilege extension context. Also, it means that there are no clashes between code used by the page and the script. For more information, see the link above.
When you execute code in the console, you can notice a drop-down above the console, which by default reads <top frame>
. This is where you select which context to execute commands in. If there is a context script injected from any extension, you will see its context there too.
The script you mention refers to the JavaScript objects already defined on the page; this is why it works if you execute it in the console by default, but when called from a context script those objects do not exist.
So, are those completely isolated? Both contexts have shared access to DOM. This enables content scripts to inject code into the page's context: you can create a <script>
element and insert it into the page. This will execute the code in the page's context.
For the canonical question covering this topic, see Inject code in a page using a Content script. This is barely mentioned at all in the official documentation and frankly blew my mind the first time I saw it's possible.
So, in your case, you need a content script that acts like a proxy, injecting a page-level script:
// content.js - this is what you pass to `executeScript`
var s = document.createElement('script');
s.src = chrome.runtime.getURL('test.js'); // This is the actual script
s.onload = function() {
this.parentNode.removeChild(this);
};
(document.head||document.documentElement).appendChild(s);
For this to work, you need to add test.js
to web-accessible resources in the manifest (since the webpage itself will request to load it when you add that <script>
tag):
"web_accessible_resources" : [ "test.js" ],