I have a Manifest V3 chrome extension I’m developing. When I statically display the content scripts in my manifest.json, they are injected into webpages like expected; however, when I shift them into a dynamic injection from a service worker when the extension is installed, it fails to inject the scripts (or even run them) on the page loads. In the service worker, I have asked it to print out all registered content scripts afterwards, and the content scripts do show up as registered.
The manifest.json
file without the content_scripts
:
{
"manifest_version": 3,
"name": "some_name",
"description": "...",
"version": "...",
"action": {
"default_icon": {
"16": "images/icon16.png",
"48": "images/icon48.png",
"128": "images/icon128.png"
}
},
"permissions": [
"scripting",
"storage"
],
"background": {
"service_worker": "dist/service_workers/registerContentScripts.js"
},
"content_scripts": [
],
"web_accessible_resources": [{
"matches": ["<all_urls>"],
"resources": ["static/*"]
}]
}
My src/service_workers/registerContentScripts.ts
:
import all_settings from '../default_settings';
const registerContentScripts = async () => {
if (!(await chrome.storage.session.get('registered')).registered) {
chrome.storage.session.set({ registered: true });
const cssScripts = all_settings.sites
.filter(site => site.css)
.map(site => {
chrome.scripting.registerContentScripts([{
id: site.name.replace(/s/g, '_').toLowerCase() + '_css',
css: site.css,
matches: ["<all_urls>"],
runAt: "document_start"
}]);
});
const removerScript = chrome.scripting.registerContentScripts([{
id: 'remover',
js: ['dist/content_scripts/remover.js'],
matches: all_settings.sites.flatMap(site => site.matches),
runAt: "document_end"
}])
.then(_ => console.log('Registered remover content scripts'))
.catch(err => console.error('Failed to register remover content scripts', err));
const replacerScript = chrome.scripting.registerContentScripts([{
id: 'replacer',
js: ['dist/content_scripts/replacer.js'],
matches: all_settings.sites.flatMap(site => site.matches),
runAt: "document_end"
}])
.then(_ => console.log('Registered replacer content scripts'))
.catch(err => console.error('Failed to register replacer content scripts', err));
await Promise.all([...cssScripts, removerScript, replacerScript])
.then(_ => console.log('Registered content scripts'))
.catch(err => console.error('Failed to register content scripts', err));
}
};
chrome.runtime.onInstalled.addListener(
() => registerContentScripts().then(
_ => chrome.scripting.getRegisteredContentScripts().then(
scripts => console.log(scripts)
)
)
);
chrome.webNavigation.onBeforeNavigate.addListener(registerContentScripts);
Sample of one of the content script outputs:
[
{
"allFrames": false,
"css": [
"static/css/injected_css/google.css"
],
"id": "imperial_website_css",
"matchOriginAsFallback": false,
"matches": [
"https://*.google.com/*"
],
"persistAcrossSessions": true,
"runAt": "document_start"
},
...
]
For this particular project, registering content scripts dynamically is quite a bit more preferable than statically, as I hope to add a lot of settings-based interactivity eventually, so going back to static injection is not a great option. Again, I have tried copying the content scripts I get from the console output back into my manifest.json, and it works perfectly. I’m not getting any errors on either the service worker nor the webpage’s web console (from the DevTools panel on the webpage, the content script does not even show up, and some logging at the start of that content script also does not show up, so it is entirely not being loaded).