What to do
- I need to use
browser.tabs.executeScript
to inject a content script. - It should only run once for each site. (e.g. after reload, when a new tab is created and a site is loaded)
- Basically all like when I would specify it in
content_scripts
in themanifest.json
.
So why not use the declarative way with manifest.json
?
(ugh, that code in the headline is huge, looks like a CSS issue…)
Simply, because the code I need to inject is generated programmatically. Actually, in my case, it is the ID of the corresponding tab. Basically, all I want to do is letting my content script know in which tab (per ID) it is running.
This is later needed for communicating to the background script and applying further CSS changes.
Show me code
Thus, what i do is this:
browser.tabs.executeScript(tab.id, {
code: `const MY_TAB_ID = ${tab.id}`,
allFrames: true,
runAt: "document_start"
});
Now, this needs to be triggered for each new tab or reload etc. So I’ve looked at the tabs.onCreated
, but this obviously misses tab reloads. So onUpdated
seems to be the right way.
However, it triggers for all sort of arcane tab updates. Hash anchor changed? -> Update. Tab muted -> Update.
So I needed to filter it and based on looking at some of the updates that come in, I came up with this filter:
const TAB_FILTER_URLS = ["http://*/*", "https://*/*"];
// [...]
browser.tabs.onUpdated.addListener((tabId, changeInfo, tabInfo) => {
/*
only run additional injection if:
* the url is changed (when navigating to a new tab)
* the status is still loading (to exclude simple #anchor changes)
*/
if ("url" in changeInfo && tabInfo.status === "loading") {
console.log("insert content script into tab", tabId, tabInfo);
insertContentScriptFast(tabInfo).catch(console.error); // function executes code above
}
}, {
urls: TAB_FILTER_URLS
});
So what is the problem?
Now I notice, this still runs twice for a site load. (tested on https://pinafore.social/)
This is really strange…
So why is it so hard to run a content script only once? (per site load)
And BTW, have you found a solution on how to do it properly? (no,l checking inside the content script whether it has run already, is no solution. See below.)
Related
Even the official add-on example “beastify” choose the easy way of just checking in the content script whether it has ruin already.
While I could do the same, this is not only an ugly workaround, but actually hard for me, as time matters for me (because of another issue) and I cannot inject a content script, check the result, and inject the other one, only afterwards…
So I would need to check that each time…
If you want to see the bigger content, here is the whole JS file: