peter duffy
peter duffy

Reputation: 185

Content script not being allowed to access extension resources

In my extension I dynamically inject a content script. The content script is

let txt = fetch(chrome.runtime.getURL('example.txt')).then(r=>r.text());

The error I get is GET chrome-extension://invalid/ net::ERR_FAILED (anonymous) @ content-script.js:36.

In my research I have found the page https://developer.chrome.com/docs/extensions/mv3/manifest/web_accessible_resources/ that talks about how some scripts get denied resources. But it says that content scripts are not effected.

What is the issue? Are the docs wrong?

Upvotes: 0

Views: 1153

Answers (1)

Thomas Mueller
Thomas Mueller

Reputation: 678

I assume you're referring to this sentence from Manifest - Web Accessible Resources:
"Content scripts themselves do not need to be allowed."

That means you don't need to declare content-script.js as a web-accessible resource in order to inject content-script.js into a web page.

But example.txt is not a content script. It's just a file that's part of your extension. Because content-script.js runs in the context of a web page, content-script.js can only fetch() example.txt if you declare example.txt as a web-accessible resource.

If you don't want to declare example.txt as a web-accessible resource, you can have content-script.js send a message to the extension's service worker. The service worker can then fetch() example.txt and send the result back to content-script.js.

Another problem:
fetch() returns a Promise, and so does r.text()

let txt = fetch(chrome.runtime.getURL('example.txt')).then(r=>r.text());

This code doesn't put the contents of example.txt into the variable txt.
Instead, the variable txt contains a Promise that evaluates to the contents of example.txt.
You either need to chain another then-handler to your code, or use async/await. See await Error at Top Level Module in Chrome Extension Service_worker

Example with web-accessible resource

manifest.json

{
    "manifest_version": 3,
    "name": "Fetch in Content Script",
    "version": "1.0",
    "action": {
    },
    "content_scripts": [
        {
            "matches": ["*://*/*"],
            "js": ["content_script.js"]
        }
    ],
    "web_accessible_resources": [
        {
            "resources": [ "example.txt" ],
            "matches": [ "*://*/*" ]
        }
    ]
}

content_script.js

fetch(chrome.runtime.getURL('example.txt'))
.then(response => response.text())
.then(text => {
    console.log(text);
});

Example with message passing

manifest.json

{
    "manifest_version": 3,
    "name": "Fetch in Content Script",
    "version": "1.0",
    "action": {
    },
    "background": {
        "service_worker": "background.js"
    },
    "content_scripts": [
        {
            "matches": ["*://*/*"],
            "js": ["content_script.js"]
        }
    ]
}

background.js

function on_message(message, sender, sendResponse) {
    fetch(chrome.runtime.getURL(message))
        .then(response => response.text())
        .then(text => sendResponse(text));
    /*
        https://developer.chrome.com/docs/extensions/mv3/messaging/#simple
        Note: The sendResponse callback is only valid if used synchronously,
        or if the event handler returns true to indicate that it will respond asynchronously.
        The sendMessage function's callback will be invoked automatically
        if no handlers return true or if the sendResponse callback is garbage-collected.
    */
    return true;
}

chrome.runtime.onMessage.addListener(on_message);

content_script.js

chrome.runtime.sendMessage("example.txt")
.then(response => {
    if (chrome.runtime.lastError) {
        console.log(chrome.runtime.lastError);
    }
    else {
        console.log(response);
    }
});

Upvotes: 1

Related Questions