diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /dom/workers/test/serviceworkers | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/workers/test/serviceworkers')
339 files changed, 13569 insertions, 0 deletions
diff --git a/dom/workers/test/serviceworkers/activate_event_error_worker.js b/dom/workers/test/serviceworkers/activate_event_error_worker.js new file mode 100644 index 000000000..a5f159d35 --- /dev/null +++ b/dom/workers/test/serviceworkers/activate_event_error_worker.js @@ -0,0 +1,4 @@ +// Worker that errors on receiving an activate event. +onactivate = function(e) { + undefined.doSomething; +} diff --git a/dom/workers/test/serviceworkers/blocking_install_event_worker.js b/dom/workers/test/serviceworkers/blocking_install_event_worker.js new file mode 100644 index 000000000..0bc6f7b7f --- /dev/null +++ b/dom/workers/test/serviceworkers/blocking_install_event_worker.js @@ -0,0 +1,23 @@ +function postMessageToTest(msg) { + return clients.matchAll({ includeUncontrolled: true }) + .then(list => { + for (var client of list) { + if (client.url.endsWith('test_install_event_gc.html')) { + client.postMessage(msg); + break; + } + } + }); +} + +addEventListener('install', evt => { + // This must be a simple promise to trigger the CC failure. + evt.waitUntil(new Promise(function() { })); + postMessageToTest({ type: 'INSTALL_EVENT' }); +}); + +addEventListener('message', evt => { + if (evt.data.type === 'ping') { + postMessageToTest({ type: 'pong' }); + } +}); diff --git a/dom/workers/test/serviceworkers/browser.ini b/dom/workers/test/serviceworkers/browser.ini new file mode 100644 index 000000000..c0aae30d6 --- /dev/null +++ b/dom/workers/test/serviceworkers/browser.ini @@ -0,0 +1,10 @@ +[DEFAULT] +support-files = + browser_base_force_refresh.html + browser_cached_force_refresh.html + download/window.html + download/worker.js + force_refresh_browser_worker.js + +[browser_force_refresh.js] +[browser_download.js] diff --git a/dom/workers/test/serviceworkers/browser_base_force_refresh.html b/dom/workers/test/serviceworkers/browser_base_force_refresh.html new file mode 100644 index 000000000..1b0d2defe --- /dev/null +++ b/dom/workers/test/serviceworkers/browser_base_force_refresh.html @@ -0,0 +1,30 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> +</head> +<body> +<script type="text/javascript"> +addEventListener('load', function(event) { + navigator.serviceWorker.register('force_refresh_browser_worker.js').then(function(swr) { + if (!swr) { + return; + } + var custom = new Event('base-register', { bubbles: true }); + document.dispatchEvent(custom); + }); + + navigator.serviceWorker.ready.then(function() { + var custom = new Event('base-sw-ready', { bubbles: true }); + document.dispatchEvent(custom); + }); + + var custom = new Event('base-load', { bubbles: true }); + document.dispatchEvent(custom); +}); +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/browser_cached_force_refresh.html b/dom/workers/test/serviceworkers/browser_cached_force_refresh.html new file mode 100644 index 000000000..33bd8cdaa --- /dev/null +++ b/dom/workers/test/serviceworkers/browser_cached_force_refresh.html @@ -0,0 +1,64 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> +</head> +<body> +<script type="text/javascript"> +function ok(exp, msg) { + if (!exp) { + throw(msg); + } +} + +function is(actual, expected, msg) { + if (actual !== expected) { + throw('got "' + actual + '", but expected "' + expected + '" - ' + msg); + } +} + +function fail(err) { + var custom = new CustomEvent('cached-failure', { + bubbles: true, + detail: err + }); + document.dispatchEvent(custom); +} + +function getUncontrolledClients(sw) { + return new Promise(function(resolve, reject) { + navigator.serviceWorker.addEventListener('message', function onMsg(evt) { + if (evt.data.type === 'CLIENTS') { + navigator.serviceWorker.removeEventListener('message', onMsg); + resolve(evt.data.detail); + } + }); + sw.postMessage({ type: 'GET_UNCONTROLLED_CLIENTS' }) + }); +} + +addEventListener('load', function(event) { + if (!navigator.serviceWorker.controller) { + return fail(window.location.href + ' is not controlled!'); + } + + getUncontrolledClients(navigator.serviceWorker.controller) + .then(function(clientList) { + is(clientList.length, 1, 'should only have one client'); + is(clientList[0].url, window.location.href, + 'client url should match current window'); + is(clientList[0].frameType, 'top-level', + 'client should be a top-level window'); + var custom = new Event('cached-load', { bubbles: true }); + document.dispatchEvent(custom); + }) + .catch(function(err) { + fail(err); + }); +}); +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/browser_download.js b/dom/workers/test/serviceworkers/browser_download.js new file mode 100644 index 000000000..bd4da10c9 --- /dev/null +++ b/dom/workers/test/serviceworkers/browser_download.js @@ -0,0 +1,83 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +Cu.import('resource://gre/modules/Services.jsm'); +var Downloads = Cu.import("resource://gre/modules/Downloads.jsm", {}).Downloads; +var DownloadsCommon = Cu.import("resource:///modules/DownloadsCommon.jsm", {}).DownloadsCommon; +Cu.import('resource://gre/modules/NetUtil.jsm'); + +var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", + "http://mochi.test:8888/") + +function getFile(aFilename) { + if (aFilename.startsWith('file:')) { + var url = NetUtil.newURI(aFilename).QueryInterface(Ci.nsIFileURL); + return url.file.clone(); + } + + var file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile); + file.initWithPath(aFilename); + return file; +} + +function windowObserver(win, topic) { + if (topic !== 'domwindowopened') { + return; + } + + win.addEventListener('load', function onLoadWindow() { + win.removeEventListener('load', onLoadWindow, false); + if (win.document.documentURI === + 'chrome://mozapps/content/downloads/unknownContentType.xul') { + executeSoon(function() { + var button = win.document.documentElement.getButton('accept'); + button.disabled = false; + win.document.documentElement.acceptDialog(); + }); + } + }, false); +} + +function test() { + waitForExplicitFinish(); + + Services.ww.registerNotification(windowObserver); + + SpecialPowers.pushPrefEnv({'set': [['dom.serviceWorkers.enabled', true], + ['dom.serviceWorkers.exemptFromPerDomainMax', true], + ['dom.serviceWorkers.testing.enabled', true]]}, + function() { + var url = gTestRoot + 'download/window.html'; + var tab = gBrowser.addTab(); + gBrowser.selectedTab = tab; + + Downloads.getList(Downloads.ALL).then(function(downloadList) { + var downloadListener; + + function downloadVerifier(aDownload) { + if (aDownload.succeeded) { + var file = getFile(aDownload.target.path); + ok(file.exists(), 'download completed'); + is(file.fileSize, 33, 'downloaded file has correct size'); + file.remove(false); + DownloadsCommon.removeAndFinalizeDownload(aDownload); + + downloadList.removeView(downloadListener); + gBrowser.removeTab(tab); + Services.ww.unregisterNotification(windowObserver); + + executeSoon(finish); + } + } + + downloadListener = { + onDownloadAdded: downloadVerifier, + onDownloadChanged: downloadVerifier + }; + + return downloadList.addView(downloadListener); + }).then(function() { + gBrowser.loadURI(url); + }); + }); +} diff --git a/dom/workers/test/serviceworkers/browser_force_refresh.js b/dom/workers/test/serviceworkers/browser_force_refresh.js new file mode 100644 index 000000000..a2c9c871c --- /dev/null +++ b/dom/workers/test/serviceworkers/browser_force_refresh.js @@ -0,0 +1,91 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", + "http://mochi.test:8888/") + +function refresh() { + EventUtils.synthesizeKey('R', { accelKey: true }); +} + +function forceRefresh() { + EventUtils.synthesizeKey('R', { accelKey: true, shiftKey: true }); +} + +function frameScript() { + function eventHandler(event) { + sendAsyncMessage("test:event", {type: event.type, detail: event.detail}); + } + + // These are tab-local, so no need to unregister them. + addEventListener('base-load', eventHandler, true, true); + addEventListener('base-register', eventHandler, true, true); + addEventListener('base-sw-ready', eventHandler, true, true); + addEventListener('cached-load', eventHandler, true, true); + addEventListener('cached-failure', eventHandler, true, true); +} + +function test() { + waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({'set': [['dom.serviceWorkers.enabled', true], + ['dom.serviceWorkers.exemptFromPerDomainMax', true], + ['dom.serviceWorkers.testing.enabled', true], + ['dom.caches.enabled', true], + ['browser.cache.disk.enable', false], + ['browser.cache.memory.enable', false]]}, + function() { + var url = gTestRoot + 'browser_base_force_refresh.html'; + var tab = gBrowser.addTab(); + gBrowser.selectedTab = tab; + + tab.linkedBrowser.messageManager.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true); + gBrowser.loadURI(url); + + function done() { + tab.linkedBrowser.messageManager.removeMessageListener("test:event", eventHandler); + + gBrowser.removeTab(tab); + executeSoon(finish); + } + + var maxCacheLoadCount = 3; + var cachedLoadCount = 0; + var baseLoadCount = 0; + + function eventHandler(msg) { + if (msg.data.type === 'base-load') { + baseLoadCount += 1; + if (cachedLoadCount === maxCacheLoadCount) { + is(baseLoadCount, 2, 'cached load should occur before second base load'); + return done(); + } + if (baseLoadCount !== 1) { + ok(false, 'base load without cached load should only occur once'); + return done(); + } + } else if (msg.data.type === 'base-register') { + ok(!cachedLoadCount, 'cached load should not occur before base register'); + is(baseLoadCount, 1, 'register should occur after first base load'); + } else if (msg.data.type === 'base-sw-ready') { + ok(!cachedLoadCount, 'cached load should not occur before base ready'); + is(baseLoadCount, 1, 'ready should occur after first base load'); + refresh(); + } else if (msg.data.type === 'cached-load') { + ok(cachedLoadCount < maxCacheLoadCount, 'cached load should not occur too many times'); + is(baseLoadCount, 1, 'cache load occur after first base load'); + cachedLoadCount += 1; + if (cachedLoadCount < maxCacheLoadCount) { + return refresh(); + } + forceRefresh(); + } else if (msg.data.type === 'cached-failure') { + ok(false, 'failure: ' + msg.data.detail); + done(); + } + + return; + } + + tab.linkedBrowser.messageManager.addMessageListener("test:event", eventHandler); + }); +} diff --git a/dom/workers/test/serviceworkers/bug1151916_driver.html b/dom/workers/test/serviceworkers/bug1151916_driver.html new file mode 100644 index 000000000..e540ad239 --- /dev/null +++ b/dom/workers/test/serviceworkers/bug1151916_driver.html @@ -0,0 +1,53 @@ +<html> + <body> + <script language="javascript"> + function fail(msg) { + window.parent.postMessage({ status: "failed", message: msg }, "*"); + } + + function success(msg) { + window.parent.postMessage({ status: "success", message: msg }, "*"); + } + + if (!window.parent) { + dump("This file must be embedded in an iframe!"); + } + + navigator.serviceWorker.getRegistration() + .then(function(reg) { + if (!reg) { + navigator.serviceWorker.ready.then(function(reg) { + if (reg.active.state == "activating") { + reg.active.onstatechange = function(e) { + reg.active.onstatechange = null; + if (reg.active.state == "activated") { + success("Registered and activated"); + } + } + } else { + success("Registered and activated"); + } + }); + navigator.serviceWorker.register("bug1151916_worker.js", + { scope: "." }); + } else { + // Simply force the sw to load a resource and touch self.caches. + if (!reg.active) { + fail("no-active-worker"); + return; + } + + fetch("madeup.txt").then(function(res) { + res.text().then(function(v) { + if (v == "Hi there") { + success("Loaded from cache"); + } else { + fail("Response text did not match"); + } + }, fail); + }, fail); + } + }, fail); + </script> + </body> +</html> diff --git a/dom/workers/test/serviceworkers/bug1151916_worker.js b/dom/workers/test/serviceworkers/bug1151916_worker.js new file mode 100644 index 000000000..06585e8e7 --- /dev/null +++ b/dom/workers/test/serviceworkers/bug1151916_worker.js @@ -0,0 +1,13 @@ +onactivate = function(e) { + e.waitUntil(self.caches.open("default-cache").then(function(cache) { + var response = new Response("Hi there"); + return cache.put("madeup.txt", response); + })); +} + +onfetch = function(e) { + if (e.request.url.match(/madeup.txt$/)) { + var p = self.caches.match("madeup.txt", { cacheName: "default-cache" }); + e.respondWith(p); + } +} diff --git a/dom/workers/test/serviceworkers/bug1240436_worker.js b/dom/workers/test/serviceworkers/bug1240436_worker.js new file mode 100644 index 000000000..5a588aedf --- /dev/null +++ b/dom/workers/test/serviceworkers/bug1240436_worker.js @@ -0,0 +1,2 @@ +// a contains a ZERO WIDTH JOINER (0x200D) +var a = "";
\ No newline at end of file diff --git a/dom/workers/test/serviceworkers/chrome.ini b/dom/workers/test/serviceworkers/chrome.ini new file mode 100644 index 000000000..e064e7fd0 --- /dev/null +++ b/dom/workers/test/serviceworkers/chrome.ini @@ -0,0 +1,16 @@ +[DEFAULT] +skip-if = os == 'android' +support-files = + chrome_helpers.js + empty.js + serviceworker.html + serviceworkerinfo_iframe.html + serviceworkermanager_iframe.html + serviceworkerregistrationinfo_iframe.html + worker.js + worker2.js + +[test_privateBrowsing.html] +[test_serviceworkerinfo.xul] +[test_serviceworkermanager.xul] +[test_serviceworkerregistrationinfo.xul] diff --git a/dom/workers/test/serviceworkers/chrome_helpers.js b/dom/workers/test/serviceworkers/chrome_helpers.js new file mode 100644 index 000000000..a438333e2 --- /dev/null +++ b/dom/workers/test/serviceworkers/chrome_helpers.js @@ -0,0 +1,74 @@ +let { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +Cu.import("resource://gre/modules/Task.jsm"); + +let swm = Cc["@mozilla.org/serviceworkers/manager;1"]. + getService(Ci.nsIServiceWorkerManager); + +let EXAMPLE_URL = "https://example.com/chrome/dom/workers/test/serviceworkers/"; + +function waitForIframeLoad(iframe) { + return new Promise(function (resolve) { + iframe.onload = resolve; + }); +} + +function waitForRegister(scope, callback) { + return new Promise(function (resolve) { + let listener = { + onRegister: function (registration) { + if (registration.scope !== scope) { + return; + } + swm.removeListener(listener); + resolve(callback ? callback(registration) : registration); + } + }; + swm.addListener(listener); + }); +} + +function waitForUnregister(scope) { + return new Promise(function (resolve) { + let listener = { + onUnregister: function (registration) { + if (registration.scope !== scope) { + return; + } + swm.removeListener(listener); + resolve(registration); + } + }; + swm.addListener(listener); + }); +} + +function waitForServiceWorkerRegistrationChange(registration, callback) { + return new Promise(function (resolve) { + let listener = { + onChange: function () { + registration.removeListener(listener); + if (callback) { + callback(); + } + resolve(callback ? callback() : undefined); + } + }; + registration.addListener(listener); + }); +} + +function waitForServiceWorkerShutdown() { + return new Promise(function (resolve) { + let observer = { + observe: function (subject, topic, data) { + if (topic !== "service-worker-shutdown") { + return; + } + SpecialPowers.removeObserver(observer, "service-worker-shutdown"); + resolve(); + } + }; + SpecialPowers.addObserver(observer, "service-worker-shutdown", false); + }); +} diff --git a/dom/workers/test/serviceworkers/claim_clients/client.html b/dom/workers/test/serviceworkers/claim_clients/client.html new file mode 100644 index 000000000..eecfb294e --- /dev/null +++ b/dom/workers/test/serviceworkers/claim_clients/client.html @@ -0,0 +1,44 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1130684 - claim client </title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + if (!parent) { + info("This page shouldn't be launched directly!"); + } + + window.onload = function() { + parent.postMessage("READY", "*"); + } + + navigator.serviceWorker.oncontrollerchange = function() { + parent.postMessage({ + event: "controllerchange", + controller: (navigator.serviceWorker.controller !== null) + }, "*"); + } + + navigator.serviceWorker.onmessage = function(e) { + parent.postMessage({ + event: "message", + data: e.data + }, "*"); + } + +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/claim_fetch_worker.js b/dom/workers/test/serviceworkers/claim_fetch_worker.js new file mode 100644 index 000000000..ea62c37b8 --- /dev/null +++ b/dom/workers/test/serviceworkers/claim_fetch_worker.js @@ -0,0 +1,12 @@ +onfetch = function(e) { + if (e.request.url.indexOf("service_worker_controlled") >= 0) { + // pass through + e.respondWith(fetch(e.request)); + } else { + e.respondWith(new Response("Fetch was intercepted")); + } +} + +onmessage = function(e) { + clients.claim(); +} diff --git a/dom/workers/test/serviceworkers/claim_oninstall_worker.js b/dom/workers/test/serviceworkers/claim_oninstall_worker.js new file mode 100644 index 000000000..269afa721 --- /dev/null +++ b/dom/workers/test/serviceworkers/claim_oninstall_worker.js @@ -0,0 +1,7 @@ +oninstall = function(e) { + var claimFailedPromise = new Promise(function(resolve, reject) { + clients.claim().then(reject, () => resolve()); + }); + + e.waitUntil(claimFailedPromise); +} diff --git a/dom/workers/test/serviceworkers/claim_worker_1.js b/dom/workers/test/serviceworkers/claim_worker_1.js new file mode 100644 index 000000000..e5f6392d3 --- /dev/null +++ b/dom/workers/test/serviceworkers/claim_worker_1.js @@ -0,0 +1,28 @@ +onactivate = function(e) { + var result = { + resolve_value: false, + match_count_before: -1, + match_count_after: -1, + message: "claim_worker_1" + }; + + self.clients.matchAll().then(function(matched) { + // should be 0 + result.match_count_before = matched.length; + }).then(function() { + var claimPromise = self.clients.claim().then(function(ret) { + result.resolve_value = ret; + }); + + return claimPromise.then(self.clients.matchAll().then(function(matched) { + // should be 2 + result.match_count_after = matched.length; + for (i = 0; i < matched.length; i++) { + matched[i].postMessage(result); + } + if (result.match_count_after !== 2) { + dump("ERROR: claim_worker_1 failed to capture clients.\n"); + } + })); + }); +} diff --git a/dom/workers/test/serviceworkers/claim_worker_2.js b/dom/workers/test/serviceworkers/claim_worker_2.js new file mode 100644 index 000000000..be8281d34 --- /dev/null +++ b/dom/workers/test/serviceworkers/claim_worker_2.js @@ -0,0 +1,27 @@ +onactivate = function(e) { + var result = { + resolve_value: false, + match_count_before: -1, + match_count_after: -1, + message: "claim_worker_2" + }; + + self.clients.matchAll().then(function(matched) { + // should be 0 + result.match_count_before = matched.length; + }).then(function() { + var claimPromise = self.clients.claim().then(function(ret) { + result.resolve_value = ret; + }); + + return claimPromise.then(self.clients.matchAll().then(function(matched) { + // should be 1 + result.match_count_after = matched.length; + if (result.match_count_after === 1) { + matched[0].postMessage(result); + } else { + dump("ERROR: claim_worker_2 failed to capture clients.\n"); + } + })); + }); +} diff --git a/dom/workers/test/serviceworkers/close_test.js b/dom/workers/test/serviceworkers/close_test.js new file mode 100644 index 000000000..6138d6421 --- /dev/null +++ b/dom/workers/test/serviceworkers/close_test.js @@ -0,0 +1,19 @@ +function ok(v, msg) { + client.postMessage({status: "ok", result: !!v, message: msg}); +} + +var client; +onmessage = function(e) { + if (e.data.message == "start") { + self.clients.matchAll().then(function(clients) { + client = clients[0]; + try { + close(); + ok(false, "close() should throw"); + } catch (e) { + ok(e.name === "InvalidAccessError", "close() should throw InvalidAccessError"); + } + client.postMessage({status: "done"}); + }); + } +} diff --git a/dom/workers/test/serviceworkers/controller/index.html b/dom/workers/test/serviceworkers/controller/index.html new file mode 100644 index 000000000..740e24f15 --- /dev/null +++ b/dom/workers/test/serviceworkers/controller/index.html @@ -0,0 +1,74 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 94048 - test install event.</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + // Make sure to use good, unique messages, since the actual expression will not show up in test results. + function my_ok(result, msg) { + parent.postMessage({status: "ok", result: result, message: msg}, "*"); + } + + function finish() { + parent.postMessage({status: "done"}, "*"); + } + + navigator.serviceWorker.ready.then(function(swr) { + my_ok(swr.scope.match(/serviceworkers\/control$/), + "This page should be controlled by upper level registration"); + my_ok(swr.installing == undefined, + "Upper level registration should not have a installing worker."); + if (navigator.serviceWorker.controller) { + // We are controlled. + // Register a new worker for this sub-scope. After that, controller should still be for upper level, but active should change to be this scope's. + navigator.serviceWorker.register("../worker2.js", { scope: "./" }).then(function(e) { + my_ok("installing" in e, "ServiceWorkerRegistration.installing exists."); + my_ok(e.installing instanceof ServiceWorker, "ServiceWorkerRegistration.installing is a ServiceWorker."); + + my_ok("waiting" in e, "ServiceWorkerRegistration.waiting exists."); + my_ok("active" in e, "ServiceWorkerRegistration.active exists."); + + my_ok(e.installing && + e.installing.scriptURL.match(/worker2.js$/), + "Installing is serviceworker/controller"); + + my_ok("scope" in e, "ServiceWorkerRegistration.scope exists."); + my_ok(e.scope.match(/serviceworkers\/controller\/$/), "Scope is serviceworker/controller " + e.scope); + + my_ok("unregister" in e, "ServiceWorkerRegistration.unregister exists."); + + my_ok(navigator.serviceWorker.controller.scriptURL.match(/worker\.js$/), + "Controller is still worker.js"); + + e.unregister().then(function(result) { + my_ok(result, "Unregistering the SW should succeed"); + finish(); + }, function(e) { + dump("Error unregistering the SW: " + e + "\n"); + }); + }); + } else { + my_ok(false, "Should've been controlled!"); + finish(); + } + }).catch(function(e) { + my_ok(false, "Some test threw an error " + e); + finish(); + }); +</script> +</pre> +</body> +</html> + + diff --git a/dom/workers/test/serviceworkers/create_another_sharedWorker.html b/dom/workers/test/serviceworkers/create_another_sharedWorker.html new file mode 100644 index 000000000..f49194fa5 --- /dev/null +++ b/dom/workers/test/serviceworkers/create_another_sharedWorker.html @@ -0,0 +1,6 @@ +<!DOCTYPE HTML> +<title>Shared workers: create antoehr sharedworekr client</title> +<pre id=log>Hello World</pre> +<script> + var worker = new SharedWorker('sharedWorker_fetch.js'); +</script> diff --git a/dom/workers/test/serviceworkers/download/window.html b/dom/workers/test/serviceworkers/download/window.html new file mode 100644 index 000000000..7d7893e0e --- /dev/null +++ b/dom/workers/test/serviceworkers/download/window.html @@ -0,0 +1,46 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> +</head> +<body> +<script type="text/javascript"> + +function wait_until_controlled() { + return new Promise(function(resolve) { + if (navigator.serviceWorker.controller) { + return resolve(); + } + navigator.serviceWorker.addEventListener('controllerchange', function onController() { + if (navigator.serviceWorker.controller) { + navigator.serviceWorker.removeEventListener('controllerchange', onController); + return resolve(); + } + }); + }); +} +addEventListener('load', function(event) { + var registration; + navigator.serviceWorker.register('worker.js').then(function(swr) { + registration = swr; + + // While the iframe below is a navigation, we still wait until we are + // controlled here. We want an active client to hold the service worker + // alive since it calls unregister() on itself. + return wait_until_controlled(); + + }).then(function() { + var frame = document.createElement('iframe'); + document.body.appendChild(frame); + frame.src = 'fake_download'; + + // The service worker is unregistered in the fetch event. The window and + // frame are cleaned up from the browser chrome script. + }); +}); +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/download/worker.js b/dom/workers/test/serviceworkers/download/worker.js new file mode 100644 index 000000000..fe46d1a3b --- /dev/null +++ b/dom/workers/test/serviceworkers/download/worker.js @@ -0,0 +1,30 @@ +addEventListener('install', function(evt) { + evt.waitUntil(self.skipWaiting()); +}); + +addEventListener('activate', function(evt) { + // We claim the current clients in order to ensure that we have an + // active client when we call unregister in the fetch handler. Otherwise + // the unregister() can kill the current worker before returning a + // response. + evt.waitUntil(clients.claim()); +}); + +addEventListener('fetch', function(evt) { + // This worker may live long enough to receive a fetch event from the next + // test. Just pass such requests through to the network. + if (evt.request.url.indexOf('fake_download') === -1) { + return; + } + + // We should only get a single download fetch event. Automatically unregister. + evt.respondWith(registration.unregister().then(function() { + return new Response('service worker generated download', { + headers: { + 'Content-Disposition': 'attachment; filename="fake_download.bin"', + // fake encoding header that should have no effect + 'Content-Encoding': 'gzip', + } + }); + })); +}); diff --git a/dom/workers/test/serviceworkers/empty.js b/dom/workers/test/serviceworkers/empty.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/dom/workers/test/serviceworkers/empty.js diff --git a/dom/workers/test/serviceworkers/error_reporting_helpers.js b/dom/workers/test/serviceworkers/error_reporting_helpers.js new file mode 100644 index 000000000..fbc4ca6fc --- /dev/null +++ b/dom/workers/test/serviceworkers/error_reporting_helpers.js @@ -0,0 +1,68 @@ +"use strict"; + +/** + * Helpers for use in tests that want to verify that localized error messages + * are logged during the test. Because most of our errors (ex: + * ServiceWorkerManager) generate nsIScriptError instances with flattened + * strings (the interpolated arguments aren't kept around), we load the string + * bundle and use it to derive the exact string message we expect for the given + * payload. + **/ + +let stringBundleService = + SpecialPowers.Cc["@mozilla.org/intl/stringbundle;1"] + .getService(SpecialPowers.Ci.nsIStringBundleService); +let localizer = + stringBundleService.createBundle("chrome://global/locale/dom/dom.properties"); + +/** + * Start monitoring the console for the given localized error message string(s) + * with the given arguments to be logged. Call before running code that will + * generate the console message. Pair with a call to + * `wait_for_expected_message` invoked after the message should have been + * generated. + * + * Multiple error messages can be expected, just repeat the msgId and args + * argument pair as needed. + * + * @param {String} msgId + * The localization message identifier used in the properties file. + * @param {String[]} args + * The list of formatting arguments we expect the error to be generated with. + * @return {Object} Promise/handle to pass to wait_for_expected_message. + */ +function expect_console_message(/* msgId, args, ... */) { + let expectations = []; + // process repeated paired arguments of: msgId, args + for (let i = 0; i < arguments.length; i += 2) { + let msgId = arguments[i]; + let args = arguments[i + 1]; + expectations.push({ + errorMessage: localizer.formatStringFromName(msgId, args, args.length) + }); + } + return new Promise(resolve => { + SimpleTest.monitorConsole(resolve, expectations); + }); +} +let expect_console_messages = expect_console_message; + +/** + * Stop monitoring the console, returning a Promise that will be resolved when + * the sentinel console message sent through the async data path has been + * received. The Promise will not reject on failure; instead a mochitest + * failure will have been generated by ok(false)/equivalent by the time it is + * resolved. + */ +function wait_for_expected_message(expectedPromise) { + SimpleTest.endMonitorConsole(); + return expectedPromise; +} + +/** + * Derive an absolute URL string from a relative URL to simplify error message + * argument generation. + */ +function make_absolute_url(relUrl) { + return new URL(relUrl, window.location).href; +} diff --git a/dom/workers/test/serviceworkers/eval_worker.js b/dom/workers/test/serviceworkers/eval_worker.js new file mode 100644 index 000000000..c60f2f637 --- /dev/null +++ b/dom/workers/test/serviceworkers/eval_worker.js @@ -0,0 +1 @@ +eval('1+1'); diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource.resource b/dom/workers/test/serviceworkers/eventsource/eventsource.resource new file mode 100644 index 000000000..eb62cbd4c --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource.resource @@ -0,0 +1,22 @@ +:this file must be enconded in utf8 +:and its Content-Type must be equal to text/event-stream + +retry:500 +data: 2 +unknow: unknow + +event: other_event_name +retry:500 +data: 2 +unknow: unknow + +event: click +retry:500 + +event: blur +retry:500 + +event:keypress +retry:500 + + diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource.resource^headers^ b/dom/workers/test/serviceworkers/eventsource/eventsource.resource^headers^ new file mode 100644 index 000000000..5b88be7c3 --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource.resource^headers^ @@ -0,0 +1,3 @@ +Content-Type: text/event-stream +Cache-Control: no-cache, must-revalidate +Access-Control-Allow-Origin: * diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_cors_response.html b/dom/workers/test/serviceworkers/eventsource/eventsource_cors_response.html new file mode 100644 index 000000000..7c6f7302f --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource_cors_response.html @@ -0,0 +1,75 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1182103 - Test EventSource scenarios with fetch interception</title> + <script type="text/javascript"> + + var prefix = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/eventsource/"; + + function ok(aCondition, aMessage) { + parent.postMessage({status: "callback", data: "ok", condition: aCondition, message: aMessage}, "*"); + } + + function doUnregister() { + navigator.serviceWorker.getRegistration().then(swr => { + swr.unregister().then(function(result) { + ok(result, "Unregister should return true."); + parent.postMessage({status: "callback", data: "done"}, "*"); + }, function(e) { + ok(false, "Unregistering the SW failed with " + e); + }); + }); + } + + function doEventSource() { + var source = new EventSource(prefix + "eventsource.resource"); + source.onmessage = function(e) { + source.onmessage = null; + source.close(); + ok(true, "EventSource should work with cors responses"); + doUnregister(); + }; + source.onerror = function(error) { + source.onerror = null; + source.close(); + ok(false, "Something went wrong"); + }; + } + + function onLoad() { + if (!parent) { + dump("eventsource/eventsource_cors_response.html shouldn't be launched directly!"); + } + + window.addEventListener("message", function onMessage(e) { + if (e.data.status === "callback") { + switch(e.data.data) { + case "eventsource": + doEventSource(); + window.removeEventListener("message", onMessage); + break; + default: + ok(false, "Something went wrong") + break + } + } + }); + + navigator.serviceWorker.ready.then(function() { + parent.postMessage({status: "callback", data: "ready"}, "*"); + }); + + navigator.serviceWorker.addEventListener("message", function(event) { + parent.postMessage(event.data, "*"); + }); + } + + </script> +</head> +<body onload="onLoad()"> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_cors_response_intercept_worker.js b/dom/workers/test/serviceworkers/eventsource/eventsource_cors_response_intercept_worker.js new file mode 100644 index 000000000..579e9f568 --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource_cors_response_intercept_worker.js @@ -0,0 +1,20 @@ +// Cross origin request +var prefix = 'http://example.com/tests/dom/workers/test/serviceworkers/eventsource/'; + +self.importScripts('eventsource_worker_helper.js'); + +self.addEventListener('fetch', function (event) { + var request = event.request; + var url = new URL(request.url); + + if (url.pathname !== '/tests/dom/workers/test/serviceworkers/eventsource/eventsource.resource') { + return; + } + + ok(request.mode === 'cors', 'EventSource should make a CORS request'); + ok(request.cache === 'no-store', 'EventSource should make a no-store request'); + var fetchRequest = new Request(prefix + 'eventsource.resource', { mode: 'cors'}); + event.respondWith(fetch(fetchRequest).then((fetchResponse) => { + return fetchResponse; + })); +}); diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_mixed_content_cors_response.html b/dom/workers/test/serviceworkers/eventsource/eventsource_mixed_content_cors_response.html new file mode 100644 index 000000000..f6ae0d96f --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource_mixed_content_cors_response.html @@ -0,0 +1,75 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1182103 - Test EventSource scenarios with fetch interception</title> + <script type="text/javascript"> + + var prefix = "https://example.com/tests/dom/workers/test/serviceworkers/eventsource/"; + + function ok(aCondition, aMessage) { + parent.postMessage({status: "callback", data: "ok", condition: aCondition, message: aMessage}, "*"); + } + + function doUnregister() { + navigator.serviceWorker.getRegistration().then(swr => { + swr.unregister().then(function(result) { + ok(result, "Unregister should return true."); + parent.postMessage({status: "callback", data: "done"}, "*"); + }, function(e) { + ok(false, "Unregistering the SW failed with " + e); + }); + }); + } + + function doEventSource() { + var source = new EventSource(prefix + "eventsource.resource"); + source.onmessage = function(e) { + source.onmessage = null; + source.close(); + ok(false, "Something went wrong"); + }; + source.onerror = function(error) { + source.onerror = null; + source.close(); + ok(true, "EventSource should not work with mixed content cors responses"); + doUnregister(); + }; + } + + function onLoad() { + if (!parent) { + dump("eventsource/eventsource_cors_response.html shouldn't be launched directly!"); + } + + window.addEventListener("message", function onMessage(e) { + if (e.data.status === "callback") { + switch(e.data.data) { + case "eventsource": + doEventSource(); + window.removeEventListener("message", onMessage); + break; + default: + ok(false, "Something went wrong") + break + } + } + }); + + navigator.serviceWorker.ready.then(function() { + parent.postMessage({status: "callback", data: "ready"}, "*"); + }); + + navigator.serviceWorker.addEventListener("message", function(event) { + parent.postMessage(event.data, "*"); + }); + } + + </script> +</head> +<body onload="onLoad()"> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_mixed_content_cors_response_intercept_worker.js b/dom/workers/test/serviceworkers/eventsource/eventsource_mixed_content_cors_response_intercept_worker.js new file mode 100644 index 000000000..187d0bc6f --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource_mixed_content_cors_response_intercept_worker.js @@ -0,0 +1,19 @@ +var prefix = 'http://example.com/tests/dom/workers/test/serviceworkers/eventsource/'; + +self.importScripts('eventsource_worker_helper.js'); + +self.addEventListener('fetch', function (event) { + var request = event.request; + var url = new URL(request.url); + + if (url.pathname !== '/tests/dom/workers/test/serviceworkers/eventsource/eventsource.resource') { + return; + } + + ok(request.mode === 'cors', 'EventSource should make a CORS request'); + ok(request.cache === 'no-store', 'EventSource should make a no-store request'); + var fetchRequest = new Request(prefix + 'eventsource.resource', { mode: 'cors'}); + event.respondWith(fetch(fetchRequest).then((fetchResponse) => { + return fetchResponse; + })); +}); diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_opaque_response.html b/dom/workers/test/serviceworkers/eventsource/eventsource_opaque_response.html new file mode 100644 index 000000000..f92811e63 --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource_opaque_response.html @@ -0,0 +1,75 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1182103 - Test EventSource scenarios with fetch interception</title> + <script type="text/javascript"> + + var prefix = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/eventsource/"; + + function ok(aCondition, aMessage) { + parent.postMessage({status: "callback", data: "ok", condition: aCondition, message: aMessage}, "*"); + } + + function doUnregister() { + navigator.serviceWorker.getRegistration().then(swr => { + swr.unregister().then(function(result) { + ok(result, "Unregister should return true."); + parent.postMessage({status: "callback", data: "done"}, "*"); + }, function(e) { + ok(false, "Unregistering the SW failed with " + e); + }); + }); + } + + function doEventSource() { + var source = new EventSource(prefix + "eventsource.resource"); + source.onmessage = function(e) { + source.onmessage = null; + source.close(); + ok(false, "Something went wrong"); + }; + source.onerror = function(error) { + source.onerror = null; + source.close(); + ok(true, "EventSource should not work with opaque responses"); + doUnregister(); + }; + } + + function onLoad() { + if (!parent) { + dump("eventsource/eventsource_opaque_response.html shouldn't be launched directly!"); + } + + window.addEventListener("message", function onMessage(e) { + if (e.data.status === "callback") { + switch(e.data.data) { + case "eventsource": + doEventSource(); + window.removeEventListener("message", onMessage); + break; + default: + ok(false, "Something went wrong") + break + } + } + }); + + navigator.serviceWorker.ready.then(function() { + parent.postMessage({status: "callback", data: "ready"}, "*"); + }); + + navigator.serviceWorker.addEventListener("message", function(event) { + parent.postMessage(event.data, "*"); + }); + } + + </script> +</head> +<body onload="onLoad()"> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_opaque_response_intercept_worker.js b/dom/workers/test/serviceworkers/eventsource/eventsource_opaque_response_intercept_worker.js new file mode 100644 index 000000000..45a80e324 --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource_opaque_response_intercept_worker.js @@ -0,0 +1,20 @@ +// Cross origin request +var prefix = 'http://example.com/tests/dom/workers/test/serviceworkers/eventsource/'; + +self.importScripts('eventsource_worker_helper.js'); + +self.addEventListener('fetch', function (event) { + var request = event.request; + var url = new URL(request.url); + + if (url.pathname !== '/tests/dom/workers/test/serviceworkers/eventsource/eventsource.resource') { + return; + } + + ok(request.mode === 'cors', 'EventSource should make a CORS request'); + ok(request.cache === 'no-store', 'EventSource should make a no-store request'); + var fetchRequest = new Request(prefix + 'eventsource.resource', { mode: 'no-cors'}); + event.respondWith(fetch(fetchRequest).then((fetchResponse) => { + return fetchResponse; + })); +}); diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_register_worker.html b/dom/workers/test/serviceworkers/eventsource/eventsource_register_worker.html new file mode 100644 index 000000000..59e8e92ab --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource_register_worker.html @@ -0,0 +1,27 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1182103 - Test EventSource scenarios with fetch interception</title> + <script type="text/javascript"> + + function getURLParam (aTarget, aValue) { + return decodeURI(aTarget.search.replace(new RegExp("^(?:.*[&\\?]" + encodeURI(aValue).replace(/[\.\+\*]/g, "\\$&") + "(?:\\=([^&]*))?)?.*$", "i"), "$1")); + } + + function onLoad() { + navigator.serviceWorker.ready.then(function() { + parent.postMessage({status: "callback", data: "done"}, "*"); + }); + + navigator.serviceWorker.register(getURLParam(document.location, "script"), {scope: "."}); + } + + </script> +</head> +<body onload="onLoad()"> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_synthetic_response.html b/dom/workers/test/serviceworkers/eventsource/eventsource_synthetic_response.html new file mode 100644 index 000000000..d9f380e2f --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource_synthetic_response.html @@ -0,0 +1,75 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1182103 - Test EventSource scenarios with fetch interception</title> + <script type="text/javascript"> + + var prefix = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/eventsource/"; + + function ok(aCondition, aMessage) { + parent.postMessage({status: "callback", data: "ok", condition: aCondition, message: aMessage}, "*"); + } + + function doUnregister() { + navigator.serviceWorker.getRegistration().then(swr => { + swr.unregister().then(function(result) { + ok(result, "Unregister should return true."); + parent.postMessage({status: "callback", data: "done"}, "*"); + }, function(e) { + ok(false, "Unregistering the SW failed with " + e); + }); + }); + } + + function doEventSource() { + var source = new EventSource(prefix + "eventsource.resource"); + source.onmessage = function(e) { + source.onmessage = null; + source.close(); + ok(true, "EventSource should work with synthetic responses"); + doUnregister(); + }; + source.onerror = function(error) { + source.onmessage = null; + source.close(); + ok(false, "Something went wrong"); + }; + } + + function onLoad() { + if (!parent) { + dump("eventsource/eventsource_synthetic_response.html shouldn't be launched directly!"); + } + + window.addEventListener("message", function onMessage(e) { + if (e.data.status === "callback") { + switch(e.data.data) { + case "eventsource": + doEventSource(); + window.removeEventListener("message", onMessage); + break; + default: + ok(false, "Something went wrong") + break + } + } + }); + + navigator.serviceWorker.ready.then(function() { + parent.postMessage({status: "callback", data: "ready"}, "*"); + }); + + navigator.serviceWorker.addEventListener("message", function(event) { + parent.postMessage(event.data, "*"); + }); + } + + </script> +</head> +<body onload="onLoad()"> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_synthetic_response_intercept_worker.js b/dom/workers/test/serviceworkers/eventsource/eventsource_synthetic_response_intercept_worker.js new file mode 100644 index 000000000..8692f9186 --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource_synthetic_response_intercept_worker.js @@ -0,0 +1,24 @@ +self.importScripts('eventsource_worker_helper.js'); + +self.addEventListener('fetch', function (event) { + var request = event.request; + var url = new URL(request.url); + + if (url.pathname !== '/tests/dom/workers/test/serviceworkers/eventsource/eventsource.resource') { + return; + } + + ok(request.mode === 'cors', 'EventSource should make a CORS request'); + var headerList = { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache, must-revalidate' + }; + var headers = new Headers(headerList); + var init = { + headers: headers, + mode: 'cors' + }; + var body = 'data: data0\r\r'; + var response = new Response(body, init); + event.respondWith(response); +}); diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_worker_helper.js b/dom/workers/test/serviceworkers/eventsource/eventsource_worker_helper.js new file mode 100644 index 000000000..6d5dbb024 --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource_worker_helper.js @@ -0,0 +1,12 @@ +function ok(aCondition, aMessage) { + return new Promise(function(resolve, reject) { + self.clients.matchAll().then(function(res) { + if (!res.length) { + reject(); + return; + } + res[0].postMessage({status: "callback", data: "ok", condition: aCondition, message: aMessage}); + resolve(); + }); + }); +} diff --git a/dom/workers/test/serviceworkers/fetch.js b/dom/workers/test/serviceworkers/fetch.js new file mode 100644 index 000000000..38d20a638 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch.js @@ -0,0 +1,11 @@ +addEventListener('fetch', function(event) { + if (event.request.url.indexOf("fail.html") !== -1) { + event.respondWith(fetch("hello.html", {"integrity": "abc"})); + } else if (event.request.url.indexOf("fake.html") !== -1) { + event.respondWith(fetch("hello.html")); + } +}); + +addEventListener("activate", function(event) { + event.waitUntil(clients.claim()); +}); diff --git a/dom/workers/test/serviceworkers/fetch/context/beacon.sjs b/dom/workers/test/serviceworkers/fetch/context/beacon.sjs new file mode 100644 index 000000000..8401bc29b --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/beacon.sjs @@ -0,0 +1,43 @@ +/* + * This is based on dom/tests/mochitest/beacon/beacon-originheader-handler.sjs. + */ + +function handleRequest(request, response) +{ + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Type", "text/plain", false); + + // case XHR-REQUEST: the xhr-request tries to query the + // stored context from the beacon request. + if (request.queryString == "queryContext") { + var context = getState("interceptContext"); + // if the beacon already stored the context - return. + if (context) { + response.write(context); + setState("interceptContext", ""); + return; + } + // otherwise wait for the beacon request + response.processAsync(); + setObjectState("sw-xhr-response", response); + return; + } + + // case BEACON-REQUEST: get the beacon context and + // store the context on the server. + var context = request.queryString; + setState("interceptContext", context); + + // if there is an xhr-request waiting, return the context now. + try{ + getObjectState("sw-xhr-response", function(xhrResponse) { + if (!xhrResponse) { + return; + } + setState("interceptContext", ""); + xhrResponse.write(context); + xhrResponse.finish(); + }); + } catch(e) { + } +} diff --git a/dom/workers/test/serviceworkers/fetch/context/context_test.js b/dom/workers/test/serviceworkers/fetch/context/context_test.js new file mode 100644 index 000000000..b98d2ab3c --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/context_test.js @@ -0,0 +1,135 @@ +self.addEventListener("fetch", function(event) { + if (event.request.url.indexOf("index.html") >= 0 || + event.request.url.indexOf("register.html") >= 0 || + event.request.url.indexOf("unregister.html") >= 0 || + event.request.url.indexOf("ping.html") >= 0 || + event.request.url.indexOf("xml.xml") >= 0 || + event.request.url.indexOf("csp-violate.sjs") >= 0) { + // Handle pass-through requests + event.respondWith(fetch(event.request)); + } else if (event.request.url.indexOf("fetch.txt") >= 0) { + var body = event.request.context == "fetch" ? + "so fetch" : "so unfetch"; + event.respondWith(new Response(body)); + } else if (event.request.url.indexOf("img.jpg") >= 0) { + if (event.request.context == "image") { + event.respondWith(fetch("realimg.jpg")); + } + } else if (event.request.url.indexOf("responsive.jpg") >= 0) { + if (event.request.context == "imageset") { + event.respondWith(fetch("realimg.jpg")); + } + } else if (event.request.url.indexOf("audio.ogg") >= 0) { + if (event.request.context == "audio") { + event.respondWith(fetch("realaudio.ogg")); + } + } else if (event.request.url.indexOf("video.ogg") >= 0) { + if (event.request.context == "video") { + event.respondWith(fetch("realaudio.ogg")); + } + } else if (event.request.url.indexOf("beacon.sjs") >= 0) { + if (event.request.url.indexOf("queryContext") == -1) { + event.respondWith(fetch("beacon.sjs?" + event.request.context)); + } else { + event.respondWith(fetch(event.request)); + } + } else if (event.request.url.indexOf("csp-report.sjs") >= 0) { + respondToServiceWorker(event, "csp-report"); + } else if (event.request.url.indexOf("embed") >= 0) { + respondToServiceWorker(event, "embed"); + } else if (event.request.url.indexOf("object") >= 0) { + respondToServiceWorker(event, "object"); + } else if (event.request.url.indexOf("font") >= 0) { + respondToServiceWorker(event, "font"); + } else if (event.request.url.indexOf("iframe") >= 0) { + if (event.request.context == "iframe") { + event.respondWith(fetch("context_test.js")); + } + } else if (event.request.url.indexOf("frame") >= 0) { + if (event.request.context == "frame") { + event.respondWith(fetch("context_test.js")); + } + } else if (event.request.url.indexOf("newwindow") >= 0) { + respondToServiceWorker(event, "newwindow"); + } else if (event.request.url.indexOf("ping") >= 0) { + respondToServiceWorker(event, "ping"); + } else if (event.request.url.indexOf("plugin") >= 0) { + respondToServiceWorker(event, "plugin"); + } else if (event.request.url.indexOf("script.js") >= 0) { + if (event.request.context == "script") { + event.respondWith(new Response("")); + } + } else if (event.request.url.indexOf("style.css") >= 0) { + respondToServiceWorker(event, "style"); + } else if (event.request.url.indexOf("track") >= 0) { + respondToServiceWorker(event, "track"); + } else if (event.request.url.indexOf("xhr") >= 0) { + if (event.request.context == "xmlhttprequest") { + event.respondWith(new Response("")); + } + } else if (event.request.url.indexOf("xslt") >= 0) { + respondToServiceWorker(event, "xslt"); + } else if (event.request.url.indexOf("myworker") >= 0) { + if (event.request.context == "worker") { + event.respondWith(fetch("worker.js")); + } + } else if (event.request.url.indexOf("myparentworker") >= 0) { + if (event.request.context == "worker") { + event.respondWith(fetch("parentworker.js")); + } + } else if (event.request.url.indexOf("mysharedworker") >= 0) { + if (event.request.context == "sharedworker") { + event.respondWith(fetch("sharedworker.js")); + } + } else if (event.request.url.indexOf("myparentsharedworker") >= 0) { + if (event.request.context == "sharedworker") { + event.respondWith(fetch("parentsharedworker.js")); + } + } else if (event.request.url.indexOf("cache") >= 0) { + var cache; + var origContext = event.request.context; + event.respondWith(caches.open("cache") + .then(function(c) { + cache = c; + // Store the Request in the cache. + return cache.put(event.request, new Response("fake")); + }).then(function() { + // Read it back. + return cache.keys(event.request); + }).then(function(res) { + var req = res[0]; + // Check to see if the context remained the same. + var success = req.context === origContext; + return clients.matchAll() + .then(function(clients) { + // Report it back to the main page. + clients.forEach(function(c) { + c.postMessage({data: "cache", success: success}); + }); + })}).then(function() { + // Cleanup. + return caches.delete("cache"); + }).then(function() { + return new Response("ack"); + })); + } + // Fail any request that we don't know about. + try { + event.respondWith(Promise.reject(event.request.url)); + dump("Fetch event received invalid context value " + event.request.context + + " for " + event.request.url + "\n"); + } catch(e) { + // Eat up the possible InvalidStateError exception that we may get if some + // code above has called respondWith too. + } +}); + +function respondToServiceWorker(event, data) { + event.respondWith(clients.matchAll() + .then(function(clients) { + clients.forEach(function(c) { + c.postMessage({data: data, context: event.request.context}); + }); + return new Response("ack"); + })); +} diff --git a/dom/workers/test/serviceworkers/fetch/context/csp-violate.sjs b/dom/workers/test/serviceworkers/fetch/context/csp-violate.sjs new file mode 100644 index 000000000..4c3e76d15 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/csp-violate.sjs @@ -0,0 +1,6 @@ +function handleRequest(request, response) +{ + response.setHeader("Content-Security-Policy", "default-src 'none'; report-uri /tests/dom/workers/test/serviceworkers/fetch/context/csp-report.sjs", false); + response.setHeader("Content-Type", "text/html", false); + response.write("<link rel=stylesheet href=style.css>"); +} diff --git a/dom/workers/test/serviceworkers/fetch/context/index.html b/dom/workers/test/serviceworkers/fetch/context/index.html new file mode 100644 index 000000000..c6dfef99c --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/index.html @@ -0,0 +1,422 @@ +<!DOCTYPE html> +<script> + var isAndroid = navigator.userAgent.includes("Android"); + var isB2G = !isAndroid && /Mobile|Tablet/.test(navigator.userAgent); + + function ok(v, msg) { + window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*"); + } + + function is(a, b, msg) { + ok(a === b, msg + ", expected '" + b + "', got '" + a + "'"); + } + + function todo(v, msg) { + window.parent.postMessage({status: "todo", result: !!v, message: msg}, "*"); + } + + function finish() { + window.parent.postMessage({status: "done"}, "*"); + } + + function testFetch() { + return fetch("fetch.txt").then(function(r) { + return r.text(); + }).then(function(body) { + is(body, "so fetch", "A fetch() Request should have the 'fetch' context"); + }); + } + + function testImage() { + return new Promise(function(resolve, reject) { + var img = document.createElement("img"); + img.src = "img.jpg"; + // The service worker will respond with an existing image only if the + // Request has the correct context, otherwise the Promise will get + // rejected and the test will fail. + img.onload = resolve; + img.onerror = reject; + }); + } + + function testImageSrcSet() { + return new Promise(function(resolve, reject) { + var img = document.createElement("img"); + img.srcset = "responsive.jpg 100w"; + // The service worker will respond with an existing image only if the + // Request has the correct context, otherwise the Promise will get + // rejected and the test will fail. + img.onload = resolve; + img.onerror = reject; + }); + } + + function testPicture() { + return new Promise(function(resolve, reject) { + var pic = document.createElement("picture"); + var img = document.createElement("img"); + pic.appendChild(img); + img.src = "responsive.jpg?picture"; + // The service worker will respond with an existing image only if the + // Request has the correct context, otherwise the Promise will get + // rejected and the test will fail. + img.onload = resolve; + img.onerror = reject; + }); + } + + function testAudio() { + return new Promise(function(resolve, reject) { + var audio = document.createElement("audio"); + audio.src = "audio.ogg"; + audio.preload = "metadata"; + // The service worker will respond with an existing audio only if the + // Request has the correct context, otherwise the Promise will get + // rejected and the test will fail. + audio.onloadedmetadata = resolve; + audio.onerror = reject; + }); + } + + function testVideo() { + return new Promise(function(resolve, reject) { + var video = document.createElement("video"); + video.src = "video.ogg"; + video.preload = "metadata"; + // The service worker will respond with an existing video only if the + // Request has the correct context, otherwise the Promise will get + // rejected and the test will fail. + video.onloadedmetadata = resolve; + video.onerror = reject; + }); + } + + function testBeacon() { + ok(navigator.sendBeacon("beacon.sjs"), "Sending the beacon should succeed"); + // query the context from beacon.sjs + return fetch("beacon.sjs?queryContext") + .then(function(r) { + return r.text(); + }).then(function(body) { + is(body, "beacon", "The context for the intercepted beacon should be correct"); + }); + } + + function testCSPReport() { + return new Promise(function(resolve, reject) { + var iframe = document.createElement("iframe"); + iframe.src = "csp-violate.sjs"; + document.documentElement.appendChild(iframe); + navigator.serviceWorker.addEventListener("message", function onMessage(e) { + if (e.data.data == "csp-report") { + is(e.data.context, "cspreport", "Expected the cspreport context on a CSP violation report"); + navigator.serviceWorker.removeEventListener("message", onMessage); + resolve(); + } + }, false); + }); + } + + function testEmbed() { + return Promise.resolve().then(function() { + todo(false, "<embed> tag is not currently intercepted. See Bug 1168676."); + }); + + return new Promise(function(resolve, reject) { + var embed = document.createElement("embed"); + embed.src = "embed"; + document.documentElement.appendChild(embed); + navigator.serviceWorker.addEventListener("message", function onMessage(e) { + if (e.data.data == "embed") { + is(e.data.context, "embed", "Expected the object context on an embed"); + navigator.serviceWorker.removeEventListener("message", onMessage); + resolve(); + } + }, false); + }); + } + + function testObject() { + return Promise.resolve().then(function() { + todo(false, "<object> tag is not currently intercepted. See Bug 1168676"); + }); + + return new Promise(function(resolve, reject) { + var object = document.createElement("object"); + object.data = "object"; + document.documentElement.appendChild(object); + navigator.serviceWorker.addEventListener("message", function onMessage(e) { + if (e.data.data == "object") { + is(e.data.context, "object", "Expected the object context on an object"); + navigator.serviceWorker.removeEventListener("message", onMessage); + resolve(); + } + }, false); + }); + } + + function testFont() { + return new Promise(function(resolve, reject) { + var css = '@font-face { font-family: "sw-font"; src: url("font"); }'; + css += '* { font-family: "sw-font"; }'; + var style = document.createElement("style"); + style.appendChild(document.createTextNode(css)); + document.documentElement.appendChild(style); + navigator.serviceWorker.addEventListener("message", function onMessage(e) { + if (e.data.data == "font") { + is(e.data.context, "font", "Expected the font context on an font"); + navigator.serviceWorker.removeEventListener("message", onMessage); + resolve(); + } + }, false); + }); + } + + function testIFrame() { + return new Promise(function(resolve, reject) { + var iframe = document.createElement("iframe"); + iframe.src = "iframe"; + document.documentElement.appendChild(iframe); + // The service worker will respond with an existing document only if the + // Request has the correct context, otherwise the Promise will get + // rejected and the test will fail. + iframe.onload = resolve; + iframe.onerror = reject; + }); + } + + function testFrame() { + return new Promise(function(resolve, reject) { + var frame = document.createElement("frame"); + frame.src = "frame"; + document.documentElement.appendChild(frame); + // The service worker will respond with an existing document only if the + // Request has the correct context, otherwise the Promise will get + // rejected and the test will fail. + frame.onload = resolve; + frame.onerror = reject; + }); + } + + function testInternal() { + if (isB2G) { + // We can't open new windows on b2g, so skip this part of the test there. + return Promise.resolve(); + } + return new Promise(function(resolve, reject) { + // Test this with a new window opened through script. There are of course + // other possible ways of testing this too. + var win = window.open("newwindow", "_blank", "width=100,height=100"); + navigator.serviceWorker.addEventListener("message", function onMessage(e) { + if (e.data.data == "newwindow") { + is(e.data.context, "internal", "Expected the internal context on a newly opened window"); + navigator.serviceWorker.removeEventListener("message", onMessage); + win.close(); + resolve(); + } + }, false); + }); + } + + function testPing() { + return new Promise(function(resolve, reject) { + var iframe = document.createElement("iframe"); + iframe.src = "ping.html"; + document.documentElement.appendChild(iframe); + navigator.serviceWorker.addEventListener("message", function onMessage(e) { + if (e.data.data == "ping") { + is(e.data.context, "ping", "Expected the ping context on an anchor ping"); + navigator.serviceWorker.removeEventListener("message", onMessage); + resolve(); + } + }, false); + }); + } + + function testPlugin() { + return Promise.resolve().then(function() { + todo(false, "plugins are not currently intercepted. See Bug 1168676."); + }); + + var isMobile = /Mobile|Tablet/.test(navigator.userAgent); + if (isMobile || parent.isMulet()) { + // We can't use plugins on mobile, so skip this part of the test there. + return Promise.resolve(); + } + + return new Promise(function(resolve, reject) { + var embed = document.createElement("embed"); + embed.type = "application/x-test"; + embed.setAttribute("posturl", "plugin"); + embed.setAttribute("postmode", "stream"); + embed.setAttribute("streammode", "normal"); + embed.setAttribute("src", "fetch.txt"); + document.documentElement.appendChild(embed); + navigator.serviceWorker.addEventListener("message", function onMessage(e) { + if (e.data.data == "plugin") { + is(e.data.context, "plugin", "Expected the plugin context on a request coming from a plugin"); + navigator.serviceWorker.removeEventListener("message", onMessage); + // Without this, the test leaks in e10s! + embed.parentNode.removeChild(embed); + resolve(); + } + }, false); + }); + } + + function testScript() { + return new Promise(function(resolve, reject) { + var script = document.createElement("script"); + script.src = "script.js"; + document.documentElement.appendChild(script); + // The service worker will respond with an existing script only if the + // Request has the correct context, otherwise the Promise will get + // rejected and the test will fail. + script.onload = resolve; + script.onerror = reject; + }); + } + + function testStyle() { + return new Promise(function(resolve, reject) { + var link = document.createElement("link"); + link.rel = "stylesheet"; + link.href = "style.css"; + document.documentElement.appendChild(link); + navigator.serviceWorker.addEventListener("message", function onMessage(e) { + if (e.data.data == "style") { + is(e.data.context, "style", "Expected the style context on a request coming from a stylesheet"); + navigator.serviceWorker.removeEventListener("message", onMessage); + resolve(); + } + }, false); + }); + } + + function testTrack() { + return new Promise(function(resolve, reject) { + var video = document.createElement("video"); + var track = document.createElement("track"); + track.src = "track"; + video.appendChild(track); + document.documentElement.appendChild(video); + navigator.serviceWorker.addEventListener("message", function onMessage(e) { + if (e.data.data == "track") { + is(e.data.context, "track", "Expected the track context on a request coming from a track"); + navigator.serviceWorker.removeEventListener("message", onMessage); + resolve(); + } + }, false); + }); + } + + function testXHR() { + return new Promise(function(resolve, reject) { + var xhr = new XMLHttpRequest(); + xhr.open("get", "xhr", true); + xhr.send(); + // The service worker will respond with an existing resource only if the + // Request has the correct context, otherwise the Promise will get + // rejected and the test will fail. + xhr.onload = resolve; + xhr.onerror = reject; + }); + } + + function testXSLT() { + return new Promise(function(resolve, reject) { + var iframe = document.createElement("iframe"); + iframe.src = "xml.xml"; + document.documentElement.appendChild(iframe); + navigator.serviceWorker.addEventListener("message", function onMessage(e) { + if (e.data.data == "xslt") { + is(e.data.context, "xslt", "Expected the xslt context on an XSLT stylesheet"); + navigator.serviceWorker.removeEventListener("message", onMessage); + // Without this, the test leaks in e10s! + iframe.parentNode.removeChild(iframe); + resolve(); + } + }, false); + }); + } + + function testWorker() { + return new Promise(function(resolve, reject) { + var worker = new Worker("myworker"); + worker.onmessage = function(e) { + if (e.data == "ack") { + worker.terminate(); + resolve(); + } + }; + worker.onerror = reject; + }); + } + + function testNestedWorker() { + return new Promise(function(resolve, reject) { + var worker = new Worker("myparentworker"); + worker.onmessage = function(e) { + if (e.data == "ack") { + worker.terminate(); + resolve(); + } + }; + worker.onerror = reject; + }); + } + + function testSharedWorker() { + return new Promise(function(resolve, reject) { + var worker = new SharedWorker("mysharedworker"); + worker.port.start(); + worker.port.onmessage = function(e) { + if (e.data == "ack") { + resolve(); + } + }; + worker.onerror = reject; + }); + } + + function testNestedWorkerInSharedWorker() { + return new Promise(function(resolve, reject) { + var worker = new SharedWorker("myparentsharedworker"); + worker.port.start(); + worker.port.onmessage = function(e) { + if (e.data == "ack") { + resolve(); + } + }; + worker.onerror = reject; + }); + } + + function testCache() { + return new Promise(function(resolve, reject) { + // Issue an XHR that will be intercepted by the SW in order to start off + // the test with a RequestContext value that is not the default ("fetch"). + // This needs to run inside a fetch event handler because synthesized + // RequestContext objects can only have the "fetch" context, and we'd + // prefer to test the more general case of some other RequestContext value. + var xhr = new XMLHttpRequest(); + xhr.open("get", "cache", true); + xhr.send(); + navigator.serviceWorker.addEventListener("message", function onMessage(e) { + if (e.data.data == "cache") { + ok(e.data.success, "The RequestContext can be persisted in the cache."); + navigator.serviceWorker.removeEventListener("message", onMessage); + resolve(); + } + }, false); + }); + } + + var testName = location.search.substr(1); + window[testName]().then(function() { + finish(); + }, function(e) { + ok(false, "A promise was rejected: " + e); + finish(); + }); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/context/parentsharedworker.js b/dom/workers/test/serviceworkers/fetch/context/parentsharedworker.js new file mode 100644 index 000000000..eac8d5e71 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/parentsharedworker.js @@ -0,0 +1,8 @@ +onconnect = function(e) { + e.ports[0].start(); + var worker = new Worker("myworker?shared"); + worker.onmessage = function(e2) { + e.ports[0].postMessage(e2.data); + self.close(); + }; +}; diff --git a/dom/workers/test/serviceworkers/fetch/context/parentworker.js b/dom/workers/test/serviceworkers/fetch/context/parentworker.js new file mode 100644 index 000000000..839fb6640 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/parentworker.js @@ -0,0 +1,4 @@ +var worker = new Worker("myworker"); +worker.onmessage = function(e) { + postMessage(e.data); +}; diff --git a/dom/workers/test/serviceworkers/fetch/context/ping.html b/dom/workers/test/serviceworkers/fetch/context/ping.html new file mode 100644 index 000000000..b1bebe41e --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/ping.html @@ -0,0 +1,7 @@ +<!DOCTYPE html> +<script> + onload = function() { + document.querySelector("a").click(); + }; +</script> +<a ping="ping" href="fetch.txt">link</a> diff --git a/dom/workers/test/serviceworkers/fetch/context/realaudio.ogg b/dom/workers/test/serviceworkers/fetch/context/realaudio.ogg Binary files differnew file mode 100644 index 000000000..1a41623f8 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/realaudio.ogg diff --git a/dom/workers/test/serviceworkers/fetch/context/realimg.jpg b/dom/workers/test/serviceworkers/fetch/context/realimg.jpg Binary files differnew file mode 100644 index 000000000..5b920f7c0 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/realimg.jpg diff --git a/dom/workers/test/serviceworkers/fetch/context/register.html b/dom/workers/test/serviceworkers/fetch/context/register.html new file mode 100644 index 000000000..6528d0eae --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/register.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<script> + function ok(v, msg) { + window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*"); + } + + function done(reg) { + ok(reg.active, "The active worker should be available."); + window.parent.postMessage({status: "registrationdone"}, "*"); + } + + navigator.serviceWorker.ready.then(done); + navigator.serviceWorker.register("context_test.js", {scope: "."}); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/context/sharedworker.js b/dom/workers/test/serviceworkers/fetch/context/sharedworker.js new file mode 100644 index 000000000..94dca5839 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/sharedworker.js @@ -0,0 +1,5 @@ +onconnect = function(e) { + e.ports[0].start(); + e.ports[0].postMessage("ack"); + self.close(); +}; diff --git a/dom/workers/test/serviceworkers/fetch/context/unregister.html b/dom/workers/test/serviceworkers/fetch/context/unregister.html new file mode 100644 index 000000000..1f13508fa --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/unregister.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<script> + navigator.serviceWorker.getRegistration(".").then(function(registration) { + registration.unregister().then(function(success) { + if (success) { + window.parent.postMessage({status: "unregistrationdone"}, "*"); + } + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + }); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/context/worker.js b/dom/workers/test/serviceworkers/fetch/context/worker.js new file mode 100644 index 000000000..e26e5bc69 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/worker.js @@ -0,0 +1 @@ +postMessage("ack"); diff --git a/dom/workers/test/serviceworkers/fetch/context/xml.xml b/dom/workers/test/serviceworkers/fetch/context/xml.xml new file mode 100644 index 000000000..69c64adf1 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/xml.xml @@ -0,0 +1,3 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/xsl" href="xslt"?> +<root/> diff --git a/dom/workers/test/serviceworkers/fetch/deliver-gzip.sjs b/dom/workers/test/serviceworkers/fetch/deliver-gzip.sjs new file mode 100644 index 000000000..abacdd2ad --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/deliver-gzip.sjs @@ -0,0 +1,17 @@ +function handleRequest(request, response) { + // The string "hello" repeated 10 times followed by newline. Compressed using gzip. + var bytes = [0x1f, 0x8b, 0x08, 0x08, 0x4d, 0xe2, 0xf9, 0x54, 0x00, 0x03, 0x68, + 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0xcb, 0x48, 0xcd, 0xc9, 0xc9, 0xcf, + 0x20, 0x85, 0xe0, 0x02, 0x00, 0xf5, 0x4b, 0x38, 0xcf, 0x33, 0x00, + 0x00, 0x00]; + + response.setHeader("Content-Encoding", "gzip", false); + response.setHeader("Content-Length", "" + bytes.length, false); + response.setHeader("Content-Type", "text/plain", false); + + var bos = Components.classes["@mozilla.org/binaryoutputstream;1"] + .createInstance(Components.interfaces.nsIBinaryOutputStream); + bos.setOutputStream(response.bodyOutputStream); + + bos.writeByteArray(bytes, bytes.length); +} diff --git a/dom/workers/test/serviceworkers/fetch/fetch_tests.js b/dom/workers/test/serviceworkers/fetch/fetch_tests.js new file mode 100644 index 000000000..54da1b66e --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/fetch_tests.js @@ -0,0 +1,416 @@ +var origin = 'http://mochi.test:8888'; + +function fetchXHRWithMethod(name, method, onload, onerror, headers) { + expectAsyncResult(); + + onload = onload || function() { + my_ok(false, "XHR load should not complete successfully"); + finish(); + }; + onerror = onerror || function() { + my_ok(false, "XHR load for " + name + " should be intercepted successfully"); + finish(); + }; + + var x = new XMLHttpRequest(); + x.open(method, name, true); + x.onload = function() { onload(x) }; + x.onerror = function() { onerror(x) }; + headers = headers || []; + headers.forEach(function(header) { + x.setRequestHeader(header[0], header[1]); + }); + x.send(); +} + +var corsServerPath = '/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs'; +var corsServerURL = 'http://example.com' + corsServerPath; + +function redirectURL(hops) { + return hops[0].server + corsServerPath + "?hop=1&hops=" + + encodeURIComponent(hops.toSource()); +} + +function fetchXHR(name, onload, onerror, headers) { + return fetchXHRWithMethod(name, 'GET', onload, onerror, headers); +} + +fetchXHR('bare-synthesized.txt', function(xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok(xhr.responseText == "synthesized response body", "load should have synthesized response"); + finish(); +}); + +fetchXHR('test-respondwith-response.txt', function(xhr) { + my_ok(xhr.status == 200, "test-respondwith-response load should be successful"); + my_ok(xhr.responseText == "test-respondwith-response response body", "load should have response"); + finish(); +}); + +fetchXHR('synthesized-404.txt', function(xhr) { + my_ok(xhr.status == 404, "load should 404"); + my_ok(xhr.responseText == "synthesized response body", "404 load should have synthesized response"); + finish(); +}); + +fetchXHR('synthesized-headers.txt', function(xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok(xhr.getResponseHeader("X-Custom-Greeting") === "Hello", "custom header should be set"); + my_ok(xhr.responseText == "synthesized response body", "custom header load should have synthesized response"); + finish(); +}); + +fetchXHR('synthesized-redirect-real-file.txt', function(xhr) { +dump("Got status AARRGH " + xhr.status + " " + xhr.responseText + "\n"); + my_ok(xhr.status == 200, "load should be successful"); + my_ok(xhr.responseText == "This is a real file.\n", "Redirect to real file should complete."); + finish(); +}); + +fetchXHR('synthesized-redirect-twice-real-file.txt', function(xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok(xhr.responseText == "This is a real file.\n", "Redirect to real file (twice) should complete."); + finish(); +}); + +fetchXHR('synthesized-redirect-synthesized.txt', function(xhr) { + my_ok(xhr.status == 200, "synth+redirect+synth load should be successful"); + my_ok(xhr.responseText == "synthesized response body", "load should have redirected+synthesized response"); + finish(); +}); + +fetchXHR('synthesized-redirect-twice-synthesized.txt', function(xhr) { + my_ok(xhr.status == 200, "synth+redirect+synth (twice) load should be successful"); + my_ok(xhr.responseText == "synthesized response body", "load should have redirected+synthesized (twice) response"); + finish(); +}); + +fetchXHR('redirect.sjs', function(xhr) { + my_ok(xhr.status == 404, "redirected load should be uninterrupted"); + finish(); +}); + +fetchXHR('ignored.txt', function(xhr) { + my_ok(xhr.status == 404, "load should be uninterrupted"); + finish(); +}); + +fetchXHR('rejected.txt', null, function(xhr) { + my_ok(xhr.status == 0, "load should not complete"); + finish(); +}); + +fetchXHR('nonresponse.txt', null, function(xhr) { + my_ok(xhr.status == 0, "load should not complete"); + finish(); +}); + +fetchXHR('nonresponse2.txt', null, function(xhr) { + my_ok(xhr.status == 0, "load should not complete"); + finish(); +}); + +fetchXHR('nonpromise.txt', null, function(xhr) { + my_ok(xhr.status == 0, "load should not complete"); + finish(); +}); + +fetchXHR('headers.txt', function(xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok(xhr.responseText == "1", "request header checks should have passed"); + finish(); +}, null, [["X-Test1", "header1"], ["X-Test2", "header2"]]); + +fetchXHR('http://user:pass@mochi.test:8888/user-pass', function(xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok(xhr.responseText == 'http://user:pass@mochi.test:8888/user-pass', 'The username and password should be preserved'); + finish(); +}); + +var expectedUncompressedResponse = ""; +for (var i = 0; i < 10; ++i) { + expectedUncompressedResponse += "hello"; +} +expectedUncompressedResponse += "\n"; + +// ServiceWorker does not intercept, at which point the network request should +// be correctly decoded. +fetchXHR('deliver-gzip.sjs', function(xhr) { + my_ok(xhr.status == 200, "network gzip load should be successful"); + my_ok(xhr.responseText == expectedUncompressedResponse, "network gzip load should have synthesized response."); + my_ok(xhr.getResponseHeader("Content-Encoding") == "gzip", "network Content-Encoding should be gzip."); + my_ok(xhr.getResponseHeader("Content-Length") == "35", "network Content-Length should be of original gzipped file."); + finish(); +}); + +fetchXHR('hello.gz', function(xhr) { + my_ok(xhr.status == 200, "gzip load should be successful"); + my_ok(xhr.responseText == expectedUncompressedResponse, "gzip load should have synthesized response."); + my_ok(xhr.getResponseHeader("Content-Encoding") == "gzip", "Content-Encoding should be gzip."); + my_ok(xhr.getResponseHeader("Content-Length") == "35", "Content-Length should be of original gzipped file."); + finish(); +}); + +fetchXHR('hello-after-extracting.gz', function(xhr) { + my_ok(xhr.status == 200, "gzip load after extracting should be successful"); + my_ok(xhr.responseText == expectedUncompressedResponse, "gzip load after extracting should have synthesized response."); + my_ok(xhr.getResponseHeader("Content-Encoding") == "gzip", "Content-Encoding after extracting should be gzip."); + my_ok(xhr.getResponseHeader("Content-Length") == "35", "Content-Length after extracting should be of original gzipped file."); + finish(); +}); + +fetchXHR(corsServerURL + '?status=200&allowOrigin=*', function(xhr) { + my_ok(xhr.status == 200, "cross origin load with correct headers should be successful"); + my_ok(xhr.getResponseHeader("access-control-allow-origin") == null, "cors headers should be filtered out"); + finish(); +}); + +// Verify origin header is sent properly even when we have a no-intercept SW. +var uriOrigin = encodeURIComponent(origin); +fetchXHR('http://example.org' + corsServerPath + '?ignore&status=200&origin=' + uriOrigin + + '&allowOrigin=' + uriOrigin, function(xhr) { + my_ok(xhr.status == 200, "cross origin load with correct headers should be successful"); + my_ok(xhr.getResponseHeader("access-control-allow-origin") == null, "cors headers should be filtered out"); + finish(); +}); + +// Verify that XHR is considered CORS tainted even when original URL is same-origin +// redirected to cross-origin. +fetchXHR(redirectURL([{ server: origin }, + { server: 'http://example.org', + allowOrigin: origin }]), function(xhr) { + my_ok(xhr.status == 200, "cross origin load with correct headers should be successful"); + my_ok(xhr.getResponseHeader("access-control-allow-origin") == null, "cors headers should be filtered out"); + finish(); +}); + +// Test that CORS preflight requests cannot be intercepted. Performs a +// cross-origin XHR that the SW chooses not to intercept. This requires a +// preflight request, which the SW must not be allowed to intercept. +fetchXHR(corsServerURL + '?status=200&allowOrigin=*', null, function(xhr) { + my_ok(xhr.status == 0, "cross origin load with incorrect headers should be a failure"); + finish(); +}, [["X-Unsafe", "unsafe"]]); + +// Test that CORS preflight requests cannot be intercepted. Performs a +// cross-origin XHR that the SW chooses to intercept and respond with a +// cross-origin fetch. This requires a preflight request, which the SW must not +// be allowed to intercept. +fetchXHR('http://example.org' + corsServerPath + '?status=200&allowOrigin=*', null, function(xhr) { + my_ok(xhr.status == 0, "cross origin load with incorrect headers should be a failure"); + finish(); +}, [["X-Unsafe", "unsafe"]]); + +// Test that when the page fetches a url the controlling SW forces a redirect to +// another location. This other location fetch should also be intercepted by +// the SW. +fetchXHR('something.txt', function(xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok(xhr.responseText == "something else response body", "load should have something else"); + finish(); +}); + +// Test fetch will internally get it's SkipServiceWorker flag set. The request is +// made from the SW through fetch(). fetch() fetches a server-side JavaScript +// file that force a redirect. The redirect location fetch does not go through +// the SW. +fetchXHR('redirect_serviceworker.sjs', function(xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok(xhr.responseText == "// empty worker, always succeed!\n", "load should have redirection content"); + finish(); +}); + +fetchXHR('empty-header', function(xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok(xhr.responseText == "emptyheader", "load should have the expected content"); + finish(); +}, null, [["emptyheader", ""]]); + +expectAsyncResult(); +fetch('http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200&allowOrigin=*') +.then(function(res) { + my_ok(res.ok, "Valid CORS request should receive valid response"); + my_ok(res.type == "cors", "Response type should be CORS"); + res.text().then(function(body) { + my_ok(body === "<res>hello pass</res>\n", "cors response body should match"); + finish(); + }); +}, function(e) { + my_ok(false, "CORS Fetch failed"); + finish(); +}); + +expectAsyncResult(); +fetch('http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200', { mode: 'no-cors' }) +.then(function(res) { + my_ok(res.type == "opaque", "Response type should be opaque"); + my_ok(res.status == 0, "Status should be 0"); + res.text().then(function(body) { + my_ok(body === "", "opaque response body should be empty"); + finish(); + }); +}, function(e) { + my_ok(false, "no-cors Fetch failed"); + finish(); +}); + +expectAsyncResult(); +fetch('opaque-on-same-origin') +.then(function(res) { + my_ok(false, "intercepted opaque response for non no-cors request should fail."); + finish(); +}, function(e) { + my_ok(true, "intercepted opaque response for non no-cors request should fail."); + finish(); +}); + +expectAsyncResult(); +fetch('http://example.com/opaque-no-cors', { mode: "no-cors" }) +.then(function(res) { + my_ok(res.type == "opaque", "intercepted opaque response for no-cors request should have type opaque."); + finish(); +}, function(e) { + my_ok(false, "intercepted opaque response for no-cors request should pass."); + finish(); +}); + +expectAsyncResult(); +fetch('http://example.com/cors-for-no-cors', { mode: "no-cors" }) +.then(function(res) { + my_ok(res.type == "opaque", "intercepted non-opaque response for no-cors request should resolve to opaque response."); + finish(); +}, function(e) { + my_ok(false, "intercepted non-opaque response for no-cors request should resolve to opaque response. It should not fail."); + finish(); +}); + +function arrayBufferFromString(str) { + var arr = new Uint8Array(str.length); + for (var i = 0; i < str.length; ++i) { + arr[i] = str.charCodeAt(i); + } + return arr; +} + +expectAsyncResult(); +fetch(new Request('body-simple', {method: 'POST', body: 'my body'})) +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body == 'my bodymy body', "the body of the intercepted fetch should be visible in the SW"); + finish(); +}); + +expectAsyncResult(); +fetch(new Request('body-arraybufferview', {method: 'POST', body: arrayBufferFromString('my body')})) +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body == 'my bodymy body', "the ArrayBufferView body of the intercepted fetch should be visible in the SW"); + finish(); +}); + +expectAsyncResult(); +fetch(new Request('body-arraybuffer', {method: 'POST', body: arrayBufferFromString('my body').buffer})) +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body == 'my bodymy body', "the ArrayBuffer body of the intercepted fetch should be visible in the SW"); + finish(); +}); + +expectAsyncResult(); +var usp = new URLSearchParams(); +usp.set("foo", "bar"); +usp.set("baz", "qux"); +fetch(new Request('body-urlsearchparams', {method: 'POST', body: usp})) +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body == 'foo=bar&baz=quxfoo=bar&baz=qux', "the URLSearchParams body of the intercepted fetch should be visible in the SW"); + finish(); +}); + +expectAsyncResult(); +var fd = new FormData(); +fd.set("foo", "bar"); +fd.set("baz", "qux"); +fetch(new Request('body-formdata', {method: 'POST', body: fd})) +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body.indexOf("Content-Disposition: form-data; name=\"foo\"\r\n\r\nbar") < + body.indexOf("Content-Disposition: form-data; name=\"baz\"\r\n\r\nqux"), + "the FormData body of the intercepted fetch should be visible in the SW"); + finish(); +}); + +expectAsyncResult(); +fetch(new Request('body-blob', {method: 'POST', body: new Blob(new String('my body'))})) +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body == 'my bodymy body', "the Blob body of the intercepted fetch should be visible in the SW"); + finish(); +}); + +expectAsyncResult(); +fetch('interrupt.sjs') +.then(function(res) { + my_ok(true, "interrupted fetch succeeded"); + res.text().then(function(body) { + my_ok(false, "interrupted fetch shouldn't have complete body"); + finish(); + }, + function() { + my_ok(true, "interrupted fetch shouldn't have complete body"); + finish(); + }) +}, function(e) { + my_ok(false, "interrupted fetch failed"); + finish(); +}); + +['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'].forEach(function(method) { + fetchXHRWithMethod('xhr-method-test.txt', method, function(xhr) { + my_ok(xhr.status == 200, method + " load should be successful"); + my_ok(xhr.responseText == ("intercepted " + method), method + " load should have synthesized response"); + finish(); + }); +}); + +expectAsyncResult(); +fetch(new Request('empty-header', {headers:{"emptyheader":""}})) +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body == "emptyheader", "The empty header was observed in the fetch event"); + finish(); +}, function(err) { + my_ok(false, "A promise was rejected with " + err); + finish(); +}); + +expectAsyncResult(); +fetch('fetchevent-extendable') +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body == "extendable", "FetchEvent inherits from ExtendableEvent"); + finish(); +}, function(err) { + my_ok(false, "A promise was rejected with " + err); + finish(); +}); + +expectAsyncResult(); +fetch('fetchevent-request') +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body == "non-nullable", "FetchEvent.request must be non-nullable"); + finish(); +}, function(err) { + my_ok(false, "A promise was rejected with " + err); + finish(); +}); diff --git a/dom/workers/test/serviceworkers/fetch/fetch_worker_script.js b/dom/workers/test/serviceworkers/fetch/fetch_worker_script.js new file mode 100644 index 000000000..61efb647c --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/fetch_worker_script.js @@ -0,0 +1,29 @@ +function my_ok(v, msg) { + postMessage({type: "ok", value: v, msg: msg}); +} + +function finish() { + postMessage('finish'); +} + +function expectAsyncResult() { + postMessage('expect'); +} + +expectAsyncResult(); +try { + var success = false; + importScripts("nonexistent_imported_script.js"); +} catch(x) { +} + +my_ok(success, "worker imported script should be intercepted"); +finish(); + +function check_intercepted_script() { + success = true; +} + +importScripts('fetch_tests.js') + +finish(); //corresponds to the gExpected increment before creating this worker diff --git a/dom/workers/test/serviceworkers/fetch/hsts/embedder.html b/dom/workers/test/serviceworkers/fetch/hsts/embedder.html new file mode 100644 index 000000000..c98555423 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/hsts/embedder.html @@ -0,0 +1,7 @@ +<!DOCTYPE html> +<script> + window.onmessage = function(e) { + window.parent.postMessage(e.data, "*"); + }; +</script> +<iframe src="http://example.com/tests/dom/workers/test/serviceworkers/fetch/hsts/index.html"></iframe> diff --git a/dom/workers/test/serviceworkers/fetch/hsts/hsts_test.js b/dom/workers/test/serviceworkers/fetch/hsts/hsts_test.js new file mode 100644 index 000000000..ab54164ed --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/hsts/hsts_test.js @@ -0,0 +1,11 @@ +self.addEventListener("fetch", function(event) { + if (event.request.url.indexOf("index.html") >= 0) { + event.respondWith(fetch("realindex.html")); + } else if (event.request.url.indexOf("image-20px.png") >= 0) { + if (event.request.url.indexOf("https://") == 0) { + event.respondWith(fetch("image-40px.png")); + } else { + event.respondWith(Response.error()); + } + } +}); diff --git a/dom/workers/test/serviceworkers/fetch/hsts/image-20px.png b/dom/workers/test/serviceworkers/fetch/hsts/image-20px.png Binary files differnew file mode 100644 index 000000000..ae6a8a6b8 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/hsts/image-20px.png diff --git a/dom/workers/test/serviceworkers/fetch/hsts/image-40px.png b/dom/workers/test/serviceworkers/fetch/hsts/image-40px.png Binary files differnew file mode 100644 index 000000000..fe391dc8a --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/hsts/image-40px.png diff --git a/dom/workers/test/serviceworkers/fetch/hsts/image.html b/dom/workers/test/serviceworkers/fetch/hsts/image.html new file mode 100644 index 000000000..cadbdef5a --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/hsts/image.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<script> +onload=function(){ + var img = new Image(); + img.src = "http://example.com/tests/dom/workers/test/serviceworkers/fetch/hsts/image-20px.png"; + img.onload = function() { + window.parent.postMessage({status: "image", data: img.width}, "*"); + }; + img.onerror = function() { + window.parent.postMessage({status: "image", data: "error"}, "*"); + }; +}; +</script> diff --git a/dom/workers/test/serviceworkers/fetch/hsts/realindex.html b/dom/workers/test/serviceworkers/fetch/hsts/realindex.html new file mode 100644 index 000000000..b3d1d527e --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/hsts/realindex.html @@ -0,0 +1,8 @@ +<!DOCTYPE html> +<script> + var securityInfoPresent = !!SpecialPowers.wrap(document).docShell.currentDocumentChannel.securityInfo; + window.parent.postMessage({status: "protocol", + data: location.protocol, + securityInfoPresent: securityInfoPresent}, + "*"); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/hsts/register.html b/dom/workers/test/serviceworkers/fetch/hsts/register.html new file mode 100644 index 000000000..bcdc146ae --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/hsts/register.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<script> + function ok(v, msg) { + window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*"); + } + + function done(reg) { + ok(reg.active, "The active worker should be available."); + window.parent.postMessage({status: "registrationdone"}, "*"); + } + + navigator.serviceWorker.ready.then(done); + navigator.serviceWorker.register("hsts_test.js", {scope: "."}); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/hsts/register.html^headers^ b/dom/workers/test/serviceworkers/fetch/hsts/register.html^headers^ new file mode 100644 index 000000000..a46bf65bd --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/hsts/register.html^headers^ @@ -0,0 +1,2 @@ +Cache-Control: no-cache +Strict-Transport-Security: max-age=60 diff --git a/dom/workers/test/serviceworkers/fetch/hsts/unregister.html b/dom/workers/test/serviceworkers/fetch/hsts/unregister.html new file mode 100644 index 000000000..1f13508fa --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/hsts/unregister.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<script> + navigator.serviceWorker.getRegistration(".").then(function(registration) { + registration.unregister().then(function(success) { + if (success) { + window.parent.postMessage({status: "unregistrationdone"}, "*"); + } + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + }); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/https/clonedresponse/https_test.js b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/https_test.js new file mode 100644 index 000000000..48f7b9307 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/https_test.js @@ -0,0 +1,15 @@ +self.addEventListener("install", function(event) { + event.waitUntil(caches.open("cache").then(function(cache) { + return cache.add("index.html"); + })); +}); + +self.addEventListener("fetch", function(event) { + if (event.request.url.indexOf("index.html") >= 0) { + event.respondWith(new Promise(function(resolve, reject) { + caches.match(event.request).then(function(response) { + resolve(response.clone()); + }); + })); + } +}); diff --git a/dom/workers/test/serviceworkers/fetch/https/clonedresponse/index.html b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/index.html new file mode 100644 index 000000000..a43554844 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/index.html @@ -0,0 +1,4 @@ +<!DOCTYPE html> +<script> + window.parent.postMessage({status: "done"}, "*"); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/https/clonedresponse/register.html b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/register.html new file mode 100644 index 000000000..41774f70d --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/register.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<script> + function ok(v, msg) { + window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*"); + } + + function done(reg) { + ok(reg.active, "The active worker should be available."); + window.parent.postMessage({status: "registrationdone"}, "*"); + } + + navigator.serviceWorker.ready.then(done); + navigator.serviceWorker.register("https_test.js", {scope: "."}); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/https/clonedresponse/unregister.html b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/unregister.html new file mode 100644 index 000000000..1f13508fa --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/unregister.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<script> + navigator.serviceWorker.getRegistration(".").then(function(registration) { + registration.unregister().then(function(success) { + if (success) { + window.parent.postMessage({status: "unregistrationdone"}, "*"); + } + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + }); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/https/https_test.js b/dom/workers/test/serviceworkers/fetch/https/https_test.js new file mode 100644 index 000000000..6f87bb5ee --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/https_test.js @@ -0,0 +1,23 @@ +self.addEventListener("install", function(event) { + event.waitUntil(caches.open("cache").then(function(cache) { + var synth = new Response('<!DOCTYPE html><script>window.parent.postMessage({status: "done-synth-sw"}, "*");</script>', + {headers:{"Content-Type": "text/html"}}); + return Promise.all([ + cache.add("index.html"), + cache.put("synth-sw.html", synth), + ]); + })); +}); + +self.addEventListener("fetch", function(event) { + if (event.request.url.indexOf("index.html") >= 0) { + event.respondWith(caches.match(event.request)); + } else if (event.request.url.indexOf("synth-sw.html") >= 0) { + event.respondWith(caches.match(event.request)); + } else if (event.request.url.indexOf("synth-window.html") >= 0) { + event.respondWith(caches.match(event.request)); + } else if (event.request.url.indexOf("synth.html") >= 0) { + event.respondWith(new Response('<!DOCTYPE html><script>window.parent.postMessage({status: "done-synth"}, "*");</script>', + {headers:{"Content-Type": "text/html"}})); + } +}); diff --git a/dom/workers/test/serviceworkers/fetch/https/index.html b/dom/workers/test/serviceworkers/fetch/https/index.html new file mode 100644 index 000000000..a43554844 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/index.html @@ -0,0 +1,4 @@ +<!DOCTYPE html> +<script> + window.parent.postMessage({status: "done"}, "*"); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/https/register.html b/dom/workers/test/serviceworkers/fetch/https/register.html new file mode 100644 index 000000000..fa666fe95 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/register.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<script> + function ok(v, msg) { + window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*"); + } + + function done(reg) { + ok(reg.active, "The active worker should be available."); + window.parent.postMessage({status: "registrationdone"}, "*"); + } + + navigator.serviceWorker.ready.then(reg => { + return window.caches.open("cache").then(function(cache) { + var synth = new Response('<!DOCTYPE html><script>window.parent.postMessage({status: "done-synth-window"}, "*");</scri' + 'pt>', + {headers:{"Content-Type": "text/html"}}); + return cache.put('synth-window.html', synth).then(_ => done(reg)); + }); + }); + navigator.serviceWorker.register("https_test.js", {scope: "."}); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/https/unregister.html b/dom/workers/test/serviceworkers/fetch/https/unregister.html new file mode 100644 index 000000000..1f13508fa --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/unregister.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<script> + navigator.serviceWorker.getRegistration(".").then(function(registration) { + registration.unregister().then(function(success) { + if (success) { + window.parent.postMessage({status: "unregistrationdone"}, "*"); + } + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + }); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-20px.png b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-20px.png Binary files differnew file mode 100644 index 000000000..ae6a8a6b8 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-20px.png diff --git a/dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-40px.png b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-40px.png Binary files differnew file mode 100644 index 000000000..fe391dc8a --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-40px.png diff --git a/dom/workers/test/serviceworkers/fetch/imagecache-maxage/index.html b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/index.html new file mode 100644 index 000000000..426c27a73 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/index.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<script> +var width, url, width2, url2; +function maybeReport() { + if (width !== undefined && url !== undefined && + width2 !== undefined && url2 !== undefined) { + window.parent.postMessage({status: "result", + width: width, + width2: width2, + url: url, + url2: url2}, "*"); + } +} +onload = function() { + width = document.querySelector("img").width; + width2 = document.querySelector("img").width; + maybeReport(); +}; +navigator.serviceWorker.onmessage = function(event) { + if (event.data.suffix == "2") { + url2 = event.data.url; + } else { + url = event.data.url; + } + maybeReport(); +}; +</script> +<img src="image.png"> +<img src="image2.png"> diff --git a/dom/workers/test/serviceworkers/fetch/imagecache-maxage/maxage_test.js b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/maxage_test.js new file mode 100644 index 000000000..1922111df --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/maxage_test.js @@ -0,0 +1,41 @@ +function synthesizeImage(suffix) { + // Serve image-20px for the first page, and image-40px for the second page. + return clients.matchAll().then(clients => { + var url = "image-20px.png"; + clients.forEach(client => { + if (client.url.indexOf("?new") > 0) { + url = "image-40px.png"; + } + client.postMessage({suffix: suffix, url: url}); + }); + return fetch(url); + }).then(response => { + return response.arrayBuffer(); + }).then(ab => { + var headers; + if (suffix == "") { + headers = { + "Content-Type": "image/png", + "Date": "Tue, 1 Jan 1990 01:02:03 GMT", + "Cache-Control": "max-age=1", + }; + } else { + headers = { + "Content-Type": "image/png", + "Cache-Control": "no-cache", + }; + } + return new Response(ab, { + status: 200, + headers: headers, + }); + }); +} + +self.addEventListener("fetch", function(event) { + if (event.request.url.indexOf("image.png") >= 0) { + event.respondWith(synthesizeImage("")); + } else if (event.request.url.indexOf("image2.png") >= 0) { + event.respondWith(synthesizeImage("2")); + } +}); diff --git a/dom/workers/test/serviceworkers/fetch/imagecache-maxage/register.html b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/register.html new file mode 100644 index 000000000..af4dde2e2 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/register.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<script> + function ok(v, msg) { + window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*"); + } + + function done(reg) { + ok(reg.active, "The active worker should be available."); + window.parent.postMessage({status: "registrationdone"}, "*"); + } + + navigator.serviceWorker.ready.then(done); + navigator.serviceWorker.register("maxage_test.js", {scope: "."}); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/imagecache-maxage/unregister.html b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/unregister.html new file mode 100644 index 000000000..1f13508fa --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/unregister.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<script> + navigator.serviceWorker.getRegistration(".").then(function(registration) { + registration.unregister().then(function(success) { + if (success) { + window.parent.postMessage({status: "unregistrationdone"}, "*"); + } + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + }); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/image-20px.png b/dom/workers/test/serviceworkers/fetch/imagecache/image-20px.png Binary files differnew file mode 100644 index 000000000..ae6a8a6b8 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache/image-20px.png diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/image-40px.png b/dom/workers/test/serviceworkers/fetch/imagecache/image-40px.png Binary files differnew file mode 100644 index 000000000..fe391dc8a --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache/image-40px.png diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/imagecache_test.js b/dom/workers/test/serviceworkers/fetch/imagecache/imagecache_test.js new file mode 100644 index 000000000..598d8213f --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache/imagecache_test.js @@ -0,0 +1,15 @@ +function synthesizeImage() { + return clients.matchAll().then(clients => { + var url = "image-40px.png"; + clients.forEach(client => { + client.postMessage(url); + }); + return fetch(url); + }); +} + +self.addEventListener("fetch", function(event) { + if (event.request.url.indexOf("image-20px.png") >= 0) { + event.respondWith(synthesizeImage()); + } +}); diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/index.html b/dom/workers/test/serviceworkers/fetch/imagecache/index.html new file mode 100644 index 000000000..93b30f184 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache/index.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<script> +var width, url; +function maybeReport() { + if (width !== undefined && url !== undefined) { + window.parent.postMessage({status: "result", + width: width, + url: url}, "*"); + } +} +onload = function() { + width = document.querySelector("img").width; + maybeReport(); +}; +navigator.serviceWorker.onmessage = function(event) { + url = event.data; + maybeReport(); +}; +</script> +<img src="image-20px.png"> diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/postmortem.html b/dom/workers/test/serviceworkers/fetch/imagecache/postmortem.html new file mode 100644 index 000000000..72a650d26 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache/postmortem.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<script> +onload = function() { + var width = document.querySelector("img").width; + window.parent.postMessage({status: "postmortem", + width: width}, "*"); +}; +</script> +<img src="image-20px.png"> diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/register.html b/dom/workers/test/serviceworkers/fetch/imagecache/register.html new file mode 100644 index 000000000..f6d1eb382 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache/register.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<!-- Load the image here to put it in the image cache --> +<img src="image-20px.png"> +<script> + function ok(v, msg) { + window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*"); + } + + function done(reg) { + ok(reg.active, "The active worker should be available."); + window.parent.postMessage({status: "registrationdone"}, "*"); + } + + navigator.serviceWorker.ready.then(done); + navigator.serviceWorker.register("imagecache_test.js", {scope: "."}); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/unregister.html b/dom/workers/test/serviceworkers/fetch/imagecache/unregister.html new file mode 100644 index 000000000..1f13508fa --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache/unregister.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<script> + navigator.serviceWorker.getRegistration(".").then(function(registration) { + registration.unregister().then(function(success) { + if (success) { + window.parent.postMessage({status: "unregistrationdone"}, "*"); + } + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + }); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/https_test.js b/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/https_test.js new file mode 100644 index 000000000..0f08ba74e --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/https_test.js @@ -0,0 +1,28 @@ +function sendResponseToParent(response) { + return ` + <!DOCTYPE html> + <script> + window.parent.postMessage({status: "done", data: "${response}"}, "*"); + </script> + `; +} + +self.addEventListener("fetch", function(event) { + if (event.request.url.indexOf("index.html") >= 0) { + var response = "good"; + try { + importScripts("http://example.org/tests/dom/workers/test/foreign.js"); + } catch(e) { + dump("Got error " + e + " when importing the script\n"); + } + if (response === "good") { + try { + importScripts("/tests/dom/workers/test/redirect_to_foreign.sjs"); + } catch(e) { + dump("Got error " + e + " when importing the script\n"); + } + } + event.respondWith(new Response(sendResponseToParent(response), + {headers: {'Content-Type': 'text/html'}})); + } +}); diff --git a/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/register.html b/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/register.html new file mode 100644 index 000000000..41774f70d --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/register.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<script> + function ok(v, msg) { + window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*"); + } + + function done(reg) { + ok(reg.active, "The active worker should be available."); + window.parent.postMessage({status: "registrationdone"}, "*"); + } + + navigator.serviceWorker.ready.then(done); + navigator.serviceWorker.register("https_test.js", {scope: "."}); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/unregister.html b/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/unregister.html new file mode 100644 index 000000000..1f13508fa --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/unregister.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<script> + navigator.serviceWorker.getRegistration(".").then(function(registration) { + registration.unregister().then(function(success) { + if (success) { + window.parent.postMessage({status: "unregistrationdone"}, "*"); + } + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + }); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/index.html b/dom/workers/test/serviceworkers/fetch/index.html new file mode 100644 index 000000000..4db0fb139 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/index.html @@ -0,0 +1,183 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 94048 - test install event.</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<div id="style-test" style="background-color: white"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + function my_ok(result, msg) { + window.opener.postMessage({status: "ok", result: result, message: msg}, "*"); + } + + function check_intercepted_script() { + document.getElementById('intercepted-script').test_result = + document.currentScript == document.getElementById('intercepted-script'); + } + + function fetchXHR(name, onload, onerror, headers) { + gExpected++; + + onload = onload || function() { + my_ok(false, "load should not complete successfully"); + finish(); + }; + onerror = onerror || function() { + my_ok(false, "load should be intercepted successfully"); + finish(); + }; + + var x = new XMLHttpRequest(); + x.open('GET', name, true); + x.onload = function() { onload(x) }; + x.onerror = function() { onerror(x) }; + headers = headers || []; + headers.forEach(function(header) { + x.setRequestHeader(header[0], header[1]); + }); + x.send(); + } + + var gExpected = 0; + var gEncountered = 0; + function finish() { + gEncountered++; + if (gEncountered == gExpected) { + window.opener.postMessage({status: "done"}, "*"); + } + } + + function test_onload(creator, complete) { + gExpected++; + var elem = creator(); + elem.onload = function() { + complete.call(elem); + finish(); + }; + elem.onerror = function() { + my_ok(false, elem.tagName + " load should complete successfully"); + finish(); + }; + document.body.appendChild(elem); + } + + function expectAsyncResult() { + gExpected++; + } + + my_ok(navigator.serviceWorker.controller != null, "should be controlled"); +</script> +<script src="fetch_tests.js"></script> +<script> + test_onload(function() { + var elem = document.createElement('img'); + elem.src = "nonexistent_image.gifs"; + elem.id = 'intercepted-img'; + return elem; + }, function() { + my_ok(this.complete, "image should be complete"); + my_ok(this.naturalWidth == 1 && this.naturalHeight == 1, "image should be 1x1 gif"); + }); + + test_onload(function() { + var elem = document.createElement('script'); + elem.id = 'intercepted-script'; + elem.src = "nonexistent_script.js"; + return elem; + }, function() { + my_ok(this.test_result, "script load should be intercepted"); + }); + + test_onload(function() { + var elem = document.createElement('link'); + elem.href = "nonexistent_stylesheet.css"; + elem.rel = "stylesheet"; + return elem; + }, function() { + var styled = document.getElementById('style-test'); + my_ok(window.getComputedStyle(styled).backgroundColor == 'rgb(0, 0, 0)', + "stylesheet load should be intercepted"); + }); + + test_onload(function() { + var elem = document.createElement('iframe'); + elem.id = 'intercepted-iframe'; + elem.src = "nonexistent_page.html"; + return elem; + }, function() { + my_ok(this.test_result, "iframe load should be intercepted"); + }); + + test_onload(function() { + var elem = document.createElement('iframe'); + elem.id = 'intercepted-iframe-2'; + elem.src = "navigate.html"; + return elem; + }, function() { + my_ok(this.test_result, "iframe should successfully load"); + }); + + gExpected++; + var xmlDoc = document.implementation.createDocument(null, null, null); + xmlDoc.load('load_cross_origin_xml_document_synthetic.xml'); + xmlDoc.onload = function(evt) { + var content = new XMLSerializer().serializeToString(evt.target); + my_ok(!content.includes('parsererror'), "Load synthetic cross origin XML Document should be allowed"); + finish(); + }; + + gExpected++; + var xmlDoc = document.implementation.createDocument(null, null, null); + xmlDoc.load('load_cross_origin_xml_document_cors.xml'); + xmlDoc.onload = function(evt) { + var content = new XMLSerializer().serializeToString(evt.target); + my_ok(!content.includes('parsererror'), "Load CORS cross origin XML Document should be allowed"); + finish(); + }; + + gExpected++; + var xmlDoc = document.implementation.createDocument(null, null, null); + xmlDoc.load('load_cross_origin_xml_document_opaque.xml'); + xmlDoc.onload = function(evt) { + var content = new XMLSerializer().serializeToString(evt.target); + my_ok(content.includes('parsererror'), "Load opaque cross origin XML Document should not be allowed"); + finish(); + }; + + gExpected++; + var worker = new Worker('nonexistent_worker_script.js'); + worker.onmessage = function(e) { + my_ok(e.data == "worker-intercept-success", "worker load intercepted"); + finish(); + }; + worker.onerror = function() { + my_ok(false, "worker load should be intercepted"); + }; + + gExpected++; + var worker = new Worker('fetch_worker_script.js'); + worker.onmessage = function(e) { + if (e.data == "finish") { + finish(); + } else if (e.data == "expect") { + gExpected++; + } else if (e.data.type == "ok") { + my_ok(e.data.value, "Fetch test on worker: " + e.data.msg); + } + }; + worker.onerror = function() { + my_ok(false, "worker should not cause any errors"); + }; +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/fetch/interrupt.sjs b/dom/workers/test/serviceworkers/fetch/interrupt.sjs new file mode 100644 index 000000000..f6fe870ef --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/interrupt.sjs @@ -0,0 +1,20 @@ +function handleRequest(request, response) { + var body = "a"; + for (var i = 0; i < 20; i++) { + body += body; + } + + response.seizePower(); + response.write("HTTP/1.1 200 OK\r\n") + var count = 10; + response.write("Content-Length: " + body.length * count + "\r\n"); + response.write("Content-Type: text/plain; charset=utf-8\r\n"); + response.write("Cache-Control: no-cache, must-revalidate\r\n"); + response.write("\r\n"); + + for (var i = 0; i < count; i++) { + response.write(body); + } + + throw Components.results.NS_BINDING_ABORTED; +} diff --git a/dom/workers/test/serviceworkers/fetch/origin/https/index-https.sjs b/dom/workers/test/serviceworkers/fetch/origin/https/index-https.sjs new file mode 100644 index 000000000..7266925ea --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/origin/https/index-https.sjs @@ -0,0 +1,4 @@ +function handleRequest(request, response) { + response.setStatusLine(null, 308, "Permanent Redirect"); + response.setHeader("Location", "https://example.org/tests/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html", false); +} diff --git a/dom/workers/test/serviceworkers/fetch/origin/https/origin_test.js b/dom/workers/test/serviceworkers/fetch/origin/https/origin_test.js new file mode 100644 index 000000000..9839fc5f0 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/origin/https/origin_test.js @@ -0,0 +1,29 @@ +var prefix = "/tests/dom/workers/test/serviceworkers/fetch/origin/https/"; + +function addOpaqueRedirect(cache, file) { + return fetch(new Request(prefix + file, { redirect: "manual" })).then(function(response) { + return cache.put(prefix + file, response); + }); +} + +self.addEventListener("install", function(event) { + event.waitUntil( + self.caches.open("origin-cache") + .then(c => { + return addOpaqueRedirect(c, 'index-https.sjs'); + }) + ); +}); + +self.addEventListener("fetch", function(event) { + if (event.request.url.indexOf("index-cached-https.sjs") >= 0) { + event.respondWith( + self.caches.open("origin-cache") + .then(c => { + return c.match(prefix + 'index-https.sjs'); + }) + ); + } else { + event.respondWith(fetch(event.request)); + } +}); diff --git a/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html b/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html new file mode 100644 index 000000000..87f348945 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html @@ -0,0 +1,6 @@ +<!DOCTYPE html> +<script> + window.opener.postMessage({status: "domain", data: document.domain}, "*"); + window.opener.postMessage({status: "origin", data: location.origin}, "*"); + window.opener.postMessage({status: "done"}, "*"); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html^headers^ b/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html^headers^ new file mode 100644 index 000000000..5ed82fd06 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html^headers^ @@ -0,0 +1 @@ +Access-Control-Allow-Origin: https://example.com diff --git a/dom/workers/test/serviceworkers/fetch/origin/https/register.html b/dom/workers/test/serviceworkers/fetch/origin/https/register.html new file mode 100644 index 000000000..2e99adba5 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/origin/https/register.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<script> + function ok(v, msg) { + window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*"); + } + + function done(reg) { + ok(reg.active, "The active worker should be available."); + window.parent.postMessage({status: "registrationdone"}, "*"); + } + + navigator.serviceWorker.ready.then(done); + navigator.serviceWorker.register("origin_test.js", {scope: "."}); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/origin/https/unregister.html b/dom/workers/test/serviceworkers/fetch/origin/https/unregister.html new file mode 100644 index 000000000..1f13508fa --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/origin/https/unregister.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<script> + navigator.serviceWorker.getRegistration(".").then(function(registration) { + registration.unregister().then(function(success) { + if (success) { + window.parent.postMessage({status: "unregistrationdone"}, "*"); + } + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + }); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/origin/index-to-https.sjs b/dom/workers/test/serviceworkers/fetch/origin/index-to-https.sjs new file mode 100644 index 000000000..1cc916ff3 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/origin/index-to-https.sjs @@ -0,0 +1,4 @@ +function handleRequest(request, response) { + response.setStatusLine(null, 308, "Permanent Redirect"); + response.setHeader("Location", "https://example.org/tests/dom/workers/test/serviceworkers/fetch/origin/realindex.html", false); +} diff --git a/dom/workers/test/serviceworkers/fetch/origin/index.sjs b/dom/workers/test/serviceworkers/fetch/origin/index.sjs new file mode 100644 index 000000000..a79588e76 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/origin/index.sjs @@ -0,0 +1,4 @@ +function handleRequest(request, response) { + response.setStatusLine(null, 308, "Permanent Redirect"); + response.setHeader("Location", "http://example.org/tests/dom/workers/test/serviceworkers/fetch/origin/realindex.html", false); +} diff --git a/dom/workers/test/serviceworkers/fetch/origin/origin_test.js b/dom/workers/test/serviceworkers/fetch/origin/origin_test.js new file mode 100644 index 000000000..d2be9573b --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/origin/origin_test.js @@ -0,0 +1,41 @@ +var prefix = "/tests/dom/workers/test/serviceworkers/fetch/origin/"; + +function addOpaqueRedirect(cache, file) { + return fetch(new Request(prefix + file, { redirect: "manual" })).then(function(response) { + return cache.put(prefix + file, response); + }); +} + +self.addEventListener("install", function(event) { + event.waitUntil( + self.caches.open("origin-cache") + .then(c => { + return Promise.all( + [ + addOpaqueRedirect(c, 'index.sjs'), + addOpaqueRedirect(c, 'index-to-https.sjs') + ] + ); + }) + ); +}); + +self.addEventListener("fetch", function(event) { + if (event.request.url.indexOf("index-cached.sjs") >= 0) { + event.respondWith( + self.caches.open("origin-cache") + .then(c => { + return c.match(prefix + 'index.sjs'); + }) + ); + } else if (event.request.url.indexOf("index-to-https-cached.sjs") >= 0) { + event.respondWith( + self.caches.open("origin-cache") + .then(c => { + return c.match(prefix + 'index-to-https.sjs'); + }) + ); + } else { + event.respondWith(fetch(event.request)); + } +}); diff --git a/dom/workers/test/serviceworkers/fetch/origin/realindex.html b/dom/workers/test/serviceworkers/fetch/origin/realindex.html new file mode 100644 index 000000000..87f348945 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/origin/realindex.html @@ -0,0 +1,6 @@ +<!DOCTYPE html> +<script> + window.opener.postMessage({status: "domain", data: document.domain}, "*"); + window.opener.postMessage({status: "origin", data: location.origin}, "*"); + window.opener.postMessage({status: "done"}, "*"); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/origin/realindex.html^headers^ b/dom/workers/test/serviceworkers/fetch/origin/realindex.html^headers^ new file mode 100644 index 000000000..3a6a85d89 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/origin/realindex.html^headers^ @@ -0,0 +1 @@ +Access-Control-Allow-Origin: http://mochi.test:8888 diff --git a/dom/workers/test/serviceworkers/fetch/origin/register.html b/dom/workers/test/serviceworkers/fetch/origin/register.html new file mode 100644 index 000000000..2e99adba5 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/origin/register.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<script> + function ok(v, msg) { + window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*"); + } + + function done(reg) { + ok(reg.active, "The active worker should be available."); + window.parent.postMessage({status: "registrationdone"}, "*"); + } + + navigator.serviceWorker.ready.then(done); + navigator.serviceWorker.register("origin_test.js", {scope: "."}); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/origin/unregister.html b/dom/workers/test/serviceworkers/fetch/origin/unregister.html new file mode 100644 index 000000000..1f13508fa --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/origin/unregister.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<script> + navigator.serviceWorker.getRegistration(".").then(function(registration) { + registration.unregister().then(function(success) { + if (success) { + window.parent.postMessage({status: "unregistrationdone"}, "*"); + } + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + }); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/plugin/plugins.html b/dom/workers/test/serviceworkers/fetch/plugin/plugins.html new file mode 100644 index 000000000..78e31b3c2 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/plugin/plugins.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<script> + var obj, embed; + + function ok(v, msg) { + window.opener.postMessage({status: "ok", result: !!v, message: msg}, "*"); + } + + function finish() { + document.documentElement.removeChild(obj); + document.documentElement.removeChild(embed); + window.opener.postMessage({status: "done"}, "*"); + } + + function test_object() { + obj = document.createElement("object"); + obj.setAttribute('data', "object"); + document.documentElement.appendChild(obj); + } + + function test_embed() { + embed = document.createElement("embed"); + embed.setAttribute('src', "embed"); + document.documentElement.appendChild(embed); + } + + navigator.serviceWorker.addEventListener("message", function onMessage(e) { + if (e.data.context === "object") { + ok(false, "<object> should not be intercepted"); + } else if (e.data.context === "embed") { + ok(false, "<embed> should not be intercepted"); + } else if (e.data.context === "fetch" && e.data.resource === "foo.txt") { + navigator.serviceWorker.removeEventListener("message", onMessage); + finish(); + } + }, false); + + test_object(); + test_embed(); + // SW will definitely intercept fetch API, use this to see if plugins are + // intercepted before fetch(). + fetch("foo.txt"); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/plugin/worker.js b/dom/workers/test/serviceworkers/fetch/plugin/worker.js new file mode 100644 index 000000000..e97d06205 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/plugin/worker.js @@ -0,0 +1,14 @@ +self.addEventListener("fetch", function(event) { + var resource = event.request.url.split('/').pop(); + event.waitUntil( + clients.matchAll() + .then(clients => { + clients.forEach(client => { + if (client.url.includes("plugins.html")) { + client.postMessage({context: event.request.context, + resource: resource}); + } + }); + }) + ); +}); diff --git a/dom/workers/test/serviceworkers/fetch/real-file.txt b/dom/workers/test/serviceworkers/fetch/real-file.txt new file mode 100644 index 000000000..3ca2088ec --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/real-file.txt @@ -0,0 +1 @@ +This is a real file. diff --git a/dom/workers/test/serviceworkers/fetch/redirect.sjs b/dom/workers/test/serviceworkers/fetch/redirect.sjs new file mode 100644 index 000000000..dab558f4a --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/redirect.sjs @@ -0,0 +1,4 @@ +function handleRequest(request, response) { + response.setStatusLine(request.httpVersion, 301, "Moved Permanently"); + response.setHeader("Location", "synthesized-redirect-twice-real-file.txt"); +} diff --git a/dom/workers/test/serviceworkers/fetch/requesturl/index.html b/dom/workers/test/serviceworkers/fetch/requesturl/index.html new file mode 100644 index 000000000..bc3e400a9 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/requesturl/index.html @@ -0,0 +1,7 @@ +<!DOCTYPE html> +<script> + navigator.serviceWorker.onmessage = window.onmessage = e => { + window.parent.postMessage(e.data, "*"); + }; +</script> +<iframe src="redirector.html"></iframe> diff --git a/dom/workers/test/serviceworkers/fetch/requesturl/redirect.sjs b/dom/workers/test/serviceworkers/fetch/requesturl/redirect.sjs new file mode 100644 index 000000000..7b92fec20 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/requesturl/redirect.sjs @@ -0,0 +1,4 @@ +function handleRequest(request, response) { + response.setStatusLine(null, 308, "Permanent Redirect"); + response.setHeader("Location", "http://example.org/tests/dom/workers/test/serviceworkers/fetch/requesturl/secret.html", false); +} diff --git a/dom/workers/test/serviceworkers/fetch/requesturl/redirector.html b/dom/workers/test/serviceworkers/fetch/requesturl/redirector.html new file mode 100644 index 000000000..73bf4af49 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/requesturl/redirector.html @@ -0,0 +1,2 @@ +<!DOCTYPE html> +<meta http-equiv="refresh" content="3;URL=/tests/dom/workers/test/serviceworkers/fetch/requesturl/redirect.sjs"> diff --git a/dom/workers/test/serviceworkers/fetch/requesturl/register.html b/dom/workers/test/serviceworkers/fetch/requesturl/register.html new file mode 100644 index 000000000..19a2e022c --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/requesturl/register.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<script> + function ok(v, msg) { + window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*"); + } + + function done(reg) { + ok(reg.active, "The active worker should be available."); + window.parent.postMessage({status: "registrationdone"}, "*"); + } + + navigator.serviceWorker.ready.then(done); + navigator.serviceWorker.register("requesturl_test.js", {scope: "."}); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/requesturl/requesturl_test.js b/dom/workers/test/serviceworkers/fetch/requesturl/requesturl_test.js new file mode 100644 index 000000000..c8be3daf4 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/requesturl/requesturl_test.js @@ -0,0 +1,17 @@ +addEventListener("fetch", event => { + var url = event.request.url; + var badURL = url.indexOf("secret.html") > -1; + event.respondWith( + new Promise(resolve => { + clients.matchAll().then(clients => { + for (var client of clients) { + if (client.url.indexOf("index.html") > -1) { + client.postMessage({status: "ok", result: !badURL, message: "Should not find a bad URL (" + url + ")"}); + break; + } + } + resolve(fetch(event.request)); + }); + }) + ); +}); diff --git a/dom/workers/test/serviceworkers/fetch/requesturl/secret.html b/dom/workers/test/serviceworkers/fetch/requesturl/secret.html new file mode 100644 index 000000000..694c33635 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/requesturl/secret.html @@ -0,0 +1,5 @@ +<!DOCTYPE html> +secret stuff +<script> + window.parent.postMessage({status: "done"}, "*"); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/requesturl/unregister.html b/dom/workers/test/serviceworkers/fetch/requesturl/unregister.html new file mode 100644 index 000000000..1f13508fa --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/requesturl/unregister.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<script> + navigator.serviceWorker.getRegistration(".").then(function(registration) { + registration.unregister().then(function(success) { + if (success) { + window.parent.postMessage({status: "unregistrationdone"}, "*"); + } + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + }); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/sandbox/index.html b/dom/workers/test/serviceworkers/fetch/sandbox/index.html new file mode 100644 index 000000000..1094a3995 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/sandbox/index.html @@ -0,0 +1,5 @@ +<!DOCTYPE html> +<script> + window.parent.postMessage({status: "ok", result: true, message: "The iframe is not being intercepted"}, "*"); + window.parent.postMessage({status: "done"}, "*"); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/sandbox/intercepted_index.html b/dom/workers/test/serviceworkers/fetch/sandbox/intercepted_index.html new file mode 100644 index 000000000..87261a495 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/sandbox/intercepted_index.html @@ -0,0 +1,5 @@ +<!DOCTYPE html> +<script> + window.parent.postMessage({status: "ok", result: false, message: "The iframe is being intercepted"}, "*"); + window.parent.postMessage({status: "done"}, "*"); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/sandbox/register.html b/dom/workers/test/serviceworkers/fetch/sandbox/register.html new file mode 100644 index 000000000..427b1a8da --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/sandbox/register.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<script> + function ok(v, msg) { + window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*"); + } + + function done(reg) { + ok(reg.active, "The active worker should be available."); + window.parent.postMessage({status: "registrationdone"}, "*"); + } + + navigator.serviceWorker.ready.then(done); + navigator.serviceWorker.register("sandbox_test.js", {scope: "."}); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/sandbox/sandbox_test.js b/dom/workers/test/serviceworkers/fetch/sandbox/sandbox_test.js new file mode 100644 index 000000000..1ed351794 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/sandbox/sandbox_test.js @@ -0,0 +1,5 @@ +self.addEventListener("fetch", function(event) { + if (event.request.url.indexOf("index.html") >= 0) { + event.respondWith(fetch("intercepted_index.html")); + } +}); diff --git a/dom/workers/test/serviceworkers/fetch/sandbox/unregister.html b/dom/workers/test/serviceworkers/fetch/sandbox/unregister.html new file mode 100644 index 000000000..1f13508fa --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/sandbox/unregister.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<script> + navigator.serviceWorker.getRegistration(".").then(function(registration) { + registration.unregister().then(function(success) { + if (success) { + window.parent.postMessage({status: "unregistrationdone"}, "*"); + } + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + }); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html new file mode 100644 index 000000000..6098a45dd --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<script> + window.onmessage = function(e) { + window.parent.postMessage(e.data, "*"); + if (e.data.status == "protocol") { + document.querySelector("iframe").src = "image.html"; + } + }; +</script> +<iframe src="http://example.com/tests/dom/workers/test/serviceworkers/fetch/upgrade-insecure/index.html"></iframe> diff --git a/dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html^headers^ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html^headers^ new file mode 100644 index 000000000..602d9dc38 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html^headers^ @@ -0,0 +1 @@ +Content-Security-Policy: upgrade-insecure-requests diff --git a/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-20px.png b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-20px.png Binary files differnew file mode 100644 index 000000000..ae6a8a6b8 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-20px.png diff --git a/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-40px.png b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-40px.png Binary files differnew file mode 100644 index 000000000..fe391dc8a --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-40px.png diff --git a/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image.html b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image.html new file mode 100644 index 000000000..34e24e35a --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<script> +onload=function(){ + var img = new Image(); + img.src = "http://example.com/tests/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-20px.png"; + img.onload = function() { + window.parent.postMessage({status: "image", data: img.width}, "*"); + }; + img.onerror = function() { + window.parent.postMessage({status: "image", data: "error"}, "*"); + }; +}; +</script> diff --git a/dom/workers/test/serviceworkers/fetch/upgrade-insecure/realindex.html b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/realindex.html new file mode 100644 index 000000000..aaa255aad --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/realindex.html @@ -0,0 +1,4 @@ +<!DOCTYPE html> +<script> + window.parent.postMessage({status: "protocol", data: location.protocol}, "*"); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/upgrade-insecure/register.html b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/register.html new file mode 100644 index 000000000..6309b9b21 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/register.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<script> + function ok(v, msg) { + window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*"); + } + + function done(reg) { + ok(reg.active, "The active worker should be available."); + window.parent.postMessage({status: "registrationdone"}, "*"); + } + + navigator.serviceWorker.ready.then(done); + navigator.serviceWorker.register("upgrade-insecure_test.js", {scope: "."}); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/upgrade-insecure/unregister.html b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/unregister.html new file mode 100644 index 000000000..1f13508fa --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/unregister.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<script> + navigator.serviceWorker.getRegistration(".").then(function(registration) { + registration.unregister().then(function(success) { + if (success) { + window.parent.postMessage({status: "unregistrationdone"}, "*"); + } + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + }); +</script> diff --git a/dom/workers/test/serviceworkers/fetch/upgrade-insecure/upgrade-insecure_test.js b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/upgrade-insecure_test.js new file mode 100644 index 000000000..ab54164ed --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/upgrade-insecure_test.js @@ -0,0 +1,11 @@ +self.addEventListener("fetch", function(event) { + if (event.request.url.indexOf("index.html") >= 0) { + event.respondWith(fetch("realindex.html")); + } else if (event.request.url.indexOf("image-20px.png") >= 0) { + if (event.request.url.indexOf("https://") == 0) { + event.respondWith(fetch("image-40px.png")); + } else { + event.respondWith(Response.error()); + } + } +}); diff --git a/dom/workers/test/serviceworkers/fetch_event_worker.js b/dom/workers/test/serviceworkers/fetch_event_worker.js new file mode 100644 index 000000000..1caef71e8 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch_event_worker.js @@ -0,0 +1,337 @@ +var seenIndex = false; + +onfetch = function(ev) { + if (ev.request.url.includes("ignore")) { + return; + } + + if (ev.request.url.includes("bare-synthesized.txt")) { + ev.respondWith(Promise.resolve( + new Response("synthesized response body", {}) + )); + } + + else if (ev.request.url.includes('file_CrossSiteXHR_server.sjs')) { + // N.B. this response would break the rules of CORS if it were allowed, but + // this test relies upon the preflight request not being intercepted and + // thus this response should not be used. + if (ev.request.method == 'OPTIONS') { + ev.respondWith(new Response('', {headers: {'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Headers': 'X-Unsafe'}})) + } else if (ev.request.url.includes('example.org')) { + ev.respondWith(fetch(ev.request)); + } + } + + else if (ev.request.url.includes("synthesized-404.txt")) { + ev.respondWith(Promise.resolve( + new Response("synthesized response body", { status: 404 }) + )); + } + + else if (ev.request.url.includes("synthesized-headers.txt")) { + ev.respondWith(Promise.resolve( + new Response("synthesized response body", { + headers: { + "X-Custom-Greeting": "Hello" + } + }) + )); + } + + else if (ev.request.url.includes("test-respondwith-response.txt")) { + ev.respondWith(new Response("test-respondwith-response response body", {})); + } + + else if (ev.request.url.includes("synthesized-redirect-real-file.txt")) { + ev.respondWith(Promise.resolve( + Response.redirect("fetch/real-file.txt") + )); + } + + else if (ev.request.url.includes("synthesized-redirect-twice-real-file.txt")) { + ev.respondWith(Promise.resolve( + Response.redirect("synthesized-redirect-real-file.txt") + )); + } + + else if (ev.request.url.includes("synthesized-redirect-synthesized.txt")) { + ev.respondWith(Promise.resolve( + Response.redirect("bare-synthesized.txt") + )); + } + + else if (ev.request.url.includes("synthesized-redirect-twice-synthesized.txt")) { + ev.respondWith(Promise.resolve( + Response.redirect("synthesized-redirect-synthesized.txt") + )); + } + + else if (ev.request.url.includes("rejected.txt")) { + ev.respondWith(Promise.reject()); + } + + else if (ev.request.url.includes("nonresponse.txt")) { + ev.respondWith(Promise.resolve(5)); + } + + else if (ev.request.url.includes("nonresponse2.txt")) { + ev.respondWith(Promise.resolve({})); + } + + else if (ev.request.url.includes("nonpromise.txt")) { + try { + // This should coerce to Promise(5) instead of throwing + ev.respondWith(5); + } catch (e) { + // test is expecting failure, so return a success if we get a thrown + // exception + ev.respondWith(new Response('respondWith(5) threw ' + e)); + } + } + + else if (ev.request.url.includes("headers.txt")) { + var ok = true; + ok &= ev.request.headers.get("X-Test1") == "header1"; + ok &= ev.request.headers.get("X-Test2") == "header2"; + ev.respondWith(Promise.resolve( + new Response(ok.toString(), {}) + )); + } + + else if (ev.request.url.includes('user-pass')) { + ev.respondWith(new Response(ev.request.url)); + } + + else if (ev.request.url.includes("nonexistent_image.gif")) { + var imageAsBinaryString = atob("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs"); + var imageLength = imageAsBinaryString.length; + + // If we just pass |imageAsBinaryString| to the Response constructor, an + // encoding conversion occurs that corrupts the image. Instead, we need to + // convert it to a typed array. + // typed array. + var imageAsArray = new Uint8Array(imageLength); + for (var i = 0; i < imageLength; ++i) { + imageAsArray[i] = imageAsBinaryString.charCodeAt(i); + } + + ev.respondWith(Promise.resolve( + new Response(imageAsArray, { headers: { "Content-Type": "image/gif" } }) + )); + } + + else if (ev.request.url.includes("nonexistent_script.js")) { + ev.respondWith(Promise.resolve( + new Response("check_intercepted_script();", {}) + )); + } + + else if (ev.request.url.includes("nonexistent_stylesheet.css")) { + ev.respondWith(Promise.resolve( + new Response("#style-test { background-color: black !important; }", { + headers : { + "Content-Type": "text/css" + } + }) + )); + } + + else if (ev.request.url.includes("nonexistent_page.html")) { + ev.respondWith(Promise.resolve( + new Response("<script>window.frameElement.test_result = true;</script>", { + headers : { + "Content-Type": "text/html" + } + }) + )); + } + + else if (ev.request.url.includes("navigate.html")) { + var navigateModeCorrectlyChecked = false; + var requests = [ // should not throw + new Request(ev.request), + new Request(ev.request, undefined), + new Request(ev.request, null), + new Request(ev.request, {}), + new Request(ev.request, {someUnrelatedProperty: 42}), + ]; + try { + var request3 = new Request(ev.request, {method: "GET"}); // should throw + } catch(e) { + navigateModeCorrectlyChecked = requests[0].mode == "navigate"; + } + if (navigateModeCorrectlyChecked) { + ev.respondWith(Promise.resolve( + new Response("<script>window.frameElement.test_result = true;</script>", { + headers : { + "Content-Type": "text/html" + } + }) + )); + } + } + + else if (ev.request.url.includes("nonexistent_worker_script.js")) { + ev.respondWith(Promise.resolve( + new Response("postMessage('worker-intercept-success')", {}) + )); + } + + else if (ev.request.url.includes("nonexistent_imported_script.js")) { + ev.respondWith(Promise.resolve( + new Response("check_intercepted_script();", {}) + )); + } + + else if (ev.request.url.includes("deliver-gzip")) { + // Don't handle the request, this will make Necko perform a network request, at + // which point SetApplyConversion must be re-enabled, otherwise the request + // will fail. + return; + } + + else if (ev.request.url.includes("hello.gz")) { + ev.respondWith(fetch("fetch/deliver-gzip.sjs")); + } + + else if (ev.request.url.includes("hello-after-extracting.gz")) { + ev.respondWith(fetch("fetch/deliver-gzip.sjs").then(function(res) { + return res.text().then(function(body) { + return new Response(body, { status: res.status, statusText: res.statusText, headers: res.headers }); + }); + })); + } + + else if (ev.request.url.includes('opaque-on-same-origin')) { + var url = 'http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200'; + ev.respondWith(fetch(url, { mode: 'no-cors' })); + } + + else if (ev.request.url.includes('opaque-no-cors')) { + if (ev.request.mode != "no-cors") { + ev.respondWith(Promise.reject()); + return; + } + + var url = 'http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200'; + ev.respondWith(fetch(url, { mode: ev.request.mode })); + } + + else if (ev.request.url.includes('cors-for-no-cors')) { + if (ev.request.mode != "no-cors") { + ev.respondWith(Promise.reject()); + return; + } + + var url = 'http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200&allowOrigin=*'; + ev.respondWith(fetch(url)); + } + + else if (ev.request.url.includes('example.com')) { + ev.respondWith(fetch(ev.request)); + } + + else if (ev.request.url.includes("index.html")) { + if (seenIndex) { + var body = "<script>" + + "opener.postMessage({status: 'ok', result: " + ev.isReload + "," + + "message: 'reload status should be indicated'}, '*');" + + "opener.postMessage({status: 'done'}, '*');" + + "</script>"; + ev.respondWith(new Response(body, {headers: {'Content-Type': 'text/html'}})); + } else { + seenIndex = true; + ev.respondWith(fetch(ev.request.url)); + } + } + + else if (ev.request.url.includes("body-")) { + ev.respondWith(ev.request.text().then(function (body) { + return new Response(body + body); + })); + } + + else if (ev.request.url.includes('something.txt')) { + ev.respondWith(Response.redirect('fetch/somethingelse.txt')); + } + + else if (ev.request.url.includes('somethingelse.txt')) { + ev.respondWith(new Response('something else response body', {})); + } + + else if (ev.request.url.includes('redirect_serviceworker.sjs')) { + // The redirect_serviceworker.sjs server-side JavaScript file redirects to + // 'http://mochi.test:8888/tests/dom/workers/test/serviceworkers/worker.js' + // The redirected fetch should not go through the SW since the original + // fetch was initiated from a SW. + ev.respondWith(fetch('redirect_serviceworker.sjs')); + } + + else if (ev.request.url.includes('load_cross_origin_xml_document_synthetic.xml')) { + if (ev.request.mode != 'same-origin') { + ev.respondWith(Promise.reject()); + return; + } + + ev.respondWith(Promise.resolve( + new Response("<response>body</response>", { headers: {'Content-Type': 'text/xtml'}}) + )); + } + + else if (ev.request.url.includes('load_cross_origin_xml_document_cors.xml')) { + if (ev.request.mode != 'same-origin') { + ev.respondWith(Promise.reject()); + return; + } + + var url = 'http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200&allowOrigin=*'; + ev.respondWith(fetch(url, { mode: 'cors' })); + } + + else if (ev.request.url.includes('load_cross_origin_xml_document_opaque.xml')) { + if (ev.request.mode != 'same-origin') { + Promise.resolve( + new Response("<error>Invalid Request mode</error>", { headers: {'Content-Type': 'text/xtml'}}) + ); + return; + } + + var url = 'http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200'; + ev.respondWith(fetch(url, { mode: 'no-cors' })); + } + + else if (ev.request.url.includes('xhr-method-test.txt')) { + ev.respondWith(new Response('intercepted ' + ev.request.method)); + } + + else if (ev.request.url.includes('empty-header')) { + if (!ev.request.headers.has("emptyheader") || + ev.request.headers.get("emptyheader") !== "") { + ev.respondWith(Promise.reject()); + return; + } + ev.respondWith(new Response("emptyheader")); + } + + else if (ev.request.url.includes('fetchevent-extendable')) { + if (ev instanceof ExtendableEvent) { + ev.respondWith(new Response("extendable")); + } else { + ev.respondWith(Promise.reject()); + } + } + + else if (ev.request.url.includes('fetchevent-request')) { + var threw = false; + try { + new FetchEvent("foo"); + } catch(e) { + if (e.name == "TypeError") { + threw = true; + } + } finally { + ev.respondWith(new Response(threw ? "non-nullable" : "nullable")); + } + } +}; diff --git a/dom/workers/test/serviceworkers/file_blob_response_worker.js b/dom/workers/test/serviceworkers/file_blob_response_worker.js new file mode 100644 index 000000000..4b4379d0b --- /dev/null +++ b/dom/workers/test/serviceworkers/file_blob_response_worker.js @@ -0,0 +1,38 @@ +function makeFileBlob(obj) { + return new Promise(function(resolve, reject) { + var request = indexedDB.open('file_blob_response_worker', 1); + request.onerror = reject; + request.onupgradeneeded = function(evt) { + var db = evt.target.result; + db.onerror = reject; + + var objectStore = db.createObjectStore('test', { autoIncrement: true }); + var index = objectStore.createIndex('test', 'index'); + }; + + request.onsuccess = function(evt) { + var db = evt.target.result; + db.onerror = reject; + + var blob = new Blob([JSON.stringify(obj)], + { type: 'application/json' }); + var data = { blob: blob, index: 5 }; + + objectStore = db.transaction('test', 'readwrite').objectStore('test'); + objectStore.add(data).onsuccess = function(evt) { + var key = evt.target.result; + objectStore = db.transaction('test').objectStore('test'); + objectStore.get(key).onsuccess = function(evt) { + resolve(evt.target.result.blob); + }; + }; + }; + }); +} + +self.addEventListener('fetch', function(evt) { + var result = { value: 'success' }; + evt.respondWith(makeFileBlob(result).then(function(blob) { + return new Response(blob) + })); +}); diff --git a/dom/workers/test/serviceworkers/force_refresh_browser_worker.js b/dom/workers/test/serviceworkers/force_refresh_browser_worker.js new file mode 100644 index 000000000..96d9d0f17 --- /dev/null +++ b/dom/workers/test/serviceworkers/force_refresh_browser_worker.js @@ -0,0 +1,34 @@ +var name = 'browserRefresherCache'; + +self.addEventListener('install', function(event) { + event.waitUntil( + Promise.all([caches.open(name), + fetch('./browser_cached_force_refresh.html')]).then(function(results) { + var cache = results[0]; + var response = results[1]; + return cache.put('./browser_base_force_refresh.html', response); + }) + ); +}); + +self.addEventListener('fetch', function (event) { + event.respondWith( + caches.open(name).then(function(cache) { + return cache.match(event.request); + }).then(function(response) { + return response || fetch(event.request); + }) + ); +}); + +self.addEventListener('message', function (event) { + if (event.data.type === 'GET_UNCONTROLLED_CLIENTS') { + event.waitUntil(clients.matchAll({ includeUncontrolled: true }) + .then(function(clientList) { + var resultList = clientList.map(function(c) { + return { url: c.url, frameType: c.frameType }; + }); + event.source.postMessage({ type: 'CLIENTS', detail: resultList }); + })); + } +}); diff --git a/dom/workers/test/serviceworkers/force_refresh_worker.js b/dom/workers/test/serviceworkers/force_refresh_worker.js new file mode 100644 index 000000000..f0752d0cb --- /dev/null +++ b/dom/workers/test/serviceworkers/force_refresh_worker.js @@ -0,0 +1,33 @@ +var name = 'refresherCache'; + +self.addEventListener('install', function(event) { + event.waitUntil( + Promise.all([caches.open(name), + fetch('./sw_clients/refresher_cached.html'), + fetch('./sw_clients/refresher_cached_compressed.html')]).then(function(results) { + var cache = results[0]; + var response = results[1]; + var compressed = results[2]; + return Promise.all([cache.put('./sw_clients/refresher.html', response), + cache.put('./sw_clients/refresher_compressed.html', compressed)]); + }) + ); +}); + +self.addEventListener('fetch', function (event) { + event.respondWith( + caches.open(name).then(function(cache) { + return cache.match(event.request); + }).then(function(response) { + // If this is one of our primary cached responses, then the window + // must have generated the request via a normal window reload. That + // should be detectable in the event.request.cache attribute. + if (response && event.request.cache !== 'no-cache') { + dump('### ### FetchEvent.request.cache is "' + event.request.cache + + '" instead of expected "no-cache"\n'); + return Response.error(); + } + return response || fetch(event.request); + }) + ); +}); diff --git a/dom/workers/test/serviceworkers/gzip_redirect_worker.js b/dom/workers/test/serviceworkers/gzip_redirect_worker.js new file mode 100644 index 000000000..72aeba222 --- /dev/null +++ b/dom/workers/test/serviceworkers/gzip_redirect_worker.js @@ -0,0 +1,13 @@ +self.addEventListener('fetch', function (event) { + if (!event.request.url.endsWith('sw_clients/does_not_exist.html')) { + return; + } + + event.respondWith(new Response('', { + status: 301, + statusText: 'Moved Permanently', + headers: { + 'Location': 'refresher_compressed.html' + } + })); +}); diff --git a/dom/workers/test/serviceworkers/header_checker.sjs b/dom/workers/test/serviceworkers/header_checker.sjs new file mode 100644 index 000000000..706104103 --- /dev/null +++ b/dom/workers/test/serviceworkers/header_checker.sjs @@ -0,0 +1,9 @@ +function handleRequest(request, response) { + if (request.getHeader("Service-Worker") === "script") { + response.setStatusLine("1.1", 200, "OK"); + response.setHeader("Content-Type", "text/javascript"); + response.write("// empty"); + } else { + response.setStatusLine("1.1", 404, "Not Found"); + } +} diff --git a/dom/workers/test/serviceworkers/hello.html b/dom/workers/test/serviceworkers/hello.html new file mode 100644 index 000000000..97eb03c90 --- /dev/null +++ b/dom/workers/test/serviceworkers/hello.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + </head> + <body> + Hello. + </body> +<html> diff --git a/dom/workers/test/serviceworkers/importscript.sjs b/dom/workers/test/serviceworkers/importscript.sjs new file mode 100644 index 000000000..6d177a734 --- /dev/null +++ b/dom/workers/test/serviceworkers/importscript.sjs @@ -0,0 +1,11 @@ +function handleRequest(request, response) { + if (request.queryString == 'clearcounter') { + setState('counter', ''); + } else if (!getState('counter')) { + response.setHeader("Content-Type", "application/javascript", false); + response.write("callByScript();"); + setState('counter', '1'); + } else { + response.write("no cache no party!"); + } +} diff --git a/dom/workers/test/serviceworkers/importscript_worker.js b/dom/workers/test/serviceworkers/importscript_worker.js new file mode 100644 index 000000000..3cddec194 --- /dev/null +++ b/dom/workers/test/serviceworkers/importscript_worker.js @@ -0,0 +1,37 @@ +var counter = 0; +function callByScript() { + ++counter; +} + +// Use multiple scripts in this load to verify we support that case correctly. +// See bug 1249351 for a case where we broke this. +importScripts('lorem_script.js', 'importscript.sjs'); + +importScripts('importscript.sjs'); + +var missingScriptFailed = false; +try { + importScripts(['there-is-nothing-here.js']); +} catch(e) { + missingScriptFailed = true; +} + +onmessage = function(e) { + self.clients.matchAll().then(function(res) { + if (!res.length) { + dump("ERROR: no clients are currently controlled.\n"); + } + + if (!missingScriptFailed) { + res[0].postMessage("KO"); + } + + try { + importScripts(['importscript.sjs']); + res[0].postMessage("KO"); + return; + } catch(e) {} + + res[0].postMessage(counter == 2 ? "OK" : "KO"); + }); +}; diff --git a/dom/workers/test/serviceworkers/install_event_error_worker.js b/dom/workers/test/serviceworkers/install_event_error_worker.js new file mode 100644 index 000000000..c06d648b8 --- /dev/null +++ b/dom/workers/test/serviceworkers/install_event_error_worker.js @@ -0,0 +1,4 @@ +// Worker that errors on receiving an install event. +oninstall = function(e) { + undefined.doSomething; +}; diff --git a/dom/workers/test/serviceworkers/install_event_worker.js b/dom/workers/test/serviceworkers/install_event_worker.js new file mode 100644 index 000000000..f965d28aa --- /dev/null +++ b/dom/workers/test/serviceworkers/install_event_worker.js @@ -0,0 +1,3 @@ +oninstall = function(e) { + dump("Got install event\n"); +} diff --git a/dom/workers/test/serviceworkers/lorem_script.js b/dom/workers/test/serviceworkers/lorem_script.js new file mode 100644 index 000000000..5502a44da --- /dev/null +++ b/dom/workers/test/serviceworkers/lorem_script.js @@ -0,0 +1,8 @@ +var lorem_str = ` +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis +nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo +consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum +dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, +sunt in culpa qui officia deserunt mollit anim id est laborum. +` diff --git a/dom/workers/test/serviceworkers/match_all_advanced_worker.js b/dom/workers/test/serviceworkers/match_all_advanced_worker.js new file mode 100644 index 000000000..3721aedfe --- /dev/null +++ b/dom/workers/test/serviceworkers/match_all_advanced_worker.js @@ -0,0 +1,5 @@ +onmessage = function(e) { + self.clients.matchAll().then(function(clients) { + e.source.postMessage(clients.length); + }); +} diff --git a/dom/workers/test/serviceworkers/match_all_client/match_all_client_id.html b/dom/workers/test/serviceworkers/match_all_client/match_all_client_id.html new file mode 100644 index 000000000..7ac6fc9d0 --- /dev/null +++ b/dom/workers/test/serviceworkers/match_all_client/match_all_client_id.html @@ -0,0 +1,31 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1139425 - controlled page</title> +<script class="testbody" type="text/javascript"> + var testWindow = parent; + if (opener) { + testWindow = opener; + } + + window.onload = function() { + navigator.serviceWorker.ready.then(function(swr) { + swr.active.postMessage("Start"); + }); + } + + navigator.serviceWorker.onmessage = function(msg) { + // worker message; + testWindow.postMessage(msg.data, "*"); + window.close(); + }; +</script> + +</head> +<body> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/match_all_client_id_worker.js b/dom/workers/test/serviceworkers/match_all_client_id_worker.js new file mode 100644 index 000000000..a7d9ff594 --- /dev/null +++ b/dom/workers/test/serviceworkers/match_all_client_id_worker.js @@ -0,0 +1,28 @@ +onmessage = function(e) { + dump("MatchAllClientIdWorker:" + e.data + "\n"); + var id = []; + var iterations = 5; + var counter = 0; + + for (var i = 0; i < iterations; i++) { + self.clients.matchAll().then(function(res) { + if (!res.length) { + dump("ERROR: no clients are currently controlled.\n"); + } + + client = res[0]; + id[counter] = client.id; + counter++; + if (counter >= iterations) { + var response = true; + for (var index = 1; index < iterations; index++) { + if (id[0] != id[index]) { + response = false; + break; + } + } + client.postMessage(response); + } + }); + } +} diff --git a/dom/workers/test/serviceworkers/match_all_clients/match_all_controlled.html b/dom/workers/test/serviceworkers/match_all_clients/match_all_controlled.html new file mode 100644 index 000000000..25317b9fc --- /dev/null +++ b/dom/workers/test/serviceworkers/match_all_clients/match_all_controlled.html @@ -0,0 +1,65 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1058311 - controlled page</title> +<script class="testbody" type="text/javascript"> + var re = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/; + var frameType = "none"; + var testWindow = parent; + + if (parent != window) { + frameType = "nested"; + } else if (opener) { + frameType = "auxiliary"; + testWindow = opener; + } else if (parent != window) { + frameType = "top-level"; + } else { + postResult(false, "Unexpected frameType"); + } + + window.onload = function() { + navigator.serviceWorker.ready.then(function(swr) { + swr.active.postMessage("Start"); + }); + } + + function postResult(result, msg) { + response = { + result: result, + message: msg + }; + + testWindow.postMessage(response, "*"); + } + + navigator.serviceWorker.onmessage = function(msg) { + // worker message; + result = re.test(msg.data.id); + postResult(result, "Client id test"); + + result = msg.data.url == window.location; + postResult(result, "Client url test"); + + result = msg.data.visibilityState === document.visibilityState; + postResult(result, "Client visibility test. expected=" +document.visibilityState); + + result = msg.data.focused === document.hasFocus(); + postResult(result, "Client focus test. expected=" + document.hasFocus()); + + result = msg.data.frameType === frameType; + postResult(result, "Client frameType test. expected=" + frameType); + + postResult(true, "DONE"); + window.close(); + }; +</script> + +</head> +<body> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/match_all_properties_worker.js b/dom/workers/test/serviceworkers/match_all_properties_worker.js new file mode 100644 index 000000000..f007a5ce8 --- /dev/null +++ b/dom/workers/test/serviceworkers/match_all_properties_worker.js @@ -0,0 +1,20 @@ +onmessage = function(e) { + dump("MatchAllPropertiesWorker:" + e.data + "\n"); + self.clients.matchAll().then(function(res) { + if (!res.length) { + dump("ERROR: no clients are currently controlled.\n"); + } + + for (i = 0; i < res.length; i++) { + client = res[i]; + response = { + id: client.id, + url: client.url, + visibilityState: client.visibilityState, + focused: client.focused, + frameType: client.frameType + }; + client.postMessage(response); + } + }); +} diff --git a/dom/workers/test/serviceworkers/match_all_worker.js b/dom/workers/test/serviceworkers/match_all_worker.js new file mode 100644 index 000000000..9d1c8c363 --- /dev/null +++ b/dom/workers/test/serviceworkers/match_all_worker.js @@ -0,0 +1,10 @@ +function loop() { + self.clients.matchAll().then(function(result) { + setTimeout(loop, 0); + }); +} + +onactivate = function(e) { + // spam matchAll until the worker is closed. + loop(); +} diff --git a/dom/workers/test/serviceworkers/message_posting_worker.js b/dom/workers/test/serviceworkers/message_posting_worker.js new file mode 100644 index 000000000..26db99775 --- /dev/null +++ b/dom/workers/test/serviceworkers/message_posting_worker.js @@ -0,0 +1,8 @@ +onmessage = function(e) { + self.clients.matchAll().then(function(res) { + if (!res.length) { + dump("ERROR: no clients are currently controlled.\n"); + } + res[0].postMessage(e.data); + }); +}; diff --git a/dom/workers/test/serviceworkers/message_receiver.html b/dom/workers/test/serviceworkers/message_receiver.html new file mode 100644 index 000000000..82cb587c7 --- /dev/null +++ b/dom/workers/test/serviceworkers/message_receiver.html @@ -0,0 +1,6 @@ +<!DOCTYPE html> +<script> + navigator.serviceWorker.onmessage = function(e) { + window.parent.postMessage(e.data, "*"); + }; +</script> diff --git a/dom/workers/test/serviceworkers/mochitest.ini b/dom/workers/test/serviceworkers/mochitest.ini new file mode 100644 index 000000000..29ac9e036 --- /dev/null +++ b/dom/workers/test/serviceworkers/mochitest.ini @@ -0,0 +1,317 @@ +[DEFAULT] + +support-files = + worker.js + worker2.js + worker3.js + fetch_event_worker.js + parse_error_worker.js + activate_event_error_worker.js + install_event_worker.js + install_event_error_worker.js + simpleregister/index.html + simpleregister/ready.html + controller/index.html + unregister/index.html + unregister/unregister.html + workerUpdate/update.html + sw_clients/simple.html + sw_clients/service_worker_controlled.html + match_all_worker.js + match_all_advanced_worker.js + worker_unregister.js + worker_update.js + message_posting_worker.js + fetch/index.html + fetch/fetch_worker_script.js + fetch/fetch_tests.js + fetch/deliver-gzip.sjs + fetch/redirect.sjs + fetch/real-file.txt + fetch/context/index.html + fetch/context/register.html + fetch/context/unregister.html + fetch/context/context_test.js + fetch/context/realimg.jpg + fetch/context/realaudio.ogg + fetch/context/beacon.sjs + fetch/context/csp-violate.sjs + fetch/context/ping.html + fetch/context/worker.js + fetch/context/parentworker.js + fetch/context/sharedworker.js + fetch/context/parentsharedworker.js + fetch/context/xml.xml + fetch/hsts/hsts_test.js + fetch/hsts/embedder.html + fetch/hsts/image.html + fetch/hsts/image-20px.png + fetch/hsts/image-40px.png + fetch/hsts/realindex.html + fetch/hsts/register.html + fetch/hsts/register.html^headers^ + fetch/hsts/unregister.html + fetch/https/index.html + fetch/https/register.html + fetch/https/unregister.html + fetch/https/https_test.js + fetch/https/clonedresponse/index.html + fetch/https/clonedresponse/register.html + fetch/https/clonedresponse/unregister.html + fetch/https/clonedresponse/https_test.js + fetch/imagecache/image-20px.png + fetch/imagecache/image-40px.png + fetch/imagecache/imagecache_test.js + fetch/imagecache/index.html + fetch/imagecache/postmortem.html + fetch/imagecache/register.html + fetch/imagecache/unregister.html + fetch/imagecache-maxage/index.html + fetch/imagecache-maxage/image-20px.png + fetch/imagecache-maxage/image-40px.png + fetch/imagecache-maxage/maxage_test.js + fetch/imagecache-maxage/register.html + fetch/imagecache-maxage/unregister.html + fetch/importscript-mixedcontent/register.html + fetch/importscript-mixedcontent/unregister.html + fetch/importscript-mixedcontent/https_test.js + fetch/interrupt.sjs + fetch/origin/index.sjs + fetch/origin/index-to-https.sjs + fetch/origin/realindex.html + fetch/origin/realindex.html^headers^ + fetch/origin/register.html + fetch/origin/unregister.html + fetch/origin/origin_test.js + fetch/origin/https/index-https.sjs + fetch/origin/https/realindex.html + fetch/origin/https/realindex.html^headers^ + fetch/origin/https/register.html + fetch/origin/https/unregister.html + fetch/origin/https/origin_test.js + fetch/requesturl/index.html + fetch/requesturl/redirect.sjs + fetch/requesturl/redirector.html + fetch/requesturl/register.html + fetch/requesturl/requesturl_test.js + fetch/requesturl/secret.html + fetch/requesturl/unregister.html + fetch/sandbox/index.html + fetch/sandbox/intercepted_index.html + fetch/sandbox/register.html + fetch/sandbox/unregister.html + fetch/sandbox/sandbox_test.js + fetch/upgrade-insecure/upgrade-insecure_test.js + fetch/upgrade-insecure/embedder.html + fetch/upgrade-insecure/embedder.html^headers^ + fetch/upgrade-insecure/image.html + fetch/upgrade-insecure/image-20px.png + fetch/upgrade-insecure/image-40px.png + fetch/upgrade-insecure/realindex.html + fetch/upgrade-insecure/register.html + fetch/upgrade-insecure/unregister.html + match_all_properties_worker.js + match_all_clients/match_all_controlled.html + test_serviceworker_interfaces.js + serviceworker_wrapper.js + message_receiver.html + close_test.js + serviceworker_not_sharedworker.js + match_all_client/match_all_client_id.html + match_all_client_id_worker.js + source_message_posting_worker.js + scope/scope_worker.js + redirect_serviceworker.sjs + importscript.sjs + importscript_worker.js + bug1151916_worker.js + bug1151916_driver.html + bug1240436_worker.js + notificationclick.html + notificationclick-otherwindow.html + notificationclick.js + notificationclick_focus.html + notificationclick_focus.js + notificationclose.html + notificationclose.js + worker_updatefoundevent.js + worker_updatefoundevent2.js + updatefoundevent.html + empty.js + notification_constructor_error.js + notification_get_sw.js + notification/register.html + notification/unregister.html + notification/listener.html + notification_alt/register.html + notification_alt/unregister.html + sanitize/frame.html + sanitize/register.html + sanitize/example_check_and_unregister.html + sanitize_worker.js + swa/worker_scope_different.js + swa/worker_scope_different.js^headers^ + swa/worker_scope_different2.js + swa/worker_scope_different2.js^headers^ + swa/worker_scope_precise.js + swa/worker_scope_precise.js^headers^ + swa/worker_scope_too_deep.js + swa/worker_scope_too_deep.js^headers^ + swa/worker_scope_too_narrow.js + swa/worker_scope_too_narrow.js^headers^ + claim_oninstall_worker.js + claim_worker_1.js + claim_worker_2.js + claim_clients/client.html + claim_fetch_worker.js + force_refresh_worker.js + sw_clients/refresher.html + sw_clients/refresher_compressed.html + sw_clients/refresher_compressed.html^headers^ + sw_clients/refresher_cached.html + sw_clients/refresher_cached_compressed.html + sw_clients/refresher_cached_compressed.html^headers^ + strict_mode_warning.js + skip_waiting_installed_worker.js + skip_waiting_scope/index.html + thirdparty/iframe1.html + thirdparty/iframe2.html + thirdparty/register.html + thirdparty/unregister.html + thirdparty/sw.js + register_https.html + gzip_redirect_worker.js + sw_clients/navigator.html + eval_worker.js + test_eval_allowed.html^headers^ + opaque_intercept_worker.js + notify_loaded.js + test_request_context.js + fetch/plugin/worker.js + fetch/plugin/plugins.html + eventsource/* + sw_clients/file_blob_upload_frame.html + redirect_post.sjs + xslt_worker.js + xslt/* + unresolved_fetch_worker.js + header_checker.sjs + openWindow_worker.js + redirect.sjs + open_window/client.html + lorem_script.js + file_blob_response_worker.js + !/dom/security/test/cors/file_CrossSiteXHR_server.sjs + !/dom/tests/mochitest/notification/MockServices.js + !/dom/tests/mochitest/notification/NotificationTest.js + blocking_install_event_worker.js + sw_bad_mime_type.js + sw_bad_mime_type.js^headers^ + error_reporting_helpers.js + fetch.js + hello.html + create_another_sharedWorker.html + sharedWorker_fetch.js + +[test_bug1151916.html] +[test_bug1240436.html] +[test_claim.html] +[test_claim_fetch.html] +[test_claim_oninstall.html] +[test_close.html] +[test_controller.html] +[test_cross_origin_url_after_redirect.html] +[test_csp_upgrade-insecure_intercept.html] +[test_empty_serviceworker.html] +[test_error_reporting.html] +[test_escapedSlashes.html] +[test_eval_allowed.html] +[test_eventsource_intercept.html] +[test_fetch_event.html] +skip-if = (debug && e10s) # Bug 1262224 +[test_fetch_integrity.html] +[test_file_blob_response.html] +[test_file_blob_upload.html] +[test_force_refresh.html] +[test_gzip_redirect.html] +[test_hsts_upgrade_intercept.html] +[test_https_fetch.html] +[test_https_fetch_cloned_response.html] +[test_https_origin_after_redirect.html] +[test_https_origin_after_redirect_cached.html] +[test_https_synth_fetch_from_cached_sw.html] +[test_imagecache.html] +[test_imagecache_max_age.html] +[test_importscript.html] +[test_importscript_mixedcontent.html] +tags = mcb +[test_install_event.html] +[test_install_event_gc.html] +[test_installation_simple.html] +[test_match_all.html] +[test_match_all_advanced.html] +[test_match_all_client_id.html] +[test_match_all_client_properties.html] +[test_navigator.html] +[test_not_intercept_plugin.html] +[test_notification_constructor_error.html] +[test_notification_get.html] +[test_notificationclick.html] +[test_notificationclick_focus.html] +[test_notificationclick-otherwindow.html] +[test_notificationclose.html] +[test_opaque_intercept.html] +[test_openWindow.html] +tags = openwindow +[test_origin_after_redirect.html] +[test_origin_after_redirect_cached.html] +[test_origin_after_redirect_to_https.html] +[test_origin_after_redirect_to_https_cached.html] +[test_post_message.html] +[test_post_message_advanced.html] +[test_post_message_source.html] +[test_register_base.html] +[test_register_https_in_http.html] +[test_request_context_audio.html] +[test_request_context_beacon.html] +[test_request_context_cache.html] +[test_request_context_cspreport.html] +[test_request_context_embed.html] +[test_request_context_fetch.html] +[test_request_context_font.html] +[test_request_context_frame.html] +[test_request_context_iframe.html] +[test_request_context_image.html] +[test_request_context_imagesrcset.html] +[test_request_context_internal.html] +[test_request_context_nestedworker.html] +[test_request_context_nestedworkerinsharedworker.html] +[test_request_context_object.html] +[test_request_context_picture.html] +[test_request_context_ping.html] +[test_request_context_plugin.html] +[test_request_context_script.html] +[test_request_context_sharedworker.html] +[test_request_context_style.html] +[test_request_context_track.html] +[test_request_context_video.html] +[test_request_context_worker.html] +[test_request_context_xhr.html] +[test_request_context_xslt.html] +[test_sandbox_intercept.html] +[test_scopes.html] +[test_sanitize.html] +[test_sanitize_domain.html] +[test_service_worker_allowed.html] +[test_serviceworker_header.html] +[test_serviceworker_interfaces.html] +[test_serviceworker_not_sharedworker.html] +[test_skip_waiting.html] +[test_strict_mode_warning.html] +[test_third_party_iframes.html] +[test_unregister.html] +[test_unresolved_fetch_interception.html] +[test_workerUnregister.html] +[test_workerUpdate.html] +[test_workerupdatefoundevent.html] +[test_xslt.html] diff --git a/dom/workers/test/serviceworkers/notification/listener.html b/dom/workers/test/serviceworkers/notification/listener.html new file mode 100644 index 000000000..1c6e282ec --- /dev/null +++ b/dom/workers/test/serviceworkers/notification/listener.html @@ -0,0 +1,27 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1114554 - proxy to forward messages from SW to test</title> +<script class="testbody" type="text/javascript"> + var testWindow = parent; + if (opener) { + testWindow = opener; + } + + navigator.serviceWorker.onmessage = function(msg) { + // worker message; + testWindow.postMessage(msg.data, "*"); + if (msg.data.type == 'finish') { + window.close(); + } + }; +</script> + +</head> +<body> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/notification/register.html b/dom/workers/test/serviceworkers/notification/register.html new file mode 100644 index 000000000..b7df73bed --- /dev/null +++ b/dom/workers/test/serviceworkers/notification/register.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<script> + function done() { + parent.callback(); + } + + navigator.serviceWorker.ready.then(done); + navigator.serviceWorker.register("../notification_get_sw.js", {scope: "."}).catch(function(e) { + dump("Registration failure " + e.message + "\n"); + }); +</script> diff --git a/dom/workers/test/serviceworkers/notification/unregister.html b/dom/workers/test/serviceworkers/notification/unregister.html new file mode 100644 index 000000000..d5a141f83 --- /dev/null +++ b/dom/workers/test/serviceworkers/notification/unregister.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<script> + navigator.serviceWorker.getRegistration(".").then(function(registration) { + registration.unregister().then(function(success) { + if (success) { + window.parent.callback(); + } + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + }); +</script> diff --git a/dom/workers/test/serviceworkers/notification_alt/register.html b/dom/workers/test/serviceworkers/notification_alt/register.html new file mode 100644 index 000000000..b7df73bed --- /dev/null +++ b/dom/workers/test/serviceworkers/notification_alt/register.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<script> + function done() { + parent.callback(); + } + + navigator.serviceWorker.ready.then(done); + navigator.serviceWorker.register("../notification_get_sw.js", {scope: "."}).catch(function(e) { + dump("Registration failure " + e.message + "\n"); + }); +</script> diff --git a/dom/workers/test/serviceworkers/notification_alt/unregister.html b/dom/workers/test/serviceworkers/notification_alt/unregister.html new file mode 100644 index 000000000..d5a141f83 --- /dev/null +++ b/dom/workers/test/serviceworkers/notification_alt/unregister.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<script> + navigator.serviceWorker.getRegistration(".").then(function(registration) { + registration.unregister().then(function(success) { + if (success) { + window.parent.callback(); + } + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + }); +</script> diff --git a/dom/workers/test/serviceworkers/notification_constructor_error.js b/dom/workers/test/serviceworkers/notification_constructor_error.js new file mode 100644 index 000000000..644dba480 --- /dev/null +++ b/dom/workers/test/serviceworkers/notification_constructor_error.js @@ -0,0 +1 @@ +new Notification("Hi there"); diff --git a/dom/workers/test/serviceworkers/notification_get_sw.js b/dom/workers/test/serviceworkers/notification_get_sw.js new file mode 100644 index 000000000..540c9d93c --- /dev/null +++ b/dom/workers/test/serviceworkers/notification_get_sw.js @@ -0,0 +1,49 @@ +function postAll(data) { + self.clients.matchAll().then(function(clients) { + if (clients.length == 0) { + dump("***************** NO CLIENTS FOUND! Test messages are being lost *******************\n"); + } + clients.forEach(function(client) { + client.postMessage(data); + }); + }); +} + +function ok(a, msg) { + postAll({type: 'status', status: !!a, msg: a + ": " + msg }); +} + +function is(a, b, msg) { + postAll({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg }); +} + +function done() { + postAll({type: 'finish'}); +} + +onmessage = function(e) { +dump("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% MESSAGE " + e.data + "\n"); + var start; + if (e.data == 'create') { + start = registration.showNotification("This is a title"); + } else { + start = Promise.resolve(); + } + + start.then(function() { + dump("CALLING getNotification\n"); + registration.getNotifications().then(function(notifications) { + dump("RECD getNotification\n"); + is(notifications.length, 1, "There should be one stored notification"); + var notification = notifications[0]; + if (!notification) { + done(); + return; + } + ok(notification instanceof Notification, "Should be a Notification"); + is(notification.title, "This is a title", "Title should match"); + notification.close(); + done(); + }); + }); +} diff --git a/dom/workers/test/serviceworkers/notificationclick-otherwindow.html b/dom/workers/test/serviceworkers/notificationclick-otherwindow.html new file mode 100644 index 000000000..f64e82aab --- /dev/null +++ b/dom/workers/test/serviceworkers/notificationclick-otherwindow.html @@ -0,0 +1,30 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1114554 - controlled page</title> +<script class="testbody" type="text/javascript"> + var testWindow = parent; + if (opener) { + testWindow = opener; + } + + navigator.serviceWorker.ready.then(function(swr) { + var ifr = document.createElement("iframe"); + document.documentElement.appendChild(ifr); + ifr.contentWindow.ServiceWorkerRegistration.prototype.showNotification + .call(swr, "Hi there. The ServiceWorker should receive a click event for this.", { data: { complex: ["jsval", 5] }}); + }); + + navigator.serviceWorker.onmessage = function(msg) { + testWindow.callback(msg.data.result); + }; +</script> + +</head> +<body> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/notificationclick.html b/dom/workers/test/serviceworkers/notificationclick.html new file mode 100644 index 000000000..448764a1c --- /dev/null +++ b/dom/workers/test/serviceworkers/notificationclick.html @@ -0,0 +1,27 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1114554 - controlled page</title> +<script class="testbody" type="text/javascript"> + var testWindow = parent; + if (opener) { + testWindow = opener; + } + + navigator.serviceWorker.ready.then(function(swr) { + swr.showNotification("Hi there. The ServiceWorker should receive a click event for this.", { data: { complex: ["jsval", 5] }}); + }); + + navigator.serviceWorker.onmessage = function(msg) { + testWindow.callback(msg.data.result); + }; +</script> + +</head> +<body> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/notificationclick.js b/dom/workers/test/serviceworkers/notificationclick.js new file mode 100644 index 000000000..39e7adb81 --- /dev/null +++ b/dom/workers/test/serviceworkers/notificationclick.js @@ -0,0 +1,19 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/publicdomain/zero/1.0/ +// +onnotificationclick = function(e) { + self.clients.matchAll().then(function(clients) { + if (clients.length === 0) { + dump("********************* CLIENTS LIST EMPTY! Test will timeout! ***********************\n"); + return; + } + + clients.forEach(function(client) { + client.postMessage({ result: e.notification.data && + e.notification.data['complex'] && + e.notification.data['complex'][0] == "jsval" && + e.notification.data['complex'][1] == 5 }); + + }); + }); +} diff --git a/dom/workers/test/serviceworkers/notificationclick_focus.html b/dom/workers/test/serviceworkers/notificationclick_focus.html new file mode 100644 index 000000000..0152d397f --- /dev/null +++ b/dom/workers/test/serviceworkers/notificationclick_focus.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1144660 - controlled page</title> +<script class="testbody" type="text/javascript"> + var testWindow = parent; + if (opener) { + testWindow = opener; + } + + navigator.serviceWorker.ready.then(function(swr) { + swr.showNotification("Hi there. The ServiceWorker should receive a click event for this."); + }); + + navigator.serviceWorker.onmessage = function(msg) { + dump("GOT Message " + JSON.stringify(msg.data) + "\n"); + testWindow.callback(msg.data.ok); + }; +</script> + +</head> +<body> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/notificationclick_focus.js b/dom/workers/test/serviceworkers/notificationclick_focus.js new file mode 100644 index 000000000..5fb73651e --- /dev/null +++ b/dom/workers/test/serviceworkers/notificationclick_focus.js @@ -0,0 +1,40 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/publicdomain/zero/1.0/ +// + +function promisifyTimerFocus(client, delay) { + return new Promise(function(resolve, reject) { + setTimeout(function() { + client.focus().then(resolve, reject); + }, delay); + }); +} + +onnotificationclick = function(e) { + e.waitUntil(self.clients.matchAll().then(function(clients) { + if (clients.length === 0) { + dump("********************* CLIENTS LIST EMPTY! Test will timeout! ***********************\n"); + return Promise.resolve(); + } + + var immediatePromise = clients[0].focus(); + var withinTimeout = promisifyTimerFocus(clients[0], 100); + + var afterTimeout = promisifyTimerFocus(clients[0], 2000).then(function() { + throw "Should have failed!"; + }, function() { + return Promise.resolve(); + }); + + return Promise.all([immediatePromise, withinTimeout, afterTimeout]).then(function() { + clients.forEach(function(client) { + client.postMessage({ok: true}); + }); + }).catch(function(e) { + dump("Error " + e + "\n"); + clients.forEach(function(client) { + client.postMessage({ok: false}); + }); + }); + })); +} diff --git a/dom/workers/test/serviceworkers/notificationclose.html b/dom/workers/test/serviceworkers/notificationclose.html new file mode 100644 index 000000000..10c8da453 --- /dev/null +++ b/dom/workers/test/serviceworkers/notificationclose.html @@ -0,0 +1,37 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1265841 - controlled page</title> +<script class="testbody" type="text/javascript"> + var testWindow = parent; + if (opener) { + testWindow = opener; + } + + navigator.serviceWorker.ready.then(function(swr) { + return swr.showNotification( + "Hi there. The ServiceWorker should receive a close event for this.", + { data: { complex: ["jsval", 5] }}).then(function() { + return swr; + }); + }).then(function(swr) { + return swr.getNotifications(); + }).then(function(notifications) { + notifications.forEach(function(notification) { + notification.close(); + }); + }); + + navigator.serviceWorker.onmessage = function(msg) { + testWindow.callback(msg.data.result); + }; +</script> + +</head> +<body> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/notificationclose.js b/dom/workers/test/serviceworkers/notificationclose.js new file mode 100644 index 000000000..d48218075 --- /dev/null +++ b/dom/workers/test/serviceworkers/notificationclose.js @@ -0,0 +1,19 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/publicdomain/zero/1.0/ +// +onnotificationclose = function(e) { + self.clients.matchAll().then(function(clients) { + if (clients.length === 0) { + dump("********************* CLIENTS LIST EMPTY! Test will timeout! ***********************\n"); + return; + } + + clients.forEach(function(client) { + client.postMessage({ result: e.notification.data && + e.notification.data['complex'] && + e.notification.data['complex'][0] == "jsval" && + e.notification.data['complex'][1] == 5 }); + + }); + }); +} diff --git a/dom/workers/test/serviceworkers/notify_loaded.js b/dom/workers/test/serviceworkers/notify_loaded.js new file mode 100644 index 000000000..d07573b2c --- /dev/null +++ b/dom/workers/test/serviceworkers/notify_loaded.js @@ -0,0 +1 @@ +parent.postMessage('SCRIPT_LOADED', '*'); diff --git a/dom/workers/test/serviceworkers/opaque_intercept_worker.js b/dom/workers/test/serviceworkers/opaque_intercept_worker.js new file mode 100644 index 000000000..d593be783 --- /dev/null +++ b/dom/workers/test/serviceworkers/opaque_intercept_worker.js @@ -0,0 +1,25 @@ +var name = 'opaqueInterceptCache'; + +// Cross origin request to ensure that an opaque response is used +var prefix = 'http://example.com/tests/dom/workers/test/serviceworkers/' + +self.addEventListener('install', function(event) { + var request = new Request(prefix + 'notify_loaded.js', { mode: 'no-cors' }); + event.waitUntil( + Promise.all([caches.open(name), fetch(request)]).then(function(results) { + var cache = results[0]; + var response = results[1]; + return cache.put('./sw_clients/does_not_exist.js', response); + }) + ); +}); + +self.addEventListener('fetch', function (event) { + event.respondWith( + caches.open(name).then(function(cache) { + return cache.match(event.request); + }).then(function(response) { + return response || fetch(event.request); + }) + ); +}); diff --git a/dom/workers/test/serviceworkers/openWindow_worker.js b/dom/workers/test/serviceworkers/openWindow_worker.js new file mode 100644 index 000000000..46c0f998e --- /dev/null +++ b/dom/workers/test/serviceworkers/openWindow_worker.js @@ -0,0 +1,116 @@ +// the worker won't shut down between events because we increased +// the timeout values. +var client; +var window_count = 0; +var expected_window_count = 7; +var resolve_got_all_windows = null; +var got_all_windows = new Promise(function(res, rej) { + resolve_got_all_windows = res; +}); + +// |expected_window_count| needs to be updated for every new call that's +// expected to actually open a new window regardless of what |clients.openWindow| +// returns. +function testForUrl(url, throwType, clientProperties, resultsArray) { + return clients.openWindow(url) + .then(function(e) { + if (throwType != null) { + resultsArray.push({ + result: false, + message: "openWindow should throw " + throwType + }); + } else if (clientProperties) { + resultsArray.push({ + result: (e instanceof WindowClient), + message: "openWindow should resolve to a WindowClient" + }); + resultsArray.push({ + result: e.url == clientProperties.url, + message: "Client url should be " + clientProperties.url + }); + // Add more properties + } else { + resultsArray.push({ + result: e == null, + message: "Open window should resolve to null. Got: " + e + }); + } + }) + .catch(function(err) { + if (throwType == null) { + resultsArray.push({ + result: false, + message: "Unexpected throw: " + err + }); + } else { + resultsArray.push({ + result: err.toString().indexOf(throwType) >= 0, + message: "openWindow should throw: " + err + }); + } + }) +} + +onmessage = function(event) { + if (event.data == "testNoPopup") { + client = event.source; + + var results = []; + var promises = []; + promises.push(testForUrl("about:blank", "TypeError", null, results)); + promises.push(testForUrl("http://example.com", "InvalidAccessError", null, results)); + promises.push(testForUrl("_._*`InvalidURL", "InvalidAccessError", null, results)); + Promise.all(promises).then(function(e) { + client.postMessage(results); + }); + } + if (event.data == "NEW_WINDOW") { + window_count += 1; + if (window_count == expected_window_count) { + resolve_got_all_windows(); + } + } + + if (event.data == "CHECK_NUMBER_OF_WINDOWS") { + got_all_windows.then(function() { + return clients.matchAll(); + }).then(function(cl) { + event.source.postMessage({result: cl.length == expected_window_count, + message: "The number of windows is correct."}); + for (i = 0; i < cl.length; i++) { + cl[i].postMessage("CLOSE"); + } + }); + } +} + +onnotificationclick = function(e) { + var results = []; + var promises = []; + + var redirect = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/redirect.sjs?" + var redirect_xorigin = "http://example.com/tests/dom/workers/test/serviceworkers/redirect.sjs?" + var same_origin = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/open_window/client.html" + var different_origin = "http://example.com/tests/dom/workers/test/serviceworkers/open_window/client.html" + + + promises.push(testForUrl("about:blank", "TypeError", null, results)); + promises.push(testForUrl(different_origin, null, null, results)); + promises.push(testForUrl(same_origin, null, {url: same_origin}, results)); + promises.push(testForUrl("open_window/client.html", null, {url: same_origin}, results)); + + // redirect tests + promises.push(testForUrl(redirect + "open_window/client.html", null, + {url: same_origin}, results)); + promises.push(testForUrl(redirect + different_origin, null, null, results)); + + promises.push(testForUrl(redirect_xorigin + "open_window/client.html", null, + null, results)); + promises.push(testForUrl(redirect_xorigin + same_origin, null, + {url: same_origin}, results)); + + Promise.all(promises).then(function(e) { + client.postMessage(results); + }); +} + diff --git a/dom/workers/test/serviceworkers/open_window/client.html b/dom/workers/test/serviceworkers/open_window/client.html new file mode 100644 index 000000000..82b93ff7e --- /dev/null +++ b/dom/workers/test/serviceworkers/open_window/client.html @@ -0,0 +1,48 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1172870 - page opened by ServiceWorkerClients.OpenWindow</title> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + window.onload = function() { + if (window.location == "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/open_window/client.html") { + navigator.serviceWorker.ready.then(function(result) { + navigator.serviceWorker.onmessage = function(event) { + if (event.data !== "CLOSE") { + dump("ERROR: unexepected reply from the service worker.\n"); + } + if (parent) { + parent.postMessage("CLOSE", "*"); + } + window.close(); + } + navigator.serviceWorker.controller.postMessage("NEW_WINDOW"); + }) + } else { + window.onmessage = function(event) { + if (event.data !== "CLOSE") { + dump("ERROR: unexepected reply from the iframe.\n"); + } + window.close(); + } + + var iframe = document.createElement('iframe'); + iframe.src = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/open_window/client.html"; + document.body.appendChild(iframe); + } + } + +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/parse_error_worker.js b/dom/workers/test/serviceworkers/parse_error_worker.js new file mode 100644 index 000000000..b6a8ef0a1 --- /dev/null +++ b/dom/workers/test/serviceworkers/parse_error_worker.js @@ -0,0 +1,2 @@ +// intentional parse error. +var foo = {; diff --git a/dom/workers/test/serviceworkers/redirect.sjs b/dom/workers/test/serviceworkers/redirect.sjs new file mode 100644 index 000000000..b6249cadf --- /dev/null +++ b/dom/workers/test/serviceworkers/redirect.sjs @@ -0,0 +1,5 @@ +function handleRequest(request, response) +{ + response.setStatusLine(request.httpVersion, 301, "Moved Permanently"); + response.setHeader("Location", request.queryString, false); +} diff --git a/dom/workers/test/serviceworkers/redirect_post.sjs b/dom/workers/test/serviceworkers/redirect_post.sjs new file mode 100644 index 000000000..8b805be63 --- /dev/null +++ b/dom/workers/test/serviceworkers/redirect_post.sjs @@ -0,0 +1,35 @@ +const CC = Components.Constructor; +const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream"); + +function handleRequest(request, response) +{ + var query = {}; + request.queryString.split('&').forEach(function (val) { + var [name, value] = val.split('='); + query[name] = unescape(value); + }); + + var bodyStream = new BinaryInputStream(request.bodyInputStream); + var bodyBytes = []; + while ((bodyAvail = bodyStream.available()) > 0) + Array.prototype.push.apply(bodyBytes, bodyStream.readByteArray(bodyAvail)); + + var body = decodeURIComponent( + escape(String.fromCharCode.apply(null, bodyBytes))); + + var currentHop = query.hop ? parseInt(query.hop) : 0; + + var obj = JSON.parse(body); + if (currentHop < obj.hops) { + var newURL = '/tests/dom/workers/test/serviceworkers/redirect_post.sjs?hop=' + + (1 + currentHop); + response.setStatusLine(null, 307, 'redirect'); + response.setHeader('Location', newURL); + return; + } + + response.setHeader('Content-Type', 'application/json'); + response.write(body); +} diff --git a/dom/workers/test/serviceworkers/redirect_serviceworker.sjs b/dom/workers/test/serviceworkers/redirect_serviceworker.sjs new file mode 100644 index 000000000..9d3a0a2cd --- /dev/null +++ b/dom/workers/test/serviceworkers/redirect_serviceworker.sjs @@ -0,0 +1,4 @@ +function handleRequest(request, response) { + response.setStatusLine("1.1", 302, "Found"); + response.setHeader("Location", "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/worker.js"); +} diff --git a/dom/workers/test/serviceworkers/register_https.html b/dom/workers/test/serviceworkers/register_https.html new file mode 100644 index 000000000..d14d8c380 --- /dev/null +++ b/dom/workers/test/serviceworkers/register_https.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<script> +function ok(condition, message) { + parent.postMessage({type: "ok", status: condition, msg: message}, "*"); +} + +function done() { + parent.postMessage({type: "done"}, "*"); +} + +ok(location.protocol == "https:", "We should be loaded from HTTPS"); + +navigator.serviceWorker.register("empty.js", {scope: "register-https"}) + .then(reg => { + ok(false, "Registration should fail"); + done(); + }).catch(err => { + ok(err.name === "SecurityError", "Registration should fail with SecurityError"); + done(); + }); +</script> diff --git a/dom/workers/test/serviceworkers/sanitize/example_check_and_unregister.html b/dom/workers/test/serviceworkers/sanitize/example_check_and_unregister.html new file mode 100644 index 000000000..4de8f317b --- /dev/null +++ b/dom/workers/test/serviceworkers/sanitize/example_check_and_unregister.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<script> + function done(exists) { + parent.postMessage(exists, '*'); + } + + function fail() { + parent.postMessage("FAIL", '*'); + } + + navigator.serviceWorker.getRegistration(".").then(function(reg) { + if (reg) { + reg.unregister().then(done.bind(undefined, true), fail); + } else { + dump("getRegistration() returned undefined registration\n"); + done(false); + } + }, function(e) { + dump("getRegistration() failed\n"); + fail(); + }); +</script> + diff --git a/dom/workers/test/serviceworkers/sanitize/frame.html b/dom/workers/test/serviceworkers/sanitize/frame.html new file mode 100644 index 000000000..b4bf7a1ff --- /dev/null +++ b/dom/workers/test/serviceworkers/sanitize/frame.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<script> + fetch("intercept-this").then(function(r) { + if (!r.ok) { + return "FAIL"; + } + return r.text(); + }).then(function(body) { + parent.postMessage(body, '*'); + }); +</script> diff --git a/dom/workers/test/serviceworkers/sanitize/register.html b/dom/workers/test/serviceworkers/sanitize/register.html new file mode 100644 index 000000000..f1edd27be --- /dev/null +++ b/dom/workers/test/serviceworkers/sanitize/register.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<script> + function done() { + parent.postMessage('', '*'); + } + + navigator.serviceWorker.ready.then(done); + navigator.serviceWorker.register("../sanitize_worker.js", {scope: "."}); +</script> + diff --git a/dom/workers/test/serviceworkers/sanitize_worker.js b/dom/workers/test/serviceworkers/sanitize_worker.js new file mode 100644 index 000000000..66495e186 --- /dev/null +++ b/dom/workers/test/serviceworkers/sanitize_worker.js @@ -0,0 +1,5 @@ +onfetch = function(e) { + if (e.request.url.indexOf("intercept-this") != -1) { + e.respondWith(new Response("intercepted")); + } +} diff --git a/dom/workers/test/serviceworkers/scope/scope_worker.js b/dom/workers/test/serviceworkers/scope/scope_worker.js new file mode 100644 index 000000000..4164e7a24 --- /dev/null +++ b/dom/workers/test/serviceworkers/scope/scope_worker.js @@ -0,0 +1,2 @@ +// This worker is used to test if calling register() without a scope argument +// leads to scope being relative to service worker script. diff --git a/dom/workers/test/serviceworkers/serviceworker.html b/dom/workers/test/serviceworkers/serviceworker.html new file mode 100644 index 000000000..11edd001a --- /dev/null +++ b/dom/workers/test/serviceworkers/serviceworker.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <script> + navigator.serviceWorker.register("worker.js"); + </script> + </head> + <body> + This is a test page. + </body> +<html> diff --git a/dom/workers/test/serviceworkers/serviceworker_not_sharedworker.js b/dom/workers/test/serviceworkers/serviceworker_not_sharedworker.js new file mode 100644 index 000000000..077da2366 --- /dev/null +++ b/dom/workers/test/serviceworkers/serviceworker_not_sharedworker.js @@ -0,0 +1,21 @@ +function OnMessage(e) +{ + if (e.data.msg == "whoareyou") { + if ("ServiceWorker" in self) { + self.clients.matchAll().then(function(clients) { + clients[0].postMessage({result: "serviceworker"}); + }); + } else { + port.postMessage({result: "sharedworker"}); + } + } +}; + +var port; +onconnect = function(e) { + port = e.ports[0]; + port.onmessage = OnMessage; + port.start(); +}; + +onmessage = OnMessage; diff --git a/dom/workers/test/serviceworkers/serviceworker_wrapper.js b/dom/workers/test/serviceworkers/serviceworker_wrapper.js new file mode 100644 index 000000000..6a7ec0c0f --- /dev/null +++ b/dom/workers/test/serviceworkers/serviceworker_wrapper.js @@ -0,0 +1,101 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/publicdomain/zero/1.0/ +// +// ServiceWorker equivalent of worker_wrapper.js. + +var client; + +function ok(a, msg) { + dump("OK: " + !!a + " => " + a + ": " + msg + "\n"); + client.postMessage({type: 'status', status: !!a, msg: a + ": " + msg }); +} + +function is(a, b, msg) { + dump("IS: " + (a===b) + " => " + a + " | " + b + ": " + msg + "\n"); + client.postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg }); +} + +function workerTestArrayEquals(a, b) { + if (!Array.isArray(a) || !Array.isArray(b) || a.length != b.length) { + return false; + } + for (var i = 0, n = a.length; i < n; ++i) { + if (a[i] !== b[i]) { + return false; + } + } + return true; +} + +function workerTestDone() { + client.postMessage({ type: 'finish' }); +} + +function workerTestGetVersion(cb) { + addEventListener('message', function workerTestGetVersionCB(e) { + if (e.data.type !== 'returnVersion') { + return; + } + removeEventListener('message', workerTestGetVersionCB); + cb(e.data.result); + }); + client.postMessage({ + type: 'getVersion' + }); +} + +function workerTestGetUserAgent(cb) { + addEventListener('message', function workerTestGetUserAgentCB(e) { + if (e.data.type !== 'returnUserAgent') { + return; + } + removeEventListener('message', workerTestGetUserAgentCB); + cb(e.data.result); + }); + client.postMessage({ + type: 'getUserAgent' + }); +} + +function workerTestGetOSCPU(cb) { + addEventListener('message', function workerTestGetOSCPUCB(e) { + if (e.data.type !== 'returnOSCPU') { + return; + } + removeEventListener('message', workerTestGetOSCPUCB); + cb(e.data.result); + }); + client.postMessage({ + type: 'getOSCPU' + }); +} + +function workerTestGetStorageManager(cb) { + addEventListener('message', function workerTestGetStorageManagerCB(e) { + if (e.data.type !== 'returnStorageManager') { + return; + } + removeEventListener('message', workerTestGetStorageManagerCB); + cb(e.data.result); + }); + client.postMessage({ + type: 'getStorageManager' + }); +} + +addEventListener('message', function workerWrapperOnMessage(e) { + removeEventListener('message', workerWrapperOnMessage); + var data = e.data; + self.clients.matchAll().then(function(clients) { + client = clients[0]; + try { + importScripts(data.script); + } catch(e) { + client.postMessage({ + type: 'status', + status: false, + msg: 'worker failed to import ' + data.script + "; error: " + e.message + }); + } + }); +}); diff --git a/dom/workers/test/serviceworkers/serviceworkerinfo_iframe.html b/dom/workers/test/serviceworkers/serviceworkerinfo_iframe.html new file mode 100644 index 000000000..a0a2de760 --- /dev/null +++ b/dom/workers/test/serviceworkers/serviceworkerinfo_iframe.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <script> + window.onmessage = function (event) { + if (event.data !== "register") { + return; + } + var promise = navigator.serviceWorker.register("worker.js"); + window.onmessage = function (event) { + if (event.data !== "unregister") { + return; + } + promise.then(function (registration) { + registration.unregister(); + }); + window.onmessage = null; + }; + }; + </script> + </head> + <body> + This is a test page. + </body> +<html> diff --git a/dom/workers/test/serviceworkers/serviceworkermanager_iframe.html b/dom/workers/test/serviceworkers/serviceworkermanager_iframe.html new file mode 100644 index 000000000..0df93da96 --- /dev/null +++ b/dom/workers/test/serviceworkers/serviceworkermanager_iframe.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <script> + window.onmessage = function (event) { + if (event.data !== "register") { + return; + } + var promise = navigator.serviceWorker.register("worker.js"); + window.onmessage = function (event) { + if (event.data !== "register") { + return; + } + promise = promise.then(function (registration) { + return navigator.serviceWorker.register("worker2.js"); + }); + window.onmessage = function (event) { + if (event.data !== "unregister") { + return; + } + promise.then(function (registration) { + registration.unregister(); + }); + window.onmessage = null; + }; + }; + }; + </script> + </head> + <body> + This is a test page. + </body> +<html> diff --git a/dom/workers/test/serviceworkers/serviceworkerregistrationinfo_iframe.html b/dom/workers/test/serviceworkers/serviceworkerregistrationinfo_iframe.html new file mode 100644 index 000000000..f093d38db --- /dev/null +++ b/dom/workers/test/serviceworkers/serviceworkerregistrationinfo_iframe.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <script> + var reg; + window.onmessage = function (event) { + if (event.data !== "register") { + return; + } + var promise = navigator.serviceWorker.register("worker.js"); + window.onmessage = function (event) { + if (event.data === "register") { + promise.then(function (registration) { + return navigator.serviceWorker.register("worker2.js") + .then(function(registration) { + reg = registration; + }); + }); + } else if (event.data === "unregister") { + reg.unregister(); + } + }; + }; + </script> + </head> + <body> + This is a test page. + </body> +<html> diff --git a/dom/workers/test/serviceworkers/sharedWorker_fetch.js b/dom/workers/test/serviceworkers/sharedWorker_fetch.js new file mode 100644 index 000000000..4970a1fd2 --- /dev/null +++ b/dom/workers/test/serviceworkers/sharedWorker_fetch.js @@ -0,0 +1,29 @@ +var clients = new Array(); +clients.length = 0; + +var broadcast = function(message) { + var length = clients.length; + for (var i = 0; i < length; i++) { + port = clients[i]; + port.postMessage(message); + } +} + +onconnect = function(e) { + clients.push(e.ports[0]); + if (clients.length == 1) { + clients[0].postMessage("Connected"); + } else if (clients.length == 2) { + broadcast("BothConnected"); + clients[0].onmessage = function(e) { + if (e.data == "StartFetchWithWrongIntegrity") { + // The fetch will succeed because the integrity value is invalid and we + // are looking for the console message regarding the bad integrity value. + fetch("SharedWorker_SRIFailed.html", {"integrity": "abc"}).then( + function () { + clients[0].postMessage('SRI_failed'); + }); + } + } + } +} diff --git a/dom/workers/test/serviceworkers/simpleregister/index.html b/dom/workers/test/serviceworkers/simpleregister/index.html new file mode 100644 index 000000000..2c0eb5345 --- /dev/null +++ b/dom/workers/test/serviceworkers/simpleregister/index.html @@ -0,0 +1,51 @@ +<html> + <head></head> + <body> + <script type="text/javascript"> + var expectedEvents = 2; + function eventReceived() { + window.parent.postMessage({ type: "check", status: expectedEvents > 0, msg: "updatefound received" }, "*"); + + if (--expectedEvents) { + window.parent.postMessage({ type: "finish" }, "*"); + } + } + + navigator.serviceWorker.getRegistrations().then(function(a) { + window.parent.postMessage({ type: "check", status: Array.isArray(a), + msg: "getRegistrations returns an array" }, "*"); + window.parent.postMessage({ type: "check", status: a.length > 0, + msg: "getRegistrations returns an array with 1 item" }, "*"); + for (var i = 0; i < a.length; ++i) { + window.parent.postMessage({ type: "check", status: a[i] instanceof ServiceWorkerRegistration, + msg: "getRegistrations returns an array of ServiceWorkerRegistration objects" }, "*"); + if (a[i].scope.match(/simpleregister\//)) { + a[i].onupdatefound = function(e) { + eventReceived(); + } + } + } + }); + + navigator.serviceWorker.getRegistration('http://mochi.test:8888/tests/dom/workers/test/serviceworkers/simpleregister/') + .then(function(a) { + window.parent.postMessage({ type: "check", status: a instanceof ServiceWorkerRegistration, + msg: "getRegistration returns a ServiceWorkerRegistration" }, "*"); + a.onupdatefound = function(e) { + eventReceived(); + } + }); + + navigator.serviceWorker.getRegistration('http://www.something_else.net/') + .then(function(a) { + window.parent.postMessage({ type: "check", status: false, + msg: "getRegistration should throw for security error!" }, "*"); + }, function(a) { + window.parent.postMessage({ type: "check", status: true, + msg: "getRegistration should throw for security error!" }, "*"); + }); + + window.parent.postMessage({ type: "ready" }, "*"); + </script> + </body> +</html> diff --git a/dom/workers/test/serviceworkers/simpleregister/ready.html b/dom/workers/test/serviceworkers/simpleregister/ready.html new file mode 100644 index 000000000..3afc4bfdb --- /dev/null +++ b/dom/workers/test/serviceworkers/simpleregister/ready.html @@ -0,0 +1,15 @@ +<html> + <head></head> + <body> + <script type="text/javascript"> + + window.addEventListener('message', function(evt) { + navigator.serviceWorker.ready.then(function() { + evt.ports[0].postMessage("WOW!"); + }); + }, false); + + </script> + </body> +</html> + diff --git a/dom/workers/test/serviceworkers/skip_waiting_installed_worker.js b/dom/workers/test/serviceworkers/skip_waiting_installed_worker.js new file mode 100644 index 000000000..68573f100 --- /dev/null +++ b/dom/workers/test/serviceworkers/skip_waiting_installed_worker.js @@ -0,0 +1,6 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +self.addEventListener('install', evt => { + evt.waitUntil(self.skipWaiting()); +}); diff --git a/dom/workers/test/serviceworkers/skip_waiting_scope/index.html b/dom/workers/test/serviceworkers/skip_waiting_scope/index.html new file mode 100644 index 000000000..b8a64d512 --- /dev/null +++ b/dom/workers/test/serviceworkers/skip_waiting_scope/index.html @@ -0,0 +1,37 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1131352 - Add ServiceWorkerGlobalScope skipWaiting()</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + if (!parent) { + info("skip_waiting_scope/index.html shouldn't be launched directly!"); + } + + navigator.serviceWorker.ready.then(function() { + parent.postMessage("READY", "*"); + }); + + navigator.serviceWorker.oncontrollerchange = function() { + parent.postMessage({ + event: "controllerchange", + controllerScriptURL: navigator.serviceWorker.controller && + navigator.serviceWorker.controller.scriptURL + }, "*"); + } + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/source_message_posting_worker.js b/dom/workers/test/serviceworkers/source_message_posting_worker.js new file mode 100644 index 000000000..36ce951fe --- /dev/null +++ b/dom/workers/test/serviceworkers/source_message_posting_worker.js @@ -0,0 +1,16 @@ +onmessage = function(e) { + if (!e.source) { + dump("ERROR: message doesn't have a source."); + } + + if (!(e instanceof ExtendableMessageEvent)) { + e.source.postMessage("ERROR. event is not an extendable message event."); + } + + // The client should be a window client + if (e.source instanceof WindowClient) { + e.source.postMessage(e.data); + } else { + e.source.postMessage("ERROR. source is not a window client."); + } +}; diff --git a/dom/workers/test/serviceworkers/strict_mode_warning.js b/dom/workers/test/serviceworkers/strict_mode_warning.js new file mode 100644 index 000000000..38418de3d --- /dev/null +++ b/dom/workers/test/serviceworkers/strict_mode_warning.js @@ -0,0 +1,4 @@ +function f() { + return 1; + return 2; +} diff --git a/dom/workers/test/serviceworkers/sw_bad_mime_type.js b/dom/workers/test/serviceworkers/sw_bad_mime_type.js new file mode 100644 index 000000000..f371807db --- /dev/null +++ b/dom/workers/test/serviceworkers/sw_bad_mime_type.js @@ -0,0 +1 @@ +// I need some contents. diff --git a/dom/workers/test/serviceworkers/sw_bad_mime_type.js^headers^ b/dom/workers/test/serviceworkers/sw_bad_mime_type.js^headers^ new file mode 100644 index 000000000..a1f9e38d9 --- /dev/null +++ b/dom/workers/test/serviceworkers/sw_bad_mime_type.js^headers^ @@ -0,0 +1 @@ +Content-Type: text/plain diff --git a/dom/workers/test/serviceworkers/sw_clients/file_blob_upload_frame.html b/dom/workers/test/serviceworkers/sw_clients/file_blob_upload_frame.html new file mode 100644 index 000000000..e594c514d --- /dev/null +++ b/dom/workers/test/serviceworkers/sw_clients/file_blob_upload_frame.html @@ -0,0 +1,77 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>test file blob upload with SW interception</title> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + +if (!parent) { + dump("sw_clients/file_blob_upload_frame.html shouldn't be launched directly!"); +} + +function makeFileBlob(obj) { + return new Promise(function(resolve, reject) { + + var request = indexedDB.open(window.location.pathname, 1); + request.onerror = reject; + request.onupgradeneeded = function(evt) { + var db = evt.target.result; + db.onerror = reject; + + var objectStore = db.createObjectStore('test', { autoIncrement: true }); + var index = objectStore.createIndex('test', 'index'); + }; + + request.onsuccess = function(evt) { + var db = evt.target.result; + db.onerror = reject; + + var blob = new Blob([JSON.stringify(obj)], + { type: 'application/json' }); + var data = { blob: blob, index: 5 }; + + objectStore = db.transaction('test', 'readwrite').objectStore('test'); + objectStore.add(data).onsuccess = function(evt) { + var key = evt.target.result; + objectStore = db.transaction('test').objectStore('test'); + objectStore.get(key).onsuccess = function(evt) { + resolve(evt.target.result.blob); + }; + }; + }; + }); +} + +navigator.serviceWorker.ready.then(function() { + parent.postMessage({ status: 'READY' }, '*'); +}); + +var URL = '/tests/dom/workers/test/serviceworkers/redirect_post.sjs'; + +addEventListener('message', function(evt) { + if (evt.data.type = 'TEST') { + makeFileBlob(evt.data.body).then(function(blob) { + return fetch(URL, { method: 'POST', body: blob }); + }).then(function(response) { + return response.json(); + }).then(function(result) { + parent.postMessage({ status: 'OK', result: result }, '*'); + }).catch(function(e) { + parent.postMessage({ status: 'ERROR', result: e.toString() }, '*'); + }); + } +}); + +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/sw_clients/navigator.html b/dom/workers/test/serviceworkers/sw_clients/navigator.html new file mode 100644 index 000000000..f6019bf28 --- /dev/null +++ b/dom/workers/test/serviceworkers/sw_clients/navigator.html @@ -0,0 +1,35 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 982726 - test match_all not crashing</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + if (!parent) { + dump("sw_clients/navigator.html shouldn't be launched directly!\n"); + } + + window.addEventListener("message", function(event) { + if (event.data.type === "NAVIGATE") { + window.location = event.data.url; + } + }); + + navigator.serviceWorker.ready.then(function() { + parent.postMessage("NAVIGATOR_READY", "*"); + }); + +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/sw_clients/refresher.html b/dom/workers/test/serviceworkers/sw_clients/refresher.html new file mode 100644 index 000000000..054f6bfc8 --- /dev/null +++ b/dom/workers/test/serviceworkers/sw_clients/refresher.html @@ -0,0 +1,39 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 982726 - test match_all not crashing</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <!-- some tests will intercept this bogus script request --> + <script type="text/javascript" src="does_not_exist.js"></script> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + if (!parent) { + dump("sw_clients/simple.html shouldn't be launched directly!"); + } + + window.addEventListener("message", function(event) { + if (event.data === "REFRESH") { + window.location.reload(); + } else if (event.data === "FORCE_REFRESH") { + window.location.reload(true); + } + }); + + navigator.serviceWorker.ready.then(function() { + parent.postMessage("READY", "*"); + }); + +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/sw_clients/refresher_cached.html b/dom/workers/test/serviceworkers/sw_clients/refresher_cached.html new file mode 100644 index 000000000..3ec0cc427 --- /dev/null +++ b/dom/workers/test/serviceworkers/sw_clients/refresher_cached.html @@ -0,0 +1,38 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 982726 - test match_all not crashing</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + if (!parent) { + info("sw_clients/simple.html shouldn't be launched directly!"); + } + + window.addEventListener("message", function(event) { + if (event.data === "REFRESH") { + window.location.reload(); + } else if (event.data === "FORCE_REFRESH") { + window.location.reload(true); + } + }); + + navigator.serviceWorker.ready.then(function() { + parent.postMessage("READY_CACHED", "*"); + }); + +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/sw_clients/refresher_cached_compressed.html b/dom/workers/test/serviceworkers/sw_clients/refresher_cached_compressed.html Binary files differnew file mode 100644 index 000000000..55e97ac24 --- /dev/null +++ b/dom/workers/test/serviceworkers/sw_clients/refresher_cached_compressed.html diff --git a/dom/workers/test/serviceworkers/sw_clients/refresher_cached_compressed.html^headers^ b/dom/workers/test/serviceworkers/sw_clients/refresher_cached_compressed.html^headers^ new file mode 100644 index 000000000..4204d8601 --- /dev/null +++ b/dom/workers/test/serviceworkers/sw_clients/refresher_cached_compressed.html^headers^ @@ -0,0 +1,2 @@ +Content-Type: text/html +Content-Encoding: gzip diff --git a/dom/workers/test/serviceworkers/sw_clients/refresher_compressed.html b/dom/workers/test/serviceworkers/sw_clients/refresher_compressed.html Binary files differnew file mode 100644 index 000000000..7a45bcafa --- /dev/null +++ b/dom/workers/test/serviceworkers/sw_clients/refresher_compressed.html diff --git a/dom/workers/test/serviceworkers/sw_clients/refresher_compressed.html^headers^ b/dom/workers/test/serviceworkers/sw_clients/refresher_compressed.html^headers^ new file mode 100644 index 000000000..4204d8601 --- /dev/null +++ b/dom/workers/test/serviceworkers/sw_clients/refresher_compressed.html^headers^ @@ -0,0 +1,2 @@ +Content-Type: text/html +Content-Encoding: gzip diff --git a/dom/workers/test/serviceworkers/sw_clients/service_worker_controlled.html b/dom/workers/test/serviceworkers/sw_clients/service_worker_controlled.html new file mode 100644 index 000000000..e0d7bce57 --- /dev/null +++ b/dom/workers/test/serviceworkers/sw_clients/service_worker_controlled.html @@ -0,0 +1,38 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>controlled page</title> + <!-- + Paged controlled by a service worker for testing matchAll(). + See bug 982726, 1058311. + --> +<script class="testbody" type="text/javascript"> + function fail(msg) { + info("service_worker_controlled.html: " + msg); + opener.postMessage("FAIL", "*"); + } + + if (!parent) { + info("service_worker_controlled.html should not be launched directly!"); + } + + window.onload = function() { + navigator.serviceWorker.ready.then(function(swr) { + parent.postMessage("READY", "*"); + }); + } + + navigator.serviceWorker.onmessage = function(msg) { + // forward message to the test page. + parent.postMessage(msg.data, "*"); + }; +</script> + +</head> +<body> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/sw_clients/simple.html b/dom/workers/test/serviceworkers/sw_clients/simple.html new file mode 100644 index 000000000..3e4d7deca --- /dev/null +++ b/dom/workers/test/serviceworkers/sw_clients/simple.html @@ -0,0 +1,30 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 982726 - test match_all not crashing</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + if (!parent) { + info("sw_clients/simple.html shouldn't be launched directly!"); + } + + navigator.serviceWorker.ready.then(function() { + parent.postMessage("READY", "*"); + }); + +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/swa/worker_scope_different.js b/dom/workers/test/serviceworkers/swa/worker_scope_different.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/dom/workers/test/serviceworkers/swa/worker_scope_different.js diff --git a/dom/workers/test/serviceworkers/swa/worker_scope_different.js^headers^ b/dom/workers/test/serviceworkers/swa/worker_scope_different.js^headers^ new file mode 100644 index 000000000..e85a7f09d --- /dev/null +++ b/dom/workers/test/serviceworkers/swa/worker_scope_different.js^headers^ @@ -0,0 +1 @@ +Service-Worker-Allowed: different/path diff --git a/dom/workers/test/serviceworkers/swa/worker_scope_different2.js b/dom/workers/test/serviceworkers/swa/worker_scope_different2.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/dom/workers/test/serviceworkers/swa/worker_scope_different2.js diff --git a/dom/workers/test/serviceworkers/swa/worker_scope_different2.js^headers^ b/dom/workers/test/serviceworkers/swa/worker_scope_different2.js^headers^ new file mode 100644 index 000000000..e37307d66 --- /dev/null +++ b/dom/workers/test/serviceworkers/swa/worker_scope_different2.js^headers^ @@ -0,0 +1 @@ +Service-Worker-Allowed: /different/path diff --git a/dom/workers/test/serviceworkers/swa/worker_scope_precise.js b/dom/workers/test/serviceworkers/swa/worker_scope_precise.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/dom/workers/test/serviceworkers/swa/worker_scope_precise.js diff --git a/dom/workers/test/serviceworkers/swa/worker_scope_precise.js^headers^ b/dom/workers/test/serviceworkers/swa/worker_scope_precise.js^headers^ new file mode 100644 index 000000000..30b053055 --- /dev/null +++ b/dom/workers/test/serviceworkers/swa/worker_scope_precise.js^headers^ @@ -0,0 +1 @@ +Service-Worker-Allowed: /tests/dom/workers/test/serviceworkers/swa diff --git a/dom/workers/test/serviceworkers/swa/worker_scope_too_deep.js b/dom/workers/test/serviceworkers/swa/worker_scope_too_deep.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/dom/workers/test/serviceworkers/swa/worker_scope_too_deep.js diff --git a/dom/workers/test/serviceworkers/swa/worker_scope_too_deep.js^headers^ b/dom/workers/test/serviceworkers/swa/worker_scope_too_deep.js^headers^ new file mode 100644 index 000000000..b2056fc4a --- /dev/null +++ b/dom/workers/test/serviceworkers/swa/worker_scope_too_deep.js^headers^ @@ -0,0 +1 @@ +Service-Worker-Allowed: /tests/dom/workers/test/serviceworkers/swa/deep/way/too/specific diff --git a/dom/workers/test/serviceworkers/swa/worker_scope_too_narrow.js b/dom/workers/test/serviceworkers/swa/worker_scope_too_narrow.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/dom/workers/test/serviceworkers/swa/worker_scope_too_narrow.js diff --git a/dom/workers/test/serviceworkers/swa/worker_scope_too_narrow.js^headers^ b/dom/workers/test/serviceworkers/swa/worker_scope_too_narrow.js^headers^ new file mode 100644 index 000000000..22add13bf --- /dev/null +++ b/dom/workers/test/serviceworkers/swa/worker_scope_too_narrow.js^headers^ @@ -0,0 +1 @@ +Service-Worker-Allowed: /tests/dom/workers diff --git a/dom/workers/test/serviceworkers/test_bug1151916.html b/dom/workers/test/serviceworkers/test_bug1151916.html new file mode 100644 index 000000000..92811775b --- /dev/null +++ b/dom/workers/test/serviceworkers/test_bug1151916.html @@ -0,0 +1,105 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1151916 - Test principal is set on cached serviceworkers</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +<!-- + If the principal is not set, accessing self.caches in the worker will crash. +--> +</head> +<body> +<p id="display"></p> +<div id="content"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + var frame; + + function listenForMessage() { + var p = new Promise(function(resolve, reject) { + window.onmessage = function(e) { + if (e.data.status == "failed") { + ok(false, "iframe had error " + e.data.message); + reject(e.data.message); + } else if (e.data.status == "success") { + ok(true, "iframe step success " + e.data.message); + resolve(e.data.message); + } else { + ok(false, "Unexpected message " + e.data); + reject(); + } + } + }); + + return p; + } + + // We have the iframe register for its own scope so that this page is not + // holding any references when we GC. + function register() { + var p = listenForMessage(); + + frame = document.createElement("iframe"); + document.body.appendChild(frame); + frame.src = "bug1151916_driver.html"; + + return p; + } + + function unloadFrame() { + frame.src = "about:blank"; + frame.parentNode.removeChild(frame); + frame = null; + } + + function gc() { + return new Promise(function(resolve) { + SpecialPowers.exactGC(resolve); + }); + } + + function testCaches() { + var p = listenForMessage(); + + frame = document.createElement("iframe"); + document.body.appendChild(frame); + frame.src = "bug1151916_driver.html"; + + return p; + } + + function unregister() { + return navigator.serviceWorker.getRegistration("./bug1151916_driver.html").then(function(reg) { + ok(reg instanceof ServiceWorkerRegistration, "Must have valid registration."); + return reg.unregister(); + }); + } + + function runTest() { + register() + .then(unloadFrame) + .then(gc) + .then(testCaches) + .then(unregister) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + }).then(SimpleTest.finish); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.caches.enabled", true], + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_bug1240436.html b/dom/workers/test/serviceworkers/test_bug1240436.html new file mode 100644 index 000000000..e93535840 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_bug1240436.html @@ -0,0 +1,34 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test for encoding of service workers</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + SimpleTest.waitForExplicitFinish(); + + function runTest() { + navigator.serviceWorker.register("bug1240436_worker.js") + .then(reg => reg.unregister()) + .then(() => ok(true, "service worker register script succeed")) + .catch(err => ok(false, "service worker register script faled " + err)) + .then(() => SimpleTest.finish()); + } + + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_claim.html b/dom/workers/test/serviceworkers/test_claim.html new file mode 100644 index 000000000..d7015850f --- /dev/null +++ b/dom/workers/test/serviceworkers/test_claim.html @@ -0,0 +1,172 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1130684 - Test service worker clients claim onactivate </title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + var registration_1; + var registration_2; + var client; + + function register_1() { + return navigator.serviceWorker.register("claim_worker_1.js", + { scope: "./" }) + .then((swr) => registration_1 = swr); + } + + function register_2() { + return navigator.serviceWorker.register("claim_worker_2.js", + { scope: "./claim_clients/client.html" }) + .then((swr) => registration_2 = swr); + } + + function unregister(reg) { + return reg.unregister().then(function(result) { + ok(result, "Unregister should return true."); + }); + } + + function createClient() { + var p = new Promise(function(res, rej) { + window.onmessage = function(e) { + if (e.data === "READY") { + res(); + } + } + }); + + content = document.getElementById("content"); + ok(content, "parent exists."); + + client = document.createElement("iframe"); + client.setAttribute('src', "claim_clients/client.html"); + content.appendChild(client); + + return p; + } + + function testController() { + ok(navigator.serviceWorker.controller.scriptURL.match("claim_worker_1"), + "Controlling service worker has the correct url."); + } + + function testClientWasClaimed(expected) { + var resolveClientMessage, resolveClientControllerChange; + var messageFromClient = new Promise(function(res, rej) { + resolveClientMessage = res; + }); + var controllerChangeFromClient = new Promise(function(res, rej) { + resolveClientControllerChange = res; + }); + window.onmessage = function(e) { + if (!e.data.event) { + ok(false, "Unknown message received: " + e.data); + } + + if (e.data.event === "controllerchange") { + ok(e.data.controller, + "Client was claimed and received controllerchange event."); + resolveClientControllerChange(); + } + + if (e.data.event === "message") { + ok(e.data.data.resolve_value === undefined, + "Claim should resolve with undefined."); + ok(e.data.data.message === expected.message, + "Client received message from claiming worker."); + ok(e.data.data.match_count_before === expected.match_count_before, + "MatchAll clients count before claim should be " + expected.match_count_before); + ok(e.data.data.match_count_after === expected.match_count_after, + "MatchAll clients count after claim should be " + expected.match_count_after); + resolveClientMessage(); + } + } + + return Promise.all([messageFromClient, controllerChangeFromClient]) + .then(() => window.onmessage = null); + } + + function testClaimFirstWorker() { + // wait for the worker to control us + var controllerChange = new Promise(function(res, rej) { + navigator.serviceWorker.oncontrollerchange = function(e) { + ok(true, "controller changed event received."); + res(); + }; + }); + + var messageFromWorker = new Promise(function(res, rej) { + navigator.serviceWorker.onmessage = function(e) { + ok(e.data.resolve_value === undefined, + "Claim should resolve with undefined."); + ok(e.data.message === "claim_worker_1", + "Received message from claiming worker."); + ok(e.data.match_count_before === 0, + "Worker doesn't control any client before claim."); + ok(e.data.match_count_after === 2, "Worker should claim 2 clients."); + res(); + } + }); + + var clientClaim = testClientWasClaimed({ + message: "claim_worker_1", + match_count_before: 0, + match_count_after: 2 + }); + + return Promise.all([controllerChange, messageFromWorker, clientClaim]) + .then(testController); + } + + function testClaimSecondWorker() { + navigator.serviceWorker.oncontrollerchange = function(e) { + ok(false, "Claim_worker_2 shouldn't claim this window."); + } + + navigator.serviceWorker.onmessage = function(e) { + ok(false, "Claim_worker_2 shouldn't claim this window."); + } + + var clientClaim = testClientWasClaimed({ + message: "claim_worker_2", + match_count_before: 0, + match_count_after: 1 + }); + + return clientClaim.then(testController); + } + + function runTest() { + createClient() + .then(register_1) + .then(testClaimFirstWorker) + .then(register_2) + .then(testClaimSecondWorker) + .then(function() { return unregister(registration_1); }) + .then(function() { return unregister(registration_2); }) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + }).then(SimpleTest.finish); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_claim_fetch.html b/dom/workers/test/serviceworkers/test_claim_fetch.html new file mode 100644 index 000000000..8db6d304e --- /dev/null +++ b/dom/workers/test/serviceworkers/test_claim_fetch.html @@ -0,0 +1,98 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1130684 - Test fetch events are intercepted after claim </title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> + <a ping="ping" href="fetch.txt">link</a> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + var registration; + + function register() { + return navigator.serviceWorker.register("claim_fetch_worker.js", + { scope: "./" }) + .then((swr) => registration = swr); + } + + function unregister() { + return registration.unregister().then(function(result) { + ok(result, "Unregister should return true."); + }); + } + + function createClient() { + var p = new Promise(function(res, rej){ + window.onmessage = function(e) { + if(e.data === "READY") { + res(); + } + } + }); + + var content = document.getElementById("content"); + ok(content, "Parent exists."); + + iframe = document.createElement("iframe"); + iframe.setAttribute('src', "sw_clients/service_worker_controlled.html"); + content.appendChild(iframe); + + return p; + } + + function testFetch(before) { + return fetch("fetch/real-file.txt").then(function(res) { + ok(res.ok, "Response should be valid."); + return res.text().then(function(body) { + if (before) { + ok(body !== "Fetch was intercepted", "Fetch events should not be intercepted."); + } else { + ok(body === "Fetch was intercepted", "Fetch events should be intercepted."); + } + }); + }); + } + + function claimThisPage() { + ok(registration.active, "Worker is active."); + var p = new Promise(function (res, rej) { + navigator.serviceWorker.oncontrollerchange = res; + }); + + registration.active.postMessage("Claim"); + + return p; + } + + function runTest() { + register() + .then(createClient) + .then(testFetch.bind(this, true)) + .then(claimThisPage) + .then(testFetch.bind(this, false)) + .then(unregister) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + }).then(SimpleTest.finish); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_claim_oninstall.html b/dom/workers/test/serviceworkers/test_claim_oninstall.html new file mode 100644 index 000000000..4605cfb76 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_claim_oninstall.html @@ -0,0 +1,78 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1130684 - Test service worker clients.claim oninstall</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + var registration; + + function register() { + return navigator.serviceWorker.register("claim_oninstall_worker.js", + { scope: "./" }) + .then((swr) => registration = swr); + } + + + function unregister() { + return registration.unregister().then(function(result) { + ok(result, "Unregister should return true."); + }); + } + + function testClaim() { + ok(registration.installing, "Worker should be in installing state"); + + navigator.serviceWorker.oncontrollerchange = function() { + ok(false, "Claim should not succeed when the worker is not active."); + } + + var p = new Promise(function(res, rej) { + var worker = registration.installing; + worker.onstatechange = function(e) { + if (worker.state === 'installed') { + is(worker, registration.waiting, "Worker should be in waiting state"); + } else if (worker.state === 'activated') { + // The worker will become active only if claim will reject inside the + // install handler. + is(worker, registration.active, + "Claim should reject if the worker is not active"); + ok(navigator.serviceWorker.controller === null, "Client is not controlled."); + e.target.onstatechange = null; + res(); + } + } + }); + + return p; + } + + function runTest() { + register() + .then(testClaim) + .then(unregister) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + }).then(SimpleTest.finish); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_client_focus.html b/dom/workers/test/serviceworkers/test_client_focus.html new file mode 100644 index 000000000..b0bf43bb3 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_client_focus.html @@ -0,0 +1,96 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1130686 - Test service worker client.focus </title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +<!-- + This test checks that client.focus is available. + Actual focusing is tested by test_notificationclick_focus.html since only notification events have permission to change focus. +--> +</head> +<body> +<p id="display"></p> +<div id="content"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + var registration; + var worker; + + function start() { + return navigator.serviceWorker.register("client_focus_worker.js", + { scope: "./sw_clients/focus_stealing_client.html" }) + .then((swr) => registration = swr); + } + + function unregister() { + return registration.unregister().then(function(result) { + ok(result, "Unregister should return true."); + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + } + + function loseFocus() { + var p = new Promise(function(res, rej) { + window.onmessage = function(e) { + if (e.data == "READY") { + ok(true, "iframe created."); + iframe.contentWindow.focus(); + } + } + window.onblur = function() { + ok(true, "blurred"); + res(); + } + }); + + content = document.getElementById("content"); + ok(content, "parent exists."); + + iframe = document.createElement("iframe"); + content.appendChild(iframe); + + iframe.setAttribute('src', "sw_clients/focus_stealing_client.html"); + return p; + } + + function testFocus() { + var p = new Promise(function(res, rej) { + navigator.serviceWorker.onmessage = function(e) { + ok(e.data, "client object is marked as focused."); + ok(document.hasFocus(), "document has focus."); + res(); + } + }); + + ok(registration.active, "active worker exists."); + registration.active.postMessage("go"); + return p; + } + + function runTest() { + start() + .then(loseFocus) + .then(testFocus) + .then(unregister) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + }).then(SimpleTest.finish); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_close.html b/dom/workers/test/serviceworkers/test_close.html new file mode 100644 index 000000000..d2f72b9ef --- /dev/null +++ b/dom/workers/test/serviceworkers/test_close.html @@ -0,0 +1,64 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1131353 - test WorkerGlobalScope.close() on service workers</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +<iframe></iframe> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + var iframe; + function runTest() { + navigator.serviceWorker.ready.then(setupSW); + navigator.serviceWorker.register("close_test.js", {scope: "."}); + + function setupSW(registration) { + var worker = registration.waiting || + registration.active; + var iframe = document.createElement("iframe"); + iframe.src = "message_receiver.html"; + iframe.onload = function() { + worker.postMessage({ message: "start" }); + }; + document.body.appendChild(iframe); + } + + window.onmessage = function(e) { + if (e.data.status == "ok") { + ok(e.data.result, e.data.message); + } else if (e.data.status == "done") { + navigator.serviceWorker.getRegistration().then(function(registration) { + registration.unregister().then(function(result) { + ok(result, "Unregistering the service worker should succeed"); + SimpleTest.finish(); + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + SimpleTest.finish(); + }); + }); + } + }; + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}, runTest); + }; +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_controller.html b/dom/workers/test/serviceworkers/test_controller.html new file mode 100644 index 000000000..789d7746d --- /dev/null +++ b/dom/workers/test/serviceworkers/test_controller.html @@ -0,0 +1,84 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1002570 - test controller instance.</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + var content; + var iframe; + var registration; + + function simpleRegister() { + // We use the control scope for the less specific registration. The window will register a worker on controller/ + return navigator.serviceWorker.register("worker.js", { scope: "./control" }) + .then(function(reg) { + registration = reg; + });; + } + + function unregister() { + return registration.unregister().then(function(result) { + ok(result, "Unregister should return true."); + }, function(e) { + dump("Unregistering the SW failed: " + e + "\n"); + }); + } + + function testController() { + var p = new Promise(function(resolve, reject) { + window.onmessage = function(e) { + if (e.data.status == "ok") { + ok(e.data.result, e.data.message); + } else if (e.data.status == "done") { + window.onmessage = null; + content.removeChild(iframe); + resolve(); + } + } + }); + + content = document.getElementById("content"); + ok(content, "Parent exists."); + + iframe = document.createElement("iframe"); + iframe.setAttribute('src', "controller/index.html"); + content.appendChild(iframe); + + return p; + } + + // This document just flips the prefs and opens the iframe for the actual test. + function runTest() { + simpleRegister() + .then(testController) + .then(unregister) + .then(function() { + SimpleTest.finish(); + }).catch(function(e) { + ok(false, "Some test failed with error " + e); + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_cross_origin_url_after_redirect.html b/dom/workers/test/serviceworkers/test_cross_origin_url_after_redirect.html new file mode 100644 index 000000000..e56bb84ca --- /dev/null +++ b/dom/workers/test/serviceworkers/test_cross_origin_url_after_redirect.html @@ -0,0 +1,50 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test access to a cross origin Request.url property from a service worker for a redirected intercepted iframe</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +<iframe></iframe> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + var iframe; + function runTest() { + iframe = document.querySelector("iframe"); + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/requesturl/register.html"; + window.onmessage = function(e) { + if (e.data.status == "ok") { + ok(e.data.result, e.data.message); + } else if (e.data.status == "registrationdone") { + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/requesturl/index.html"; + } else if (e.data.status == "done") { + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/requesturl/unregister.html"; + } else if (e.data.status == "unregistrationdone") { + window.onmessage = null; + ok(true, "Test finished successfully"); + SimpleTest.finish(); + } + }; + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}, runTest); + }; +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_csp_upgrade-insecure_intercept.html b/dom/workers/test/serviceworkers/test_csp_upgrade-insecure_intercept.html new file mode 100644 index 000000000..fe4cb991c --- /dev/null +++ b/dom/workers/test/serviceworkers/test_csp_upgrade-insecure_intercept.html @@ -0,0 +1,55 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test that a CSP upgraded request can be intercepted by a service worker</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content"> +<iframe></iframe> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + var iframe; + function runTest() { + iframe = document.querySelector("iframe"); + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/upgrade-insecure/register.html"; + window.onmessage = function(e) { + if (e.data.status == "ok") { + ok(e.data.result, e.data.message); + } else if (e.data.status == "registrationdone") { + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html"; + } else if (e.data.status == "protocol") { + is(e.data.data, "https:", "Correct protocol expected"); + } else if (e.data.status == "image") { + is(e.data.data, 40, "The image request was upgraded before interception"); + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/upgrade-insecure/unregister.html"; + } else if (e.data.status == "unregistrationdone") { + window.onmessage = null; + SimpleTest.finish(); + } + }; + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + // This is needed so that we can test upgrading a non-secure load inside an https iframe. + ["security.mixed_content.block_active_content", false], + ["security.mixed_content.block_display_content", false], + ]}, runTest); + }; +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_empty_serviceworker.html b/dom/workers/test/serviceworkers/test_empty_serviceworker.html new file mode 100644 index 000000000..e42951896 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_empty_serviceworker.html @@ -0,0 +1,46 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test that registering an empty service worker works</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + function runTest() { + navigator.serviceWorker.ready.then(done); + navigator.serviceWorker.register("empty.js", {scope: "."}); + } + + function done(registration) { + ok(registration.waiting || registration.active, "registration worked"); + registration.unregister().then(function(success) { + ok(success, "unregister worked"); + SimpleTest.finish(); + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); + }; +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_error_reporting.html b/dom/workers/test/serviceworkers/test_error_reporting.html new file mode 100644 index 000000000..619d602e8 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_error_reporting.html @@ -0,0 +1,76 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test Error Reporting of Service Worker Failures</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/SpawnTask.js"></script> + <script src="error_reporting_helpers.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> + <meta http-equiv="Content-type" content="text/html;charset=UTF-8"> +</head> +<body> + +<script type="text/javascript"> +"use strict"; + +/** + * Test that a bunch of service worker coding errors and failure modes that + * might otherwise be hard to diagnose are surfaced as console error messages. + * The driving use-case is minimizing cursing from a developer looking at a + * document in Firefox testing a page that involves service workers. + * + * This test assumes that errors will be reported via + * ServiceWorkerManager::ReportToAllClients and that that method is reliable and + * tested via some other file. + **/ + +add_task(function setupPrefs() { + return SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.caches.testing.enabled", true], + ]}); +}); + +/** + * Ensure an error is logged during the initial registration of a SW when a 404 + * is received. + */ +add_task(function* register_404() { + // Start monitoring for the error + let expectedMessage = expect_console_message( + "ServiceWorkerRegisterNetworkError", + [make_absolute_url("network_error/"), "404", make_absolute_url("404.js")]); + + // Register, generating the 404 error. This will reject with a TypeError + // which we need to consume so it doesn't get thrown at our generator. + yield navigator.serviceWorker.register("404.js", { scope: "network_error/" }) + .then( + () => { ok(false, "should have rejected"); }, + (e) => { ok(e.name === "TypeError", "404 failed as expected"); }); + + yield wait_for_expected_message(expectedMessage); +}); + +/** + * Ensure an error is logged when the service worker is being served with a + * MIME type of text/plain rather than a JS type. + */ +add_task(function* register_bad_mime_type() { + let expectedMessage = expect_console_message( + "ServiceWorkerRegisterMimeTypeError", + [make_absolute_url("bad_mime_type/"), "text/plain", + make_absolute_url("sw_bad_mime_type.js")]); + + // consume the expected rejection so it doesn't get thrown at us. + yield navigator.serviceWorker.register("sw_bad_mime_type.js", { scope: "bad_mime_type/" }) + .then( + () => { ok(false, "should have rejected"); }, + (e) => { ok(e.name === "SecurityError", "bad MIME type failed as expected"); }); + + yield wait_for_expected_message(expectedMessage); +}); +</script> + +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_escapedSlashes.html b/dom/workers/test/serviceworkers/test_escapedSlashes.html new file mode 100644 index 000000000..001c66024 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_escapedSlashes.html @@ -0,0 +1,102 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test for escaped slashes in navigator.serviceWorker.register</title> + <script type="text/javascript" src="http://mochi.test:8888/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="http://mochi.test:8888/tests/SimpleTest/test.css" /> + <base href="https://mozilla.org/"> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + +var tests = [ + { status: true, + scriptURL: "a.js?foo%2fbar", + scopeURL: null }, + { status: false, + scriptURL: "foo%2fbar", + scopeURL: null }, + { status: true, + scriptURL: "a.js?foo%2Fbar", + scopeURL: null }, + { status: false, + scriptURL: "foo%2Fbar", + scopeURL: null }, + { status: true, + scriptURL: "a.js?foo%5cbar", + scopeURL: null }, + { status: false, + scriptURL: "foo%5cbar", + scopeURL: null }, + { status: true, + scriptURL: "a.js?foo%2Cbar", + scopeURL: null }, + { status: false, + scriptURL: "foo%5Cbar", + scopeURL: null }, + { status: true, + scriptURL: "ok.js", + scopeURL: "/scope?foo%2fbar"}, + { status: false, + scriptURL: "ok.js", + scopeURL: "/foo%2fbar"}, + { status: true, + scriptURL: "ok.js", + scopeURL: "/scope?foo%2Fbar"}, + { status: false, + scriptURL: "ok.js", + scopeURL: "foo%2Fbar"}, + { status: true, + scriptURL: "ok.js", + scopeURL: "/scope?foo%5cbar"}, + { status: false, + scriptURL: "ok.js", + scopeURL: "foo%5cbar"}, + { status: true, + scriptURL: "ok.js", + scopeURL: "/scope?foo%5Cbar"}, + { status: false, + scriptURL: "ok.js", + scopeURL: "foo%5Cbar"}, +]; + +function runTest() { + if (!tests.length) { + SimpleTest.finish(); + return; + } + + var test = tests.shift(); + navigator.serviceWorker.register(test.scriptURL, test.scopeURL) + .then(reg => { + ok(false, "Register should fail"); + }, err => { + if (!test.status) { + is(err.name, "TypeError", "Registration should fail with TypeError"); + } else { + ok(test.status, "Register should fail"); + } + }) + .then(runTest); +} + +SimpleTest.waitForExplicitFinish(); +onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.serviceWorkers.enabled", true], + ]}, runTest); +}; + +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_eval_allowed.html b/dom/workers/test/serviceworkers/test_eval_allowed.html new file mode 100644 index 000000000..bfe8ac280 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_eval_allowed.html @@ -0,0 +1,51 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1160458 - CSP activated by default in Service Workers</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + function register() { + return navigator.serviceWorker.register("eval_worker.js"); + } + + function runTest() { + try { + eval("1"); + ok(false, "should throw"); + } + catch (ex) { + ok(true, "did throw"); + } + register() + .then(function(swr) { + ok(true, "eval restriction didn't get inherited"); + swr.unregister() + .then(function() { + SimpleTest.finish(); + }); + }).catch(function(e) { + ok(false, "Some test failed with error " + e); + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}, runTest); +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_eval_allowed.html^headers^ b/dom/workers/test/serviceworkers/test_eval_allowed.html^headers^ new file mode 100644 index 000000000..51ffaa71d --- /dev/null +++ b/dom/workers/test/serviceworkers/test_eval_allowed.html^headers^ @@ -0,0 +1 @@ +Content-Security-Policy: "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self'" diff --git a/dom/workers/test/serviceworkers/test_eventsource_intercept.html b/dom/workers/test/serviceworkers/test_eventsource_intercept.html new file mode 100644 index 000000000..55df62bb7 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_eventsource_intercept.html @@ -0,0 +1,103 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1182103 - Test EventSource scenarios with fetch interception</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + function testFrame(src) { + return new Promise(function(resolve, reject) { + var iframe = document.createElement("iframe"); + iframe.src = src; + window.onmessage = function(e) { + if (e.data.status == "callback") { + switch(e.data.data) { + case "ok": + ok(e.data.condition, e.data.message); + break; + case "ready": + iframe.contentWindow.postMessage({status: "callback", data: "eventsource"}, "*"); + break; + case "done": + window.onmessage = null; + iframe.src = "about:blank"; + document.body.removeChild(iframe); + iframe = null; + resolve(); + break; + default: + ok(false, "Something went wrong"); + break; + } + } else { + ok(false, "Something went wrong"); + } + }; + document.body.appendChild(iframe); + }); + } + + function runTest() { + Promise.resolve() + .then(() => { + info("Going to intercept and test opaque responses"); + return testFrame("eventsource/eventsource_register_worker.html" + + "?script=eventsource_opaque_response_intercept_worker.js"); + }) + .then(() => { + return testFrame("eventsource/eventsource_opaque_response.html"); + }) + .then(() => { + info("Going to intercept and test cors responses"); + return testFrame("eventsource/eventsource_register_worker.html" + + "?script=eventsource_cors_response_intercept_worker.js"); + }) + .then(() => { + return testFrame("eventsource/eventsource_cors_response.html"); + }) + .then(() => { + info("Going to intercept and test synthetic responses"); + return testFrame("eventsource/eventsource_register_worker.html" + + "?script=eventsource_synthetic_response_intercept_worker.js"); + }) + .then(() => { + return testFrame("eventsource/eventsource_synthetic_response.html"); + }) + .then(() => { + info("Going to intercept and test mixed content cors responses"); + return testFrame("https://example.com/tests/dom/workers/test/serviceworkers/" + + "eventsource/eventsource_register_worker.html" + + "?script=eventsource_mixed_content_cors_response_intercept_worker.js"); + }) + .then(() => { + return testFrame("https://example.com/tests/dom/workers/test/serviceworkers/" + + "eventsource/eventsource_mixed_content_cors_response.html"); + }) + .then(SimpleTest.finish) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.caches.enabled", true], + ]}, runTest); +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_fetch_event.html b/dom/workers/test/serviceworkers/test_fetch_event.html new file mode 100644 index 000000000..764be87b1 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_fetch_event.html @@ -0,0 +1,83 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 94048 - test install event.</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + SimpleTest.requestCompleteLog(); + + var registration; + function simpleRegister() { + var p = navigator.serviceWorker.register("fetch_event_worker.js", { scope: "./fetch" }); + return p.then(function(swr) { + registration = swr; + return new Promise(function(resolve) { + swr.installing.onstatechange = resolve; + }); + }); + } + + function unregister() { + return registration.unregister().then(function(success) { + ok(success, "Service worker should be unregistered successfully"); + }, function(e) { + dump("SW unregistration error: " + e + "\n"); + }); + } + + function testController() { + var p = new Promise(function(resolve, reject) { + var reloaded = false; + window.onmessage = function(e) { + if (e.data.status == "ok") { + ok(e.data.result, e.data.message); + } else if (e.data.status == "done") { + if (reloaded) { + window.onmessage = null; + w.close(); + resolve(); + } else { + w.location.reload(); + reloaded = true; + } + } + } + }); + + var w = window.open("fetch/index.html"); + return p; + } + + function runTest() { + simpleRegister() + .then(testController) + .then(unregister) + .then(function() { + SimpleTest.finish(); + }).catch(function(e) { + ok(false, "Some test failed with error " + e); + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_fetch_integrity.html b/dom/workers/test/serviceworkers/test_fetch_integrity.html new file mode 100644 index 000000000..50eb05581 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_fetch_integrity.html @@ -0,0 +1,178 @@ +<!DOCTYPE HTML> +<html> +<head> + <title> Test fetch.integrity on console report for serviceWorker and sharedWorker </title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/SpawnTask.js"></script> + <script src="error_reporting_helpers.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> + <meta http-equiv="Content-type" content="text/html;charset=UTF-8"> +</head> +<body> +<div id="content" style="display: none"></div> +<script type="text/javascript"> +"use strict"; + +let security_localizer = + stringBundleService.createBundle("chrome://global/locale/security/security.properties"); + +function expect_security_console_message(/* msgId, args, ... */) { + let expectations = []; + // process repeated paired arguments of: msgId, args + for (let i = 0; i < arguments.length; i += 4) { + let msgId = arguments[i]; + let args = arguments[i + 1]; + let filename = arguments[i + 2]; + let windowId = arguments[i + 3]; + expectations.push({ + errorMessage: security_localizer.formatStringFromName(msgId, args, args.length), + sourceName: filename, + windowID: windowId + }); + } + return new Promise(resolve => { + SimpleTest.monitorConsole(resolve, expectations); + }); +} + +// (This doesn't really need to be its own task, but it allows the actual test +// case to be self-contained.) +add_task(function setupPrefs() { + return SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}); +}); + +add_task(function* test_integrity_serviceWorker() { + var filename = make_absolute_url("fetch.js"); + var filename2 = make_absolute_url("fake.html"); + + // The SW will claim us once it activates; this is async, start listening now. + let waitForControlled = new Promise((resolve) => { + navigator.serviceWorker.oncontrollerchange = resolve; + }); + + let registration = yield navigator.serviceWorker.register("fetch.js", + { scope: "./" }); + yield waitForControlled; + + info("Test for mNavigationInterceptions.") + // The client_win will reload to another URL after opening filename2. + let client_win = window.open(filename2); + + // XXX windowID should be innerWindowID + let mainWindowID = SpecialPowers.getDOMWindowUtils(window).outerWindowID; + let clientWindowID = SpecialPowers.getDOMWindowUtils(client_win).outerWindowID; + let expectedMessage = expect_security_console_message( + "MalformedIntegrityHash", + ["abc"], + filename, + mainWindowID, + "NoValidMetadata", + [""], + filename, + mainWindowID + ); + let expectedMessage2 = expect_security_console_message( + "MalformedIntegrityHash", + ["abc"], + filename, + clientWindowID, + "NoValidMetadata", + [""], + filename, + clientWindowID + ); + + info("Test for mControlledDocuments and report error message to console."); + // The fetch will succeed because the integrity value is invalid and we are + // looking for the console message regarding the bad integrity value. + yield fetch("fail.html"); + + yield wait_for_expected_message(expectedMessage); + + yield wait_for_expected_message(expectedMessage2); + + yield registration.unregister(); + client_win.close(); +}); + +add_task(function* test_integrity_sharedWorker() { + var filename = make_absolute_url("sharedWorker_fetch.js"); + + info("Attch main window to a SharedWorker."); + let sharedWorker = new SharedWorker(filename); + let waitForConnected = new Promise((resolve) => { + sharedWorker.port.onmessage = function (e) { + if (e.data == "Connected") { + resolve(); + } else { + reject(); + } + } + }); + yield waitForConnected; + + info("Attch another window to the same SharedWorker."); + // Open another window and its also managed by the shared worker. + let client_win = window.open("create_another_sharedWorker.html"); + let waitForBothConnected = new Promise((resolve) => { + sharedWorker.port.onmessage = function (e) { + if (e.data == "BothConnected") { + resolve(); + } else { + reject(); + } + } + }); + yield waitForBothConnected; + + // XXX windowID should be innerWindowID + let mainWindowID = SpecialPowers.getDOMWindowUtils(window).outerWindowID; + let clientWindowID = SpecialPowers.getDOMWindowUtils(client_win).outerWindowID; + let expectedMessage = expect_security_console_message( + "MalformedIntegrityHash", + ["abc"], + filename, + mainWindowID, + "NoValidMetadata", + [""], + filename, + mainWindowID + ); + let expectedMessage2 = expect_security_console_message( + "MalformedIntegrityHash", + ["abc"], + filename, + clientWindowID, + "NoValidMetadata", + [""], + filename, + clientWindowID + ); + + info("Start to fetch a URL with wrong integrity.") + sharedWorker.port.start(); + sharedWorker.port.postMessage("StartFetchWithWrongIntegrity"); + + let waitForSRIFailed = new Promise((resolve) => { + sharedWorker.port.onmessage = function (e) { + if (e.data == "SRI_failed") { + resolve(); + } else { + reject(); + } + } + }); + yield waitForSRIFailed; + + yield wait_for_expected_message(expectedMessage); + + yield wait_for_expected_message(expectedMessage2); + client_win.close(); +}); + +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_file_blob_response.html b/dom/workers/test/serviceworkers/test_file_blob_response.html new file mode 100644 index 000000000..6db0656c6 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_file_blob_response.html @@ -0,0 +1,86 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1253777 - Test interception using file blob response body</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + var registration; + var scope = './file_blob_response/'; + function start() { + return navigator.serviceWorker.register("file_blob_response_worker.js", + { scope: scope }) + .then(function(swr) { + registration = swr; + return new Promise(function(resolve) { + registration.installing.onstatechange = function(evt) { + if (evt.target.state === 'activated') { + evt.target.onstate = null; + resolve(); + } + } + }); + }); + } + + function unregister() { + return registration.unregister().then(function(result) { + ok(result, "Unregister should return true."); + }, function(e) { + ok(false, "Unregistering the SW failed with " + e + "\n"); + }); + } + + function withFrame(url) { + return new Promise(function(resolve, reject) { + var content = document.getElementById("content"); + ok(content, "Parent exists."); + + var frame = document.createElement("iframe"); + frame.setAttribute('src', url); + content.appendChild(frame); + + frame.addEventListener('load', function loadCallback(evt) { + frame.removeEventListener('load', loadCallback); + resolve(frame); + }); + }); + } + + function runTest() { + start() + .then(function() { + return withFrame(scope + 'dummy.txt'); + }) + .then(function(frame) { + var result = JSON.parse(frame.contentWindow.document.body.textContent); + frame.remove(); + is(result.value, 'success'); + }) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + }) + .then(unregister) + .then(SimpleTest.finish); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_file_blob_upload.html b/dom/workers/test/serviceworkers/test_file_blob_upload.html new file mode 100644 index 000000000..30a31eb7e --- /dev/null +++ b/dom/workers/test/serviceworkers/test_file_blob_upload.html @@ -0,0 +1,145 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1203680 - Test interception of file blob uploads</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + var registration; + var iframe; + function start() { + return navigator.serviceWorker.register("empty.js", + { scope: "./sw_clients/" }) + .then((swr) => registration = swr); + } + + function unregister() { + if (iframe) { + iframe.remove(); + iframe = null; + } + + return registration.unregister().then(function(result) { + ok(result, "Unregister should return true."); + }, function(e) { + ok(false, "Unregistering the SW failed with " + e + "\n"); + }); + } + + function withFrame() { + var content = document.getElementById("content"); + ok(content, "Parent exists."); + + iframe = document.createElement("iframe"); + iframe.setAttribute('src', "sw_clients/file_blob_upload_frame.html"); + content.appendChild(iframe); + + return new Promise(function(resolve, reject) { + window.addEventListener('message', function readyCallback(evt) { + window.removeEventListener('message', readyCallback); + if (evt.data.status === 'READY') { + resolve(); + } else { + reject(evt.data.result); + } + }); + }); + } + + function postBlob(body) { + return new Promise(function(resolve, reject) { + window.addEventListener('message', function postBlobCallback(evt) { + window.removeEventListener('message', postBlobCallback); + if (evt.data.status === 'OK') { + is(JSON.stringify(body), JSON.stringify(evt.data.result), + 'body echoed back correctly'); + resolve(); + } else { + reject(evt.data.result); + } + }); + + iframe.contentWindow.postMessage({ type: 'TEST', body: body }, '*'); + }); + } + + function generateMessage(length) { + + var lorem = + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis egestas ' + 'vehicula tortor eget ultrices. Sed et luctus est. Nunc eu orci ligula. ' + 'In vel ornare eros, eget lacinia diam. Praesent vel metus mattis, ' + 'cursus nulla sit amet, rhoncus diam. Aliquam nulla tortor, aliquet et ' + 'viverra non, dignissim vel tellus. Praesent sed ex in dolor aliquet ' + 'aliquet. In at facilisis sem, et aliquet eros. Maecenas feugiat nisl ' + 'quis elit blandit posuere. Duis viverra odio sed eros consectetur, ' + 'viverra mattis ligula volutpat.'; + + var result = ''; + + while (result.length < length) { + var remaining = length - result.length; + if (remaining < lorem.length) { + result += lorem.slice(0, remaining); + } else { + result += lorem; + } + } + + return result; + } + + var smallBody = generateMessage(64); + var mediumBody = generateMessage(1024); + + // TODO: Test large bodies over the default pipe size. Currently stalls + // due to bug 1134372. + //var largeBody = generateMessage(100 * 1024); + + function runTest() { + start() + .then(withFrame) + .then(function() { + return postBlob({ hops: 0, message: smallBody }); + }) + .then(function() { + return postBlob({ hops: 1, message: smallBody }); + }) + .then(function() { + return postBlob({ hops: 10, message: smallBody }); + }) + .then(function() { + return postBlob({ hops: 0, message: mediumBody }); + }) + .then(function() { + return postBlob({ hops: 1, message: mediumBody }); + }) + .then(function() { + return postBlob({ hops: 10, message: mediumBody }); + }) + .then(unregister) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + }).then(SimpleTest.finish); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_force_refresh.html b/dom/workers/test/serviceworkers/test_force_refresh.html new file mode 100644 index 000000000..05caf0e6a --- /dev/null +++ b/dom/workers/test/serviceworkers/test_force_refresh.html @@ -0,0 +1,84 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 982726 - Test service worker post message </title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + var registration; + function start() { + return navigator.serviceWorker.register("force_refresh_worker.js", + { scope: "./sw_clients/" }) + .then((swr) => registration = swr); + } + + function unregister() { + return registration.unregister().then(function(result) { + ok(result, "Unregister should return true."); + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + } + + + function testForceRefresh(swr) { + var p = new Promise(function(res, rej) { + var count = 0; + var cachedCount = 0; + window.onmessage = function(e) { + if (e.data === "READY") { + count += 1; + if (count == 2) { + is(cachedCount, 1, "should have received cached message before " + + "second non-cached message"); + res(); + } + iframe.contentWindow.postMessage("REFRESH", "*"); + } else if (e.data === "READY_CACHED") { + cachedCount += 1; + is(count, 1, "should have received non-cached message before " + + "cached message"); + iframe.contentWindow.postMessage("FORCE_REFRESH", "*"); + } + } + }); + + var content = document.getElementById("content"); + ok(content, "Parent exists."); + + iframe = document.createElement("iframe"); + iframe.setAttribute('src', "sw_clients/refresher_compressed.html"); + content.appendChild(iframe); + + return p.then(() => content.removeChild(iframe)); + } + + function runTest() { + start() + .then(testForceRefresh) + .then(unregister) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + }).then(SimpleTest.finish); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.caches.enabled", true], + ]}, runTest); +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_gzip_redirect.html b/dom/workers/test/serviceworkers/test_gzip_redirect.html new file mode 100644 index 000000000..7ac92122c --- /dev/null +++ b/dom/workers/test/serviceworkers/test_gzip_redirect.html @@ -0,0 +1,84 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 982726 - Test service worker post message </title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + var registration; + function start() { + return navigator.serviceWorker.register("gzip_redirect_worker.js", + { scope: "./sw_clients/" }) + .then((swr) => registration = swr); + } + + function unregister() { + return registration.unregister().then(function(result) { + ok(result, "Unregister should return true."); + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + } + + + function testGzipRedirect(swr) { + var p = new Promise(function(res, rej) { + var navigatorReady = false; + var finalReady = false; + + window.onmessage = function(e) { + if (e.data === "NAVIGATOR_READY") { + ok(!navigatorReady, "should only get navigator ready message once"); + ok(!finalReady, "should get navigator ready before final redirect ready message"); + navigatorReady = true; + iframe.contentWindow.postMessage({ + type: "NAVIGATE", + url: "does_not_exist.html" + }, "*"); + } else if (e.data === "READY") { + ok(navigatorReady, "should only get navigator ready message once"); + ok(!finalReady, "should get final ready message only once"); + finalReady = true; + res(); + } + } + }); + + var content = document.getElementById("content"); + ok(content, "Parent exists."); + + iframe = document.createElement("iframe"); + iframe.setAttribute('src', "sw_clients/navigator.html"); + content.appendChild(iframe); + + return p.then(() => content.removeChild(iframe)); + } + + function runTest() { + start() + .then(testGzipRedirect) + .then(unregister) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + }).then(SimpleTest.finish); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}, runTest); +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_hsts_upgrade_intercept.html b/dom/workers/test/serviceworkers/test_hsts_upgrade_intercept.html new file mode 100644 index 000000000..dfce406b8 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_hsts_upgrade_intercept.html @@ -0,0 +1,66 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test that an HSTS upgraded request can be intercepted by a service worker</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content"> +<iframe></iframe> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + var iframe; + var framesLoaded = 0; + function runTest() { + iframe = document.querySelector("iframe"); + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/hsts/register.html"; + window.onmessage = function(e) { + if (e.data.status == "ok") { + ok(e.data.result, e.data.message); + } else if (e.data.status == "registrationdone") { + iframe.src = "http://example.com/tests/dom/workers/test/serviceworkers/fetch/hsts/index.html"; + } else if (e.data.status == "protocol") { + is(e.data.data, "https:", "Correct protocol expected"); + ok(e.data.securityInfoPresent, "Security info present on intercepted value"); + switch (++framesLoaded) { + case 1: + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/hsts/embedder.html"; + break; + case 2: + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/hsts/image.html"; + break; + } + } else if (e.data.status == "image") { + is(e.data.data, 40, "The image request was upgraded before interception"); + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/hsts/unregister.html"; + } else if (e.data.status == "unregistrationdone") { + window.onmessage = null; + SpecialPowers.cleanUpSTSData("http://example.com"); + SimpleTest.finish(); + } + }; + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + // This is needed so that we can test upgrading a non-secure load inside an https iframe. + ["security.mixed_content.block_active_content", false], + ["security.mixed_content.block_display_content", false], + ]}, runTest); + }; +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_https_fetch.html b/dom/workers/test/serviceworkers/test_https_fetch.html new file mode 100644 index 000000000..e990200f8 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_https_fetch.html @@ -0,0 +1,62 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1133763 - test fetch event in HTTPS origins</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +<iframe></iframe> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + var iframe; + function runTest() { + iframe = document.querySelector("iframe"); + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/register.html"; + var ios; + window.onmessage = function(e) { + if (e.data.status == "ok") { + ok(e.data.result, e.data.message); + } else if (e.data.status == "registrationdone") { + ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"] + .getService(SpecialPowers.Ci.nsIIOService); + ios.offline = true; + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/index.html"; + } else if (e.data.status == "done") { + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/synth-sw.html"; + } else if (e.data.status == "done-synth-sw") { + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/synth-window.html"; + } else if (e.data.status == "done-synth-window") { + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/synth.html"; + } else if (e.data.status == "done-synth") { + ios.offline = false; + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/unregister.html"; + } else if (e.data.status == "unregistrationdone") { + window.onmessage = null; + ok(true, "Test finished successfully"); + SimpleTest.finish(); + } + }; + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.caches.enabled", true] + ]}, runTest); + }; +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_https_fetch_cloned_response.html b/dom/workers/test/serviceworkers/test_https_fetch_cloned_response.html new file mode 100644 index 000000000..1cf1dbef1 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_https_fetch_cloned_response.html @@ -0,0 +1,56 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1133763 - test fetch event in HTTPS origins with a cloned response</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +<iframe></iframe> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + var iframe; + function runTest() { + iframe = document.querySelector("iframe"); + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/clonedresponse/register.html"; + var ios; + window.onmessage = function(e) { + if (e.data.status == "ok") { + ok(e.data.result, e.data.message); + } else if (e.data.status == "registrationdone") { + ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"] + .getService(SpecialPowers.Ci.nsIIOService); + ios.offline = true; + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/clonedresponse/index.html"; + } else if (e.data.status == "done") { + ios.offline = false; + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/clonedresponse/unregister.html"; + } else if (e.data.status == "unregistrationdone") { + window.onmessage = null; + ok(true, "Test finished successfully"); + SimpleTest.finish(); + } + }; + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.caches.enabled", true] + ]}, runTest); + }; +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_https_origin_after_redirect.html b/dom/workers/test/serviceworkers/test_https_origin_after_redirect.html new file mode 100644 index 000000000..3878a1df6 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_https_origin_after_redirect.html @@ -0,0 +1,57 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test the origin of a redirected response from a service worker</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +<iframe></iframe> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + var iframe; + function runTest() { + iframe = document.querySelector("iframe"); + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/origin/https/register.html"; + var win; + window.onmessage = function(e) { + if (e.data.status == "ok") { + ok(e.data.result, e.data.message); + } else if (e.data.status == "registrationdone") { + win = window.open("https://example.com/tests/dom/workers/test/serviceworkers/fetch/origin/https/index-https.sjs", "mywindow", "width=100,height=100"); + } else if (e.data.status == "domain") { + is(e.data.data, "example.org", "Correct domain expected"); + } else if (e.data.status == "origin") { + is(e.data.data, "https://example.org", "Correct origin expected"); + } else if (e.data.status == "done") { + win.close(); + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/origin/https/unregister.html"; + } else if (e.data.status == "unregistrationdone") { + window.onmessage = null; + ok(true, "Test finished successfully"); + SimpleTest.finish(); + } + }; + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.caches.enabled", true], + ]}, runTest); + }; +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_https_origin_after_redirect_cached.html b/dom/workers/test/serviceworkers/test_https_origin_after_redirect_cached.html new file mode 100644 index 000000000..81a1d1da0 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_https_origin_after_redirect_cached.html @@ -0,0 +1,57 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test the origin of a redirected response from a service worker</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +<iframe></iframe> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + var iframe; + function runTest() { + iframe = document.querySelector("iframe"); + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/origin/https/register.html"; + var win; + window.onmessage = function(e) { + if (e.data.status == "ok") { + ok(e.data.result, e.data.message); + } else if (e.data.status == "registrationdone") { + win = window.open("https://example.com/tests/dom/workers/test/serviceworkers/fetch/origin/https/index-cached-https.sjs", "mywindow", "width=100,height=100"); + } else if (e.data.status == "domain") { + is(e.data.data, "example.org", "Correct domain expected"); + } else if (e.data.status == "origin") { + is(e.data.data, "https://example.org", "Correct origin expected"); + } else if (e.data.status == "done") { + win.close(); + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/origin/https/unregister.html"; + } else if (e.data.status == "unregistrationdone") { + window.onmessage = null; + ok(true, "Test finished successfully"); + SimpleTest.finish(); + } + }; + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.caches.enabled", true], + ]}, runTest); + }; +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_https_synth_fetch_from_cached_sw.html b/dom/workers/test/serviceworkers/test_https_synth_fetch_from_cached_sw.html new file mode 100644 index 000000000..7bf3b352a --- /dev/null +++ b/dom/workers/test/serviceworkers/test_https_synth_fetch_from_cached_sw.html @@ -0,0 +1,69 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1156847 - test fetch event generating a synthesized response in HTTPS origins from a cached SW</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" tyle="display: none"> +<iframe></iframe> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + var iframe; + function runTest() { + iframe = document.querySelector("iframe"); + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/register.html"; + var ios; + window.onmessage = function(e) { + if (e.data.status == "ok") { + ok(e.data.result, e.data.message); + } else if (e.data.status == "registrationdone") { + ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"] + .getService(SpecialPowers.Ci.nsIIOService); + ios.offline = true; + + // In order to load synth.html from a cached service worker, we first + // remove the existing window that is keeping the service worker alive, + // and do a GC to ensure that the SW is destroyed. This way, when we + // load synth.html for the second time, we will first recreate the + // service worker from the cache. This is intended to test that we + // properly store and retrieve the security info from the cache. + iframe.parentNode.removeChild(iframe); + iframe = null; + SpecialPowers.exactGC(function() { + iframe = document.createElement("iframe"); + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/synth.html"; + document.body.appendChild(iframe); + }); + } else if (e.data.status == "done-synth") { + ios.offline = false; + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/unregister.html"; + } else if (e.data.status == "unregistrationdone") { + window.onmessage = null; + ok(true, "Test finished successfully"); + SimpleTest.finish(); + } + }; + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.caches.enabled", true] + ]}, runTest); + }; +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_imagecache.html b/dom/workers/test/serviceworkers/test_imagecache.html new file mode 100644 index 000000000..8627b54af --- /dev/null +++ b/dom/workers/test/serviceworkers/test_imagecache.html @@ -0,0 +1,55 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1202085 - Test that images from different controllers don't cached together</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content"> +<iframe></iframe> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + var iframe; + function runTest() { + iframe = document.querySelector("iframe"); + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/imagecache/register.html"; + window.onmessage = function(e) { + if (e.data.status == "ok") { + ok(e.data.result, e.data.message); + } else if (e.data.status == "registrationdone") { + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/imagecache/index.html"; + } else if (e.data.status == "result") { + is(e.data.url, "image-40px.png", "Correct url expected"); + is(e.data.width, 40, "Correct width expected"); + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/imagecache/unregister.html"; + } else if (e.data.status == "unregistrationdone") { + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/imagecache/postmortem.html"; + } else if (e.data.status == "postmortem") { + is(e.data.width, 20, "Correct width expected"); + window.onmessage = null; + ok(true, "Test finished successfully"); + SimpleTest.finish(); + } + }; + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}, runTest); + }; +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_imagecache_max_age.html b/dom/workers/test/serviceworkers/test_imagecache_max_age.html new file mode 100644 index 000000000..eb3c1f166 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_imagecache_max_age.html @@ -0,0 +1,71 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test that the image cache respects a synthesized image's Cache headers</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content"> +<iframe></iframe> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + var iframe; + var framesLoaded = 0; + function runTest() { + iframe = document.querySelector("iframe"); + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/imagecache-maxage/register.html"; + window.onmessage = function(e) { + if (e.data.status == "ok") { + ok(e.data.result, e.data.message); + } else if (e.data.status == "registrationdone") { + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/imagecache-maxage/index.html"; + } else if (e.data.status == "result") { + switch (++framesLoaded) { + case 1: + is(e.data.url, "image-20px.png", "Correct url expected"); + is(e.data.url2, "image-20px.png", "Correct url expected"); + is(e.data.width, 20, "Correct width expected"); + is(e.data.width2, 20, "Correct width expected"); + // Wait for 100ms so that the image gets expired. + setTimeout(function() { + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/imagecache-maxage/index.html?new" + }, 100); + break; + case 2: + is(e.data.url, "image-40px.png", "Correct url expected"); + is(e.data.url2, "image-40px.png", "Correct url expected"); + is(e.data.width, 40, "Correct width expected"); + is(e.data.width2, 40, "Correct width expected"); + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/imagecache-maxage/unregister.html"; + break; + default: + ok(false, "This should never happen"); + } + } else if (e.data.status == "unregistrationdone") { + window.onmessage = null; + SimpleTest.finish(); + } + }; + } + + SimpleTest.requestFlakyTimeout("This test needs to simulate the passing of time"); + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}, runTest); + }; +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_importscript.html b/dom/workers/test/serviceworkers/test_importscript.html new file mode 100644 index 000000000..5d2d5b352 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_importscript.html @@ -0,0 +1,72 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test service worker - script cache policy</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<div id="content"></div> +<script class="testbody" type="text/javascript"> + function start() { + return navigator.serviceWorker.register("importscript_worker.js", + { scope: "./sw_clients/" }) + .then((swr) => registration = swr); + } + + function unregister() { + return fetch("importscript.sjs?clearcounter").then(function() { + return registration.unregister(); + }).then(function(result) { + ok(result, "Unregister should return true."); + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + } + + function testPostMessage(swr) { + var p = new Promise(function(res, rej) { + window.onmessage = function(e) { + if (e.data === "READY") { + swr.active.postMessage("do magic"); + return; + } + + ok(e.data === "OK", "Worker posted the correct value: " + e.data); + res(); + } + }); + + var content = document.getElementById("content"); + ok(content, "Parent exists."); + + iframe = document.createElement("iframe"); + iframe.setAttribute('src', "sw_clients/service_worker_controlled.html"); + content.appendChild(iframe); + + return p.then(() => content.removeChild(iframe)); + } + + function runTest() { + start() + .then(testPostMessage) + .then(unregister) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + }).then(SimpleTest.finish); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_importscript_mixedcontent.html b/dom/workers/test/serviceworkers/test_importscript_mixedcontent.html new file mode 100644 index 000000000..a659af92b --- /dev/null +++ b/dom/workers/test/serviceworkers/test_importscript_mixedcontent.html @@ -0,0 +1,53 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1198078 - test that we respect mixed content blocking in importScript() inside service workers</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +<iframe></iframe> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + var iframe; + function runTest() { + iframe = document.querySelector("iframe"); + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/register.html"; + var ios; + window.onmessage = function(e) { + if (e.data.status == "ok") { + ok(e.data.result, e.data.message); + } else if (e.data.status == "registrationdone") { + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/index.html"; + } else if (e.data.status == "done") { + ok(e.data.data, "good", "Mixed content blocking should work correctly for service workers"); + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/unregister.html"; + } else if (e.data.status == "unregistrationdone") { + window.onmessage = null; + ok(true, "Test finished successfully"); + SimpleTest.finish(); + } + }; + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["security.mixed_content.block_active_content", false], + ]}, runTest); + }; +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_install_event.html b/dom/workers/test/serviceworkers/test_install_event.html new file mode 100644 index 000000000..0e59510fe --- /dev/null +++ b/dom/workers/test/serviceworkers/test_install_event.html @@ -0,0 +1,144 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 94048 - test install event.</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + function simpleRegister() { + var p = navigator.serviceWorker.register("worker.js", { scope: "./install_event" }); + return p; + } + + function nextRegister(reg) { + ok(reg instanceof ServiceWorkerRegistration, "reg should be a ServiceWorkerRegistration"); + var p = navigator.serviceWorker.register("install_event_worker.js", { scope: "./install_event" }); + return p.then(function(swr) { + ok(reg === swr, "register should resolve to the same registration object"); + var update_found_promise = new Promise(function(resolve, reject) { + swr.addEventListener('updatefound', function(e) { + ok(true, "Received onupdatefound"); + resolve(); + }); + }); + + var worker_activating = new Promise(function(res, reject) { + ok(swr.installing instanceof ServiceWorker, "There should be an installing worker if promise resolves."); + ok(swr.installing.state == "installing", "Installing worker's state should be 'installing'"); + swr.installing.onstatechange = function(e) { + if (e.target.state == "activating") { + e.target.onstatechange = null; + res(); + } + } + }); + + return Promise.all([update_found_promise, worker_activating]); + }, function(e) { + ok(false, "Unexpected Error in nextRegister! " + e); + }); + } + + function installError() { + // Silence worker errors so they don't cause the test to fail. + window.onerror = function(e) {} + return navigator.serviceWorker.register("install_event_error_worker.js", { scope: "./install_event" }) + .then(function(swr) { + ok(swr.installing instanceof ServiceWorker, "There should be an installing worker if promise resolves."); + ok(swr.installing.state == "installing", "Installing worker's state should be 'installing'"); + return new Promise(function(resolve, reject) { + swr.installing.onstatechange = function(e) { + ok(e.target.state == "redundant", "Installation of worker with error should fail."); + resolve(); + } + }); + }).then(function() { + return navigator.serviceWorker.getRegistration("./install_event").then(function(swr) { + var newest = swr.waiting || swr.active; + ok(newest, "Waiting or active worker should still exist"); + ok(newest.scriptURL.match(/install_event_worker.js$/), "Previous worker should remain the newest worker"); + }); + }); + } + + function testActive(worker) { + is(worker.state, "activating", "Should be activating"); + return new Promise(function(resolve, reject) { + worker.onstatechange = function(e) { + e.target.onstatechange = null; + is(e.target.state, "activated", "Activation of worker with error in activate event handler should still succeed."); + resolve(); + } + }); + } + + function activateErrorShouldSucceed() { + // Silence worker errors so they don't cause the test to fail. + window.onerror = function() { } + return navigator.serviceWorker.register("activate_event_error_worker.js", { scope: "./activate_error" }) + .then(function(swr) { + var p = new Promise(function(resolve, reject) { + ok(swr.installing.state == "installing", "activateErrorShouldSucceed(): Installing worker's state should be 'installing'"); + swr.installing.onstatechange = function(e) { + e.target.onstatechange = null; + if (swr.waiting) { + swr.waiting.onstatechange = function(e) { + e.target.onstatechange = null; + testActive(swr.active).then(resolve, reject); + } + } else { + testActive(swr.active).then(resolve, reject); + } + } + }); + + return p.then(function() { + return Promise.resolve(swr); + }); + }).then(function(swr) { + return swr.unregister(); + }); + } + + function unregister() { + return navigator.serviceWorker.getRegistration("./install_event").then(function(reg) { + return reg.unregister(); + }); + } + + function runTest() { + Promise.resolve() + .then(simpleRegister) + .then(nextRegister) + .then(installError) + .then(activateErrorShouldSucceed) + .then(unregister) + .then(function() { + SimpleTest.finish(); + }).catch(function(e) { + ok(false, "Some test failed with error " + e); + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_install_event_gc.html b/dom/workers/test/serviceworkers/test_install_event_gc.html new file mode 100644 index 000000000..ccccd2b43 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_install_event_gc.html @@ -0,0 +1,121 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test install event being GC'd before waitUntil fulfills</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<script class="testbody" type="text/javascript"> +var script = 'blocking_install_event_worker.js'; +var scope = 'sw_clients/simple.html?install-event-gc'; +var registration; + +function register() { + return navigator.serviceWorker.register(script, { scope: scope }) + .then(swr => registration = swr); +} + +function unregister() { + if (!registration) { + return; + } + return registration.unregister(); +} + +function waitForInstallEvent() { + return new Promise((resolve, reject) => { + navigator.serviceWorker.addEventListener('message', evt => { + if (evt.data.type === 'INSTALL_EVENT') { + resolve(); + } + }); + }); +} + +function gcWorker() { + return new Promise(function(resolve, reject) { + // We are able to trigger asynchronous garbage collection and cycle + // collection by emitting "child-cc-request" and "child-gc-request" + // observer notifications. The worker RuntimeService will translate + // these notifications into the appropriate operation on all known + // worker threads. + // + // In the failure case where GC/CC causes us to abort the installation, + // we will know something happened from the statechange event. + const statechangeHandler = evt => { + // Reject rather than resolving to avoid the possibility of us seeing + // an unrelated racing statechange somehow. Since in the success case we + // will still see a state change on termination, we do explicitly need to + // be removed on the success path. + ok(registration.installing, 'service worker is still installing?'); + reject(); + }; + registration.installing.addEventListener('statechange', statechangeHandler); + // In the success case since the service worker installation is effectively + // hung, we instead depend on sending a 'ping' message to the service worker + // and hearing it 'pong' back. Since we issue our postMessage after we + // trigger the GC/CC, our 'ping' will only be processed after the GC/CC and + // therefore the pong will also strictly occur after the cycle collection. + navigator.serviceWorker.addEventListener('message', evt => { + if (evt.data.type === 'pong') { + registration.installing.removeEventListener( + 'statechange', statechangeHandler); + resolve(); + } + }); + // At the current time, the service worker will exist in our same process + // and notifyObservers is synchronous. However, in the future, service + // workers may end up in a separate process and in that case it will be + // appropriate to use notifyObserversInParentProcess or something like it. + // (notifyObserversInParentProcess is a synchronous IPC call to the parent + // process's main thread. IPDL PContent::CycleCollect is an async message. + // Ordering will be maintained if the postMessage goes via PContent as well, + // but that seems unlikely.) + SpecialPowers.notifyObservers(null, 'child-gc-request', null); + SpecialPowers.notifyObservers(null, 'child-cc-request', null); + SpecialPowers.notifyObservers(null, 'child-gc-request', null); + // (Only send the ping after we set the gc/cc/gc in motion.) + registration.installing.postMessage({ type: 'ping' }); + }); +} + +function terminateWorker() { + return SpecialPowers.pushPrefEnv({ + set: [ + ["dom.serviceWorkers.idle_timeout", 0], + ["dom.serviceWorkers.idle_extended_timeout", 0] + ] + }).then(_ => { + registration.installing.postMessage({ type: 'RESET_TIMER' }); + }); +} + +function runTest() { + Promise.all([ + waitForInstallEvent(), + register() + ]).then(_ => ok(registration.installing, 'service worker is installing')) + .then(gcWorker) + .then(_ => ok(registration.installing, 'service worker is still installing')) + .then(terminateWorker) + .catch(e => ok(false, e)) + .then(unregister) + .then(SimpleTest.finish); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.caches.enabled", true], +]}, runTest); +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_installation_simple.html b/dom/workers/test/serviceworkers/test_installation_simple.html new file mode 100644 index 000000000..1b0d6c947 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_installation_simple.html @@ -0,0 +1,212 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 930348 - test stub Navigator ServiceWorker utilities.</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + function simpleRegister() { + var p = navigator.serviceWorker.register("worker.js", { scope: "simpleregister/" }); + ok(p instanceof Promise, "register() should return a Promise"); + return Promise.resolve(); + } + + function sameOriginWorker() { + p = navigator.serviceWorker.register("http://some-other-origin/worker.js"); + return p.then(function(w) { + ok(false, "Worker from different origin should fail"); + }, function(e) { + ok(e.name === "SecurityError", "Should fail with a SecurityError"); + }); + } + + function sameOriginScope() { + p = navigator.serviceWorker.register("worker.js", { scope: "http://www.example.com/" }); + return p.then(function(w) { + ok(false, "Worker controlling scope for different origin should fail"); + }, function(e) { + ok(e.name === "SecurityError", "Should fail with a SecurityError"); + }); + } + + function httpsOnly() { + var promise = new Promise(function(resolve) { + SpecialPowers.pushPrefEnv({'set': [["dom.serviceWorkers.testing.enabled", false]] }, resolve); + }); + + return promise.then(function() { + return navigator.serviceWorker.register("/worker.js"); + }).then(function(w) { + ok(false, "non-HTTPS pages cannot register ServiceWorkers"); + }, function(e) { + ok(e.name === "SecurityError", "Should fail with a SecurityError"); + }).then(function() { + return new Promise((resolve) => SpecialPowers.popPrefEnv(resolve)); + }); + } + + function realWorker() { + var p = navigator.serviceWorker.register("worker.js", { scope: "realworker" }); + return p.then(function(wr) { + ok(wr instanceof ServiceWorkerRegistration, "Register a ServiceWorker"); + + info(wr.scope); + ok(wr.scope == (new URL("realworker", document.baseURI)).href, "Scope should match"); + // active, waiting, installing should return valid worker instances + // because the registration is for the realworker scope, so the workers + // should be obtained for that scope and not for + // test_installation_simple.html + var worker = wr.installing; + ok(worker && wr.scope.match(/realworker$/) && + worker.scriptURL.match(/worker.js$/), "Valid worker instance should be available."); + return wr.unregister().then(function(success) { + ok(success, "The worker should be unregistered successfully"); + }, function(e) { + dump("Error unregistering the worker: " + e + "\n"); + }); + }, function(e) { + info("Error: " + e.name); + ok(false, "realWorker Registration should have succeeded!"); + }); + } + + function networkError404() { + return navigator.serviceWorker.register("404.js", { scope: "network_error/"}).then(function(w) { + ok(false, "404 response should fail with TypeError"); + }, function(e) { + ok(e.name === "TypeError", "404 response should fail with TypeError"); + }); + } + + function redirectError() { + return navigator.serviceWorker.register("redirect_serviceworker.sjs", { scope: "redirect_error/" }).then(function(swr) { + ok(false, "redirection should fail"); + }, function (e) { + ok(e.name === "SecurityError", "redirection should fail with SecurityError"); + }); + } + + function parseError() { + var p = navigator.serviceWorker.register("parse_error_worker.js", { scope: "parse_error/" }); + return p.then(function(wr) { + ok(false, "Registration should fail with parse error"); + return navigator.serviceWorker.getRegistration("parse_error/").then(function(swr) { + // See https://github.com/slightlyoff/ServiceWorker/issues/547 + is(swr, undefined, "A failed registration for a scope with no prior controllers should clear itself"); + }); + }, function(e) { + ok(e instanceof Error, "Registration should fail with parse error"); + }); + } + + // FIXME(nsm): test for parse error when Update step doesn't happen (directly from register). + + function updatefound() { + var frame = document.createElement("iframe"); + frame.setAttribute("id", "simpleregister-frame"); + frame.setAttribute("src", new URL("simpleregister/index.html", document.baseURI).href); + document.body.appendChild(frame); + var resolve, reject; + var p = new Promise(function(res, rej) { + resolve = res; + reject = rej; + }); + + var reg; + function continueTest() { + navigator.serviceWorker.register("worker2.js", { scope: "simpleregister/" }) + .then(function(r) { + reg = r; + });; + } + + window.onmessage = function(e) { + if (e.data.type == "ready") { + continueTest(); + } else if (e.data.type == "finish") { + window.onmessage = null; + // We have to make frame navigate away, otherwise it will call + // MaybeStopControlling() when this document is unloaded. At that point + // the pref has been disabled, so the ServiceWorkerManager is not available. + frame.setAttribute("src", new URL("about:blank").href); + reg.unregister().then(function(success) { + ok(success, "The worker should be unregistered successfully"); + resolve(); + }, function(e) { + dump("Error unregistering the worker: " + e + "\n"); + }); + } else if (e.data.type == "check") { + ok(e.data.status, e.data.msg); + } + } + return p; + } + + var readyPromiseResolved = false; + + function readyPromise() { + var frame = document.createElement("iframe"); + frame.setAttribute("id", "simpleregister-frame-ready"); + frame.setAttribute("src", new URL("simpleregister/ready.html", document.baseURI).href); + document.body.appendChild(frame); + + var channel = new MessageChannel(); + frame.addEventListener('load', function() { + frame.contentWindow.postMessage('your port!', '*', [channel.port2]); + }, false); + + channel.port1.onmessage = function() { + readyPromiseResolved = true; + } + + return Promise.resolve(); + } + + function checkReadyPromise() { + ok(readyPromiseResolved, "The ready promise has been resolved!"); + return Promise.resolve(); + } + + function runTest() { + simpleRegister() + .then(readyPromise) + .then(sameOriginWorker) + .then(sameOriginScope) + .then(httpsOnly) + .then(realWorker) + .then(networkError404) + .then(redirectError) + .then(parseError) + .then(updatefound) + .then(checkReadyPromise) + // put more tests here. + .then(function() { + SimpleTest.finish(); + }).catch(function(e) { + ok(false, "Some test failed with error " + e); + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.caches.testing.enabled", true], + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_match_all.html b/dom/workers/test/serviceworkers/test_match_all.html new file mode 100644 index 000000000..f4e65a730 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_match_all.html @@ -0,0 +1,80 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 982726 - test match_all not crashing</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + // match_all_worker will call matchAll until the worker shuts down. + // Test passes if the browser doesn't crash on leaked promise objects. + var registration; + var content; + var iframe; + + function simpleRegister() { + return navigator.serviceWorker.register("match_all_worker.js", + { scope: "./sw_clients/" }) + .then((swr) => registration = swr); + } + + function closeAndUnregister() { + content.removeChild(iframe); + + return registration.unregister().then(function(result) { + ok(result, "Unregister should return true."); + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + } + + function openClient() { + var p = new Promise(function(resolve, reject) { + window.onmessage = function(e) { + if (e.data === "READY") { + resolve(); + } + } + }); + + content = document.getElementById("content"); + ok(content, "Parent exists."); + + iframe = document.createElement("iframe"); + iframe.setAttribute('src', "sw_clients/simple.html"); + content.appendChild(iframe); + + return p; + } + + function runTest() { + simpleRegister() + .then(openClient) + .then(closeAndUnregister) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + }).then(function() { + ok(true, "Didn't crash on resolving matchAll promises while worker shuts down."); + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_match_all_advanced.html b/dom/workers/test/serviceworkers/test_match_all_advanced.html new file mode 100644 index 000000000..a458ed70b --- /dev/null +++ b/dom/workers/test/serviceworkers/test_match_all_advanced.html @@ -0,0 +1,100 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 982726 - Test matchAll with multiple clients</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + var client_iframes = []; + var registration; + + function start() { + return navigator.serviceWorker.register("match_all_advanced_worker.js", + { scope: "./sw_clients/" }).then(function(swr) { + registration = swr; + window.onmessage = function (e) { + if (e.data === "READY") { + ok(registration.active, "Worker is active."); + registration.active.postMessage("RUN"); + } + } + }); + } + + function unregister() { + return registration.unregister().then(function(result) { + ok(result, "Unregister should return true."); + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + } + + + function testMatchAll() { + var p = new Promise(function(res, rej) { + navigator.serviceWorker.onmessage = function (e) { + ok(e.data === client_iframes.length, "MatchAll returned the correct number of clients."); + res(); + } + }); + + content = document.getElementById("content"); + ok(content, "Parent exists."); + + iframe = document.createElement("iframe"); + iframe.setAttribute('src', "sw_clients/service_worker_controlled.html"); + content.appendChild(iframe); + + client_iframes.push(iframe); + return p; + } + + function removeAndTest() { + content = document.getElementById("content"); + ok(content, "Parent exists."); + + content.removeChild(client_iframes.pop()); + content.removeChild(client_iframes.pop()); + + return testMatchAll(); + } + + function runTest() { + start() + .then(testMatchAll) + .then(testMatchAll) + .then(testMatchAll) + .then(removeAndTest) + .then(function(e) { + content = document.getElementById("content"); + while (client_iframes.length) { + content.removeChild(client_iframes.pop()); + } + }).then(unregister).catch(function(e) { + ok(false, "Some test failed with error " + e); + }).then(function() { + SimpleTest.finish(); + }); + + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_match_all_client_id.html b/dom/workers/test/serviceworkers/test_match_all_client_id.html new file mode 100644 index 000000000..4c8ed9673 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_match_all_client_id.html @@ -0,0 +1,91 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1058311 - Test matchAll client id </title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + var registration; + var clientURL = "match_all_client/match_all_client_id.html"; + function start() { + return navigator.serviceWorker.register("match_all_client_id_worker.js", + { scope: "./match_all_client/" }) + .then((swr) => registration = swr); + } + + function unregister() { + return registration.unregister().then(function(result) { + ok(result, "Unregister should return true."); + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + } + + function getMessageListener() { + return new Promise(function(res, rej) { + window.onmessage = function(e) { + ok(e.data, "Same client id for multiple calls."); + is(e.origin, "http://mochi.test:8888", "Event should have the correct origin"); + + if (!e.data) { + rej(); + return; + } + + info("DONE from: " + e.source); + res(); + } + }); + } + + function testNestedWindow() { + var p = getMessageListener(); + + var content = document.getElementById("content"); + ok(content, "Parent exists."); + + iframe = document.createElement("iframe"); + + content.appendChild(iframe); + iframe.setAttribute('src', clientURL); + + return p.then(() => content.removeChild(iframe)); + } + + function testAuxiliaryWindow() { + var p = getMessageListener(); + var w = window.open(clientURL); + + return p.then(() => w.close()); + } + + function runTest() { + info(window.opener == undefined); + start() + .then(testAuxiliaryWindow) + .then(testNestedWindow) + .then(unregister) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + }).then(SimpleTest.finish); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_match_all_client_properties.html b/dom/workers/test/serviceworkers/test_match_all_client_properties.html new file mode 100644 index 000000000..14e3445a4 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_match_all_client_properties.html @@ -0,0 +1,97 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1058311 - Test matchAll clients properties </title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + var registration; + var clientURL = "match_all_clients/match_all_controlled.html"; + function start() { + return navigator.serviceWorker.register("match_all_properties_worker.js", + { scope: "./match_all_clients/" }) + .then((swr) => registration = swr); + } + + function unregister() { + return registration.unregister().then(function(result) { + ok(result, "Unregister should return true."); + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + } + + function getMessageListener() { + return new Promise(function(res, rej) { + window.onmessage = function(e) { + if (e.data.message === undefined) { + info("rejecting promise"); + rej(); + return; + } + + ok(e.data.result, e.data.message); + + if (!e.data.result) { + rej(); + } + if (e.data.message == "DONE") { + info("DONE from: " + e.source); + res(); + } + } + }); + } + + function testNestedWindow() { + var p = getMessageListener(); + + var content = document.getElementById("content"); + ok(content, "Parent exists."); + + iframe = document.createElement("iframe"); + + content.appendChild(iframe); + iframe.setAttribute('src', clientURL); + + return p.then(() => content.removeChild(iframe)); + } + + function testAuxiliaryWindow() { + var p = getMessageListener(); + var w = window.open(clientURL); + + return p.then(() => w.close()); + } + + function runTest() { + info("catalin"); + info(window.opener == undefined); + start() + .then(testAuxiliaryWindow) + .then(testNestedWindow) + .then(unregister) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + }).then(SimpleTest.finish); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_navigator.html b/dom/workers/test/serviceworkers/test_navigator.html new file mode 100644 index 000000000..164f41bcd --- /dev/null +++ b/dom/workers/test/serviceworkers/test_navigator.html @@ -0,0 +1,40 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 930348 - test stub Navigator ServiceWorker utilities.</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + function checkEnabled() { + ok(navigator.serviceWorker, "navigator.serviceWorker should exist when ServiceWorkers are enabled."); + ok(typeof navigator.serviceWorker.register === "function", "navigator.serviceWorker.register() should be a function."); + ok(typeof navigator.serviceWorker.getRegistration === "function", "navigator.serviceWorker.getAll() should be a function."); + ok(typeof navigator.serviceWorker.getRegistrations === "function", "navigator.serviceWorker.getAll() should be a function."); + ok(navigator.serviceWorker.ready instanceof Promise, "navigator.serviceWorker.ready should be a Promise."); + ok(navigator.serviceWorker.controller === null, "There should be no controller worker for an uncontrolled document."); + } + + SimpleTest.waitForExplicitFinish(); + + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true] + ]}, function() { + checkEnabled(); + SimpleTest.finish(); + }); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_not_intercept_plugin.html b/dom/workers/test/serviceworkers/test_not_intercept_plugin.html new file mode 100644 index 000000000..a90e068d3 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_not_intercept_plugin.html @@ -0,0 +1,78 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1187766 - Test loading plugins scenarios with fetch interception.</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + SimpleTest.requestCompleteLog(); + + var registration; + function simpleRegister() { + var p = navigator.serviceWorker.register("./fetch/plugin/worker.js", { scope: "./fetch/plugin/" }); + return p.then(function(swr) { + registration = swr; + return new Promise(function(resolve) { + swr.installing.onstatechange = resolve; + }); + }); + } + + function unregister() { + return registration.unregister().then(function(success) { + ok(success, "Service worker should be unregistered successfully"); + }, function(e) { + dump("SW unregistration error: " + e + "\n"); + }); + } + + function testPlugins() { + var p = new Promise(function(resolve, reject) { + window.onmessage = function(e) { + if (e.data.status == "ok") { + ok(e.data.result, e.data.message); + } else if (e.data.status == "done") { + window.onmessage = null; + w.close(); + resolve(); + } + } + }); + + var w = window.open("fetch/plugin/plugins.html"); + return p; + } + + function runTest() { + simpleRegister() + .then(testPlugins) + .then(unregister) + .then(function() { + SimpleTest.finish(); + }).catch(function(e) { + ok(false, "Some test failed with error " + e); + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.requestcontext.enabled", true], + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_notification_constructor_error.html b/dom/workers/test/serviceworkers/test_notification_constructor_error.html new file mode 100644 index 000000000..6a8ecf8c0 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_notification_constructor_error.html @@ -0,0 +1,52 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug XXXXXXX - Check that Notification constructor throws in ServiceWorkerGlobalScope</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script> + <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + function simpleRegister() { + return navigator.serviceWorker.register("notification_constructor_error.js", { scope: "notification_constructor_error/" }).then(function(swr) { + ok(false, "Registration should fail."); + }, function(e) { + is(e.name, 'TypeError', "Registration should fail with a TypeError."); + }); + } + + function runTest() { + MockServices.register(); + simpleRegister() + .then(function() { + MockServices.unregister(); + SimpleTest.finish(); + }).catch(function(e) { + ok(false, "Some test failed with error " + e); + MockServices.unregister(); + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["notification.prompt.testing", true], + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_notification_get.html b/dom/workers/test/serviceworkers/test_notification_get.html new file mode 100644 index 000000000..dbb312e7b --- /dev/null +++ b/dom/workers/test/serviceworkers/test_notification_get.html @@ -0,0 +1,213 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>ServiceWorkerRegistration.getNotifications() on main thread and worker thread.</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script> + <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script type="text/javascript"> + + SimpleTest.requestFlakyTimeout("untriaged"); + + function testFrame(src) { + return new Promise(function(resolve, reject) { + var iframe = document.createElement("iframe"); + iframe.src = src; + window.callback = function(result) { + iframe.src = "about:blank"; + document.body.removeChild(iframe); + iframe = null; + SpecialPowers.exactGC(function() { + resolve(result); + }); + }; + document.body.appendChild(iframe); + }); + } + + function registerSW() { + return testFrame('notification/register.html').then(function() { + ok(true, "Registered service worker."); + }); + } + + function unregisterSW() { + return testFrame('notification/unregister.html').then(function() { + ok(true, "Unregistered service worker."); + }); + } + + // To check that the scope is respected when retrieving notifications. + function registerAlternateSWAndAddNotification() { + return testFrame('notification_alt/register.html').then(function() { + ok(true, "Registered alternate service worker."); + return navigator.serviceWorker.getRegistration("./notification_alt/").then(function(reg) { + return reg.showNotification("This is a notification_alt"); + }); + }); + } + + function unregisterAlternateSWAndAddNotification() { + return testFrame('notification_alt/unregister.html').then(function() { + ok(true, "unregistered alternate service worker."); + }); + } + + function testDismiss() { + // Dismissed persistent notifications should be removed from the + // notification list. + var alertsService = SpecialPowers.Cc["@mozilla.org/alerts-service;1"] + .getService(SpecialPowers.Ci.nsIAlertsService); + return navigator.serviceWorker.getRegistration("./notification/") + .then(function(reg) { + return reg.showNotification( + "This is a notification that will be closed", { tag: "dismiss" }) + .then(function() { + return reg; + }); + }).then(function(reg) { + return reg.getNotifications() + .then(function(notifications) { + is(notifications.length, 1, "There should be one visible notification"); + is(notifications[0].tag, "dismiss", "Tag should match"); + + // Simulate dismissing the notification by using the alerts service + // directly, instead of `Notification#close`. + var principal = SpecialPowers.wrap(document).nodePrincipal; + var id = principal.origin + "#tag:dismiss"; + alertsService.closeAlert(id, principal); + + return reg; + }); + }).then(function(reg) { + return reg.getNotifications(); + }).then(function(notifications) { + // Make sure dismissed notifications are no longer retrieved. + is(notifications.length, 0, "There should be no more stored notifications"); + }); + } + + function testGet() { + // Non persistent notifications will not show up in getNotification(). + var n = new Notification("Scope does not match"); + var options = NotificationTest.payload; + return navigator.serviceWorker.getRegistration("./notification/") + .then(function(reg) { + return reg.showNotification("This is a title", options) + .then(function() { + return reg; + }); + }).then(function(reg) { + return registerAlternateSWAndAddNotification().then(function() { + return reg; + }); + }).then(function(reg) { + return reg.getNotifications(); + }).then(function(notifications) { + is(notifications.length, 1, "There should be one stored notification"); + var notification = notifications[0]; + ok(notification instanceof Notification, "Should be a Notification"); + is(notification.title, "This is a title", "Title should match"); + for (var key in options) { + if (key === "data") { + ok(NotificationTest.customDataMatches(notification.data), + "data property should match"); + continue; + } + is(notification[key], options[key], key + " property should match"); + } + notification.close(); + }).then(function() { + return navigator.serviceWorker.getRegistration("./notification/").then(function(reg) { + return reg.getNotifications(); + }); + }).then(function(notifications) { + // Make sure closed notifications are no longer retrieved. + is(notifications.length, 0, "There should be no more stored notifications"); + }).catch(function(e) { + ok(false, "Something went wrong " + e.message); + }).then(unregisterAlternateSWAndAddNotification); + } + + function testGetWorker() { + todo(false, "navigator.serviceWorker is not available on workers yet"); + return Promise.resolve(); + } + + function waitForSWTests(reg, msg) { + return new Promise(function(resolve, reject) { + var content = document.getElementById("content"); + + iframe = document.createElement("iframe"); + + content.appendChild(iframe); + iframe.setAttribute('src', "notification/listener.html"); + + window.onmessage = function(e) { + if (e.data.type == 'status') { + ok(e.data.status, "Service worker test: " + e.data.msg); + } else if (e.data.type == 'finish') { + content.removeChild(iframe); + resolve(); + } + } + + iframe.onload = function(e) { + iframe.onload = null; + reg.active.postMessage(msg); + } + }); + } + + function testGetServiceWorker() { + return navigator.serviceWorker.getRegistration("./notification/") + .then(function(reg) { + return waitForSWTests(reg, 'create'); + }); + } + + // Create a Notification here, make sure ServiceWorker sees it. + function testAcrossThreads() { + return navigator.serviceWorker.getRegistration("./notification/") + .then(function(reg) { + return reg.showNotification("This is a title") + .then(function() { + return reg; + }); + }).then(function(reg) { + return waitForSWTests(reg, 'do-not-create'); + }); + } + + SimpleTest.waitForExplicitFinish(); + + MockServices.register(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.webnotifications.workers.enabled", true], + ["dom.webnotifications.serviceworker.enabled", true], + ["notification.prompt.testing", true], + ]}, function() { + registerSW() + .then(testGet) + .then(testGetWorker) + .then(testGetServiceWorker) + .then(testAcrossThreads) + .then(testDismiss) + .then(unregisterSW) + .then(function() { + MockServices.unregister(); + SimpleTest.finish(); + }); + }); +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_notificationclick-otherwindow.html b/dom/workers/test/serviceworkers/test_notificationclick-otherwindow.html new file mode 100644 index 000000000..4a785be9a --- /dev/null +++ b/dom/workers/test/serviceworkers/test_notificationclick-otherwindow.html @@ -0,0 +1,62 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=916893 +--> +<head> + <title>Bug 1114554 - Test ServiceWorkerGlobalScope.notificationclick event.</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script> + <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1114554">Bug 1114554</a> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> +</pre> +<script type="text/javascript"> + SimpleTest.requestFlakyTimeout("Mock alert service dispatches show and click events."); + + function testFrame(src) { + var iframe = document.createElement("iframe"); + iframe.src = src; + window.callback = function(result) { + window.callback = null; + document.body.removeChild(iframe); + iframe = null; + ok(result, "Got notificationclick event with correct data."); + MockServices.unregister(); + registration.unregister().then(function() { + SimpleTest.finish(); + }); + }; + document.body.appendChild(iframe); + } + + var registration; + + function runTest() { + MockServices.register(); + testFrame('notificationclick-otherwindow.html'); + navigator.serviceWorker.register("notificationclick.js", { scope: "notificationclick-otherwindow.html" }).then(function(reg) { + registration = reg; + }, function(e) { + ok(false, "registration should have passed!"); + }); + }; + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.webnotifications.workers.enabled", true], + ["dom.webnotifications.serviceworker.enabled", true], + ["notification.prompt.testing", true], + ]}, runTest); +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_notificationclick.html b/dom/workers/test/serviceworkers/test_notificationclick.html new file mode 100644 index 000000000..d5c3ecf8b --- /dev/null +++ b/dom/workers/test/serviceworkers/test_notificationclick.html @@ -0,0 +1,62 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=916893 +--> +<head> + <title>Bug 1114554 - Test ServiceWorkerGlobalScope.notificationclick event.</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script> + <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1114554">Bug 1114554</a> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> +</pre> +<script type="text/javascript"> + SimpleTest.requestFlakyTimeout("Mock alert service dispatches show and click events."); + + function testFrame(src) { + var iframe = document.createElement("iframe"); + iframe.src = src; + window.callback = function(result) { + window.callback = null; + document.body.removeChild(iframe); + iframe = null; + ok(result, "Got notificationclick event with correct data."); + MockServices.unregister(); + registration.unregister().then(function() { + SimpleTest.finish(); + }); + }; + document.body.appendChild(iframe); + } + + var registration; + + function runTest() { + MockServices.register(); + testFrame('notificationclick.html'); + navigator.serviceWorker.register("notificationclick.js", { scope: "notificationclick.html" }).then(function(reg) { + registration = reg; + }, function(e) { + ok(false, "registration should have passed!"); + }); + }; + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.webnotifications.workers.enabled", true], + ["dom.webnotifications.serviceworker.enabled", true], + ["notification.prompt.testing", true], + ]}, runTest); +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_notificationclick_focus.html b/dom/workers/test/serviceworkers/test_notificationclick_focus.html new file mode 100644 index 000000000..81d6e269c --- /dev/null +++ b/dom/workers/test/serviceworkers/test_notificationclick_focus.html @@ -0,0 +1,63 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=916893 +--> +<head> + <title>Bug 1144660 - Test client.focus() permissions on notification click</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script> + <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1114554">Bug 1114554</a> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> +</pre> +<script type="text/javascript"> + SimpleTest.requestFlakyTimeout("Mock alert service dispatches show and click events."); + + function testFrame(src) { + var iframe = document.createElement("iframe"); + iframe.src = src; + window.callback = function(result) { + window.callback = null; + document.body.removeChild(iframe); + iframe = null; + ok(result, "All tests passed."); + MockServices.unregister(); + registration.unregister().then(function() { + SimpleTest.finish(); + }); + }; + document.body.appendChild(iframe); + } + + var registration; + + function runTest() { + MockServices.register(); + testFrame('notificationclick_focus.html'); + navigator.serviceWorker.register("notificationclick_focus.js", { scope: "notificationclick_focus.html" }).then(function(reg) { + registration = reg; + }, function(e) { + ok(false, "registration should have passed!"); + }); + }; + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.webnotifications.workers.enabled", true], + ["dom.webnotifications.serviceworker.enabled", true], + ["notification.prompt.testing", true], + ["dom.disable_open_click_delay", 1000], + ]}, runTest); +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_notificationclose.html b/dom/workers/test/serviceworkers/test_notificationclose.html new file mode 100644 index 000000000..3b81132c4 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_notificationclose.html @@ -0,0 +1,62 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1265841 +--> +<head> + <title>Bug 1265841 - Test ServiceWorkerGlobalScope.notificationclose event.</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script> + <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1265841">Bug 1265841</a> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> +</pre> +<script type="text/javascript"> + SimpleTest.requestFlakyTimeout("Mock alert service dispatches show, click, and close events."); + + function testFrame(src) { + var iframe = document.createElement("iframe"); + iframe.src = src; + window.callback = function(result) { + window.callback = null; + document.body.removeChild(iframe); + iframe = null; + ok(result, "Got notificationclose event with correct data."); + MockServices.unregister(); + registration.unregister().then(function() { + SimpleTest.finish(); + }); + }; + document.body.appendChild(iframe); + } + + var registration; + + function runTest() { + MockServices.register(); + testFrame('notificationclose.html'); + navigator.serviceWorker.register("notificationclose.js", { scope: "notificationclose.html" }).then(function(reg) { + registration = reg; + }, function(e) { + ok(false, "registration should have passed!"); + }); + }; + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.webnotifications.workers.enabled", true], + ["dom.webnotifications.serviceworker.enabled", true], + ["notification.prompt.testing", true], + ]}, runTest); +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_opaque_intercept.html b/dom/workers/test/serviceworkers/test_opaque_intercept.html new file mode 100644 index 000000000..5cb12e518 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_opaque_intercept.html @@ -0,0 +1,85 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 982726 - Test service worker post message </title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + var registration; + function start() { + return navigator.serviceWorker.register("opaque_intercept_worker.js", + { scope: "./sw_clients/" }) + .then((swr) => registration = swr); + } + + function unregister() { + return registration.unregister().then(function(result) { + ok(result, "Unregister should return true."); + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + } + + + function testOpaqueIntercept(swr) { + var p = new Promise(function(res, rej) { + var ready = false; + var scriptLoaded = false; + window.onmessage = function(e) { + if (e.data === "READY") { + ok(!ready, "ready message should only be received once"); + ok(!scriptLoaded, "ready message should be received before script loaded"); + if (ready) { + res(); + return; + } + ready = true; + iframe.contentWindow.postMessage("REFRESH", "*"); + } else if (e.data === "SCRIPT_LOADED") { + ok(ready, "script loaded should be received after ready"); + ok(!scriptLoaded, "script loaded message should be received only once"); + scriptLoaded = true; + res(); + } + } + }); + + var content = document.getElementById("content"); + ok(content, "Parent exists."); + + iframe = document.createElement("iframe"); + iframe.setAttribute('src', "sw_clients/refresher.html"); + content.appendChild(iframe); + + return p.then(() => content.removeChild(iframe)); + } + + function runTest() { + start() + .then(testOpaqueIntercept) + .then(unregister) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + }).then(SimpleTest.finish); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.caches.enabled", true], + ]}, runTest); +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_openWindow.html b/dom/workers/test/serviceworkers/test_openWindow.html new file mode 100644 index 000000000..2417648b9 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_openWindow.html @@ -0,0 +1,117 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1172870 +--> +<head> + <title>Bug 1172870 - Test clients.openWindow</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script> + <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1172870">Bug 1172870</a> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> +</pre> +<script type="text/javascript"> + SimpleTest.requestFlakyTimeout("Mock alert service dispatches show and click events."); + + function setup(ctx) { + MockServices.register(); + + return navigator.serviceWorker.register("openWindow_worker.js", {scope: "./"}) + .then(function(swr) { + ok(swr, "Registration successful"); + ctx.registration = swr; + return ctx; + }); + } + + function waitForActiveServiceWorker(ctx) { + return navigator.serviceWorker.ready.then(function(result) { + ok(ctx.registration.active, "Service Worker is active"); + return ctx; + }); + } + + function setupMessageHandler(ctx) { + return new Promise(function(res, rej) { + navigator.serviceWorker.onmessage = function(event) { + navigator.serviceWorker.onmessage = null; + for (i = 0; i < event.data.length; i++) { + ok(event.data[i].result, event.data[i].message); + } + res(ctx); + } + }); + } + + function testPopupNotAllowed(ctx) { + var p = setupMessageHandler(ctx); + ok(ctx.registration.active, "Worker is active."); + ctx.registration.active.postMessage("testNoPopup"); + + return p; + } + + function testPopupAllowed(ctx) { + var p = setupMessageHandler(ctx); + ctx.registration.showNotification("testPopup"); + + return p; + } + + function checkNumberOfWindows(ctx) { + return new Promise(function(res, rej) { + navigator.serviceWorker.onmessage = function(event) { + navigator.serviceWorker.onmessage = null; + ok(event.data.result, event.data.message); + res(ctx); + } + ctx.registration.active.postMessage("CHECK_NUMBER_OF_WINDOWS"); + }); + } + + function clear(ctx) { + MockServices.unregister(); + + return ctx.registration.unregister().then(function(result) { + ctx.registration = null; + ok(result, "Unregister was successful."); + }); + } + + function runTest() { + setup({}) + .then(waitForActiveServiceWorker) + // Permission to allow popups persists for some time after a notification + // click event, so the order here is important. + .then(testPopupNotAllowed) + .then(testPopupAllowed) + .then(checkNumberOfWindows) + .then(clear) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + }).then(SimpleTest.finish); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.openWindow.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.webnotifications.workers.enabled", true], + ["dom.webnotifications.serviceworker.enabled", true], + ["notification.prompt.testing", true], + ["dom.disable_open_click_delay", 1000], + ["dom.serviceWorkers.idle_timeout", 299999], + ["dom.serviceWorkers.idle_extended_timeout", 299999] + ]}, runTest); +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_origin_after_redirect.html b/dom/workers/test/serviceworkers/test_origin_after_redirect.html new file mode 100644 index 000000000..b68537d9d --- /dev/null +++ b/dom/workers/test/serviceworkers/test_origin_after_redirect.html @@ -0,0 +1,58 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test the origin of a redirected response from a service worker</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +<iframe></iframe> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + var iframe; + function runTest() { + iframe = document.querySelector("iframe"); + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/register.html"; + var win; + window.onmessage = function(e) { + if (e.data.status == "ok") { + ok(e.data.result, e.data.message); + } else if (e.data.status == "registrationdone") { + win = window.open("/tests/dom/workers/test/serviceworkers/fetch/origin/index.sjs", "mywindow", "width=100,height=100"); + } else if (e.data.status == "domain") { + is(e.data.data, "example.org", "Correct domain expected"); + } else if (e.data.status == "origin") { + is(e.data.data, "http://example.org", "Correct origin expected"); + } else if (e.data.status == "done") { + win.close(); + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/unregister.html"; + } else if (e.data.status == "unregistrationdone") { + window.onmessage = null; + ok(true, "Test finished successfully"); + SimpleTest.finish(); + } + }; + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.openWindow.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.caches.enabled", true], + ]}, runTest); + }; +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_origin_after_redirect_cached.html b/dom/workers/test/serviceworkers/test_origin_after_redirect_cached.html new file mode 100644 index 000000000..69644abfa --- /dev/null +++ b/dom/workers/test/serviceworkers/test_origin_after_redirect_cached.html @@ -0,0 +1,58 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test the origin of a redirected response from a service worker</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +<iframe></iframe> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + var iframe; + function runTest() { + iframe = document.querySelector("iframe"); + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/register.html"; + var win; + window.onmessage = function(e) { + if (e.data.status == "ok") { + ok(e.data.result, e.data.message); + } else if (e.data.status == "registrationdone") { + win = window.open("/tests/dom/workers/test/serviceworkers/fetch/origin/index-cached.sjs", "mywindow", "width=100,height=100"); + } else if (e.data.status == "domain") { + is(e.data.data, "example.org", "Correct domain expected"); + } else if (e.data.status == "origin") { + is(e.data.data, "http://example.org", "Correct origin expected"); + } else if (e.data.status == "done") { + win.close(); + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/unregister.html"; + } else if (e.data.status == "unregistrationdone") { + window.onmessage = null; + ok(true, "Test finished successfully"); + SimpleTest.finish(); + } + }; + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.openWindow.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.caches.enabled", true], + ]}, runTest); + }; +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https.html b/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https.html new file mode 100644 index 000000000..dcac11aea --- /dev/null +++ b/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https.html @@ -0,0 +1,57 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test the origin of a redirected response from a service worker</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +<iframe></iframe> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + var iframe; + function runTest() { + iframe = document.querySelector("iframe"); + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/register.html"; + var win; + window.onmessage = function(e) { + if (e.data.status == "ok") { + ok(e.data.result, e.data.message); + } else if (e.data.status == "registrationdone") { + win = window.open("/tests/dom/workers/test/serviceworkers/fetch/origin/index-to-https.sjs", "mywindow", "width=100,height=100"); + } else if (e.data.status == "domain") { + is(e.data.data, "example.org", "Correct domain expected"); + } else if (e.data.status == "origin") { + is(e.data.data, "https://example.org", "Correct origin expected"); + } else if (e.data.status == "done") { + win.close(); + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/unregister.html"; + } else if (e.data.status == "unregistrationdone") { + window.onmessage = null; + ok(true, "Test finished successfully"); + SimpleTest.finish(); + } + }; + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.caches.enabled", true], + ]}, runTest); + }; +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https_cached.html b/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https_cached.html new file mode 100644 index 000000000..3922fdb90 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https_cached.html @@ -0,0 +1,58 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test the origin of a redirected response from a service worker</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +<iframe></iframe> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + var iframe; + function runTest() { + iframe = document.querySelector("iframe"); + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/register.html"; + var win; + window.onmessage = function(e) { + if (e.data.status == "ok") { + ok(e.data.result, e.data.message); + } else if (e.data.status == "registrationdone") { + win = window.open("/tests/dom/workers/test/serviceworkers/fetch/origin/index-to-https-cached.sjs", "mywindow", "width=100,height=100"); + } else if (e.data.status == "domain") { + is(e.data.data, "example.org", "Correct domain expected"); + } else if (e.data.status == "origin") { + is(e.data.data, "https://example.org", "Correct origin expected"); + } else if (e.data.status == "done") { + win.close(); + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/unregister.html"; + } else if (e.data.status == "unregistrationdone") { + window.onmessage = null; + ok(true, "Test finished successfully"); + SimpleTest.finish(); + } + }; + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.openWindow.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.caches.enabled", true], + ]}, runTest); + }; +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_post_message.html b/dom/workers/test/serviceworkers/test_post_message.html new file mode 100644 index 000000000..e366423cb --- /dev/null +++ b/dom/workers/test/serviceworkers/test_post_message.html @@ -0,0 +1,80 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 982726 - Test service worker post message </title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + var magic_value = "MAGIC_VALUE_123"; + var registration; + function start() { + return navigator.serviceWorker.register("message_posting_worker.js", + { scope: "./sw_clients/" }) + .then((swr) => registration = swr); + } + + function unregister() { + return registration.unregister().then(function(result) { + ok(result, "Unregister should return true."); + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + } + + + function testPostMessage(swr) { + var p = new Promise(function(res, rej) { + window.onmessage = function(e) { + if (e.data === "READY") { + swr.active.postMessage(magic_value); + } else if (e.data === magic_value) { + ok(true, "Worker posted the correct value."); + res(); + } else { + ok(false, "Wrong value. Expected: " + magic_value + + ", got: " + e.data); + res(); + } + } + }); + + var content = document.getElementById("content"); + ok(content, "Parent exists."); + + iframe = document.createElement("iframe"); + iframe.setAttribute('src', "sw_clients/service_worker_controlled.html"); + content.appendChild(iframe); + + return p.then(() => content.removeChild(iframe)); + } + + function runTest() { + start() + .then(testPostMessage) + .then(unregister) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + }).then(SimpleTest.finish); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.openWindow.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_post_message_advanced.html b/dom/workers/test/serviceworkers/test_post_message_advanced.html new file mode 100644 index 000000000..8ea0d2300 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_post_message_advanced.html @@ -0,0 +1,109 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 982726 - Test service worker post message advanced </title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + var registration; + var base = ["string", true, 42]; + var blob = new Blob(["blob_content"]); + var file = new File(["file_content"], "file"); + var obj = { body : "object_content" }; + + function readBlob(blob) { + return new Promise(function(resolve, reject) { + var reader = new FileReader(); + reader.onloadend = () => resolve(reader.result); + reader.readAsText(blob); + }); + } + + function equals(v1, v2) { + return Promise.all([v1, v2]).then(function(val) { + ok(val[0] === val[1], "Values should match."); + }); + } + + function blob_equals(b1, b2) { + return equals(readBlob(b1), readBlob(b2)); + } + + function file_equals(f1, f2) { + return equals(f1.name, f2.name).then(blob_equals(f1, f2)); + } + + function obj_equals(o1, o2) { + return equals(o1.body, o2.body); + } + + function start() { + return navigator.serviceWorker.register("message_posting_worker.js", + { scope: "./sw_clients/" }) + .then((swr) => registration = swr); + } + + function unregister() { + return registration.unregister().then(function(result) { + ok(result, "Unregister should return true."); + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + } + + function testPostMessageObject(obj, test) { + var p = new Promise(function(res, rej) { + window.onmessage = function(e) { + if (e.data === "READY") { + registration.active.postMessage(obj) + } else { + test(obj, e.data).then(res); + } + } + }); + + var content = document.getElementById("content"); + ok(content, "Parent exists."); + + iframe = document.createElement("iframe"); + iframe.setAttribute('src', "sw_clients/service_worker_controlled.html"); + content.appendChild(iframe); + + return p.then(() => content.removeChild(iframe)); + } + + function runTest() { + start() + .then(testPostMessageObject.bind(this, base[0], equals)) + .then(testPostMessageObject.bind(this, base[1], equals)) + .then(testPostMessageObject.bind(this, base[2], equals)) + .then(testPostMessageObject.bind(this, blob, blob_equals)) + .then(testPostMessageObject.bind(this, file, file_equals)) + .then(testPostMessageObject.bind(this, obj, obj_equals)) + .then(unregister) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + }).then(SimpleTest.finish); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.openWindow.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_post_message_source.html b/dom/workers/test/serviceworkers/test_post_message_source.html new file mode 100644 index 000000000..543f64b4a --- /dev/null +++ b/dom/workers/test/serviceworkers/test_post_message_source.html @@ -0,0 +1,68 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1142015 - Test service worker post message source </title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + var magic_value = "MAGIC_VALUE_RANDOM"; + var registration; + function start() { + return navigator.serviceWorker.register("source_message_posting_worker.js", + { scope: "./nonexistent_scope/" }) + .then((swr) => registration = swr); + } + + function unregister() { + return registration.unregister().then(function(result) { + ok(result, "Unregister should return true."); + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + } + + + function testPostMessage(swr) { + var p = new Promise(function(res, rej) { + navigator.serviceWorker.onmessage = function(e) { + ok(e.data === magic_value, "Worker posted the correct value."); + res(); + } + }); + + ok(swr.installing, "Installing worker exists."); + swr.installing.postMessage(magic_value); + return p; + } + + + function runTest() { + start() + .then(testPostMessage) + .then(unregister) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + }).then(SimpleTest.finish); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.openWindow.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_privateBrowsing.html b/dom/workers/test/serviceworkers/test_privateBrowsing.html new file mode 100644 index 000000000..976337711 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_privateBrowsing.html @@ -0,0 +1,113 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <title>Test for ServiceWorker - Private Browsing</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +</head> +<body> + +<script type="application/javascript"> + +const Ci = Components.interfaces; +var mainWindow; + +var contentPage = "http://mochi.test:8888/chrome/dom/workers/test/empty.html"; +var workerScope = "http://mochi.test:8888/chrome/dom/workers/test/serviceworkers/"; +var workerURL = workerScope + "worker.js"; + +function testOnWindow(aIsPrivate, aCallback) { + var win = mainWindow.OpenBrowserWindow({private: aIsPrivate}); + win.addEventListener("load", function onLoad() { + win.removeEventListener("load", onLoad, false); + win.addEventListener("DOMContentLoaded", function onInnerLoad() { + if (win.content.location.href != contentPage) { + win.gBrowser.loadURI(contentPage); + return; + } + + win.removeEventListener("DOMContentLoaded", onInnerLoad, true); + SimpleTest.executeSoon(function() { aCallback(win); }); + }, true); + + if (!aIsPrivate) { + win.gBrowser.loadURI(contentPage); + } + }, true); +} + +function setupWindow() { + mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + runTest(); +} + +var wN; +var registration; +var wP; + +function testPrivateWindow() { + testOnWindow(true, function(aWin) { + wP = aWin; + ok(!("serviceWorker" in wP.content.navigator), "ServiceWorkers are not available for private windows"); + runTest(); + }); +} + +function doTests() { + testOnWindow(false, function(aWin) { + wN = aWin; + ok("serviceWorker" in wN.content.navigator, "ServiceWorkers are available for normal windows"); + + wN.content.navigator.serviceWorker.register(workerURL, + { scope: workerScope }) + .then(function(aRegistration) { + registration = aRegistration; + ok(registration, "Registering a service worker in a normal window should succeed"); + + // Bug 1255621: We should be able to load a controlled document in a private window. + testPrivateWindow(); + }, function(aError) { + ok(false, "Error registering worker in normal window: " + aError); + testPrivateWindow(); + }); + }); +} + +var steps = [ + setupWindow, + doTests +]; + +function cleanup() { + wN.close(); + wP.close(); + + SimpleTest.finish(); +} + +function runTest() { + if (!steps.length) { + registration.unregister().then(cleanup, cleanup); + + return; + } + + var step = steps.shift(); + step(); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["browser.startup.page", 0], + ["browser.startup.homepage_override.mstone", "ignore"], +]}, runTest); + +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_register_base.html b/dom/workers/test/serviceworkers/test_register_base.html new file mode 100644 index 000000000..58b08d27a --- /dev/null +++ b/dom/workers/test/serviceworkers/test_register_base.html @@ -0,0 +1,41 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test that registering a service worker uses the docuemnt URI for the secure origin check</title> + <script type="text/javascript" src="http://mochi.test:8888/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="http://mochi.test:8888/tests/SimpleTest/test.css" /> + <base href="https://mozilla.org/"> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + function runTest() { + navigator.serviceWorker.register("http://mochi.test:8888/tests/dom/workers/test/serviceworkers/empty.js") + .then(reg => { + ok(false, "Register should fail"); + SimpleTest.finish(); + }, err => { + is(err.name, "SecurityError", "Registration should fail with SecurityError"); + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.openWindow.enabled", true], + ]}, runTest); + }; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_register_https_in_http.html b/dom/workers/test/serviceworkers/test_register_https_in_http.html new file mode 100644 index 000000000..2c8e998f7 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_register_https_in_http.html @@ -0,0 +1,46 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1172948 - Test that registering a service worker from inside an HTTPS iframe embedded in an HTTP iframe doesn't work</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + function runTest() { + var iframe = document.createElement("iframe"); + iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/register_https.html"; + document.body.appendChild(iframe); + + window.onmessage = event => { + switch (event.data.type) { + case "ok": + ok(event.data.status, event.data.msg); + break; + case "done": + SimpleTest.finish(); + break; + } + }; + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.openWindow.enabled", true], + ]}, runTest); + }; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context.js b/dom/workers/test/serviceworkers/test_request_context.js new file mode 100644 index 000000000..aebba79a7 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context.js @@ -0,0 +1,75 @@ +// Copied from /dom/plugins/test/mochitest/utils.js +function getTestPlugin(pluginName) { + var ph = SpecialPowers.Cc["@mozilla.org/plugin/host;1"] + .getService(SpecialPowers.Ci.nsIPluginHost); + var tags = ph.getPluginTags(); + var name = pluginName || "Test Plug-in"; + for (var tag of tags) { + if (tag.name == name) { + return tag; + } + } + + ok(false, "Could not find plugin tag with plugin name '" + name + "'"); + return null; +} +function setTestPluginEnabledState(newEnabledState, pluginName) { + var oldEnabledState = SpecialPowers.setTestPluginEnabledState(newEnabledState, pluginName); + if (!oldEnabledState) { + return; + } + var plugin = getTestPlugin(pluginName); + while (plugin.enabledState != newEnabledState) { + // Run a nested event loop to wait for the preference change to + // propagate to the child. Yuck! + SpecialPowers.Services.tm.currentThread.processNextEvent(true); + } + SimpleTest.registerCleanupFunction(function() { + SpecialPowers.setTestPluginEnabledState(oldEnabledState, pluginName); + }); +} +setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED); + +function isMulet() { + try { + return SpecialPowers.getBoolPref("b2g.is_mulet"); + } catch(e) { + return false; + } +} + +var iframe; +function runTest() { + iframe = document.querySelector("iframe"); + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/context/register.html"; + window.onmessage = function(e) { + if (e.data.status == "ok") { + ok(e.data.result, e.data.message); + } else if (e.data.status == "todo") { + todo(e.data.result, e.data.message); + } else if (e.data.status == "registrationdone") { + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/context/index.html?" + gTest; + } else if (e.data.status == "done") { + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/context/unregister.html"; + } else if (e.data.status == "unregistrationdone") { + window.onmessage = null; + ok(true, "Test finished successfully"); + SimpleTest.finish(); + } + }; +} + +SimpleTest.waitForExplicitFinish(); +onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["beacon.enabled", true], + ["browser.send_pings", true], + ["browser.send_pings.max_per_link", -1], + ["dom.caches.enabled", true], + ["dom.requestcontext.enabled", true], + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.openWindow.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}, runTest); +}; diff --git a/dom/workers/test/serviceworkers/test_request_context_audio.html b/dom/workers/test/serviceworkers/test_request_context_audio.html new file mode 100644 index 000000000..929a24428 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_audio.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testAudio"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_beacon.html b/dom/workers/test/serviceworkers/test_request_context_beacon.html new file mode 100644 index 000000000..ce44214d6 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_beacon.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testBeacon"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_cache.html b/dom/workers/test/serviceworkers/test_request_context_cache.html new file mode 100644 index 000000000..3d62baabc --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_cache.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testCache"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_cspreport.html b/dom/workers/test/serviceworkers/test_request_context_cspreport.html new file mode 100644 index 000000000..a27e79303 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_cspreport.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testCSPReport"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_embed.html b/dom/workers/test/serviceworkers/test_request_context_embed.html new file mode 100644 index 000000000..f8d374246 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_embed.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testEmbed"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_fetch.html b/dom/workers/test/serviceworkers/test_request_context_fetch.html new file mode 100644 index 000000000..94de8be31 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_fetch.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testFetch"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_font.html b/dom/workers/test/serviceworkers/test_request_context_font.html new file mode 100644 index 000000000..d81a0686b --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_font.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testFont"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_frame.html b/dom/workers/test/serviceworkers/test_request_context_frame.html new file mode 100644 index 000000000..d5dc1f745 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_frame.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testFrame"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_iframe.html b/dom/workers/test/serviceworkers/test_request_context_iframe.html new file mode 100644 index 000000000..d3b0675e0 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_iframe.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testIFrame"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_image.html b/dom/workers/test/serviceworkers/test_request_context_image.html new file mode 100644 index 000000000..810063da4 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_image.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testImage"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_imagesrcset.html b/dom/workers/test/serviceworkers/test_request_context_imagesrcset.html new file mode 100644 index 000000000..95b2b7214 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_imagesrcset.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testImageSrcSet"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_internal.html b/dom/workers/test/serviceworkers/test_request_context_internal.html new file mode 100644 index 000000000..45f454495 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_internal.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testInternal"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_nestedworker.html b/dom/workers/test/serviceworkers/test_request_context_nestedworker.html new file mode 100644 index 000000000..226de691b --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_nestedworker.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testNestedWorker"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_nestedworkerinsharedworker.html b/dom/workers/test/serviceworkers/test_request_context_nestedworkerinsharedworker.html new file mode 100644 index 000000000..48934a57c --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_nestedworkerinsharedworker.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testNestedWorkerInSharedWorker"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_object.html b/dom/workers/test/serviceworkers/test_request_context_object.html new file mode 100644 index 000000000..189c5adbb --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_object.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testObject"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_picture.html b/dom/workers/test/serviceworkers/test_request_context_picture.html new file mode 100644 index 000000000..1b49e7eb9 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_picture.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testPicture"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_ping.html b/dom/workers/test/serviceworkers/test_request_context_ping.html new file mode 100644 index 000000000..460e9efb4 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_ping.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testPing"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_plugin.html b/dom/workers/test/serviceworkers/test_request_context_plugin.html new file mode 100644 index 000000000..862e9d4a5 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_plugin.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testPlugin"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_script.html b/dom/workers/test/serviceworkers/test_request_context_script.html new file mode 100644 index 000000000..ec560ef72 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_script.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testScript"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_sharedworker.html b/dom/workers/test/serviceworkers/test_request_context_sharedworker.html new file mode 100644 index 000000000..93ccdf3ae --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_sharedworker.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testSharedWorker"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_style.html b/dom/workers/test/serviceworkers/test_request_context_style.html new file mode 100644 index 000000000..b557d5c04 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_style.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testStyle"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_track.html b/dom/workers/test/serviceworkers/test_request_context_track.html new file mode 100644 index 000000000..1b161c4d0 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_track.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testTrack"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_video.html b/dom/workers/test/serviceworkers/test_request_context_video.html new file mode 100644 index 000000000..1886e31d7 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_video.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testVideo"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_worker.html b/dom/workers/test/serviceworkers/test_request_context_worker.html new file mode 100644 index 000000000..9de127304 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_worker.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testWorker"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_xhr.html b/dom/workers/test/serviceworkers/test_request_context_xhr.html new file mode 100644 index 000000000..ed0d60bf8 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_xhr.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testXHR"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_request_context_xslt.html b/dom/workers/test/serviceworkers/test_request_context_xslt.html new file mode 100644 index 000000000..a6d837b69 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_request_context_xslt.html @@ -0,0 +1,19 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_request_context.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe></iframe> +<script type="text/javascript"> +var gTest = "testXSLT"; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_sandbox_intercept.html b/dom/workers/test/serviceworkers/test_sandbox_intercept.html new file mode 100644 index 000000000..273df53ff --- /dev/null +++ b/dom/workers/test/serviceworkers/test_sandbox_intercept.html @@ -0,0 +1,50 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1142727 - Test that sandboxed iframes are not intercepted</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content"> +<iframe sandbox="allow-scripts allow-same-origin"></iframe> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + var iframe; + function runTest() { + iframe = document.querySelector("iframe"); + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/sandbox/register.html"; + window.onmessage = function(e) { + if (e.data.status == "ok") { + ok(e.data.result, e.data.message); + } else if (e.data.status == "registrationdone") { + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/sandbox/index.html"; + } else if (e.data.status == "done") { + iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/sandbox/unregister.html"; + } else if (e.data.status == "unregistrationdone") { + window.onmessage = null; + ok(true, "Test finished successfully"); + SimpleTest.finish(); + } + }; + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}, runTest); + }; +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_sanitize.html b/dom/workers/test/serviceworkers/test_sanitize.html new file mode 100644 index 000000000..842cb38c3 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_sanitize.html @@ -0,0 +1,87 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1080109 - Clear ServiceWorker registrations for all domains</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + function start() { + const Cc = SpecialPowers.Cc; + const Ci = SpecialPowers.Ci; + + function testNotIntercepted() { + testFrame("sanitize/frame.html").then(function(body) { + is(body, "FAIL", "Expected frame to not be controlled"); + // No need to unregister since that already happened. + navigator.serviceWorker.getRegistration("sanitize/foo").then(function(reg) { + ok(reg === undefined, "There should no longer be a valid registration"); + }, function(e) { + ok(false, "getRegistration() should not error"); + }).then(function(e) { + SimpleTest.finish(); + }); + }); + } + + registerSW().then(function() { + return testFrame("sanitize/frame.html").then(function(body) { + is(body, "intercepted", "Expected serviceworker to intercept request"); + }); + }).then(function() { + return navigator.serviceWorker.getRegistration("sanitize/foo"); + }).then(function(reg) { + reg.active.onstatechange = function(e) { + e.target.onstatechange = null; + ok(e.target.state, "redundant", "On clearing data, serviceworker should become redundant"); + testNotIntercepted(); + }; + }).then(function() { + SpecialPowers.removeAllServiceWorkerData(); + }); + } + + function testFrame(src) { + return new Promise(function(resolve, reject) { + var iframe = document.createElement("iframe"); + iframe.src = src; + window.onmessage = function(message) { + window.onmessage = null; + iframe.src = "about:blank"; + document.body.removeChild(iframe); + iframe = null; + SpecialPowers.exactGC(function() { + resolve(message.data); + }); + }; + document.body.appendChild(iframe); + }); + } + + function registerSW() { + return testFrame("sanitize/register.html"); + } + + SimpleTest.waitForExplicitFinish(); + + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}, function() { + start(); + }); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_sanitize_domain.html b/dom/workers/test/serviceworkers/test_sanitize_domain.html new file mode 100644 index 000000000..054b60f37 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_sanitize_domain.html @@ -0,0 +1,90 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1080109 - Clear ServiceWorker registrations for specific domains</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + function start() { + const Cc = SpecialPowers.Cc; + const Ci = SpecialPowers.Ci; + + function checkDomainRegistration(domain, exists) { + return testFrame("http://" + domain + "/tests/dom/workers/test/serviceworkers/sanitize/example_check_and_unregister.html").then(function(body) { + if (body === "FAIL") { + ok(false, "Error acquiring registration or unregistering for " + domain); + } else { + if (exists) { + ok(body === true, "Expected " + domain + " to still have a registration."); + } else { + ok(body === false, "Expected " + domain + " to have no registration."); + } + } + }); + } + + registerSW().then(function() { + return testFrame("http://example.com/tests/dom/workers/test/serviceworkers/sanitize/frame.html").then(function(body) { + is(body, "intercepted", "Expected serviceworker to intercept request"); + }); + }).then(function() { + SpecialPowers.removeServiceWorkerDataForExampleDomain(); + }).then(function() { + return checkDomainRegistration("prefixexample.com", true /* exists */) + .then(function(e) { + return checkDomainRegistration("example.com", false /* exists */); + }).then(function(e) { + SimpleTest.finish(); + }); + }) + } + + function testFrame(src) { + return new Promise(function(resolve, reject) { + var iframe = document.createElement("iframe"); + iframe.src = src; + window.onmessage = function(message) { + window.onmessage = null; + iframe.src = "about:blank"; + document.body.removeChild(iframe); + iframe = null; + SpecialPowers.exactGC(function() { + resolve(message.data); + }); + }; + document.body.appendChild(iframe); + }); + } + + function registerSW() { + return testFrame("http://example.com/tests/dom/workers/test/serviceworkers/sanitize/register.html") + .then(function(e) { + // Register for prefixexample.com and then ensure it does not get unregistered. + return testFrame("http://prefixexample.com/tests/dom/workers/test/serviceworkers/sanitize/register.html"); + }); + } + + SimpleTest.waitForExplicitFinish(); + + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}, function() { + start(); + }); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_scopes.html b/dom/workers/test/serviceworkers/test_scopes.html new file mode 100644 index 000000000..2d8116f83 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_scopes.html @@ -0,0 +1,121 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 984048 - Test scope glob matching.</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + var scriptsAndScopes = [ + [ "worker.js", "./sub/dir/"], + [ "worker.js", "./sub/dir" ], + [ "worker.js", "./sub/dir.html" ], + [ "worker.js", "./sub/dir/a" ], + [ "worker.js", "./sub" ], + [ "worker.js", "./star*" ], // '*' has no special meaning + ]; + + function registerWorkers() { + var registerArray = []; + scriptsAndScopes.forEach(function(item) { + registerArray.push(navigator.serviceWorker.register(item[0], { scope: item[1] })); + }); + + // Check register()'s step 4 which uses script's url with "./" as the scope if no scope is passed. + // The other tests already check step 5. + registerArray.push(navigator.serviceWorker.register("scope/scope_worker.js")); + + // Check that SW cannot be registered for a scope "above" the script's location. + registerArray.push(new Promise(function(resolve, reject) { + navigator.serviceWorker.register("scope/scope_worker.js", { scope: "./" }) + .then(function() { + ok(false, "registration scope has to be inside service worker script scope."); + reject(); + }, function() { + ok(true, "registration scope has to be inside service worker script scope."); + resolve(); + }); + })); + return Promise.all(registerArray); + } + + function unregisterWorkers() { + var unregisterArray = []; + scriptsAndScopes.forEach(function(item) { + var p = navigator.serviceWorker.getRegistration(item[1]); + unregisterArray.push(p.then(function(reg) { + return reg.unregister(); + })); + }); + + unregisterArray.push(navigator.serviceWorker.getRegistration("scope/").then(function (reg) { + return reg.unregister(); + })); + + return Promise.all(unregisterArray); + } + + function testScopes() { + return new Promise(function(resolve, reject) { + var getScope = navigator.serviceWorker.getScopeForUrl.bind(navigator.serviceWorker); + var base = new URL(".", document.baseURI); + + function p(s) { + return base + s; + } + + function fail(fn) { + try { + getScope(p("index.html")); + ok(false, "No registration"); + } catch(e) { + ok(true, "No registration"); + } + } + + ok(getScope(p("sub.html")) === p("sub"), "Scope should match"); + ok(getScope(p("sub/dir.html")) === p("sub/dir.html"), "Scope should match"); + ok(getScope(p("sub/dir")) === p("sub/dir"), "Scope should match"); + ok(getScope(p("sub/dir/foo")) === p("sub/dir/"), "Scope should match"); + ok(getScope(p("sub/dir/afoo")) === p("sub/dir/a"), "Scope should match"); + ok(getScope(p("star*wars")) === p("star*"), "Scope should match"); + ok(getScope(p("scope/some_file.html")) === p("scope/"), "Scope should match"); + fail("index.html"); + fail("sua.html"); + fail("star/a.html"); + resolve(); + }); + } + + function runTest() { + registerWorkers() + .then(testScopes) + .then(unregisterWorkers) + .then(function() { + SimpleTest.finish(); + }).catch(function(e) { + ok(false, "Some test failed with error " + e); + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_service_worker_allowed.html b/dom/workers/test/serviceworkers/test_service_worker_allowed.html new file mode 100644 index 000000000..eca94ebb4 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_service_worker_allowed.html @@ -0,0 +1,74 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test the Service-Worker-Allowed header</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<div id="content"></div> +<script class="testbody" type="text/javascript"> + var gTests = [ + "worker_scope_different.js", + "worker_scope_different2.js", + "worker_scope_too_deep.js", + ]; + + function testPermissiveHeader() { + // Make sure that this registration succeeds, as the prefix check should pass. + return navigator.serviceWorker.register("swa/worker_scope_too_narrow.js", {scope: "swa/"}) + .then(swr => { + ok(true, "Registration should finish successfully"); + return swr.unregister(); + }, err => { + ok(false, "Unexpected error when registering the service worker: " + err); + }); + } + + function testPreciseHeader() { + // Make sure that this registration succeeds, as the prefix check should pass + // given that we parse the use the full pathname from this URL.. + return navigator.serviceWorker.register("swa/worker_scope_precise.js", {scope: "swa/"}) + .then(swr => { + ok(true, "Registration should finish successfully"); + return swr.unregister(); + }, err => { + ok(false, "Unexpected error when registering the service worker: " + err); + }); + } + + function runTest() { + Promise.all(gTests.map(testName => { + return new Promise((resolve, reject) => { + // Make sure that registration fails. + navigator.serviceWorker.register("swa/" + testName, {scope: "swa/"}) + .then(reject, resolve); + }); + })).then(values => { + values.forEach(error => { + is(error.name, "SecurityError", "Registration should fail"); + }); + Promise.all([ + testPermissiveHeader(), + testPreciseHeader(), + ]).then(SimpleTest.finish, SimpleTest.finish); + }, (x) => { + ok(false, "Registration should not succeed, but it did"); + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ]}, runTest); +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_serviceworker_header.html b/dom/workers/test/serviceworkers/test_serviceworker_header.html new file mode 100644 index 000000000..ac5a6e49f --- /dev/null +++ b/dom/workers/test/serviceworkers/test_serviceworker_header.html @@ -0,0 +1,41 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test that service worker scripts are fetched with a Service-Worker: script header</title> + <script type="text/javascript" src="http://mochi.test:8888/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="http://mochi.test:8888/tests/SimpleTest/test.css" /> + <base href="https://mozilla.org/"> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + function runTest() { + navigator.serviceWorker.register("http://mochi.test:8888/tests/dom/workers/test/serviceworkers/header_checker.sjs") + .then(reg => { + ok(true, "Register should succeed"); + reg.unregister().then(() => SimpleTest.finish()); + }, err => { + ok(false, "Register should not fail"); + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.serviceWorkers.enabled", true], + ]}, runTest); + }; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.html b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.html new file mode 100644 index 000000000..0130ca2d9 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.html @@ -0,0 +1,106 @@ +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<!DOCTYPE HTML> +<html> +<head> + <title>Validate Interfaces Exposed to Service Workers</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <script type="text/javascript" src="../worker_driver.js"></script> +</head> +<body> +<script class="testbody" type="text/javascript"> + + function setupSW(registration) { + var worker = registration.waiting || + registration.active; + window.onmessage = function(event) { + if (event.data.type == 'finish') { + registration.unregister().then(function(success) { + ok(success, "The service worker should be unregistered successfully"); + + SimpleTest.finish(); + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + SimpleTest.finish(); + }); + } else if (event.data.type == 'status') { + ok(event.data.status, event.data.msg); + + } else if (event.data.type == 'getPrefs') { + var result = {}; + event.data.prefs.forEach(function(pref) { + result[pref] = SpecialPowers.Services.prefs.getBoolPref(pref); + }); + worker.postMessage({ + type: 'returnPrefs', + prefs: event.data.prefs, + result: result + }); + + } else if (event.data.type == 'getVersion') { + var result = SpecialPowers.Cc['@mozilla.org/xre/app-info;1'].getService(SpecialPowers.Ci.nsIXULAppInfo).version; + worker.postMessage({ + type: 'returnVersion', + result: result + }); + + } else if (event.data.type == 'getUserAgent') { + worker.postMessage({ + type: 'returnUserAgent', + result: navigator.userAgent + }); + } else if (event.data.type == 'getOSCPU') { + worker.postMessage({ + type: 'returnOSCPU', + result: navigator.oscpu + }); + } + } + + worker.onerror = function(event) { + ok(false, 'Worker had an error: ' + event.data); + SimpleTest.finish(); + }; + + var iframe = document.createElement("iframe"); + iframe.src = "message_receiver.html"; + iframe.onload = function() { + worker.postMessage({ script: "test_serviceworker_interfaces.js" }); + }; + document.body.appendChild(iframe); + } + + function runTest() { + navigator.serviceWorker.ready.then(setupSW); + navigator.serviceWorker.register("serviceworker_wrapper.js", {scope: "."}); + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + // The handling of "dom.caches.enabled" here is a bit complicated. What we + // want to happen is that Cache is always enabled in service workers. So + // if service workers are disabled by default we want to force on both + // service workers and "dom.caches.enabled". But if service workers are + // enabled by default, we do not want to mess with the "dom.caches.enabled" + // value, since that would defeat the purpose of the test. Use a subframe + // to decide whether service workers are enabled by default, so we don't + // force creation of our own Navigator object before our prefs are set. + var prefs = [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]; + + var subframe = document.createElement("iframe"); + document.body.appendChild(subframe); + if (!("serviceWorker" in subframe.contentWindow.navigator)) { + prefs.push(["dom.caches.enabled", true]); + } + subframe.remove(); + + SpecialPowers.pushPrefEnv({"set": prefs}, runTest); + }; +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js new file mode 100644 index 000000000..9dbfcc099 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js @@ -0,0 +1,278 @@ +// This is a list of all interfaces that are exposed to workers. +// Please only add things to this list with great care and proper review +// from the associated module peers. + +// This file lists global interfaces we want exposed and verifies they +// are what we intend. Each entry in the arrays below can either be a +// simple string with the interface name, or an object with a 'name' +// property giving the interface name as a string, and additional +// properties which qualify the exposure of that interface. For example: +// +// [ +// "AGlobalInterface", +// { name: "ExperimentalThing", release: false }, +// { name: "ReallyExperimentalThing", nightly: true }, +// { name: "DesktopOnlyThing", desktop: true }, +// { name: "FancyControl", xbl: true }, +// { name: "DisabledEverywhere", disabled: true }, +// ]; +// +// See createInterfaceMap() below for a complete list of properties. + +// IMPORTANT: Do not change this list without review from +// a JavaScript Engine peer! +var ecmaGlobals = + [ + "Array", + "ArrayBuffer", + "Boolean", + "DataView", + "Date", + "Error", + "EvalError", + "Float32Array", + "Float64Array", + "Function", + "Infinity", + "Int16Array", + "Int32Array", + "Int8Array", + "InternalError", + {name: "Intl", android: false}, + "Iterator", + "JSON", + "Map", + "Math", + "NaN", + "Number", + "Object", + "Promise", + "Proxy", + "RangeError", + "ReferenceError", + "Reflect", + "RegExp", + "Set", + {name: "SharedArrayBuffer", release: false}, + {name: "SIMD", nightly: true}, + {name: "Atomics", release: false}, + "StopIteration", + "String", + "Symbol", + "SyntaxError", + {name: "TypedObject", nightly: true}, + "TypeError", + "Uint16Array", + "Uint32Array", + "Uint8Array", + "Uint8ClampedArray", + "URIError", + "WeakMap", + "WeakSet", + ]; +// IMPORTANT: Do not change the list above without review from +// a JavaScript Engine peer! + +// IMPORTANT: Do not change the list below without review from a DOM peer! +var interfaceNamesInGlobalScope = + [ +// IMPORTANT: Do not change this list without review from a DOM peer! + "Blob", +// IMPORTANT: Do not change this list without review from a DOM peer! + "BroadcastChannel", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Cache", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CacheStorage", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Client", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Clients", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Crypto", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CustomEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Directory", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMCursor", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMError", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMException", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMRequest", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMStringList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Event", +// IMPORTANT: Do not change this list without review from a DOM peer! + "EventTarget", +// IMPORTANT: Do not change this list without review from a DOM peer! + "ExtendableEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "ExtendableMessageEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "FetchEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "File", +// IMPORTANT: Do not change this list without review from a DOM peer! + "FileReader", +// IMPORTANT: Do not change this list without review from a DOM peer! + "FileReaderSync", +// IMPORTANT: Do not change this list without review from a DOM peer! + "FormData", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Headers", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBCursor", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBCursorWithValue", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBDatabase", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBFactory", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBIndex", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBKeyRange", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBObjectStore", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBOpenDBRequest", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBRequest", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBTransaction", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBVersionChangeEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "ImageBitmap", +// IMPORTANT: Do not change this list without review from a DOM peer! + "ImageBitmapRenderingContext", +// IMPORTANT: Do not change this list without review from a DOM peer! + "ImageData", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MessageChannel", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MessageEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MessagePort", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Notification", +// IMPORTANT: Do not change this list without review from a DOM peer! + "NotificationEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! +// IMPORTANT: Do not change this list without review from a DOM peer! + "Performance", +// IMPORTANT: Do not change this list without review from a DOM peer! + "PerformanceEntry", +// IMPORTANT: Do not change this list without review from a DOM peer! + "PerformanceMark", +// IMPORTANT: Do not change this list without review from a DOM peer! + "PerformanceMeasure", +// IMPORTANT: Do not change this list without review from a DOM peer! + { name: "PerformanceObserver", nightly: true }, +// IMPORTANT: Do not change this list without review from a DOM peer! + { name: "PerformanceObserverEntryList", nightly: true }, +// IMPORTANT: Do not change this list without review from a DOM peer! + "Request", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Response", +// IMPORTANT: Do not change this list without review from a DOM peer! + "ServiceWorker", +// IMPORTANT: Do not change this list without review from a DOM peer! + "ServiceWorkerGlobalScope", +// IMPORTANT: Do not change this list without review from a DOM peer! + "ServiceWorkerRegistration", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "StorageManager", nightly: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "SubtleCrypto", +// IMPORTANT: Do not change this list without review from a DOM peer! + "TextDecoder", +// IMPORTANT: Do not change this list without review from a DOM peer! + "TextEncoder", +// IMPORTANT: Do not change this list without review from a DOM peer! + "URL", +// IMPORTANT: Do not change this list without review from a DOM peer! + "URLSearchParams", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WebSocket", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WindowClient", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WorkerGlobalScope", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WorkerLocation", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WorkerNavigator", +// IMPORTANT: Do not change this list without review from a DOM peer! + ]; +// IMPORTANT: Do not change the list above without review from a DOM peer! + +function createInterfaceMap(version, userAgent) { + var isNightly = version.endsWith("a1"); + var isRelease = !version.includes("a"); + var isDesktop = !/Mobile|Tablet/.test(userAgent); + var isAndroid = !!navigator.userAgent.includes("Android"); + + var interfaceMap = {}; + + function addInterfaces(interfaces) + { + for (var entry of interfaces) { + if (typeof(entry) === "string") { + interfaceMap[entry] = true; + } else { + ok(!("pref" in entry), "Bogus pref annotation for " + entry.name); + if ((entry.nightly === !isNightly) || + (entry.nightlyAndroid === !(isAndroid && isNightly) && isAndroid) || + (entry.nonReleaseAndroid === !(isAndroid && !isRelease) && isAndroid) || + (entry.desktop === !isDesktop) || + (entry.android === !isAndroid && !entry.nonReleaseAndroid && !entry.nightlyAndroid) || + (entry.release === !isRelease)) { + interfaceMap[entry.name] = false; + } else { + interfaceMap[entry.name] = true; + } + } + } + } + + addInterfaces(ecmaGlobals); + addInterfaces(interfaceNamesInGlobalScope); + + return interfaceMap; +} + +function runTest(version, userAgent) { + var interfaceMap = createInterfaceMap(version, userAgent); + for (var name of Object.getOwnPropertyNames(self)) { + // An interface name should start with an upper case character. + if (!/^[A-Z]/.test(name)) { + continue; + } + ok(interfaceMap[name], + "If this is failing: DANGER, are you sure you want to expose the new interface " + name + + " to all webpages as a property on the service worker? Do not make a change to this file without a " + + " review from a DOM peer for that specific change!!! (or a JS peer for changes to ecmaGlobals)"); + delete interfaceMap[name]; + } + for (var name of Object.keys(interfaceMap)) { + ok(name in self === interfaceMap[name], + name + " should " + (interfaceMap[name] ? "" : " NOT") + " be defined on the global scope"); + if (!interfaceMap[name]) { + delete interfaceMap[name]; + } + } + is(Object.keys(interfaceMap).length, 0, + "The following interface(s) are not enumerated: " + Object.keys(interfaceMap).join(", ")); +} + +workerTestGetVersion(function(version) { + workerTestGetUserAgent(function(userAgent) { + runTest(version, userAgent); + workerTestDone(); + }); +}); diff --git a/dom/workers/test/serviceworkers/test_serviceworker_not_sharedworker.html b/dom/workers/test/serviceworkers/test_serviceworker_not_sharedworker.html new file mode 100644 index 000000000..96dd9f159 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_serviceworker_not_sharedworker.html @@ -0,0 +1,66 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1141274 - test that service workers and shared workers are separate</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +<iframe></iframe> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + var iframe; + const SCOPE = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/"; + function runTest() { + navigator.serviceWorker.ready.then(setupSW); + navigator.serviceWorker.register("serviceworker_not_sharedworker.js", + {scope: SCOPE}); + } + + var sw, worker; + function setupSW(registration) { + sw = registration.waiting || registration.active; + worker = new SharedWorker("serviceworker_not_sharedworker.js", SCOPE); + worker.port.start(); + iframe = document.querySelector("iframe"); + iframe.src = "message_receiver.html"; + iframe.onload = function() { + window.onmessage = function(e) { + is(e.data.result, "serviceworker", "We should be talking to a service worker"); + window.onmessage = null; + worker.port.onmessage = function(e) { + is(e.data.result, "sharedworker", "We should be talking to a shared worker"); + registration.unregister().then(function(success) { + ok(success, "unregister should succeed"); + SimpleTest.finish(); + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + SimpleTest.finish(); + }); + }; + worker.port.postMessage({msg: "whoareyou"}); + }; + sw.postMessage({msg: "whoareyou"}); + }; + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); + }; +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_serviceworkerinfo.xul b/dom/workers/test/serviceworkers/test_serviceworkerinfo.xul new file mode 100644 index 000000000..96e4bb1c3 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_serviceworkerinfo.xul @@ -0,0 +1,115 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Test for ServiceWorkerInfo" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="test();"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="application/javascript" src="chrome_helpers.js"/> + <script type="application/javascript"> + <![CDATA[ + + let IFRAME_URL = EXAMPLE_URL + "serviceworkerinfo_iframe.html"; + + function wait_for_active_worker(registration) { + ok(registration, "Registration is valid."); + return new Promise(function(res, rej) { + if (registration.activeWorker) { + res(registration); + return; + } + let listener = { + onChange: function() { + if (registration.activeWorker) { + registration.removeListener(listener); + res(registration); + } + } + } + registration.addListener(listener); + }); + } + + function test() { + SimpleTest.waitForExplicitFinish(); + + SpecialPowers.pushPrefEnv({'set': [ + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.idle_extended_timeout", 1000000], + ["dom.serviceWorkers.idle_timeout", 0], + ["dom.serviceWorkers.testing.enabled", true], + ]}, function () { + Task.spawn(function *() { + let iframe = $("iframe"); + let promise = new Promise(function (resolve) { + iframe.onload = function () { + resolve(); + }; + }); + iframe.src = IFRAME_URL; + yield promise; + + info("Check that a service worker eventually shuts down."); + promise = Promise.all([ + waitForRegister(EXAMPLE_URL), + waitForServiceWorkerShutdown() + ]); + iframe.contentWindow.postMessage("register", "*"); + let [registration] = yield promise; + + // Make sure the worker is active. + registration = yield wait_for_active_worker(registration); + + let activeWorker = registration.activeWorker; + ok(activeWorker !== null, "Worker is not active!"); + ok(activeWorker.debugger === null); + + info("Attach a debugger to the service worker, and check that the " + + "service worker is restarted."); + activeWorker.attachDebugger(); + let workerDebugger = activeWorker.debugger; + ok(workerDebugger !== null); + + // Verify debugger properties + ok(workerDebugger.principal instanceof Ci.nsIPrincipal); + is(workerDebugger.url, EXAMPLE_URL + "worker.js"); + + info("Verify that getRegistrationByPrincipal return the same " + + "nsIServiceWorkerRegistrationInfo"); + let reg = swm.getRegistrationByPrincipal(workerDebugger.principal, + workerDebugger.url); + is(reg, registration); + + info("Check that getWorkerByID returns the same nsIWorkerDebugger"); + is(activeWorker, reg.getWorkerByID(workerDebugger.serviceWorkerID)); + + info("Detach the debugger from the service worker, and check that " + + "the service worker eventually shuts down again."); + promise = waitForServiceWorkerShutdown(); + activeWorker.detachDebugger(); + yield promise; + ok(activeWorker.debugger === null); + + promise = waitForUnregister(EXAMPLE_URL); + iframe.contentWindow.postMessage("unregister", "*"); + registration = yield promise; + + SimpleTest.finish(); + }); + }); + } + + ]]> + </script> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <p id="display"></p> + <div id="content" style="display:none;"></div> + <pre id="test"></pre> + <iframe id="iframe"></iframe> + </body> + <label id="test-result"/> +</window> diff --git a/dom/workers/test/serviceworkers/test_serviceworkermanager.xul b/dom/workers/test/serviceworkers/test_serviceworkermanager.xul new file mode 100644 index 000000000..ead935a3c --- /dev/null +++ b/dom/workers/test/serviceworkers/test_serviceworkermanager.xul @@ -0,0 +1,80 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Test for ServiceWorkerManager" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="test();"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="application/javascript" src="chrome_helpers.js"/> + <script type="application/javascript"> + <![CDATA[ + + let IFRAME_URL = EXAMPLE_URL + "serviceworkermanager_iframe.html"; + + function test() { + SimpleTest.waitForExplicitFinish(); + + SpecialPowers.pushPrefEnv({'set': [ + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}, function () { + Task.spawn(function* () { + let registrations = swm.getAllRegistrations(); + is(registrations.length, 0); + + let iframe = $("iframe"); + let promise = waitForIframeLoad(iframe); + iframe.src = IFRAME_URL; + yield promise; + + info("Check that the service worker manager notifies its listeners " + + "when a service worker is registered."); + promise = waitForRegister(EXAMPLE_URL); + iframe.contentWindow.postMessage("register", "*"); + let registration = yield promise; + + registrations = swm.getAllRegistrations(); + is(registrations.length, 1); + is(registrations.queryElementAt(0, Ci.nsIServiceWorkerRegistrationInfo), + registration); + + info("Check that the service worker manager does not notify its " + + "listeners when a service worker is registered with the same " + + "scope as an existing registration."); + let listener = { + onRegister: function () { + ok(false, "Listener should not have been notified."); + } + }; + swm.addListener(listener); + iframe.contentWindow.postMessage("register", "*"); + + info("Check that the service worker manager notifies its listeners " + + "when a service worker is unregistered."); + promise = waitForUnregister(EXAMPLE_URL); + iframe.contentWindow.postMessage("unregister", "*"); + registration = yield promise; + swm.removeListener(listener); + + registrations = swm.getAllRegistrations(); + is(registrations.length, 0); + + SimpleTest.finish(); + }); + }); + } + + ]]> + </script> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <p id="display"></p> + <div id="content" style="display:none;"></div> + <pre id="test"></pre> + <iframe id="iframe"></iframe> + </body> + <label id="test-result"/> +</window> diff --git a/dom/workers/test/serviceworkers/test_serviceworkerregistrationinfo.xul b/dom/workers/test/serviceworkers/test_serviceworkerregistrationinfo.xul new file mode 100644 index 000000000..c879dc01b --- /dev/null +++ b/dom/workers/test/serviceworkers/test_serviceworkerregistrationinfo.xul @@ -0,0 +1,115 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Test for ServiceWorkerRegistrationInfo" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="test();"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="application/javascript" src="chrome_helpers.js"/> + <script type="application/javascript"> + <![CDATA[ + + let IFRAME_URL = EXAMPLE_URL + "serviceworkerregistrationinfo_iframe.html"; + + function test() { + SimpleTest.waitForExplicitFinish(); + + SpecialPowers.pushPrefEnv({'set': [ + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}, function () { + Task.spawn(function* () { + let iframe = $("iframe"); + let promise = waitForIframeLoad(iframe); + iframe.src = IFRAME_URL; + yield promise; + + // The change handler is not guaranteed to be called within the same + // tick of the event loop as the one in which the change happened. + // Because of this, the exact state of the service worker registration + // is only known until the handler returns. + // + // Because then-handlers are resolved asynchronously, the following + // checks are done using callbacks, which are called synchronously + // when then handler is called. These callbacks can return a promise, + // which is used to resolve the promise returned by the function. + + info("Check that a service worker registration notifies its " + + "listeners when its state changes."); + promise = waitForRegister(EXAMPLE_URL, function (registration) { + is(registration.scriptSpec, ""); + ok(registration.installingWorker === null); + ok(registration.waitingWorker === null); + ok(registration.activeWorker === null); + + return waitForServiceWorkerRegistrationChange(registration, function () { + is(registration.scriptSpec, EXAMPLE_URL + "worker.js"); + ok(registration.installingWorker !== null); + is(registration.installingWorker.scriptSpec, EXAMPLE_URL + "worker.js"); + ok(registration.waitingWorker === null); + ok(registration.activeWorker === null); + + return waitForServiceWorkerRegistrationChange(registration, function () { + ok(registration.installingWorker === null); + ok(registration.waitingWorker !== null); + ok(registration.activeWorker === null); + + return waitForServiceWorkerRegistrationChange(registration, function () { + ok(registration.installingWorker === null); + ok(registration.waitingWorker === null); + ok(registration.activeWorker !== null); + + return registration; + }); + }); + }); + }); + iframe.contentWindow.postMessage("register", "*"); + let registration = yield promise; + + promise = waitForServiceWorkerRegistrationChange(registration, function () { + is(registration.scriptSpec, EXAMPLE_URL + "worker2.js"); + ok(registration.installingWorker !== null); + is(registration.installingWorker.scriptSpec, EXAMPLE_URL + "worker2.js"); + ok(registration.waitingWorker === null); + ok(registration.activeWorker !== null); + + return waitForServiceWorkerRegistrationChange(registration, function () { + ok(registration.installingWorker === null); + ok(registration.waitingWorker !== null); + ok(registration.activeWorker !== null); + + return waitForServiceWorkerRegistrationChange(registration, function () { + ok(registration.installingWorker === null); + ok(registration.waitingWorker === null); + ok(registration.activeWorker !== null); + + return registration; + }); + }); + }); + iframe.contentWindow.postMessage("register", "*"); + yield promise; + + iframe.contentWindow.postMessage("unregister", "*"); + yield waitForUnregister(EXAMPLE_URL); + + SimpleTest.finish(); + }); + }); + } + + ]]> + </script> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <p id="display"></p> + <div id="content" style="display:none;"></div> + <pre id="test"></pre> + <iframe id="iframe"></iframe> + </body> + <label id="test-result"/> +</window> diff --git a/dom/workers/test/serviceworkers/test_skip_waiting.html b/dom/workers/test/serviceworkers/test_skip_waiting.html new file mode 100644 index 000000000..7707d6035 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_skip_waiting.html @@ -0,0 +1,95 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1131352 - Add ServiceWorkerGlobalScope skipWaiting()</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + var registration, iframe, content; + + function start() { + return navigator.serviceWorker.register("worker.js", + {scope: "./skip_waiting_scope/"}); + } + + function waitForActivated(swr) { + registration = swr; + var promise = new Promise(function(resolve, reject) { + window.onmessage = function(e) { + if (e.data === "READY") { + ok(true, "Active worker is activated now"); + resolve(); + } else { + ok(false, "Wrong value. Somenting went wrong"); + resolve(); + } + } + }); + + iframe = document.createElement("iframe"); + iframe.setAttribute("src", "skip_waiting_scope/index.html"); + + content = document.getElementById("content"); + content.appendChild(iframe); + + return promise; + } + + function checkWhetherItSkippedWaiting() { + var promise = new Promise(function(resolve, reject) { + window.onmessage = function (evt) { + if (evt.data.event === "controllerchange") { + ok(evt.data.controllerScriptURL.match("skip_waiting_installed_worker"), + "The controller changed after skiping the waiting step"); + resolve(); + } else { + ok(false, "Wrong value. Somenting went wrong"); + resolve(); + } + }; + }); + + navigator.serviceWorker.register("skip_waiting_installed_worker.js", + {scope: "./skip_waiting_scope/"}) + .then(swr => { + registration = swr; + }); + + return promise; + } + + function clean() { + content.removeChild(iframe); + + return registration.unregister(); + } + + function runTest() { + start() + .then(waitForActivated) + .then(checkWhetherItSkippedWaiting) + .then(clean) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + }).then(SimpleTest.finish); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_strict_mode_warning.html b/dom/workers/test/serviceworkers/test_strict_mode_warning.html new file mode 100644 index 000000000..5b66673b9 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_strict_mode_warning.html @@ -0,0 +1,42 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1170550 - test registration of service worker scripts with a strict mode warning</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + function runTest() { + navigator.serviceWorker + .register("strict_mode_warning.js", {scope: "strict_mode_warning"}) + .then((reg) => { + ok(true, "Registration should not fail for warnings"); + return reg.unregister(); + }) + .then(() => { + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}, runTest); + }; +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_third_party_iframes.html b/dom/workers/test/serviceworkers/test_third_party_iframes.html new file mode 100644 index 000000000..33e815379 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_third_party_iframes.html @@ -0,0 +1,175 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <meta http-equiv="Content-type" content="text/html;charset=UTF-8"> + <title>Bug 1152899 - Disallow the interception of third-party iframes using service workers when the third-party cookie preference is set</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +<iframe></iframe> +</div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript;version=1.7"> + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestLongerTimeout(2); + +let index = 0; +function next() { + info("Step " + index); + if (index >= steps.length) { + SimpleTest.finish(); + return; + } + try { + let i = index++; + steps[i](); + } catch(ex) { + ok(false, "Caught exception", ex); + } +} + +onload = next; + +let iframe; +let basePath = "/tests/dom/workers/test/serviceworkers/thirdparty/"; +let origin = window.location.protocol + "//" + window.location.host; +let thirdPartyOrigin = "https://example.com"; + +function testIframeLoaded() { + ok(true, "Iframe loaded"); + iframe.removeEventListener("load", testIframeLoaded); + let message = { + source: "parent", + href: origin + basePath + "iframe2.html" + }; + iframe.contentWindow.postMessage(message.toSource(), "*"); +} + +function loadThirdPartyIframe() { + let message = { + source: "parent", + href: thirdPartyOrigin + basePath + "iframe2.html" + } + iframe.contentWindow.postMessage(message.toSource(), "*"); +} + +function runTest(aExpectedResponses) { + iframe = document.querySelector("iframe"); + iframe.src = thirdPartyOrigin + basePath + "register.html"; + let responsesIndex = 0; + window.onmessage = function(e) { + let status = e.data.status; + let expected = aExpectedResponses[responsesIndex]; + if (status == expected.status) { + ok(true, "Received expected " + expected.status); + if (expected.next) { + expected.next(); + } + } else { + ok(false, "Expected " + expected.status + " got " + status); + } + responsesIndex++; + }; +} + +function testShouldIntercept(done) { + runTest([{ + status: "ok" + }, { + status: "registrationdone", + next: function() { + iframe.addEventListener("load", testIframeLoaded); + iframe.src = origin + basePath + "iframe1.html"; + } + }, { + status: "networkresponse", + next: loadThirdPartyIframe + }, { + status: "swresponse", + next: function() { + iframe.src = thirdPartyOrigin + basePath + "unregister.html"; + } + }, { + status: "unregistrationdone", + next: function() { + window.onmessage = null; + ok(true, "Test finished successfully"); + done(); + } + }]); +} + +function testShouldNotIntercept(done) { + runTest([{ + status: "ok" + }, { + status: "registrationdone", + next: function() { + iframe.addEventListener("load", testIframeLoaded); + iframe.src = origin + basePath + "iframe1.html"; + } + }, { + status: "networkresponse", + next: loadThirdPartyIframe + }, { + status: "networkresponse", + next: function() { + iframe.src = thirdPartyOrigin + basePath + "unregister.html"; + } + }, { + status: "unregistrationdone", + next: function() { + window.onmessage = null; + ok(true, "Test finished successfully"); + done(); + } + }]); +} + +const COOKIE_BEHAVIOR_ACCEPT = 0; +const COOKIE_BEHAVIOR_REJECTFOREIGN = 1; +const COOKIE_BEHAVIOR_REJECT = 2; +const COOKIE_BEHAVIOR_LIMITFOREIGN = 3; + +let steps = [() => { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["browser.dom.window.dump.enabled", true], + ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_ACCEPT] + ]}, next); +}, () => { + testShouldIntercept(next); +}, () => { + SpecialPowers.pushPrefEnv({"set": [ + ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_REJECTFOREIGN] + ]}, next); +}, () => { + testShouldNotIntercept(next); +}, () => { + SpecialPowers.pushPrefEnv({"set": [ + ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_REJECT] + ]}, next); +}, () => { + testShouldIntercept(next); +}, () => { + SpecialPowers.pushPrefEnv({"set": [ + ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_LIMITFOREIGN] + ]}, next); +}, () => { + testShouldIntercept(next); +}]; + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_unregister.html b/dom/workers/test/serviceworkers/test_unregister.html new file mode 100644 index 000000000..8366f50c1 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_unregister.html @@ -0,0 +1,138 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 984048 - Test unregister</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + function simpleRegister() { + return navigator.serviceWorker.register("worker.js", { scope: "unregister/" }).then(function(swr) { + if (swr.installing) { + return new Promise(function(resolve, reject) { + swr.installing.onstatechange = function(e) { + if (swr.waiting) { + swr.waiting.onstatechange = function(e) { + if (swr.active) { + resolve(); + } else if (swr.waiting && swr.waiting.state == "redundant") { + reject("Should not go into redundant"); + } + } + } else { + if (swr.active) { + resolve(); + } else { + reject("No waiting and no active!"); + } + } + } + }); + } else { + return Promise.reject("Installing should be non-null"); + } + }); + } + + function testControlled() { + var testPromise = new Promise(function(res, rej) { + window.onmessage = function(e) { + if (!("controlled" in e.data)) { + ok(false, "Something went wrong."); + rej(); + return; + } + + ok(e.data.controlled, "New window should be controlled."); + res(); + } + }) + + var div = document.getElementById("content"); + ok(div, "Parent exists"); + + var ifr = document.createElement("iframe"); + ifr.setAttribute('src', "unregister/index.html"); + div.appendChild(ifr); + + return testPromise.then(function() { + div.removeChild(ifr); + }); + } + + function unregister() { + return navigator.serviceWorker.getRegistration("unregister/") + .then(function(reg) { + if (!reg) { + info("Registration already removed"); + return; + } + + info("getRegistration() succeeded " + reg.scope); + return reg.unregister().then(function(v) { + ok(v, "Unregister should resolve to true"); + }, function(e) { + ok(false, "Unregister failed with " + e.name); + }); + }); + } + + function testUncontrolled() { + var testPromise = new Promise(function(res, rej) { + window.onmessage = function(e) { + if (!("controlled" in e.data)) { + ok(false, "Something went wrong."); + rej(); + return; + } + + ok(!e.data.controlled, "New window should not be controlled."); + res(); + } + }); + + var div = document.getElementById("content"); + ok(div, "Parent exists"); + + var ifr = document.createElement("iframe"); + ifr.setAttribute('src', "unregister/index.html"); + div.appendChild(ifr); + + return testPromise.then(function() { + div.removeChild(ifr); + }); + } + + function runTest() { + simpleRegister() + .then(testControlled) + .then(unregister) + .then(testUncontrolled) + .then(function() { + SimpleTest.finish(); + }).catch(function(e) { + ok(false, "Some test failed with error " + e); + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_unresolved_fetch_interception.html b/dom/workers/test/serviceworkers/test_unresolved_fetch_interception.html new file mode 100644 index 000000000..7296a3ddf --- /dev/null +++ b/dom/workers/test/serviceworkers/test_unresolved_fetch_interception.html @@ -0,0 +1,100 @@ +<!DOCTYPE HTML> +<html> +<!-- + Test that an unresolved respondWith promise will reset the channel when + the service worker is terminated due to idling, and that appropriate error + messages are logged for both the termination of the serice worker and the + resetting of the channel. + --> +<head> + <title>Test for Bug 1188545</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/SpawnTask.js"></script> + <script src="error_reporting_helpers.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <meta http-equiv="Content-type" content="text/html;charset=UTF-8"> +</head> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1188545">Mozilla Bug 118845</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> + +<script class="testbody" type="text/javascript"> +// (This doesn't really need to be its own task, but it allows the actual test +// case to be self-contained.) +add_task(function setupPrefs() { + return SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}); +}); + +add_task(function* grace_timeout_termination_with_interrupted_intercept() { + // Setup timeouts so that the service worker will go into grace timeout after + // a zero-length idle timeout. + yield SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.idle_timeout", 0], + ["dom.serviceWorkers.idle_extended_timeout", 299999]]}); + + // The SW will claim us once it activates; this is async, start listening now. + let waitForControlled = new Promise((resolve) => { + navigator.serviceWorker.oncontrollerchange = resolve; + }); + + let registration = yield navigator.serviceWorker.register( + "unresolved_fetch_worker.js", { scope: "./"} ); + yield waitForControlled; + ok(navigator.serviceWorker.controller, "Controlled"); // double check! + + // We want to make sure the SW is active and processing the fetch before we + // try and kill it. It sends us a message when it has done so. + let waitForFetchActive = new Promise((resolve) => { + navigator.serviceWorker.onmessage = resolve; + }); + + // Issue a fetch which the SW will respondWith() a never resolved promise. + // The fetch, however, will terminate when the SW is killed, so check that. + let hangingFetch = fetch("does_not_exist.html") + .then(() => { ok(false, "should have rejected "); }, + () => { ok(true, "hung fetch terminates when worker dies"); }); + + yield waitForFetchActive; + + let expectedMessage = expect_console_message( + // Termination error + "ServiceWorkerGraceTimeoutTermination", + [make_absolute_url("./")], + // The interception failure error generated by the RespondWithHandler + // destructor when it notices it didn't get a response before being + // destroyed. It logs via the intercepted channel nsIConsoleReportCollector + // that is eventually flushed to our document and its console. + "InterceptionFailedWithURL", + [make_absolute_url("does_not_exist.html")] + ); + + // Zero out the grace timeout too so the worker will get terminated after two + // zero-length timer firings. Note that we need to do something to get the + // SW to renew its keepalive for this to actually cause the timers to be + // rescheduled... + yield SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.idle_extended_timeout", 0]]}); + // ...which we do by postMessaging it. + navigator.serviceWorker.controller.postMessage("doomity doom doom"); + + // Now wait for signs that the worker was terminated by the fetch failing. + yield hangingFetch; + + // The worker should now be dead and the error logged, wait/assert. + yield wait_for_expected_message(expectedMessage); + + // roll back all of our test case specific preferences and otherwise cleanup + yield SpecialPowers.popPrefEnv(); + yield SpecialPowers.popPrefEnv(); + yield registration.unregister(); +}); +</script> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/test_workerUnregister.html b/dom/workers/test/serviceworkers/test_workerUnregister.html new file mode 100644 index 000000000..947861c17 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_workerUnregister.html @@ -0,0 +1,82 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 982728 - Test ServiceWorkerGlobalScope.unregister</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<div id="container"></div> +<script class="testbody" type="text/javascript"> + + function simpleRegister() { + return navigator.serviceWorker.register("worker_unregister.js", { scope: "unregister/" }).then(function(swr) { + if (swr.installing) { + return new Promise(function(resolve, reject) { + swr.installing.onstatechange = function(e) { + if (swr.waiting) { + swr.waiting.onstatechange = function(e) { + if (swr.active) { + resolve(); + } else if (swr.waiting && swr.waiting.state == "redundant") { + reject("Should not go into redundant"); + } + } + } else { + if (swr.active) { + resolve(); + } else { + reject("No waiting and no active!"); + } + } + } + }); + } else { + return Promise.reject("Installing should be non-null"); + } + }); + } + + function waitForMessages(sw) { + var p = new Promise(function(resolve, reject) { + window.onmessage = function(e) { + if (e.data === "DONE") { + ok(true, "The worker has unregistered itself"); + } else if (e.data === "ERROR") { + ok(false, "The worker has unregistered itself"); + } else if (e.data === "FINISH") { + resolve(); + } + } + }); + + var frame = document.createElement("iframe"); + frame.setAttribute("src", "unregister/unregister.html"); + document.body.appendChild(frame); + + return p; + } + + function runTest() { + simpleRegister().then(waitForMessages).catch(function(e) { + ok(false, "Something went wrong."); + }).then(function() { + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_workerUpdate.html b/dom/workers/test/serviceworkers/test_workerUpdate.html new file mode 100644 index 000000000..5621d6cb8 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_workerUpdate.html @@ -0,0 +1,62 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1065366 - Test ServiceWorkerGlobalScope.update</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<div id="container"></div> +<script class="testbody" type="text/javascript"> + + function simpleRegister() { + return navigator.serviceWorker.register("worker_update.js", { scope: "workerUpdate/" }); + } + + var registration; + function waitForMessages(sw) { + registration = sw; + var p = new Promise(function(resolve, reject) { + window.onmessage = function(e) { + if (e.data === "FINISH") { + ok(true, "The worker has updated itself"); + resolve(); + } else if (e.data === "FAIL") { + ok(false, "The worker failed to update itself"); + resolve(); + } + } + }); + + var frame = document.createElement("iframe"); + frame.setAttribute("src", "workerUpdate/update.html"); + document.body.appendChild(frame); + + return p; + } + + function runTest() { + simpleRegister().then(waitForMessages).catch(function(e) { + ok(false, "Something went wrong."); + }).then(function() { + return registration.unregister(); + }).then(function() { + SimpleTest.finish(); + }); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true] + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html b/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html new file mode 100644 index 000000000..3361eba08 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html @@ -0,0 +1,85 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1131327 - Test ServiceWorkerRegistration.onupdatefound on ServiceWorker</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + var registration; + var promise; + + function start() { + return navigator.serviceWorker.register("worker_updatefoundevent.js", + { scope: "./updatefoundevent.html" }) + .then((swr) => registration = swr); + } + + function startWaitForUpdateFound() { + registration.onupdatefound = function(e) { + } + + promise = new Promise(function(resolve, reject) { + window.onmessage = function(e) { + + if (e.data == "finish") { + ok(true, "Received updatefound"); + resolve(); + } + } + }); + + content = document.getElementById("content"); + iframe = document.createElement("iframe"); + content.appendChild(iframe); + iframe.setAttribute("src", "./updatefoundevent.html"); + + return Promise.resolve(); + } + + function registerNext() { + return navigator.serviceWorker.register("worker_updatefoundevent2.js", + { scope: "./updatefoundevent.html" }); + } + + function waitForUpdateFound() { + return promise; + } + + function unregister() { + window.onmessage = null; + return registration.unregister().then(function(result) { + ok(result, "Unregister should return true."); + }); + } + + function runTest() { + start() + .then(startWaitForUpdateFound) + .then(registerNext) + .then(waitForUpdateFound) + .then(unregister) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + }).then(SimpleTest.finish); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_xslt.html b/dom/workers/test/serviceworkers/test_xslt.html new file mode 100644 index 000000000..44270753b --- /dev/null +++ b/dom/workers/test/serviceworkers/test_xslt.html @@ -0,0 +1,128 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1182113 - Test service worker XSLT interception</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + var registration; + var worker; + + function start() { + return navigator.serviceWorker.register("xslt_worker.js", + { scope: "./" }) + .then((swr) => { + registration = swr; + + // Ensure the registration is active before continuing + var worker = registration.installing; + return new Promise((resolve) => { + if (worker.state === 'activated') { + resolve(); + return; + } + worker.addEventListener('statechange', () => { + if (worker.state === 'activated') { + resolve(); + } + }); + }); + }); + } + + function unregister() { + return registration.unregister().then(function(result) { + ok(result, "Unregister should return true."); + }, function(e) { + dump("Unregistering the SW failed with " + e + "\n"); + }); + } + + function getXmlString(xmlObject) { + serializer = new XMLSerializer(); + return serializer.serializeToString(iframe.contentDocument); + } + + function synthetic() { + content = document.getElementById("content"); + ok(content, "parent exists."); + + iframe = document.createElement("iframe"); + content.appendChild(iframe); + + iframe.setAttribute('src', "xslt/test.xml"); + + var p = new Promise(function(res, rej) { + iframe.onload = function(e) { + dump("Set request mode\n"); + registration.active.postMessage("synthetic"); + xmlString = getXmlString(iframe.contentDocument); + ok(!xmlString.includes("Error"), "Load synthetic cross origin XSLT should be allowed"); + res(); + }; + }); + + return p; + } + + function cors() { + var p = new Promise(function(res, rej) { + iframe.onload = function(e) { + xmlString = getXmlString(iframe.contentDocument); + ok(!xmlString.includes("Error"), "Load CORS cross origin XSLT should be allowed"); + res(); + }; + }); + + registration.active.postMessage("cors"); + iframe.setAttribute('src', "xslt/test.xml"); + + return p; + } + + function opaque() { + var p = new Promise(function(res, rej) { + iframe.onload = function(e) { + xmlString = getXmlString(iframe.contentDocument); + ok(xmlString.includes("Error"), "Load opaque cross origin XSLT should not be allowed"); + res(); + }; + }); + + registration.active.postMessage("opaque"); + iframe.setAttribute('src', "xslt/test.xml"); + + return p; + } + + function runTest() { + start() + .then(synthetic) + .then(opaque) + .then(cors) + .then(unregister) + .catch(function(e) { + ok(false, "Some test failed with error " + e); + }).then(SimpleTest.finish); + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}, runTest); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/thirdparty/iframe1.html b/dom/workers/test/serviceworkers/thirdparty/iframe1.html new file mode 100644 index 000000000..43fe8c572 --- /dev/null +++ b/dom/workers/test/serviceworkers/thirdparty/iframe1.html @@ -0,0 +1,30 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <meta http-equiv="Content-type" content="text/html;charset=UTF-8"> + <title>SW third party iframe test</title> + + <script type="text/javascript;version=1.7"> + function messageListener(event) { + let message = eval(event.data); + + dump("got message " + JSON.stringify(message) + "\n"); + if (message.source == "parent") { + document.getElementById("iframe2").src = message.href; + } + else if (message.source == "iframe") { + parent.postMessage(event.data, "*"); + } + } + </script> + +</head> + +<body onload="window.addEventListener('message', messageListener, false);"> + <iframe id="iframe2"></iframe> +</body> + +</html> diff --git a/dom/workers/test/serviceworkers/thirdparty/iframe2.html b/dom/workers/test/serviceworkers/thirdparty/iframe2.html new file mode 100644 index 000000000..fac6a9395 --- /dev/null +++ b/dom/workers/test/serviceworkers/thirdparty/iframe2.html @@ -0,0 +1,7 @@ +<!DOCTYPE html> +<script> + window.parent.postMessage({ + source: "iframe", + status: "networkresponse" + }, "*"); +</script> diff --git a/dom/workers/test/serviceworkers/thirdparty/register.html b/dom/workers/test/serviceworkers/thirdparty/register.html new file mode 100644 index 000000000..59b8c5c41 --- /dev/null +++ b/dom/workers/test/serviceworkers/thirdparty/register.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<script> + function ok(v, msg) { + window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*"); + } + + var isDone = false; + function done(reg) { + if (!isDone) { + ok(reg.waiting || reg.active, + "Either active or waiting worker should be available."); + window.parent.postMessage({status: "registrationdone"}, "*"); + isDone = true; + } + } + + navigator.serviceWorker.register("sw.js", {scope: "."}) + .then(function(registration) { + if (registration.installing) { + registration.installing.onstatechange = function(e) { + done(registration); + }; + } else { + done(registration); + } + }); +</script> diff --git a/dom/workers/test/serviceworkers/thirdparty/sw.js b/dom/workers/test/serviceworkers/thirdparty/sw.js new file mode 100644 index 000000000..ca45698c8 --- /dev/null +++ b/dom/workers/test/serviceworkers/thirdparty/sw.js @@ -0,0 +1,14 @@ +self.addEventListener("fetch", function(event) { + dump("fetch " + event.request.url + "\n"); + if (event.request.url.indexOf("iframe2.html") >= 0) { + var body = + "<script>" + + "window.parent.postMessage({" + + "source: 'iframe', status: 'swresponse'" + + "}, '*');" + + "</script>"; + event.respondWith(new Response(body, { + headers: {'Content-Type': 'text/html'} + })); + } +}); diff --git a/dom/workers/test/serviceworkers/thirdparty/unregister.html b/dom/workers/test/serviceworkers/thirdparty/unregister.html new file mode 100644 index 000000000..2cb6ee0ce --- /dev/null +++ b/dom/workers/test/serviceworkers/thirdparty/unregister.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<script> + navigator.serviceWorker.getRegistration(".").then(function(registration) { + if(!registration) { + return; + } + registration.unregister().then(() => { + window.parent.postMessage({status: "unregistrationdone"}, "*"); + }); + }); +</script> diff --git a/dom/workers/test/serviceworkers/unregister/index.html b/dom/workers/test/serviceworkers/unregister/index.html new file mode 100644 index 000000000..db23d2cb7 --- /dev/null +++ b/dom/workers/test/serviceworkers/unregister/index.html @@ -0,0 +1,26 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 984048 - Test unregister</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + if (!parent) { + info("unregister/index.html should not to be launched directly!"); + } + + parent.postMessage({ controlled: !!navigator.serviceWorker.controller }, "*"); +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/serviceworkers/unregister/unregister.html b/dom/workers/test/serviceworkers/unregister/unregister.html new file mode 100644 index 000000000..6fda82026 --- /dev/null +++ b/dom/workers/test/serviceworkers/unregister/unregister.html @@ -0,0 +1,22 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test worker::unregister</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<script type="text/javascript"> + + navigator.serviceWorker.onmessage = function(e) { parent.postMessage(e.data, "*"); } + navigator.serviceWorker.controller.postMessage("GO"); + +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/unresolved_fetch_worker.js b/dom/workers/test/serviceworkers/unresolved_fetch_worker.js new file mode 100644 index 000000000..71fbad781 --- /dev/null +++ b/dom/workers/test/serviceworkers/unresolved_fetch_worker.js @@ -0,0 +1,19 @@ +var keepPromiseAlive; +onfetch = function(event) { + event.waitUntil( + clients.matchAll() + .then(clients => { + clients.forEach(client => { + client.postMessage("continue"); + }); + }) + ); + + // Never resolve, and keep it alive on our global so it can't get GC'ed and + // make this test weird and intermittent. + event.respondWith((keepPromiseAlive = new Promise(function(res, rej) {}))); +} + +onactivate = function(event) { + event.waitUntil(clients.claim()); +} diff --git a/dom/workers/test/serviceworkers/updatefoundevent.html b/dom/workers/test/serviceworkers/updatefoundevent.html new file mode 100644 index 000000000..78088c7cd --- /dev/null +++ b/dom/workers/test/serviceworkers/updatefoundevent.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html> +<head> + <title>Bug 1131327 - Test ServiceWorkerRegistration.onupdatefound on ServiceWorker</title> +</head> +<body> +<script> + navigator.serviceWorker.onmessage = function(e) { + dump("NSM iframe got message " + e.data + "\n"); + window.parent.postMessage(e.data, "*"); + }; +</script> +</body> diff --git a/dom/workers/test/serviceworkers/worker.js b/dom/workers/test/serviceworkers/worker.js new file mode 100644 index 000000000..2aba167d1 --- /dev/null +++ b/dom/workers/test/serviceworkers/worker.js @@ -0,0 +1 @@ +// empty worker, always succeed! diff --git a/dom/workers/test/serviceworkers/worker2.js b/dom/workers/test/serviceworkers/worker2.js new file mode 100644 index 000000000..3072d0817 --- /dev/null +++ b/dom/workers/test/serviceworkers/worker2.js @@ -0,0 +1 @@ +// worker2.js diff --git a/dom/workers/test/serviceworkers/worker3.js b/dom/workers/test/serviceworkers/worker3.js new file mode 100644 index 000000000..449fc2f97 --- /dev/null +++ b/dom/workers/test/serviceworkers/worker3.js @@ -0,0 +1 @@ +// worker3.js diff --git a/dom/workers/test/serviceworkers/workerUpdate/update.html b/dom/workers/test/serviceworkers/workerUpdate/update.html new file mode 100644 index 000000000..8f984ccc4 --- /dev/null +++ b/dom/workers/test/serviceworkers/workerUpdate/update.html @@ -0,0 +1,24 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test worker::update</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<script type="text/javascript"> + + navigator.serviceWorker.onmessage = function(e) { parent.postMessage(e.data, "*"); } + navigator.serviceWorker.ready.then(function() { + navigator.serviceWorker.controller.postMessage("GO"); + }); + +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/worker_unregister.js b/dom/workers/test/serviceworkers/worker_unregister.js new file mode 100644 index 000000000..7a3e764f4 --- /dev/null +++ b/dom/workers/test/serviceworkers/worker_unregister.js @@ -0,0 +1,16 @@ +onmessage = function(e) { + clients.matchAll().then(function(c) { + if (c.length === 0) { + // We cannot proceed. + return; + } + + registration.unregister().then(function() { + c[0].postMessage('DONE'); + }, function() { + c[0].postMessage('ERROR'); + }).then(function() { + c[0].postMessage('FINISH'); + }); + }); +} diff --git a/dom/workers/test/serviceworkers/worker_update.js b/dom/workers/test/serviceworkers/worker_update.js new file mode 100644 index 000000000..9f3e55b18 --- /dev/null +++ b/dom/workers/test/serviceworkers/worker_update.js @@ -0,0 +1,19 @@ +// For now this test only calls update to verify that our registration +// job queueing works properly when called from the worker thread. We should +// test actual update scenarios with a SJS test. +onmessage = function(e) { + self.registration.update().then(function(v) { + return v === undefined ? 'FINISH' : 'FAIL'; + }).catch(function(e) { + return 'FAIL'; + }).then(function(result) { + clients.matchAll().then(function(c) { + if (c.length == 0) { + dump("!!!!!!!!!!! WORKER HAS NO CLIENTS TO FINISH TEST !!!!!!!!!!!!\n"); + return; + } + + c[0].postMessage(result); + }); + }); +} diff --git a/dom/workers/test/serviceworkers/worker_updatefoundevent.js b/dom/workers/test/serviceworkers/worker_updatefoundevent.js new file mode 100644 index 000000000..a297bf455 --- /dev/null +++ b/dom/workers/test/serviceworkers/worker_updatefoundevent.js @@ -0,0 +1,23 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +onactivate = function(e) { + e.waitUntil(new Promise(function(resolve, reject) { + registration.onupdatefound = function(e) { + clients.matchAll().then(function(clients) { + if (!clients.length) { + reject("No clients found"); + } + + if (registration.scope.match(/updatefoundevent\.html$/)) { + clients[0].postMessage("finish"); + resolve(); + } else { + dump("Scope did not match"); + } + }, reject); + } + })); +} diff --git a/dom/workers/test/serviceworkers/worker_updatefoundevent2.js b/dom/workers/test/serviceworkers/worker_updatefoundevent2.js new file mode 100644 index 000000000..da4c592aa --- /dev/null +++ b/dom/workers/test/serviceworkers/worker_updatefoundevent2.js @@ -0,0 +1 @@ +// Not useful. diff --git a/dom/workers/test/serviceworkers/xslt/test.xml b/dom/workers/test/serviceworkers/xslt/test.xml new file mode 100644 index 000000000..83c777633 --- /dev/null +++ b/dom/workers/test/serviceworkers/xslt/test.xml @@ -0,0 +1,6 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/xsl" href="test.xsl"?> +<result> + <Title>Example</Title> + <Error>Error</Error> +</result> diff --git a/dom/workers/test/serviceworkers/xslt/xslt.sjs b/dom/workers/test/serviceworkers/xslt/xslt.sjs new file mode 100644 index 000000000..db681ab50 --- /dev/null +++ b/dom/workers/test/serviceworkers/xslt/xslt.sjs @@ -0,0 +1,12 @@ +function handleRequest(request, response) { + response.setHeader("Content-Type", "application/xslt+xml", false); + response.setHeader("Access-Control-Allow-Origin", "*"); + + var body = request.queryString; + if (!body) { + response.setStatusLine(null, 500, "Invalid querystring"); + return; + } + + response.write(unescape(body)); +} diff --git a/dom/workers/test/serviceworkers/xslt_worker.js b/dom/workers/test/serviceworkers/xslt_worker.js new file mode 100644 index 000000000..bf9bdbc56 --- /dev/null +++ b/dom/workers/test/serviceworkers/xslt_worker.js @@ -0,0 +1,52 @@ +var testType = 'synthetic'; + +var xslt = "<?xml version=\"1.0\"?> " + + "<xsl:stylesheet version=\"1.0\"" + + " xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">" + + " <xsl:template match=\"node()|@*\">" + + " <xsl:copy>" + + " <xsl:apply-templates select=\"node()|@*\"/>" + + " </xsl:copy>" + + " </xsl:template>" + + " <xsl:template match=\"Error\"/>" + + "</xsl:stylesheet>"; + +onfetch = function(event) { + if (event.request.url.includes('test.xsl')) { + if (testType == 'synthetic') { + if (event.request.mode != 'cors') { + event.respondWith(Response.error()); + return; + } + + event.respondWith(Promise.resolve( + new Response(xslt, { headers: {'Content-Type': 'application/xslt+xml'}}) + )); + } + else if (testType == 'cors') { + if (event.request.mode != 'cors') { + event.respondWith(Response.error()); + return; + } + + var url = "http://example.com/tests/dom/workers/test/serviceworkers/xslt/xslt.sjs?" + escape(xslt); + event.respondWith(fetch(url, { mode: 'cors' })); + } + else if (testType == 'opaque') { + if (event.request.mode != 'cors') { + event.respondWith(Response.error()); + return; + } + + var url = "http://example.com/tests/dom/workers/test/serviceworkers/xslt/xslt.sjs?" + escape(xslt); + event.respondWith(fetch(url, { mode: 'no-cors' })); + } + else { + event.respondWith(Response.error()); + } + } +}; + +onmessage = function(event) { + testType = event.data; +}; |