Proposal: Asynchronous Listener Registration#989
Conversation
Rob--W
left a comment
There was a problem hiding this comment.
This proposal looks useful to extensions, and feasibly implementable by browsers.
Right now it is focused on adjusting event registration behavior at initialization.
Would you be willing to consider expanding to the post-initialization phase? With a method to control listener registration behavior, we can reduce the number of unnecessary wake-ups (#719) and writes to disk (frequency and size). E.g.:
browser.runtime.markBackgroundEventStage({
// Commit listeners and flush (as proposed)
listenersReady: true, // can only be true once
// Whether to persist across browser/extension restarts
persistAcrossSessions: true, // default true
// Whether to persist past worker termination.
persistAcrossUnload: true, // default true
});
try {
browser.runtime.markBackgroundEventStage({
persistAcrossUnload: false,
});
browser.webNavigation.onBeforeNavigate.addListener(...);
} finally {
browser.runtime.markBackgroundEventStage({
persistAcrossUnload: false,
});
}This is just an example, an enum could also apply here. The method would update a renderer-side flag specific to the extension worker, and whenever addListener is called, the flag would be passed along to help the browser with making the decision on whether to persist, and to what extent.
|
|
||
| ## Future Work | ||
|
|
||
| N/A |
There was a problem hiding this comment.
Potential future work: dispatch runtime.onPerformanceWarning event when events have been dropped. This would enable extensions to detect bugs. This event was already described in #456 and implemented in Firefox, documented at https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onPerformanceWarning
There was a problem hiding this comment.
What do you mean by "events have been dropped"? Because of queue overflow / initialization timeout?
Completing initialization without re-registering a listener can lead to "events being dropped" in some sense, but that would be working as intended.
There was a problem hiding this comment.
What do you mean by "events have been dropped"? Because of queue overflow / initialization timeout?
Yes to both. If the extension is somehow in a deadlock (e.g. initialization depends on an event, but events are all suppressed), then knowing that (critical) events have been dropped would be useful.
If the browser is permitted to flush the queue and force the initialization when a timeout is reached, notifying extensions would enable them to detect bugs. Perhaps this should be one of the events that are not delayed once registered.
In theory a full queue could eventually trigger a crash and theoretically trigger crash reports that could integrate in the Reporting API. Only Chrome recognizes the "crash-reporting" endpoint though.
Completing initialization without re-registering a listener can lead to "events being dropped" in some sense, but that would be working as intended.
Indeed.
I agree this is a real problem, and this API shouldn't preclude a future solution for "non-persistent" listeners. But I prefer not to fold that into this proposal. I'd rather add this as "Future Work", and I think a per-listener API would be better, e.g. an explicit |
|
Additionally, I realized |
Co-authored-by: Rob Wu <rob@robwu.nl>
Co-authored-by: Rob Wu <rob@robwu.nl>
Co-authored-by: Rob Wu <rob@robwu.nl>
Co-authored-by: Rob Wu <rob@robwu.nl>
|
Sorry for the delayed response -- please take another look. |
|
@AndreaOrru checked on the potential backwards compatibility issue. Tested the recent versions of Chrome, Firefox, Safari and Orion on desktop and all of them load fine with additional properties in the background manifest key. See https://jeurissen.co/webext-demos/background-unknown-key |
|
|
||
| ### Behavior | ||
|
|
||
| #### Initialization Lifecycle |
There was a problem hiding this comment.
We should call out in this section that handling of and limits on the event queues up to individual user agents.
While the limits themselves may be undefined, we may want to define some reporting mechanisms for these cases similar to what @Rob--W suggested in this comment.
|
This solutions has an inherent problem of delaying the first dispatch, it's conceptually like the |
These features are developed independently, the PR for the synchronous storage is at #793 and actively going through review and revision cycles right now. It is a subject of discussion whether synchronous storage should persist across browser restarts. If that is not the case, an implementation could try to synchronously access data where available, and otherwise fall back to the delayed registration proposed here. |
|
@AndreaOrru The current proposal postpones all events, by design. While this penalty hopefully encourages extension developers to minimize the delay, the reality may be different. I'm sketching a few scenarios below where the specified behavior may be too strict. They could be mitigated but that may result in additional complexity. I'd like to see your thoughts on these. To be clear, this not intended as a long lasting blocker, but to check if the API is in the desired shape before we proceed to the implementation. In a comment above I mentioned Time-sensitive events: Network requests may be blocked on webRequest event handlers (unconditionally in Firefox, in Chrome conditional for policy-installed extensions except Documenting risks of API usage: When events are delayed, TOCTOU issues become more likely. This has always been an issue with asynchronous APIs and is not new, but I think that it is worth noting that the use of this API increases the risk of that. There may be some API-specific solutions in some cases (e.g. Some events may have different requirements for handling them. E.g. maybe I see the appeal in the proposal here from the ease of implementation and their obvious semantics. Perhaps it is sufficient - I am not opposed to the approach. Alternative API design knobs I can think of:
|
|
Thanks @Rob--W, this is a good articulation of the main trade-offs in the proposal. Allow me to group a few paragraphs and answer inline. =)
Yes, the current design postpones extension API events until
I don't think we should switch the default behavior to "dispatch as soon as a listener for this event is registered". Although it improves latency in some cases, it introduces order-dependent behavior during initialization, as you correctly point out. That undermines the atomicity that this proposal is trying to provide, which seems like a greater loss to me.
Agreed that not all events have the same requirements. More granular controls, such as per-event manifest declarations or an API to opt individual events in/out of postponed dispatch, may be useful. My inclination is to treat those as future work unless the group feels the default semantics are too broad for the initial implementation.
This seems qualitatively different from dispatching e.g.
Acknowledged, but I don't see a way around it; it's the nature of async APIs, and this proposal can increase the frequency of such scenarios. The documentation should warn that delayed event delivery can increase stale-state / TOCTOU risks. Extensions should revalidate when handling delayed events and prefer stable identifiers where available. @dotproto, once we reach consensus on the semantics of "late registration", I will update the proposal with your suggestions. @Rob--W, let me know if you're comfortable with the trade-offs, and I'll update the proposal to call out some of these points explicitly as well. |
Currently, the WebExtensions platform requires event listeners to be registered synchronously during a service worker's initial script evaluation. This makes e.g. conditional listener registration based on asynchronous state (like
browser.storage) brittle, forcing developers to rely on complex workarounds.This proposal introduces an
"async_initialization": truemanifest opt-in and a newbrowser.runtime.markInitializationComplete()API to address this issue.