Content scripts being executed before page load in Chrome 109

679 views
Skip to first unread message

Simeon Velichkov

unread,
Jan 22, 2023, 5:08:55 AM1/22/23
to Chromium Extensions
I noticed something strange in Version 109.0.5414.74 (Official Build) (64-bit) on Linux, not sure if there is a bug ticket for it already, but I have an extension that is:

1. injecting a content script to all pages
2. sends a message from that content script to the underlying service worker
3. then the service worker injects some CSS and optionally JavaScript using the scripting API, it's a styling extension

What happens now on Chrome 109 is that if I have a:

1. page with white background
2. type in the first characters of a domain inside the address bar for a domain that will have a css injected to change it's background to gray
3. the page that I am currently on gets its background color changed to gray

Which leads me to believe that my content script got executed prior navigating to the page. Note that this is an extension for personal use that I am using since 2012, and several months ago I also migrated to manifest v3, so I can definitely tell that something changed in latest Chrome, since those styles are being applied on sites I visit very frequently every single day.

Simeon Velichkov

unread,
Jan 22, 2023, 5:33:25 AM1/22/23
to Chromium Extensions, Simeon Velichkov
I just put a console log in my service worker and indeed I get a message from the content script for the window.location of the page that has not yet been navigated to. This leads to the extension acting upon the currently loaded page instead.

Note that the title of this issue should read 'Content scripts being executed before navigating to page in Chrome 109' instead.

wOxxOm

unread,
Jan 22, 2023, 9:15:47 AM1/22/23
to Chromium Extensions, simeonv...@gmail.com

Simeon Velichkov

unread,
Jan 22, 2023, 10:35:02 AM1/22/23
to Chromium Extensions, wOxxOm, Simeon Velichkov
Hi, thanks for confirming this and for the links. I was using the `sender.tab.id` but the problem, as outlined in the above article, was that the tab ID was the same when pre-rendering, hence injecting the code into the current one. However, what I found is that using sender.documentLifecycle === 'prerender' is unreliable, as it sometimes produces only the prerender event, other times it produces only the active, or sometimes it does not produce any of them. And then also I was merely using the content script to chrome.runtime.sendMessage to the background page which with pre-rendering simply did not get executed all the time as it was either executed during the prerender or not executed at all if you start from the home page in Chrome .. not entirely sure. But it seems like this approach is no longer viable unless you figure out all of the events that will get fired consistently and work with those instead.

In my particular case though, using the content script on all_urls just to send back the message to the underlying server worker was identical to using chrome.tabs.onUpdated.addListener with a info.status === 'loading', also I can sort of get the host that I needed out of the tab.url, so it seems to be working. I will have to leave it for while just to be sure.

wOxxOm

unread,
Jan 22, 2023, 10:48:05 AM1/22/23
to Chromium Extensions, simeonv...@gmail.com, wOxxOm
No, my point is that you need to use sender.frameId because currently you inject in the default frameId 0 or in the entire tab.

It's unclear how exactly you use sender.documentLifecycle, so maybe you need to use documentId instead of frameId to make it 100% reliable, although I can't say more without seeing the actual code.

There's also such thing as tab.pendingUrl, which may come in handy depending on what you do exactly.

Simeon Velichkov

unread,
Jan 22, 2023, 11:10:03 AM1/22/23
to Chromium Extensions, wOxxOm, Simeon Velichkov
I see your point now, using the frameId to inject the code during prerender but inside the correct frame, that would have been neat I agree.

I guess that would have helped with the cases when I was seeing only the prerender event catched in the chrome.runtime.onMessage.addListener in the service worker, but then sometimes my content script did not send an event at all, or at least I did not see console logs in the service worker. My guess was that starting from the Chrome's home page which is not allowed for content scripts, the prerender executes my content script's code, but it fails to send the message to the service worker, and I did not get  second chance to send the message, since my code got executed already. That's my theory at least.

As for the documentId, I really don't know how can I use that in my case, since I do not keep the current documentId anywhere, and subsequently there is no way for me to know if the documentId that just arrives is new or not.

wOxxOm

unread,
Jan 22, 2023, 11:19:47 AM1/22/23
to Chromium Extensions, simeonv...@gmail.com, wOxxOm
Note that sender.frameId identifies the main page when it's prerendered, not a sub frame.
You need to use frameId because it's not 0 when prerendering.

documentId is reported in various events including onMessage and webNavigation.onCommitted, so you can use it instead of frameId in executeScript, insertCSS, sendMessage. BTW you can probably use webNavigation.onCommitted instead of content script and messaging.

Also, the content script won't re-run when using bfcache for back-forward history navigation. The content script can detect this by using pageshow event. The background script can use webNavigation API.

Without seeing the code it's hard to say what's wrong.

Simeon Velichkov

unread,
Jan 22, 2023, 11:29:19 AM1/22/23
to Chromium Extensions, wOxxOm, Simeon Velichkov
No that's very useful already, thank you. The code is on GitHub, but not that interesting to be shared. I also went on with the chrome.tabs.onUpdated.addListener instead, but I would definitely come back to this thread if I have similar issues again.
Reply all
Reply to author
Forward
0 new messages