Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Making sense of registrations #365

Closed
jakearchibald opened this issue Jul 9, 2014 · 37 comments
Closed

Making sense of registrations #365

jakearchibald opened this issue Jul 9, 2014 · 37 comments

Comments

@jakearchibald
Copy link
Contributor

At the moment navigator.serviceWorker doesn't really expose the concept of registrations. I think this is a mistake. It causes these problems:

  • .register resolves with a serviceworker that may be installing/waiting/active, this doesn't feel like the right return value for a registration
  • .getAll returns an array of workers in various states (getAll() resolution array #170)
  • .active vs .controller - one is part of the registration (along with .installing and .waiting) and the other is part of the document's use of a registration, but that's not really clear
  • Same with .onupdatefound - this is to do with the registration that matches the document is within scope of. .oncontrollerchange is to do with the document's selection of a registration

How about we offer up registration objects:

interface ServiceWorkerRegistration : EventTarget {
  [Unforgeable] readonly attribute ServiceWorker? installing;
  [Unforgeable] readonly attribute ServiceWorker? waiting;
  [Unforgeable] readonly attribute ServiceWorker? active;

  readonly attribute ScalarValueString scriptURL;
  readonly attribute ScalarValueString scope;

  // events
  attribute EventHandler onupdatefound;
};

Which means we can tidy up navigator.serviceWorker:

interface ServiceWorkerContainer : EventTarget {
  [Unforgeable] readonly attribute ServiceWorker? controller;
  readonly attribute Promise<ServiceWorker> ready;

  Promise<ServiceWorkerRegistration> getRegistration(optional ScalarValueString url = "");
  Promise<sequence<ServiceWorkerRegistration>?> getAllRegistrations();
  Promise<ServiceWorkerRegistration> register(ScalarValueString scriptURL, optional RegistrationOptionList options);
  Promise<any> unregister(optional ScalarValueString scope = "/*");

  // events
  attribute EventHandler oncontrollerchange;
  attribute EventHandler onreloadpage;
  attribute EventHandler onerror;
};

Now we don't have sync access to registration state, other than .controller, which is the document's use of the registration. Instead of .getAll we have .getAllRegistrations. Instead of installing/waiting/activate we have .getRegistration, which can also be used to get a registration (if there is any) for a particular url.

If you get a registration object, is it possible (or sensible) to have the installing/waiting/activate properties continue to update live, or is that breaking everything?

@michael-nordman
Copy link
Collaborator

This is a good split, actually matches our impl more closely. I don't think this needs to be async for the current document, so a simple .registration attribute would be fine and I think a small improvement.

The .getRegistration(url) method would definitely need to be async. I think your intent is the url param to be a document url, not a scope. The name of the method or param doesn't make that obvious.

Would we also need an .onregistrationchange event handler so existing pages can become aware of a brand new registration coming down the pipe?

@dominiccooney
Copy link

I think this will simplify the implementation in Blink.

Does this mean that a page can enumerate the registrations for the entire origin, or just registrations that could match the page's URL?

.installing, waiting, active can be synchronous; making them Promise would not significantly simplify things.

re: installing, waiting, active "updating live" I think you need to decide how many registration objects there can be for a given scope at one time. Do successive calls to register with the same scope create new registrations (and resolve to a different object) or update existing ones?

In the former case you complicate getRegistration (which one should it pick while there's a new registration installing) but could simplify ServiceWorkerRegistration to have a single 'worker' property and .state can infer about as much as separate waiting, etc. properties.

I think it is necessary to clarify whether getRegistration takes a scope or a URL. I assume it takes a scope and matches exactly because otherwise what is the point?

@dominiccooney
Copy link

I just wanted to point out that #357 is also confused by the single/multiple registration confusion.

Separately, thinking about whether getRegistration takes a scope or a URL, I think either could be valid depending on the use case. So maybe there are actually two APIs here:

For a ServiceWorker, get its registration (would use scope.)

For this page, get the best matching registration (basically [[MatchScope]]. Would use a URL.) The classic navigator.serviceWorker.installing, waiting, active properties were implicitly using this.

Are there higher-level use cases that you can look to for guidance about which APIs are needed?

@jungkees
Copy link
Collaborator

Agreed on the suggested direction. The only concern was whether the concept, registration, would be useful to authors not adding unnecessary complexity. However, I cannot disagree that exposing this concept explains the actual model better and reflects underlying implementation better. (@nikhilm Is it true with gecko implementation too?)

re: installing, waiting, active "updating live" I think you need to decide how many registration objects there can be for a given scope at one time. Do successive calls to register with the same scope create new registrations (and resolve to a different object) or update existing ones?

I think we should update existing ones. Once an entry was added to [[ScopeToRegistrationMap]], that registration object should be uniquely maintained until it gets deleted by [[Unregister]].

For the decision of the getRegistration() semantic, I agree we should figure out what the major use cases are. It seems it's a question of whether we'll expose [[MatchScope]] or [[GeRegistration]] to authors. Basically, getRegistration() without any argument falls on [[MatchScope]] case (implicitly with the document's URL) as it returns the registration that the document will use when navigated.

If we expose getRegistration() method, this sort of usage can be dealt with for example:

// Push registration with the document's default registration 
navigator.serviceWorker.ready.then(() => {
  navigator.push.register(/*...*/).then((pushReg) => {
  });
});

// Push registration with a particular SW registration
navigator.serviceWorker.getRegistration("https://example.com/cocacola").then((registration) => {
  return navigator.push.register(/*...,*/ registration).then((pushReg) => {
  });
});

@jakearchibald
Copy link
Contributor Author

@jungkees here's a crazy idea I haven't properly thought through… what if ServiceWorker-dependant APIs such as .push were on ServiceWorkerRegistration. So:

navigator.serviceWorker.ready.then(function(reg) {
  return reg.push.register();
});

Push & background sync are strongly linked to the ServiceWorker registration already. If you unregister SW, you're also unregistering push & sync.

@jakearchibald
Copy link
Contributor Author

@michael-nordman

I don't think this needs to be async for the current document, so a simple .registration attribute would be fine and I think a small improvement.

Is that true even if the page isn't using that registration for fetches? Eg: shift+reload, or a fresh registration before refreshing the page.

The .getRegistration(url) method would definitely need to be async. I think your intent is the url param to be a document url, not a scope. The name of the method or param doesn't make that obvious.

Yeah, the intent is document url. Although if we get rid of the * from scope (which I'm still keen on), then passing a scope in returns the correct registration. But yeah, I'll see if I can think of a better method name (suggestions welcome).

@jakearchibald
Copy link
Contributor Author

@coonsta:

Does this mean that a page can enumerate the registrations for the entire origin, or just registrations that could match the page's URL?

Entire origin

re: installing, waiting, active "updating live" I think you need to decide how many registration objects there can be for a given scope at one time. Do successive calls to register with the same scope create new registrations (and resolve to a different object) or update existing ones?

Definitely update, even if the script URL is different. The scope is the unique part of the registration. If you call .register and there's already a registration with that scope & script url, it's a noop. If there's a registration with that scope but a different url, it fetches the script & it becomes the installing worker for that registration, even if it's byte identical (because things like base urls are different). This should be covered in the algorithms.

@jakearchibald
Copy link
Contributor Author

Here's the usecases I have for the various properties:

.installing

  • postmessage to & fro to track install progress

.waiting

  • provide a "Upgrade" button on the page that, when clicked, postmessages into the waiting worker & calls clients.reloadAll()

.active

  • If there's an active worker but your document isn't controlled, you know it will be on the next refresh
  • All the usecases of .controller, but doing so from an out-of-scope page

.controller

  • postmessage to tell the controller to cache additional assets ("read later" button)
  • use postmessage to coordinate across all controlled windows
  • if controller is not-null, you know subresources will go through a serviceworker (I did this in trained-to-thrill)

getRegistration(url) would let "https://www.google.com/whatever" get the registration for "https://www.google.com/calendar" and tell it do add a particular event.

Basically, .controller is by far the most useful one, which is why I wanted to farm the others off into another object.

@michael-nordman
Copy link
Collaborator

I see, overwhelmingly pages don't have a need to look at .registration. In light of that, it makes more sense to me to not have a special case sync .registration attribute and instead rely on the more general purpose async .getRegistration(docUrl) method when needed.

@nikhilm
Copy link
Contributor

nikhilm commented Jul 11, 2014

I'm in favour of this change for the most part. More comments following
Some things:

  1. Not sure why scriptURL should be exposed on ServiceWorkerRegistration
  2. unregister() can be moved into the ServiceWorkerRegistration interface.

@michael-nordman a document's associated registration never changes, so onregistrationchange not required

@nikhilm
Copy link
Contributor

nikhilm commented Jul 11, 2014

Does this change allow us to get rid of states (parsed, installing...) and onstatechange? Or is that still useful? .controller is obviously always installed and activated. Same for .active. How much use case is there to know when installing or waiting have changed states?

@nikhilm
Copy link
Contributor

nikhilm commented Jul 11, 2014

I don't think allowing a page access to registrations via getRegistrations() not in it's scope is a good idea. Previously this was only allowed through register(), and the intent was you could postMessage with the worker. register() still allows doing that. On the other hand, allowing broad access is:

  1. There is no super useful use case for this right now. Gecko will not implement either this or getAll() (if we stick to the old interface) in the first implementation. IMHO there is too much surface area for very low returns, but I could be convinced otherwise.
  2. It is very easy for a page to attempt to message the active worker of every registration in its origin, requiring spawning several workers, a big deal on resource constrained devices, and even on not so constrained ones.

We should look long and hard at both the argument form of getRegistration() and getAllRegistrations() before adding them to the specification.

@michael-nordman
Copy link
Collaborator

@nikhilm, not sure thats true in the case of a brand new registration and existing pages that would be controlled by that upon reload. With the existing api, the existing pages would see a 'onupdatefound' event and become aware of the newly installing thing. I'm assuming window.serviceWorker.registration would be NULL in the precondition case.

@nikhilm
Copy link
Contributor

nikhilm commented Jul 11, 2014

Agreed on the suggested direction. The only concern was whether the concept, registration, would be useful to authors not adding unnecessary complexity. However, I cannot disagree that exposing this concept explains the actual model better and reflects underlying implementation better. (@nikhilm Is it true with gecko implementation too?)

Yes, but it doesn't really matter. i.e. its just a matter of doing some different typing, so I'd be happy with either :)

re: installing, waiting, active "updating live" I think you need to decide how many registration objects there can be for a given scope at one time. Do successive calls to register with the same scope create new registrations (and resolve to a different object) or update existing ones?

I think we should update existing ones. Once an entry was added to [[ScopeToRegistrationMap]], that registration object should be uniquely maintained until it gets deleted by [[Unregister]].

Agreed about [[ScopeToRegistrationMap]] entries remaining constant between register() and unregister(). It simplifies a lot of reasoning in the implementation.

@nikhilm
Copy link
Contributor

nikhilm commented Jul 11, 2014

@nikhilm, not sure thats true in the case of a brand new registration and existing pages that would be controlled by that upon reload. With the existing api, the existing pages would see a 'onupdatefound' event and become aware of the newly installing thing. I'm assuming window.serviceWorker.registration would be NULL in the precondition case.

I think .ready can be used for this case.

@dominiccooney
Copy link

Thanks for the commentary, particularly about use cases, and clarifications. This is useful.

In general what's being proposed on this thread is implementable in Blink. Given that cross-scope communication is possible haphazardly with the existing spec, formalizing that as a simple, explicit API seems like a good rationalization.

There seem to be some redundant properties now: ServiceWorker.scope vs ServiceWorkerRegistration.scope. Also, it is unclear to me how scriptURL is a property of a ServiceWorkerRegistration and not a ServiceWorker; which Service Worker's script URL does it refer to?

@jungkees
Copy link
Collaborator

ServiceWorker.scope vs ServiceWorkerRegistration.scope

Unless otherwise ServiceWorer.scope is a useful sugar for authors, I agree to drop this from ServiceWorker. Registration maps worker to scope, so it naturally belongs to registration.

Also, it is unclear to me how scriptURL is a property of a ServiceWorkerRegistration and not a ServiceWorker; which Service Worker's script URL does it refer to?

In the existing spec algorithms, registration.scriptURL holds the url of the currently installing script. That is, even before the instance of installing worker is created, the algorithm used this url to fetch the target worker script. But now we want to expose the registration object to JS, I don't think this property makes much sense to authors. We can drop this from ServiceWorkerRegistration and I'll revisit the algorithms as such.

@jungkees
Copy link
Collaborator

@jakearchibald

what if ServiceWorker-dependant APIs such as .push were on ServiceWorkerRegistration.

Great idea. Those APIs can define its API space within ServiceWorkerRegistration as for example (SysApps WG's Task Scheduler API):

partial interface ServiceWorkerRegistration {
  readonly attribute TaskScheduler taskScheduler;
};

partial interface ServiceWorkerGlobalScope {
  attribute EventHandler onalarm;
};

In the script, it can be used as long as there's a SW registration:

navigator.serviceWorker.ready.then(registration => {
  return registration.taskScheduler.add(Date.now() + (10 * 60000), {
    message: "It's been 10 minutes, your soup is ready!"
  });  
}).then(onTaskAdded, onError);

I'm not sure whether exposing getRegistration(url), which @nikhilm opposed to, would add some meaningful value to authors here.

/cc @jakearchibald @slightlyoff @sicking

@jakearchibald
Copy link
Contributor Author

@nikhilm

  1. Not sure why scriptURL should be exposed on ServiceWorkerRegistration

The registration has a script url that it uses for updates https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#update-algorithm. You can change the url or the registration by calling .register with the same scope but a different url. If the fetch/parse/install of the new worker fails, you won't have a worker in the registration with this new url, but updates should still use the new url.

  1. unregister() can be moved into the ServiceWorkerRegistration interface.

Yeah, that makes sense. That removes confusion around .unregister('/*') - should that remove all serviceworkers on the origin? Don't have that confusion if it's a method of the registration object itself.

@michael-nordman a document's associated registration never changes, so onregistrationchange not required

It can if the installing worker calls .replace() https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#installation-algorithm (step 13).

@jakearchibald
Copy link
Contributor Author

@nikhilm

I don't think allowing a page access to registrations via getRegistrations() not in it's scope is a good idea.

  1. There is no super useful use case for this right now. Gecko will not implement either this or getAll( (if we stick to the old interface) in the first implementation. IMHO there is too much surface area for very low returns, but I could be convinced otherwise.

I'm probably being naive, but I can't see how getRegistration() adds much extra complexity.

Input: docURL

  1. Let promise be a new promise.
  2. Let docURL be the result of running the URL parser on docURL with entry settings object's API base URL.
  3. If the origin of docURL does not match the document's origin, then reject promise with security error & abort these steps
  4. Set registration to the result of running [[MatchScope]] algorithm, or its equivalent, passing docURL as the argument.
  5. If registration is null, reject promise with not found error & abort these steps
  6. Resolve promise with registration

It doesn't use anything we don't use elsewhere, it's pretty much an extremely cut-down version of register. What's more, we're not offering anything you couldn't do in a completely crazy way with an iframe & contentWindow.navigator.serviceWorker.ready.

Use-cases:

  • Postmessage to /docs/ to ask it to cache a document offline, from outside of /docs/
  • If we move push registration to the SW registration object, this would allow / to register/unregister push for all of the apps on the origin that have their own serviceworker registration
  1. It is very easy for a page to attempt to message the active worker of every registration in its origin, requiring spawning several workers, a big deal on resource constrained devices, and even on not so constrained ones.

Yeah, but they can also register 1000 new scopes for an origin, which involves fetching 1000 scripts & spawning them. We should make sure we punish the origin for doing this, rather than slowing down the whole browser.

@jakearchibald
Copy link
Contributor Author

@jungkees

Unless otherwise ServiceWorer.scope is a useful sugar for authors, I agree to drop this from ServiceWorker. Registration maps worker to scope, so it naturally belongs to registration.

Agreed. scriptURL needs to remain part of ServiceWorkerRegistration and ServiceWorker, but scope should only be on the registration.

jakearchibald added a commit that referenced this issue Jul 11, 2014
…, removing .scope from ServiceWorker objects. Part of #365
@nikhilm
Copy link
Contributor

nikhilm commented Jul 11, 2014

@jungkees

Unless otherwise ServiceWorer.scope is a useful sugar for authors, I agree to drop this from ServiceWorker. Registration maps worker to scope, so it naturally belongs to registration.

Agreed. scriptURL needs to remain part of ServiceWorkerRegistration and ServiceWorker, but scope should only be on the registration.

@jakearchibald I don't think scriptURL needs to be exposed to authors on ServiceWorkerRegistration. There it is simply an intermediate needed by the algorithms.

@nikhilm
Copy link
Contributor

nikhilm commented Jul 11, 2014

I concede on #365 (comment) :)

@jungkees
Copy link
Collaborator

Sounds good :) @jakearchibald already changed the ts file. I'm having a vacation for a week until the 21st of JUL. I'll change the web spec accordingly next week.

@jakearchibald
Copy link
Contributor Author

Happy to drop scriptURL from the registration object, although it feels like we're hiding a bit of the registration information from the developer. I guess we can add it back later if needed. We'd certainly want to show it in devtools.

@jungkees
Copy link
Collaborator

Addressed c3c3d09.

@prudentbot
Copy link

@jungkees here's a crazy idea I haven't properly thought through… what if ServiceWorker-dependant APIs such as .push were on ServiceWorkerRegistration.
Push & background sync are strongly linked to the ServiceWorker registration already. If you unregister SW, you're also unregistering push & sync.

I'm not entirely convinced exposing push on top of ServiceWorkers is a good idea. If a developer is just trying to employ window-independent network listening, then having him/her use ServiceWorkers directly seems like unnecessary development overhead.

There are also some wonky constraints that ServiceWorkers implicitly impose in such a model. For example, the current Push spec only allows one registration per origin, however multiple ServiceWorkers are allowed per origin. This problem isn't necessarily unsolvable, but it is indicative of problems that services that wish to use ServiceWorkers in a similar way could have.

@jungkees
Copy link
Collaborator

I'm not entirely convinced exposing push on top of ServiceWorkers is a good idea. If a developer is just trying to employ window-independent network listening, then having him/her use ServiceWorkers directly seems like unnecessary development overhead.

I don't think I understood what "window-independent network listening" suggests. There's been no other mechanism in browser to fire/execute functional events like "push" without a browsing context before.

For example, the current Push spec only allows one registration per origin, however multiple ServiceWorkers are allowed per origin. This problem isn't necessarily unsolvable, but it is indicative of problems that services that wish to use ServiceWorkers in a similar way could have.

Defining API space in ServiceWorkerRegistration naturally means the unit of such services becomes a URL scope instead of a domain. So we'll have to suggest that change to Push spec. Setting up a URL scope for certain service hinges on developer's choice. If they want to set up only one push service for the entire domain, the domain ServiceWorker with default scope would be sufficient. Otherwise, they can set up multiple services within a domain. I think this gives flexibility to the developers indeed. For example, if a portal site "example.com" wants to enable different push services for "example.com/news" and "example.com/health", the developers will have a choice to register two separate services in differently-scoped Service Workers instead of putting all the service logic in a domain Service Worker.

@jakearchibald
Copy link
Contributor Author

@Ndvr

the current Push spec only allows one registration per origin

The spec hasn't been updated. At the moment it allows multiple registrations per origin, but it's supposed to be one registration per serviceworker. Multiple registrations per serviceworker don't make sense (make distinctions with data), and registrations without a serviceworker don't make sense (no where to fire the event).

Push and background sync already had a serviceworker dependency. Moving the APIs there makes this clear.

@prudentbot
Copy link

First of all, I wanted to introduce myself. I'm Nikhil's new intern, and I'm a bit new to these concepts, but I've been put in charge implementing the new Push API so this is all directly relevant to me. I plead for patience if I misunderstood something.

I don't think I understood what "window-independent network listening" suggests. There's been no other mechanism in browser to fire/execute functional events like "push" without a browsing context before.

What I mean by that is that ServiceWorkers seems to me to have have its feet in two pools (for this situation in particular). You've got developers that are using them to build offline applications, and developers that are employing them to listen to server events without directly requiring the page Javascript to be executed.

The thing is that there's not really all that much overlap between these two use cases. Why would developers that just want window-independent listening care about all the awesome things ServiceWorkers can do?

Now, I understand that the "window independence", as I'm calling it here, is sort of a defining part of ServiceWorkers, so I can see why placing services which require window independence on ServiceWorkers is an elegant solution. However, I'm not sure it's better than just exposing a dump API to developers and implementing ServiceWorkers in manner that is transparent to the developer.

The spec hasn't been updated. At the moment it allows multiple registrations per origin, but it's supposed to be one registration per serviceworker. Multiple registrations per serviceworker don't make sense (make distinctions with data), and registrations without a serviceworker don't make sense (no where to fire the event).

The spec hasn't been updated, and that is certainly irresponsible, but we've been developing under the one registration per origin assumption, as well as implementing a new browser-facing API that is exposed directly on navigator. Of course I'm willing to move along the track @jakearchibald suggested, I'd just like to be convinced that it's necessary first.

@jakearchibald
Copy link
Contributor Author

Oh, I thought it was agreed that push was linked to ServiceWorker. Without that link, which ServiceWorker gets the push event, or does it go elsewhere?

@annevk
Copy link
Member

annevk commented Jul 30, 2014

@Ndvr that assumption is wrong. Any reason why that assumption was made? You can have multiple service workers per origin...

@johnmellor
Copy link

Hi @Ndvr, look forward to working with you on Push!

What I mean by that is that ServiceWorkers seems to me to have have its feet in two pools (for this situation in particular). You've got developers that are using them to build offline applications, and developers that are employing them to listen to server events without directly requiring the page Javascript to be executed.

SWs are often introduced as being about offline, as that was the original motivation (and is included in the same spec). But it's more subtle than that.

The core functionality of SWs is purely to be able to receive events from the browser at any time, whether or not the web app is currently open in one or more tabs. On top of this foundation, several features are being built:

  • onfetch event: Proxy resource requests while offline
  • onpush event: Receive push messages sent to the web app
  • onsync event: Do occasional background work like syncing data

These features should be treated as independent (though they happen to work well in combination), but they all rely on the ability of SWs to receive events without needing a tab to be open. This variety of use cases is why Service Workers were renamed from their old name "Navigation Controllers".

we've been developing under the one registration per origin assumption

Oh, we've always been implementing one registration per SW; I thought Nikhil/Doug were on board with that?

as well as implementing a new browser-facing API that is exposed directly on navigator

Our prototype also did this, but we'd like to change it to be on ServiceWorkerRegistration as discussed on public-webapps. See also w3c/push-api#2 and WICG/background-sync#6.

@prudentbot
Copy link

Okay, I'd like to apologize, I was making a silly mistake.

############# Disclaimer: Irrelevant ################

Coming at this from the push side of things, I was thinking it was ultimately unnecessary to expose ServiceWorkers to developers that were implementing push. All that is happening from a push developer's perspective is creating a registration and writing a request handler for when messages arrive, and that isn't overly complicated. In my mind, we fundamentally had this functionality for receiving events from the browser at any time, and we might as well make utilizing this as uncomplicated as possible for web devs. So I was thinking we could have a ServiceWorker in the basement, so to speak, and just interact with it via a navigator.push. navigator.push.onMessage or whatever would basically just define the onpush behavior for the ServiceWorker (it seemed weird that push.onMessage may not have access to the DOM if the page context isn't around, but if I understand correctly the web dev would have had to deal with that anyway within worker.js). Push.register would be some business logic that would essentially translate the registration request into a service worker registration, which could allow us to keep everything kosher with regards to origins. Also, it seems like we could potentially do this in a way that would avoid having to reload the page before we started receiving push events.

I'm not sure if the engineering is sound on that, but it doesn't really matter. I did some thinking and even if it does simplify push in particular it seems a bit unclean to hide the ServiceWorker dependency when there are so many non-push services that could potentially employ ServiceWorkers in a similar way.

################ End Irrelevance ################

Bottom line is that I'm sorry for wasting everyone's time, and in light of my new understanding @jakearchibald 's proposal seems reasonable and elegant.

@johnmellor
Copy link

navigator.push.onMessage or whatever would basically just define the onpush behavior for the ServiceWorker

Ah, that wouldn't have worked, because event handlers are JavaScript functions, which get destroyed when the execution context (the page) is closed. The reason it works in a Service Worker, is that -- by convention -- the SW will add event listeners for the same events each time it is run; so e.g. when the browser receives a push message and the SW isn't yet running, it will first run the SW script, which should set an event listener for the push event, then and only then the browser will be able to dispatch a push event to the SW (and if the SW misbehaves and fails to register a push event, then I guess the browser has to discard the event, or log an error to devtools).

it seems like we could potentially do this in a way that would avoid having to reload the page before we started receiving push events

We already can! Note that the definition of ready will return an active service worker. So the first time you visit a web app (no pre-existing SW), if it registers a SW, it will go through the install and activate lifecycle events, then it will become the active SW. At this stage it is not controlling the current page, but it is still the active SW for this URL (meaning if you opened this URL in a new tab right now it would control that). As soon as there is such an active SW, the ready Promise will be resolved with that SW registration, and the page will be able to register for Push, all without requiring any reloads.

@prudentbot
Copy link

Okay, both of those explanations made sense. Thanks @johnmellor !

@KenjiBaheux
Copy link
Collaborator

Blink work tracked in crbug.com/396400 (SWRegistration: done), crbug.com/399533, crbug.com/404951 ...

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

No branches or pull requests

9 participants