Content script execution order changes after chromium update

247 views
Skip to first unread message

Yeferson Licet

unread,
Oct 1, 2023, 10:49:46 PM10/1/23
to Chromium Extensions
Hello there, this message is to report unusual behavior that has been causing issues. This problem might also occur with other extensions. The issue is as follows:

We have two scripts:

Script1
'content_script' is defined in manifest.json with:
runAt set as document_start,
all_frames set as true,
The script1 involves creating a node inside the website DOM.

Script2
A script registered via registerContentScripts API with:
runAt set as 'document_start',
world described as 'MAIN',
allFrames set as true,
The script2 reads the node created by script1.

On all websites, the execution order is consistently Script 1 followed by Script 2. However, after a Chromium update, the execution order switched to Script 2 and then Script 1. This behavior has been consistently reproduced. We mitigated the issue by adding a retry logic to find the node, but we would appreciate understanding the underlying issue.

This problem persists until users update the extension to a newer version or reinstall the extension.

Thanks (Happy to provide a repo with minimal code to reproduce)

wOxxOm

unread,
Oct 2, 2023, 3:55:50 AM10/2/23
to Chromium Extensions, Yeferson Licet
The design of the API itself is problematic as it never gave the guarantee about the order. A possible solution would be to introduce an additional `index` parameter in chrome.scripting.registerContentScripts which would insert the scripts at the specified position in the combined list of content scripts both from manifest.json and registerContentScripts. This would allow the extension to modify the behavior of the main content script at document_start by inserting a new dynamic script at index:0 and doing somehting like setting a variable that will be later used bythe main script.

Oliver Dunk

unread,
Oct 2, 2023, 4:36:00 AM10/2/23
to Yeferson Licet, Chromium Extensions
Hi Yeferson,

It would definitely be great if you could share some sample code and the chrome://version where you see the new behaviour.

I just tested with the code here and observed the same order in both Stable and Canary.

As a note, there are sometimes APIs where we intentionally don't specify an order - as far as I know, this is one of them. I'm definitely curious to figure out what happened but longer term your solution of adding some retry logic seems like a more robust one :)
Oliver Dunk | DevRel, Chrome Extensions | https://developer.chrome.com/ | London, GB


--
You received this message because you are subscribed to the Google Groups "Chromium Extensions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-extens...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/chromium-extensions/d828c322-ed02-4072-831d-f8b15037e1bcn%40chromium.org.

wOxxOm

unread,
Oct 2, 2023, 4:55:26 AM10/2/23
to Chromium Extensions, Oliver Dunk, Chromium Extensions, Yeferson Licet
>  there are sometimes APIs where we intentionally don't specify an order

The most likely explanation is that it never occurred to anyone. Most chrome API are designed ad hoc, by people who don't have experience in designing a consistent API. Not having a deterministic order might be intentional only when the order cannot be determined for objective reasons e.g. when executing asynchronous code, but not in this case, as the order is stored as a fully static list, synchronously available by the time the decision to inject the scripts is made.

Oliver Dunk

unread,
Oct 2, 2023, 5:08:03 AM10/2/23
to wOxxOm, Chromium Extensions, Yeferson Licet
I definitely agree that this is sometimes the case simply because it was never decided on. That said, there are absolutely times where we have had a discussion but still decided not to add an ordering. I suspect this likely falls into the first category, but I wanted to highlight that distinction (and even if we did discuss it, I suspect we might choose to say that this is a reasonable change).

As an example, after some discussion in the WECG and agreement between browsers we added a note on rule prioritization to the declarativeNetRequest docs. In that case, we make no guarantees about the order in which static, dynamic and session rulesets are executed with respect to each other. That's because we don't see a huge amount of value in specifying that right now and would rather leave implementations open to do whatever makes the most sense for them at any given time vs. documenting an order which is then much harder to change in the future :)
Oliver Dunk | DevRel, Chrome Extensions | https://developer.chrome.com/ | London, GB

wOxxOm

unread,
Oct 2, 2023, 5:14:55 AM10/2/23
to Chromium Extensions, Oliver Dunk, Chromium Extensions, Yeferson Licet, wOxxOm
AFAICT, declarativeNetRequest already has `priority` field, so the extension can ensure deterministic order, which implicilty reinforces my point. By the way, the "priority" field might be a good solution for chrome.scripting.registerContentScripts as well.

wOxxOm

unread,
Oct 2, 2023, 7:16:22 AM10/2/23
to Chromium Extensions, wOxxOm, Oliver Dunk, Chromium Extensions, Yeferson Licet
Another solution would be to add a second parameter to registerContentScripts like {ignoreManifest: true} that would use the supplied dynamic scripts instead of manifest.json's. An extension can also use chrome.runtime.getManifest().content_scripts to make a complicated re-ordering.

Yeferson Licet

unread,
Oct 2, 2023, 8:13:49 AM10/2/23
to Chromium Extensions, wOxxOm, Oliver Dunk, Chromium Extensions, Yeferson Licet
Hello, here is the repo https://github.com/y3fers0n/chrome-scripting-repro
The issue was recreated starting with Chromium 117.0.5938.88 and updating to Chromium 117.0.5938.92 (You can download brave with a specific chromium version to test it)

Yeferson Licet

unread,
Oct 2, 2023, 6:59:10 PM10/2/23
to Chromium Extensions, Yeferson Licet, wOxxOm, Oliver Dunk, Chromium Extensions
You may be wondering why two scripts? Well, scripts registered using registerContentScripts don't have access to chrome.runtime, but scripts executed using chrome.scripting.executeScript or registering using manifest do have access to chrome.runtime API

wOxxOm

unread,
Oct 2, 2023, 7:06:15 PM10/2/23
to Chromium Extensions, Yeferson Licet, wOxxOm, Oliver Dunk, Chromium Extensions
If messaging is the only reason, you can get rid of the main content script by using externally_connectable messaging with "matches": "<all_urls>", which is allowed since Chrome 107 (the documentation is outdated).

You also don't need registerContentScripts to specify "world" in Chrome 111 and newer: just do it in manifest.json's content_scripts section directly.

Yeferson Licet

unread,
Oct 2, 2023, 7:36:47 PM10/2/23
to Chromium Extensions, wOxxOm, Yeferson Licet, Oliver Dunk, Chromium Extensions
Great! I'll take a look, is there any other advantage of using externally connectable messaging vs chrome.runtime?.connect?. Thanks in advance

wOxxOm

unread,
Oct 2, 2023, 8:50:19 PM10/2/23
to Chromium Extensions, Yeferson Licet, wOxxOm, Oliver Dunk, Chromium Extensions
The advantage is that you don't need a second script, of course. You can do everything with just one script in the MAIN world. Note that external messaging isn't limited to sendMessage: you can use chrome.runtime.connect in the MAIN-world script and chrome.runtime.onConnectExternal in the background script.

The disadvantage is that the web page can successfully open a port to your extension too, so you may want to add a check in your onConnectExternal to allow only one port per tabId/frameId.

Oliver Dunk

unread,
Oct 19, 2023, 12:16:39 PM10/19/23
to wOxxOm, Chromium Extensions, Yeferson Licet
Just to follow up on the possible behaviour change here, I tried the code at https://github.com/y3fers0n/chrome-scripting-repro but saw logs indicating that it worked successfully in the latest version of Chrome Canary. Do you see the same?

It's worth noting that we added the world parameter to the support manifest keys recently which may allow you to remove the service worker code: https://developer.chrome.com/docs/extensions/mv3/manifest/content_scripts/#world-timings

Thanks wOxxOm for all of the help here as well.

Oliver Dunk | DevRel, Chrome Extensions | https://developer.chrome.com/ | London, GB
Reply all
Reply to author
Forward
0 new messages