This document describes an API to facility cross origin communication between service workers and webpages.
With the navigator.connect API clients (either webpages or some flavor of worker) can connect to services, with these services being implemented by service workers.
// http://example.com/webapp.js navigator.connect('https://example.com/services/echo').then( function(port) { port.postMessage('hello'); port.onmessage = function(event) { // Handle reply from the service. }; } ); // https://example.com/serviceworker.js this.addEventListener('crossoriginconnect', function(event) { // Optionally check event.client.origin to determine if that origin should be // allowed access to this service. event.acceptConnection(event.client.targetUrl === 'https://example.com/services/echo'); }); this.addEventListener('crossoriginmessage', function(event) { event.source.postMessage(event.data, event.ports); });
The terms event handler, event handler event type, and queue a task are defined in [[!HTML5]].
Promise
is defined in [[!ECMASCRIPT]].
EventInit
,
DOMException
,
and AbortError
are defined in [[!DOM]].
MessageChannel
,
MessagePort
, and
MessageEvent
are
defined in [[!WEBMESSAGING]]
Service Worker, ServiceWorkerRegistration, and ServiceWorkerGlobalScope are defined in [[!SERVICE-WORKERS]].
Most of what is described here can already be achieved using cross origin iframes, regular cross origin messaging, and regular service worker messaging. As such the security and privacy implications of this API are minor. Having said that, there are a few parts that are a few interesting security and privacy considerations to keep in mind:
User agents should not leak any information on sites a user has visited etc to other webpages. In particular this means that a service rejecting a connection attempt should be indistinguishable from no service being installed to service a particular URL.
Currently this API only allows connecting to services that have already been installed. If in the future some mechanism where attempting to connect to a service can trigger installation of a service worker, this of course has its own set of security and privacy implications.
The connect
method when invoked runs the "Connect" algorithm.
Maybe connect should have an additional parameter to connect to pass extra structured data to the connection event handler.
Somehow describe that both closing the message port returned by connect and the context connect was called in being destroyed result in this webpage being removed from the cross origin clients for all service workers.
Every cross origin client connected to, or attempting to connect to a service worker, is represented by a CrossOriginServiceWorkerClient instance. Clients that are currently connected are stored in an internal bookkeeping list of clients associated with each Service Worker Registration.
The origin
attribute exposes the origin of the client that connected to this Service Worker.
The targetUrl
attribute exposes the url it connected to.
The
postMessage
method when invoked calls postMessage on the hidden
MessagePort
associated with this client. If postMessage
is called on a client whose connection has not been accepted yet (or when the connection
has been rejected), postMessage will raise some exception.
Whenever a message is targetted at the implicit MessagePort
associated with a CrossOriginServiceWorkerClient
, the "Deliver Message" algorithm is executed.
The
getAll
method returns all cross origin clients that are currently connected
to this service worker. Specifically this includes all clients for which an
oncrossoriginconnect event has been dispatched, and whose connection has been accepted.
If a client has closed its messageport it will no longer be returned by getAll, and
similarly when a client is unloaded it will no longer be returned by getAll.
The getAll method returns the same list of clients for all service workers that are part of the same service worker registration, even though connect and message events are only dispatched to the active service worker version for a particular registration.
It might be useful to allow filtering on targetUrl in a call to getAll, although that can easily enough be added on top of it.
The Service Worker specification defines a ServiceWorkerGlobalScope
interface [[!SERVICE-WORKERS]], which this specification extends.
The oncrossoriginconnect
attribute is an event handler whose corresponding event handler event type
is crossoriginconnect
.
The oncrossoriginmessage
attribute is an event handler whose corresponding event handler event type
is crossoriginmessage
.
crossoriginconnect
event
The CrossOriginConnectEvent interface represents a received cross origin connection attempts.
The acceptConnection
method must be called with a promise that resolves to true or false.
crossoriginmessage
event
This is a MessageEvent
whose event.source
is an
instance of CrossOriginServiceWorkerClient
.
This isn't possible with the current spec for MessageEvent. The service worker spec has a similar problem with their onmessage event and using a ServiceWorkerClient as source.
Promise
.
DOMException
whose name is "AbortError
" and
terminate these steps.
It might be nice to be able to connect to a service without that service having to have been installed first. Maybe web manifests can help here to provide a way to determine what service worker to install for a specific service URL?
crossoriginconnect
using
CrossOriginConnectEvent
interface at the returned global object.
Better define these next steps.
acceptConnection
, or null
if no event listeners
called acceptConnection
.
true
or a Promise resolving to
true
, do the following:
MessageChannel
.
CrossOriginServiceWorkerClient
associated with the second
port of messageChannel.
DOMException
whose
name is "AbortError
".
Figure out sensible behavior when there is no active worker. Probably wait for some worker to become the active worker?
Somehow phrase this to allow other ways for a user agent to connect to services, in particular services that are not implemented as a service worker.
MessageEvent
targetted at the
implicit MessagePort
.
ServiceWorkerRegistration
associated with this connection.
CrossOriginServiceWorkerClient
.
crossoriginmessage
at the returned global object.
Note that this means that a message is always delivered to the currently active service worker for a given registration, even if the crossoriginconnect event was delivered to a different version.
Also note that this means that a later service worker registration with a narrower scope that would have captured the connection attempt had it existed at the time the connection was made does not now suddenly get messages for this connection. A ServiceWorkerRegistration is associated with a connection at connection time; further messages don't try to look up a different service worker registration for the target URL, but reuse the same registration.