Detecting page reloads

864 views
Skip to first unread message

Red ross

unread,
May 12, 2023, 12:54:13 PM5/12/23
to Chromium Extensions
Was looking into some options for detecting page reloads to inject a content script when it does and i'm stymied by the behaviour with Google docs.

## Context
Events for `tabs.onUpdated` have `changeInfo` in payload which would contain one of  url, status, and title.

Overall,
- When the page has loaded, `changeInfo.url` will be populated in an event.
- When the page has finished loading, `changeInfo.status` will be `completed`.
- If it reloads, the status goes back to loading and eventually to completed in subsequent events.

So if one wants to inject a content script, doing so when changeInfo.status is `completed` makes sense since it would include both the very first load and page reload.

## Weirdness
In Google docs, I see that `changeInfo.status` keeps switching between `loading` and `completed` on every newline. I'm told this is due to subframe updates. Am still checking on other potential causes though.

This makes detecting reloads tricky however, since we can't trust the `changeInfo.status`.

Some other options I've considered:
- use `webNavigation.onCommitted` and tabs.onUpdated in tandem to ensure that a reload has in-fact occurred. But adding a new permission for this seems wrng.

- Create a port with every content script being injected. When the page reloads, the port will be disconnected and we'll know that we should re-inject.

- Use `beforeunload` in the injected content script and post-back to the extension when the page is about to be discarded.

- Message the content script and check if its out there or just try to inject it anyways.

Has anyone run into this before?
















Simeon Vincent

unread,
May 12, 2023, 2:14:49 PM5/12/23
to Red ross, Chromium Extensions
Before I comment on the approaches you've listed, I want to ask why are you manually injecting content scripts?

The static content script declarations included in manifest.json will always be injected into a website. If you need broad control over whether or not the scripts are injected, such as enabling or disabling integration with a specific site, dynamic content script registrations allow you to add or remove registrations at runtime. These two are the most reliable ways to inject your scripts when a navigation occurs, thereby completely avoiding the need to manually detect and react to navigations.

With a single page app (SPA) like Google Docs, you may well also want to detect psudo-navigations – "navigations" inside a page that don't cause a frame to load. The web's Navigation API (developer.chrome.com, MDN), which shipped in Chrome 102, can be used to observe NavigationEvents in a page.

Okay, with that out of the way, I'm going to assume for the moment that for whatever reason you HAVE TO react at runtime from the background context and share some thoughts on the ideas you outlined.

Some other options I've considered:
- use `webNavigation.onCommitted` and tabs.onUpdated in tandem to ensure that a reload has in-fact occurred. But adding a new permission for this seems wrng.

WebNavigation is the more appropriate API for detecting navigation in a tab. Of the approach you've listed, I think this is best.

- Create a port with every content script being injected. When the page reloads, the port will be disconnected and we'll know that we should re-inject.

This approach could work, but you'll need to be cognizant of the extension service worker's lifetime. Chrome made some improvements to SW liftimes behavior near the beginning of the year; every event an extension receives will now extend the SW's lifetime, potentially indefinitely. That said, I'd still recommend keeping an eye out for unexpected behavior that could be attributed to the service worker terminating.

- Use `beforeunload` in the injected content script and post-back to the extension when the page is about to be discarded.

This approach has an inherent race condition. I think it should work but is not guaranteed to. I wouldn't go this route unless it was my only option, and I think you have much better options.

- Message the content script and check if its out there or just try to inject it anyways.

At first I was worried about the latency incurred by waiting for the check to time out. I had forgotten that the chrome.tabs.sendMesasge() call will asynchronously throw "Uncaught Error: Could not establish connection. Receiving end does not exist." if a content script hasn't registered a listener. If you get that error, you know your listenter is missing and can inject a script to register a listener and react to messages.

I actually rather like this approach. The downside to all of these, though, is that your content scripts will only be injected after the page has been initialized and has begun executing script. If you don't need to execute logic or shim page APIs before the page starts running script, that's not a deal breaker. Still, if you're adding UI to the page on load, that latency may make your UX feel a bit weird to the user, so it may be relevant.

Simeon - @dotproto


--
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/f2158570-95f1-4704-b3f6-64600960fbfbn%40chromium.org.

Red ross

unread,
May 13, 2023, 2:19:21 AM5/13/23
to Chromium Extensions, Simeon Vincent, Chromium Extensions, Red ross
Hey Simeon, thanks for the in-depth response!

TIL on registerContentScripts()! I had looked only at static declarations which wouldn't work for us since we needed to control where to inject more dynamically.
The dynamic script registration approach you mentioned should solve for our script injection use-case without the need for these options.


> With a single page app (SPA) like Google Docs, you may well also want to detect psudo-navigations – "navigations" inside a page that don't cause a frame to load. The web's Navigation API (developer.chrome.comMDN), which shipped in Chrome 102, can be used to observe NavigationEvents in a page.
>

True, I like the Navigation API approach to capture in-page navigations especially if we're already injecting a script, though I suppose webNavigation could also help here.
Reply all
Reply to author
Forward
0 new messages