javascriptgoogle-chrome-extensionchrome-extension-manifest-v3

chrome.scripting.executeScript to load dynamic files (plugin system for a browser extension)


I have a Chrome browser extension using Manifest V3.

The Manifest.json file declares the following permissions:

"permissions": ["storage", "tabs", "scripting"]

I also declared the following, just in case:

"web_accessible_resources": [
        {
            "resources": ["plugins/*"],
            "matches": ["*://*.google.com/*"],
        }
]

I have a content_script which load properly and send the following message to the service_worker:

// Request the background script to inject the additional script
chrome.runtime.sendMessage({ action: "injectPluginsContentScripts" });

In the service worker's side, I have the following code:

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    if (message.action === "injectPluginsContentScripts") {
        for (let i = 0; i < ARR_PLUGIN_CONTENT_SCRIPTS.length; i++) {
            // Inject the specified script into the content script context
            console.log("Trying to load " + "plugins/" + ARR_PLUGIN_CONTENT_SCRIPTS[i]);
            chrome.scripting.executeScript(
                {
                    target: { tabId: sender.tab.id, allFrames: true },
                    files: ["plugins/" + ARR_PLUGIN_CONTENT_SCRIPTS[i]],
                },
                () => {
                    if (chrome.runtime.lastError) {
                        console.error(chrome.runtime.lastError);
                    } else {
                        console.log(`Importing content_script ${ARR_PLUGIN_CONTENT_SCRIPTS[i]}.`);
                    }
                }
            );
        }
    }
});

The output from the service worker console is:

Trying to load plugins/whirpool/test.js
vh_service_worker.js:31 message
: 
"Cannot access contents of url \"https://www.google.com\". Extension manifest must request permission to access this host."

I have try to make an absolute URL with the following variant:

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    if (message.action === "injectPluginsContentScripts") {
        for (let i = 0; i < ARR_PLUGIN_CONTENT_SCRIPTS.length; i++) {
            // Inject the specified script into the content script context
            console.log("Trying to load " + chrome.runtime.getURL("plugins/" + ARR_PLUGIN_CONTENT_SCRIPTS[i]));
            chrome.scripting.executeScript(
                {
                    target: { tabId: sender.tab.id, allFrames: true },
                    files: [chrome.runtime.getURL("plugins/" + ARR_PLUGIN_CONTENT_SCRIPTS[i])],
                },
                () => {
                    if (chrome.runtime.lastError) {
                        console.error(chrome.runtime.lastError);
                    } else {
                        console.log(`Importing content_script ${ARR_PLUGIN_CONTENT_SCRIPTS[i]}.`);
                    }
                }
            );
        }
    }
});

Which leads me to the following output:

Trying to load chrome-extension://nudljpeookalmkgfenhnabdkpafnleko/plugins/whirpool/test.js
message
: 
"Could not load file: 'chrome-extension://nudljpeookalmkgfenhnabdkpafnleko/plugins/whirpool/test.js'."

If I copy&paste chrome-extension://nudljpeookalmkgfenhnabdkpafnleko/plugins/whirpool/test.js into the browser, I have access to the file source.

Any idea why Chrome refuse to load the script? My goal is to run a script in the world/scope of the extension without having to explicitly declare it in the manifest file.


Solution

    1. Add "host_permissions: ["*://*.google.com/*"] to manifest.json.
    2. Remove "web_accessible_resources" from manifest.json
    3. Removechrome.runtime.getURL()
    4. Consider using sender.frameId
    5. Specify the entire array in executeScript's files:
    chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
      if (message.action === 'injectPluginsContentScripts') {
        chrome.scripting.executeScript({
          target: {tabId: sender.tab.id, frameIds: [sender.frameId]},
          files: ARR_PLUGIN_CONTENT_SCRIPTS.map(s => 'plugins/' + s),
        });
      }
    });