Skip to content

Proposal: Add once as an option in API addListener #805

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

Open
erosman opened this issue Apr 3, 2025 · 5 comments
Open

Proposal: Add once as an option in API addListener #805

erosman opened this issue Apr 3, 2025 · 5 comments
Labels
needs-triage: chrome Chrome needs to assess this issue for the first time supportive: firefox Supportive from Firefox supportive: safari Supportive from Safari

Comments

@erosman
Copy link

erosman commented Apr 3, 2025

Background

EventTarget: addEventListener() has the once option which would result in its automatic removal when invoked.

API addListener could also benefit from having such option. It can be specially useful for listeners with confined conditions. Currently, the only option is to removeListener as soon as it is invoked.
It also appears that browsers might not automatically remove listeners when the conditions no longer match (e.g. tab is removed in the following example).

Benefit

The purpose and benefit of once is:

  • avoid additional asynchronous request to remove the listener e.g. browser.***.removeListener()
  • avoid unwanted events being processed by the browser during the time it takes to perform the above asynchronous removal (which can be many in case of net requests)

Proposal

Add once as an option to API addListener

Example

browser.webRequest.onBeforeRequest.addListener(listener, {
  urls: ['<all_urls>'], 
  tabId: tab.id,
  once: true
})

See also:

@github-actions github-actions bot added needs-triage: chrome Chrome needs to assess this issue for the first time needs-triage: firefox Firefox needs to assess this issue for the first time needs-triage: safari Safari needs to assess this issue for the first time labels Apr 3, 2025
@xeenon xeenon added supportive: safari Supportive from Safari and removed needs-triage: safari Safari needs to assess this issue for the first time labels Apr 3, 2025
@polywock
Copy link

polywock commented Apr 4, 2025

Speaking of EventTarget:addEventListener(), I would love to see signals ported over. It could also emulate once, handle the temporary listeners discussed in #719, timeouts, and more.

Manually remove listeners as needed.

const controller = new AbortController()
browser.storage.local.onChanged.addListener(myListener, {signal: controller.signal})

// ... later, when listener is no longer needed ...
controller.abort()

Automatically remove a listener after a specified duration.

browser.storage.local.onChanged.addListener(myListener, {
    signal: AbortSignal.timeout(1000)
})

Remove after one use. Not as a pretty as once.

const controller = new AbortController()
browser.storage.local.onChanged.addListener(() => {
     controller.abort() 
     // ... do something once 
}, {signal: controller.signal})

Temporary listeners tied to background lifetime (#719)

const controller = new AbortController()
browser.storage.local.onChanged.addListener(myListener, {signal: controller.signal})
// Once background suspends, the browser will remove the listener. 

@carlosjeurissen
Copy link
Contributor

carlosjeurissen commented Apr 4, 2025

Ideally we would make the switch to addEventListener completely. This way we get these for free. Just like the matchMedia() API did in the past. They used to have addListener(callback) but replaced it with addEventListener('change', callback). See https://drafts.csswg.org/cssom-view/#dom-mediaquerylist-addlistener

Take for example, the recently proposed browser.runtime.onInvalidated.addListener in #792. As alternative we could introduce browser.runtime.lifecycle which would be a browser.runtime.LifecycleEventTarget object which extends EventTarget. Allowing assignment of events using browser.runtime.lifecycle.addEventListener("invalidated", console.log);. browser.runtime.lifecycle could support the events invalidated, installed, restartRequired, startup, suspend, suspendCanceled, updateAvailable.

@xeenon
Copy link
Collaborator

xeenon commented Apr 4, 2025

I would also prefer we switch to addEventListener and EventTarget completely instead of adding one-off features we like from the DOM API.

@fregante
Copy link

fregante commented Apr 5, 2025

The argument against that has been that it's hard to feature-detect, whereas events defined this way can be detected via 'onInvalidated' in chrome.runtime. So it would be nice to preserve this ability in case of switch.

As for the abort signal, aren't extensions service workers incompatible? AbortSignal.timeout(5000) is restarted every time the worker is restarted. A timeout of 3_600_000 would never be reached. This is the reason why the Alarms API exists, I think.


I implemented both once and signal in my library here, with the same warning above:

The first API could be an official way to offer the full "addEventListener" API without breaking feature detection nor backwards compatibility.

@polywock
Copy link

polywock commented Apr 5, 2025

@fregante Even if it's limited to the background's lifetime, it would still be beneficial when used alongside #416 (or similar workarounds).

@Rob--W Rob--W added supportive: firefox Supportive from Firefox and removed needs-triage: firefox Firefox needs to assess this issue for the first time labels Apr 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs-triage: chrome Chrome needs to assess this issue for the first time supportive: firefox Supportive from Firefox supportive: safari Supportive from Safari
Projects
None yet
Development

No branches or pull requests

6 participants