Skip to content

Your service worker creates an infinite loop on page load #24868

New issue

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

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

Already on GitHub? Sign in to your account

Closed
adamzr opened this issue Nov 24, 2017 · 11 comments
Closed

Your service worker creates an infinite loop on page load #24868

adamzr opened this issue Nov 24, 2017 · 11 comments

Comments

@adamzr
Copy link

adamzr commented Nov 24, 2017

At https://getbootstrap.com/ I get an infinite loop trying to load the documentation tab. See dev tools:
screen shot 2017-11-24 at 10 48 23 am

There's an infinite loop in the service worker.

Chrome Version 62.0.3202.94 (Official Build) (64-bit) on MacOS

@XhmikosR
Copy link
Member

Works fine here, I'm not sure how to reproduce. This is the second time we get a report so something must be wrong.

/CC @gauntface @addyosmani if you guys have any ideas.

@phaidon
Copy link

phaidon commented Nov 26, 2017

If I do it I get the following message in the console:
mac os x/chrome

ServiceWorker registration failed: DOMException: Only secure origins are allowed (see: https://goo.gl/Y0ZkNV).

@gauntface
Copy link

To reproduce I did the following:

  1. Go to https://getbootstrap.com/
  2. Go to Document site (everything should be fine)
  3. Open DevTools and go to Application tab and click the update on reload
  4. Refresh the page - get the loop

@gauntface
Copy link

Best guess is this chunk of code:

(function e() {
    "use strict";
    if ("serviceWorker" in navigator) {
        window.addEventListener("load", function() {
            navigator.serviceWorker.register("/sw.js").then(function(e) {
                console.log("ServiceWorker registration successful with scope: ", e.scope);
                e.onupdatefound = function() {
                    var t = e.installing;
                    t.onstatechange = function() {
                        switch (t.state) {
                            case "installed":
                                if (navigator.serviceWorker.controller) {
                                    console.log("new update available");
                                    location.reload(true)
                                }
                                break;
                            default:
                        }
                    }
                }
            }).catch(function(e) {
                console.log("ServiceWorker registration failed: ", e)
            })
        })
    }
})();

@gauntface
Copy link

Ok, so what I hit was that update on reload was continually resulting in the page loading, installing a new SW, reloading, which then forced a new service worker to install.

I'm not sure how else you would get in this loop. Are others seeing the "new update available" log?

@XhmikosR
Copy link
Member

XhmikosR commented Nov 27, 2017

@gauntface: Thanks for the reply, I really appreciate it!

Now, the source for that is https://github.com/twbs/bootstrap/blob/v4-dev/assets/js/src/pwa.js which was basically added in 5951508#diff-a81ed3855367b28b54556dd67ace9caa

Looking at the code it seems we are explicitly reloading the page when in navigator.serviceWorker.controller which doesn't seem right indeed.

I can reproduce too with Chrome 62.0.3202.94 and Dev Tools open. Firefox on the other hand doesn't have this issue.

/CC @Lyricalz since he is the author of that patch.

EDIT:

Should we just call registration.update()? What is the right way to handle this? Or can we just remove the whole onupdatefound block?

@adamzr
Copy link
Author

adamzr commented Nov 27, 2017

You could do what https://www.chromestatus.com does and display a toast to prompt the user to update. See https://github.com/GoogleChrome/chromium-dashboard/blob/master/static/js/service-worker-registration.es6.js

@XhmikosR
Copy link
Member

Hmm, that would require quite a few changes.

Isn't there a default cache that the browser will respect and if it's expired request a new one? What is the downside if we just remove the whole onupdatefound block?

Any PRs are welcome BTW (don't forget to CC me).

@MikeyBeLike
Copy link
Contributor

i'll try to get a PR in for this soon!

@gauntface
Copy link

Few ideas off the top of my head:

1.) Simply don't reload and wait for the user to visit the page again
2.) Call skipWaiting and clientsClaim which will allow the current service worker to control all currently open windows. Then on the users next click, they'll be hitting the new service worker.
3.) Do the same as 2, but reload on activation. This may incur the loop with updateOnReload, but it shouldn't get an infinite loop for normal users since the service worker won't update unless it's actually changed, I think with your current flow, if service worker could be installing but not activated due to multiple windows being open and you'll just keep on looping until the windows are shut.

Skip waiting and clients claim can be done with:

new WorkboxSW({
  skipWaiting: true,
  clientsClaim: true,
});

@XhmikosR
Copy link
Member

Thanks for all the info!

@Lyricalz: I say we just remove the offending code and don't do anything fancy when an update is available, just download it and nuke the old one.

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

No branches or pull requests

5 participants