Allow the NavigationController to manage resources on first load #73

Closed
jansepar opened this Issue Aug 14, 2013 · 108 comments

Projects

None yet

Copied from my post on the discussion on chromium: https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/Du9lhfui1Mo

Just found this, and it seems extremely interesting and has lots of potential to be a very useful addition to browsers. I was disappointed to read this bit though:

"The first time http://videos.example.com/index.html is loaded, all the resources it requests will come from the network. That means that even if the browser runs the install snippet for ctrl.js, fetches it, and finishes installing it before it begins fetching logo.png, the new controller script won't be consulted about loading logo.png. This is down to the first rule of Navigation Controllers"

I think there is a lot of value that can come from giving developers the power to have full control over resource loading, even on the first load. For example, having the ability to swap image URLs before they are kicked off by the preloader would be a big win for responsive images. I am the author of the Capturing API (https://hacks.mozilla.org/2013/03/capturing-improving-performance-of-the-adaptive-web/) which provides this exact functionality in a non-optimal way. In order to control resource loading with Capturing, we must first buffer the entire document before being able to manipulate resources, which is a bummer, but it's ability to control resources on the page is very, very useful. If the Navigation Controller worked on first page load, the need for Capturing would be eliminated.

It does not seem like total control of resource loading is the goal of the Navigation Controller, but the API is very close to being able to provide exactly that without much change at all. I would love to have a conversation about whether or not adding this functionality is feasible!

Collaborator

Sounds like you're looking for a means to block page load until the 'controller' is up and running on first load.

Some of us had talked about an option like that in the registration process at some point. I think it was dropped mostly as a matter of reducing the scope for the sake of clarity more than a fundamental problem with it. At the time of those discussion we had envisioned a header based registration mechanism such that the body of the initial page itself was re-requested thru the controller once it was up and running.

Collaborator
alecf commented Aug 14, 2013

One option is something like this, which is slightly underspecified right now:

navigator.registerController("/*", "controller.js")
    .then(function(controller) { 
      if (...controller came online for the first time) { 
          // maybe warn the user first
          document.reload() 
    });

@alecf with that mechanism, wouldn't that risk flashing some of the content that gets loaded before the controller is done loading? The document reload could be avoided if the script was blocking. Or if somehow the browser was aware that a controller was going to load, and it could block the load of the next resource until the controller is finished.

Collaborator

The way alec pointed out is pretty close approximation and it would result in the main page load also being routed thru the controller for the reload.

Being browser developers, we're understandably reluctant to introduce things that involve "blocking page loads" :)

Member

We're waging a war in the webperf community to rid "blocking resources" whenever and wherever possible... I would upgrade "reluctant to introduce blocking resources" to something much, much stronger. First, we're stuck on a controller download, then on parsing and eval, and then on potentially slow routing calls for each request -- ouch x3.

Copied my comment from discussion on https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/Du9lhfui1Mo

So, while there are performance implications of giving developers full control of resource loading, I really think it's the best solution going forward. One thing we all have to stop and realize is that when developers want to control resources, they manage to do it - just in non-optimal ways that also have big performance implications. One solution developers have and use is proxying pages and making modifications to resources before hitting the client, which can have big security implications, and does not have the advantage of making decisions based on device conditions. Another option developers have and use is writing alternates to src and href (such as data-src and data-href) and loading them after the DOM content is loaded, thus needing to wait for rendering to complete before loading resources. Another option is Mobify's Capturing API, which also blocks page rendering.

So when thinking about giving the Navigation Controller the power to control resources on first load, its not a matter of blocking vs no blocking, its a matter of blocking vs the 3 options previously listed.

Hi guys, I'm a colleague of Shawn's at Mobify, and I thought I'd butt in here because this API is really intriguing to me:

Not providing an API that allows full control will just lead people to using the reload() workaround posted above, which is clearly at least as bad as blocking page rendering on the controller download, if not worse, because after reload a different set of resources could potentially be downloaded.

This API is already potentially quite "performance dangerous" (not to mention "basic functionality dangerous") in the sense of providing a very deep hook into resource scheduling, far in excess of what's previously been available, but the most likely application is in fact performance improvement, e.g. the Application specific caching rules as presented in the groups thread linked above, or choosing device appropriate resources for various devices, and making those decisions (and starting those downloads) as early as possible.

I haven't dug too deeply into the API itself yet, but would it be hypothetically possible to throw a "bootstrap" controller into a page inline to overcome the "additional blocking resource" objection Ilya brought up?

jsdnxx commented Aug 15, 2013

Would it be a terrible idea to have some sort of {prefetch:false} flag on a per-scope? This would allow prefetch to be the default (and more performant) action, but allow developers to override it in scenarios where more explicit control is necessary or desired.

Collaborator
alecf commented Aug 15, 2013

@noahadams - perhaps flip this around - I'm not sure reload is "at least as bad as blocking" - from my perspective, blocking is the worst possible option, because it effectively prevents ALL resource loads and introduces complicated behavior. Since you can virtually emulate the blocking behavior with interstitial content while the page loads, I can't see a good reason to introduce blocking.

From mobify's own blog about 'm.' sites and redirects:

With 3G latencies, you're looking at up to 2 seconds for the redirect to an m-dot to complete. The visitor to your mobile site will count this against you, which negatively affects their impression of your site and reduces the likelihood of them returning. With delays as small as 500 milliseconds resulting in a 1.9% conversion penalty, performance is money.

This situation is worse, because you actually have to start reading and parsing html and multiple resources before the pageload can continue.

A few examples...

What happens here:

<img src="foo.png">
<script> navigator.registerController("/*", "controller.js", {blocking: true})</script>
<img src="bar.png">

Do we block loading of "bar.png"? is foo.png visible on the screen?

what about this:

<img src="foo.png">
<script> navigator.registerController("/*", "controller.js", {blocking: true})</script>
<script src="http://somecdn.com/jquery.min.js">

Is that script loaded before or after controller.js? when is it evaluated?

What if it takes 2 seconds to get controller.js?

To me these examples demonstrate that there is no way any web platform API is will ever support a method that blocks the main thread, especially one dependent on a network request. document.write() was bad enough, this is far worse. Further, a properly designed website could immediately put up an interstitial message, "Loading resources.." or what have you, if your site truly is non-functional without the controller.

@alecf the Navigation Controller doesn't have to block the rendering thread in all cases, it just has to block resources from loading. Say for example, you had a document like this:

<html>
<head>
<script> navigator.registerController("/*", "controller.js", {blockResourcesLoading: true})</script>
</head>
<body>
<img src="a.png">
<h1>Foo</h1>
<img src="b.png">
<h1>Bar</h1>
</body>
</html>

I would imagine in this case, Foo and Bar would render regardless of whether or not the controller was finished, and only the images would be delayed from loading until the controller was finished downloading. When the controller is finished loading and we have the instructions, the images could then start downloading.

Now, if you had an external script tag in the head placed after the controller, like this...

<html>
<head>
<script> navigator.registerController("/*", "controller.js", {blockResourcesLoading: true})</script>
<script src="jquery.js"></script>
</head>
<body>
<img src="foo.png">
<h1>Foo</h1>
<img src="bar.png">
<h1>Bar</h1>
</body>
</html>

...then yes, I would envision that the main rendering thread would be blocked, because loading jquery would be delayed waiting for the controller to finish loading, and well, external scripts block rendering. But scripts in the head block rendering anyways - and we all know the best practice is to throw scripts at the end of body. Therefore if developers follow that best practices, there would be no blocking of the main rendering thread even if the Navigation Controller behaved as I'm suggesting. The real performance loss here is that the preparser/preloader would be delayed until the controller is finished loading.

As for "what if it takes 2 seconds to download controller.js", based on the spec, it seems as though the controller wouldn't get large enough to take that long to download... Of course, its possible.

Once again, I just want to emphasis that in order to solve the responsive image problem, people already are blocking resources - just in different ways. Some are using proxies to rewrite resources, some are changing src attributes and loading images at the end of rendering - neither of these are optimal. Aside from giving users full control over resource loading, I can't think of a better alternative to solve the responsive image problem.

Member

Once you have a hammer, everything looks like a nail.

We don't need Navigation Controller to solve the responsive images problem. The responsive images problem needs to be solved via appropriate API's - srcset, picture, client-hints, etc. The argument that NC "doesn't have to" block rendering is not practical: most every page out there (unfortunately) has blocking CSS and JavaScript, so the end-result will be that we block ourselves not only from rendering, but also from fetching those resources ahead of time. In Chrome, just the lookahead parser gives us ~20% improvement [1]. Further optimizations with aggressive pre-connects, pre-fetch, etc, will help us hide more of the mobile latency.

[1] https://plus.google.com/+IlyaGrigorik/posts/8AwRUE7wqAE

Also, since we've already had a lengthy discussion on this before. As a reference:
https://plus.google.com/117340630485537362092/posts/iv3iPnK3M1b
https://plus.google.com/+IlyaGrigorik/posts/S6j45VxNESB (more discussions here)

I completely understand your (Mobify) motivation to have NC controller override all browser behavior -- you've built a business in rewriting poorly implemented sites into something more mobile friendly.. But let's face it, the actual answer is: you shouldn't need a proxy layer here, the site should be rebuilt to begin with. Adding more proxies can make this type of work easier, but it won't give us the performance we want (yes, it means the laggards have to manually update their sites).

tl;dr: let's keep responsive images out of this discussion.

First I just want say that I hope it's clear that I really appreciate the fact that we are all trying to come up with great ideas to benefit the web, and I think that it's pretty awesome that we can do it collaborative and open forum like this :)

I think if the overall goal is to do everything that we can to improve the performance of the web, then I don't think we should be limited to hoping that laggards will manually update their sites. Automated tools are a very scalable way of achieving the goal of making the web faster. Google's own Pagespeed Service is a great example of this - it's not an optimal solution since pages must be routed and proxied through Googles servers, but it can definitely significantly improve the performance of most websites. I liked something you said in one of our earlier discussions on G+:

"My only knit-pick is the "we will all benefit from another demonstrably effective technique to consider". If we qualify that with a bunch of caveats, like "on some sites and in some cases, your mileage will vary, and still slower than what the platform could deliver, assuming it implemented the right features".. then we're good! That's all. :-)"

Even if we couldn't figure out a way to give developers the ability to have full control of resource loading without incurring a penalty, I still think it's a worthwhile feature that could be very useful to create automated tools to help speedup the web without needing to educate every single developer on the rules of performance. Then like you said, as long as we indicate that "your mileage will vary, and (your site is) still slower than what the platform could deliver", and as long as we can slowly educate them afterwards on how to take advantage of the platform, then we are good :)

And one note for responsive images: I have a few gripes about picture and srcset, but I won't list them here.
client-hints seem very promising, but I have some issues with it that are more philosophical.

@alecf I suppose you're right about the reload() workaround approach to blocking behaviour being at least less complicated (though I have my own UX gripes about loading interstitials in pages and in browsers, I won't raise them here).

My one concern about using it would be the case of a stateful transition between origins (that is to say, a cross-domain POST), though I'll admit that that's an uncommon edge case.

I think there's an argument to be made that a blocking version of this would have blocking semantics similar to a blocking <script src="..."> tag, that is to say you expect parsing to stop until it has finished being evaluated and to see its side effects after it has loaded. A sane interaction with the pre-load scanner is another issue.

What about the potential for bootstrapping a controller inline with enough logic to "correctly" load the current page and using the "upgrade dance" later to install something more full featured?

Collaborator
alecf commented Aug 16, 2013

I think there's certainly something interesting in notion of inline controllers for bootstrap... it sounds like we should file a new issue for that suggestion. I'd be interested in hearing this thought out in particular (file a new issue, discuss these things there...)

  1. what if two new tabs both open around the same time - and both have inline controllers. Can one page affect another?
  2. What if you have an inline controller, but also have a running controller, who wins?
  3. Is the inline controller persistent in any way? If I load another page that doesn't refer to a controller, is it affected by the inline controller?
Member

@jansepar we may be getting off topic here, but we shouldn't be putting in large new features which will guarantee a significantly degraded performance -- blocking all resource downloads is a big red flag, and then there is still the question of overhead per lookup. Besides, you basically do this already with your implementation, so it's not clear what you would win here.

@igrigorik I think the potential for an inline controller could be a good compromise that gives resource control on first load without degrading performance! Looking forward to seeing what comes out of that discussion which should be opening in a separate ticket. In regards to my implementation vs doing it with NC - there would definitely be some big performance wins of controlling resources via an API (NC), rather than capturing the entire document.

Member

@alecf @jansepar is there such open issue I can track? Can't seem to find anything..

Also, #54 looks to be related.

@igrigorik @noahadams is planning on creating an issue for being able to bootstrap the controller inline.

Contributor

I think some applications may want to have some kind of always-on service worker. I see a way to allow that without hurting the page performance too much: via an http header.

Service-Worker: /service-worker.js

This enables no-performance-loss installation:

  • (a) you don't need to parse the page to find out you need a service worker and
  • (b) if the service worker is sufficiently small you can use an HTTP/2.0 push to deliver the file to the browser directly; avoiding any RTT loss.
  • (c) http headers are not possibly subject to XSS attacks
Collaborator
alecf commented Sep 30, 2013

The header is an interesting idea, but it's always on once you register it.. in fact since there is no pattern here, I don't see a way to register it in the general case.. you'd at least need to change the header to

Service-Worker: /service-worker.js, /*

But I'm still not convinced that this helps enough to justify a whole new header.

Just to be clear: the usecase that is covered by this is one where the user loads a page, and then goes "offline" before that page is reloaded.. and you don't want to then reload offline, right? All the other ways of using this (responsive images on first load, etc) are beyond the scope of Service Worker design, even if people try to use Service Workers to solve them.

Member

@FremyCompany "this enables no-performance-loss installation" is not true. Stuffing the controller reference into an HTTP header may speed things up just a tad - by definition, headers will arrive first, allowing the browser to start fetching the controller - but it still does not address the problem of having to block dispatch of all other resources until the controller is loaded.

@alecf agreed, don't think the header adds much here.

Contributor

@igrigorik The advantage of headers is that you don't have to wait to parse the page, and also that the header is only sent once over HTTP 2.0 because of header compression. You don't pay the cost of inlining multiple times.

Regarding the blocking resource issue, this is a developer issue. If the developer need something it will achieve it anyway; for example by putting in an HTML comment all the HTML and waiting for the ServiceProvider to be loaded to reload the page; then extract the HTML from the comment on DOMContentReady. That will do the same thing, only slower.

Also, do not forget that we are not forced to apply the service worker to all URLs; we ca restrict to some element only which may still leave the page usable in the mean time.

Member

The advantage of headers is that you don't have to wait to parse the page, and also that the header is only sent once over HTTP 2.0 because of header compression. You don't pay the cost of inlining multiple times.

Yep, that is true.

Regarding the blocking resource issue, this is a developer issue. If the developer need something it will achieve it anyway; for example by putting in an HTML comment all the HTML and waiting for the ServiceProvider to be loaded to reload the page; then extract the HTML from the comment on DOMContentReady. That will do the same thing, only slower.

Everything is a developer issue if you get the API wrong. Perhaps the header is a reasonable solution, but this point alone is not sufficient as an argument for it.

Also, do not forget that we are not forced to apply the service worker to all URLs; we ca restrict to some element only which may still leave the page usable in the mean time.

That's true, but practically speaking, if you actually want to take your app offline, that's not the case, is it? As opposed to just using NavController to intercept a few different requests and rewrite them.. As such, I would fully expect most people to just claim "/*".

Contributor

I think you are right about /* but to he honest I'm still hoping that some "critical" resources can be put into an improved appcache instead, allowing those resources to be kept offline longer and bypass the service worker.

The amount of such resources, being limited and rarely changed, should be sufficiently low to be managed by hand.

That's the hope at least...

piranna commented Oct 3, 2013

I think you are right about /* but to he honest I'm still hoping that
some "critical" resources can be put into an improved appcache instead,
allowing those resources to be kept offline longer and bypass the service
worker.

Maybe AppCache and ServiceWorker cache could be combined? It's clear that
regarding resource fetching and caching there's some overlapping...

Collaborator
alecf commented Oct 3, 2013

There is absolutely no way we're combining AppCache and ServiceWorker - if anything I expect that using them together will result in several developers feeling so bad about themselves for trying that they give up on web development entirely, and write native apps as a penance for their sins.

I think we need to get back to the issue at hand which is the attempt to "go offline" during the initial load of the document, the first time its ever seen by the browser. This is only the very first time - registration is persistent across future pageloads and even browser restarts!

We're jumping through hoops to avoid this:

navigator.registerServiceWorker("/*", "service-worker.js").then(function() { window.reload(); })

or alternatively

if (!navigator.serviceWorker) // no service worker registered yet
    window.location = "/installer";

Or something similar. I just can't see introducing anything that would block all resources from loading the first time a user visits a page. The browser will just sit there with a blank white page spinning/progressing until the service worker is downloaded and started. A developer who did that would essentially be saying "I want my web page to suck for 10-30 seconds for all first time visitors" - if your site is really that heavily dependent on the service worker, you WANT some kind of "installer" or progress meter to give feedback to your users, so they don't just hit the "back" button and never visit your site again. (like the god-awful but unfortunately necessary progress bar that gmail has)

piranna commented Oct 3, 2013

There is absolutely no way we're combining AppCache and ServiceWorker - if
anything I expect that using them together will result in several
developers feeling so bad about themselves for trying that they give up on
web development entirely, and write native apps as a penance for their sins.

Well, maybe it's because that I'm mainly a systems programmer (I used to
program in OpenGL and also wrote my own kernel...) and now I'm a Javascript
and networks programmer just by serendipity :-)

I think we need to get back to the issue at hand which is the attempt to

"go offline" during the initial load of the document, the first time its
ever seen by the browser. This is only the very first time -
registration is persistent across future pageloads and even browser
restarts!

We're jumping through hoops to avoid this:

navigator.registerServiceWorker("/*", "service-worker.js").then(function() { window.reload(); })

In some way I asked before about combining ServiceWorker and AppCache and
in a previous message with the browser system cache due to this thing. If
we have already downloaded some resources and they are available in
AppCache or browser system cache, why it's needed to be reloaded the page
so the ServiceWorker can be aware of it when maybe it would be enought
internally with just setting a "ServiceWorker flag" or maybe better just do
a hard link of the resource from the AppCache or browser system cache to
the ServiceWorker cache and start to manage it from there? This will fix
the problem about needing to do the reload. Or if you don't like it, since
a web page knows what are the resources it has downloaded (you just need to
go to the Chrome Inspector > Network tab to see them), why not just re-do
the fetch of the already downloaded files? This would also prevent to do a
full page reload, and combined with the previous sugestion (link on the
ServiceWorker cache the already downloaded files), if the service worker
doesn't need to do anything with the files or fake them they will not be
needed to be downloaded at all.

"Si quieres viajar alrededor del mundo y ser invitado a hablar en un monton
de sitios diferentes, simplemente escribe un sistema operativo Unix."
– Linus Tordvals, creador del sistema operativo Linux

Collaborator
alecf commented Oct 3, 2013

wait, think about what you're really asking though:

If we have already downloaded some resources and they are available in
AppCache or browser system cache, why it's needed to be reloaded the page
so the ServiceWorker can be aware of it

Putting aside appcache for a moment: if they are available in the system cache and are fresh, then you don't need a service worker present to be aware of them. If the service worker is registered and requests its cache be populated, then that mechanism is really a function of the browser implementation of the SW cache - if the implementation is written such that it can just refer to the existing, fresh data in the system cache from the SW cache implementation, then it won't have to re-download those resources when the SW is instantiated.

I don't really see what this has to do with having SW loaded in the first invocation of the page - it sounds like you're more concerned about the transition from a non-SW-controlled page to a SW-controlled page, but trying to solve it by avoiding non-SW pages altogether.

piranna commented Oct 3, 2013

wait, think about what you're really asking though:

If we have already downloaded some resources and they are available in
AppCache or browser system cache, why it's needed to be reloaded the page
so the ServiceWorker can be aware of it

Putting aside appcache for a moment: if they are available in the system
cache and are fresh, then you don't need a service worker present to be
aware of them. If the service worker is registered and requests its cache
be populated, then that mechanism is really a function of the browser
implementation of the SW cache - if the implementation is written such that
it can just refer to the existing, fresh data in the system cache from the
SW cache implementation, then it won't have to re-download those resources
when the SW is instantiated.

Ok, just what I was asking for :-)

I don't really see what this has to do with having SW loaded in the first
invocation of the page - it sounds like you're more concerned about the
transition from a non-SW-controlled page to a SW-controlled page, but
trying to solve it by avoiding non-SW pages altogether.

No, I'm concerned about the fact to reload the page so the SW is aware of
all the content of the page, also downloaded one. Since first time I read
about SW (just last week, maybe two weeks ago) I believed that since
install the SW would be available, maybe leading to half-state pages, but I
though being aware of it would be a good idea, for example loading an
AppCache that install the SW and all the content is proccesed by the SW
since then, no "please reload your application" or some flickering. Later
has been shown the fact that maybe would be interesting that SW manage all
the page content. Ok, you can register it on a inline script tag on the top
of the page, problem is that the page itself wouldn't be managed until
reload, so this is my point: since UA is aware of all the content
downloaded by the page, why don't tell them to the SW, maybe re-doing the
downloaded content request in background so the SW could be aware of them?

Hum, now that I think it, maybe this content request would also be done by
the page itself (no needed support by the browser) using XHR calls if it
knows what's the content that's being already downloaded (on the
top-page-script-tag example would be only the html page, that can be get
the url from window.location), but for the inline content it would need to
allow the half-state page... :-/

"Si quieres viajar alrededor del mundo y ser invitado a hablar en un monton
de sitios diferentes, simplemente escribe un sistema operativo Unix."
– Linus Tordvals, creador del sistema operativo Linux

Contributor

@alecf Why would it be hard to have both a SW and an appcache? I seriously don't get it... I could totally use an appcache for my "/style", "/resources" and "/scripts" folders while requiring a ServiceWorker for the "/data" or "/api" folders. Then the website can load perfectly fine without SW if needed because the essential content wil rely on the appcache, while still providing a case-by-case caching functionnality for more variable and user-dependent content, and it is not a critical issue if that happens after a very small latency because the core content can be ready independtly.

By the way, it is totally false that the page will display blank while the SW is loaded, because a developer with a minimum of logic will make sure not to have blocking content until it has enough stuff going on to display its progressbar; or it will make sure the SW is fast, or more likely both. This 10-30s analogy is hyperbolic and totally misses the point. The SW may allow huge win on the wires by allowing finer content negotation; diff files and request prioritization that in the end may make the page appears to load faster even on first run.

Member

@alecf #73 (comment) is a good summary. One thing I would clarify: it's not just about "going offline". I have a feeling that many apps will use SW without ever leveraging the offline portion of it. SW provides a "scriptable proxy / router", and many sites will lean on it as such. What we want to mitigate, as you already pointed out, is the window.reload() case + boot screen.

To enumerate the options we've discussed so far:

  1. Do nothing. We'll end up with Gmail-like boot screens -- not a great outcome.
  2. Allow SW to be inlined into the document -- this seems reasonable, but requires API changes. This would mitigate the blocking concern since the bootstrap script is part of the doc itself. Yes, it means it would be duplicated across docs.. that's a known tradeoff: keep it small, etc.
  3. If you're running over SPDY/HTTP 2.0, you could use server push and existing syntax to mitigate some of the inlining costs.

(3) is feasible today without any modifications to the spec, but it does seem like we should provide (2) -- it feels odd to force an asset to be an external resource only. Something like:

navigator.registerServiceWorker("/*", function() { ... }).then(...)

Member

Expanding on previous question about inline workers.. What stops us from simply allowing something like:

<script id="serviceworker" type="javascript/worker">
    // inline worker, as defined in: https://github.com/slightlyoff/ServiceWorker/blob/master/explainer.md#a-quick-game-of-onfetch
    this.version = 1;
    var base = "http://videos.example.com";
    var inventory = new URL("/services/inventory/data.json", base);  
    // ... snip ...
</script>

<script>
  var blob = new Blob([document.querySelector('#serviceworker').textContent]);
  var worker = new Worker(window.URL.createObjectURL(blob));

  navigator.registerServiceWorker("/*", worker).then(
    function(serviceWorker) {
      console.log("success!");
      serviceWorker.postMessage("Howdy from your installing page.");
    },
    function(why) {
      console.error("Installing the worker failed!:", why);
    }
  );
</script>

Or, something close to that...?

Contributor

I'm still not convinced with inlining in HTML. This condition resource downloading to the detection of a JavaScript code execution. What do you do if the author did put a < script > to an external URI between the "javascript/worker" and its implementation? This is also a burden for to make sure all pages include this inlined script.

As a consequence, I'm still totally convinced that an HTTP Header is the best solution. HTTP2 Servers can reply with the url of the controller code + push it straight away if thought necessary, and HTTP1 server that want to perform inlining can return a data uri.

I'm a huge fan of pushing a domain controller at the HTTP level because, at the end of the day, a domain controller interacts with the server; this is not be so different from a "we support TLS" header: it's a request from the server to switch protocol, and add a layer in the communication channel.

I could totally see how some website provider would like to use a custom domain controller for all its websites (because they want to use some special protocol that supports more efficient compression, or whatever) without modifying the code of the sites hosted on the server.

"What do you do if the author did put a < script > to an external URI between the "javascript/worker" and its implementation?"

I don't think it's a problem to simply specify that all requests that you want to control via the ServiceWorker must be placed below the API call to the service worker. Removing a feature that could be very valuable simply because someone might use it incorrectly doesn't seem like it makes sense.

While I think using HTTP push works well, having a JS equivalent is still very valuable. With an inline script variant there is really little to no downside in regards to performance, and it would allow people to build tools that do not require configuration of any backends. Asking users to insert a snippet of JS is simple - asking them something to configure their nginx/apache/iis/django/rails/spring setup is not :)

Member

@FremyCompany as you point out yourself, an HTTP 1.x server will have to inline the controller for best performance -- by definition, we need a way to do that (and no such mechanism currently exists). Your point about also allowing the controller to be delivered via server-push is a valid one. Ideally, both approaches should work.

Re, switching protocols: this is completely orthogonal. If you want to negotiate custom application protocols, take a look at ALPN extension to TLS.. but then you'd have to teach the browser how to speak your new protocol -- good luck. :)

piranna commented Oct 30, 2013

Re, switching protocols: this is completely orthogonal. If you want to
negotiate custom application protocols, take a look at ALPN extension to
TLS.. but then you'd have to teach the browser how to speak your new
protocol -- good luck. :)

This is just my own personal use case why I'm interested on ServiceWorkers,
and I think that with Protocol Handlers (
https://developer.mozilla.org/es/docs/Web-based_protocol_handlers) this can
be achieved easily...

"Si quieres viajar alrededor del mundo y ser invitado a hablar en un monton
de sitios diferentes, simplemente escribe un sistema operativo Unix."
– Linus Tordvals, creador del sistema operativo Linux

Member

@piranna that has nothing to do with on-the-wire transport protocols or ServiceWorker... It allows you to register a "handler" which can be a different web service, but that will still run over regular HTTP.

piranna commented Oct 31, 2013

Yes, but if I didn't understood bad how ServiceWorker will works according
to the manifest publised on GitHub, nothing can't prevent me to define a
protocol handler that points to a URL managed by a ServiceWorker, also if
it's defined on a diferent domain.

El 31/10/2013 04:28, "Ilya Grigorik" notifications@github.com escribió:

@piranna that has nothing to do with on-the-wire transport protocols or
ServiceWorker... It allows you to register a "handler" which can be a
different web service, but that will still run over regular HTTP.


Reply to this email directly or view it on GitHub.

Contributor

@jansepar This is an issue, to the contrary. That means that every time the browser encounters a < script >, it cannot issue requests from the look-ahead parser. We clearly do not want that to happen.

@igrigorik I propose a method enabling to inline the handler in the case of HTTP1 servers: they can use a "data:" uri in the header instead of using a relative url to the server. This is working totally fine and only need to be done once (because the service worker could notify he's already installed by adding a specific header with his version to further requests). All this can be done at the server level without touching the code of the page itself.

Regarding the protocols switch, I think it's not a real issue to override or embrace the HTTP semantics because we can open a WebSocket to the main server and then use it as a bidirectionnal channel. There's also the possibility to add features on top of HTTP like accepting a DIFF file and using the previous version in cache and the DIFF to generate the new version in cache. This does not require redefining the HTTP semantics, it just requires an intermediate layer between the resource loader and the network layer.

Contributor

What I meant with my DIFF example is that we can add features on top of HTTP, like differential update of files, while

  • remaining totally compatible with browsers that do not support ServiceWorkers (those will not send the required opt-in header for differential updates and will either get a 304 or a 200)
  • not requiring any cooperation from the hosted application to be effective (differential update of files could work on any websites running today, and could be offered by some network provider as part of his web hosting)

This is why I think Always-On SeviceWorkers should be exposed at the HTTP Header level, and not inside the web page itself.

Contributor

Also, http headers are far less likely to be corrupted during an XSS attack.

While the attacked websites can still reinstall a proper worker once the issue is discovered, - at least if the user refresh the app and does not always resume it from thombstoned state, - between the moment where the attack started and the attack is bloqued, many critical user information may have leaked to the attacker. ServiceWorkers are very powerful, so allowing XSS code to define them is probably dangerous.

Using an header also makes it possible for the browser to do a HEAD request from time to time to the page currently being loaded or resume from thombstoning to make sure the ServiceWorker wasn't updated or corrupted (and ask the user to refresh the page automatically if it was). A website could then issue a "Service-Worker: None" header to force all clients to terminate the service worker currently in use, for example because it has been determined it had been corrupted by an attacker.

@FremyCompany I'm not sure I understand how this conflicts with the look-ahead parser. I think the ServiceWorker would have to work hand-in-hand with the look-ahead parser, regardless of whether or not we allow inline ServiceWorkers, because any script kicked off through the look-ahead parser would need to route through the ServiceWorker anyways. Say for example, the ServiceWorker is asynchronously installed (forgetting this ticket for a second) - subsequent page loads will still use the look-ahead parser to kick off all requests for resources in the page, and loading of those resources would have to be aware of the ServiceWorker in order to properly route the requests.

The example that @igrigorik wrote up seems like it would work in such a way that allows for installation of the ServiceWorker before the lookahead parser will have a chance to kick off any requests for resources. This assumes though that the scripts for installing the ServiceWorker are at the top of the document, declared before any other scripts, stylesheets, etc.

As for security concerns, what you're proposing suggests that the only way of setting up the ServiceWorker would have to be through HTTP Push or a data URI set in HTTP1.1 headers, correct? I personally think that this would be a large barrier to entry for adoption of this feature - as mentioned before, inserting a snippet of JS into a site is easy, asking people to configure their backends is not. Requiring scripts to be pushed from the origin using HTTP Push is inherently more safe not just for ServiceWorker, but for many other scripts as well, but requiring certain JS features to be usable only if they are pushed from the server using HTTP Push (or datauris in headers) does not seem like a good way going forward for the web. I think there are other limitations we could place on ServiceWorker to protect against XSS attacks.

Contributor

Preventing the look-ahead parser to issue any request while waiting for a script to execute because it could potentially install an "Always-On" ServiceWorker seem a hard case to sell to me. We need a deterministic way to decide whether or not issuing requests should go through a worker or not.

Even in the case we settle for an HTTP Header, we can let websites (that do not prevent it via CSP and do not already define the header via HTTP) use a META[HTTP-EQUIV] tag before any <script>, <link> or <style> to do exactly the same.

Preventing the look-ahead parser to issue any request while waiting for a script to execute because it could potentially install an "Always-On" ServiceWorker seem a hard case to sell to me. We need a deterministic way to decide whether or not issuing requests should go through a worker or not.

The inline scripts in Ilya's example can also include some attribute that indicates to the parser "do not kick off the preloadScanner for this inline script".
That means that parsing would stop for the duration of these scripts' execution, but since it should be relatively fast, the damage is significantly less then waiting for a new blocking resource to download.

Regarding the data URI in HTTP header idea, it's interesting, but I won't be surprised if some compatibility issues arise with intermediate proxies if this is deployed as HTTP rather than HTTPS. Intermediate proxies may have some expectations on the maximum length of an HTTP response value, which may break if the script is large enough.
A meta tag can work better, but an inline script seems cleaner to me.

Contributor

@yoavweiss I've a problem with an abritrary script with just a flag. Because it can do abritrarily complex operations, it can, for exemple, trigger a synchronous XMLHttpRequest, which means the browser will have to execute the request without using a worker, and then use the worker for the following requests, which the draft explicitely said we do not want (ie: swap workers during the page load).

The other option is to make sure any such request will fail, and we will have to specify the interaction of such a worker script with other network layers. This could be very tricky and a lot of work. A purely descriptive approach avoids these pitfalls.

If we go the purely declarative way, we could enable either an <meta http-equiv> or a special kind of <script> with a specific "type" attribute that would only contain the worker's code.

But even this can be an issue, however, because this tag will have to be placed after some other meta tags (because a meta tag can cause the document's encoding to change and therefore the page should reparsed which cannot happen once a script has been sent to the worker for execution) but before any because links would trigger downloads (and the look ahead parser cannot possibly know a <script worker> is due in the next lines; once any download has been done, the worker cannot be created anymore for the current page because that would cause a worker switch we wanted to avoid).

What I don't like with the <script> solution is that it would be the first time the placement of a <script> has an impact of whether it is executed or not. Meta tags already have the notion of order dependance.

However, I'm still not convinced the inlining use case is legit. If the website owner really cares about performance, he will simply switch to an HTTP2 server. Before we have two interoperable implementations of ServiceWorker, HTTP2 is very likely to be implemented in every browser already, and major websites (which are likely candidates for service workers) will have made the switch as some already did with SPDY.

In addition, inlining a script at the top of the page as required for this will definitely push further other very important declaration like scripts and stylesheets, in addition to being impractical if your websites has multitple static pages (because you would need to modify your script in every one of them when you make a change).

At some point, we should design with best practices in mind, and the best practice here is to use an header which can be compressed using HTTP2 and rely on Server Push to "inline" the script the first time if needed.

Member

IMHO, data-URI's are not the way to go here. I don't want to see large obfuscated blobs in my document, or in my headers (besides, this is a non-starter due to size constraints and overhead), for a regular (text) script. Yes, it probably works today, but it's a hack, we should be able to do better. Also, I would like to see some notion of "one-time" SW -- i.e. inlined worker is only applied in the context of the current page.

@FremyCompany maybe I'm missing something, but I really don't see what the distinction is between a SW instantiated from inline script and from external file -- it's the same sandbox and same API's once the worker is running. Also, as @jansepar already pointed out: if you have an XSS hole on your site, you can already define your own "SW"... just override the XHR prototype and go to town.

Finally, I'm as big of an HTTP 2.0 fan as there ever was.. But we shouldn't make 2.0 a requirement for SW.

Contributor

@igrigorik If your model is to propose that the inlined script is only running the worker for the current page, that seems reasonable to me. The issue I see is in the case you define a SW that applies on multiple pages is that if you can do it with XSS then you can also reach other pages running after that may otherwise not have been compromised. If the goal is to limite the scope of the worker to the current page, I do think an inline script may do the trick, with the before-mentioned restrictions (before any <link> but after http-equiv/charset <meta>s)

Contributor

@igrigorik

The difference I see is between InHeaders and InPage declarations, because the page can be subject to XSS but also to corruption of the source code (like on Github...), while the server headers are generally more robust, because they are lower-level and configured at the machine level. Good websites do not have their configuration files in their github, for instance.

That's why we have things like CSP https://developer.mozilla.org/en-US/docs/Security/CSP/Using_Content_Security_Policy) using headers.

Member

@FremyCompany MITM can also inject headers... If you have an XSS hole, then what would stop you from injecting a snippet with SW declaration pointing to an external file? Am I missing something obvious?

Contributor

I think the issue is I wasn't able to make you understand what I meant. I'm not advocating for a difference between inline/external file, I'm advocating for a difference between inline and in headers. Man-in-the-middle is an issue you have to deal at the transport layer (via TLS) but XSS is an issue you're concerned at the application level.

Though technically, an XSS attack allows you to inject HTML you cannot possibly host a file on the server (so you would have no way to set a SW via an XSS attack if you're forced to reference a file on the current domain).

I believe it's okay to allow an inline worker to affect the current page, because it doesn't increase by much the attack surface of such an XSS attack, but if we allow this worker to be reused accross pages we potentially increase the attack surface by much.

The fact the file is hosted on the server gives the server a second chance to catch the problem and react to it. Also, as a rule of thumb, headers are more secure than the raw content.

Member

I'm still wondering if the raised XSS concerns are a red-herring... that said, I'll defer to others on the list. I'll just note that HTTP headers as a transport for arbitrary large SW blobs is not a good idea.

@slightlyoff @jansepar what are your thoughts on SW + per-page control?

@igrigorik I'm fine with per-page control because my primary use of SW would be for the ability to control network fetches. But I would like to better understand the reason/motivation for SW effecting multiple pages - is it to avoid having to do the setup for every pageload? Maybe there are more reasons I haven't thought of.

Member

@jansepar if inline SW only affects current page... any gotchas with respect to caching / other functionality? E.g. what would the implementation do if you tried to create a cache and fetch resources for offline? Technically, if you visit a single-page app and keep it open / go offline.. I guess this makes sense. But, once page is unloaded, the cache would get wiped?

@igrigorik Hmm. Well, if your application is offline and you need to do a page fetch, that means your page must have already downloaded - in that case, that probably means you're probably using a single-page app already. That's a big assumption though... some people might want to rely on native page load functionality but still be able to intercept those page loads.

Maybe if your application is offline, the SW would persist until your connection came back? It wouldn't make much sense for you to be able to load the next page through SW, but then fail to load on subsequent pages (unless those pages also had the SW script in them...but that would be a little ridiculous).

How about just making SW optionally persist across multiple pages vs just the current page? (let the developer choose through an option in the API)

Member

@jansepar I think we're talking about different things.. and rereading my earlier question, not sure it actually makes sense. ;) In an attempt to get back on the same page, let me try to clarify the use case that we're after..

As site owner I want to use SW to intercept all requests including those made on the initial page load, which means that SW must be instantiated before any request can be made. Currently, to get this behavior I would have to throw up a "loading screen" which waits for the controller to be fetched, and then issue a hard reload... For obvious reasons this is a suboptimal experience.

Ideally, to avoid the loading screen > reload workflow, I'd like to be able to inline the actual controller in the head of my page: this avoids the extra roundtrip and latency associated with fetching the controller and allows the page to instantiate it before any other requests are made. Proposed syntax: #73 (comment) ... With that in place:

  • The controller should be instantiated before any request is made
  • The controller expiry is same as that of the document (e.g. if the page expires in 48hrs, then controller is also valid for same duration since its part of the page itself).
  • The inlined controller has all the same characteristics as external controller: it can create caches, etc.
  • The controller is invalidated and updated when the inlined script changes within the page.

Does that make sense?

P.S. As an alternative to inlining it would be nice to support "server push" as well: the server pushes the resource, which goes into the browser cache, and the controller is then referenced as an external asset... If the controller is found in cache, is it guaranteed to be instantiated before any requests are made?

jansepar commented Dec 6, 2013

All of the points you listed sound great to me! Couldn't have come up with a better summary myself :)

As for your last question, "If the controller is found in cache, is it guaranteed to be instantiated before any requests are made?", that's a tricky one. If we wanted SW to be able to intercept all requests on initial page load, it needs to be a synchronous operation, even if an external SW script is used.

Does the browser have the ability with Server Push to say "Hey, that script is already being downloaded! Thus, I will not kick off a new request for that script"? If the answer is yes, then blocking on an external SW script may not be so bad if that script is already half way downloaded due to Server Push - at least you would not incur the latency / new connection penalty. Thoughts?

Member

As for your last question, "If the controller is found in cache, is it guaranteed to be instantiated before any requests are made?", that's a tricky one. If we wanted SW to be able to intercept all requests on initial page load, it needs to be a synchronous operation, even if an external SW script is used.

Hmm, I don't think its "tricky".. it's fundamental! Putting inlining aside, this is a general problem: I've come to your page before, I've installed the controller. A day later I open a new window and go back to same page: what's the sequence that determines that controller will be used? Do we block all requests until we check the browser cache for the controller? How do the current implementations handle this?

Contributor

I think we have to solve this by returning to the initial goals: given the goal of a Service Worker is to provide (1) an offline mode with smarter cache handling and (2) a new custom communication layer (for example some website may need a DIFF-based communication, or even a GIT/SVN-based cache update).

The second use case may accomodate with a Service Worker that loads late and miss a few requests, because those requests will be issued faster and it's all about loss and gains. However, if you want to have some offline support and intelligent use of the cache, then it's absolutely necessary that ALL requests go through the service worker, even if it takes a dozen ms to load the worker.

In conclusion, I think we may have to provide some synchronous method for loading the worker. In my prototype implementation (I still have to write a dawn blog post about this), the worker (defined in the headers) is loaded completely before the page is returned to the browser for parsing, making the process technically synchronous.

However, the thing we really need is not synchronicity but rather wait before issue any request that the worker is ready (or failled to load) - I could have done that, but that was slighly more difficult to implement, actually.

To mitigate the latency cost of using a worker, we may also have a special cache called "AppCache" from which the browser can match any GET request without asking the service worker, if their Expires header is not a date in the past. This would be ideal for things like CSS stylesheets and JS librairies for which we do not need any special treatment.

I was also surprised by the fact there was no way to define the URLs of interest of the worker before its starts, in order to initiate immediately all requests to URL the worker is not going to handle anyway (example: if a worker does not handle binary files like PNG or JPG, it may specify that it's not interested in handling the "/images" folder for faster processing).

Member

I think we have to solve this by returning to the initial goals: given the goal of a Service Worker is to provide (1) an offline mode with smarter cache handling and (2) a new custom communication layer (for example some website may need a DIFF-based communication, or even a GIT/SVN-based cache update).

(2) is a non-goal for SW and we should park this conversation.

In conclusion, I think we may have to provide some synchronous method for loading the worker. In my prototype implementation (I still have to write a dawn blog post about this), the worker (defined in the headers) is loaded completely before the page is returned to the browser for parsing, making the process technically synchronous.

SW in HTTP headers is a bad idea.

To mitigate the latency cost of using a worker, we may also have a special cache called "AppCache" from which the browser can match any GET request without asking the service worker, if their Expires header is not a date in the past. This would be ideal for things like CSS stylesheets and JS librairies for which we do not need any special treatment.

Honestly, you're digging in the wrong direction. We don't need any special cases.

jansepar commented Dec 9, 2013

Hmm, I don't think its "tricky".. it's fundamental! Putting inlining aside, this is a general problem: I've come to your page before, I've installed the controller. A day later I open a new window and go back to same page: what's the sequence that determines that controller will be used?

One of the points you made previously was that "the controller expiry is same as that of the document (e.g. if the page expires in 48hrs, then controller is also valid for same duration since its part of the page itself).". I think that makes sense - if the page is already cached, continue using the installed SW. If the page has expired, the page is fetched, and the SW is sent down using server-push.

Do we block all requests until we check the browser cache for the controller? How do the current implementations handle this?

If the SW comes with a guarantee that it can intercept all requests then yeah, the browser would have to block all requests until it checks its cache. You could make the argument that the primary goal here is purely for control of requests only when offline, therefore maybe it would be acceptable for certain requests to "leak" that don't get routed through SW. I would prefer this to not be the case though.

Member

If the SW comes with a guarantee that it can intercept all requests then yeah, the browser would have to block all requests until it checks its cache. You could make the argument that the primary goal here is purely for control of requests only when offline, therefore maybe it would be acceptable for certain requests to "leak" that don't get routed through SW. I would prefer this to not be the case though.

"Leaky controllers" would be rather unfortunate and would serve as a great disincentive against actually using SW -- let's not go there! I guess to answer my own question, there are two cases:

  • The UA should perform a parallel lookup for the request page and its controller: if valid controller is found, it must be instantiated and all requests must be routed through it. As an interesting edge case: valid controller is found, but the page itself has expired... in this instance, the request for the page itself gets routed through the SW.
  • The UA performs parallel lookup and finds valid page but no SW controller/.. The UA begins parsing cached resource and detects inlined controller: controller is instantiated and all requests are routed through it.

Does that sound about right?

jansepar commented Dec 9, 2013

It's sounding good! One point I'd like to get clarification on:

As an interesting edge case: valid controller is found, but the page itself has expired... in this instance, the request for the page itself gets routed through the SW.

You previously made a comment suggesting that "the controller expiry was to be the same as the document itself" - but I'm guessing that was only for inline SW, correct? If so, the only way for your above comment to work is if the controller exceeded the lifetime of the page, which could only happen with an external SW, assuming the lifetime of the controller setup from an external SW is longer then the lifetime of the page (I assume this would be set via request headers).

Also, I think we can agree that there should be no leaky controllers - have we decided then on how that would work with external SW requests? Would we not allow external SWs unless that SW request was downloaded through the use of a server-push (which would have a very small penalty)?

Contributor

I think we have to solve this by returning to the initial goals: given the goal of a Service Worker is to provide (1) an offline mode with smarter cache handling and (2) a new custom communication layer (for example some website may need a DIFF-based communication, or even a GIT/SVN-based cache update).

(2) is a non-goal for SW and we should park this conversation.

I'm not sure I understand your reasoning here... As far as I can reason about, this API allows that, I even think it encourages that, and as a matter of fact it will be used in this way - and this is a good thing.

Most websites and applications are under GIT or SVN now. There's absolutely no reason to believe people will not want to "update" their cache using the same GIT protocol and tagged releases (or something similar), and I see no reason to view this as a bad idea.

A major issue of the HTTP protocol now is the bad cache usage when only a few bytes are changed and that a whole file must be reissued. HTTP2 and things like HTML Imports will help solve this issue by allowing us to divide a file into multiple smaller ones, but this will increase the number of server-pushed 304 replies and will somehow increase the network overhead anyway even in case nothing changed.

Differential updates is a matter of fact in a majority of version control systems now, and an offline cache works exactly like a partial local clone of a remote git repo. It just makes sense to allow website and webapp owners to build upon their own versioning infrastructure.


To mitigate the latency cost of using a worker, we may also have a special cache called "AppCache" from which the browser can match any GET request without asking the service worker, if their Expires header is not a date in the past. This would be ideal for things like CSS stylesheets and JS librairies for which we do not need any special treatment.

Honestly, you're digging in the wrong direction. We don't need any special cases.

Well... why? I don't consider this as a special case at all, I consider this as the normal case, the one everyone is facing right now today. I don't see why we should force an application to either pay a latency cost on all its requests because it uses a worker, or none of them because it doesn't.

If it's really the "special" thing around the magic "AppCache" constant that makes you uncomfortable, I can get that, we may just as well add some property to the Cache class to mark the cache as "automatic" in which case any request mapping an URL in the cache can be replied without disturbing the worker.

At all times, the worker will be in control of what is to be found in those caches, and which caches are defined as automatic. What problems do you envision using this system? I see only benefits to it...


Just so you see what I've in my head, I'm seeing a worker that goes online when it starts, looks for a list of files that have been updated since last check, removes those files from the automatic caches, start their download in the background (or more precisely download a difference of those files) and then returns to the main app. As those files are being downloaded, they are added back to the automatic cache. Once the update process is done, the worker is not doing any work and does not impact latency at all.

We can even imagine this update process being triggered by the app itself (i.e. a message is shown to the user asking if he want to update, then when he agrees, a specific message is sent to the worker to initiate the update procedure).

Member

You previously made a comment suggesting that "the controller expiry was to be the same as the document itself" - but I'm guessing that was only for inline SW, correct?

Yep.

If so, the only way for your above comment to work is if the controller exceeded the lifetime of the page, which could only happen with an external SW, assuming the lifetime of the controller setup from an external SW is longer then the lifetime of the page (I assume this would be set via request headers).

Yep. For inlined controllers, controller lifetime == page cache lifetime.

Also, I think we can agree that there should be no leaky controllers - have we decided then on how that would work with external SW requests? Would we not allow external SWs unless that SW request was downloaded through the use of a server-push (which would have a very small penalty)?

No, I don't think we need that, we should use the behavior as defined today: if an external controller is specified and it is not in cache, then it is downloaded asynchronously > "installed" > kicks-in on the next page refresh/load. If all you care about is offline then this behavior is just fine. On the other hand, if you also want to use SW for the first page load, then you can inline the controller and you'll have a guarantee that it will be executed it every case (first visit, repeat visit, etc).

If it helps, I can try to put together a more coherent doc around this and outline the various paths..

jansepar commented Dec 9, 2013

That sounds good to me! 👍

A doc would be useful to summarize this thread for others - or just an update/PR on the README outlining how SW on first page load will work.

Member

Readme PR would require some careful wordsmithing... I'd like to get some feedback from implementers first.

To that end, a quick summary of this mile-long discussion:
https://docs.google.com/document/d/1fH3ZKm0kVE6sKn-PHn0ejs3rW3-Xc6GYtVECrexONng/edit#

Collaborator

I've read the doc and most of the comments, but apologies if I've missed something.

In the install event of the service worker you can take over immediately (event.replace), this includes handling subsequent connections made by pages. However, it doesn't block connections while the worker downloads or installs, but I really see this as a feature not a bug.

I would rather pages weren't service-worker dependent, meaning they can get content on screen on first-load quicker, and work at all on browsers that don't support service worker.

Yes, you can throw up a loading screen & replace/reload if the service worker isn't present, but this is better than a blank screen. Ideally the content should just be served without the worker, but I realise there'll be cases where you'll want to wait for it, but the wait & replace/reload technique sounds like the right kind of hoop to jump through to get it. Hopefully it'd encourage developers to think about what they could put on their loading page to make it seem like less of a block, game instructions, global high scores perhaps.

I'm not against worker registration via a header, it's good for the preloader, but I still wouldn't want it to block other requests.

The inline worker is interesting, but what does it solve? What would you use it for?

Member

The inline worker is interesting, but what does it solve? What would you use it for?

For a second, put the initial install case aside - i.e. let's assume controller is installed. With that in place, I have a fully scriptable forward proxy: I can rewrite URLs, I can cancel requests, I can fabricate responses... With some additional smarts I can add a layer that does UA dependent rewriting: fetch this stylesheet for device X, that one for device Y; rewrite this URL to perform image resizing / cropping / scaling, and so on. In other words, I don't even care about the offline part of this equation...

Now, I'm not saying these are all "good things", but I do fully expect that they will be (ab)used as such. As of today, SW gives me a whole lot of power, and if it means that the first page load will have to suffer from "please wait while we install this SW controller thing -> refresh", then I would expect to see a whole lot of loading pages in the future - that would suck.

Hence the reason for this thread... I would like to either avoid, or minimize the need for above scenario. As of right now, we're stuck in the worst case and I do expect many FEO products to abuse it once its available.

I'm not convinced that inline worker is the right way to solve this either, but the basic thinking is: provide a way to instantiate the SW during initial request with minimal impact on performance (for one, no extra RTTs) of the page, and in a way that avoids loading screens.

Collaborator

I agree, people are going to use it for those things. However, I'd rather people had to jump through a hoop to do the loading screen & replace/reload thing, which may hint that they're doing something non-standard. Maybe when they code the serviceworker feature detect they'll think "hey, this is going to be broken for users without SW!"

If we make an API that offers SW dependency, we're encouraging it.

Offering an opportunity for inlining doesn't particularly encourage dependency, anymore than providing such an API in the first place does, but it facilitates a number of use cases beyond offline support.

A few such "abuse cases" I can imagine:

  • All of Ilya's listed FEO functionality
  • Application specific cache logic beyond what HTTP provides
  • Polyfilling of new browser functionality with new fetch behaviour
  • Controlling loading of resources referenced from CSS and other scripts

These things may seem unnatural or discourageable when coming at this API from the offline use case angle, but seem quite natural to me where my initial understanding of the API was that it facilitated a JS controlled proxy layer in the browser, and as such it also seems natural to use this API as early as possible/prudent/performant in the navigation lifecycle of a site.

Collaborator

Offering an opportunity for inlining doesn't particularly encourage dependency, anymore than providing such an API in the first place does

I disagree. By making dependency a first-class feature we'd be encouraging it.

All of Ilya's listed FEO functionality

As far as I can tell, you're referring to:

UA dependent rewriting: fetch this stylesheet for device X, that one for device Y

Firstly, ew. Secondly, in what cases would you do this? What would you serve to browsers that don't support SW? Can this be done without a serviceworker, just by creating link elements dynamically?

rewrite this URL to perform image resizing / cropping / scaling

In what cases would you do this? What would you serve to browsers that don't support SW? Isn't this what client hints & other responsive image solutions are for?

Application specific cache logic beyond what HTTP provides

Can you go into detail on this & why it requires the SW to be there to handle the first in-page connection? Would it also need to handle the page request? What would you do in browsers that don't support SW?

Polyfilling of new browser functionality with new fetch behaviour

Example? Would it also need to handle the page request? What would you do in browsers that don't support SW?

Controlling loading of resources referenced from CSS and other scripts

Example?

I think the current inline worker proposal makes lock-in far too easy (eg, cache & serve the page with the worker on), the expiry stuff doesn't make sense here because you're having to execute script to discover changes to the worker.

An alternative would be to expose the "fetch" event to windows, which would give them control over page resources and take priority over SW fetch events. In essence, "fetch" events would bubble from pages to the service worker to the standard network stack.

window.addEventListener('fetch', function(event) {
  if (event.request.url == whatever) event.redirectTo(blah);
});

However, there's no point doing this until we have a good solid use-case that isn't damaging to performance and doesn't encourage SW dependency where it isn't needed.

jansepar commented Jan 8, 2014

@jakearchibald Been on vacation - sorry for lack of response :) Responses inline below:

Firstly, ew. Secondly, in what cases would you do this? What would you serve to browsers that don't support SW? Can this be done without a serviceworker, just by creating link elements dynamically?

The use cause we'd like to have for this is to give developers the ability to route requests to alternate destinations on the front-end to do things like image resizing, javascript minification, and more. While the coming responsive image solutions are nice, they require either significant changes to the backend markup (changing every image), backend server (client-hints), or changes to the front-end (picture element). The ability to control resource loading would easily have allowed us to route those images requests to a different server through adding a few lines of Javascript (which is great because significant backend changes are usually much too difficult for websites that are locked into old platforms).

But again, this is just one example. Another interesting use of resource control I've seen (using our Capturing API) was PeerCDN - automatically route requests to their distributed CDN with a single line of JS. We primarily wanted resource control to be able to create performant mobile websites for clients without requiring a proxy service (typically the solution deployed by "m." websites).

Example? Would it also need to handle the page request? What would you do in browsers that don't support SW?

A good example of a polyfill is the polyfill (picturefill.js). In order to write the polyfill for this, the src on the img fallback had to be prepended with data-, which is unfortunate. With the ability to route requests, we could remain semantic and avoid data- altogether.

An alternative would be to expose the "fetch" event to windows, which would give them control over page resources and take priority over SW fetch events. In essence, "fetch" events would bubble from pages to the service worker to the standard network stack.

I am not at all opposed to this :). I actually proposed something VERY similar in my recent article on Smashing Magazine. Checkout the "How Can Browser Vendors Better Support Responsive Images?" section. (just to make this clear - I don't think we need this to solve responsive images, but I do think it would be helpful for a lot of websites).

But the idea of a "fetch" event shouldn't prevent the ServiceWorker from having the functionality that we've discussed in this thread.

Collaborator

In terms of a responsive image polyfill, img[postpone] may land sooner https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/ResourcePriorities/Overview.html#attr-postpone - this would prevent the image preloading if it was also display: none.

Given the size of this thread, I'd like to close it and open another proposing window.onfetch. Any objections?

jansepar commented Jan 8, 2014

I would object to that - despite my thinking that window.onfetch (or a beforeload like Safari extensions provides) would be a great addition to browsers, I don't think it precludes what we've discussed here. One reason is by a comment from Ilya regarding what the intention of the Service Worker is:

One thing I would clarify: it's not just about "going offline". I have a feeling that many apps will use SW without ever leveraging the offline portion of it. SW provides a "scriptable proxy / router", and many sites will lean on it as such. What we want to mitigate, as you already pointed out, is the window.reload() case + boot screen.

And it's true - SW provides a "scriptable proxy/router", it is so close to being able to provide full resource control that I don't see why we wouldn't include the minor changes proposed by Ilya. They simply give you a mechanism to declare your SW inline instead of in an external router. If you can't inline your SW scripts, then SW becomes a "scriptable proxy/router that only works after you've loaded a bunch of stuff". Even if onfetch/beforeload existed, people still might prefer to use SW instead.

@igrigorik what are your thoughts?

Collaborator

Even if onfetch/beforeload existed, people still might prefer to use SW instead.

What does an inline worker offer that window.onfetch doesn't?

piranna commented Jan 8, 2014

What does an inline worker offer that window.onfetch doesn't?

That it can be enabled for a full path tree instead of only for an unique URL.

Collaborator

Right, but that should be avoided because it causes severe lock in. Eg:

navigator.registerServiceWorker('/*', "this.onfetch=function(e){ e.forwardTo('http://google.com'); }");

Now, every page on my domain redirects to Google, and there's no way to stop it because I can no longer run script on my domain.

What use-case does the inline worker solve that window.onfetch or a proper external serviceworker doesn't solve?

Member
window.addEventListener('fetch', function(event) {
  if (event.request.url == whatever) event.redirectTo(blah);
});

What does an inline worker offer that window.onfetch doesn't?

That it can be enabled for a full path tree instead of only for an unique URL.

Perhaps I'm missing something obvious, but how so? Following Jake's example, you define a global handler and you can match on any part of the URL?

@jakearchibald assuming I get the fetch event, what can I do with it? Is this the same FetchEvent (https://github.com/slightlyoff/ServiceWorker/blob/master/service_worker.js#L249) that exposes respondWith, forwardTo, etc?

I think this is a promising direction. It doesn't interfere with SW, and it provides the intended control. Basically, we're talking about enabling chrome.webRequest.onBeforeRequest...

P.S. One edge case that comes to mind: inline script begins executing, control returns back to parser thread, preloader is initiated and a new resource is discovered.. but we can't execute the fetch handler until inline script completes.

Collaborator
alecf commented Jan 10, 2014

I'd be interested to hear @abarth 's take on window.onfetch since he proposed removing onbeforeload, among other reasons citing security issues around invoking a script in the middle of dom mutation. (Something different about SW is that the JS is guaranteed to be running in another context) Maybe it's possible to design window.onfetch in a safe way though.

Collaborator
abarth commented Jan 10, 2014

I'm not sure what onfetch is, but the problem with the beforeload event is that it's an event that's dispatched synchronously from a large number of deep call stacks that don't otherwise need to deal with executing script.

P.S. One edge case that comes to mind: inline script begins executing, control returns back to parser thread, preloader is initiated and a new resource is discovered.. but we can't execute the fetch handler until inline script completes.

AFAIK, in WebKit (and presumably blink) the Preloader doesn't kick off until the first external request is encountered in the main rendering thread. As long as the onfetch handler is registered in an inline script placed before any elements that cause an external request (<script src>, <img src>, etc), then the inline script would always be installed before the browser needs to fetch (this behaviour is different in Gecko though). This is the same restriction we have with Capturing (tag must be placed above any elements that make requests to external files). Definitely correct me if I'm wrong :)

Member

@jansepar I'm not asking about the order, rather.. the contention between fetch function and inline script being executed. Unlike SW, onfetch is not running in its own / isolated worker.

Collaborator
abarth commented Jan 11, 2014

Soon Blink will dispatch preloads directly from the parser thread without joining the main thread. If on fetch requires joining the main thread for resource loads, it's not compatible with our future plans for resource loading.

piranna commented Jan 11, 2014

I think this is a promising direction. It doesn't interfere with SW, and
it provides the intended control. Basically, we're talking about enabling
chrome.webRequest.onBeforeRequesthttp://developer.chrome.com/extensions/webRequest.html
...

Seems so, and seems awesome :-)

"Si quieres viajar alrededor del mundo y ser invitado a hablar en un monton
de sitios diferentes, simplemente escribe un sistema operativo Unix."
– Linus Tordvals, creador del sistema operativo Linux

@abarth @igrigorik ah, I see what you mean. What if onfetch ran in an isolated worker as well?

Also - I wonder if we should move this discussion elsewhere (although the outcome of the onfetch conversation can determine if this thread will be closed out).

Collaborator

Heh, @phuu and I were talking about this on Friday and came to the same conclusion regarding the preloader. I don't think inline workers or onfetch are feasible because of this. It becomes a worse version of document.write unless its registration is handled asynchronously, and if that's the case it offers nothing over a standard serviceworker.

The only way I can see it working is if the worker registration was declarative (response header, or attribute) and optionally required it to load, install & activate before handling page requests. However, this is performance ugly.

Member

@abarth fwiw, independent of the onfetch discussion here, we'll have to join against ServiceWorker... that's a feature.

Collaborator

@igrigorik ServiceWorker isn't main thread, preloader can call onfetch in the active worker or (if absolutely need be) not run for urls with an active serviceworker.

Member

@jakearchibald yep, I'm just pointing out that it won't be a straight shot from parser thread to preloader.

Re, "if absolutely need be": so, SW does not guarantee that if its installed, that all requests must be routed through it?

Collaborator

Ah, no, sorry, that was really ambiguous.

I mean the preloader can disable itself for urls with an active serviceworker, since it knows that upfront (which it wouldn't with the inline worker/onfetch thing).

Obviously having the preloader call the worker's onfetch would be better.

Collaborator
abarth commented Jan 13, 2014

Disabling the preload scanner will significantly impact performance.

Collaborator

@abarth Right. For serviceworker pages the preloader should call onfetch so it can work out which caches to fetch from.

Collaborator
alecf commented Jan 13, 2014

I don't see a problem running preloads through the serviceworker - it would be even greater if there was a hint in the request indicating that the request originates from a preload - the SW might be able to get involved more:

  1. tell the parser to stop preloading (marginally useful, but I'm thinking this is a way of SW saying "I got this - I know more abouit this app than you do, so I'll do the preloading, thank you very much)
  2. make slightly different request when done via preloading - i.e. changing the cachability of the resource returned, etc. (maybe more useful - i.e. an app might decide client-side that preloaded resources might have a shorter or longer lifetime than what the server says)
  3. indicate to the server that this is a preloaded request (though I'm assuming that exists already? is there a special header that is sent to the server for that?)
Member

@alecf I don't think we gain anything by (1) and (2)... as far as SW is concerned, preload requests are no different from parser-initiated requests, and should be treated as such. I don't see why we need to special case them. For (3), no Chrome does not mark preload requests with any special headers/flags -- I believe IE does though, not sure about others.

@abarth I don't think the preload scanner would ever have to be disabled to be able to achieve onfetch or an inline service worker.

Heh, @phuu and I were talking about this on Friday and came to the same conclusion regarding the preloader. I don't think inline workers or onfetch are feasible because of this. It becomes a worse version of document.write unless its registration is handled asynchronously, and if that's the case it offers nothing over a standard serviceworker.

The only way I can see it working is if the worker registration was declarative (response header, or attribute) and optionally required it to load, install & activate before handling page requests. However, this is performance ugly.

If the onfetch event was registered at the top of the page, why would this be any different then inside of a response header or attribute? The event should be able to register before any resources are requested from the preloader thread, and if onfetch ran in a worker as well, there would be no need for onfetch to join the main thread for resource loads, which was a big concern.

Collaborator

@jansepar: The preload scanner runs before & ahead of JavaScript execution. So:

var fetchStr = 'fetch';
window['on' + fetchStr] = function() { ... };

The above has to be parsed & executed before the preloader can continue its work.

<script src="1.js"></script>
<script src="2.js"></script>
<script src="3.js"></script>

The above would have to download one after the other, because each may contain a fetch listener that'd change how the next script is fetched.

Member

@jakearchibald that's not entirely true. Preload scanner is invoked when the parser is blocked - namely, when a blocking script is encountered. Further, as of today an inline script actually blocks both the parser and the preload scanner - the latter part is considered a bug and will/should be addressed in the future. Long story short, today inlining the fetch script would work.

This points to a larger question: how does the UA know to block requests on SW? Say I have my SW declaration at the top of the page, and bunch of scripts below it... How will UA distinguish between the cases of: (a) I've registered this controller before, therefore route these requests through me, vs (b) this is a new controller, preloader/parser please go ahead...

Collaborator

Preload scanner is invoked when the parser is blocked

I don't believe that's true in Firefox, possibly other UAs too. Besides, having it work in inline scripts but not external scripts sounds like bad magic.

Say I have my SW declaration at the top of the page, and bunch of scripts below it... How will UA distinguish between the cases of: (a) I've registered this controller before, therefore route these requests through me, vs (b) this is a new controller, preloader/parser please go ahead...

Controller registration is entirely async and will have no say over the resource loading of this page unless the registered controller calls replace() in its install event.

When a fetch happens, the UA will look to see if there's an active worker that applies to the page url. If not, things continue as normal. Otherwise, the preparser will trigger fetch events in the worker for requests it wants to make.

Say you have 3 scripts at the bottom of the page, it's possible that the first 2 will be requested normally, then the worker install completes, it calls replace(), and the 3rd request goes through the service worker.

replace() is async, so we can prevent the actual worker replacement happening while the preparser is running if need be.

Member

Preload scanner is invoked when the parser is blocked

I don't believe that's true in Firefox, possibly other UAs too. Besides, having it work in inline scripts but not external scripts sounds like bad magic.

It does hold for FF, IE, and others -- exception, IE 8/9. This is something we recently ran into on PageSpeed side, I can dig up the WPT runs if needed. That said, I'm not suggesting this is the right way to solve the problem. As I mentioned earlier, this behavior is considered a bug... even if its consistent between popular UAs.

When a fetch happens, the UA will look to see if there's an active worker that applies to the page url. If not, things continue as normal. Otherwise, the preparser will trigger fetch events in the worker for requests it wants to make.

What does active worker actually mean? Say I visited awesome-widgets.com yesterday for the first time and it installed a SW instance. A week later, and a few browser reboots later, I come back to that site: what is the UA logic here? Is it going to check some local URL registry and see if it has the controller script in cache? Then block/wait to spin up the controller and forward requests to it?

Ultimately, what I'm curious about is: if I can guarantee that the controller is in cache, what can we say about when the controller will be executed / how the requests are routed through it.

Collaborator

Say I visited awesome-widgets.com yesterday for the first time and it installed a SW instance. A week later, and a few browser reboots later, I come back to that site: what is the UA logic here? Is it going to check some local URL registry and see if it has the controller script in cache? Then block/wait to spin up the controller and forward requests to it?

Yep!

registerServiceWorker('/*', 'worker.js')

  • If worker.js is already registered for '/*', abort these steps
  • Fetch worker.js
  • If worker.js is byte identical to the active or installing worker, abort these steps
  • Register 'worker.js' for '/*'
  • Execute worker.js, it is now the installing worker
  • Dispatch "install" in the installing worker
  • If event.waitUntil(promise) is called, wait on the promise to fulfill (it's most likely setting up caches)
  • It's now installed

When a browser tab closes:

  • If there are other tabs open using the same active worker, abort these steps
  • For this url, is there an installing worker that's completed installing?
  • Promote this worker to the active worker
  • Dispatch "activate" (this is where the worker will make backwards incompatible changes, such as deleting caches & data migrations)

When the browser fetches a page, or a request is made from a page:

  • Is the page url in the scope of an active worker?
  • If the active worker is still activating, wait
  • Dispatch "fetch" in the active worker
  • If event.respondWith or event.forwardTo was called, do that, otherwise request normally

The intention is to avoid v1 and v2 of a worker running at the same time. Because if v2 migrates data and deletes caches, it leaves v1 unusable or worse, silently saving data to a location that's no longer being used.

The above would have to download one after the other, because each may contain a fetch listener that'd change how the next script is fetched.

@jakearchibald I don't think this is how onfetch event would/should work. My thinking is that requests dispatched from the preloader would simply trigger an event, and if someone happened to bind to the onfetch event, then great! If not, simply make the request. I would definitely not want onfetch to provide any sort of guarantee that scripts should be blocked from downloading in the event that one script might want to change how to fetch the next script - that would kill performance by removing parallel downloads.

Besides, having it work in inline scripts but not external scripts sounds like bad magic.

Totally agreed :). But I don't think that would be at all necessary. The preloader doesn't kick off until a blocking script is encountered - therefore if the script for registering the onfetch handler was the first script in the page, this would ensure that all external requests could trigger the onfetch event. Although one issue with the statement "the preloader doesn't kick off until a blocking script is encountered" is that it's only true in Blink/WebKit - I believe IE/FF handle this very differently, meaning there might have to be some sort of way of indicating to the browser to block fetching until the onfetch event handler has been installed (maybe through a header?) to prevent the first couple of resources from leaking through. Same goes for installing an inline ServiceWorker to route requests on initial load.

Collaborator

My thinking is that requests dispatched from the preloader would simply trigger an event, and if someone happened to bind to the onfetch event, then great! If not, simply make the request.

This is exactly what you get with serviceworker currently. This thread appears to be about guaranteeing the preloader won't run until the fetch listener is in place.

Although one issue with the statement "the preloader doesn't kick off until a blocking script is encountered" is that it's only true in Blink/WebKit

Right, and this is behaviour that will change in the future.

meaning there might have to be some sort of way of indicating to the browser to block fetching until the onfetch event handler has been installed (maybe through a header?)

Yeah, I said that a few comments ago (#73 (comment)).

The only way I currently see this working is by doing worker registration via a response header, along with another indicating that the worker is required either for the current request or subsequent requests. We wanted to avoid this due to performance.

Member

With lots of help from @slightlyoff and @jakearchibald (thanks guys), I think I finally have a handle on the general shape of this problem... Below is my attempt at the summary of the discussion so far, and a proposal for how to move forward. First, let's start with the basics:

  • On first load, SW installation runs in parallel with parser / preloader.
  • By default, page will use same controller it began its life with - i.e. on first load, none.
  • However, SW can claim immediate control over routing via replace() when the oninstall event is fired.
  • There is race condition here between parser/preloader threads and SW instantiation -- we don't know how big of an issues this is until we get some implementation experience.

On a more tactical side, possible ways to minimize the impact of said race condition:

  • Avoid importScript() and cache setup/fetching within the controller script, as both of these will require extra network roundtrips and will delay the oninstall event.
  • You can use SPDY / HTTP/2 server-push to push the SW controller script ahead of the document. This means the controller is in the client's HTTP cache by the time the parser encounters registerServiceWorker(...), and if no other network requests need to be made (see previous point), then the controller can be instantiated without any extra delays.

Everything above is implementable with the current spec as is.

On the "inlining the SW controller" discussion:

  • Inlining the controller is another way of achieving server-push, albeit without the requirement for SPDY / HTTP/2.
  • Inlining has all the same pitfalls: you have to avoid using external requests (imports, caches) in the controller, and there is still a race between instantiating the inlined controller vs. parser/preloader threads. Once again, we need some implementation experience to understand how big of a problem this is.

On the "onfetch" proposal/idea:

  • onfetch would have to run in a worker to avoid blocking parser/preloader.
  • onfetch + worker has the same race condition (registration vs. parser/preloader threads) as SW.
  • The one advantage that onfetch might have is that it's much simpler than SW and doesn't have to deal with install/upgrade cycles... This might be interesting to explore in the future, but before we go down this path, we should figure out if its solving a real problem to begin with - we'll know the answer to this once we have some implementation experience with SW and know overhead of instantiating the worker, etc.

Finally, some thoughts on moving forward with this discussion:

  • Server-push + optimized SW (no imports or caches) provide the first-page control we're discussing here. No changes required to the spec, and in the spirit of keeping the surface area small for v1, that's a good place to start and gather data from.
  • Once we have some implementation experience with SW/preloader/parser race, we can revisit this and see if it makes sense to add some additional control over this behavior (e.g. if its a non-issue then we're done, otherwise perhaps some extra flag to block on SW instantiation), or we could revisit the onfetch discussion.
  • Once we've addressed the questions above and have more experience with the actual use-case, we can revisit and see if it makes sense to add the "inline controller" path.

Phew. Hopefully that makes sense.

Great summary @igrigorik! I only have one question - in order for developers to use SW (initially), is Server Push a requirement? Or are we saying that SW can still be installed using the markup pattern defined in the spec, but if you want to get SW installed ASAP to (possibly) control loading on the initial page load, then the advice would be "use server push"?

Member

@jansepar there are no hard dependencies between server-push and SW. That said, if you want to accelerate the instantiation of the SW controller then you can leverage push to avoid the extra roundtrips (just as you described). Further, the logic here is that to start push allows us to experiment with this feature and see how well it works (or not) in practice. Once we have some experience with it, we can revisit the discussion to see if we need more controls and if inlining, etc., is worth the effort.

Yup, sounds perfectly reasonable! Thanks for that last bit of clarification
:)
On Jan 15, 2014 9:56 PM, "Ilya Grigorik" notifications@github.com wrote:

@jansepar https://github.com/jansepar there are no hard dependencies
between server-push and SW. That said, if you want to accelerate the
instantiation of the SW controller then you can leverage push to avoid the
extra roundtrips (just as you described). Further, the logic here is that to
start
push allows us to experiment with this feature and see how well it
works (or not) in practice. Once we have some experience with it, we can
revisit the discussion to see if we need more controls and if inlining
discussion is worth the effort.


Reply to this email directly or view it on GitHubhttps://github.com/slightlyoff/ServiceWorker/issues/73#issuecomment-32444318
.

Collaborator

Looks like we have rough consensus.

It seems plausible that we'll need some sort of HTTP header or other control to allow pages to disable/control the preload scanner, which would allow us to reduce the size of the race further. That's something we should probably propose elsewhere (and as @igrigorik rightly points out, wait on impl experience to understand the need for).

Closing the issue for now. Great conversation, all! This thread will be a valuable reference for us in the future if/when we revisit the topic.

Contributor

Just a small note (I'm glad it was resolved the best option would be to introduce an HTTP header, this is what I always felt was the right option) but, in some cases, we may want to allow a SW worker to have access to the requests that were done during the pageload before it was installed on the page. In such case, the worker can let the preloader do its job and still put in one of its specialized cache the content that was fetched independently by the browser before it could handle the requests. This is another way to resolve the race-condition vs performance trade-of worth looking at.

@igrigorik igrigorik referenced this issue in GoogleChrome/samples Nov 12, 2014
Closed

SW Sample: Registration with Immediate Replacement #30

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