Clarifying service worker behavior on chrome.runtime.onConnect.addListener event

1,278 views
Skip to first unread message

Browser Extenstion

unread,
Jul 13, 2022, 8:26:25 AM7/13/22
to Chromium Extensions
Les suppose we have content script and service worker:

content_script.js
let port;
function connect() {
    port = chrome.runtime.connect({name: 'connect'});
    port.onDisconnect.addListener(connect);
}
port.postMessage({message: 'message'});
port.onMessage.addListener(response => {
    console.log(response);
})
connect();

sw.js
chrome.runtime.onConnect.addListener(port => {
    port.onMessage.addListener(response => {
        setInterval(() => {port.postMessage({message: 'response'})}, 10000)
    })
})

Chrome 103.0.5060.114
The port in content script will be disconnected in 5 minutes.
Using the workaround from this page .
The service worker actually stay connected (active), but the port in content script disconnected. You'll newer receive any response after 5 minutes.


Chrome Canary 105.0.5176.0
The port in content script remains connected if you receive any response from the service worker.
The service worker stay persistent connected (active) without using workaround, even without sending messages from content script.


So I want to clarify what expected behavior in upcoming stable versions?

wOxxOm

unread,
Jul 13, 2022, 10:48:13 AM7/13/22
to Chromium Extensions, Browser Extenstion
The workarounds listed on that page use setTimeout to establish a new connection explicitly, which prolongs the lifetime timer of the service worker in accordance with the specification. When used correctly, it works in all versions of Chrome that support ManifestV3 including 103 and 105.

Regarding your example that uses setInterval + postMessage, it doesn't prolong the lifetime of the service worker in my tests, and it shouldn't per the specification of service worker, so there's either a bug in Chrome or you have an open devtools for the service worker, which intentionally stops the lifetime timers. If I don't open devtools, I see that the service worker dies in 5 minutes + 30 seconds as it should, then onDisconnect event is fired which calls connect() again thus starting up the service worker again. You can see its age if you open Chrome's built-in Task Manager and show the "Start time" column.

Simeon Vincent

unread,
Jul 13, 2022, 12:17:59 PM7/13/22
to wOxxOm, Chromium Extensions, Browser Extenstion
The workarounds listed on that page use setTimeout to establish a new connection explicitly, which prolongs the lifetime timer of the service worker in accordance with the specification. - wOxxOm

The "'Persistent' service worker while a connectable tab is present" workaround outlined in wOxxOm's StackOverflow post is not officially supported and is not guaranteed to work in future versions of Chrome. 

Simeon - @dotproto
Chrome Extensions DevRel


--
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/c68bc3bf-7ca1-4af4-9682-ab9dbdff362en%40chromium.org.

wOxxOm

unread,
Jul 13, 2022, 12:21:49 PM7/13/22
to Chromium Extensions, Simeon Vincent, Chromium Extensions, Browser Extenstion, wOxxOm
It is guaranteed to work because there's no way to disable it.

Simeon Vincent

unread,
Jul 13, 2022, 12:47:18 PM7/13/22
to wOxxOm, Chromium Extensions, Browser Extenstion
Why is that? I'm not aware of any fundamental reason why opening a port must extend a service woker's lifetime, but I suspect that you've dug through Chromium source more than I have so you may know something I don't.

Simeon - @dotproto
Chrome Extensions DevRel

wOxxOm

unread,
Jul 13, 2022, 1:11:51 PM7/13/22
to Chromium Extensions, Simeon Vincent, Chromium Extensions, Browser Extenstion, wOxxOm
Opening a port creates an ExtendableEvent defined in the service worker specification, which in Chrome lives for 5 minutes. An ExtendableEvent by definition extends the lifetime of a service worker, otherwise it would be a violation of the specification.

This workaround can't be disabled because it emulates a real user activity. A user can interact with the browser within 5 minutes and each interaction can be a signal for the extension to open a port. The workaround uses a hardcoded delay but it could be random and it could originate from the content script and even from a DOM event. It means there's no way to suppress this behavior.

wOxxOm

unread,
Jul 13, 2022, 1:29:04 PM7/13/22
to Chromium Extensions, wOxxOm, Simeon Vincent, Chromium Extensions, Browser Extenstion
Note that messaging isn't the only avenue that can be used to prolong the SW lifetime indefinitely. Extensions can use other `chrome` events e.g. chrome.storage.onChanged in SW and periodically write to the storage in the content script, chrome.webNavigation.onReferenceFragmentUpdated can be used on a dummy hidden iframe inside a tab where the content script periodically updates its #hash part of the URL. There are many ways to emulate a real site activity.
Message has been deleted
Message has been deleted

Browser Extenstion

unread,
Jul 13, 2022, 3:47:34 PM7/13/22
to Chromium Extensions, wOxxOm, Simeon Vincent, Chromium Extensions, Browser Extenstion
@wOxxOm, first of all thank you very much for your workarounds and other helpful instructions.

Here the code that does work in 105 and does not work in 103.
I'm using "persistent" service worker while a connectable tab is present and not workaround for ports because I'm using the connection only for specific domains, but I still need the persistent service worker for other reasons.

So, in order to check the behavior:
1. Install the extension (change the crx to zip)
2. Open any domain, for example https://example.com
3. Open console and wait for responses.

Service worker sends response every 30 seconds.
So, in current version (103) after 9 responses you will never receive any data.
In canary version (105) you will receive responses until you disconnect the tab. And this is the expected behavior.
MV3 Bug onConnected.crx

wOxxOm

unread,
Jul 14, 2022, 10:54:50 AM7/14/22
to Chromium Extensions, Browser Extenstion, wOxxOm, Simeon Vincent, Chromium Extensions
I confirm that service worker doesn't die in Chrome 104 and newer when using your extension and this is the expected behavior introduced in crrev.com/1011741 that fixed a bug in earlier versions of Chrome. This bug caused the SW to die in earlier versions of Chrome when even one port's lifetime reached the maximum 5 minute period even if other ports were still connected. Your extension used one such port named "connect". The other port it uses is reconnected preemptively thus keeping the SW alive.

Simeon Vincent

unread,
Jul 14, 2022, 6:44:40 PM7/14/22
to wOxxOm, Chromium Extensions, Browser Extenstion
Opening a port creates an ExtendableEvent defined in the service worker specification, which in Chrome lives for 5 minutes. An ExtendableEvent by definition extends the lifetime of a service worker, otherwise it would be a violation of the specification.

I don't think your interpretation of the specification is accurate. There are two relevant areas of the specification that come into play: the definition of a service worker's lifetime and the way ExtentableEvent interacts with that lifetime.

Regarding the definition of a service worker lifetime, the Lifetime section of the spec currently states:

The lifetime of a service worker is tied to the execution lifetime of events and not references held by service worker clients to the ServiceWorker object.

A user agent may terminate service workers at any time it:
  • Has no event to handle.
  • Detects abnormal operation: such as infinite loops and tasks exceeding imposed time limits (if any) while handling the events.
Rephrasing this slightly, a UA may terminate a service worker if it does not behave as desired. The only description of "abnormal operation" that the spec provides are the examples that immediately follow: "such as infinite loops and tasks exceeding imposed time limits". Given the non-specific, open-ended nature of this language, I would argue that the specification allows individual UAs to define for themselves what they consider "abnormal operation." Even if we read the description of abnormal operation literally only permitting the UA to terminate for "infinite loops" and "tasks exceeding imposed time limits", the definition, implementation, and enforcement of these time limits are not specified. This implicitly leaves it to UAs to decide how they define and enforce "abnormal operation."

As for the interaction of ExtendableEvent with a service worker's lifetime, the spec states the following in the definition of event.waitUntil(f),:

The user agent should not terminate a service worker if Service Worker Has No Pending Events returns false for that service worker.

At first this text may seem to prohibit UAs from terminating a service worker so long as any extendable event is active (has promises that haven't timed out or been handled), but that interpretation depends on an inaccurate reading of the term "should not". The Document conventions section states that terms like "must", "may", and "should not" "are to be interpreted as described in RFC 2119." This RFC contains the following definition for "should not."

4. SHOULD NOT   This phrase, or the phrase "NOT RECOMMENDED" mean that
   there may exist valid reasons in particular circumstances when the
   particular behavior is acceptable or even useful, but the full
   implications should be understood and the case carefully weighed
   before implementing any behavior described with this label.

In other words, the specification recommends against terminating a service worker when it has pending events, but allows that UAs may do so in some circumstances. Ultimately termination of a service worker may occur at the discretion of a UA.

Chrome's current intent is that an extension's service worker will be terminated after five minutes (plus a small window). The only exception to this is that native messaging ports opened by the service worker should extend the service worker's lifetime for the duration of the native messaging port (plus a small window). Anything else that extends an extension service worker's lifetime is not behaving as expected and is subject to change.

Simeon - @dotproto
Chrome Extensions DevRel

wOxxOm

unread,
Jul 15, 2022, 1:29:30 AM7/15/22
to Chromium Extensions, Simeon Vincent, Chromium Extensions, Browser Extenstion, wOxxOm
When I joked about ExtendeableEvent extending the lifetime it was a pun. Actually, any external event extends the lifetime of the SW as you already quoted from the specification.

You highlighted "abnormal operation" but it doesn't apply to the situation here: there is no infinite loop, there is no task exceeding the time limit. Your mistake is that you don't understand what a task is. It's a term in JS event loop. In the code that uses messaging ports all the tasks last for microseconds.

Simeon Vincent

unread,
Jul 15, 2022, 2:31:49 PM7/15/22
to wOxxOm, Chromium Extensions, Browser Extenstion
Your comments about ExtendableEvent didn't register as a joke to me; Poe's law strikes again.

I think you're right about the meaning of task in that context. The spec doesn't decorate this term as it does in other areas, but I agree that it makes sense contextually. 

Simeon - @dotproto
Chrome Extensions DevRel

Reply all
Reply to author
Forward
0 new messages