How do I react to long-lived events from MV3 extensions?

Skip to first unread message


Apr 27, 2021, 3:43:39 AM4/27/21
to Chromium Extensions
Hi from disillusioned would-be developer #5,434,167. :)

For a long time (years, actually) I've been needing to build a little personal-use helper browser extension to help manage tabs and windows and other random tidbits. I've been dragging my feet due to disorientation and uncertainty about the canonical/correct/most future-proof ways to handle certain und(er)defined areas. I recently decided to gather some confidence and tentatively start poking around, and started by getting live reload (most important!) up and running, then once that was working (and resaving files would reload both the extension background page and all the pages it had open), I decided my getting-started project would be playing around with Chrome's new tab grouping capabilities.

'tabGroups' requires manifest version of at least 3.

Oh. Hmm, I do want to be future-proof even if I don't end up using tab groups, so I should probably yak-shave now and figure MV3 out from the start. So I dived in... and promptly hit my head on the bottom of the pool. The docs site has been redesigned, but the added content feels underwhelming and leaves me with more questions than I had before I started. The MV3 initiative itself gives a strong impression of being rushed to production in an unfinished state (and the byproduct of a a lot of process struggle ☹).

To get to my point, my question is this: how do I build an extension that a) lets me play with tab groups AND b) gives me long-running live-reload?

Borrowing some context from A few questions and answers regarding MV3 a few months ago (one of the scant sources of limited insight I was able to find), specifically this message from Simeon Vincent...
There are certainly ways to write extensions that won't work with service workers, but our position is that most if not all extensions can be adapted. As such, I think the most practical way to convince the engineering team that the extension platform must have a long lived execution context is to present a Manifest V3 extension that does not work and cannot be reasonably adapted to an ephemeral model with the current or planned APIs. Once we have something to dig into, I expect that we'll look at how the extension works and propose other ways to address the issues the extension runs into. And if it cannot be adapted we'll be in a much better position to assess the impact that missing capability has on the platform and the constraints we're working.

...I'm wondering if watching for system-level events that happen outside the browser sandbox and scope of the Web platform might be one such irreducible exception.

While it's an implementation detail from the perspective of MV3 itself, I'm incidentally using native messaging for the simple reason that it's simultaneously simpler to work with in practice and more secure than using a background Web server (which just feels irredeemably janky to me). The browser launches the message host for me, so JavaScript can drive the whole execution chain. I don't have to worry about launching a server myself then ensuring it stays running, since the browser can readily restart the process (which is a tiny C program that uses 1.6MB of RAM, and restarts instantly). I also have one less thing listening on a network port. And stdio is an arguably far far more resilient approach than any authentication scheme I might keyboard-smash my way through (on top of binding to AF_LOOPBACK).

But with the above in mind, perhaps the canonical solution for native messaging is some sort of model where the browser maintains long-running message host connections itself and wakes up the relevant service workers when new data comes over the wire. I expect this is likely the direction you're heading for internally.

Looking more generally at responding to network events, this idea could also inform some sort of "augmented EventSource" capability baked into WebExtensions that lets the browser itself wait for incoming data from an SSE, waking up the service worker to look at new data.

These use cases feel doable on the surface, with the caveat that the service worker would need access to a very, very fast state-storage mechanism to persist data into in order to be able to maintain stateful context between messages... and it's not hard for me to immediately imagine edge cases involving having to {de,}serialize MBs of data whenever new info comes over the wire, eg when depending on a 3rd party API that might use a hyper-efficient/diff-based protocol etc.

Thinking about how to apply the same ideas to WebSockets definitely gives me a headache :D, not because WS is inherently harder in and of itself, but because in practice it is almost always expects strong stateful context at both ends. SSE is mostly capable of the same, but feels easier to tackle due both due to lack of bidirectionality and lower usage share - I think developers have built assumptions around WebSockets that would be seriously challenged by a "wake up, process, go back to sleep" model.

And this is where my "seriously why not just call this whole area the exception" argument comes in. SSE kind of looks doable in isolation, but not for very long acknowledging the bigger picture.

(Of course, the quickest, shortest, most efficient solution to my problem - reloading tabs and extensions while developing them - is enabling remote debugging on my primary web browser. This would enable 1,000 possibilities but breaks Chrome signin due to navigator.webdriver. <p style="font-size: ∞">sigh</p>...)


(Random aside - what's the correct way to quote arbitrary text? There's no quote button and copying a quote dequotes it (?!). I had to edit the HTML of this message manually before submitting.)


Apr 27, 2021, 10:35:17 AM4/27/21
to Chromium Extensions,
Ideally this is something that must be implemented by the browser for unpacked extensions because even if you detect the change of the file, there's no way to restart the service worker (see also so you would have to restart the entire extension via chrome.runtime.reload, which closes all chrome-extension:// tabs and frames and orphanizes the content scripts so debugging is a nightmare.

I don't see a bug report on for hot-reload specifically so someone should make one.

> There are certainly ways to write extensions that won't work with service workers, but our position is that most if not all extensions can be adapted

And this is the biggest problem with the chromium extensions team - they never wrote a single complex extension themselves and never have investigated the field properly via histograms so they operate in a blissful enthusiastic ignorance underestimating the importance of problematic complex cases and exaggerating the practically nonexistent benefits for simple cases. They should have learned from other chromium components teams (and V8) who are occasionally publishing in-depth articles on chromium/V8 blog that contain proper analysis of a problem they solved along with actual measurements that helped identify the correct solution. If the extensions team has ever performed a proper investigation they would see that service workers are inferior to MV2 background scripts in so many regards that restoring the lost or broken features would be too much for the tiny extensions team to tackle. The lack of a persistent background context negatively affects not just a few edge cases like the extensions team wants us to believe but actually the majority of users because most of the extension users will probably have at least one extension that listens to frequent events (tabs.onUpdated, tabs.onActivated, webNavigation, onMessage) so its service worker will start many times a day, hundreds or even thousands times a day for users who use the browser a lot. Each start of the service worker is a heavy operation that takes at least 50ms on a high end computer plus all the time needed to parse/compile/execute the background scripts which may take 100-1000ms depending on the amount of code. And then restoring the state... Whereas the time needed for the actual work in the event listener would be typically 1ms. The overhead is beyond ridiculous. There's nothing "ephemeral" in this model.
Reply all
Reply to author
0 new messages