Skip to content

Instantly share code, notes, and snippets.

@jungkees
Last active August 29, 2015 14:03
Show Gist options
  • Save jungkees/3154398b8deee7c70139 to your computer and use it in GitHub Desktop.
Save jungkees/3154398b8deee7c70139 to your computer and use it in GitHub Desktop.
Writing ServiceWorker-Using Specifications

This document is obsolete. See the extensibility section of the spec instead.

This is a sketch of the guide to binding an API space to a Service Worker. This content will be incorporated in the section 7. Extensibility of Service Workers spec upon necessary iterations.

Bind to a Service Worker

Promise<T> apiSpace.method(ArgumentList, optional ServiceWorker serviceWorker);
  • serviceWorker is an optional argument. When omitted, the document's Service Worker registration's active worker is used; when specified, the specified worker is used.
  • API-SW binding requires an active worker. That is, if the designated Service Worker is not an active worker at the time of the method invocation, it fails (i.e. rejects the promise if the return type of method is a promise.)
  • I have an impression that navigator.serviceWorker.ready should resolve only with an active worker ("activating" or "activated") not a worker in waiting.
    • When an API space successfully registers it with a worker in waiting, the corresponding functional event is still not getting fired. And if we allow this, it may end up with a situation where two different versions of the Service Workers are running at the same time.

[Template algorithm] Register API space with a Service Worker

apiSpace.method(list of arguments, serviceWorker) must run these steps:

  1. Let p be a newly-created promise.
  2. Return p.
  3. Run these steps asynchronously:
  4. If the optional argument serviceWorker is omitted, then: 1. Let registration be the result of running [[MatchScope]] algorithm with entry settings object's API base URL as its argument. 1. If registration is null, then:
    1. Reject p with an "InvalidStateError" exception.
    2. Abort these steps. 1. Else if registration.activeWorker is null, then:
    3. Reject p with an "InvalidStateError" exception.
    4. Abort these steps. 1. Else,
    5. Bind the apiSpace with registration.
    6. Do the necessary steps specific to the API.
    7. Resolve p with T object.
  5. Else, 1. If serviceWorker.state is either "activating" or "activated", then:
    1. Let registration be the result of running [[GetRegistration]] algorithm with serviceWorker.scope as its argument.
    2. Bind the apiSpace with registration.
    3. Do the necessary steps specific to the API.
    4. Resolve p with T object. 1. Else,
    5. Reject p with an "InvalidStateError" exception.

Examples

// will happen to be successful if the document has an active worker on
// its registration
navigator.push.register();
// but it's uncertain, so you may wait for the active worker
navigator.serviceWorker.ready.then((sw) => {
  // no need to specify the worker here
  navigator.push.register();
  // though you can
  // navigator.push.register(sw);
});

// If API init with a specific Service Worker is required, 
// this is allowed
// e.g example.com/index.html has buttons to register list of push
// services within exmple.com and this is one of them
navigator.serviceWorker.register(
  "/cocacola/sw.js", 
  { scope: "/cocacola/*" }
).then((sw) => {
  sw.onstatechange = (e) => {
    if (sw.state === "activated") {
      navigator.push.register(sw);
    }
  };
});

Define DerivedEvent and EventHandler for its functional event

Specifications may define their functional events (i.e. event interfaces derived from Event interface) and the corresponding event handlers. The event handlers should be added on the ServiceWorkerGlobalScope interface as such:

partial interface ServiceWorkerGlobalScope {
  attribute EventHandler onfunctionalevent;
};

[Template algorithm] Fire a functional event to a Service Worker

  1. Let scope be an absolute URL, serialized, representing the URL scope of the Service Worker which the API is registered with.
  2. Let event be a new initialized DerivedEvent.
  3. Invoke [[HandleFunction]] with scope and event.

[[HandleFunction]] will be defined in Service Workers spec as roughly the following algorithm: Input: scope, event Output: None

  1. Let registration be the result of running [[GetRegistration]] with scope as its argument.
  2. If registration is null, then:
  3. Return null.
  4. Let matchedWorker be registration.activeWorker.
  5. If matchedWorker is null, then:
  6. Return null.
  7. Run a worker for a script with URL matchedWorker.scriptURL and a script settings object settings object.
  8. Fire an event event on the ServiceWorkerGlobalScope associated with the Service Worker.
  9. Return.
@annevk
Copy link

annevk commented Jul 3, 2014

It seems weird that /scope/ is the identifier of the service worker. Is that really how this should work? What if the service worker changes its scope (if we give it that ability at some point), do all of these update?

@jungkees
Copy link
Author

jungkees commented Jul 7, 2014

A document uses a registration and its active worker is controlling that document. An API using SW binds its API space to a registration. It'd be natural for APIs to look up a valid registration, it registered to, to locate an active worker to fire its functional events on. The key for a registration is a scope. IMHO, the ability to change SW's scope seems sort of an excessive setting.

@jakearchibald
Copy link

We have ServiceWorkerRegistration now, so I guess that replaces the serviceworker argument (if the API can't add to the ServiceWorkerRegistration interface)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment