Bug 1267733 P4 Add a wpt test that verifies a service worker update can recover from a broken navigation interception. r=jdm a=ritu
--- a/testing/web-platform/mozilla/meta/MANIFEST.json
+++ b/testing/web-platform/mozilla/meta/MANIFEST.json
@@ -564,16 +564,22 @@
}
],
"service-workers/service-worker/update-after-oneday.https.html": [
{
"path": "service-workers/service-worker/update-after-oneday.https.html",
"url": "/_mozilla/service-workers/service-worker/update-after-oneday.https.html"
}
],
+ "service-workers/service-worker/update-recovery.https.html": [
+ {
+ "path": "service-workers/service-worker/update-recovery.https.html",
+ "url": "/_mozilla/service-workers/service-worker/update-recovery.https.html"
+ }
+ ],
"service-workers/service-worker/update.https.html": [
{
"path": "service-workers/service-worker/update.https.html",
"url": "/_mozilla/service-workers/service-worker/update.https.html"
}
],
"service-workers/service-worker/waiting.https.html": [
{
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/update-recovery-worker.py
@@ -0,0 +1,25 @@
+def main(request, response):
+ # Set mode to 'init' for initial fetch.
+ mode = 'init'
+ if 'update-recovery-mode' in request.cookies:
+ mode = request.cookies['update-recovery-mode'].value
+
+ # no-cache itself to ensure the user agent finds a new version for each update.
+ headers = [('Cache-Control', 'no-cache, must-revalidate'),
+ ('Pragma', 'no-cache')]
+
+ extra_body = ''
+
+ if mode == 'init':
+ # Install a bad service worker that will break the controlled
+ # document navigation.
+ response.set_cookie('update-recovery-mode', 'bad')
+ extra_body = "addEventListener('fetch', function(e) { e.respondWith(Promise.reject()); });"
+ elif mode == 'bad':
+ # When the update tries to pull the script again, update to
+ # a worker service worker that does not break document
+ # navigation. Serve the same script from then on.
+ response.delete_cookie('update-recovery-mode')
+
+ headers.append(('Content-Type', 'application/javascript'))
+ return headers, '%s' % (extra_body)
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/update-recovery.https.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<title>Service Worker: recovery by navigation update</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/testharness-helpers.js"></script>
+<script src="resources/test-helpers.sub.js"></script>
+<script>
+async_test(function(t) {
+ var scope = 'resources/simple.txt';
+ var worker_url = 'resources/update-recovery-worker.py';
+ var expected_url = normalizeURL(worker_url);
+ var registration;
+
+ function with_bad_iframe(url) {
+ return new Promise(function(resolve, reject) {
+ var frame = document.createElement('iframe');
+
+ // There is no cross-browser event to listen for to detect an
+ // iframe that fails to load due to a bad interception. Unfortunately
+ // we have to use a timeout.
+ var timeout = setTimeout(function() {
+ frame.remove();
+ resolve();
+ }, 5000);
+
+ // If we do get a load event, though, we know something went wrong.
+ frame.addEventListener('load', function() {
+ clearTimeout(timeout);
+ frame.remove();
+ reject('expected bad iframe should not fire a load event!');
+ });
+
+ frame.src = url;
+ document.body.appendChild(frame);
+ });
+ }
+
+ function with_update(t) {
+ return new Promise(function(resolve, reject) {
+ registration.addEventListener('updatefound', function onUpdate() {
+ registration.removeEventListener('updatefound', onUpdate);
+ wait_for_state(t, registration.installing, 'activated').then(function() {
+ resolve();
+ });
+ });
+ });
+ }
+
+ service_worker_unregister_and_register(t, worker_url, scope)
+ .then(function(r) {
+ registration = r;
+ return wait_for_state(t, registration.installing, 'activated');
+ })
+ .then(function() {
+ return Promise.all([
+ with_update(t),
+ with_bad_iframe(scope)
+ ]);
+ })
+ .then(function() {
+ return with_iframe(scope);
+ })
+ .then(function(frame) {
+ assert_equals(frame.contentWindow.navigator.serviceWorker.controller.scriptURL,
+ expected_url);
+ frame.remove();
+ return service_worker_unregister_and_done(t, scope);
+ })
+ .catch(unreached_rejection(t));
+ }, 'Recover from a bad service worker by updating after a failed navigation.');
+</script>