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

Allowing multiple scopes (& should scopes be primary key?) #566

Open
jakearchibald opened this issue Nov 24, 2014 · 12 comments
Open

Allowing multiple scopes (& should scopes be primary key?) #566

jakearchibald opened this issue Nov 24, 2014 · 12 comments
Milestone

Comments

@jakearchibald
Copy link
Contributor

This is boiling down the discussions from #445 & meetings into a few options.

The goals

  • Cater for sites that aren't contained within a single path, such as //docs.google.com/spreadsheets/d/docid and //docs.google.com/a/mozilla.com/spreadsheets/d/docid
  • Allow the scope to be changed (without losing push/sync/geo registrations)
  • Optional: allow ServiceWorkers to be created that don't care about fetch events

Proposal 1

navigator.serviceWorker.register(scriptURL, {
  // name is the ID of the registration, rather than scope, as it is now
  // name is optional, defaults to empty string which is a valid ID
  name: "my-app-name"
  // Scope is optional, can be null, a string, or an array of strings
  // Default is null
  // null means the SW will not receive any fetch events
  scope: "/spreadsheets/"
}).then(function(reg) {
  reg.name === "my-app-name";
  // Getting the scope
  reg.scopeList.get().then(arrayOfScopes => {});
  // Setting the scope
  reg.scopeList.set(arrayOrString).then(() => {});
  // Adding
  reg.scopeList.add("/a/mozilla.com/spreadsheets/").then(() => {});
  // Removing
  reg.scopeList.remove("/a/mozilla.com/spreadsheets/").then(() => {});
  // scope-change operations reject if another SW controls that scope
});

// Getting the above registration
navigator.serviceWorker.getRegistration("my-app-name").then(reg => {});
// Getting the default no-name registration
navigator.serviceWorker.getRegistration().then(reg => {});
// Get the registration that would be used for a navigation to url
navigator.serviceWorker.getRegistrationControlling(url).then(reg => {});
  • Fetch events are opt in
  • reg.name never changes & is the "primary key" of the registration
  • Moving scopes is easy
  • Incompatible with current API (although no browser has the current API in stable, Chrome is wanting to ship at the start of 2015)

Proposal 2

As proposal 1, except scope defaults to /. Scope can still be set to null or an empty array to opt out of fetch events.

  • Fetch events are opt out
  • reg.name never changes & is the "primary key" of the registration
  • Moving scopes is easy
  • Still incompatible with current API, but less so

Proposal 3

navigator.serviceWorker.register(scriptURL, {
  // Scope is required & is the ID of the SW
  // Can be  a string, or an array of strings
  // Default is "/"
  // An empty array means the SW will not receive any fetch events
  scope: "/spreadsheets/"
}).then(function(reg) {
  reg.scopeList; // same as proposal 1
});

// Get the registration that would be used for a navigation to url.
// If url is ommited, gets the registration for the current document/worker url
navigator.serviceWorker.getRegistration(url).then(reg => {});
// The worker without scopes can only be found via:
navigator.serviceWorker.getRegistrations().then(regs => {});
// Unless getRegistration does something special with an empty string
  • Fetch events are opt-out
  • Only one ServiceWorker can opt out of fetch events (per origin)
  • The combination of scopes is the "primary key" of the registration (similar to the current API)
  • Moving scopes is easy
  • Smaller API surface
  • Compatible with the current API, aside from potentially losing reg.scope (no longer appropriate for sync access, I think)

Summary

  • Proposal 1: opt-in fetch events, new name property for identifying registrations
  • Proposal 2: as proposal 1, but fetch is opt-out
  • Proposal 3: closest to current spec, retaining scope as the id

All proposals allow changing scope, multiple scopes, and no scopes.

@annevk annevk mentioned this issue Nov 24, 2014
@jakearchibald
Copy link
Contributor Author

Meeting summary:

  • The backwards compatible bits of proposal 3 feels fine
  • Disagreement over null scopes, it's something we can add later if we see people using scopes of /this/path/totally/doesnt/exist
  • Allowing pages to change scope is a bit dangerous, as you end up with likely-cached content doing the work, meaning you end up with a two-step update. Decided: only let a ServiceWorker change scope during the activate event
  • Add .setScope() to the ActivateEvent object to accomplish this (still needs to be async as it rejects if another registration uses this scope)
  • This model lets us keep reg.scope as a sync getter, it's no worse than reg.installing etc
  • To decide: If a registration changes to have multiple scopes ['/a/', '/b/'], and a page calls .register(scriptURL, {scope: '/a/'}) does it reject or resolve with the registration with scope ['/a/', '/b/']?
  • //docs.google.com/spreadsheets/d/docid and //docs.google.com/a/mozilla.com/spreadsheets/d/docid are actually seperate apps with seperate binaries etc, as is Google's settings page, so we're still lacking in examples of one app with disjointed paths
  • //github.com/settings/ & //github.com/watching/ may be an example, as may Evernote - @sicking can you look into Evernote?

@jakearchibald
Copy link
Contributor Author

@slightlyoff are you agreed on having a way to move scopes during activation? Am I right in thinking you want further evidence on the need for multiple scopes?

@sandropaganotti-zz
Copy link

@jakearchibald changing scope during activation would allow also to set the scope to a parent path or it will still be forbidden?

@slightlyoff
Copy link
Contributor

@jakearchibald :

Yes, agreed to API for moving scopes during activation.

Also correct that we don't yet have a compelling example that demands multiple scopes and that examples along those lines would help convince me of the need.

@wanderview
Copy link
Member

Optional: allow ServiceWorkers to be created that don't care about fetch events

It seems to me this goal can already be mostly met by the UA optimizing around the onfetch handler not being set. If there are no fetch event handlers then the UA can skip network interception completely. This state would need to be persisted when the SW shutdown.

@jakearchibald
Copy link
Contributor Author

If the SW has terminated, and an event needs to fire within the SW, the SW should only spin up if an event listener of that type was added last time the SW started.

Does that work? @annevk, how do you feel about that?

@jakearchibald
Copy link
Contributor Author

This would break cases where listener adding wasn't consistent, such as adding depending on Math.random() or Date.now(), but I'm cool with that.

@wanderview
Copy link
Member

This would break cases where listener adding wasn't consistent, such as adding depending on Math.random() or Date.now(), but I'm cool with that.

Yea, I think this falls in the same bucket as async importScripts(), saving state on the global, etc. Its not going to work consistently and should be discouraged in SW scripts.

@annevk
Copy link
Member

annevk commented Mar 26, 2015

I think an explicit switch would be way better than anything that requires observing whether or not there is an event listener added.

@KenjiBaheux
Copy link
Collaborator

I feel that this issue and others like issue #1026 or issue #1272 and the long-thought-after static routes are similar in that they are about refining if/how a Service Worker gets involved.

I know that there is strong interest on those issues, including from Google and Facebook. I'm wondering if we can identify a path forward. So, here is my modest contribution to the discussion.

Currently, we can only define that a Service Worker gets involved for a navigation to anything below its scope (prefix match) on a given origin.

I've heard about the following use cases:

  1. A way to avoid one's SW oversteps on other products, in particular when the scope doesn't end with a slash, e.g. /foo matching against /football. This creates an on-going burden on other teams as they have to register a no-op SW on their more specific scope. This is issue Scope matching algorithm breaks sites that don't end in a slash #1272.
  2. A way to skip the SW for certain resources and/or paths. This is issue Consider mechanisms to bypass the service worker for things that we know won't be in it's cache #1026.
  3. A way to get an existing SW also involved on navigation to related scopes, assuming no conflicts. For instance, get a SW registered to example.com with scope /productA to also control /settings. This particular issue.
  4. Future-looking: the ability to define static routes. This was mentioned in issue Making a concurrent request for navigations #920 and is a generalization of use case nb 2.

Use case 1
This use case feels different than the other use cases because it only refines one thing, the (primary) scope. The primary scope also happens to be essential to the registration step. So, it seems natural to have this on the register call, via a new option (e.g. strict match vs. prefix). I believe that this was one of the various proposals on the relevant issue.

Use cases 2, 3 and 4
On the other hand, use cases 2, 3 and 4 can consist of multiple refinements which are not essential to the registration step and are most likely not critical for the developer (e.g. having a secondary scope fail because a different team has already registered a primary-scoped SW will likely be considered as WAI by the developer).

It seems:

  • logical to do these in the same place as it's ultimately about refining if/how your SW gets involved.
  • natural to use the install event handler to refine a SW's behavior on navigations or sub-resources fetches.
  • ergonomic to have methods that would let developers proceed with one thing at a time. It would let them know which refinement failed and allow them to decide whether to make the install fail or proceed.

For comparison, if we were to do these refinements as part of the register call, then it seems harder for a developer to reason about why the call failed, or separate the critical from the optional, etc.

Now, I'm not saying that the methods have to be identical - they accept different things after all - but having a similar feel should be possible and welcomed. e.g. re-use the strict match vs. prefix option, parallelism of method names (addScope, addRoute), etc.

Also, while addressing use case Nb 4 would also cover use case Nb 2, I think we should be able to proceed with an MVP for Nb 2 while keeping in mind the generic use cases in Nb 4.

Handling scope conflicts
Tentative strawman:

  • Secondary scopes must follow the scope rules (e.g. max default scope or max allowed via the Service-Worker-Allowed header)
  • Primary scopes take precedence over secondary scopes.
  • Secondary scopes are first come, first serve.

Does this sound reasonable?

@annevk
Copy link
Member

annevk commented Apr 27, 2018

Presumably you can have multiple secondary scopes and only one primary scope per service worker?

@KenjiBaheux
Copy link
Collaborator

Yes. One primary, multiple secondaries.

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

No branches or pull requests

6 participants