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>