Manifest v3: Registering event listeners

513 views
Skip to first unread message

Matthew Atkinson

unread,
Jan 3, 2020, 12:33:31 PM1/3/20
to Chromium Extensions
Hello,

The documentation on migrating to service workers has a section about registering event listeners [0]. My understanding is that it's saying event listeners must be registered at the top level of the service worker script, or they are not guaranteed to be registered.

I'm aware of a thread about service worker life-cycle [1] but I think this is a more specific question, hence starting a new one. Apologies if that is not cricket.

The extension I maintain needs to inject content scripts into every page, and communicate with those scripts when certain events occur, in order to fully function. It checks to see if it has permissions to do so, and registers appropriate event listeners if so. It doesn't register the listeners if it doesn't have permission to access all pages (in that case it falls back to user activation via the browserAction).

Here's some simplified code from the top level of the background script...

---
chrome.permissions.contains({ origins: '<all_urls>'}, function(result) {
  if (result === true) {
    chrome.tabs.onActivated.addListener(tabActivatedHandler)
    chrome.webNavigation.onBeforeNavigate.addListener(beforeNavigateHandler)
    chrome.webNavigation.onCompleted.addListener(navigationCompletedHandler)
    chrome.webNavigation.onHistoryStateUpdated.addListener(historyStateUpdatedHandler)
  }
})
---

It sounds like the documentation [0] is saying that I can't nest these handler registrations, as they may not run because they are in an async call - is that the case?

It would be wasteful to have to register these handlers regardless of if the extension has the required permissions to use them, and have them firing all the time, and checking on every event whether the extension has permission to proceed. Is that what is intended? Am I missing a more efficient way to do this? The documentation [0] alludes to there being other ways to register event listeners, but doesn't say what they are.

In reality, the extension actually listens to permissions-added/-removed events, and uses the handlers for those to register or unregister the listeners above. It sounds like that would work OK, as long as the permissions-added/-removed listeners are registered at the top level of the background script - is that correct? Even if so, at start-up, the extension needs to decide if it should register the event handlers or not:

---
function permissionsCheck() {
  chrome.permissions.contains({ origins: '<all_urls>'}, function(result) {
    if (result === true) {
      permissionsGained()
    } else {
      permissionsLost()
    }
  })
}

// Register the event listeners on start-up (should be OK, as this is at the top level)
chrome.permissions.onAdded.addListener(permissionsCheck)
chrome.permissions.onRemoved.addListener(permissionsCheck)

// Also need to perform the check on start-up, to register events if needed.
// Would this work?
permissionsCheck()
---

Again, from the documentation [0], it sounds like that, because permissionsCheck() makes an async call via chrome.permissions.contains(), that async code is not guaranteed to run, which means the event handlers may not be registered at start-up - is that the case?

best regards,


Matthew

[0] Top Level Event Listeners: <https://developer.chrome.com/extensions/migrating_to_service_workers#event_listeners>
[1] Manifest V3, Service Worker lifecycle and async operations <https://groups.google.com/a/chromium.org/forum/#!searchin/chromium-extensions/manifest$20v3$20event|sort:date/chromium-extensions/3IQ-CvD8ICQ/ZRprnQpFAAAJ>

Simeon Vincent

unread,
Jan 3, 2020, 7:59:01 PM1/3/20
to Chromium Extensions
In reality, the extension actually listens to permissions-added/-removed events, and uses the handlers for those to register or unregister the listeners above. It sounds like that would work OK, as long as the permissions-added/-removed listeners are registered at the top level of the background script - is that correct? Even if so, at start-up, the extension needs to decide if it should register the event handlers or not

It sounds to me like you and I are on the same page. I'd like to discuss this a bit more with the extensions engineers, but as I understand it your best course of action ATM is to always register the listeners in the root scope, asynchronously check if you have the permissions you expect, and unregister the listeners if you're no longer interested. I agree that this is a bit weird, but I think that's the only way you can guarantee that your extension receives the event dispatches you expect. 

I should also note that extension APIs are only exposed if you have the appropriate permission. As such, if you are using optional_permissions in your manifest, you may need to guard those listener registration calls by checking whether the namespace is truthy.

if (chrome.webNavigation) {
  chrome
.tabs.onActivated.addListener(tabActivatedHandler)
  chrome
.webNavigation.onBeforeNavigate.addListener(beforeNavigateHandler)
  chrome
.webNavigation.onCompleted.addListener(navigationCompletedHandler)
  chrome
.webNavigation.onHistoryStateUpdated.addListener(historyStateUpdatedHandler)

}
chrome
.permissions.contains({ origins: '<all_urls>'}, function(result) {

 
if (result === false) {
    chrome
.tabs.onActivated.removeListener(tabActivatedHandler)
    chrome
.webNavigation.onBeforeNavigate.removeListener(beforeNavigateHandler)
    chrome
.webNavigation.onCompleted.removeListener(navigationCompletedHandler)
    chrome
.webNavigation.onHistoryStateUpdated.removeListener(historyStateUpdatedHandler)
 
}
});

Simeon - @dotproto
Extensions Developer Advocate

Matthew Atkinson

unread,
Jan 12, 2020, 4:55:03 PM1/12/20
to Chromium Extensions
Hi Simon,

Thanks for your reply and I’m glad to hear that you might be able to find out more about this and maybe devise a more straightforward way with the developers at some point.

Whilst it’s great to have an approach I can use, it does seem to be a bit rough around the edges. For example, what if an event that I’ve registered a handler for occurs before I can de-register the handler due to a lack of permissions? In that case, the user might find errors in their console, and attribute them to my code, but there’s not really any other efficient way around it.

I will try out your suggested approach in the meantime, though, thanks again.

best regards,


Matthew

Kent Brewster

unread,
Jan 13, 2020, 12:02:17 PM1/13/20
to Simeon Vincent, Chromium Extensions
Hi, Simeon. Not sure if you recall talking to me at Chrome Web Summit; I'm the person who runs Pinterest's browser exension:

https://chrome.google.com/webstore/detail/pinterest-save-button/gpdjojdkbbmdfjfahjcgigfpmkopogic

... which has over ten million users and has been on the Store going on seven years.

We submitted a very minor update 10am Friday; it's coming up on three days later and we're still pending review. I don't recall this ever going over eight hours before, so I wanted to check in and ask if there's anything else I can do to 1) make this happen faster or 2) be able to say to my superiors with any degree of certainty: "this is how long deploying the Chrome browser extension will take."

Thanks very much,

--Kent

Kent Brewster

unread,
Jan 17, 2020, 12:27:39 PM1/17/20
to Simeon Vincent, Chromium Extensions
Following up: our update went live around 10pm Monday night, PDT; 80 hours pending.

I'm wondering now if the review process slows down over the weekend? We don't usually ship on Friday; this was a special case.

--Kent
Reply all
Reply to author
Forward
0 new messages