Block self-update from top level scripts.

Differential Revision: https://phabricator.services.mozilla.com/D3221

bugzilla-url: https://bugzilla.mozilla.org/show_bug.cgi?id=1472303
gecko-commit: 8a40d04dfcbb0f272bfa6696354fd87d982b7a9a
gecko-integration-branch: autoland
gecko-reviewers: asuth
diff --git a/service-workers/service-worker/resources/update-top-level-worker.py b/service-workers/service-worker/resources/update-top-level-worker.py
new file mode 100644
index 0000000..f77ef28
--- /dev/null
+++ b/service-workers/service-worker/resources/update-top-level-worker.py
@@ -0,0 +1,18 @@
+import time
+
+def main(request, response):
+    # 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')]
+    content_type = 'application/javascript'
+
+    headers.append(('Content-Type', content_type))
+
+    body = '''
+let promise = self.registration.update()
+onmessage = (evt) => {
+  promise.then(r => {
+    evt.source.postMessage(self.registration === r ? 'PASS' : 'FAIL');
+  });
+};'''
+    return headers, '/* %s %s */ %s' % (time.time(), time.clock(), body)
diff --git a/service-workers/service-worker/update-top-level.https.html b/service-workers/service-worker/update-top-level.https.html
new file mode 100644
index 0000000..e382028
--- /dev/null
+++ b/service-workers/service-worker/update-top-level.https.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title>Service Worker: Registration update()</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/test-helpers.sub.js"></script>
+<script>
+'use strict';
+
+function wait_for_message() {
+  return new Promise(resolve => {
+    navigator.serviceWorker.addEventListener("message",
+      e => {
+        resolve(e.data);
+      }, { once: true });
+  });
+}
+
+promise_test(async t => {
+  const script = './resources/update-top-level-worker.py';
+  const scope = './resources/empty.html?update-result';
+
+  let reg = await navigator.serviceWorker.register(script, { scope });
+  t.add_cleanup(async _ => await reg.unregister());
+  await wait_for_state(t, reg.installing, 'activated');
+
+  reg.addEventListener("updatefound",
+    () => assert_unreached("shouldn't find an update"));
+
+  reg.active.postMessage("ping");
+  assert_equals(await wait_for_message(), 'PASS', 'did not hang');
+}, 'A serviceworker with a top-level update should not hang');
+</script>