w3c / webextensions

Charter and administrivia for the WebExtensions Community Group (WECG)
Other
594 stars 54 forks source link

Consider common API for push messages across browsers #208

Open oliverdunk opened 2 years ago

oliverdunk commented 2 years ago

In the 1Password extension, we open a long-lived WebSocket connection which allows the server to trigger a sync whenever anything changes in an account. With the move to shorter lifetimes in MV3, this is no longer feasible. It may be possible short-term if it becomes an allowed exemption in the offscreen documents proposal, but even if this is the case, it would be better to find a solution for this use case which makes better use of available resources.

Chrome already implements the chrome.gcm API, which (despite the name) supports Firebase Cloud Messaging and offers an extension API built specifically for push messaging.

In other browsers (at least in MV2), there are fewer options:

It would be great to discuss our options here for a few reasons:

Related: https://github.com/w3c/webextensions/issues/72

dotproto commented 2 years ago

Thanks for opening this issue, @oliverdunk!

The Push API is heavily linked to the idea of notifications. I haven't looked in to this too much but it's possible the two concepts should be decoupled.

I hope you don't mind, but I took the liberty to update the title to differentiate between push messages and notifications. While these topics are commonly paired in the web world, to my knowledge we do this this is primarily for anti-abuse reasons. Since browser extensions have a different threat model, I think we should discuss them independently.


This topic has been top of mind for me recently. When extension developers ask how to use WebSockets in the background of their Manifest V3 extensions, I've been recommending that they consider adopting a push message + fetch() based model. In this model, extension authors would send data to extensions using push messages (chrome.gcm or Push) and extensions would send update to the backend using one-off fetch() requests.

The main problem with this approach is that Web Push implicitly links push messages with notifications. I haven't tested this in other browsers recently, but in Chrome if a service worker receives a push message and does not display a notification, Chrome will automatically generate a notification for the origin.

I see two potential paths to bringing push message support to webExtensions:

The primary difference between these options centers on the overlap between extension APIs and web platform APIs. This may be worthy of a separate discussion or possibly an issue to raise with the W3C's TAG.

Option A could be further broken down into providing a browser-specific APIs (A1) or a common cross-browser API (A2).

Option A1 has the benefit of potentially being faster for browsers to land as they could more directly expose their implementations to extension developers but the disadvantage of being more difficult for extension developers to adopt. That concern may be mitigated through the introduction of a community-maintained library to abstract out platform-specific aspects of the underlying extension APIs.

Option A2 would provide a better experience than A1 extension developers, but requires more upfront work from browser vendors in order to align on a common namespace and API design before they can begin their respective implementations.

Option B seems like the more web-friendly approach as it leverages existing web platform APIs and capabilities. While this aligns with the statement in Chrome's Extensions Manifest that "We should reuse the web platform wherever possible instead of creating new proprietary APIs", the primary disadvantage is that it may introduce confusion on Web Platform vs. WebExtension capabilities and behaviors.

At the moment I'm personally leaning towards Option B.

larry-xu commented 2 years ago

Our extension uses HTTP long polling for syncing updates from the server, which is not feasible in the MV3 service worker environment. Ideally we would like to continue using HTTP long polling for its simplicity, but a push messaging API that works across all browsers may also help resolve this issue.

I investigated the Push API a bit, but it seems to require a unique service worker per subscription. Currently our extension subscribes to updates from multiple servers via independent connections to each server. I believe supporting this with the Push API would require the browser extension to register multiple service workers. Is this possible?

oliverdunk commented 2 years ago

@Rob--W: Something I forgot to ask in the meeting is if you see this as MV3 dependent. Assuming MV3 lands in Chrome before it's ready in Firefox, it would be great to still have a common API here. Does it sound possible to support the Push API in background pages or would that be too much work?

Rob--W commented 2 years ago

I don't know the implementation details off the top of my head, but if there is consensus on the shape of the API, I'd expect us to also be interested in a Push API. FYI here is an existing feature request for a Push API in extensions: https://bugzilla.mozilla.org/show_bug.cgi?id=1378096

justinlulejian commented 1 year ago

In relation to a common push API I'd also like to propose that extensions be able to pass false for userVisibleOnly. This would allow extensions to use push messaging as a communication method without being required to show a user visible effect when a message is received. They could still show a notification, but would have the choice.

Extensions, being a program that runs on behalf of the user in the background that can request and be granted the notification permission, would seem to justify having an exception to the user visible requirement.

From a developer perspective this would allow an additional background client/server communication method that may be preferable to them versus other methods (e.g. WebSockets).

From a browser perspective it would encourage a more efficient way of communication since the browser would only wake up the extension when the server had something to communicate, versus having a persistent open connection (e.g. WebSockets). This is particularly relevant for service worker based extensions that have lifetime restrictions.

This would not change how the notification permissions gates push API functionality. (On chromium I can confirm) we block push subscriptions and push messages if that permissions is not granted.

For chromium we see a lot of value due to requiring the use of service worker based extensions in MV3. We have a change open ([Extensions] Allow extensions to set userVisibleOnly false.) with this behavior implemented and will probably merge it soon.

I'm also happy to write a separate proposal for this for review if desired, but thought to start the discussion here.

dotproto commented 1 year ago

I'm supportive of this approach to allowing push messages without notifications in extensions. Chrome already allows extensions to receive push messages without displaying notifications via the chrome.gcm API. Allowing extensions to declare userVisibleOnly: false allows extensions to adopt push message code that is much more in line with the web platform than the current extension-specific API.

To make the suggestion a little more concrete, here's a snippet that a developer might add to their extension in order to create a silent push message subscription.

self.addEventListener('activated', (event) => {
  // Note: This promise will reject if "userVisibleOnly" cannot be granted.
  const subscribeRequest = self.registration.pushManager.subscribe({
    userVisibleOnly: false,
    applicationServerKey: "<KEY>",
  });

  event.waitUntil(subscribeRequest);
});

self.addEventListener('push', function(event) {
  console.log('[Service Worker] Push Received.', event);
});
justinlulejian commented 1 year ago

I'm supportive of this approach to allowing push messages without notifications in extensions. Chrome already allows extensions to receive push messages without displaying notifications via the chrome.gcm API. Allowing extensions to declare userVisibleOnly: false allows extensions to adopt push message code that is much more in line with the web platform than the current extension-specific API.

To make the suggestion a little more concrete, here's a snippet that a developer might add to their extension in order to create a silent push message subscription.

self.addEventListener('activated', (event) => {
  // Note: This promise will reject if "userVisibleOnly" cannot be granted.
  const subscribeRequest = self.registration.pushManager.subscribe({
    userVisibleOnly: false,
    applicationServerKey: "<KEY>",
  });

  event.waitUntil(subscribeRequest);
});

self.addEventListener('push', function(event) {
  console.log('[Service Worker] Push Received.', event);
});

Thank you for the code snippet Simeon! That's exactly the intent.

And also thanks for reminding me of the other benefit here. It's a web platform alternative that is not restricted to chromium extension APIs and Google (Firebase cloud messaging) servers.

kiaraarose commented 1 year ago

WebKit bug tracking this issue: https://bugs.webkit.org/show_bug.cgi?id=260969

oliverdunk commented 1 year ago

Chromium bug: https://bugs.chromium.org/p/chromium/issues/detail?id=1319986

yankovichv commented 10 months ago

Could somebody have a forecast for the launch of this feature?

justinlulejian commented 10 months ago

We've implemented the ability to receive push messages without notifications in extensions in Chrome: crrev.com/c/4705862. It should work for both MV2 and MV3 extensions that use service workers. It can be tested now in Canary (121.0.6100.0+) and Dev (121.0.6103.3). Calling PushManager.subscribe() does still require notification permissions which can be granted at runtime or through the manifest.