Basic Service Worker support lands in Servo

29th June 2016

creativcoder / 4min


The last few weeks were exciting! With amazing guidance from Josh and awesome people at Servo I finally have a rudimentary implementation of Service Workers (hereby mentioned as SW) in Servo capable enough to monitor network requests made from different origins and optionally send back custom responses on a registered origin with basic lifecyle events (statechange, activate, install) supported.

Currently Service Workers are gated behind a dom.serviceworker.enabled flag in Servo. To make them available in DOM's window.navigator object, Servo needs to be launched with:

./mach run --release http://example.com --pref dom.serviceworker.enabled=true

An example test page specific to Servo's implementation is available under html/service-workers. This page can be used to test the current implementation.

This post will give an overview of the current implementation of SWs in Servo. Please note that the codebase is likely to change in future due to dependency over multitude of features such as the Fetch API (Tracking Issue) whose implementation is in progress and the Promise API (Tracking Issue). I also maintain a repo keeping track of SW implementation in Servo.

To get a basic idea of what Service Workers are, refer to my previous blog post. Before we talk about SWs implementation, it's helpful to mention some of the key concepts in Servo's architecture.

I will mention only the topics that are important to SWs implementation. To get a broad overview of Servo's architecture; refer to the Servo Wiki or my blog post.

Constellation

A Constellation in Servo is a co-ordinator of various pipelines along with other entities such as the windowing system, resource manager, caching system etc.

Pipeline

A Pipeline in Servo is an abstraction of a Script Thread, Layout Thread and a Paint Thread (basically a page which gets parsed and rendered to a viewport).

We'll only focus on Script Thread here.

The Script Thread is holds the DOM for a web document and is responsible for handling execution of javascript on the page.

Service workers are designed so as not to depend on any context/document so they must be at a higher level in the abstraction hierarchy than the Script Thread. They can listen to network requests globally. As such, a service worker manager thread (hereby mentioned as SWM) was devised to manage service worker registrations at different origins. The idea is to have a single manager thread that can store information of all registered SW's and can listen to network requests, activation or timeouts of any of the SW's.

The following interfaces of the Service Workers API are available in Servo (without promise integration):

Client Context:

ServiceWorker

ServiceWorkerContainer

ServiceWorkerRegistration

Execution Context:

ServiceWorkerGlobalScope

  • The initialization phase:

alt init_phase

The Constellation thread is created before any of the above mentioned threads. So it creates the required channels (here channel means creating a (Sender<T> Receiver<T>) pair for inter-thread communication) for SWM and passes the SWM's sender when the constellation creates the script thread which spawns the SWM thread. We also create a channel for the resource thread to communicate with constellation. This is needed so that the resource thread can ask for any custom response from SWM mediated by constellation.

  • The registration phase:

alt reg_phase

Let's take an example of a URL https://example.com where we register a SW over the scope: /profile. We can register a Service Worker by calling

navigator.serviceWorker.register('sw.js', { scope: '/profile' });

which returns a registration object. This registration object is then stored in the ScriptThread's thread local storage. At this point, the worker does not start controlling pages or listening to events; instead it forwards the attributes required to spawn service to the SWM which handles the initialization of ServiceWorkerGlobalScope upon url navigations.

  • The network phase:

alt net_phase

When the browser is navigated to the URL https://example.com/profile where the SW was registered, the http_loader sends a Sender channel to the SWM and asks whether the currently loaded URL has a running service worker that may have a custom response. Here the SWM forwards the network's sender to the running ServiceWorkerGlobalScope's event loop which then able to reply with any custom response that is cached in.

PS : This is still a WIP post, and there will be additions.


Want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please follow Rust's code of conduct. This comment thread directly maps to a discussion on GitHub, so you can also comment there if you prefer.

;