Thomas Jones Thomas Jones - 22 days ago 5
Javascript Question

Firefox WebExtention executeScript runAt ignorance

I'm writing small WebExtention for Firefox browser. It turns out Firefox handles the

runAt
property differently for content scripts from manifest.json vs. from
browser.tabs.executeScript
method.

content-script.js:

console.log('content script fired!');


background script settings-script.js:

function handleUpdated(tabId, changeInfo, tabInfo) {
var pageVar = "console.log('executeScript fired!');
var executing = browser.tabs.executeScript(tabId, {
code: pageVar,
runAt: "document_start"
});
}

browser.tabs.onCreated.addListener(handleUpdated);
browser.tabs.onUpdated.addListener(handleUpdated);


test-page.html:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test my behaviour</title>
</head>
<body>
<script type="text/javascript">
console.log('start very first page script');
</script>
</body>
</html>


manifest.json:

"permissions": [
"activeTab",
"storage",
"tabs",
"http://*/*",
"https://*/*",
"file://*/*"
],
"background": {
"scripts": ["background/settings-script.js"]
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content-script.js"],
"run_at": "document_start"
}
],


The console output looks like this:

'content script fired!'
'start very first page script'
'executeScript fired!'


So, why does Firefox ignore my
runAt
property and keep executing script on
document_idle
when I inject it with
tabs.executeScript()
programmatically?

Is there something I've missed?

Xan Xan
Answer

So, why does Firefox ignore my runAt property and keep executing script on document_idle when I inject it with tabs.executeScript() programmatically?

It does not. It's just too late - you're in a race with the page.

tabs.executeScript will honor runAt as the earliest opportunity to run a script. But by the time your executeScript actually runs, the page has already fully loaded.

Consider this: with scripts defined in manifest.json, Chrome knows in advance that some script has to precede the page loading.

On the other hand, tabs.onCreated/tabs.onUpdated fire at some point after creation/update started. There is a natural processing delay, while the page merrily continues to load - it's not an event that blocks until all extensions decided what to do. And when you call the executeScript API, it again takes some time for Chrome to adjust its internal machinery.

All the while your trivial page ends loading already. For a slow-loading page you may see difference, but not with one you show as a test.


A proper solution to decide, at runtime, what scripts to include in advance does not really exist. chrome.declarativeContent.RequestContentScript is, in theory, supposed to allow that - but it's not supported even in stable Chrome builds, and certainly not in Firefox.

If you need to implement some conditional logic in document_start scripts, it's very hard - you can't do anything asynchronously, so chrome.storage and Messaging do not help.