Bug 1267733 P4 Add a wpt test that verifies a service worker update can recover from a broken navigation interception. r=jdm a=ritu
authorBen Kelly <ben@wanderview.com>
Fri, 29 Apr 2016 16:55:58 -0700
changeset 326081 8008dbbe1841442ea9d0f376bb9e47058d1a8f7c
parent 326080 b7cef86d445d793c6dc0334a84a1a57e0db56c3d
child 326082 49d3cd645f038fdf64f52d65414f98c59dff148b
push id1128
push userjlund@mozilla.com
push dateWed, 01 Jun 2016 01:31:59 +0000
treeherdermozilla-release@fe0d30de989d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm, ritu
bugs1267733
milestone47.0
Bug 1267733 P4 Add a wpt test that verifies a service worker update can recover from a broken navigation interception. r=jdm a=ritu
testing/web-platform/mozilla/meta/MANIFEST.json
testing/web-platform/mozilla/tests/service-workers/service-worker/resources/update-recovery-worker.py
testing/web-platform/mozilla/tests/service-workers/service-worker/update-recovery.https.html
--- 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>