declarative vs. programmatic content script injection.

758 views
Skip to first unread message

Stephane Saux

unread,
Aug 26, 2019, 1:28:17 PM8/26/19
to Chromium Extensions
The documentation makes me believe that the two should be equivalent.
declarative:
In the manifest:
Enter code here...            "js": ["jquery-3.4.1.min.js","jquery-ui.min.js","content.js","test_addtl_content.js"]


in both content.js and test_addtl_content.js, the first statement is a console.log ("in content.js" and "in addtl_content.js", respectively), and when the extension load on a page, I can see them in the dev console. An object defined in test_addtl_content.js is visible in the dev window when selecting the extension's context.

programmatic (I have both tabs and activeTab permissions set):
manifest:
            "js": ["jquery-3.4.1.min.js","jquery-ui.min.js","content.js"]


content.js:
chrome.extension.sendMessage({message:'runContentScript', contentScript: "test_addtl_content.js"},function (r) {
    if ( r ) {
        hdnLog("hdn: runContentScript status " + r.status);
    }
});


background script:
    } else if ( request.message == "runContentScript" ) {
        console.log("runContentScript called, with contentScript " + request.contentScript);
        chrome.tabs.executeScript(
            null,
            {
                file: request.contentScript
            },
            function () {
                console.log("runContentScript executeScript callback");
            }
        );
        sendResponse({status:"done"});
    }


With this setup, I do get the  status from send message in the page's dev console, and I do get the console.log messages (in the handler and in the execute script callback) in the background page inspection dev console, but the test_addl_content.js has not executed in the page itself: the console.log is not shown and an object in that file is not defined in the page in either the top or extension context.

In the background page I do get this seemingly unrelated error:
Unchecked runtime.lastError: Cannot access contents of url "devtools://devtools/bundled/devtools_app.html?remoteBase=https://chrome-devtools-frontend.appspot.com/serve_file/@ed9d447d30203dc5069e540f05079e493fc1c132/&can_dock=true&dockSide=undocked". Extension manifest must request permission to access this host.

I do not see this message when using the declarative method, so it must be related somehow.
Even though the documentation https://developer.chrome.com/extensions/content_scripts doesn't seem to require it, I also tried to get the URL for the additional content script, but it makes no difference:
    } else if ( request.message == "runContentScript" ) {
        var contentScriptUrl = chrome.runtime.getURL(request.contentScript);
        console.log("runContentScript called, with contentScript " + request.contentScript + ", url: " + contentScriptUrl);
        chrome.tabs.executeScript(
            null,
            {
                file: contentScriptUrl
            },
            function () {
                console.log("runContentScript executeScript callback");
            }
        );
        sendResponse({status:"done"});
    }



Gildas

unread,
Aug 26, 2019, 1:34:16 PM8/26/19
to Chromium Extensions
Your issue could be related to the fact that you don't pass any tab identifier when calling chrome.tabs.executeScript. The risk is that your background page might try to execute the script in the window of the dev tools. Re-do your test but without opening the dev tools when doing it (i.e. open it when the test is finished) and it may work as expected.

Stephane Saux

unread,
Aug 26, 2019, 2:26:39 PM8/26/19
to Chromium Extensions
Ok, it did remove the error displayed in the background inspection page (which I opened after the page had fully loaded), but it didn't execute the script on the page itself (the object is still not there in the page's dev window when it's opened after the page has fully loaded).

Simeon Vincent

unread,
Aug 26, 2019, 6:16:05 PM8/26/19
to Chromium Extensions
Stephane, I believe that you may be attempting to inject scripts when you don't have permission to do so. For clarity, the acitveTab permission grants you temporary host permissions for the current tab when the user invokes your extension (e.g. clicking the browser action, hitting a keyboard shortcut etc.). In order to use it, you need to wait for an action to occur and dynamically inject the content script using the chrome.tabs.executeScript API. 

Here's a quick demo of activeTab in action:

manifest.json
{
 
"name": "Dynamic content scripts",
 
"version": "1.0",
 
"manifest_version": 2,
 
"browser_action": {
   
"default_title": "Injects some scripts!"
 
},
 
"permissions": [
   
"activeTab"
 
],
 
"background": {
   
"scripts": [
     
"background.js"
   
],
   
"persistent": false
 
}
}

background.js
chrome.browserAction.onClicked.addListener(function() {
  console
.log("Injecting scripts…");
  chrome
.tabs.executeScript({ file: "content-1.js" });
  chrome
.tabs.executeScript({ file: "content-2.js" });
});

content-1.js
console.log('content 1');


content-2.js
console.log('content 2');

In the above extension, when you click the browser action you'll see 2 messages logged to the page's console. See activeTab for more information on this permission.

Simeon - @dotproto
Extensions Developer Advocate

Stephane Saux

unread,
Sep 2, 2019, 5:11:59 PM9/2/19
to Chromium Extensions
Thanks Simeon. Ok, I get it (although I don't think the documentation was clear about this limitation.) For the record, the reason I wanted to inject a script from my declarative content script was because when the document is a XMLDocument instead of a HTMLDocument some JS libraries will generate a JS error (so essentially they don't load). That's the case for jquery ui. My idea was to check the document class in the declarative content script and then decide whether loading jquery UI actually made sense so that my extension would not cause any JS error. I think this is a legitimate reason to use the programmatic content script loading. Similar reasons could include the detection of an element with a particular selector (so that some pages would load different functionality based on their particular content.

Alexei Miagkov

unread,
Sep 3, 2019, 12:14:35 PM9/3/19
to Stephane Saux, Chromium Extensions
Here is a different working approach where you abort early inside the content script:


--
You received this message because you are subscribed to the Google Groups "Chromium Extensions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-extens...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/chromium-extensions/8c8595a5-b5e8-4ad2-8ced-320d0b392182%40chromium.org.
Reply all
Reply to author
Forward
0 new messages