Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dynamically registered event listeners for extension service worker #1698

Open
hanguokai opened this issue Dec 22, 2023 · 4 comments
Open

dynamically registered event listeners for extension service worker #1698

hanguokai opened this issue Dec 22, 2023 · 4 comments

Comments

@hanguokai
Copy link
Member

Note that I'm talking about browser extension service worker, not Web service worker. But I would like to seek advice from Web service worker experts here.

Unlike the web environment, browser extensions usually provide users with some functional options in the extension settings interface. Ideally, the extension only registers relevant events in extension service worker when the user turns on a feature. So some developers are proposing to dynamically register events at WECG.

Here is my proposal:

At present, an extension Event object has these method:

addListener()
removeListener()
hasListener()
hasListeners()

addRules()
getRules()
removeRules()

For dynamically registered events for service worker, I proposal these new methods instead of changing existing methods:

subscribe(function-name: String)
unsubscribe(function-name: String)
hasSubscribed(function-name: String)

For example (there are lots of different events in browser extensions):

browser.webNavigation.onCommitted.subscribe("myFunctionName");
browser.webNavigation.onCommitted.unsubscribe("myFunctionName");

"FunctionName" is a global function name that declared in service worker, rather than a function object, and doesn't need to be registered in the first event loop when service worker wakes up. This allows the browser to remember both the event and this specific function independently of service worker's lifecycle, not just remember the event like addListener() does.

These new methods can be called in both service-worker context and non-service-worker context (e.g. a user setting page) dynamically, but only trigger events in service worker context, not in other contexts.

After calling some_event.subscribe(function-name), the browser triggers related events in service worker. If the service worker is inactive, wake up it first. Then looks for the function (by name) to execute.

I'd love to hear from the browser implementation perspective, such as whether it's possible or what problems it will encounter.

@tomayac
Copy link
Contributor

tomayac commented Jan 8, 2024

Looping in @oliverdunk and @patrickkettner who may have opinions on this.

@oliverdunk
Copy link
Member

Thanks for opening this Jackie. As we discussed in the last WECG meeting, I think improving extension events is something everyone is supportive of longer term and very curious to see if any web folk have thoughts on how we can make things more ergonomic. That said, I'm not sure if this proposal is the right one:

  • What does subscribe do that addListener doesn't? You mention that it would remember the function, but I think we could do this with addListener as well. I'm reluctant to add a new subscription method without compelling reason.
  • We're making the assumption here that it is feasible for browsers to remember functions long-term (and that developers would be ok with this) which I think would need extra validation.

I'd prefer to look at ways of using self.addEventListener in extension service workers for extension events to align with the web. And then once we've got there, we can find potential improvements to registration.

@hanguokai
Copy link
Member Author

To understand this proposal, we must first understand the current way of working.

The current way of working:

  1. Developers register event handlers at top level (i.e. the first event loop) in service worker. And the browser remembers what event types this service worker want to handle. (When service worker is terminated, the browser still remembers what event types this service worker want to handle).
  2. when new events are fired, browser wakes up service worker if it is not active.
  3. after the first event loop of service worker, browser dispatch events to event handlers that are registered in the first event loop.

This proposal is

  1. Developers can register event handlers for service worker at anywhere and anytime: including first event loop and later(e.g. in a function that runs later) in service worker, and even can be registered at outside of service worker at anytime (e.g in other extension pages).
  2. Here you will ask if event handers are not registered at top level, where does the browser find them after the first event loop of service worker?
  3. Here is the key point. When subscribe() an event, in addition to remember the event type, the browser also needs to remember the function name, so that the brwoser can find them.

Anyway, in order to be able to dynamically add or remove, enable or disable event handlers, the browser must remember some additional information beyond service worker lifecycle.

@asutherland
Copy link

asutherland commented Jan 9, 2024

I think specifying the function name dynamically via a string is a non-starter. Although there is precedent in HTML for inline JS that could be said to resemble this, it's bad precedent and is a major source of XSS issues.

For the particular use-case of WebExtensions that seems to be motivating this request, my general impression from discussion with our (Mozilla's) add-on teams is that one of our greatest concerns is dynamism in WebExtensions that enables bad actors to create extensions that seem benign but later are dynamically reconfigured to violate the user's privacy and/or security. An API surface like this would intentionally create problems for static analyses.

If the high-level goal is for WebExtensions to be able to reduce their performance impact on the browser until the user activates specific functionality, I would suggest that the correct approach for this is to have the enabling/disabling located on the WebExtension API surface that would be responsible for generating the event. Individual WebExtension logic can then add event listeners using addEventListener in the usual way. This a more appropriate layering for efficiency and devtools integration.

NavigationPreloadManager is not a perfect example of this, but is a pattern that WECG could follow if there somehow is not already an existing idiom for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants