diff options
Diffstat (limited to 'dom/workers/test')
649 files changed, 28311 insertions, 0 deletions
diff --git a/dom/workers/test/404_server.sjs b/dom/workers/test/404_server.sjs new file mode 100644 index 000000000..f5dff1abb --- /dev/null +++ b/dom/workers/test/404_server.sjs @@ -0,0 +1,10 @@ +function handleRequest(request, response) +{ + response.setStatusLine(request.httpVersion, 404, "Not found"); + + // Any valid JS. + if (request.queryString == 'js') { + response.setHeader("Content-Type", "text/javascript", false); + response.write('4 + 4'); + } +} diff --git a/dom/workers/test/WorkerDebugger.console_childWorker.js b/dom/workers/test/WorkerDebugger.console_childWorker.js new file mode 100644 index 000000000..8cee6809e --- /dev/null +++ b/dom/workers/test/WorkerDebugger.console_childWorker.js @@ -0,0 +1,3 @@ +"use strict"; + +self.onmessage = function () {}; diff --git a/dom/workers/test/WorkerDebugger.console_debugger.js b/dom/workers/test/WorkerDebugger.console_debugger.js new file mode 100644 index 000000000..662bd3520 --- /dev/null +++ b/dom/workers/test/WorkerDebugger.console_debugger.js @@ -0,0 +1,41 @@ +"use strict" + +function ok(a, msg) { + postMessage(JSON.stringify({ type: 'status', what: !!a, msg: msg })); +} + +function is(a, b, msg) { + ok(a === b, msg); +} + +function finish() { + postMessage(JSON.stringify({ type: 'finish' })); +} + +function magic() { + console.log("Hello from the debugger script!"); + + var foo = retrieveConsoleEvents(); + ok(Array.isArray(foo), "We received an array."); + ok(foo.length >= 2, "At least 2 messages."); + + is(foo[0].arguments[0], "Can you see this console message?", "First message ok."); + is(foo[1].arguments[0], "Can you see this second console message?", "Second message ok."); + + setConsoleEventHandler(function(consoleData) { + is(consoleData.arguments[0], "Random message.", "Random message ok!"); + + // The consoleEventHandler can be null. + setConsoleEventHandler(null); + + finish(); + }); +} + +this.onmessage = function (event) { + switch (event.data) { + case "do magic": + magic(); + break; + } +}; diff --git a/dom/workers/test/WorkerDebugger.console_worker.js b/dom/workers/test/WorkerDebugger.console_worker.js new file mode 100644 index 000000000..2db43a3d7 --- /dev/null +++ b/dom/workers/test/WorkerDebugger.console_worker.js @@ -0,0 +1,10 @@ +"use strict"; + +console.log("Can you see this console message?"); +console.warn("Can you see this second console message?"); + +var worker = new Worker("WorkerDebugger.console_childWorker.js"); + +setInterval(function() { + console.log("Random message."); +}, 200); diff --git a/dom/workers/test/WorkerDebugger.initialize_childWorker.js b/dom/workers/test/WorkerDebugger.initialize_childWorker.js new file mode 100644 index 000000000..a85764bd9 --- /dev/null +++ b/dom/workers/test/WorkerDebugger.initialize_childWorker.js @@ -0,0 +1,6 @@ +"use strict"; + +self.onmessage = function () {}; + +debugger; +postMessage("worker"); diff --git a/dom/workers/test/WorkerDebugger.initialize_debugger.js b/dom/workers/test/WorkerDebugger.initialize_debugger.js new file mode 100644 index 000000000..f52e95b15 --- /dev/null +++ b/dom/workers/test/WorkerDebugger.initialize_debugger.js @@ -0,0 +1,6 @@ +"use strict"; + +var dbg = new Debugger(global); +dbg.onDebuggerStatement = function (frame) { + frame.eval("postMessage('debugger');"); +}; diff --git a/dom/workers/test/WorkerDebugger.initialize_worker.js b/dom/workers/test/WorkerDebugger.initialize_worker.js new file mode 100644 index 000000000..5a24efd3a --- /dev/null +++ b/dom/workers/test/WorkerDebugger.initialize_worker.js @@ -0,0 +1,9 @@ +"use strict"; + +var worker = new Worker("WorkerDebugger.initialize_childWorker.js"); +worker.onmessage = function (event) { + postMessage("child:" + event.data); +}; + +debugger; +postMessage("worker"); diff --git a/dom/workers/test/WorkerDebugger.postMessage_childWorker.js b/dom/workers/test/WorkerDebugger.postMessage_childWorker.js new file mode 100644 index 000000000..8cee6809e --- /dev/null +++ b/dom/workers/test/WorkerDebugger.postMessage_childWorker.js @@ -0,0 +1,3 @@ +"use strict"; + +self.onmessage = function () {}; diff --git a/dom/workers/test/WorkerDebugger.postMessage_debugger.js b/dom/workers/test/WorkerDebugger.postMessage_debugger.js new file mode 100644 index 000000000..4a231b7ab --- /dev/null +++ b/dom/workers/test/WorkerDebugger.postMessage_debugger.js @@ -0,0 +1,9 @@ +"use strict" + +this.onmessage = function (event) { + switch (event.data) { + case "ping": + postMessage("pong"); + break; + } +}; diff --git a/dom/workers/test/WorkerDebugger.postMessage_worker.js b/dom/workers/test/WorkerDebugger.postMessage_worker.js new file mode 100644 index 000000000..8ddf6cf86 --- /dev/null +++ b/dom/workers/test/WorkerDebugger.postMessage_worker.js @@ -0,0 +1,3 @@ +"use strict"; + +var worker = new Worker("WorkerDebugger.postMessage_childWorker.js"); diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_debugger.js b/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_debugger.js new file mode 100644 index 000000000..908c9f316 --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_debugger.js @@ -0,0 +1,9 @@ +"use strict"; + +const SANDBOX_URL = "WorkerDebuggerGlobalScope.createSandbox_sandbox.js"; + +var prototype = { + self: this, +}; +var sandbox = createSandbox(SANDBOX_URL, prototype); +loadSubScript(SANDBOX_URL, sandbox); diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_sandbox.js b/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_sandbox.js new file mode 100644 index 000000000..f94d65062 --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_sandbox.js @@ -0,0 +1,9 @@ +"use strict"; + +self.addEventListener("message", function(event) { + switch (event.data) { + case "ping": + self.postMessage("pong"); + break; + } +}); diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_worker.js b/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_worker.js new file mode 100644 index 000000000..8cee6809e --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_worker.js @@ -0,0 +1,3 @@ +"use strict"; + +self.onmessage = function () {}; diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js new file mode 100644 index 000000000..b76f45a4d --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js @@ -0,0 +1,14 @@ +"use strict"; + +function f() { + debugger; +} + +self.onmessage = function (event) { + switch (event.data) { + case "ping": + debugger; + postMessage("pong"); + break; + }; +}; diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_debugger.js b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_debugger.js new file mode 100644 index 000000000..dcd4dbecd --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_debugger.js @@ -0,0 +1,29 @@ +"use strict"; + +var frames = []; + +var dbg = new Debugger(global); +dbg.onDebuggerStatement = function (frame) { + frames.push(frame); + postMessage("paused"); + enterEventLoop(); + frames.pop(); + postMessage("resumed"); +}; + +this.onmessage = function (event) { + switch (event.data) { + case "eval": + frames[frames.length - 1].eval("f()"); + postMessage("evalled"); + break; + + case "ping": + postMessage("pong"); + break; + + case "resume": + leaveEventLoop(); + break; + }; +}; diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_worker.js b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_worker.js new file mode 100644 index 000000000..c43738516 --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_worker.js @@ -0,0 +1,25 @@ +"use strict"; + +function f() { + debugger; +} + +var worker = new Worker("WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js"); + +worker.onmessage = function (event) { + postMessage("child:" + event.data); +}; + +self.onmessage = function (event) { + var message = event.data; + if (message.indexOf(":") >= 0) { + worker.postMessage(message.split(":")[1]); + return; + } + switch (message) { + case "ping": + debugger; + postMessage("pong"); + break; + }; +}; diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.reportError_childWorker.js b/dom/workers/test/WorkerDebuggerGlobalScope.reportError_childWorker.js new file mode 100644 index 000000000..823e7c477 --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.reportError_childWorker.js @@ -0,0 +1,5 @@ +"use strict"; + +self.onerror = function () { + postMessage("error"); +} diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.reportError_debugger.js b/dom/workers/test/WorkerDebuggerGlobalScope.reportError_debugger.js new file mode 100644 index 000000000..67ea08de5 --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.reportError_debugger.js @@ -0,0 +1,12 @@ +"use strict"; + +this.onmessage = function (event) { + switch (event.data) { + case "report": + reportError("reported"); + break; + case "throw": + throw new Error("thrown"); + break; + } +}; diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.reportError_worker.js b/dom/workers/test/WorkerDebuggerGlobalScope.reportError_worker.js new file mode 100644 index 000000000..67ccfc2ca --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.reportError_worker.js @@ -0,0 +1,11 @@ +"use strict"; + +var worker = new Worker("WorkerDebuggerGlobalScope.reportError_childWorker.js"); + +worker.onmessage = function (event) { + postMessage("child:" + event.data); +}; + +self.onerror = function () { + postMessage("error"); +}; diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.setImmediate_debugger.js b/dom/workers/test/WorkerDebuggerGlobalScope.setImmediate_debugger.js new file mode 100644 index 000000000..b5075c70f --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.setImmediate_debugger.js @@ -0,0 +1,12 @@ +"use strict"; + +this.onmessage = function (event) { + switch (event.data) { + case "ping": + setImmediate(function () { + postMessage("pong1"); + }); + postMessage("pong2"); + break; + } +}; diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.setImmediate_worker.js b/dom/workers/test/WorkerDebuggerGlobalScope.setImmediate_worker.js new file mode 100644 index 000000000..5a72b0f24 --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.setImmediate_worker.js @@ -0,0 +1,3 @@ +"use strict" + +self.onmessage = function () {}; diff --git a/dom/workers/test/WorkerDebuggerManager_childWorker.js b/dom/workers/test/WorkerDebuggerManager_childWorker.js new file mode 100644 index 000000000..8cee6809e --- /dev/null +++ b/dom/workers/test/WorkerDebuggerManager_childWorker.js @@ -0,0 +1,3 @@ +"use strict"; + +self.onmessage = function () {}; diff --git a/dom/workers/test/WorkerDebuggerManager_worker.js b/dom/workers/test/WorkerDebuggerManager_worker.js new file mode 100644 index 000000000..0737d17eb --- /dev/null +++ b/dom/workers/test/WorkerDebuggerManager_worker.js @@ -0,0 +1,3 @@ +"use strict"; + +var worker = new Worker("WorkerDebuggerManager_childWorker.js"); diff --git a/dom/workers/test/WorkerDebugger_childWorker.js b/dom/workers/test/WorkerDebugger_childWorker.js new file mode 100644 index 000000000..8cee6809e --- /dev/null +++ b/dom/workers/test/WorkerDebugger_childWorker.js @@ -0,0 +1,3 @@ +"use strict"; + +self.onmessage = function () {}; diff --git a/dom/workers/test/WorkerDebugger_frozen_iframe1.html b/dom/workers/test/WorkerDebugger_frozen_iframe1.html new file mode 100644 index 000000000..591923121 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_frozen_iframe1.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <script> + var worker = new Worker("WorkerDebugger_frozen_worker1.js"); + worker.onmessage = function () { + parent.postMessage("ready", "*"); + }; + </script> + </head> + <body> + This is page 1. + </body> +<html> diff --git a/dom/workers/test/WorkerDebugger_frozen_iframe2.html b/dom/workers/test/WorkerDebugger_frozen_iframe2.html new file mode 100644 index 000000000..96d5c56eb --- /dev/null +++ b/dom/workers/test/WorkerDebugger_frozen_iframe2.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <script> + var worker = new Worker("WorkerDebugger_frozen_worker2.js"); + worker.onmessage = function () { + parent.postMessage("ready", "*"); + }; + </script> + </head> + <body> + This is page 2. + </body> +<html> diff --git a/dom/workers/test/WorkerDebugger_frozen_worker1.js b/dom/workers/test/WorkerDebugger_frozen_worker1.js new file mode 100644 index 000000000..371d2c064 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_frozen_worker1.js @@ -0,0 +1,5 @@ +"use strict"; + +onmessage = function () {}; + +postMessage("ready"); diff --git a/dom/workers/test/WorkerDebugger_frozen_worker2.js b/dom/workers/test/WorkerDebugger_frozen_worker2.js new file mode 100644 index 000000000..371d2c064 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_frozen_worker2.js @@ -0,0 +1,5 @@ +"use strict"; + +onmessage = function () {}; + +postMessage("ready"); diff --git a/dom/workers/test/WorkerDebugger_promise_debugger.js b/dom/workers/test/WorkerDebugger_promise_debugger.js new file mode 100644 index 000000000..7d7eaf532 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_promise_debugger.js @@ -0,0 +1,30 @@ +"use strict"; + +var self = this; + +self.onmessage = function (event) { + if (event.data !== "resolve") { + return; + } + // This then-handler should be executed inside the top-level event loop, + // within the context of the debugger's global. + Promise.resolve().then(function () { + var dbg = new Debugger(global); + dbg.onDebuggerStatement = function () { + self.onmessage = function (event) { + if (event.data !== "resume") { + return; + } + // This then-handler should be executed inside the nested event loop, + // within the context of the debugger's global. + Promise.resolve().then(function () { + postMessage("resumed"); + leaveEventLoop(); + }); + }; + postMessage("paused"); + enterEventLoop(); + }; + postMessage("resolved"); + }); +}; diff --git a/dom/workers/test/WorkerDebugger_promise_worker.js b/dom/workers/test/WorkerDebugger_promise_worker.js new file mode 100644 index 000000000..a77737af5 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_promise_worker.js @@ -0,0 +1,25 @@ +"use strict"; + +self.onmessage = function (event) { + if (event.data !== "resolve") { + return; + } + // This then-handler should be executed inside the top-level event loop, + // within the context of the worker's global. + Promise.resolve().then(function () { + self.onmessage = function (event) { + if (event.data !== "pause") { + return; + } + // This then-handler should be executed inside the top-level event loop, + // within the context of the worker's global. Because the debugger + // statement here below enters a nested event loop, the then-handler + // should not be executed until the debugger statement returns. + Promise.resolve().then(function () { + postMessage("resumed"); + }); + debugger; + } + postMessage("resolved"); + }); +}; diff --git a/dom/workers/test/WorkerDebugger_sharedWorker.js b/dom/workers/test/WorkerDebugger_sharedWorker.js new file mode 100644 index 000000000..5ad97d4c5 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_sharedWorker.js @@ -0,0 +1,11 @@ +"use strict"; + +self.onconnect = function (event) { + event.ports[0].onmessage = function (event) { + switch (event.data) { + case "close": + close(); + break; + } + }; +}; diff --git a/dom/workers/test/WorkerDebugger_suspended_debugger.js b/dom/workers/test/WorkerDebugger_suspended_debugger.js new file mode 100644 index 000000000..2ed4e16c4 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_suspended_debugger.js @@ -0,0 +1,6 @@ +"use strict"; + +var dbg = new Debugger(global); +dbg.onDebuggerStatement = function (frame) { + postMessage("debugger"); +}; diff --git a/dom/workers/test/WorkerDebugger_suspended_worker.js b/dom/workers/test/WorkerDebugger_suspended_worker.js new file mode 100644 index 000000000..c096be7e4 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_suspended_worker.js @@ -0,0 +1,6 @@ +"use strict"; + +self.onmessage = function () { + postMessage("worker"); + debugger; +}; diff --git a/dom/workers/test/WorkerDebugger_worker.js b/dom/workers/test/WorkerDebugger_worker.js new file mode 100644 index 000000000..f301ac1e8 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_worker.js @@ -0,0 +1,8 @@ +"use strict"; + +var worker = new Worker("WorkerDebugger_childWorker.js"); +self.onmessage = function (event) { + postMessage("child:" + event.data); +}; +debugger; +postMessage("worker"); diff --git a/dom/workers/test/WorkerTest.jsm b/dom/workers/test/WorkerTest.jsm new file mode 100644 index 000000000..86431b7f8 --- /dev/null +++ b/dom/workers/test/WorkerTest.jsm @@ -0,0 +1,17 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +this.EXPORTED_SYMBOLS = [ + "WorkerTest" +]; + +this.WorkerTest = { + go: function(message, messageCallback, errorCallback) { + let worker = new ChromeWorker("WorkerTest_worker.js"); + worker.onmessage = messageCallback; + worker.onerror = errorCallback; + worker.postMessage(message); + return worker; + } +}; diff --git a/dom/workers/test/WorkerTest_badworker.js b/dom/workers/test/WorkerTest_badworker.js new file mode 100644 index 000000000..7a1df54bd --- /dev/null +++ b/dom/workers/test/WorkerTest_badworker.js @@ -0,0 +1,7 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function(event) { + throw "Shouldn't be able to read this!"; +} diff --git a/dom/workers/test/WorkerTest_subworker.js b/dom/workers/test/WorkerTest_subworker.js new file mode 100644 index 000000000..3e9242ed6 --- /dev/null +++ b/dom/workers/test/WorkerTest_subworker.js @@ -0,0 +1,43 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function(event) { + let chromeURL = event.data.replace("test_chromeWorkerJSM.xul", + "WorkerTest_badworker.js"); + + let mochitestURL = event.data.replace("test_chromeWorkerJSM.xul", + "WorkerTest_badworker.js") + .replace("chrome://mochitests/content/chrome", + "http://mochi.test:8888/tests"); + + // We should be able to XHR to anything we want, including a chrome URL. + let xhr = new XMLHttpRequest(); + xhr.open("GET", mochitestURL, false); + xhr.send(); + + if (!xhr.responseText) { + throw "Can't load script file via XHR!"; + } + + // We shouldn't be able to make a ChromeWorker to a non-chrome URL. + let worker = new ChromeWorker(mochitestURL); + worker.onmessage = function(event) { + throw event.data; + }; + worker.onerror = function(event) { + event.preventDefault(); + + // And we shouldn't be able to make a regular Worker to a non-chrome URL. + worker = new Worker(mochitestURL); + worker.onmessage = function(event) { + throw event.data; + }; + worker.onerror = function(event) { + event.preventDefault(); + postMessage("Done"); + }; + worker.postMessage("Hi"); + }; + worker.postMessage("Hi"); +}; diff --git a/dom/workers/test/WorkerTest_worker.js b/dom/workers/test/WorkerTest_worker.js new file mode 100644 index 000000000..445408f8f --- /dev/null +++ b/dom/workers/test/WorkerTest_worker.js @@ -0,0 +1,11 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function(event) { + let worker = new ChromeWorker("WorkerTest_subworker.js"); + worker.onmessage = function(event) { + postMessage(event.data); + } + worker.postMessage(event.data); +} diff --git a/dom/workers/test/atob_worker.js b/dom/workers/test/atob_worker.js new file mode 100644 index 000000000..680ca904b --- /dev/null +++ b/dom/workers/test/atob_worker.js @@ -0,0 +1,46 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +var data = [ -1, 0, 1, 1.5, /* null ,*/ undefined, true, false, "foo", + "123456789012345", "1234567890123456", "12345678901234567"]; + +var str = ""; +for (var i = 0; i < 30; i++) { + data.push(str); + str += i % 2 ? "b" : "a"; +} + +onmessage = function(event) { + data.forEach(function(string) { + var encoded = btoa(string); + postMessage({ type: "btoa", value: encoded }); + postMessage({ type: "atob", value: atob(encoded) }); + }); + + var threw; + try { + atob(); + } + catch(e) { + threw = true; + } + + if (!threw) { + throw "atob didn't throw when called without an argument!"; + } + threw = false; + + try { + btoa(); + } + catch(e) { + threw = true; + } + + if (!threw) { + throw "btoa didn't throw when called without an argument!"; + } + + postMessage({ type: "done" }); +} diff --git a/dom/workers/test/browser.ini b/dom/workers/test/browser.ini new file mode 100644 index 000000000..ae1e27d13 --- /dev/null +++ b/dom/workers/test/browser.ini @@ -0,0 +1,12 @@ +[DEFAULT] +support-files = + bug1047663_tab.html + bug1047663_worker.sjs + frame_script.js + head.js + !/dom/base/test/file_empty.html + !/dom/base/test/file_bug945152.jar + +[browser_bug1047663.js] +[browser_bug1104623.js] +run-if = buildapp == 'browser' diff --git a/dom/workers/test/browser_bug1047663.js b/dom/workers/test/browser_bug1047663.js new file mode 100644 index 000000000..8fa2c5358 --- /dev/null +++ b/dom/workers/test/browser_bug1047663.js @@ -0,0 +1,50 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const TAB_URL = EXAMPLE_URL + "bug1047663_tab.html"; +const WORKER_URL = EXAMPLE_URL + "bug1047663_worker.sjs"; + +function test() { + waitForExplicitFinish(); + + Task.spawn(function* () { + let tab = yield addTab(TAB_URL); + + // Create a worker. Post a message to it, and check the reply. Since the + // server side JavaScript file returns the first source for the first + // request, the reply should be "one". If the reply is correct, terminate + // the worker. + yield createWorkerInTab(tab, WORKER_URL); + let message = yield postMessageToWorkerInTab(tab, WORKER_URL, "ping"); + is(message, "one"); + yield terminateWorkerInTab(tab, WORKER_URL); + + // Create a second worker with the same URL. Post a message to it, and check + // the reply. The server side JavaScript file returns the second source for + // all subsequent requests, but since the cache is still enabled, the reply + // should still be "one". If the reply is correct, terminate the worker. + yield createWorkerInTab(tab, WORKER_URL); + message = yield postMessageToWorkerInTab(tab, WORKER_URL, "ping"); + is(message, "one"); + yield terminateWorkerInTab(tab, WORKER_URL); + + // Disable the cache in this tab. This should also disable the cache for all + // workers in this tab. + yield disableCacheInTab(tab); + + // Create a third worker with the same URL. Post a message to it, and check + // the reply. Since the server side JavaScript file returns the second + // source for all subsequent requests, and the cache is now disabled, the + // reply should now be "two". If the reply is correct, terminate the worker. + yield createWorkerInTab(tab, WORKER_URL); + message = yield postMessageToWorkerInTab(tab, WORKER_URL, "ping"); + is(message, "two"); + yield terminateWorkerInTab(tab, WORKER_URL); + + removeTab(tab); + + finish(); + }); +} diff --git a/dom/workers/test/browser_bug1104623.js b/dom/workers/test/browser_bug1104623.js new file mode 100644 index 000000000..64a8eeb9c --- /dev/null +++ b/dom/workers/test/browser_bug1104623.js @@ -0,0 +1,53 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function whenBrowserLoaded(aBrowser, aCallback) { + aBrowser.addEventListener("load", function onLoad(event) { + if (event.target == aBrowser.contentDocument) { + aBrowser.removeEventListener("load", onLoad, true); + executeSoon(aCallback); + } + }, true); +} + +function test() { + waitForExplicitFinish(); + + let testURL = "chrome://mochitests/content/chrome/dom/base/test/file_empty.html"; + + let tab = gBrowser.addTab(testURL); + gBrowser.selectedTab = tab; + + whenBrowserLoaded(tab.linkedBrowser, function() { + let doc = tab.linkedBrowser.contentDocument; + let contentWin = tab.linkedBrowser.contentWindow; + + let blob = new contentWin.Blob(['onmessage = function() { postMessage(true); }']); + ok(blob, "Blob has been created"); + + let blobURL = contentWin.URL.createObjectURL(blob); + ok(blobURL, "Blob URL has been created"); + + let worker = new contentWin.Worker(blobURL); + ok(worker, "Worker has been created"); + + worker.onerror = function(error) { + ok(false, "Worker.onerror:" + error.message); + worker.terminate(); + contentWin.URL.revokeObjectURL(blob); + gBrowser.removeTab(tab); + executeSoon(finish); + } + + worker.onmessage = function() { + ok(true, "Worker.onmessage"); + worker.terminate(); + contentWin.URL.revokeObjectURL(blob); + gBrowser.removeTab(tab); + executeSoon(finish); + } + + worker.postMessage(true); + }); +} diff --git a/dom/workers/test/bug1014466_data1.txt b/dom/workers/test/bug1014466_data1.txt new file mode 100644 index 000000000..a32a4347a --- /dev/null +++ b/dom/workers/test/bug1014466_data1.txt @@ -0,0 +1 @@ +1234567890 diff --git a/dom/workers/test/bug1014466_data2.txt b/dom/workers/test/bug1014466_data2.txt new file mode 100644 index 000000000..4d40154ce --- /dev/null +++ b/dom/workers/test/bug1014466_data2.txt @@ -0,0 +1 @@ +ABCDEFGH diff --git a/dom/workers/test/bug1014466_worker.js b/dom/workers/test/bug1014466_worker.js new file mode 100644 index 000000000..beb324f0f --- /dev/null +++ b/dom/workers/test/bug1014466_worker.js @@ -0,0 +1,64 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function ok(a, msg) { + postMessage({type: "status", status: !!a, msg: msg }); +} + +onmessage = function(event) { + + function getResponse(url) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", url, false); + xhr.send(); + return xhr.responseText; + } + + const testFile1 = "bug1014466_data1.txt"; + const testFile2 = "bug1014466_data2.txt"; + const testData1 = getResponse(testFile1); + const testData2 = getResponse(testFile2); + + var response_count = 0; + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function() { + if (xhr.readyState == xhr.DONE && xhr.status == 200) { + response_count++; + switch (response_count) { + case 1: + ok(xhr.responseText == testData1, "Check data 1"); + test_data2(); + break; + case 2: + ok(xhr.responseText == testData2, "Check data 2"); + postMessage({type: "finish" }); + break; + default: + ok(false, "Unexpected response received"); + postMessage({type: "finish" }); + break; + } + } + } + xhr.onerror = function(event) { + ok(false, "Got an error event: " + event); + postMessage({type: "finish" }); + } + + function test_data1() { + xhr.open("GET", testFile1, true); + xhr.responseType = "text"; + xhr.send(); + } + + function test_data2() { + xhr.abort(); + xhr.open("GET", testFile2, true); + xhr.responseType = "text"; + xhr.send(); + } + + test_data1(); +} diff --git a/dom/workers/test/bug1020226_frame.html b/dom/workers/test/bug1020226_frame.html new file mode 100644 index 000000000..7f810d893 --- /dev/null +++ b/dom/workers/test/bug1020226_frame.html @@ -0,0 +1,20 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1020226 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1020226</title> +</head> +<body> + +<script type="application/javascript"> + var worker = new Worker("bug1020226_worker.js"); + worker.onmessage = function(e) { + window.parent.postMessage("loaded", "*"); + } +</script> +</body> +</html> + diff --git a/dom/workers/test/bug1020226_worker.js b/dom/workers/test/bug1020226_worker.js new file mode 100644 index 000000000..868b1a2f2 --- /dev/null +++ b/dom/workers/test/bug1020226_worker.js @@ -0,0 +1,12 @@ +var p = new Promise(function(resolve, reject) { + // This causes a runnable to be queued. + reject(new Error()); + postMessage("loaded"); + + // This prevents that runnable from running until the window calls terminate(), + // at which point the worker goes into the Canceling state and then an + // HoldWorker() is attempted, which fails, which used to result in + // multiple calls to the error reporter, one after the worker's context had + // been GCed. + while (true); +}); diff --git a/dom/workers/test/bug1047663_tab.html b/dom/workers/test/bug1047663_tab.html new file mode 100644 index 000000000..62ab9be7d --- /dev/null +++ b/dom/workers/test/bug1047663_tab.html @@ -0,0 +1,8 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"/> + </head> + <body> + </body> +</html> diff --git a/dom/workers/test/bug1047663_worker.sjs b/dom/workers/test/bug1047663_worker.sjs new file mode 100644 index 000000000..a39bf4474 --- /dev/null +++ b/dom/workers/test/bug1047663_worker.sjs @@ -0,0 +1,40 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const WORKER_1 = ` + "use strict"; + + self.onmessage = function () { + postMessage("one"); + }; +`; + +const WORKER_2 = ` + "use strict"; + + self.onmessage = function () { + postMessage("two"); + }; +`; + +function handleRequest(request, response) { + let count = getState("count"); + if (count === "") { + count = "1"; + } + + // This header is necessary for the cache to trigger. + response.setHeader("Cache-control", "max-age=3600"); + + // If this is the first request, return the first source. + if (count === "1") { + response.write(WORKER_1); + setState("count", "2"); + } + // For all subsequent requests, return the second source. + else { + response.write(WORKER_2); + } +} diff --git a/dom/workers/test/bug1060621_worker.js b/dom/workers/test/bug1060621_worker.js new file mode 100644 index 000000000..a0fcd3f60 --- /dev/null +++ b/dom/workers/test/bug1060621_worker.js @@ -0,0 +1,2 @@ +navigator.foobar = 42; +postMessage('done'); diff --git a/dom/workers/test/bug1062920_worker.js b/dom/workers/test/bug1062920_worker.js new file mode 100644 index 000000000..d3df38870 --- /dev/null +++ b/dom/workers/test/bug1062920_worker.js @@ -0,0 +1,6 @@ +postMessage({ appCodeName: navigator.appCodeName, + appName: navigator.appName, + appVersion: navigator.appVersion, + platform: navigator.platform, + userAgent: navigator.userAgent, + product: navigator.product }); diff --git a/dom/workers/test/bug1063538_worker.js b/dom/workers/test/bug1063538_worker.js new file mode 100644 index 000000000..dc53dd289 --- /dev/null +++ b/dom/workers/test/bug1063538_worker.js @@ -0,0 +1,25 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var gJar = "jar:http://example.org/tests/dom/base/test/file_bug945152.jar!/data_big.txt"; +var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true}); +var progressFired = false; + +xhr.onloadend = function(e) { + postMessage({type: 'finish', progressFired: progressFired }); + self.close(); +}; + +xhr.onprogress = function(e) { + if (e.loaded > 0) { + progressFired = true; + xhr.abort(); + } +}; + +onmessage = function(e) { + xhr.open("GET", gJar, true); + xhr.send(); +} diff --git a/dom/workers/test/bug1104064_worker.js b/dom/workers/test/bug1104064_worker.js new file mode 100644 index 000000000..5e627c86f --- /dev/null +++ b/dom/workers/test/bug1104064_worker.js @@ -0,0 +1,10 @@ +onmessage = function() { + var counter = 0; + var id = setInterval(function() { + ++counter; + if (counter == 2) { + clearInterval(id); + postMessage('done'); + } + }, 0); +} diff --git a/dom/workers/test/bug1132395_sharedWorker.js b/dom/workers/test/bug1132395_sharedWorker.js new file mode 100644 index 000000000..988eea106 --- /dev/null +++ b/dom/workers/test/bug1132395_sharedWorker.js @@ -0,0 +1,12 @@ +dump("SW created\n"); +onconnect = function(evt) { + dump("SW onconnect\n"); + evt.ports[0].onmessage = function(e) { + dump("SW onmessage\n"); + var blob = new Blob(['123'], { type: 'text/plain' }); + dump("SW blob created\n"); + var url = URL.createObjectURL(blob); + dump("SW url created: " + url + "\n"); + evt.ports[0].postMessage('alive \\o/'); + }; +} diff --git a/dom/workers/test/bug1132924_worker.js b/dom/workers/test/bug1132924_worker.js new file mode 100644 index 000000000..cabd40686 --- /dev/null +++ b/dom/workers/test/bug1132924_worker.js @@ -0,0 +1,10 @@ +onmessage = function() { + var a = new XMLHttpRequest(); + a.open('GET', 'empty.html', false); + a.onreadystatechange = function() { + if (a.readyState == 4) { + postMessage(a.response); + } + } + a.send(null); +} diff --git a/dom/workers/test/bug978260_worker.js b/dom/workers/test/bug978260_worker.js new file mode 100644 index 000000000..126b9c901 --- /dev/null +++ b/dom/workers/test/bug978260_worker.js @@ -0,0 +1,7 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// Tell the main thread we're here. +postMessage("loaded"); diff --git a/dom/workers/test/bug998474_worker.js b/dom/workers/test/bug998474_worker.js new file mode 100644 index 000000000..a14ed2810 --- /dev/null +++ b/dom/workers/test/bug998474_worker.js @@ -0,0 +1,6 @@ +self.addEventListener("connect", function(e) { + var port = e.ports[0]; + port.onmessage = function(e) { + port.postMessage(eval(e.data)); + }; +}); diff --git a/dom/workers/test/chrome.ini b/dom/workers/test/chrome.ini new file mode 100644 index 000000000..c01a20b2b --- /dev/null +++ b/dom/workers/test/chrome.ini @@ -0,0 +1,86 @@ +[DEFAULT] +skip-if = os == 'android' +support-files = + WorkerDebugger.console_childWorker.js + WorkerDebugger.console_debugger.js + WorkerDebugger.console_worker.js + WorkerDebugger.initialize_childWorker.js + WorkerDebugger.initialize_debugger.js + WorkerDebugger.initialize_worker.js + WorkerDebugger.postMessage_childWorker.js + WorkerDebugger.postMessage_debugger.js + WorkerDebugger.postMessage_worker.js + WorkerDebuggerGlobalScope.createSandbox_debugger.js + WorkerDebuggerGlobalScope.createSandbox_sandbox.js + WorkerDebuggerGlobalScope.createSandbox_worker.js + WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js + WorkerDebuggerGlobalScope.enterEventLoop_debugger.js + WorkerDebuggerGlobalScope.enterEventLoop_worker.js + WorkerDebuggerGlobalScope.reportError_childWorker.js + WorkerDebuggerGlobalScope.reportError_debugger.js + WorkerDebuggerGlobalScope.reportError_worker.js + WorkerDebuggerGlobalScope.setImmediate_debugger.js + WorkerDebuggerGlobalScope.setImmediate_worker.js + WorkerDebuggerManager_childWorker.js + WorkerDebuggerManager_worker.js + WorkerDebugger_childWorker.js + WorkerDebugger_frozen_iframe1.html + WorkerDebugger_frozen_iframe2.html + WorkerDebugger_frozen_worker1.js + WorkerDebugger_frozen_worker2.js + WorkerDebugger_promise_debugger.js + WorkerDebugger_promise_worker.js + WorkerDebugger_sharedWorker.js + WorkerDebugger_suspended_debugger.js + WorkerDebugger_suspended_worker.js + WorkerDebugger_worker.js + WorkerTest.jsm + WorkerTest_subworker.js + WorkerTest_worker.js + bug1062920_worker.js + chromeWorker_subworker.js + chromeWorker_worker.js + dom_worker_helper.js + empty.html + fileBlobSubWorker_worker.js + fileBlob_worker.js + filePosting_worker.js + fileReadSlice_worker.js + fileReaderSyncErrors_worker.js + fileReaderSync_worker.js + fileSlice_worker.js + fileSubWorker_worker.js + file_worker.js + sharedWorker_privateBrowsing.js + workersDisabled_worker.js + +[test_WorkerDebugger.initialize.xul] +[test_WorkerDebugger.postMessage.xul] +[test_WorkerDebugger.xul] +[test_WorkerDebuggerGlobalScope.createSandbox.xul] +[test_WorkerDebuggerGlobalScope.enterEventLoop.xul] +[test_WorkerDebuggerGlobalScope.reportError.xul] +skip-if = (os == 'linux') # Bug 1244697 +[test_WorkerDebuggerGlobalScope.setImmediate.xul] +[test_WorkerDebuggerManager.xul] +skip-if = (os == 'linux') # Bug 1244409 +[test_WorkerDebugger_console.xul] +[test_WorkerDebugger_frozen.xul] +[test_WorkerDebugger_promise.xul] +[test_WorkerDebugger_suspended.xul] +[test_chromeWorker.xul] +[test_chromeWorkerJSM.xul] +[test_extension.xul] +[test_extensionBootstrap.xul] +[test_file.xul] +[test_fileBlobPosting.xul] +[test_fileBlobSubWorker.xul] +[test_filePosting.xul] +[test_fileReadSlice.xul] +[test_fileReaderSync.xul] +[test_fileReaderSyncErrors.xul] +[test_fileSlice.xul] +[test_fileSubWorker.xul] +[test_workersDisabled.xul] +[test_bug1062920.xul] +[test_sharedWorker_privateBrowsing.html] diff --git a/dom/workers/test/chromeWorker_subworker.js b/dom/workers/test/chromeWorker_subworker.js new file mode 100644 index 000000000..5ad1a9923 --- /dev/null +++ b/dom/workers/test/chromeWorker_subworker.js @@ -0,0 +1,7 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function(event) { + postMessage("Done!"); +}; diff --git a/dom/workers/test/chromeWorker_worker.js b/dom/workers/test/chromeWorker_worker.js new file mode 100644 index 000000000..5738a9ae5 --- /dev/null +++ b/dom/workers/test/chromeWorker_worker.js @@ -0,0 +1,20 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +if (!("ctypes" in self)) { + throw "No ctypes!"; +} + +// Go ahead and verify that the ctypes lazy getter actually works. +if (ctypes.toString() != "[object ctypes]") { + throw "Bad ctypes object: " + ctypes.toString(); +} + +onmessage = function(event) { + let worker = new ChromeWorker("chromeWorker_subworker.js"); + worker.onmessage = function(event) { + postMessage(event.data); + } + worker.postMessage(event.data); +} diff --git a/dom/workers/test/clearTimeouts_worker.js b/dom/workers/test/clearTimeouts_worker.js new file mode 100644 index 000000000..b471515b3 --- /dev/null +++ b/dom/workers/test/clearTimeouts_worker.js @@ -0,0 +1,12 @@ +var count = 0; +function timerFunction() { + if (++count == 30) { + close(); + postMessage("ready"); + while (true) { } + } +} + +for (var i = 0; i < 10; i++) { + setInterval(timerFunction, 500); +} diff --git a/dom/workers/test/consoleReplaceable_worker.js b/dom/workers/test/consoleReplaceable_worker.js new file mode 100644 index 000000000..aaf104af1 --- /dev/null +++ b/dom/workers/test/consoleReplaceable_worker.js @@ -0,0 +1,16 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +onmessage = function(event) { + postMessage({event: 'console exists', status: !!console, last : false}); + var logCalled = false; + console.log = function() { + logCalled = true; + } + console.log("foo"); + postMessage({event: 'console.log is replaceable', status: logCalled, last: false}); + console = 42; + postMessage({event: 'console is replaceable', status: console === 42, last : true}); +} diff --git a/dom/workers/test/console_worker.js b/dom/workers/test/console_worker.js new file mode 100644 index 000000000..6b5f9d8a1 --- /dev/null +++ b/dom/workers/test/console_worker.js @@ -0,0 +1,109 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +onmessage = function(event) { + // TEST: does console exist? + postMessage({event: 'console exists', status: !!console, last : false}); + + postMessage({event: 'console is the same object', status: console === console, last: false}); + + postMessage({event: 'trace without function', status: true, last : false}); + + for (var i = 0; i < 10; ++i) { + console.log(i, i, i); + } + + function trace1() { + function trace2() { + function trace3() { + console.trace("trace " + i); + } + trace3(); + } + trace2(); + } + trace1(); + + foobar585956c = function(a) { + console.trace(); + return a+"c"; + }; + + function foobar585956b(a) { + return foobar585956c(a+"b"); + } + + function foobar585956a(omg) { + return foobar585956b(omg + "a"); + } + + function foobar646025(omg) { + console.log(omg, "o", "d"); + } + + function startTimer(timer) { + console.time(timer); + } + + function stopTimer(timer) { + console.timeEnd(timer); + } + + function timeStamp(label) { + console.timeStamp(label); + } + + function testGroups() { + console.groupCollapsed("a", "group"); + console.group("b", "group"); + console.groupEnd("b", "group"); + } + + foobar585956a('omg'); + foobar646025('omg'); + timeStamp(); + timeStamp('foo'); + testGroups(); + startTimer('foo'); + setTimeout(function() { + stopTimer('foo'); + nextSteps(event); + }, 10); +} + +function nextSteps(event) { + + function namelessTimer() { + console.time(); + console.timeEnd(); + } + + namelessTimer(); + + var str = "Test Message." + console.log(str); + console.info(str); + console.warn(str); + console.error(str); + console.exception(str); + console.assert(true, str); + console.assert(false, str); + console.profile(str); + console.profileEnd(str); + console.timeStamp(); + console.clear(); + postMessage({event: '4 messages', status: true, last : false}); + + // Recursive: + if (event.data == true) { + var worker = new Worker('console_worker.js'); + worker.onmessage = function(event) { + postMessage(event.data); + } + worker.postMessage(false); + } else { + postMessage({event: 'bye bye', status: true, last : true}); + } +} diff --git a/dom/workers/test/content_worker.js b/dom/workers/test/content_worker.js new file mode 100644 index 000000000..7e092ec8c --- /dev/null +++ b/dom/workers/test/content_worker.js @@ -0,0 +1,12 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +var props = { + 'ctypes': 1, + 'OS': 1 +}; +for (var prop in props) { + postMessage({ "prop": prop, "value": self[prop] }); +} +postMessage({ "testfinished": 1 }); diff --git a/dom/workers/test/crashtests/1153636.html b/dom/workers/test/crashtests/1153636.html new file mode 100644 index 000000000..6ad0d550f --- /dev/null +++ b/dom/workers/test/crashtests/1153636.html @@ -0,0 +1,5 @@ +<script> + +new Worker("data:text/javascript;charset=UTF-8,self.addEventListener('',function(){},false);"); + +</script> diff --git a/dom/workers/test/crashtests/1158031.html b/dom/workers/test/crashtests/1158031.html new file mode 100644 index 000000000..6d896bc46 --- /dev/null +++ b/dom/workers/test/crashtests/1158031.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<script> + +function boom() +{ + var w = new Worker("data:text/javascript;charset=UTF-8,"); + w.postMessage(new Blob([], {})); +} + +</script> +<body onload="boom();"></body> diff --git a/dom/workers/test/crashtests/1228456.html b/dom/workers/test/crashtests/1228456.html new file mode 100644 index 000000000..6d1f0f0a7 --- /dev/null +++ b/dom/workers/test/crashtests/1228456.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<script> + +function boom() +{ + var w; + for (var i = 0; i < 99; ++i) { + w = new SharedWorker("data:text/javascript;charset=UTF-8," + encodeURIComponent(i + ";")); + } + w.port.postMessage(""); +} + +</script> +<body onload="boom();"></body> diff --git a/dom/workers/test/crashtests/779707.html b/dom/workers/test/crashtests/779707.html new file mode 100644 index 000000000..97a8113da --- /dev/null +++ b/dom/workers/test/crashtests/779707.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<html> +<head> +<script> + +function boom() +{ + var x = new XMLHttpRequest(); + x.open('GET', "data:text/plain,2", false); + x.send(); + + new Worker("data:text/javascript,3"); +} + +</script> +</head> + +<body onload="boom();"></body> +</html> diff --git a/dom/workers/test/crashtests/943516.html b/dom/workers/test/crashtests/943516.html new file mode 100644 index 000000000..5f4667850 --- /dev/null +++ b/dom/workers/test/crashtests/943516.html @@ -0,0 +1,10 @@ +<!-- +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<script> +// Using a DOM bindings object as a weak map key should not crash when attempting to +// call the preserve wrapper callback. +new Worker("data:text/javascript;charset=UTF-8,(new WeakMap()).set(self, 0);") +</script> diff --git a/dom/workers/test/crashtests/crashtests.list b/dom/workers/test/crashtests/crashtests.list new file mode 100644 index 000000000..a7518d3c2 --- /dev/null +++ b/dom/workers/test/crashtests/crashtests.list @@ -0,0 +1,5 @@ +load 779707.html +load 943516.html +load 1153636.html +load 1158031.html +load 1228456.html diff --git a/dom/workers/test/csp_worker.js b/dom/workers/test/csp_worker.js new file mode 100644 index 000000000..63b3adeaf --- /dev/null +++ b/dom/workers/test/csp_worker.js @@ -0,0 +1,28 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function(event) { + if (event.data.do == "eval") { + var res; + try { + res = eval("40+2"); + } + catch(ex) { + res = ex+""; + } + postMessage(res); + } + else if (event.data.do == "nest") { + var worker = new Worker(event.data.uri); + if (--event.data.level) { + worker.postMessage(event.data); + } + else { + worker.postMessage({ do: "eval" }); + } + worker.onmessage = (e) => { + postMessage(e.data); + } + } +} diff --git a/dom/workers/test/csp_worker.js^headers^ b/dom/workers/test/csp_worker.js^headers^ new file mode 100644 index 000000000..7b835bf2a --- /dev/null +++ b/dom/workers/test/csp_worker.js^headers^ @@ -0,0 +1 @@ +Content-Security-Policy: default-src 'self' blob: ; script-src 'unsafe-eval' diff --git a/dom/workers/test/dom_worker_helper.js b/dom/workers/test/dom_worker_helper.js new file mode 100644 index 000000000..52e802ac7 --- /dev/null +++ b/dom/workers/test/dom_worker_helper.js @@ -0,0 +1,176 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +Cu.import("resource://gre/modules/AddonManager.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +const wdm = Cc["@mozilla.org/dom/workers/workerdebuggermanager;1"]. + getService(Ci.nsIWorkerDebuggerManager); + +const BASE_URL = "chrome://mochitests/content/chrome/dom/workers/test/"; + +var gRemainingTests = 0; + +function waitForWorkerFinish() { + if (gRemainingTests == 0) { + SimpleTest.waitForExplicitFinish(); + } + ++gRemainingTests; +} + +function finish() { + --gRemainingTests; + if (gRemainingTests == 0) { + SimpleTest.finish(); + } +} + +function assertThrows(fun, message) { + let throws = false; + try { + fun(); + } catch (e) { + throws = true; + } + ok(throws, message); +} + +function* generateDebuggers() { + let e = wdm.getWorkerDebuggerEnumerator(); + while (e.hasMoreElements()) { + let dbg = e.getNext().QueryInterface(Ci.nsIWorkerDebugger); + yield dbg; + } +} + +function findDebugger(url) { + for (let dbg of generateDebuggers()) { + if (dbg.url === url) { + return dbg; + } + } + return null; +} + +function waitForRegister(url, dbgUrl) { + return new Promise(function (resolve) { + wdm.addListener({ + onRegister: function (dbg) { + dump("FAK " + dbg.url + "\n"); + if (dbg.url !== url) { + return; + } + ok(true, "Debugger with url " + url + " should be registered."); + wdm.removeListener(this); + if (dbgUrl) { + info("Initializing worker debugger with url " + url + "."); + dbg.initialize(dbgUrl); + } + resolve(dbg); + } + }); + }); +} + +function waitForUnregister(url) { + return new Promise(function (resolve) { + wdm.addListener({ + onUnregister: function (dbg) { + if (dbg.url !== url) { + return; + } + ok(true, "Debugger with url " + url + " should be unregistered."); + wdm.removeListener(this); + resolve(); + } + }); + }); +} + +function waitForDebuggerClose(dbg) { + return new Promise(function (resolve) { + dbg.addListener({ + onClose: function () { + ok(true, "Debugger should be closed."); + dbg.removeListener(this); + resolve(); + } + }); + }); +} + +function waitForDebuggerError(dbg) { + return new Promise(function (resolve) { + dbg.addListener({ + onError: function (filename, lineno, message) { + dbg.removeListener(this); + resolve(new Error(message, filename, lineno)); + } + }); + }); +} + +function waitForDebuggerMessage(dbg, message) { + return new Promise(function (resolve) { + dbg.addListener({ + onMessage: function (message1) { + if (message !== message1) { + return; + } + ok(true, "Should receive " + message + " message from debugger."); + dbg.removeListener(this); + resolve(); + } + }); + }); +} + +function waitForWindowMessage(window, message) { + return new Promise(function (resolve) { + let onmessage = function (event) { + if (event.data !== event.data) { + return; + } + window.removeEventListener("message", onmessage, false); + resolve(); + }; + window.addEventListener("message", onmessage, false); + }); +} + +function waitForWorkerMessage(worker, message) { + return new Promise(function (resolve) { + worker.addEventListener("message", function onmessage(event) { + if (event.data !== message) { + return; + } + ok(true, "Should receive " + message + " message from worker."); + worker.removeEventListener("message", onmessage); + resolve(); + }); + }); +} + +function waitForMultiple(promises) { + return new Promise(function (resolve) { + let values = []; + for (let i = 0; i < promises.length; ++i) { + let index = i; + promises[i].then(function (value) { + is(index + 1, values.length + 1, + "Promise " + (values.length + 1) + " out of " + promises.length + + " should be resolved."); + values.push(value); + if (values.length === promises.length) { + resolve(values); + } + }); + } + }); +}; diff --git a/dom/workers/test/empty.html b/dom/workers/test/empty.html new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/dom/workers/test/empty.html diff --git a/dom/workers/test/errorPropagation_iframe.html b/dom/workers/test/errorPropagation_iframe.html new file mode 100644 index 000000000..c5f688487 --- /dev/null +++ b/dom/workers/test/errorPropagation_iframe.html @@ -0,0 +1,55 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> + <meta charset="utf-8"> + <body> + <script type="text/javascript"> + var worker; + + function start(workerCount, messageCallback) { + var seenWindowError; + window.onerror = function(message, filename, lineno) { + if (!seenWindowError) { + seenWindowError = true; + messageCallback({ + type: "window", + data: { message: message, filename: filename, lineno: lineno } + }); + return true; + } + }; + + worker = new Worker("errorPropagation_worker.js"); + + worker.onmessage = function(event) { + messageCallback(event.data); + }; + + var seenWorkerError; + worker.onerror = function(event) { + if (!seenWorkerError) { + seenWorkerError = true; + messageCallback({ + type: "worker", + data: { + message: event.message, + filename: event.filename, + lineno: event.lineno + } + }); + event.preventDefault(); + } + }; + + worker.postMessage(workerCount); + } + + function stop() { + worker.terminate(); + } + </script> + </body> +</html> diff --git a/dom/workers/test/errorPropagation_worker.js b/dom/workers/test/errorPropagation_worker.js new file mode 100644 index 000000000..84f5916e0 --- /dev/null +++ b/dom/workers/test/errorPropagation_worker.js @@ -0,0 +1,50 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +var seenScopeError; +onerror = function(message, filename, lineno) { + if (!seenScopeError) { + seenScopeError = true; + postMessage({ + type: "scope", + data: { message: message, filename: filename, lineno: lineno } + }); + return true; + } +}; + +onmessage = function(event) { + var workerId = parseInt(event.data); + + if (workerId > 1) { + var worker = new Worker("errorPropagation_worker.js"); + + worker.onmessage = function(event) { + postMessage(event.data); + }; + + var seenWorkerError; + worker.onerror = function(event) { + if (!seenWorkerError) { + seenWorkerError = true; + postMessage({ + type: "worker", + data: { + message: event.message, + filename: event.filename, + lineno: event.lineno + } + }); + event.preventDefault(); + } + }; + + worker.postMessage(workerId - 1); + return; + } + + var interval = setInterval(function() { + throw new Error("expectedError"); + }, 100); +}; diff --git a/dom/workers/test/errorwarning_worker.js b/dom/workers/test/errorwarning_worker.js new file mode 100644 index 000000000..1c372104c --- /dev/null +++ b/dom/workers/test/errorwarning_worker.js @@ -0,0 +1,42 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function errorHandler() { + postMessage({ type: 'error' }); +} + +onmessage = function(event) { + if (event.data.errors) { + try { + // This is an error: + postMessage({ type: 'ignore', value: b.aaa }); + } catch(e) { + errorHandler(); + } + } else { + var a = {}; + // This is a warning: + postMessage({ type: 'ignore', value: a.foo }); + } + + if (event.data.loop != 0) { + var worker = new Worker('errorwarning_worker.js'); + worker.onerror = errorHandler; + worker.postMessage({ loop: event.data.loop - 1, errors: event.data.errors }); + + worker.onmessage = function(e) { + postMessage(e.data); + } + + } else { + postMessage({ type: 'finish' }); + } +} + +onerror = errorHandler; +onerror = onerror; +if (!onerror || onerror != onerror) { + throw "onerror wasn't set properly"; +} diff --git a/dom/workers/test/eventDispatch_worker.js b/dom/workers/test/eventDispatch_worker.js new file mode 100644 index 000000000..915f60c93 --- /dev/null +++ b/dom/workers/test/eventDispatch_worker.js @@ -0,0 +1,67 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +const fakeEventType = "foo"; + +function testEventTarget(event) { + if (event.target !== self) { + throw new Error("Event has a bad target!"); + } + if (event.currentTarget) { + throw new Error("Event has a bad currentTarget!"); + } + postMessage(event.data); +} + +addEventListener(fakeEventType, function(event) { + throw new Error("Trusted event listener received untrusted event!"); +}, false, false); + +addEventListener(fakeEventType, function(event) { + if (event.target !== self || event.currentTarget !== self) { + throw new Error("Fake event has bad target!"); + } + if (event.isTrusted) { + throw new Error("Event should be untrusted!"); + } + event.stopImmediatePropagation(); + postMessage(event.data); +}, false, true); + +addEventListener(fakeEventType, function(event) { + throw new Error("This shouldn't get called because of stopImmediatePropagation."); +}, false, true); + +var count = 0; +onmessage = function(event) { + if (event.target !== self || event.currentTarget !== self) { + throw new Error("Event has bad target!"); + } + + if (!count++) { + var exception; + try { + self.dispatchEvent(event); + } + catch(e) { + exception = e; + } + + if (!exception) { + throw new Error("Recursive dispatch didn't fail!"); + } + + event = new MessageEvent(fakeEventType, { bubbles: event.bubbles, + cancelable: event.cancelable, + data: event.data, + origin: "*", + source: null + }); + self.dispatchEvent(event); + + return; + } + + setTimeout(testEventTarget, 0, event); +}; diff --git a/dom/workers/test/extensions/bootstrap/bootstrap.js b/dom/workers/test/extensions/bootstrap/bootstrap.js new file mode 100644 index 000000000..acb0a204b --- /dev/null +++ b/dom/workers/test/extensions/bootstrap/bootstrap.js @@ -0,0 +1,141 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var Ci = Components.interfaces; +var Cu = Components.utils; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +function testForExpectedSymbols(stage, data) { + const expectedSymbols = [ "Worker", "ChromeWorker" ]; + for (var symbol of expectedSymbols) { + Services.prefs.setBoolPref("workertest.bootstrap." + stage + "." + symbol, + symbol in this); + } +} + +var gWorkerAndCallback = { + _ensureStarted: function() { + if (!this._worker) { + throw new Error("Not yet started!"); + } + }, + + start: function(data) { + if (!this._worker) { + this._worker = new Worker("chrome://workerbootstrap/content/worker.js"); + this._worker.onerror = function(event) { + Cu.reportError(event.message); + event.preventDefault(); + }; + } + }, + + stop: function() { + if (this._worker) { + this._worker.terminate(); + delete this._worker; + } + }, + + set callback(val) { + this._ensureStarted(); + var callback = val.QueryInterface(Ci.nsIObserver); + if (this._callback != callback) { + if (callback) { + this._worker.onmessage = function(event) { + callback.observe(this, event.type, event.data); + }; + this._worker.onerror = function(event) { + callback.observe(this, event.type, event.message); + event.preventDefault(); + }; + } + else { + this._worker.onmessage = null; + this._worker.onerror = null; + } + this._callback = callback; + } + }, + + get callback() { + return this._callback; + }, + + postMessage: function(data) { + this._ensureStarted(); + this._worker.postMessage(data); + }, + + terminate: function() { + this._ensureStarted(); + this._worker.terminate(); + delete this._callback; + } +}; + +function WorkerTestBootstrap() { +} +WorkerTestBootstrap.prototype = { + observe: function(subject, topic, data) { + + gWorkerAndCallback.callback = subject; + + switch (topic) { + case "postMessage": + gWorkerAndCallback.postMessage(data); + break; + + case "terminate": + gWorkerAndCallback.terminate(); + break; + + default: + throw new Error("Unknown worker command"); + } + }, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]) +}; + +var gFactory = { + register: function() { + var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); + + var classID = Components.ID("{36b5df0b-8dcf-4aa2-9c45-c51d871295f9}"); + var description = "WorkerTestBootstrap"; + var contractID = "@mozilla.org/test/workertestbootstrap;1"; + var factory = XPCOMUtils._getFactory(WorkerTestBootstrap); + + registrar.registerFactory(classID, description, contractID, factory); + + this.unregister = function() { + registrar.unregisterFactory(classID, factory); + delete this.unregister; + }; + } +}; + +function install(data, reason) { + testForExpectedSymbols("install"); +} + +function startup(data, reason) { + testForExpectedSymbols("startup"); + gFactory.register(); + gWorkerAndCallback.start(data); +} + +function shutdown(data, reason) { + testForExpectedSymbols("shutdown"); + gWorkerAndCallback.stop(); + gFactory.unregister(); +} + +function uninstall(data, reason) { + testForExpectedSymbols("uninstall"); +} diff --git a/dom/workers/test/extensions/bootstrap/install.rdf b/dom/workers/test/extensions/bootstrap/install.rdf new file mode 100644 index 000000000..fdd9638cd --- /dev/null +++ b/dom/workers/test/extensions/bootstrap/install.rdf @@ -0,0 +1,31 @@ +<?xml version="1.0"?> + +<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:em="http://www.mozilla.org/2004/em-rdf#"> + + <Description about="urn:mozilla:install-manifest"> + <em:name>WorkerTestBootstrap</em:name> + <em:description>Worker functions for use in testing.</em:description> + <em:creator>Mozilla</em:creator> + <em:version>2016.03.09</em:version> + <em:id>workerbootstrap-test@mozilla.org</em:id> + <em:type>2</em:type> + <em:bootstrap>true</em:bootstrap> + <em:targetApplication> + <Description> + <!-- Firefox --> + <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> + <em:minVersion>45.0</em:minVersion> + <em:maxVersion>*</em:maxVersion> + </Description> + </em:targetApplication> + <em:targetApplication> + <Description> + <!-- Fennec --> + <em:id>{aa3c5121-dab2-40e2-81ca-7ea25febc110}</em:id> + <em:minVersion>45.0</em:minVersion> + <em:maxVersion>*</em:maxVersion> + </Description> + </em:targetApplication> + </Description> +</RDF> diff --git a/dom/workers/test/extensions/bootstrap/jar.mn b/dom/workers/test/extensions/bootstrap/jar.mn new file mode 100644 index 000000000..a9c625103 --- /dev/null +++ b/dom/workers/test/extensions/bootstrap/jar.mn @@ -0,0 +1,3 @@ +workerbootstrap.jar: +% content workerbootstrap %content/ + content/worker.js (worker.js) diff --git a/dom/workers/test/extensions/bootstrap/moz.build b/dom/workers/test/extensions/bootstrap/moz.build new file mode 100644 index 000000000..aec5c249c --- /dev/null +++ b/dom/workers/test/extensions/bootstrap/moz.build @@ -0,0 +1,20 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +XPI_NAME = 'workerbootstrap' + +JAR_MANIFESTS += ['jar.mn'] +USE_EXTENSION_MANIFEST = True +NO_JS_MANIFEST = True + +FINAL_TARGET_FILES += [ + 'bootstrap.js', + 'install.rdf', +] + +TEST_HARNESS_FILES.testing.mochitest.extensions += [ + 'workerbootstrap-test@mozilla.org.xpi', +] diff --git a/dom/workers/test/extensions/bootstrap/worker.js b/dom/workers/test/extensions/bootstrap/worker.js new file mode 100644 index 000000000..7346fc142 --- /dev/null +++ b/dom/workers/test/extensions/bootstrap/worker.js @@ -0,0 +1,7 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function(event) { + postMessage(event.data); +} diff --git a/dom/workers/test/extensions/bootstrap/workerbootstrap-test@mozilla.org.xpi b/dom/workers/test/extensions/bootstrap/workerbootstrap-test@mozilla.org.xpi Binary files differnew file mode 100644 index 000000000..2dab975db --- /dev/null +++ b/dom/workers/test/extensions/bootstrap/workerbootstrap-test@mozilla.org.xpi diff --git a/dom/workers/test/extensions/moz.build b/dom/workers/test/extensions/moz.build new file mode 100644 index 000000000..51cf80fa2 --- /dev/null +++ b/dom/workers/test/extensions/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DIRS += ['bootstrap', 'traditional'] diff --git a/dom/workers/test/extensions/traditional/WorkerTest.js b/dom/workers/test/extensions/traditional/WorkerTest.js new file mode 100644 index 000000000..5890c0d4c --- /dev/null +++ b/dom/workers/test/extensions/traditional/WorkerTest.js @@ -0,0 +1,122 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +var gWorkerAndCallback = { + _worker: null, + _callback: null, + + _ensureStarted: function() { + if (!this._worker) { + throw new Error("Not yet started!"); + } + }, + + start: function() { + if (!this._worker) { + var worker = new Worker("chrome://worker/content/worker.js"); + worker.onerror = function(event) { + Cu.reportError(event.message); + event.preventDefault(); + }; + + this._worker = worker; + } + }, + + stop: function() { + if (this._worker) { + try { + this.terminate(); + } + catch(e) { + Cu.reportError(e); + } + this._worker = null; + } + }, + + set callback(val) { + this._ensureStarted(); + if (val) { + var callback = val.QueryInterface(Ci.nsIWorkerTestCallback); + if (this.callback != callback) { + this._worker.onmessage = function(event) { + callback.onmessage(event.data); + }; + this._worker.onerror = function(event) { + callback.onerror(event.message); + event.preventDefault(); + }; + this._callback = callback; + } + } + else { + this._worker.onmessage = null; + this._worker.onerror = null; + this._callback = null; + } + }, + + get callback() { + return this._callback; + }, + + postMessage: function(data) { + this._ensureStarted(); + this._worker.postMessage(data); + }, + + terminate: function() { + this._ensureStarted(); + this._worker.terminate(); + this.callback = null; + } +}; + +function WorkerTest() { +} +WorkerTest.prototype = { + observe: function(subject, topic, data) { + switch(topic) { + case "profile-after-change": + gWorkerAndCallback.start(); + Services.obs.addObserver(this, "profile-before-change", false); + break; + case "profile-before-change": + gWorkerAndCallback.stop(); + break; + default: + Cu.reportError("Unknown topic: " + topic); + } + }, + + set callback(val) { + gWorkerAndCallback.callback = val; + }, + + get callback() { + return gWorkerAndCallback.callback; + }, + + postMessage: function(message) { + gWorkerAndCallback.postMessage(message); + }, + + terminate: function() { + gWorkerAndCallback.terminate(); + }, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsIWorkerTest]), + classID: Components.ID("{3b52b935-551f-4606-ba4c-decc18b67bfd}") +}; + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WorkerTest]); diff --git a/dom/workers/test/extensions/traditional/WorkerTest.manifest b/dom/workers/test/extensions/traditional/WorkerTest.manifest new file mode 100644 index 000000000..a5a32fb06 --- /dev/null +++ b/dom/workers/test/extensions/traditional/WorkerTest.manifest @@ -0,0 +1,3 @@ +component {3b52b935-551f-4606-ba4c-decc18b67bfd} WorkerTest.js +contract @mozilla.org/test/workertest;1 {3b52b935-551f-4606-ba4c-decc18b67bfd} +category profile-after-change WorkerTest @mozilla.org/test/workertest;1 diff --git a/dom/workers/test/extensions/traditional/install.rdf b/dom/workers/test/extensions/traditional/install.rdf new file mode 100644 index 000000000..00fc0f441 --- /dev/null +++ b/dom/workers/test/extensions/traditional/install.rdf @@ -0,0 +1,30 @@ +<?xml version="1.0"?> + +<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:em="http://www.mozilla.org/2004/em-rdf#"> + + <Description about="urn:mozilla:install-manifest"> + <em:name>WorkerTest</em:name> + <em:description>Worker functions for use in testing.</em:description> + <em:creator>Mozilla</em:creator> + <em:version>2016.03.09</em:version> + <em:id>worker-test@mozilla.org</em:id> + <em:type>2</em:type> + <em:targetApplication> + <Description> + <!-- Firefox --> + <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> + <em:minVersion>45.0</em:minVersion> + <em:maxVersion>*</em:maxVersion> + </Description> + </em:targetApplication> + <em:targetApplication> + <Description> + <!-- Fennec --> + <em:id>{aa3c5121-dab2-40e2-81ca-7ea25febc110}</em:id> + <em:minVersion>45.0</em:minVersion> + <em:maxVersion>*</em:maxVersion> + </Description> + </em:targetApplication> + </Description> +</RDF> diff --git a/dom/workers/test/extensions/traditional/jar.mn b/dom/workers/test/extensions/traditional/jar.mn new file mode 100644 index 000000000..421ee55a0 --- /dev/null +++ b/dom/workers/test/extensions/traditional/jar.mn @@ -0,0 +1,3 @@ +worker.jar: +% content worker %content/ + content/worker.js (worker.js) diff --git a/dom/workers/test/extensions/traditional/moz.build b/dom/workers/test/extensions/traditional/moz.build new file mode 100644 index 000000000..d0920420d --- /dev/null +++ b/dom/workers/test/extensions/traditional/moz.build @@ -0,0 +1,30 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +XPIDL_SOURCES += [ + 'nsIWorkerTest.idl', +] + +XPIDL_MODULE = 'WorkerTest' + +EXTRA_COMPONENTS += [ + 'WorkerTest.js', + 'WorkerTest.manifest', +] + +XPI_NAME = 'worker' + +JAR_MANIFESTS += ['jar.mn'] +USE_EXTENSION_MANIFEST = True +NO_JS_MANIFEST = True + +FINAL_TARGET_FILES += [ + 'install.rdf', +] + +TEST_HARNESS_FILES.testing.mochitest.extensions += [ + 'worker-test@mozilla.org.xpi', +] diff --git a/dom/workers/test/extensions/traditional/nsIWorkerTest.idl b/dom/workers/test/extensions/traditional/nsIWorkerTest.idl new file mode 100644 index 000000000..32a952038 --- /dev/null +++ b/dom/workers/test/extensions/traditional/nsIWorkerTest.idl @@ -0,0 +1,23 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=40: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +[scriptable, uuid(10f8ebdf-1373-4640-9c34-53dee99f526f)] +interface nsIWorkerTestCallback : nsISupports +{ + void onmessage(in DOMString data); + void onerror(in DOMString data); +}; + +[scriptable, uuid(887a0614-a0f0-4c0e-80e0-cf31e6d4e286)] +interface nsIWorkerTest : nsISupports +{ + void postMessage(in DOMString data); + void terminate(); + + attribute nsIWorkerTestCallback callback; +}; diff --git a/dom/workers/test/extensions/traditional/worker-test@mozilla.org.xpi b/dom/workers/test/extensions/traditional/worker-test@mozilla.org.xpi Binary files differnew file mode 100644 index 000000000..8d2386894 --- /dev/null +++ b/dom/workers/test/extensions/traditional/worker-test@mozilla.org.xpi diff --git a/dom/workers/test/extensions/traditional/worker.js b/dom/workers/test/extensions/traditional/worker.js new file mode 100644 index 000000000..7346fc142 --- /dev/null +++ b/dom/workers/test/extensions/traditional/worker.js @@ -0,0 +1,7 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function(event) { + postMessage(event.data); +} diff --git a/dom/workers/test/fibonacci_worker.js b/dom/workers/test/fibonacci_worker.js new file mode 100644 index 000000000..fa35385e7 --- /dev/null +++ b/dom/workers/test/fibonacci_worker.js @@ -0,0 +1,24 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function(event) { + var n = parseInt(event.data); + + if (n < 2) { + postMessage(n); + return; + } + + var results = []; + for (var i = 1; i <= 2; i++) { + var worker = new Worker("fibonacci_worker.js"); + worker.onmessage = function(event) { + results.push(parseInt(event.data)); + if (results.length == 2) { + postMessage(results[0] + results[1]); + } + }; + worker.postMessage(n - i); + } +} diff --git a/dom/workers/test/fileBlobSubWorker_worker.js b/dom/workers/test/fileBlobSubWorker_worker.js new file mode 100644 index 000000000..2dc8cd12d --- /dev/null +++ b/dom/workers/test/fileBlobSubWorker_worker.js @@ -0,0 +1,17 @@ +/** + * Expects a blob. Returns an object containing the size, type. + * Used to test posting of blob from worker to worker. + */ +onmessage = function(event) { + var worker = new Worker("fileBlob_worker.js"); + + worker.postMessage(event.data); + + worker.onmessage = function(event) { + postMessage(event.data); + } + + worker.onerror = function(event) { + postMessage(undefined); + } +}; diff --git a/dom/workers/test/fileBlob_worker.js b/dom/workers/test/fileBlob_worker.js new file mode 100644 index 000000000..2f7a31714 --- /dev/null +++ b/dom/workers/test/fileBlob_worker.js @@ -0,0 +1,13 @@ +/** + * Expects a blob. Returns an object containing the size, type. + */ +onmessage = function(event) { + var file = event.data; + + var rtnObj = new Object(); + + rtnObj.size = file.size; + rtnObj.type = file.type; + + postMessage(rtnObj); +}; diff --git a/dom/workers/test/filePosting_worker.js b/dom/workers/test/filePosting_worker.js new file mode 100644 index 000000000..2a24b2a40 --- /dev/null +++ b/dom/workers/test/filePosting_worker.js @@ -0,0 +1,3 @@ +onmessage = function(event) { + postMessage(event.data); +}; diff --git a/dom/workers/test/fileReadSlice_worker.js b/dom/workers/test/fileReadSlice_worker.js new file mode 100644 index 000000000..2c10b7d76 --- /dev/null +++ b/dom/workers/test/fileReadSlice_worker.js @@ -0,0 +1,16 @@ +/** + * Expects an object containing a blob, a start index and an end index + * for slicing. Returns the contents of the blob read as text. + */ +onmessage = function(event) { + var blob = event.data.blob; + var start = event.data.start; + var end = event.data.end; + + var slicedBlob = blob.slice(start, end); + + var fileReader = new FileReaderSync(); + var text = fileReader.readAsText(slicedBlob); + + postMessage(text); +}; diff --git a/dom/workers/test/fileReaderSyncErrors_worker.js b/dom/workers/test/fileReaderSyncErrors_worker.js new file mode 100644 index 000000000..f79ecc6f0 --- /dev/null +++ b/dom/workers/test/fileReaderSyncErrors_worker.js @@ -0,0 +1,74 @@ +/** + * Delegates "is" evaluation back to main thread. + */ +function is(actual, expected, message) { + var rtnObj = new Object(); + rtnObj.actual = actual; + rtnObj.expected = expected; + rtnObj.message = message; + postMessage(rtnObj); +} + +/** + * Tries to write to property. + */ +function writeProperty(file, property) { + var oldValue = file[property]; + file[property] = -1; + is(file[property], oldValue, "Property " + property + " should be readonly."); +} + +/** + * Passes junk arguments to FileReaderSync methods and expects an exception to + * be thrown. + */ +function fileReaderJunkArgument(blob) { + var fileReader = new FileReaderSync(); + + try { + fileReader.readAsBinaryString(blob); + is(false, true, "Should have thrown an exception calling readAsBinaryString."); + } catch(ex) { + is(true, true, "Should have thrown an exception."); + } + + try { + fileReader.readAsDataURL(blob); + is(false, true, "Should have thrown an exception calling readAsDataURL."); + } catch(ex) { + is(true, true, "Should have thrown an exception."); + } + + try { + fileReader.readAsArrayBuffer(blob); + is(false, true, "Should have thrown an exception calling readAsArrayBuffer."); + } catch(ex) { + is(true, true, "Should have thrown an exception."); + } + + try { + fileReader.readAsText(blob); + is(false, true, "Should have thrown an exception calling readAsText."); + } catch(ex) { + is(true, true, "Should have thrown an exception."); + } +} + +onmessage = function(event) { + var file = event.data; + + // Test read only properties. + writeProperty(file, "size"); + writeProperty(file, "type"); + writeProperty(file, "name"); + + // Bad types. + fileReaderJunkArgument(undefined); + fileReaderJunkArgument(-1); + fileReaderJunkArgument(1); + fileReaderJunkArgument(new Object()); + fileReaderJunkArgument("hello"); + + // Post undefined to indicate that testing has finished. + postMessage(undefined); +}; diff --git a/dom/workers/test/fileReaderSync_worker.js b/dom/workers/test/fileReaderSync_worker.js new file mode 100644 index 000000000..4a37409d5 --- /dev/null +++ b/dom/workers/test/fileReaderSync_worker.js @@ -0,0 +1,25 @@ +var reader = new FileReaderSync(); + +/** + * Expects an object containing a file and an encoding then uses a + * FileReaderSync to read the file. Returns an object containing the + * file read a binary string, text, url and ArrayBuffer. + */ +onmessage = function(event) { + var file = event.data.file; + var encoding = event.data.encoding; + + var rtnObj = new Object(); + + if (encoding != undefined) { + rtnObj.text = reader.readAsText(file, encoding); + } else { + rtnObj.text = reader.readAsText(file); + } + + rtnObj.bin = reader.readAsBinaryString(file); + rtnObj.url = reader.readAsDataURL(file); + rtnObj.arrayBuffer = reader.readAsArrayBuffer(file); + + postMessage(rtnObj); +}; diff --git a/dom/workers/test/fileSlice_worker.js b/dom/workers/test/fileSlice_worker.js new file mode 100644 index 000000000..d0c6364e2 --- /dev/null +++ b/dom/workers/test/fileSlice_worker.js @@ -0,0 +1,27 @@ +/** + * Expects an object containing a blob, a start offset, an end offset + * and an optional content type to slice the blob. Returns an object + * containing the size and type of the sliced blob. + */ +onmessage = function(event) { + var blob = event.data.blob; + var start = event.data.start; + var end = event.data.end; + var contentType = event.data.contentType; + + var slicedBlob; + if (contentType == undefined && end == undefined) { + slicedBlob = blob.slice(start); + } else if (contentType == undefined) { + slicedBlob = blob.slice(start, end); + } else { + slicedBlob = blob.slice(start, end, contentType); + } + + var rtnObj = new Object(); + + rtnObj.size = slicedBlob.size; + rtnObj.type = slicedBlob.type; + + postMessage(rtnObj); +}; diff --git a/dom/workers/test/fileSubWorker_worker.js b/dom/workers/test/fileSubWorker_worker.js new file mode 100644 index 000000000..21fbc3454 --- /dev/null +++ b/dom/workers/test/fileSubWorker_worker.js @@ -0,0 +1,17 @@ +/** + * Expects a file. Returns an object containing the size, type, name and path + * using another worker. Used to test posting of file from worker to worker. + */ +onmessage = function(event) { + var worker = new Worker("file_worker.js"); + + worker.postMessage(event.data); + + worker.onmessage = function(event) { + postMessage(event.data); + } + + worker.onerror = function(event) { + postMessage(undefined); + } +}; diff --git a/dom/workers/test/file_bug1010784_worker.js b/dom/workers/test/file_bug1010784_worker.js new file mode 100644 index 000000000..239968069 --- /dev/null +++ b/dom/workers/test/file_bug1010784_worker.js @@ -0,0 +1,9 @@ +onmessage = function(event) { + var xhr = new XMLHttpRequest(); + + xhr.open("GET", event.data, false); + xhr.send(); + xhr.open("GET", event.data, false); + xhr.send(); + postMessage("done"); +} diff --git a/dom/workers/test/file_worker.js b/dom/workers/test/file_worker.js new file mode 100644 index 000000000..23233b8ac --- /dev/null +++ b/dom/workers/test/file_worker.js @@ -0,0 +1,16 @@ +/** + * Expects a file. Returns an object containing the size, type, name and path. + */ +onmessage = function(event) { + var file = event.data; + + var rtnObj = new Object(); + + rtnObj.size = file.size; + rtnObj.type = file.type; + rtnObj.name = file.name; + rtnObj.path = file.path; + rtnObj.lastModifiedDate = file.lastModifiedDate; + + postMessage(rtnObj); +}; diff --git a/dom/workers/test/fileapi_chromeScript.js b/dom/workers/test/fileapi_chromeScript.js new file mode 100644 index 000000000..614b556ed --- /dev/null +++ b/dom/workers/test/fileapi_chromeScript.js @@ -0,0 +1,29 @@ +var { classes: Cc, interfaces: Ci, utils: Cu } = Components; +Cu.importGlobalProperties(["File"]); + +var fileNum = 1; + +function createFileWithData(fileData) { + var willDelete = fileData === null; + var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties); + var testFile = dirSvc.get("ProfD", Ci.nsIFile); + testFile.append("fileAPItestfile" + fileNum); + fileNum++; + var outStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0o666, 0); + if (willDelete) { + fileData = "some irrelevant test data\n"; + } + outStream.write(fileData, fileData.length); + outStream.close(); + var domFile = File.createFromNsIFile(testFile); + if (willDelete) { + testFile.remove(/* recursive: */ false); + } + return domFile; +} + +addMessageListener("files.open", function (message) { + sendAsyncMessage("files.opened", message.map(createFileWithData)); +}); diff --git a/dom/workers/test/foreign.js b/dom/workers/test/foreign.js new file mode 100644 index 000000000..33c982fa8 --- /dev/null +++ b/dom/workers/test/foreign.js @@ -0,0 +1 @@ +response = "bad"; diff --git a/dom/workers/test/frame_script.js b/dom/workers/test/frame_script.js new file mode 100644 index 000000000..ffc384416 --- /dev/null +++ b/dom/workers/test/frame_script.js @@ -0,0 +1,72 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { interfaces: Ci } = Components; + +let workers = {}; + +let methods = { + /** + * Create a worker with the given `url` in this tab. + */ + createWorker: function (url) { + dump("Frame script: creating worker with url '" + url + "'\n"); + + workers[url] = new content.Worker(url); + return Promise.resolve(); + }, + + /** + * Terminate the worker with the given `url` in this tab. + */ + terminateWorker: function (url) { + dump("Frame script: terminating worker with url '" + url + "'\n"); + + workers[url].terminate(); + delete workers[url]; + return Promise.resolve(); + }, + + /** + * Post the given `message` to the worker with the given `url` in this tab. + */ + postMessageToWorker: function (url, message) { + dump("Frame script: posting message to worker with url '" + url + "'\n"); + + let worker = workers[url]; + worker.postMessage(message); + return new Promise(function (resolve) { + worker.onmessage = function (event) { + worker.onmessage = null; + resolve(event.data); + }; + }); + }, + + /** + * Disable the cache for this tab. + */ + disableCache: function () { + docShell.defaultLoadFlags = Ci.nsIRequest.LOAD_BYPASS_CACHE + | Ci.nsIRequest.INHIBIT_CACHING; + } +}; + +addMessageListener("jsonrpc", function (event) { + let { id, method, params } = event.data; + Promise.resolve().then(function () { + return methods[method].apply(undefined, params); + }).then(function (result) { + sendAsyncMessage("jsonrpc", { + id: id, + result: result + }); + }).catch(function (error) { + sendAsyncMessage("jsonrpc", { + id: id, + error: error.toString() + }); + }); +}); diff --git a/dom/workers/test/gtest/TestReadWrite.cpp b/dom/workers/test/gtest/TestReadWrite.cpp new file mode 100644 index 000000000..d59888e24 --- /dev/null +++ b/dom/workers/test/gtest/TestReadWrite.cpp @@ -0,0 +1,499 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gtest/gtest.h" +#include "mozilla/BasePrincipal.h" +#include "mozilla/dom/ServiceWorkerRegistrar.h" +#include "mozilla/dom/ServiceWorkerRegistrarTypes.h" +#include "mozilla/ipc/PBackgroundSharedTypes.h" + +#include "nsAppDirectoryServiceDefs.h" +#include "nsIFile.h" +#include "nsIOutputStream.h" +#include "nsNetUtil.h" +#include "nsPrintfCString.h" + +using namespace mozilla::dom; +using namespace mozilla::ipc; + +class ServiceWorkerRegistrarTest : public ServiceWorkerRegistrar +{ +public: + ServiceWorkerRegistrarTest() + { +#if defined(DEBUG) || !defined(RELEASE_OR_BETA) + nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, + getter_AddRefs(mProfileDir)); + MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); +#else + NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, + getter_AddRefs(mProfileDir)); +#endif + MOZ_DIAGNOSTIC_ASSERT(mProfileDir); + } + + nsresult TestReadData() { return ReadData(); } + nsresult TestWriteData() { return WriteData(); } + void TestDeleteData() { DeleteData(); } + + void TestRegisterServiceWorker(const ServiceWorkerRegistrationData& aData) + { + RegisterServiceWorkerInternal(aData); + } + + nsTArray<ServiceWorkerRegistrationData>& TestGetData() { return mData; } +}; + +already_AddRefed<nsIFile> +GetFile() +{ + nsCOMPtr<nsIFile> file; + nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, + getter_AddRefs(file)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE)); + return file.forget(); +} + +bool +CreateFile(const nsACString& aData) +{ + nsCOMPtr<nsIFile> file = GetFile(); + + nsCOMPtr<nsIOutputStream> stream; + nsresult rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), file); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + uint32_t count; + rv = stream->Write(aData.Data(), aData.Length(), &count); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + if (count != aData.Length()) { + return false; + } + + return true; +} + +TEST(ServiceWorkerRegistrar, TestNoFile) +{ + nsCOMPtr<nsIFile> file = GetFile(); + ASSERT_TRUE(file) << "GetFile must return a nsIFIle"; + + bool exists; + nsresult rv = file->Exists(&exists); + ASSERT_EQ(NS_OK, rv) << "nsIFile::Exists cannot fail"; + + if (exists) { + rv = file->Remove(false); + ASSERT_EQ(NS_OK, rv) << "nsIFile::Remove cannot fail"; + } + + RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest; + + rv = swr->TestReadData(); + ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail"; + + const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData(); + ASSERT_EQ((uint32_t)0, data.Length()) << "No data should be found in an empty file"; +} + +TEST(ServiceWorkerRegistrar, TestEmptyFile) +{ + ASSERT_TRUE(CreateFile(EmptyCString())) << "CreateFile should not fail"; + + RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest; + + nsresult rv = swr->TestReadData(); + ASSERT_NE(NS_OK, rv) << "ReadData() should fail if the file is empty"; + + const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData(); + ASSERT_EQ((uint32_t)0, data.Length()) << "No data should be found in an empty file"; +} + +TEST(ServiceWorkerRegistrar, TestRightVersionFile) +{ + ASSERT_TRUE(CreateFile(NS_LITERAL_CSTRING(SERVICEWORKERREGISTRAR_VERSION "\n"))) << "CreateFile should not fail"; + + RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest; + + nsresult rv = swr->TestReadData(); + ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail when the version is correct"; + + const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData(); + ASSERT_EQ((uint32_t)0, data.Length()) << "No data should be found in an empty file"; +} + +TEST(ServiceWorkerRegistrar, TestWrongVersionFile) +{ + ASSERT_TRUE(CreateFile(NS_LITERAL_CSTRING(SERVICEWORKERREGISTRAR_VERSION "bla\n"))) << "CreateFile should not fail"; + + RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest; + + nsresult rv = swr->TestReadData(); + ASSERT_NE(NS_OK, rv) << "ReadData() should fail when the version is not correct"; + + const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData(); + ASSERT_EQ((uint32_t)0, data.Length()) << "No data should be found in an empty file"; +} + +TEST(ServiceWorkerRegistrar, TestReadData) +{ + nsAutoCString buffer(SERVICEWORKERREGISTRAR_VERSION "\n"); + + buffer.Append("^appId=123&inBrowser=1\n"); + buffer.Append("scope 0\ncurrentWorkerURL 0\ncacheName 0\n"); + buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n"); + + buffer.Append("\n"); + buffer.Append("scope 1\ncurrentWorkerURL 1\ncacheName 1\n"); + buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n"); + + ASSERT_TRUE(CreateFile(buffer)) << "CreateFile should not fail"; + + RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest; + + nsresult rv = swr->TestReadData(); + ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail"; + + const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData(); + ASSERT_EQ((uint32_t)2, data.Length()) << "2 entries should be found"; + + const mozilla::ipc::PrincipalInfo& info0 = data[0].principal(); + ASSERT_EQ(info0.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content"; + const mozilla::ipc::ContentPrincipalInfo& cInfo0 = data[0].principal(); + + nsAutoCString suffix0; + cInfo0.attrs().CreateSuffix(suffix0); + + ASSERT_STREQ("^appId=123&inBrowser=1", suffix0.get()); + ASSERT_STREQ("scope 0", cInfo0.spec().get()); + ASSERT_STREQ("scope 0", data[0].scope().get()); + ASSERT_STREQ("currentWorkerURL 0", data[0].currentWorkerURL().get()); + ASSERT_STREQ("cacheName 0", NS_ConvertUTF16toUTF8(data[0].cacheName()).get()); + + const mozilla::ipc::PrincipalInfo& info1 = data[1].principal(); + ASSERT_EQ(info1.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content"; + const mozilla::ipc::ContentPrincipalInfo& cInfo1 = data[1].principal(); + + nsAutoCString suffix1; + cInfo1.attrs().CreateSuffix(suffix1); + + ASSERT_STREQ("", suffix1.get()); + ASSERT_STREQ("scope 1", cInfo1.spec().get()); + ASSERT_STREQ("scope 1", data[1].scope().get()); + ASSERT_STREQ("currentWorkerURL 1", data[1].currentWorkerURL().get()); + ASSERT_STREQ("cacheName 1", NS_ConvertUTF16toUTF8(data[1].cacheName()).get()); +} + +TEST(ServiceWorkerRegistrar, TestDeleteData) +{ + ASSERT_TRUE(CreateFile(NS_LITERAL_CSTRING("Foobar"))) << "CreateFile should not fail"; + + RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest; + + swr->TestDeleteData(); + + nsCOMPtr<nsIFile> file = GetFile(); + + bool exists; + nsresult rv = file->Exists(&exists); + ASSERT_EQ(NS_OK, rv) << "nsIFile::Exists cannot fail"; + + ASSERT_FALSE(exists) << "The file should not exist after a DeleteData()."; +} + +TEST(ServiceWorkerRegistrar, TestWriteData) +{ + { + RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest; + + for (int i = 0; i < 10; ++i) { + ServiceWorkerRegistrationData reg; + + reg.scope() = nsPrintfCString("scope write %d", i); + reg.currentWorkerURL() = nsPrintfCString("currentWorkerURL write %d", i); + reg.cacheName() = + NS_ConvertUTF8toUTF16(nsPrintfCString("cacheName write %d", i)); + + nsAutoCString spec; + spec.AppendPrintf("spec write %d", i); + reg.principal() = + mozilla::ipc::ContentPrincipalInfo(mozilla::PrincipalOriginAttributes(i, i % 2), + mozilla::void_t(), spec); + + swr->TestRegisterServiceWorker(reg); + } + + nsresult rv = swr->TestWriteData(); + ASSERT_EQ(NS_OK, rv) << "WriteData() should not fail"; + } + + RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest; + + nsresult rv = swr->TestReadData(); + ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail"; + + const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData(); + ASSERT_EQ((uint32_t)10, data.Length()) << "10 entries should be found"; + + for (int i = 0; i < 10; ++i) { + nsAutoCString test; + + ASSERT_EQ(data[i].principal().type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo); + const mozilla::ipc::ContentPrincipalInfo& cInfo = data[i].principal(); + + mozilla::PrincipalOriginAttributes attrs(i, i % 2); + nsAutoCString suffix, expectSuffix; + attrs.CreateSuffix(expectSuffix); + cInfo.attrs().CreateSuffix(suffix); + + ASSERT_STREQ(expectSuffix.get(), suffix.get()); + + test.AppendPrintf("scope write %d", i); + ASSERT_STREQ(test.get(), cInfo.spec().get()); + + test.Truncate(); + test.AppendPrintf("scope write %d", i); + ASSERT_STREQ(test.get(), data[i].scope().get()); + + test.Truncate(); + test.AppendPrintf("currentWorkerURL write %d", i); + ASSERT_STREQ(test.get(), data[i].currentWorkerURL().get()); + + test.Truncate(); + test.AppendPrintf("cacheName write %d", i); + ASSERT_STREQ(test.get(), NS_ConvertUTF16toUTF8(data[i].cacheName()).get()); + } +} + +TEST(ServiceWorkerRegistrar, TestVersion2Migration) +{ + nsAutoCString buffer("2" "\n"); + + buffer.Append("^appId=123&inBrowser=1\n"); + buffer.Append("spec 0\nscope 0\nscriptSpec 0\ncurrentWorkerURL 0\nactiveCache 0\nwaitingCache 0\n"); + buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n"); + + buffer.Append("\n"); + buffer.Append("spec 1\nscope 1\nscriptSpec 1\ncurrentWorkerURL 1\nactiveCache 1\nwaitingCache 1\n"); + buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n"); + + ASSERT_TRUE(CreateFile(buffer)) << "CreateFile should not fail"; + + RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest; + + nsresult rv = swr->TestReadData(); + ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail"; + + const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData(); + ASSERT_EQ((uint32_t)2, data.Length()) << "2 entries should be found"; + + const mozilla::ipc::PrincipalInfo& info0 = data[0].principal(); + ASSERT_EQ(info0.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content"; + const mozilla::ipc::ContentPrincipalInfo& cInfo0 = data[0].principal(); + + nsAutoCString suffix0; + cInfo0.attrs().CreateSuffix(suffix0); + + ASSERT_STREQ("^appId=123&inBrowser=1", suffix0.get()); + ASSERT_STREQ("scope 0", cInfo0.spec().get()); + ASSERT_STREQ("scope 0", data[0].scope().get()); + ASSERT_STREQ("currentWorkerURL 0", data[0].currentWorkerURL().get()); + ASSERT_STREQ("activeCache 0", NS_ConvertUTF16toUTF8(data[0].cacheName()).get()); + + const mozilla::ipc::PrincipalInfo& info1 = data[1].principal(); + ASSERT_EQ(info1.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content"; + const mozilla::ipc::ContentPrincipalInfo& cInfo1 = data[1].principal(); + + nsAutoCString suffix1; + cInfo1.attrs().CreateSuffix(suffix1); + + ASSERT_STREQ("", suffix1.get()); + ASSERT_STREQ("scope 1", cInfo1.spec().get()); + ASSERT_STREQ("scope 1", data[1].scope().get()); + ASSERT_STREQ("currentWorkerURL 1", data[1].currentWorkerURL().get()); + ASSERT_STREQ("activeCache 1", NS_ConvertUTF16toUTF8(data[1].cacheName()).get()); +} + +TEST(ServiceWorkerRegistrar, TestVersion3Migration) +{ + nsAutoCString buffer("3" "\n"); + + buffer.Append("^appId=123&inBrowser=1\n"); + buffer.Append("spec 0\nscope 0\ncurrentWorkerURL 0\ncacheName 0\n"); + buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n"); + + buffer.Append("\n"); + buffer.Append("spec 1\nscope 1\ncurrentWorkerURL 1\ncacheName 1\n"); + buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n"); + + ASSERT_TRUE(CreateFile(buffer)) << "CreateFile should not fail"; + + RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest; + + nsresult rv = swr->TestReadData(); + ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail"; + + const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData(); + ASSERT_EQ((uint32_t)2, data.Length()) << "2 entries should be found"; + + const mozilla::ipc::PrincipalInfo& info0 = data[0].principal(); + ASSERT_EQ(info0.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content"; + const mozilla::ipc::ContentPrincipalInfo& cInfo0 = data[0].principal(); + + nsAutoCString suffix0; + cInfo0.attrs().CreateSuffix(suffix0); + + ASSERT_STREQ("^appId=123&inBrowser=1", suffix0.get()); + ASSERT_STREQ("scope 0", cInfo0.spec().get()); + ASSERT_STREQ("scope 0", data[0].scope().get()); + ASSERT_STREQ("currentWorkerURL 0", data[0].currentWorkerURL().get()); + ASSERT_STREQ("cacheName 0", NS_ConvertUTF16toUTF8(data[0].cacheName()).get()); + + const mozilla::ipc::PrincipalInfo& info1 = data[1].principal(); + ASSERT_EQ(info1.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content"; + const mozilla::ipc::ContentPrincipalInfo& cInfo1 = data[1].principal(); + + nsAutoCString suffix1; + cInfo1.attrs().CreateSuffix(suffix1); + + ASSERT_STREQ("", suffix1.get()); + ASSERT_STREQ("scope 1", cInfo1.spec().get()); + ASSERT_STREQ("scope 1", data[1].scope().get()); + ASSERT_STREQ("currentWorkerURL 1", data[1].currentWorkerURL().get()); + ASSERT_STREQ("cacheName 1", NS_ConvertUTF16toUTF8(data[1].cacheName()).get()); +} + +TEST(ServiceWorkerRegistrar, TestDedupeRead) +{ + nsAutoCString buffer("3" "\n"); + + // unique entries + buffer.Append("^appId=123&inBrowser=1\n"); + buffer.Append("spec 0\nscope 0\ncurrentWorkerURL 0\ncacheName 0\n"); + buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n"); + + buffer.Append("\n"); + buffer.Append("spec 1\nscope 1\ncurrentWorkerURL 1\ncacheName 1\n"); + buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n"); + + // dupe entries + buffer.Append("^appId=123&inBrowser=1\n"); + buffer.Append("spec 1\nscope 0\ncurrentWorkerURL 0\ncacheName 0\n"); + buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n"); + + buffer.Append("^appId=123&inBrowser=1\n"); + buffer.Append("spec 2\nscope 0\ncurrentWorkerURL 0\ncacheName 0\n"); + buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n"); + + buffer.Append("\n"); + buffer.Append("spec 3\nscope 1\ncurrentWorkerURL 1\ncacheName 1\n"); + buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n"); + + ASSERT_TRUE(CreateFile(buffer)) << "CreateFile should not fail"; + + RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest; + + nsresult rv = swr->TestReadData(); + ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail"; + + const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData(); + ASSERT_EQ((uint32_t)2, data.Length()) << "2 entries should be found"; + + const mozilla::ipc::PrincipalInfo& info0 = data[0].principal(); + ASSERT_EQ(info0.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content"; + const mozilla::ipc::ContentPrincipalInfo& cInfo0 = data[0].principal(); + + nsAutoCString suffix0; + cInfo0.attrs().CreateSuffix(suffix0); + + ASSERT_STREQ("^appId=123&inBrowser=1", suffix0.get()); + ASSERT_STREQ("scope 0", cInfo0.spec().get()); + ASSERT_STREQ("scope 0", data[0].scope().get()); + ASSERT_STREQ("currentWorkerURL 0", data[0].currentWorkerURL().get()); + ASSERT_STREQ("cacheName 0", NS_ConvertUTF16toUTF8(data[0].cacheName()).get()); + + const mozilla::ipc::PrincipalInfo& info1 = data[1].principal(); + ASSERT_EQ(info1.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content"; + const mozilla::ipc::ContentPrincipalInfo& cInfo1 = data[1].principal(); + + nsAutoCString suffix1; + cInfo1.attrs().CreateSuffix(suffix1); + + ASSERT_STREQ("", suffix1.get()); + ASSERT_STREQ("scope 1", cInfo1.spec().get()); + ASSERT_STREQ("scope 1", data[1].scope().get()); + ASSERT_STREQ("currentWorkerURL 1", data[1].currentWorkerURL().get()); + ASSERT_STREQ("cacheName 1", NS_ConvertUTF16toUTF8(data[1].cacheName()).get()); +} + +TEST(ServiceWorkerRegistrar, TestDedupeWrite) +{ + { + RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest; + + for (int i = 0; i < 10; ++i) { + ServiceWorkerRegistrationData reg; + + reg.scope() = NS_LITERAL_CSTRING("scope write dedupe"); + reg.currentWorkerURL() = nsPrintfCString("currentWorkerURL write %d", i); + reg.cacheName() = + NS_ConvertUTF8toUTF16(nsPrintfCString("cacheName write %d", i)); + + nsAutoCString spec; + spec.AppendPrintf("spec write dedupe/%d", i); + reg.principal() = + mozilla::ipc::ContentPrincipalInfo(mozilla::PrincipalOriginAttributes(0, false), + mozilla::void_t(), spec); + + swr->TestRegisterServiceWorker(reg); + } + + nsresult rv = swr->TestWriteData(); + ASSERT_EQ(NS_OK, rv) << "WriteData() should not fail"; + } + + RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest; + + nsresult rv = swr->TestReadData(); + ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail"; + + // Duplicate entries should be removed. + const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData(); + ASSERT_EQ((uint32_t)1, data.Length()) << "1 entry should be found"; + + ASSERT_EQ(data[0].principal().type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo); + const mozilla::ipc::ContentPrincipalInfo& cInfo = data[0].principal(); + + mozilla::PrincipalOriginAttributes attrs(0, false); + nsAutoCString suffix, expectSuffix; + attrs.CreateSuffix(expectSuffix); + cInfo.attrs().CreateSuffix(suffix); + + // Last entry passed to RegisterServiceWorkerInternal() should overwrite + // previous values. So expect "9" in values here. + ASSERT_STREQ(expectSuffix.get(), suffix.get()); + ASSERT_STREQ("scope write dedupe", cInfo.spec().get()); + ASSERT_STREQ("scope write dedupe", data[0].scope().get()); + ASSERT_STREQ("currentWorkerURL write 9", data[0].currentWorkerURL().get()); + ASSERT_STREQ("cacheName write 9", + NS_ConvertUTF16toUTF8(data[0].cacheName()).get()); +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + int rv = RUN_ALL_TESTS(); + return rv; +} diff --git a/dom/workers/test/gtest/moz.build b/dom/workers/test/gtest/moz.build new file mode 100644 index 000000000..5f1f185a9 --- /dev/null +++ b/dom/workers/test/gtest/moz.build @@ -0,0 +1,13 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at http://mozilla.org/MPL/2.0/. + +UNIFIED_SOURCES = [ + 'TestReadWrite.cpp', +] + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul-gtest' diff --git a/dom/workers/test/head.js b/dom/workers/test/head.js new file mode 100644 index 000000000..5f0c5c26e --- /dev/null +++ b/dom/workers/test/head.js @@ -0,0 +1,91 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const EXAMPLE_URL = "http://example.com/browser/dom/workers/test/"; +const FRAME_SCRIPT_URL = getRootDirectory(gTestPath) + "frame_script.js"; + +/** + * Add a tab with given `url`, and load a frame script in it. Returns a promise + * that will be resolved when the tab finished loading. + */ +function addTab(url) { + let tab = gBrowser.addTab(TAB_URL); + gBrowser.selectedTab = tab; + let linkedBrowser = tab.linkedBrowser; + linkedBrowser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false); + return new Promise(function (resolve) { + linkedBrowser.addEventListener("load", function onload() { + linkedBrowser.removeEventListener("load", onload, true); + resolve(tab); + }, true); + }); +} + +/** + * Remove the given `tab`. + */ +function removeTab(tab) { + gBrowser.removeTab(tab); +} + +let nextId = 0; + +/** + * Send a JSON RPC request to the frame script in the given `tab`, invoking the + * given `method` with the given `params`. Returns a promise that will be + * resolved with the result of the invocation. + */ +function jsonrpc(tab, method, params) { + let currentId = nextId++; + let messageManager = tab.linkedBrowser.messageManager; + messageManager.sendAsyncMessage("jsonrpc", { + id: currentId, + method: method, + params: params + }); + return new Promise(function (resolve, reject) { + messageManager.addMessageListener("jsonrpc", function listener(event) { + let { id, result, error } = event.data; + if (id !== currentId) { + return; + } + messageManager.removeMessageListener("jsonrpc", listener); + if (error) { + reject(error); + return; + } + resolve(result); + }); + }); +} + +/** + * Create a worker with the given `url` in the given `tab`. + */ +function createWorkerInTab(tab, url) { + return jsonrpc(tab, "createWorker", [url]); +} + +/** + * Terminate the worker with the given `url` in the given `tab`. + */ +function terminateWorkerInTab(tab, url) { + return jsonrpc(tab, "terminateWorker", [url]); +} + +/** + * Post the given `message` to the worker with the given `url` in the given + * `tab`. + */ +function postMessageToWorkerInTab(tab, url, message) { + return jsonrpc(tab, "postMessageToWorker", [url, message]); +} + +/** + * Disable the cache in the given `tab`. + */ +function disableCacheInTab(tab) { + return jsonrpc(tab, "disableCache", []); +} diff --git a/dom/workers/test/importForeignScripts_worker.js b/dom/workers/test/importForeignScripts_worker.js new file mode 100644 index 000000000..5faa29c31 --- /dev/null +++ b/dom/workers/test/importForeignScripts_worker.js @@ -0,0 +1,55 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var target = self; +var response; + +function runTests() { + response = "good"; + try { + importScripts("http://example.org/tests/dom/workers/test/foreign.js"); + } catch(e) { + dump("Got error " + e + " when calling importScripts"); + } + if (response === "good") { + try { + importScripts("redirect_to_foreign.sjs"); + } catch(e) { + dump("Got error " + e + " when calling importScripts"); + } + } + target.postMessage(response); + + // Now, test a nested worker. + if (location.search !== "?nested") { + var worker = new Worker("importForeignScripts_worker.js?nested"); + + worker.onmessage = function(e) { + target.postMessage(e.data); + target.postMessage("finish"); + } + + worker.onerror = function() { + target.postMessage("nested worker error"); + } + + worker.postMessage("start"); + } +} + +onmessage = function(e) { + if (e.data === "start") { + runTests(); + } +}; + +onconnect = function(e) { + target = e.ports[0]; + e.ports[0].onmessage = function(e) { + if (e.data === "start") { + runTests(); + } + }; +}; diff --git a/dom/workers/test/importScripts_3rdParty_worker.js b/dom/workers/test/importScripts_3rdParty_worker.js new file mode 100644 index 000000000..ebf2d3b14 --- /dev/null +++ b/dom/workers/test/importScripts_3rdParty_worker.js @@ -0,0 +1,82 @@ +const workerURL = 'http://mochi.test:8888/tests/dom/workers/test/importScripts_3rdParty_worker.js'; + +onmessage = function(a) { + if (a.data.nested) { + var worker = new Worker(workerURL); + worker.onmessage = function(event) { + postMessage(event.data); + } + + worker.onerror = function(event) { + event.preventDefault(); + postMessage({ error: event instanceof ErrorEvent && + event.filename == workerURL }); + } + + a.data.nested = false; + worker.postMessage(a.data); + return; + } + + // This first URL will use the same origin of this script. + var sameOriginURL = new URL(a.data.url); + var fileName1 = 42; + + // This is cross-origin URL. + var crossOriginURL = new URL(a.data.url); + crossOriginURL.host = 'example.com'; + crossOriginURL.port = 80; + var fileName2 = 42; + + if (a.data.test == 'none') { + importScripts(crossOriginURL.href); + return; + } + + try { + importScripts(sameOriginURL.href); + } catch(e) { + if (!(e instanceof SyntaxError)) { + postMessage({ result: false }); + return; + } + + fileName1 = e.fileName; + } + + if (fileName1 != sameOriginURL.href || !fileName1) { + postMessage({ result: false }); + return; + } + + if (a.data.test == 'try') { + var exception; + try { + importScripts(crossOriginURL.href); + } catch(e) { + fileName2 = e.filename; + exception = e; + } + + postMessage({ result: fileName2 == workerURL && + exception.name == "NetworkError" && + exception.code == DOMException.NETWORK_ERR }); + return; + } + + if (a.data.test == 'eventListener') { + addEventListener("error", function(event) { + event.preventDefault(); + postMessage({result: event instanceof ErrorEvent && + event.filename == workerURL }); + }); + } + + if (a.data.test == 'onerror') { + onerror = function(...args) { + postMessage({result: args[1] == workerURL }); + } + } + + importScripts(crossOriginURL.href); +} diff --git a/dom/workers/test/importScripts_mixedcontent.html b/dom/workers/test/importScripts_mixedcontent.html new file mode 100644 index 000000000..82933b091 --- /dev/null +++ b/dom/workers/test/importScripts_mixedcontent.html @@ -0,0 +1,46 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<script> + function ok(cond, msg) { + window.parent.postMessage({status: "ok", data: cond, msg: msg}, "*"); + } + function finish() { + window.parent.postMessage({status: "done"}, "*"); + } + + function testSharedWorker() { + var sw = new SharedWorker("importForeignScripts_worker.js"); + sw.port.onmessage = function(e) { + if (e.data == "finish") { + finish(); + return; + } + ok(e.data === "good", "mixed content for shared workers is correctly blocked"); + }; + + sw.onerror = function() { + ok(false, "Error on shared worker "); + }; + + sw.port.postMessage("start"); + } + + var worker = new Worker("importForeignScripts_worker.js"); + + worker.onmessage = function(e) { + if (e.data == "finish") { + testSharedWorker(); + return; + } + ok(e.data === "good", "mixed content is correctly blocked"); + } + + worker.onerror = function() { + ok(false, "Error on worker"); + } + + worker.postMessage("start"); +</script> diff --git a/dom/workers/test/importScripts_worker.js b/dom/workers/test/importScripts_worker.js new file mode 100644 index 000000000..7176ce838 --- /dev/null +++ b/dom/workers/test/importScripts_worker.js @@ -0,0 +1,64 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +// Try no args. This shouldn't do anything. +importScripts(); + +// This caused security exceptions in the past, make sure it doesn't! +var constructor = {}.constructor; + +importScripts("importScripts_worker_imported1.js"); + +// Try to call a function defined in the imported script. +importedScriptFunction(); + +function tryBadScripts() { + var badScripts = [ + // Has a syntax error + "importScripts_worker_imported3.js", + // Throws an exception + "importScripts_worker_imported4.js", + // Shouldn't exist! + "http://example.com/non-existing/importScripts_worker_foo.js", + // Not a valid url + "http://notadomain::notafile aword" + ]; + + for (var i = 0; i < badScripts.length; i++) { + var caughtException = false; + var url = badScripts[i]; + try { + importScripts(url); + } + catch (e) { + caughtException = true; + } + if (!caughtException) { + throw "Bad script didn't throw exception: " + url; + } + } +} + +const url = "data:text/plain,const startResponse = 'started';"; +importScripts(url); + +onmessage = function(event) { + switch (event.data) { + case 'start': + importScripts("importScripts_worker_imported2.js"); + importedScriptFunction2(); + tryBadScripts(); + postMessage(startResponse); + break; + case 'stop': + tryBadScripts(); + postMessage('stopped'); + break; + default: + throw new Error("Bad message: " + event.data); + break; + } +} + +tryBadScripts(); diff --git a/dom/workers/test/importScripts_worker_imported1.js b/dom/workers/test/importScripts_worker_imported1.js new file mode 100644 index 000000000..9c33588c4 --- /dev/null +++ b/dom/workers/test/importScripts_worker_imported1.js @@ -0,0 +1,10 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +// This caused security exceptions in the past, make sure it doesn't! +var myConstructor = {}.constructor; + +// Try to call a function defined in the imported script. +function importedScriptFunction() { +} diff --git a/dom/workers/test/importScripts_worker_imported2.js b/dom/workers/test/importScripts_worker_imported2.js new file mode 100644 index 000000000..3aafb60be --- /dev/null +++ b/dom/workers/test/importScripts_worker_imported2.js @@ -0,0 +1,10 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +// This caused security exceptions in the past, make sure it doesn't! +var myConstructor2 = {}.constructor; + +// Try to call a function defined in the imported script. +function importedScriptFunction2() { +} diff --git a/dom/workers/test/importScripts_worker_imported3.js b/dom/workers/test/importScripts_worker_imported3.js new file mode 100644 index 000000000..c54be3e5f --- /dev/null +++ b/dom/workers/test/importScripts_worker_imported3.js @@ -0,0 +1,6 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +// Deliberate syntax error, should generate a worker exception! +for (var index = 0; index < 100) {} diff --git a/dom/workers/test/importScripts_worker_imported4.js b/dom/workers/test/importScripts_worker_imported4.js new file mode 100644 index 000000000..82f8708c5 --- /dev/null +++ b/dom/workers/test/importScripts_worker_imported4.js @@ -0,0 +1,6 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +// Deliberate throw, should generate a worker exception! +throw new Error("Bah!"); diff --git a/dom/workers/test/instanceof_worker.js b/dom/workers/test/instanceof_worker.js new file mode 100644 index 000000000..a98255388 --- /dev/null +++ b/dom/workers/test/instanceof_worker.js @@ -0,0 +1,12 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function(event) { + postMessage({event: "XMLHttpRequest", + status: (new XMLHttpRequest() instanceof XMLHttpRequest), + last: false }); + postMessage({event: "XMLHttpRequestUpload", + status: ((new XMLHttpRequest()).upload instanceof XMLHttpRequestUpload), + last: true }); +} diff --git a/dom/workers/test/json_worker.js b/dom/workers/test/json_worker.js new file mode 100644 index 000000000..f35e14cf2 --- /dev/null +++ b/dom/workers/test/json_worker.js @@ -0,0 +1,338 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +var cyclicalObject = {}; +cyclicalObject.foo = cyclicalObject; + +var cyclicalArray = []; +cyclicalArray.push(cyclicalArray); + +function makeCrazyNested(obj, count) { + var innermostobj; + for (var i = 0; i < count; i++) { + obj.foo = { bar: 5 } + innermostobj = obj.foo; + obj = innermostobj; + } + return innermostobj; +} + +var crazyNestedObject = {}; +makeCrazyNested(crazyNestedObject, 100); + +var crazyCyclicalObject = {}; +var innermost = makeCrazyNested(crazyCyclicalObject, 1000); +innermost.baz = crazyCyclicalObject; + +var objectWithSaneGetter = { }; +objectWithSaneGetter.__defineGetter__("foo", function() { return 5; }); + +// We don't walk prototype chains for cloning so this won't actually do much... +function objectWithSaneGetter2() { } +objectWithSaneGetter2.prototype = { + get foo() { + return 5; + } +}; + +const throwingGetterThrownString = "bad"; + +var objectWithThrowingGetter = { }; +objectWithThrowingGetter.__defineGetter__("foo", function() { + throw throwingGetterThrownString; +}); + +var typedArrayWithValues = new Int8Array(5); +for (var index in typedArrayWithValues) { + typedArrayWithValues[index] = index; +} + +var typedArrayWithFunBuffer = new Int8Array(4); +for (var index in typedArrayWithFunBuffer) { + typedArrayWithFunBuffer[index] = 255; +} + +var typedArrayWithFunBuffer2 = new Int32Array(typedArrayWithFunBuffer.buffer); + +var xhr = new XMLHttpRequest(); + +var messages = [ + { + type: "object", + value: { }, + jsonValue: '{}' + }, + { + type: "object", + value: {foo: "bar"}, + jsonValue: '{"foo":"bar"}' + }, + { + type: "object", + value: {foo: "bar", foo2: {bee: "bop"}}, + jsonValue: '{"foo":"bar","foo2":{"bee":"bop"}}' + }, + { + type: "object", + value: {foo: "bar", foo2: {bee: "bop"}, foo3: "baz"}, + jsonValue: '{"foo":"bar","foo2":{"bee":"bop"},"foo3":"baz"}' + }, + { + type: "object", + value: {foo: "bar", foo2: [1,2,3]}, + jsonValue: '{"foo":"bar","foo2":[1,2,3]}' + }, + { + type: "object", + value: cyclicalObject, + }, + { + type: "object", + value: [null, 2, false, cyclicalObject], + }, + { + type: "object", + value: cyclicalArray, + }, + { + type: "object", + value: {foo: 1, bar: cyclicalArray}, + }, + { + type: "object", + value: crazyNestedObject, + jsonValue: JSON.stringify(crazyNestedObject) + }, + { + type: "object", + value: crazyCyclicalObject, + }, + { + type: "object", + value: objectWithSaneGetter, + jsonValue: '{"foo":5}' + }, + { + type: "object", + value: new objectWithSaneGetter2(), + jsonValue: '{}' + }, + { + type: "object", + value: objectWithThrowingGetter, + exception: true + }, + { + type: "object", + array: true, + value: [9, 8, 7], + jsonValue: '[9,8,7]' + }, + { + type: "object", + array: true, + value: [9, false, 10.5, {foo: "bar"}], + jsonValue: '[9,false,10.5,{"foo":"bar"}]' + }, + { + type: "object", + shouldEqual: true, + value: null + }, + { + type: "undefined", + shouldEqual: true, + value: undefined + }, + { + type: "string", + shouldEqual: true, + value: "Hello" + }, + { + type: "string", + shouldEqual: true, + value: JSON.stringify({ foo: "bar" }), + compareValue: '{"foo":"bar"}' + }, + { + type: "number", + shouldEqual: true, + value: 1 + }, + { + type: "number", + shouldEqual: true, + value: 0 + }, + { + type: "number", + shouldEqual: true, + value: -1 + }, + { + type: "number", + shouldEqual: true, + value: 238573459843702923492399923049 + }, + { + type: "number", + shouldEqual: true, + value: -238573459843702923492399923049 + }, + { + type: "number", + shouldEqual: true, + value: 0.25 + }, + { + type: "number", + shouldEqual: true, + value: -0.25 + }, + { + type: "boolean", + shouldEqual: true, + value: true + }, + { + type: "boolean", + shouldEqual: true, + value: false + }, + { + type: "object", + value: function (foo) { return "Bad!"; }, + exception: true + }, + { + type: "number", + isNaN: true, + value: NaN + }, + { + type: "number", + isInfinity: true, + value: Infinity + }, + { + type: "number", + isNegativeInfinity: true, + value: -Infinity + }, + { + type: "object", + value: new Int32Array(10), + jsonValue: '{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0}' + }, + { + type: "object", + value: new Float32Array(5), + jsonValue: '{"0":0,"1":0,"2":0,"3":0,"4":0}' + }, + { + type: "object", + value: typedArrayWithValues, + jsonValue: '{"0":0,"1":1,"2":2,"3":3,"4":4}' + }, + { + type: "number", + value: typedArrayWithValues[2], + compareValue: 2, + shouldEqual: true + }, + { + type: "object", + value: typedArrayWithValues.buffer, + jsonValue: '{}' + }, + { + type: "object", + value: typedArrayWithFunBuffer2, + jsonValue: '{"0":-1}' + }, + { + type: "object", + value: { foo: typedArrayWithFunBuffer2 }, + jsonValue: '{"foo":{"0":-1}}' + }, + { + type: "object", + value: [ typedArrayWithFunBuffer2 ], + jsonValue: '[{"0":-1}]' + }, + { + type: "object", + value: { foo: function(a) { alert(b); } }, + exception: true + }, + { + type: "object", + value: xhr, + exception: true + }, + { + type: "number", + value: xhr.readyState, + shouldEqual: true + }, + { + type: "object", + value: { xhr: xhr }, + exception: true + }, + { + type: "object", + value: self, + exception: true + }, + { + type: "object", + value: { p: ArrayBuffer.prototype }, + exception: true + }, + { + type: "string", + shouldEqual: true, + value: "testFinished" + } +]; + +for (var index = 0; index < messages.length; index++) { + var message = messages[index]; + if (message.hasOwnProperty("compareValue")) { + continue; + } + if (message.hasOwnProperty("shouldEqual") || + message.hasOwnProperty("shouldCompare")) { + message.compareValue = message.value; + } +} + +onmessage = function(event) { + for (var index = 0; index < messages.length; index++) { + var exception = undefined; + + try { + postMessage(messages[index].value); + } + catch (e) { + if (e instanceof DOMException) { + if (e.code != DOMException.DATA_CLONE_ERR) { + throw "DOMException with the wrong code: " + e.code; + } + } + else if (e != throwingGetterThrownString) { + throw "Exception of the wrong type: " + e; + } + exception = e; + } + + if ((exception !== undefined && !messages[index].exception) || + (exception === undefined && messages[index].exception)) { + throw "Exception inconsistency [index = " + index + ", " + + messages[index].toSource() + "]: " + exception; + } + } +} diff --git a/dom/workers/test/jsversion_worker.js b/dom/workers/test/jsversion_worker.js new file mode 100644 index 000000000..c66b72977 --- /dev/null +++ b/dom/workers/test/jsversion_worker.js @@ -0,0 +1,14 @@ +onmessage = function(evt) { + if (evt.data != 0) { + var worker = new Worker('jsversion_worker.js'); + worker.onmessage = function(evt) { + postMessage(evt.data); + } + + worker.postMessage(evt.data - 1); + return; + } + + let foo = 'bar'; + postMessage(true); +} diff --git a/dom/workers/test/loadEncoding_worker.js b/dom/workers/test/loadEncoding_worker.js new file mode 100644 index 000000000..5e4047844 --- /dev/null +++ b/dom/workers/test/loadEncoding_worker.js @@ -0,0 +1,7 @@ +/* + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +*/ +// Bug 484305 - Load workers as UTF-8. +postMessage({ encoding: "KOI8-R", text: "ðÒÉ×ÅÔ" }); +postMessage({ encoding: "UTF-8", text: "Привет" }); diff --git a/dom/workers/test/location_worker.js b/dom/workers/test/location_worker.js new file mode 100644 index 000000000..dd35ec8a3 --- /dev/null +++ b/dom/workers/test/location_worker.js @@ -0,0 +1,11 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +for (var string in self.location) { + var value = typeof self.location[string] === "function" + ? self.location[string]() + : self.location[string]; + postMessage({ "string": string, "value": value }); +} +postMessage({ "string": "testfinished" }); diff --git a/dom/workers/test/longThread_worker.js b/dom/workers/test/longThread_worker.js new file mode 100644 index 000000000..f132dd8c1 --- /dev/null +++ b/dom/workers/test/longThread_worker.js @@ -0,0 +1,14 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function(event) { + switch (event.data) { + case "start": + for (var i = 0; i < 10000000; i++) { }; + postMessage("done"); + break; + default: + throw "Bad message: " + event.data; + } +}; diff --git a/dom/workers/test/mochitest.ini b/dom/workers/test/mochitest.ini new file mode 100644 index 000000000..d4848819c --- /dev/null +++ b/dom/workers/test/mochitest.ini @@ -0,0 +1,227 @@ +[DEFAULT] +support-files = + WorkerTest_badworker.js + atob_worker.js + bug978260_worker.js + bug1014466_data1.txt + bug1014466_data2.txt + bug1014466_worker.js + bug1020226_worker.js + bug1020226_frame.html + bug998474_worker.js + bug1063538_worker.js + clearTimeouts_worker.js + content_worker.js + console_worker.js + consoleReplaceable_worker.js + csp_worker.js + csp_worker.js^headers^ + 404_server.sjs + errorPropagation_iframe.html + errorPropagation_worker.js + errorwarning_worker.js + eventDispatch_worker.js + fibonacci_worker.js + file_bug1010784_worker.js + foreign.js + importForeignScripts_worker.js + importScripts_mixedcontent.html + importScripts_worker.js + importScripts_worker_imported1.js + importScripts_worker_imported2.js + importScripts_worker_imported3.js + importScripts_worker_imported4.js + instanceof_worker.js + json_worker.js + jsversion_worker.js + loadEncoding_worker.js + location_worker.js + longThread_worker.js + multi_sharedWorker_frame.html + multi_sharedWorker_sharedWorker.js + navigator_languages_worker.js + navigator_worker.js + newError_worker.js + notification_worker.js + notification_worker_child-child.js + notification_worker_child-parent.js + notification_permission_worker.js + onLine_worker.js + onLine_worker_child.js + onLine_worker_head.js + promise_worker.js + recursion_worker.js + recursiveOnerror_worker.js + redirect_to_foreign.sjs + rvals_worker.js + sharedWorker_console.js + sharedWorker_sharedWorker.js + simpleThread_worker.js + suspend_iframe.html + suspend_worker.js + terminate_worker.js + test_csp.html^headers^ + test_csp.js + referrer_worker.html + threadErrors_worker1.js + threadErrors_worker2.js + threadErrors_worker3.js + threadErrors_worker4.js + threadTimeouts_worker.js + throwingOnerror_worker.js + timeoutTracing_worker.js + transferable_worker.js + websocket_basic_worker.js + websocket_loadgroup_worker.js + websocket_worker1.js + websocket_worker2.js + websocket_worker3.js + websocket_worker4.js + websocket_worker5.js + websocket_helpers.js + workersDisabled_worker.js + test_worker_interfaces.js + worker_driver.js + worker_wrapper.js + bug1060621_worker.js + bug1062920_worker.js + webSocket_sharedWorker.js + bug1104064_worker.js + worker_consoleAndBlobs.js + bug1132395_sharedWorker.js + bug1132924_worker.js + empty.html + referrer.sjs + referrer_test_server.sjs + sharedWorker_ports.js + sharedWorker_lifetime.js + worker_referrer.js + websocket_https.html + websocket_https_worker.js + worker_fileReader.js + fileapi_chromeScript.js + importScripts_3rdParty_worker.js + worker_bug1278777.js + worker_setTimeoutWith0.js + worker_bug1301094.js + script_createFile.js + worker_suspended.js + window_suspended.html + !/dom/base/test/file_bug945152.jar + !/dom/base/test/file_websocket_basic_wsh.py + !/dom/base/test/file_websocket_hello_wsh.py + !/dom/base/test/file_websocket_http_resource.txt + !/dom/base/test/file_websocket_permessage_deflate_disabled_wsh.py + !/dom/base/test/file_websocket_permessage_deflate_params_wsh.py + !/dom/base/test/file_websocket_permessage_deflate_rejected_wsh.py + !/dom/base/test/file_websocket_permessage_deflate_wsh.py + !/dom/base/test/file_websocket_wsh.py + !/dom/base/test/websocket_helpers.js + !/dom/base/test/websocket_tests.js + !/dom/tests/mochitest/notification/MockServices.js + !/dom/tests/mochitest/notification/NotificationTest.js + !/dom/xhr/tests/relativeLoad_import.js + !/dom/xhr/tests/relativeLoad_worker.js + !/dom/xhr/tests/relativeLoad_worker2.js + !/dom/xhr/tests/subdir/relativeLoad_sub_worker.js + !/dom/xhr/tests/subdir/relativeLoad_sub_worker2.js + !/dom/xhr/tests/subdir/relativeLoad_sub_import.js + +[test_404.html] +[test_atob.html] +[test_blobConstructor.html] +[test_blobWorkers.html] +[test_bug949946.html] +[test_bug978260.html] +[test_bug998474.html] +[test_bug1002702.html] +[test_bug1010784.html] +[test_bug1014466.html] +[test_bug1020226.html] +[test_bug1036484.html] +[test_bug1060621.html] +[test_bug1062920.html] +[test_bug1063538.html] +[test_bug1104064.html] +[test_bug1132395.html] +skip-if = true # bug 1176225 +[test_bug1132924.html] +[test_chromeWorker.html] +[test_clearTimeouts.html] +[test_console.html] +[test_consoleAndBlobs.html] +[test_consoleReplaceable.html] +[test_consoleSharedWorkers.html] +[test_contentWorker.html] +[test_csp.html] +[test_dataURLWorker.html] +[test_errorPropagation.html] +[test_errorwarning.html] +[test_eventDispatch.html] +[test_fibonacci.html] +[test_importScripts.html] +[test_importScripts_mixedcontent.html] +tags = mcb +[test_instanceof.html] +[test_json.html] +[test_jsversion.html] +[test_loadEncoding.html] +[test_loadError.html] +[test_location.html] +[test_longThread.html] +[test_multi_sharedWorker.html] +[test_multi_sharedWorker_lifetimes.html] +[test_navigator.html] +[test_navigator_languages.html] +[test_newError.html] +[test_notification.html] +[test_notification_child.html] +[test_notification_permission.html] +[test_onLine.html] +[test_promise.html] +[test_promise_resolved_with_string.html] +[test_recursion.html] +[test_recursiveOnerror.html] +[test_resolveWorker.html] +[test_resolveWorker-assignment.html] +[test_rvals.html] +[test_sharedWorker.html] +[test_simpleThread.html] +[test_suspend.html] +[test_terminate.html] +[test_threadErrors.html] +[test_threadTimeouts.html] +[test_throwingOnerror.html] +[test_timeoutTracing.html] +[test_transferable.html] +[test_websocket1.html] +skip-if = toolkit == 'android' #bug 982828 +[test_websocket2.html] +skip-if = toolkit == 'android' #bug 982828 +[test_websocket3.html] +skip-if = toolkit == 'android' #bug 982828 +[test_websocket4.html] +skip-if = toolkit == 'android' #bug 982828 +[test_websocket5.html] +skip-if = toolkit == 'android' #bug 982828 +[test_websocket_basic.html] +skip-if = toolkit == 'android' #bug 982828 +[test_websocket_https.html] +[test_websocket_loadgroup.html] +skip-if = toolkit == 'android' #bug 982828 +[test_webSocket_sharedWorker.html] +skip-if = toolkit == 'android' #bug 982828 +[test_worker_interfaces.html] +[test_workersDisabled.html] +[test_referrer.html] +[test_referrer_header_worker.html] +[test_importScripts_3rdparty.html] +[test_sharedWorker_ports.html] +[test_sharedWorker_lifetime.html] +[test_fileReader.html] +[test_navigator_workers_hardwareConcurrency.html] +[test_bug1278777.html] +[test_setTimeoutWith0.html] +[test_bug1301094.html] +[test_subworkers_suspended.html] +[test_bug1317725.html] diff --git a/dom/workers/test/multi_sharedWorker_frame.html b/dom/workers/test/multi_sharedWorker_frame.html new file mode 100644 index 000000000..6c0dfe591 --- /dev/null +++ b/dom/workers/test/multi_sharedWorker_frame.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>Test for SharedWorker</title> + </head> + <body> + <script type="text/javascript;version=1.7"> + "use strict"; + + function debug(message) { + if (typeof(message) != "string") { + throw new Error("debug() only accepts strings!"); + } + parent.postMessage(message, "*"); + } + + let worker; + + window.addEventListener("message", function(event) { + if (!worker) { + worker = new SharedWorker("multi_sharedWorker_sharedWorker.js", + "FrameWorker"); + worker.onerror = function(event) { + debug("Worker error: " + event.message); + event.preventDefault(); + + let data = { + type: "error", + message: event.message, + filename: event.filename, + lineno: event.lineno, + isErrorEvent: event instanceof ErrorEvent + }; + parent.postMessage(data, "*"); + }; + + worker.port.onmessage = function(event) { + debug("Worker message: " + JSON.stringify(event.data)); + parent.postMessage(event.data, "*"); + }; + } + + debug("Posting message: " + JSON.stringify(event.data)); + worker.port.postMessage(event.data); + }); + </script> + </body> +</html> diff --git a/dom/workers/test/multi_sharedWorker_sharedWorker.js b/dom/workers/test/multi_sharedWorker_sharedWorker.js new file mode 100644 index 000000000..47a7ae04f --- /dev/null +++ b/dom/workers/test/multi_sharedWorker_sharedWorker.js @@ -0,0 +1,72 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +if (self.name != "FrameWorker") { + throw new Error("Bad worker name: " + self.name); +} + +var registeredPorts = []; +var errorCount = 0; +var storedData; + +self.onconnect = function(event) { + var port = event.ports[0]; + + if (registeredPorts.length) { + var data = { + type: "connect" + }; + + registeredPorts.forEach(function(registeredPort) { + registeredPort.postMessage(data); + }); + } + + port.onmessage = function(event) { + switch (event.data.command) { + case "start": + break; + + case "error": + throw new Error("Expected"); + + case "store": + storedData = event.data.data; + break; + + case "retrieve": + var data = { + type: "result", + data: storedData + }; + port.postMessage(data); + break; + + default: + throw new Error("Unknown command '" + error.data.command + "'"); + } + }; + + registeredPorts.push(port); +}; + +self.onerror = function(message, filename, lineno) { + if (!errorCount++) { + var data = { + type: "worker-error", + message: message, + filename: filename, + lineno: lineno + }; + + registeredPorts.forEach(function (registeredPort) { + registeredPort.postMessage(data); + }); + + // Prevent the error from propagating the first time only. + return true; + } +}; diff --git a/dom/workers/test/navigator_languages_worker.js b/dom/workers/test/navigator_languages_worker.js new file mode 100644 index 000000000..53aa4d39e --- /dev/null +++ b/dom/workers/test/navigator_languages_worker.js @@ -0,0 +1,11 @@ +var active = true; +onmessage = function(e) { + if (e.data == 'finish') { + active = false; + return; + } + + if (active) { + postMessage(navigator.languages); + } +} diff --git a/dom/workers/test/navigator_worker.js b/dom/workers/test/navigator_worker.js new file mode 100644 index 000000000..63853aef1 --- /dev/null +++ b/dom/workers/test/navigator_worker.js @@ -0,0 +1,79 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// IMPORTANT: Do not change the list below without review from a DOM peer! +var supportedProps = [ + "appCodeName", + "appName", + "appVersion", + "platform", + "product", + "userAgent", + "onLine", + "language", + "languages", + "hardwareConcurrency", + { name: "storage", nightly: true }, +]; + +self.onmessage = function(event) { + if (!event || !event.data) { + return; + } + + startTest(event.data); +}; + +function startTest(channelData) { + // Prepare the interface map showing if a propery should exist in this build. + // For example, if interfaceMap[foo] = true means navigator.foo should exist. + var interfaceMap = {}; + + for (var prop of supportedProps) { + if (typeof(prop) === "string") { + interfaceMap[prop] = true; + continue; + } + + if (prop.nightly === !channelData.isNightly || + prop.release === !channelData.isRelease) { + interfaceMap[prop.name] = false; + continue; + } + + interfaceMap[prop.name] = true; + } + + for (var prop in navigator) { + // Make sure the list is current! + if (!interfaceMap[prop]) { + throw "Navigator has the '" + prop + "' property that isn't in the list!"; + } + } + + var obj; + + for (var prop in interfaceMap) { + // Skip the property that is not supposed to exist in this build. + if (!interfaceMap[prop]) { + continue; + } + + if (typeof navigator[prop] == "undefined") { + throw "Navigator has no '" + prop + "' property!"; + } + + obj = { name: prop }; + obj.value = navigator[prop]; + + postMessage(JSON.stringify(obj)); + } + + obj = { + name: "testFinished" + }; + + postMessage(JSON.stringify(obj)); +} diff --git a/dom/workers/test/newError_worker.js b/dom/workers/test/newError_worker.js new file mode 100644 index 000000000..46e6226f7 --- /dev/null +++ b/dom/workers/test/newError_worker.js @@ -0,0 +1,5 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +throw new Error("foo!"); diff --git a/dom/workers/test/notification_permission_worker.js b/dom/workers/test/notification_permission_worker.js new file mode 100644 index 000000000..f69acaeda --- /dev/null +++ b/dom/workers/test/notification_permission_worker.js @@ -0,0 +1,56 @@ +function info(message) { + dump("INFO: " + message + "\n"); +} + +function ok(test, message) { + postMessage({ type: 'ok', test: test, message: message }); +} + +function is(a, b, message) { + postMessage({ type: 'is', test1: a, test2: b, message: message }); +} + +if (self.Notification) { + var steps = [ + function (done) { + info("Test notification permission"); + ok(typeof Notification === "function", "Notification constructor exists"); + ok(Notification.permission === "denied", "Notification.permission is denied."); + + var n = new Notification("Hello"); + n.onerror = function(e) { + ok(true, "error called due to permission denied."); + done(); + } + }, + ]; + + onmessage = function(e) { + var context = {}; + (function executeRemainingTests(remainingTests) { + if (!remainingTests.length) { + postMessage({type: 'finish'}); + return; + } + + var nextTest = remainingTests.shift(); + var finishTest = executeRemainingTests.bind(null, remainingTests); + var startTest = nextTest.call.bind(nextTest, context, finishTest); + + try { + startTest(); + // if no callback was defined for test function, + // we must manually invoke finish to continue + if (nextTest.length === 0) { + finishTest(); + } + } catch (e) { + ok(false, "Test threw exception! " + nextTest + " " + e); + finishTest(); + } + })(steps); + } +} else { + ok(true, "Notifications are not enabled in workers on the platform."); + postMessage({ type: 'finish' }); +} diff --git a/dom/workers/test/notification_worker.js b/dom/workers/test/notification_worker.js new file mode 100644 index 000000000..cb55f8358 --- /dev/null +++ b/dom/workers/test/notification_worker.js @@ -0,0 +1,93 @@ +function ok(test, message) { + postMessage({ type: 'ok', test: test, message: message }); +} + +function is(a, b, message) { + postMessage({ type: 'is', test1: a, test2: b, message: message }); +} + +if (self.Notification) { + var steps = [ + function () { + ok(typeof Notification === "function", "Notification constructor exists"); + ok(Notification.permission, "Notification.permission exists"); + ok(typeof Notification.requestPermission === "undefined", "Notification.requestPermission should not exist"); + }, + + function (done) { + var options = { + dir: "auto", + lang: "", + body: "This is a notification body", + tag: "sometag", + icon: "icon.png", + data: ["a complex object that should be", { "structured": "cloned" }], + mozbehavior: { vibrationPattern: [30, 200, 30] }, + }; + var notification = new Notification("This is a title", options); + + ok(notification !== undefined, "Notification exists"); + is(notification.onclick, null, "onclick() should be null"); + is(notification.onshow, null, "onshow() should be null"); + is(notification.onerror, null, "onerror() should be null"); + is(notification.onclose, null, "onclose() should be null"); + is(typeof notification.close, "function", "close() should exist"); + + is(notification.dir, options.dir, "auto should get set"); + is(notification.lang, options.lang, "lang should get set"); + is(notification.body, options.body, "body should get set"); + is(notification.tag, options.tag, "tag should get set"); + is(notification.icon, options.icon, "icon should get set"); + is(notification.data[0], "a complex object that should be", "data item 0 should be a matching string"); + is(notification.data[1]["structured"], "cloned", "data item 1 should be a matching object literal"); + + // store notification in test context + this.notification = notification; + + notification.onshow = function () { + ok(true, "onshow handler should be called"); + done(); + }; + }, + + function (done) { + var notification = this.notification; + + notification.onclose = function () { + ok(true, "onclose handler should be called"); + done(); + }; + + notification.close(); + }, + ]; + + onmessage = function(e) { + var context = {}; + (function executeRemainingTests(remainingTests) { + if (!remainingTests.length) { + postMessage({type: 'finish'}); + return; + } + + var nextTest = remainingTests.shift(); + var finishTest = executeRemainingTests.bind(null, remainingTests); + var startTest = nextTest.call.bind(nextTest, context, finishTest); + + try { + startTest(); + // if no callback was defined for test function, + // we must manually invoke finish to continue + if (nextTest.length === 0) { + finishTest(); + } + } catch (e) { + ok(false, "Test threw exception! " + nextTest + " " + e); + finishTest(); + } + })(steps); + } +} else { + ok(true, "Notifications are not enabled in workers on the platform."); + postMessage({ type: 'finish' }); +} diff --git a/dom/workers/test/notification_worker_child-child.js b/dom/workers/test/notification_worker_child-child.js new file mode 100644 index 000000000..829bf43d3 --- /dev/null +++ b/dom/workers/test/notification_worker_child-child.js @@ -0,0 +1,92 @@ +function ok(test, message) { + postMessage({ type: 'ok', test: test, message: message }); +} + +function is(a, b, message) { + postMessage({ type: 'is', test1: a, test2: b, message: message }); +} + +if (self.Notification) { + var steps = [ + function () { + ok(typeof Notification === "function", "Notification constructor exists"); + ok(Notification.permission, "Notification.permission exists"); + ok(typeof Notification.requestPermission === "undefined", "Notification.requestPermission should not exist"); + //ok(typeof Notification.get === "function", "Notification.get exists"); + }, + + function (done) { + + var options = { + dir: "auto", + lang: "", + body: "This is a notification body", + tag: "sometag", + icon: "icon.png" + }; + var notification = new Notification("This is a title", options); + + ok(notification !== undefined, "Notification exists"); + is(notification.onclick, null, "onclick() should be null"); + is(notification.onshow, null, "onshow() should be null"); + is(notification.onerror, null, "onerror() should be null"); + is(notification.onclose, null, "onclose() should be null"); + is(typeof notification.close, "function", "close() should exist"); + + is(notification.dir, options.dir, "auto should get set"); + is(notification.lang, options.lang, "lang should get set"); + is(notification.body, options.body, "body should get set"); + is(notification.tag, options.tag, "tag should get set"); + is(notification.icon, options.icon, "icon should get set"); + + // store notification in test context + this.notification = notification; + + notification.onshow = function () { + ok(true, "onshow handler should be called"); + done(); + }; + }, + + function (done) { + var notification = this.notification; + + notification.onclose = function () { + ok(true, "onclose handler should be called"); + done(); + }; + + notification.close(); + }, + ]; + + onmessage = function(e) { + var context = {}; + (function executeRemainingTests(remainingTests) { + if (!remainingTests.length) { + postMessage({type: 'finish'}); + return; + } + + var nextTest = remainingTests.shift(); + var finishTest = executeRemainingTests.bind(null, remainingTests); + var startTest = nextTest.call.bind(nextTest, context, finishTest); + + try { + startTest(); + // if no callback was defined for test function, + // we must manually invoke finish to continue + if (nextTest.length === 0) { + finishTest(); + } + } catch (e) { + ok(false, "Test threw exception! " + nextTest + " " + e); + finishTest(); + } + })(steps); + } +} else { + ok(true, "Notifications are not enabled in workers on the platform."); + postMessage({ type: 'finish' }); +} + diff --git a/dom/workers/test/notification_worker_child-parent.js b/dom/workers/test/notification_worker_child-parent.js new file mode 100644 index 000000000..bb28c520d --- /dev/null +++ b/dom/workers/test/notification_worker_child-parent.js @@ -0,0 +1,26 @@ +function ok(test, message) { + postMessage({ type: 'ok', test: test, message: message }); +} + +function is(a, b, message) { + postMessage({ type: 'is', test1: a, test2: b, message: message }); +} + +if (self.Notification) { + var child = new Worker("notification_worker_child-child.js"); + child.onerror = function(e) { + ok(false, "Error loading child worker " + e); + postMessage({ type: 'finish' }); + } + + child.onmessage = function(e) { + postMessage(e.data); + } + + onmessage = function(e) { + child.postMessage('start'); + } +} else { + ok(true, "Notifications are not enabled in workers on the platform."); + postMessage({ type: 'finish' }); +} diff --git a/dom/workers/test/onLine_worker.js b/dom/workers/test/onLine_worker.js new file mode 100644 index 000000000..5a302b15e --- /dev/null +++ b/dom/workers/test/onLine_worker.js @@ -0,0 +1,65 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +importScripts("onLine_worker_head.js"); + +var N_CHILDREN = 3; +var children = []; +var finishedChildrenCount = 0; +var lastTest = false; + +for (var event of ["online", "offline"]) { + addEventListener(event, + makeHandler( + "addEventListener('%1', ..., false)", + event, 1, "Parent Worker"), + false); +} + +onmessage = function(e) { + if (e.data === 'lastTest') { + children.forEach(function(w) { + w.postMessage({ type: 'lastTest' }); + }); + lastTest = true; + } +} + +function setupChildren(cb) { + var readyCount = 0; + for (var i = 0; i < N_CHILDREN; ++i) { + var w = new Worker("onLine_worker_child.js"); + children.push(w); + + w.onerror = function(e) { + info("Error creating child " + e.message); + } + + w.onmessage = function(e) { + if (e.data.type === 'ready') { + info("Got ready from child"); + readyCount++; + if (readyCount === N_CHILDREN) { + cb(); + } + } else if (e.data.type === 'finished') { + finishedChildrenCount++; + + if (lastTest && finishedChildrenCount === N_CHILDREN) { + postMessage({ type: 'finished' }); + children = []; + close(); + } + } else if (e.data.type === 'ok') { + // Pass on test to page. + postMessage(e.data); + } + } + } +} + +setupChildren(function() { + postMessage({ type: 'ready' }); +}); diff --git a/dom/workers/test/onLine_worker_child.js b/dom/workers/test/onLine_worker_child.js new file mode 100644 index 000000000..dc28fac45 --- /dev/null +++ b/dom/workers/test/onLine_worker_child.js @@ -0,0 +1,75 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +function info(text) { + dump("Test for Bug 925437: worker: " + text + "\n"); +} + +function ok(test, message) { + postMessage({ type: 'ok', test: test, message: message }); +} + +/** + * Returns a handler function for an online/offline event. The returned handler + * ensures the passed event object has expected properties and that the handler + * is called at the right moment (according to the gState variable). + * @param nameTemplate The string identifying the hanlder. '%1' in that + * string will be replaced with the event name. + * @param eventName 'online' or 'offline' + * @param expectedState value of gState at the moment the handler is called. + * The handler increases gState by one before checking + * if it matches expectedState. + */ +function makeHandler(nameTemplate, eventName, expectedState, prefix, custom) { + prefix += ": "; + return function(e) { + var name = nameTemplate.replace(/%1/, eventName); + ok(e.constructor == Event, prefix + "event should be an Event"); + ok(e.type == eventName, prefix + "event type should be " + eventName); + ok(!e.bubbles, prefix + "event should not bubble"); + ok(!e.cancelable, prefix + "event should not be cancelable"); + ok(e.target == self, prefix + "the event target should be the worker scope"); + ok(eventName == 'online' ? navigator.onLine : !navigator.onLine, prefix + "navigator.onLine " + navigator.onLine + " should reflect event " + eventName); + + if (custom) { + custom(); + } + } +} + + + +var lastTest = false; + +function lastTestTest() { + if (lastTest) { + postMessage({ type: 'finished' }); + close(); + } +} + +for (var event of ["online", "offline"]) { + addEventListener(event, + makeHandler( + "addEventListener('%1', ..., false)", + event, 1, "Child Worker", lastTestTest + ), + false); +} + +onmessage = function(e) { + if (e.data.type === 'lastTest') { + lastTest = true; + } else if (e.data.type === 'navigatorState') { + ok(e.data.state === navigator.onLine, "Child and parent navigator state should match"); + } +} + +postMessage({ type: 'ready' }); diff --git a/dom/workers/test/onLine_worker_head.js b/dom/workers/test/onLine_worker_head.js new file mode 100644 index 000000000..4800457d3 --- /dev/null +++ b/dom/workers/test/onLine_worker_head.js @@ -0,0 +1,43 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +function info(text) { + dump("Test for Bug 925437: worker: " + text + "\n"); +} + +function ok(test, message) { + postMessage({ type: 'ok', test: test, message: message }); +} + +/** + * Returns a handler function for an online/offline event. The returned handler + * ensures the passed event object has expected properties and that the handler + * is called at the right moment (according to the gState variable). + * @param nameTemplate The string identifying the hanlder. '%1' in that + * string will be replaced with the event name. + * @param eventName 'online' or 'offline' + * @param expectedState value of gState at the moment the handler is called. + * The handler increases gState by one before checking + * if it matches expectedState. + */ +function makeHandler(nameTemplate, eventName, expectedState, prefix, custom) { + prefix += ": "; + return function(e) { + var name = nameTemplate.replace(/%1/, eventName); + ok(e.constructor == Event, prefix + "event should be an Event"); + ok(e.type == eventName, prefix + "event type should be " + eventName); + ok(!e.bubbles, prefix + "event should not bubble"); + ok(!e.cancelable, prefix + "event should not be cancelable"); + ok(e.target == self, prefix + "the event target should be the worker scope"); + ok(eventName == 'online' ? navigator.onLine : !navigator.onLine, prefix + "navigator.onLine " + navigator.onLine + " should reflect event " + eventName); + + if (custom) { + custom(); + } + } +} + + + diff --git a/dom/workers/test/promise_worker.js b/dom/workers/test/promise_worker.js new file mode 100644 index 000000000..5b0a8478b --- /dev/null +++ b/dom/workers/test/promise_worker.js @@ -0,0 +1,856 @@ +function ok(a, msg) { + dump("OK: " + !!a + " => " + a + " " + msg + "\n"); + postMessage({type: 'status', status: !!a, msg: a + ": " + msg }); +} + +function todo(a, msg) { + dump("TODO: " + !a + " => " + a + " " + msg + "\n"); + postMessage({type: 'status', status: !a, msg: a + ": " + msg }); +} + +function is(a, b, msg) { + dump("IS: " + (a===b) + " => " + a + " | " + b + " " + msg + "\n"); + postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg }); +} + +function isnot(a, b, msg) { + dump("ISNOT: " + (a!==b) + " => " + a + " | " + b + " " + msg + "\n"); + postMessage({type: 'status', status: a !== b, msg: a + " !== " + b + ": " + msg }); +} + +function promiseResolve() { + ok(Promise, "Promise object should exist"); + + var promise = new Promise(function(resolve, reject) { + ok(resolve, "Promise.resolve exists"); + ok(reject, "Promise.reject exists"); + + resolve(42); + }).then(function(what) { + ok(true, "Then - resolveCb has been called"); + is(what, 42, "ResolveCb received 42"); + runTest(); + }, function() { + ok(false, "Then - rejectCb has been called"); + runTest(); + }); +} + +function promiseResolveNoArg() { + var promise = new Promise(function(resolve, reject) { + ok(resolve, "Promise.resolve exists"); + ok(reject, "Promise.reject exists"); + + resolve(); + }).then(function(what) { + ok(true, "Then - resolveCb has been called"); + is(what, undefined, "ResolveCb received undefined"); + runTest(); + }, function() { + ok(false, "Then - rejectCb has been called"); + runTest(); + }); +} + +function promiseRejectNoHandler() { + // This test only checks that the code that reports unhandled errors in the + // Promises implementation does not crash or leak. + var promise = new Promise(function(res, rej) { + noSuchMethod(); + }); + runTest(); +} + +function promiseReject() { + var promise = new Promise(function(resolve, reject) { + reject(42); + }).then(function(what) { + ok(false, "Then - resolveCb has been called"); + runTest(); + }, function(what) { + ok(true, "Then - rejectCb has been called"); + is(what, 42, "RejectCb received 42"); + runTest(); + }); +} + +function promiseRejectNoArg() { + var promise = new Promise(function(resolve, reject) { + reject(); + }).then(function(what) { + ok(false, "Then - resolveCb has been called"); + runTest(); + }, function(what) { + ok(true, "Then - rejectCb has been called"); + is(what, undefined, "RejectCb received undefined"); + runTest(); + }); +} + +function promiseException() { + var promise = new Promise(function(resolve, reject) { + throw 42; + }).then(function(what) { + ok(false, "Then - resolveCb has been called"); + runTest(); + }, function(what) { + ok(true, "Then - rejectCb has been called"); + is(what, 42, "RejectCb received 42"); + runTest(); + }); +} + +function promiseAsync_TimeoutResolveThen() { + var handlerExecuted = false; + + setTimeout(function() { + ok(handlerExecuted, "Handler should have been called before the timeout."); + + // Allow other assertions to run so the test could fail before the next one. + setTimeout(runTest, 0); + }, 0); + + Promise.resolve().then(function() { + handlerExecuted = true; + }); + + ok(!handlerExecuted, "Handlers are not called before 'then' returns."); +} + +function promiseAsync_ResolveTimeoutThen() { + var handlerExecuted = false; + + var promise = Promise.resolve(); + + setTimeout(function() { + ok(handlerExecuted, "Handler should have been called before the timeout."); + + // Allow other assertions to run so the test could fail before the next one. + setTimeout(runTest, 0); + }, 0); + + promise.then(function() { + handlerExecuted = true; + }); + + ok(!handlerExecuted, "Handlers are not called before 'then' returns."); +} + +function promiseAsync_ResolveThenTimeout() { + var handlerExecuted = false; + + Promise.resolve().then(function() { + handlerExecuted = true; + }); + + setTimeout(function() { + ok(handlerExecuted, "Handler should have been called before the timeout."); + + // Allow other assertions to run so the test could fail before the next one. + setTimeout(runTest, 0); + }, 0); + + ok(!handlerExecuted, "Handlers are not called before 'then' returns."); +} + +function promiseAsync_SyncXHRAndImportScripts() +{ + var handlerExecuted = false; + + Promise.resolve().then(function() { + handlerExecuted = true; + + // Allow other assertions to run so the test could fail before the next one. + setTimeout(runTest, 0); + }); + + ok(!handlerExecuted, "Handlers are not called until the next microtask."); + + var xhr = new XMLHttpRequest(); + xhr.open("GET", "testXHR.txt", false); + xhr.send(null); + + ok(!handlerExecuted, "Sync XHR should not trigger microtask execution."); + + importScripts("../../../dom/xhr/tests/relativeLoad_import.js"); + + ok(!handlerExecuted, "importScripts should not trigger microtask execution."); +} + +function promiseDoubleThen() { + var steps = 0; + var promise = new Promise(function(r1, r2) { + r1(42); + }); + + promise.then(function(what) { + ok(true, "Then.resolve has been called"); + is(what, 42, "Value == 42"); + steps++; + }, function(what) { + ok(false, "Then.reject has been called"); + }); + + promise.then(function(what) { + ok(true, "Then.resolve has been called"); + is(steps, 1, "Then.resolve - step == 1"); + is(what, 42, "Value == 42"); + runTest(); + }, function(what) { + ok(false, "Then.reject has been called"); + }); +} + +function promiseThenException() { + var promise = new Promise(function(resolve, reject) { + resolve(42); + }); + + promise.then(function(what) { + ok(true, "Then.resolve has been called"); + throw "booh"; + }).catch(function(e) { + ok(true, "Catch has been called!"); + runTest(); + }); +} + +function promiseThenCatchThen() { + var promise = new Promise(function(resolve, reject) { + resolve(42); + }); + + var promise2 = promise.then(function(what) { + ok(true, "Then.resolve has been called"); + is(what, 42, "Value == 42"); + return what + 1; + }, function(what) { + ok(false, "Then.reject has been called"); + }); + + isnot(promise, promise2, "These 2 promise objs are different"); + + promise2.then(function(what) { + ok(true, "Then.resolve has been called"); + is(what, 43, "Value == 43"); + return what + 1; + }, function(what) { + ok(false, "Then.reject has been called"); + }).catch(function() { + ok(false, "Catch has been called"); + }).then(function(what) { + ok(true, "Then.resolve has been called"); + is(what, 44, "Value == 44"); + runTest(); + }, function(what) { + ok(false, "Then.reject has been called"); + }); +} + +function promiseRejectThenCatchThen() { + var promise = new Promise(function(resolve, reject) { + reject(42); + }); + + var promise2 = promise.then(function(what) { + ok(false, "Then.resolve has been called"); + }, function(what) { + ok(true, "Then.reject has been called"); + is(what, 42, "Value == 42"); + return what + 1; + }); + + isnot(promise, promise2, "These 2 promise objs are different"); + + promise2.then(function(what) { + ok(true, "Then.resolve has been called"); + is(what, 43, "Value == 43"); + return what+1; + }).catch(function(what) { + ok(false, "Catch has been called"); + }).then(function(what) { + ok(true, "Then.resolve has been called"); + is(what, 44, "Value == 44"); + runTest(); + }); +} + +function promiseRejectThenCatchThen2() { + var promise = new Promise(function(resolve, reject) { + reject(42); + }); + + promise.then(function(what) { + ok(true, "Then.resolve has been called"); + is(what, 42, "Value == 42"); + return what+1; + }).catch(function(what) { + is(what, 42, "Value == 42"); + ok(true, "Catch has been called"); + return what+1; + }).then(function(what) { + ok(true, "Then.resolve has been called"); + is(what, 43, "Value == 43"); + runTest(); + }); +} + +function promiseRejectThenCatchExceptionThen() { + var promise = new Promise(function(resolve, reject) { + reject(42); + }); + + promise.then(function(what) { + ok(false, "Then.resolve has been called"); + }, function(what) { + ok(true, "Then.reject has been called"); + is(what, 42, "Value == 42"); + throw(what + 1); + }).catch(function(what) { + ok(true, "Catch has been called"); + is(what, 43, "Value == 43"); + return what + 1; + }).then(function(what) { + ok(true, "Then.resolve has been called"); + is(what, 44, "Value == 44"); + runTest(); + }); +} + +function promiseThenCatchOrderingResolve() { + var global = 0; + var f = new Promise(function(r1, r2) { + r1(42); + }); + + f.then(function() { + f.then(function() { + global++; + }); + f.catch(function() { + global++; + }); + f.then(function() { + global++; + }); + setTimeout(function() { + is(global, 2, "Many steps... should return 2"); + runTest(); + }, 0); + }); +} + +function promiseThenCatchOrderingReject() { + var global = 0; + var f = new Promise(function(r1, r2) { + r2(42); + }) + + f.then(function() {}, function() { + f.then(function() { + global++; + }); + f.catch(function() { + global++; + }); + f.then(function() {}, function() { + global++; + }); + setTimeout(function() { + is(global, 2, "Many steps... should return 2"); + runTest(); + }, 0); + }); +} + +function promiseThenNoArg() { + var promise = new Promise(function(resolve, reject) { + resolve(42); + }); + + var clone = promise.then(); + isnot(promise, clone, "These 2 promise objs are different"); + promise.then(function(v) { + clone.then(function(cv) { + is(v, cv, "Both resolve to the same value"); + runTest(); + }); + }); +} + +function promiseThenUndefinedResolveFunction() { + var promise = new Promise(function(resolve, reject) { + reject(42); + }); + + try { + promise.then(undefined, function(v) { + is(v, 42, "Promise rejected with 42"); + runTest(); + }); + } catch (e) { + ok(false, "then should not throw on undefined resolve function"); + } +} + +function promiseThenNullResolveFunction() { + var promise = new Promise(function(resolve, reject) { + reject(42); + }); + + try { + promise.then(null, function(v) { + is(v, 42, "Promise rejected with 42"); + runTest(); + }); + } catch (e) { + ok(false, "then should not throw on null resolve function"); + } +} + +function promiseCatchNoArg() { + var promise = new Promise(function(resolve, reject) { + reject(42); + }); + + var clone = promise.catch(); + isnot(promise, clone, "These 2 promise objs are different"); + promise.catch(function(v) { + clone.catch(function(cv) { + is(v, cv, "Both reject to the same value"); + runTest(); + }); + }); +} + +function promiseNestedPromise() { + new Promise(function(resolve, reject) { + resolve(new Promise(function(resolve, reject) { + ok(true, "Nested promise is executed"); + resolve(42); + })); + }).then(function(value) { + is(value, 42, "Nested promise is executed and then == 42"); + runTest(); + }); +} + +function promiseNestedNestedPromise() { + new Promise(function(resolve, reject) { + resolve(new Promise(function(resolve, reject) { + ok(true, "Nested promise is executed"); + resolve(42); + }).then(function(what) { return what+1; })); + }).then(function(value) { + is(value, 43, "Nested promise is executed and then == 43"); + runTest(); + }); +} + +function promiseWrongNestedPromise() { + new Promise(function(resolve, reject) { + resolve(new Promise(function(r, r2) { + ok(true, "Nested promise is executed"); + r(42); + })); + reject(42); + }).then(function(value) { + is(value, 42, "Nested promise is executed and then == 42"); + runTest(); + }, function(value) { + ok(false, "This is wrong"); + }); +} + +function promiseLoop() { + new Promise(function(resolve, reject) { + resolve(new Promise(function(r1, r2) { + ok(true, "Nested promise is executed"); + r1(new Promise(function(r1, r2) { + ok(true, "Nested nested promise is executed"); + r1(42); + })); + })); + }).then(function(value) { + is(value, 42, "Nested nested promise is executed and then == 42"); + runTest(); + }, function(value) { + ok(false, "This is wrong"); + }); +} + +function promiseStaticReject() { + var promise = Promise.reject(42).then(function(what) { + ok(false, "This should not be called"); + }, function(what) { + is(what, 42, "Value == 42"); + runTest(); + }); +} + +function promiseStaticResolve() { + var promise = Promise.resolve(42).then(function(what) { + is(what, 42, "Value == 42"); + runTest(); + }, function() { + ok(false, "This should not be called"); + }); +} + +function promiseResolveNestedPromise() { + var promise = Promise.resolve(new Promise(function(r, r2) { + ok(true, "Nested promise is executed"); + r(42); + }, function() { + ok(false, "This should not be called"); + })).then(function(what) { + is(what, 42, "Value == 42"); + runTest(); + }, function() { + ok(false, "This should not be called"); + }); +} + +function promiseRejectNoHandler() { + // This test only checks that the code that reports unhandled errors in the + // Promises implementation does not crash or leak. + var promise = new Promise(function(res, rej) { + noSuchMethod(); + }); + runTest(); +} + +function promiseUtilitiesDefined() { + ok(Promise.all, "Promise.all must be defined when Promise is enabled."); + ok(Promise.race, "Promise.race must be defined when Promise is enabled."); + runTest(); +} + +function promiseAllArray() { + var p = Promise.all([1, new Date(), Promise.resolve("firefox")]); + ok(p instanceof Promise, "Return value of Promise.all should be a Promise."); + p.then(function(values) { + ok(Array.isArray(values), "Resolved value should be an array."); + is(values.length, 3, "Resolved array length should match iterable's length."); + is(values[0], 1, "Array values should match."); + ok(values[1] instanceof Date, "Array values should match."); + is(values[2], "firefox", "Array values should match."); + runTest(); + }, function() { + ok(false, "Promise.all shouldn't fail when iterable has no rejected Promises."); + runTest(); + }); +} + +function promiseAllWaitsForAllPromises() { + var arr = [ + new Promise(function(resolve) { + setTimeout(resolve.bind(undefined, 1), 50); + }), + new Promise(function(resolve) { + setTimeout(resolve.bind(undefined, 2), 10); + }), + new Promise(function(resolve) { + setTimeout(resolve.bind(undefined, new Promise(function(resolve2) { + resolve2(3); + })), 10); + }), + new Promise(function(resolve) { + setTimeout(resolve.bind(undefined, 4), 20); + }) + ]; + + var p = Promise.all(arr); + p.then(function(values) { + ok(Array.isArray(values), "Resolved value should be an array."); + is(values.length, 4, "Resolved array length should match iterable's length."); + is(values[0], 1, "Array values should match."); + is(values[1], 2, "Array values should match."); + is(values[2], 3, "Array values should match."); + is(values[3], 4, "Array values should match."); + runTest(); + }, function() { + ok(false, "Promise.all shouldn't fail when iterable has no rejected Promises."); + runTest(); + }); +} + +function promiseAllRejectFails() { + var arr = [ + new Promise(function(resolve) { + setTimeout(resolve.bind(undefined, 1), 50); + }), + new Promise(function(resolve, reject) { + setTimeout(reject.bind(undefined, 2), 10); + }), + new Promise(function(resolve) { + setTimeout(resolve.bind(undefined, 3), 10); + }), + new Promise(function(resolve) { + setTimeout(resolve.bind(undefined, 4), 20); + }) + ]; + + var p = Promise.all(arr); + p.then(function(values) { + ok(false, "Promise.all shouldn't resolve when iterable has rejected Promises."); + runTest(); + }, function(e) { + ok(true, "Promise.all should reject when iterable has rejected Promises."); + is(e, 2, "Rejection value should match."); + runTest(); + }); +} + +function promiseRaceEmpty() { + var p = Promise.race([]); + ok(p instanceof Promise, "Should return a Promise."); + // An empty race never resolves! + runTest(); +} + +function promiseRaceValuesArray() { + var p = Promise.race([true, new Date(), 3]); + ok(p instanceof Promise, "Should return a Promise."); + p.then(function(winner) { + is(winner, true, "First value should win."); + runTest(); + }, function(err) { + ok(false, "Should not fail " + err + "."); + runTest(); + }); +} + +function promiseRacePromiseArray() { + var arr = [ + new Promise(function(resolve) { + resolve("first"); + }), + Promise.resolve("second"), + new Promise(function() {}), + new Promise(function(resolve) { + setTimeout(function() { + setTimeout(function() { + resolve("fourth"); + }, 0); + }, 0); + }), + ]; + + var p = Promise.race(arr); + p.then(function(winner) { + is(winner, "first", "First queued resolution should win the race."); + runTest(); + }); +} + +function promiseRaceReject() { + var p = Promise.race([ + Promise.reject(new Error("Fail bad!")), + new Promise(function(resolve) { + setTimeout(resolve, 0); + }) + ]); + + p.then(function() { + ok(false, "Should not resolve when winning Promise rejected."); + runTest(); + }, function(e) { + ok(true, "Should be rejected"); + ok(e instanceof Error, "Should reject with Error."); + ok(e.message == "Fail bad!", "Message should match."); + runTest(); + }); +} + +function promiseRaceThrow() { + var p = Promise.race([ + new Promise(function(resolve) { + nonExistent(); + }), + new Promise(function(resolve) { + setTimeout(resolve, 0); + }) + ]); + + p.then(function() { + ok(false, "Should not resolve when winning Promise had an error."); + runTest(); + }, function(e) { + ok(true, "Should be rejected"); + ok(e instanceof ReferenceError, "Should reject with ReferenceError for function nonExistent()."); + runTest(); + }); +} + +function promiseResolveArray() { + var p = Promise.resolve([1,2,3]); + ok(p instanceof Promise, "Should return a Promise."); + p.then(function(v) { + ok(Array.isArray(v), "Resolved value should be an Array"); + is(v.length, 3, "Length should match"); + is(v[0], 1, "Resolved value should match original"); + is(v[1], 2, "Resolved value should match original"); + is(v[2], 3, "Resolved value should match original"); + runTest(); + }); +} + +function promiseResolveThenable() { + var p = Promise.resolve({ then: function(onFulfill, onReject) { onFulfill(2); } }); + ok(p instanceof Promise, "Should cast to a Promise."); + p.then(function(v) { + is(v, 2, "Should resolve to 2."); + runTest(); + }, function(e) { + ok(false, "promiseResolveThenable should've resolved"); + runTest(); + }); +} + +function promiseResolvePromise() { + var original = Promise.resolve(true); + var cast = Promise.resolve(original); + + ok(cast instanceof Promise, "Should cast to a Promise."); + is(cast, original, "Should return original Promise."); + cast.then(function(v) { + is(v, true, "Should resolve to true."); + runTest(); + }); +} + +// Bug 1009569. +// Ensure that thenables are run on a clean stack asynchronously. +// Test case adopted from +// https://gist.github.com/getify/d64bb01751b50ed6b281#file-bug1-js. +function promiseResolveThenableCleanStack() { + function immed(s) { x++; s(); } + function incX(){ x++; } + + var x = 0; + var thenable = { then: immed }; + var results = []; + + var p = Promise.resolve(thenable).then(incX); + results.push(x); + + // check what happens after all "next cycle" steps + // have had a chance to complete + setTimeout(function(){ + // Result should be [0, 2] since `thenable` will be called async. + is(results[0], 0, "Expected thenable to be called asynchronously"); + // See Bug 1023547 comment 13 for why this check has to be gated on p. + p.then(function() { + results.push(x); + is(results[1], 2, "Expected thenable to be called asynchronously"); + runTest(); + }); + },1000); +} + +// Bug 1062323 +function promiseWrapperAsyncResolution() +{ + var p = new Promise(function(resolve, reject){ + resolve(); + }); + + var results = []; + var q = p.then(function () { + results.push("1-1"); + }).then(function () { + results.push("1-2"); + }).then(function () { + results.push("1-3"); + }); + + var r = p.then(function () { + results.push("2-1"); + }).then(function () { + results.push("2-2"); + }).then(function () { + results.push("2-3"); + }); + + Promise.all([q, r]).then(function() { + var match = results[0] == "1-1" && + results[1] == "2-1" && + results[2] == "1-2" && + results[3] == "2-2" && + results[4] == "1-3" && + results[5] == "2-3"; + ok(match, "Chained promises should resolve asynchronously."); + runTest(); + }, function() { + ok(false, "promiseWrapperAsyncResolution: One of the promises failed."); + runTest(); + }); +} + +var tests = [ + promiseResolve, + promiseReject, + promiseException, + promiseAsync_TimeoutResolveThen, + promiseAsync_ResolveTimeoutThen, + promiseAsync_ResolveThenTimeout, + promiseAsync_SyncXHRAndImportScripts, + promiseDoubleThen, + promiseThenException, + promiseThenCatchThen, + promiseRejectThenCatchThen, + promiseRejectThenCatchThen2, + promiseRejectThenCatchExceptionThen, + promiseThenCatchOrderingResolve, + promiseThenCatchOrderingReject, + promiseNestedPromise, + promiseNestedNestedPromise, + promiseWrongNestedPromise, + promiseLoop, + promiseStaticReject, + promiseStaticResolve, + promiseResolveNestedPromise, + promiseResolveNoArg, + promiseRejectNoArg, + + promiseThenNoArg, + promiseThenUndefinedResolveFunction, + promiseThenNullResolveFunction, + promiseCatchNoArg, + promiseRejectNoHandler, + + promiseUtilitiesDefined, + + promiseAllArray, + promiseAllWaitsForAllPromises, + promiseAllRejectFails, + + promiseRaceEmpty, + promiseRaceValuesArray, + promiseRacePromiseArray, + promiseRaceReject, + promiseRaceThrow, + + promiseResolveArray, + promiseResolveThenable, + promiseResolvePromise, + + promiseResolveThenableCleanStack, + + promiseWrapperAsyncResolution, +]; + +function runTest() { + if (!tests.length) { + postMessage({ type: 'finish' }); + return; + } + + var test = tests.shift(); + test(); +} + +onmessage = function() { + runTest(); +} diff --git a/dom/workers/test/recursion_worker.js b/dom/workers/test/recursion_worker.js new file mode 100644 index 000000000..1a61ec9d1 --- /dev/null +++ b/dom/workers/test/recursion_worker.js @@ -0,0 +1,46 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This function should never run on a too much recursion error. +onerror = function(event) { + postMessage(event.message); +}; + +// Pure JS recursion +function recurse() { + recurse(); +} + +// JS -> C++ -> JS -> C++ recursion +function recurse2() { + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function() { + xhr.open("GET", "nonexistent.file"); + } + xhr.open("GET", "nonexistent.file"); +} + +var messageCount = 0; +onmessage = function(event) { + switch (++messageCount) { + case 2: + recurse2(); + + // An exception thrown from an event handler like xhr.onreadystatechange + // should not leave an exception pending in the code that generated the + // event. + postMessage("Done"); + return; + + case 1: + recurse(); + throw "Exception should have prevented us from getting here!"; + + default: + throw "Weird number of messages: " + messageCount; + } + + throw "Impossible to get here!"; +} diff --git a/dom/workers/test/recursiveOnerror_worker.js b/dom/workers/test/recursiveOnerror_worker.js new file mode 100644 index 000000000..e6656d68f --- /dev/null +++ b/dom/workers/test/recursiveOnerror_worker.js @@ -0,0 +1,11 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onerror = function(message, filename, lineno) { + throw new Error("2"); +}; + +onmessage = function(event) { + throw new Error("1"); +}; diff --git a/dom/workers/test/redirect_to_foreign.sjs b/dom/workers/test/redirect_to_foreign.sjs new file mode 100644 index 000000000..1a5e24a59 --- /dev/null +++ b/dom/workers/test/redirect_to_foreign.sjs @@ -0,0 +1,4 @@ +function handleRequest(request, response) { + response.setStatusLine("1.1", 302, "Found"); + response.setHeader("Location", "http://example.org/tests/dom/workers/test/foreign.js"); +} diff --git a/dom/workers/test/referrer.sjs b/dom/workers/test/referrer.sjs new file mode 100644 index 000000000..dcfd1fe37 --- /dev/null +++ b/dom/workers/test/referrer.sjs @@ -0,0 +1,15 @@ +function handleRequest(request, response) +{ + if (request.queryString == "result") { + response.write(getState("referer")); + setState("referer", "INVALID"); + } else if (request.queryString == "worker") { + response.setHeader("Content-Type", "text/javascript", false); + response.write("onmessage = function() { postMessage(42); }"); + setState("referer", request.getHeader("referer")); + } else if (request.queryString == 'import') { + setState("referer", request.getHeader("referer")); + response.write("'hello world'"); + } +} + diff --git a/dom/workers/test/referrer_test_server.sjs b/dom/workers/test/referrer_test_server.sjs new file mode 100644 index 000000000..550fefb8a --- /dev/null +++ b/dom/workers/test/referrer_test_server.sjs @@ -0,0 +1,101 @@ +Components.utils.importGlobalProperties(["URLSearchParams"]); +const SJS = "referrer_test_server.sjs?"; +const SHARED_KEY = SJS; + +var SAME_ORIGIN = "https://example.com/tests/dom/workers/test/" + SJS; +var CROSS_ORIGIN = "https://test2.example.com/tests/dom/workers/test/" + SJS; +var DOWNGRADE = "http://example.com/tests/dom/workers/test/" + SJS; + +function createUrl(aRequestType, aPolicy) { + var searchParams = new URLSearchParams(); + searchParams.append("ACTION", "request-worker"); + searchParams.append("Referrer-Policy", aPolicy); + searchParams.append("TYPE", aRequestType); + + var url = SAME_ORIGIN; + + if (aRequestType === "cross-origin") { + url = CROSS_ORIGIN; + } else if (aRequestType === "downgrade") { + url = DOWNGRADE; + } + + return url + searchParams.toString(); +} +function createWorker (aRequestType, aPolicy) { + return ` + onmessage = function() { + fetch("${createUrl(aRequestType, aPolicy)}").then(function () { + postMessage(42); + close(); + }); + } + `; +} + +function handleRequest(request, response) { + var params = new URLSearchParams(request.queryString); + var policy = params.get("Referrer-Policy"); + var type = params.get("TYPE"); + var action = params.get("ACTION"); + response.setHeader("Content-Security-Policy", "default-src *", false); + response.setHeader("Access-Control-Allow-Origin", "*", false); + + if (policy) { + response.setHeader("Referrer-Policy", policy, false); + } + + if (action === "test") { + response.setHeader("Content-Type", "text/javascript", false); + response.write(createWorker(type, policy)); + return; + } + + if (action === "resetState") { + setSharedState(SHARED_KEY, "{}"); + response.write(""); + return; + } + + if (action === "get-test-results") { + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Type", "text/plain", false); + response.write(getSharedState(SHARED_KEY)); + return; + } + + if (action === "request-worker") { + var result = getSharedState(SHARED_KEY); + result = result ? JSON.parse(result) : {}; + var referrerLevel = "none"; + var test = {}; + + if (request.hasHeader("Referer")) { + var referrer = request.getHeader("Referer"); + if (referrer.indexOf("referrer_test_server") > 0) { + referrerLevel = "full"; + } else if (referrer.indexOf("https://example.com") == 0) { + referrerLevel = "origin"; + } else { + // this is never supposed to happen + referrerLevel = "other-origin"; + } + test.referrer = referrer; + } else { + test.referrer = ""; + } + + test.policy = referrerLevel; + test.expected = policy; + + // test id equals type + "-" + policy + // Ex: same-origin-default + result[type + "-" + policy] = test; + setSharedState(SHARED_KEY, JSON.stringify(result)); + + response.write("'hello world'"); + return; + } +} + + diff --git a/dom/workers/test/referrer_worker.html b/dom/workers/test/referrer_worker.html new file mode 100644 index 000000000..5693fc340 --- /dev/null +++ b/dom/workers/test/referrer_worker.html @@ -0,0 +1,145 @@ +<!DOCTYPE html> +<html> +<head> +</head> +<body onload="tests.next();"> +<script type="text/javascript;version=1.7"> +const SJS = "referrer_test_server.sjs?"; +const BASE_URL = "https://example.com/tests/dom/workers/test/" + SJS; +const GET_RESULT = BASE_URL + 'ACTION=get-test-results'; +const RESET_STATE = BASE_URL + 'ACTION=resetState'; + +function ok(val, message) { + val = val ? "true" : "false"; + window.parent.postMessage("SimpleTest.ok(" + val + ", '" + message + "');", "*"); +} + +function info(val) { + window.parent.postMessage("SimpleTest.info(" + val + ");", "*"); +} + +function is(a, b, message) { + ok(a == b, message); +} + +function finish() { + // Let window.onerror have a chance to fire + setTimeout(function() { + setTimeout(function() { + tests.close(); + window.parent.postMessage("SimpleTest.finish();", "*"); + }, 0); + }, 0); +} + +var testCases = { + 'same-origin': { 'Referrer-Policy' : { 'default' : 'full', + 'origin' : 'origin', + 'origin-when-cross-origin' : 'full', + 'unsafe-url' : 'full', + 'same-origin' : 'full', + 'strict-origin' : 'origin', + 'strict-origin-when-cross-origin' : 'full', + 'no-referrer' : 'none', + 'unsafe-url, no-referrer' : 'none', + 'invalid' : 'full' }}, + + 'cross-origin': { 'Referrer-Policy' : { 'default' : 'full', + 'origin' : 'origin', + 'origin-when-cross-origin' : 'origin', + 'unsafe-url' : 'full', + 'same-origin' : 'none', + 'strict-origin' : 'origin', + 'strict-origin-when-cross-origin' : 'origin', + 'no-referrer' : 'none', + 'unsafe-url, no-referrer' : 'none', + 'invalid' : 'full' }}, + + // Downgrading in worker is blocked entirely without unblock option + // https://bugzilla.mozilla.org/show_bug.cgi?id=1198078#c17 + // Skip the downgrading test + /* 'downgrade': { 'Referrer-Policy' : { 'default' : 'full', + 'origin' : 'full', + 'origin-when-cross-origin"' : 'full', + 'unsafe-url' : 'full', + 'same-origin' : 'none', + 'strict-origin' : 'none', + 'strict-origin-when-cross-origin' : 'none', + 'no-referrer' : 'full', + 'unsafe-url, no-referrer' : 'none', + 'invalid' : 'full' }}, */ + + +}; + +var advance = function() { tests.next(); }; + +/** + * helper to perform an XHR + * to do checkIndividualResults and resetState + */ +function doXHR(aUrl, onSuccess, onFail) { + var xhr = new XMLHttpRequest({mozSystem: true}); + xhr.responseType = "json"; + xhr.onload = function () { + onSuccess(xhr); + }; + xhr.onerror = function () { + onFail(xhr); + }; + xhr.open('GET', aUrl, true); + xhr.send(null); +} + + +function resetState() { + doXHR(RESET_STATE, + advance, + function(xhr) { + ok(false, "error in reset state"); + finish(); + }); +} + +function checkIndividualResults(aType, aPolicy, aExpected) { + var onload = xhr => { + var results = xhr.response; + dump(JSON.stringify(xhr.response)); + // test id equals type + "-" + policy + // Ex: same-origin-default + var id = aType + "-" + aPolicy; + ok(id in results, id + " tests have to be performed."); + is(results[id].policy, aExpected, id + ' --- ' + results[id].policy + ' (' + results[id].referrer + ')'); + advance(); + }; + var onerror = xhr => { + ok(false, "Can't get results from the counter server."); + finish(); + }; + doXHR(GET_RESULT, onload, onerror); +} + +var tests = (function() { + + for (var type in testCases) { + for (var policy in testCases[type]['Referrer-Policy']) { + yield resetState(); + var searchParams = new URLSearchParams(); + searchParams.append("TYPE", type); + searchParams.append("ACTION", "test"); + searchParams.append("Referrer-Policy", policy); + var worker = new Worker(BASE_URL + searchParams.toString()); + worker.onmessage = function () { + advance(); + }; + yield worker.postMessage(42); + yield checkIndividualResults(type, policy, escape(testCases[type]['Referrer-Policy'][policy])); + } + } + + // complete. Be sure to yield so we don't call this twice. + yield finish(); +})(); +</script> +</body> +</html> diff --git a/dom/workers/test/rvals_worker.js b/dom/workers/test/rvals_worker.js new file mode 100644 index 000000000..51f9ae723 --- /dev/null +++ b/dom/workers/test/rvals_worker.js @@ -0,0 +1,13 @@ +onmessage = function(evt) { + postMessage(postMessage('ignore') == undefined); + + var id = setInterval(function() {}, 200); + postMessage(clearInterval(id) == undefined); + + id = setTimeout(function() {}, 200); + postMessage(clearTimeout(id) == undefined); + + postMessage(dump(42 + '\n') == undefined); + + postMessage('finished'); +} diff --git a/dom/workers/test/script_createFile.js b/dom/workers/test/script_createFile.js new file mode 100644 index 000000000..aee7df10e --- /dev/null +++ b/dom/workers/test/script_createFile.js @@ -0,0 +1,15 @@ +var { classes: Cc, interfaces: Ci, utils: Cu } = Components; +Cu.importGlobalProperties(["File", "Directory"]); + +addMessageListener("file.open", function (e) { + var tmpFile = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIDirectoryService) + .QueryInterface(Ci.nsIProperties) + .get('TmpD', Ci.nsIFile) + tmpFile.append('file.txt'); + tmpFile.createUnique(Components.interfaces.nsIFile.FILE_TYPE, 0o600); + + sendAsyncMessage("file.opened", { + data: File.createFromNsIFile(tmpFile) + }); +}); 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; +}; diff --git a/dom/workers/test/sharedWorker_console.js b/dom/workers/test/sharedWorker_console.js new file mode 100644 index 000000000..932235eb7 --- /dev/null +++ b/dom/workers/test/sharedWorker_console.js @@ -0,0 +1,11 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +onconnect = function(evt) { + console.profile("Hello profiling from a SharedWorker!"); + console.log("Hello world from a SharedWorker!"); + evt.ports[0].postMessage('ok!'); +} diff --git a/dom/workers/test/sharedWorker_lifetime.js b/dom/workers/test/sharedWorker_lifetime.js new file mode 100644 index 000000000..3d9a837bb --- /dev/null +++ b/dom/workers/test/sharedWorker_lifetime.js @@ -0,0 +1,5 @@ +onconnect = function(e) { + setTimeout(function() { + e.ports[0].postMessage("Still alive!"); + }, 500); +} diff --git a/dom/workers/test/sharedWorker_ports.js b/dom/workers/test/sharedWorker_ports.js new file mode 100644 index 000000000..64672e6ab --- /dev/null +++ b/dom/workers/test/sharedWorker_ports.js @@ -0,0 +1,24 @@ +var port; +onconnect = function(evt) { + evt.source.postMessage({ type: "connected" }); + + if (!port) { + port = evt.source; + evt.source.onmessage = function(evtFromPort) { + port.postMessage({type: "status", + test: "Port from the main-thread!" == evtFromPort.data, + msg: "The message is coming from the main-thread"}); + port.postMessage({type: "status", + test: (evtFromPort.ports.length == 1), + msg: "1 port transferred"}); + + evtFromPort.ports[0].onmessage = function(evtFromPort2) { + port.postMessage({type: "status", + test: (evtFromPort2.data.type == "connected"), + msg: "The original message received" }); + port.postMessage({type: "finish"}); + close(); + } + } + } +} diff --git a/dom/workers/test/sharedWorker_privateBrowsing.js b/dom/workers/test/sharedWorker_privateBrowsing.js new file mode 100644 index 000000000..9d7ec886f --- /dev/null +++ b/dom/workers/test/sharedWorker_privateBrowsing.js @@ -0,0 +1,5 @@ +var counter = 0; +onconnect = function(evt) { + evt.ports[0].postMessage(++counter); +} + diff --git a/dom/workers/test/sharedWorker_sharedWorker.js b/dom/workers/test/sharedWorker_sharedWorker.js new file mode 100644 index 000000000..5e8e93392 --- /dev/null +++ b/dom/workers/test/sharedWorker_sharedWorker.js @@ -0,0 +1,93 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +if (!("self" in this)) { + throw new Error("No 'self' exists on SharedWorkerGlobalScope!"); +} +if (this !== self) { + throw new Error("'self' not equal to global object!"); +} +if (!(self instanceof SharedWorkerGlobalScope)) { + throw new Error("self not a SharedWorkerGlobalScope instance!"); +} + +var propsToCheck = [ + "location", + "navigator", + "close", + "importScripts", + "setTimeout", + "clearTimeout", + "setInterval", + "clearInterval", + "dump", + "atob", + "btoa" +]; + +for (var index = 0; index < propsToCheck.length; index++) { + var prop = propsToCheck[index]; + if (!(prop in self)) { + throw new Error("SharedWorkerGlobalScope has no '" + prop + "' property!"); + } +} + +onconnect = function(event) { + if (!("SharedWorkerGlobalScope" in self)) { + throw new Error("SharedWorkerGlobalScope should be visible!"); + } + if (!(self instanceof SharedWorkerGlobalScope)) { + throw new Error("The global should be a SharedWorkerGlobalScope!"); + } + if (!(self instanceof WorkerGlobalScope)) { + throw new Error("The global should be a WorkerGlobalScope!"); + } + if ("DedicatedWorkerGlobalScope" in self) { + throw new Error("DedicatedWorkerGlobalScope should not be visible!"); + } + if (!(event instanceof MessageEvent)) { + throw new Error("'connect' event is not a MessageEvent!"); + } + if (!("ports" in event)) { + throw new Error("'connect' event doesn't have a 'ports' property!"); + } + if (event.ports.length != 1) { + throw new Error("'connect' event has a 'ports' property with length '" + + event.ports.length + "'!"); + } + if (!event.ports[0]) { + throw new Error("'connect' event has a null 'ports[0]' property!"); + } + if (!(event.ports[0] instanceof MessagePort)) { + throw new Error("'connect' event has a 'ports[0]' property that isn't a " + + "MessagePort!"); + } + if (!(event.ports[0] == event.source)) { + throw new Error("'connect' event source property is incorrect!"); + } + if (event.data) { + throw new Error("'connect' event has data: " + event.data); + } + + // The expression closures should trigger a warning in debug builds, but NOT + // fire error events at us. If we ever actually remove expression closures + // (in bug 1083458), we'll need something else to test this case. + (function() "Expected console warning: expression closures are deprecated"); + + event.ports[0].onmessage = function(event) { + if (!(event instanceof MessageEvent)) { + throw new Error("'message' event is not a MessageEvent!"); + } + if (!("ports" in event)) { + throw new Error("'message' event doesn't have a 'ports' property!"); + } + if (event.ports === null) { + throw new Error("'message' event has a null 'ports' property!"); + } + event.target.postMessage(event.data); + throw new Error(event.data); + }; +}; diff --git a/dom/workers/test/simpleThread_worker.js b/dom/workers/test/simpleThread_worker.js new file mode 100644 index 000000000..543d8b3dd --- /dev/null +++ b/dom/workers/test/simpleThread_worker.js @@ -0,0 +1,53 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +function messageListener(event) { + var exception; + try { + event.bubbles = true; + } + catch(e) { + exception = e; + } + + if (!(exception instanceof TypeError)) { + throw exception; + } + + switch (event.data) { + case "no-op": + break; + case "components": + postMessage(Components.toString()); + break; + case "start": + for (var i = 0; i < 1000; i++) { } + postMessage("started"); + break; + case "stop": + self.postMessage('no-op'); + postMessage("stopped"); + self.removeEventListener("message", messageListener, false); + break; + default: + throw 'Bad message: ' + event.data; + } +} + +if (!("DedicatedWorkerGlobalScope" in self)) { + throw new Error("DedicatedWorkerGlobalScope should be visible!"); +} +if (!(self instanceof DedicatedWorkerGlobalScope)) { + throw new Error("The global should be a SharedWorkerGlobalScope!"); +} +if (!(self instanceof WorkerGlobalScope)) { + throw new Error("The global should be a WorkerGlobalScope!"); +} +if ("SharedWorkerGlobalScope" in self) { + throw new Error("SharedWorkerGlobalScope should not be visible!"); +} + +addEventListener("message", { handleEvent: messageListener }); diff --git a/dom/workers/test/suspend_iframe.html b/dom/workers/test/suspend_iframe.html new file mode 100644 index 000000000..86d9d6bc1 --- /dev/null +++ b/dom/workers/test/suspend_iframe.html @@ -0,0 +1,47 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test for DOM Worker Threads Suspending</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<pre id="test"> +<div id="output"></div> +<script class="testbody" type="text/javascript"> + + var output = document.getElementById("output"); + + var worker; + + function terminateWorker() { + if (worker) { + worker.postMessage("stop"); + worker = null; + } + } + + function startWorker(messageCallback, errorCallback) { + var lastData; + worker = new Worker("suspend_worker.js"); + + worker.onmessage = function(event) { + output.textContent = (lastData ? lastData + " -> " : "") + event.data; + lastData = event.data; + messageCallback(event.data); + }; + + worker.onerror = function(event) { + this.terminate(); + errorCallback(event.message); + }; + } + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/suspend_worker.js b/dom/workers/test/suspend_worker.js new file mode 100644 index 000000000..43eb24a7a --- /dev/null +++ b/dom/workers/test/suspend_worker.js @@ -0,0 +1,13 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +var counter = 0; + +var interval = setInterval(function() { + postMessage(++counter); +}, 100); + +onmessage = function(event) { + clearInterval(interval); +} diff --git a/dom/workers/test/terminate_worker.js b/dom/workers/test/terminate_worker.js new file mode 100644 index 000000000..f1a49e032 --- /dev/null +++ b/dom/workers/test/terminate_worker.js @@ -0,0 +1,9 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function(event) { + throw "No messages should reach me!"; +} + +setInterval(function() { postMessage("Still alive!"); }, 100); diff --git a/dom/workers/test/test_404.html b/dom/workers/test/test_404.html new file mode 100644 index 000000000..e2e83a35e --- /dev/null +++ b/dom/workers/test/test_404.html @@ -0,0 +1,41 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<!-- +Tests of DOM Worker Threads +--> +<head> + <title>Test for DOM Worker Threads</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<pre id="test"> +<script class="testbody" type="text/javascript"> + + var worker = new Worker("nonexistent_worker.js"); + + worker.onmessage = function(event) { + ok(false, "Shouldn't ever get a message!"); + SimpleTest.finish(); + } + + worker.onerror = function(event) { + is(event.target, worker); + is(event.message, 'NetworkError: Failed to load worker script at "nonexistent_worker.js"'); + event.preventDefault(); + SimpleTest.finish(); + }; + + worker.postMessage("dummy"); + + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/test_WorkerDebugger.initialize.xul b/dom/workers/test/test_WorkerDebugger.initialize.xul new file mode 100644 index 000000000..9e40bb78c --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger.initialize.xul @@ -0,0 +1,58 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Test for WorkerDebugger.initialize" + 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://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <script type="application/javascript"> + <![CDATA[ + + const WORKER_URL = "WorkerDebugger.initialize_worker.js"; + const CHILD_WORKER_URL = "WorkerDebugger.initialize_childWorker.js"; + const DEBUGGER_URL = BASE_URL + "WorkerDebugger.initialize_debugger.js"; + + function test() { + Task.spawn(function* () { + SimpleTest.waitForExplicitFinish(); + + info("Create a worker that creates a child worker, wait for their " + + "debuggers to be registered, and initialize them."); + let promise = waitForMultiple([ + waitForRegister(WORKER_URL, DEBUGGER_URL), + waitForRegister(CHILD_WORKER_URL, DEBUGGER_URL) + ]); + let worker = new Worker(WORKER_URL); + yield promise; + + info("Check that the debuggers are initialized before the workers " + + "start running."); + yield waitForMultiple([ + waitForWorkerMessage(worker, "debugger"), + waitForWorkerMessage(worker, "worker"), + waitForWorkerMessage(worker, "child:debugger"), + waitForWorkerMessage(worker, "child:worker") + ]); + + 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> + </body> + <label id="test-result"/> +</window> diff --git a/dom/workers/test/test_WorkerDebugger.postMessage.xul b/dom/workers/test/test_WorkerDebugger.postMessage.xul new file mode 100644 index 000000000..7affbed21 --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger.postMessage.xul @@ -0,0 +1,61 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Test for WorkerDebugger.postMessage" + 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://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <script type="application/javascript"> + <![CDATA[ + + const WORKER_URL = "WorkerDebugger.postMessage_worker.js"; + const CHILD_WORKER_URL = "WorkerDebugger.postMessage_childWorker.js"; + const DEBUGGER_URL = BASE_URL + "WorkerDebugger.postMessage_debugger.js"; + + function test() { + Task.spawn(function* () { + SimpleTest.waitForExplicitFinish(); + + info("Create a worker that creates a child worker, wait for their " + + "debuggers to be registered, and initialize them."); + let promise = waitForMultiple([ + waitForRegister(WORKER_URL, DEBUGGER_URL), + waitForRegister(CHILD_WORKER_URL, DEBUGGER_URL) + ]); + let worker = new Worker(WORKER_URL); + let [dbg, childDbg] = yield promise; + + info("Send a request to the worker debugger. This should cause the " + + "the worker debugger to send a response."); + promise = waitForDebuggerMessage(dbg, "pong"); + dbg.postMessage("ping"); + yield promise; + + info("Send a request to the child worker debugger. This should cause " + + "the child worker debugger to send a response."); + promise = waitForDebuggerMessage(childDbg, "pong"); + childDbg.postMessage("ping"); + 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> + </body> + <label id="test-result"/> +</window> diff --git a/dom/workers/test/test_WorkerDebugger.xul b/dom/workers/test/test_WorkerDebugger.xul new file mode 100644 index 000000000..f3397bd54 --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger.xul @@ -0,0 +1,122 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Test for WorkerDebugger" + 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://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <script type="application/javascript"> + <![CDATA[ + + const WORKER_URL = "WorkerDebugger_worker.js"; + const CHILD_WORKER_URL = "WorkerDebugger_childWorker.js"; + const SHARED_WORKER_URL = "WorkerDebugger_sharedWorker.js"; + + function test() { + Task.spawn(function* () { + SimpleTest.waitForExplicitFinish(); + + info("Create a top-level chrome worker that creates a non-top-level " + + "content worker and wait for their debuggers to be registered."); + let promise = waitForMultiple([ + waitForRegister(WORKER_URL), + waitForRegister(CHILD_WORKER_URL) + ]); + worker = new ChromeWorker(WORKER_URL); + let [dbg, childDbg] = yield promise; + + info("Check that the top-level chrome worker debugger has the " + + "correct properties."); + is(dbg.isChrome, true, + "Chrome worker debugger should be chrome."); + is(dbg.parent, null, + "Top-level debugger should not have parent."); + is(dbg.type, Ci.nsIWorkerDebugger.TYPE_DEDICATED, + "Chrome worker debugger should be dedicated."); + is(dbg.window, window, + "Top-level dedicated worker debugger should have window."); + + info("Check that the non-top-level content worker debugger has the " + + "correct properties."); + is(childDbg.isChrome, false, + "Content worker debugger should be content."); + is(childDbg.parent, dbg, + "Non-top-level worker debugger should have parent."); + is(childDbg.type, Ci.nsIWorkerDebugger.TYPE_DEDICATED, + "Content worker debugger should be dedicated."); + is(childDbg.window, null, + "Non-top-level worker debugger should not have window."); + + info("Terminate the top-level chrome worker and the non-top-level " + + "content worker, and wait for their debuggers to be " + + "unregistered and closed."); + promise = waitForMultiple([ + waitForUnregister(CHILD_WORKER_URL), + waitForDebuggerClose(childDbg), + waitForUnregister(WORKER_URL), + waitForDebuggerClose(dbg), + ]); + worker.terminate(); + yield promise; + + info("Create a shared worker and wait for its debugger to be " + + "registered"); + promise = waitForRegister(SHARED_WORKER_URL); + worker = new SharedWorker(SHARED_WORKER_URL); + let sharedDbg = yield promise; + + info("Check that the shared worker debugger has the correct " + + "properties."); + is(sharedDbg.isChrome, false, + "Shared worker debugger should be content."); + is(sharedDbg.parent, null, + "Shared worker debugger should not have parent."); + is(sharedDbg.type, Ci.nsIWorkerDebugger.TYPE_SHARED, + "Shared worker debugger should be shared."); + is(sharedDbg.window, null, + "Shared worker debugger should not have window."); + + info("Create a shared worker with the same URL and check that its " + + "debugger is not registered again."); + let listener = { + onRegistered: function () { + ok(false, + "Shared worker debugger should not be registered again."); + }, + }; + wdm.addListener(listener); + worker = new SharedWorker(SHARED_WORKER_URL); + + info("Send a message to the shared worker to tell it to close " + + "itself, and wait for its debugger to be closed."); + promise = waitForMultiple([ + waitForUnregister(SHARED_WORKER_URL), + waitForDebuggerClose(sharedDbg) + ]); + worker.port.start(); + worker.port.postMessage("close"); + yield promise; + + wdm.removeListener(listener); + 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> + </body> + <label id="test-result"/> +</window> diff --git a/dom/workers/test/test_WorkerDebuggerGlobalScope.createSandbox.xul b/dom/workers/test/test_WorkerDebuggerGlobalScope.createSandbox.xul new file mode 100644 index 000000000..0440c482b --- /dev/null +++ b/dom/workers/test/test_WorkerDebuggerGlobalScope.createSandbox.xul @@ -0,0 +1,52 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Test for WorkerDebuggerGlobalScope.createSandbox" + 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://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <script type="application/javascript"> + <![CDATA[ + + const WORKER_URL = "WorkerDebuggerGlobalScope.createSandbox_worker.js"; + const DEBUGGER_URL = BASE_URL + "WorkerDebuggerGlobalScope.createSandbox_debugger.js"; + + function test() { + Task.spawn(function* () { + SimpleTest.waitForExplicitFinish(); + + info("Create a worker, wait for its debugger to be registered, and " + + "initialize it."); + let promise = waitForRegister(WORKER_URL, DEBUGGER_URL); + let worker = new Worker(WORKER_URL); + let dbg = yield promise; + + info("Send a request to the worker debugger. This should cause the " + + "worker debugger to send a response from within a sandbox."); + promise = waitForDebuggerMessage(dbg, "pong"); + dbg.postMessage("ping"); + 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> + </body> + <label id="test-result"/> +</window> + diff --git a/dom/workers/test/test_WorkerDebuggerGlobalScope.enterEventLoop.xul b/dom/workers/test/test_WorkerDebuggerGlobalScope.enterEventLoop.xul new file mode 100644 index 000000000..081395308 --- /dev/null +++ b/dom/workers/test/test_WorkerDebuggerGlobalScope.enterEventLoop.xul @@ -0,0 +1,126 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Test for WorkerDebuggerGlobalScope.enterEventLoop" + 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://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <script type="application/javascript"> + <![CDATA[ + + const WORKER_URL = "WorkerDebuggerGlobalScope.enterEventLoop_worker.js"; + const CHILD_WORKER_URL = "WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js"; + const DEBUGGER_URL = BASE_URL + "WorkerDebuggerGlobalScope.enterEventLoop_debugger.js"; + + function test() { + Task.spawn(function* () { + SimpleTest.waitForExplicitFinish(); + + info("Create a worker that creates a child worker, wait for their " + + "debuggers to be registered, and initialize them."); + let promise = waitForMultiple([ + waitForRegister(WORKER_URL, DEBUGGER_URL), + waitForRegister(CHILD_WORKER_URL, DEBUGGER_URL) + ]); + let worker = new Worker(WORKER_URL); + let [dbg, childDbg] = yield promise; + + info("Send a request to the child worker. This should cause the " + + "child worker debugger to enter a nested event loop."); + promise = waitForDebuggerMessage(childDbg, "paused"); + worker.postMessage("child:ping"); + yield promise; + + info("Send a request to the child worker debugger. This should cause " + + "the child worker debugger to enter a second nested event loop."); + promise = waitForDebuggerMessage(childDbg, "paused"); + childDbg.postMessage("eval"); + yield promise; + + info("Send a request to the child worker debugger. This should cause " + + "the child worker debugger to leave its second nested event " + + "loop. The child worker debugger should not send a response " + + "for its previous request until after it has left the nested " + + "event loop."); + promise = waitForMultiple([ + waitForDebuggerMessage(childDbg, "resumed"), + waitForDebuggerMessage(childDbg, "evalled") + ]); + childDbg.postMessage("resume"); + yield promise; + + info("Send a request to the child worker debugger. This should cause " + + "the child worker debugger to leave its first nested event loop." + + "The child worker should not send a response for its earlier " + + "request until after the child worker debugger has left the " + + "nested event loop."); + promise = waitForMultiple([ + waitForDebuggerMessage(childDbg, "resumed"), + waitForWorkerMessage(worker, "child:pong") + ]); + childDbg.postMessage("resume"); + yield promise; + + info("Send a request to the worker. This should cause the worker " + + "debugger to enter a nested event loop."); + promise = waitForDebuggerMessage(dbg, "paused"); + worker.postMessage("ping"); + yield promise; + + info("Terminate the worker. This should not cause the worker " + + "debugger to terminate as well."); + worker.terminate(); + + worker.onmessage = function () { + ok(false, "Worker should have been terminated."); + }; + + info("Send a request to the worker debugger. This should cause the " + + "worker debugger to enter a second nested event loop."); + promise = waitForDebuggerMessage(dbg, "paused"); + dbg.postMessage("eval"); + yield promise; + + info("Send a request to the worker debugger. This should cause the " + + "worker debugger to leave its second nested event loop. The " + + "worker debugger should not send a response for the previous " + + "request until after leaving the nested event loop."); + promise = waitForMultiple([ + waitForDebuggerMessage(dbg, "resumed"), + waitForDebuggerMessage(dbg, "evalled") + ]); + dbg.postMessage("resume"); + yield promise; + + info("Send a request to the worker debugger. This should cause the " + + "worker debugger to leave its first nested event loop. The " + + "worker should not send a response for its earlier request, " + + "since it has been terminated."); + promise = waitForMultiple([ + waitForDebuggerMessage(dbg, "resumed"), + ]); + dbg.postMessage("resume"); + 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> + </body> + <label id="test-result"/> +</window> diff --git a/dom/workers/test/test_WorkerDebuggerGlobalScope.reportError.xul b/dom/workers/test/test_WorkerDebuggerGlobalScope.reportError.xul new file mode 100644 index 000000000..88b078674 --- /dev/null +++ b/dom/workers/test/test_WorkerDebuggerGlobalScope.reportError.xul @@ -0,0 +1,98 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Test for WorkerDebuggerGlobalScope.reportError" + 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://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <script type="application/javascript"> + <![CDATA[ + + const WORKER_URL = "WorkerDebuggerGlobalScope.reportError_worker.js"; + const CHILD_WORKER_URL = "WorkerDebuggerGlobalScope.reportError_childWorker.js"; + const DEBUGGER_URL = BASE_URL + "WorkerDebuggerGlobalScope.reportError_debugger.js"; + + function test() { + Task.spawn(function* () { + SimpleTest.waitForExplicitFinish(); + + info("Create a worker that creates a child worker, wait for their " + + "debuggers to be registered, and initialize them."); + let promise = waitForMultiple([ + waitForRegister(WORKER_URL, DEBUGGER_URL), + waitForRegister(CHILD_WORKER_URL, DEBUGGER_URL) + ]); + let worker = new Worker(WORKER_URL); + let [dbg, childDbg] = yield promise; + + worker.onmessage = function () { + ok(false, "Debugger error events should not be fired at workers."); + }; + + info("Send a request to the worker debugger. This should cause the " + + "worker debugger to report an error."); + promise = waitForDebuggerError(dbg); + dbg.postMessage("report"); + let error = yield promise; + is(error.fileName, DEBUGGER_URL, + "fileName should be name of file from which error is reported."); + is(error.lineNumber, 6, + "lineNumber should be line number from which error is reported."); + is(error.message, "reported", "message should be reported."); + + info("Send a request to the worker debugger. This should cause the " + + "worker debugger to throw an error."); + promise = waitForDebuggerError(dbg); + dbg.postMessage("throw"); + error = yield promise; + is(error.fileName, DEBUGGER_URL, + "fileName should be name of file from which error is thrown"); + is(error.lineNumber, 9, + "lineNumber should be line number from which error is thrown"); + is(error.message, "Error: thrown", "message should be Error: thrown"); + + info("Send a reqeust to the child worker debugger. This should cause " + + "the child worker debugger to report an error."); + promise = waitForDebuggerError(childDbg); + childDbg.postMessage("report"); + error = yield promise; + is(error.fileName, DEBUGGER_URL, + "fileName should be name of file from which error is reported."); + is(error.lineNumber, 6, + "lineNumber should be line number from which error is reported."); + is(error.message, "reported", "message should be reported."); + + info("Send a message to the child worker debugger. This should cause " + + "the child worker debugger to throw an error."); + promise = waitForDebuggerError(childDbg); + childDbg.postMessage("throw"); + error = yield promise; + is(error.fileName, DEBUGGER_URL, + "fileName should be name of file from which error is thrown"); + is(error.lineNumber, 9, + "lineNumber should be line number from which error is thrown"); + is(error.message, "Error: thrown", "message should be Error: thrown"); + + 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> + </body> + <label id="test-result"/> +</window> + diff --git a/dom/workers/test/test_WorkerDebuggerGlobalScope.setImmediate.xul b/dom/workers/test/test_WorkerDebuggerGlobalScope.setImmediate.xul new file mode 100644 index 000000000..3ecac681b --- /dev/null +++ b/dom/workers/test/test_WorkerDebuggerGlobalScope.setImmediate.xul @@ -0,0 +1,54 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Test for WorkerDebuggerGlobalScope.setImmediate" + 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://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <script type="application/javascript"> + <![CDATA[ + + const WORKER_URL = "WorkerDebuggerGlobalScope.setImmediate_worker.js"; + const DEBUGGER_URL = BASE_URL + "WorkerDebuggerGlobalScope.setImmediate_debugger.js"; + + function test() { + Task.spawn(function* () { + SimpleTest.waitForExplicitFinish(); + + let promise = waitForRegister(WORKER_URL, DEBUGGER_URL); + let worker = new Worker(WORKER_URL); + let dbg = yield promise; + + info("Send a request to the worker debugger. This should cause a " + + "the worker debugger to send two responses. The worker debugger " + + "should send the second response before the first one, since " + + "the latter is delayed until the next tick of the event loop."); + promise = waitForMultiple([ + waitForDebuggerMessage(dbg, "pong2"), + waitForDebuggerMessage(dbg, "pong1") + ]); + dbg.postMessage("ping"); + 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> + </body> + <label id="test-result"/> +</window> diff --git a/dom/workers/test/test_WorkerDebuggerManager.xul b/dom/workers/test/test_WorkerDebuggerManager.xul new file mode 100644 index 000000000..6807226bd --- /dev/null +++ b/dom/workers/test/test_WorkerDebuggerManager.xul @@ -0,0 +1,106 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Test for WorkerDebuggerManager" + 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://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <script type="application/javascript"> + <![CDATA[ + + const WORKER_URL = "WorkerDebuggerManager_worker.js"; + const CHILD_WORKER_URL = "WorkerDebuggerManager_childWorker.js"; + + function test() { + Task.spawn(function* () { + SimpleTest.waitForExplicitFinish(); + + info("Check that worker debuggers are not enumerated before they are " + + "registered."); + ok(!findDebugger(WORKER_URL), + "Worker debugger should not be enumerated before it is registered."); + ok(!findDebugger(CHILD_WORKER_URL), + "Child worker debugger should not be enumerated before it is " + + "registered."); + + info("Create a worker that creates a child worker, and wait for " + + "their debuggers to be registered."); + let promise = waitForMultiple([ + waitForRegister(WORKER_URL), + waitForRegister(CHILD_WORKER_URL) + ]); + let worker = new Worker(WORKER_URL); + let [dbg, childDbg] = yield promise; + + info("Check that worker debuggers are enumerated after they are " + + "registered."); + ok(findDebugger(WORKER_URL), + "Worker debugger should be enumerated after it is registered."); + ok(findDebugger(CHILD_WORKER_URL), + "Child worker debugger should be enumerated after it is " + + "registered."); + + info("Check that worker debuggers are not closed before they are " + + "unregistered."); + is(dbg.isClosed, false, + "Worker debugger should not be closed before it is unregistered."); + is(childDbg.isClosed, false, + "Child worker debugger should not be closed before it is " + + "unregistered"); + + info("Terminate the worker and the child worker, and wait for their " + + "debuggers to be unregistered."); + promise = waitForMultiple([ + waitForUnregister(CHILD_WORKER_URL), + waitForUnregister(WORKER_URL), + ]); + worker.terminate(); + yield promise; + + info("Check that worker debuggers are not enumerated after they are " + + "unregistered."); + ok(!findDebugger(WORKER_URL), + "Worker debugger should not be enumerated after it is " + + "unregistered."); + ok(!findDebugger(CHILD_WORKER_URL), + "Child worker debugger should not be enumerated after it is " + + "unregistered."); + + info("Check that worker debuggers are closed after they are " + + "unregistered."); + is(dbg.isClosed, true, + "Worker debugger should be closed after it is unregistered."); + is(childDbg.isClosed, true, + "Child worker debugger should be closed after it is unregistered."); + + info("Check that property accesses on worker debuggers throws " + + "after they are closed."); + assertThrows(() => dbg.url, + "Property accesses on worker debugger should throw " + + "after it is closed."); + assertThrows(() => childDbg.url, + "Property accesses on child worker debugger should " + + "throw after it is closed."); + + 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> + </body> + <label id="test-result"/> +</window> diff --git a/dom/workers/test/test_WorkerDebugger_console.xul b/dom/workers/test/test_WorkerDebugger_console.xul new file mode 100644 index 000000000..0852002ea --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger_console.xul @@ -0,0 +1,97 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Test for WorkerDebuggerGlobalScope.console methods" + 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://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <script type="application/javascript"> + <![CDATA[ + + const WORKER_URL = "WorkerDebugger.console_worker.js"; + const CHILD_WORKER_URL = "WorkerDebugger.console_childWorker.js"; + const DEBUGGER_URL = BASE_URL + "WorkerDebugger.console_debugger.js"; + + consoleMessagesReceived = 0; + function test() { + function consoleListener() { + SpecialPowers.addObserver(this, "console-api-log-event", false); + } + + consoleListener.prototype = { + observe: function(aSubject, aTopic, aData) { + if (aTopic == "console-api-log-event") { + var obj = aSubject.wrappedJSObject; + if (obj.arguments[0] == "Hello from the debugger script!" && + !consoleMessagesReceived) { + consoleMessagesReceived++; + ok(true, "Something has been received"); + SpecialPowers.removeObserver(this, "console-api-log-event"); + } + } + } + } + + var cl = new consoleListener(); + + Task.spawn(function* () { + SimpleTest.waitForExplicitFinish(); + + info("Create a worker that creates a child worker, wait for their " + + "debuggers to be registered, and initialize them."); + let promise = waitForMultiple([ + waitForRegister(WORKER_URL, DEBUGGER_URL), + waitForRegister(CHILD_WORKER_URL, DEBUGGER_URL) + ]); + let worker = new Worker(WORKER_URL); + let [dbg, childDbg] = yield promise; + + info("Send a request to the worker debugger. This should cause the " + + "the worker debugger to send a response."); + dbg.addListener({ + onMessage: function(msg) { + try { + msg = JSON.parse(msg); + } catch(e) { + ok(false, "Something went wrong"); + return; + } + + if (msg.type == 'finish') { + ok(consoleMessagesReceived, "We received something via debugger console!"); + dbg.removeListener(this); + SimpleTest.finish(); + return; + } + + if (msg.type == 'status') { + ok(msg.what, msg.msg); + return; + } + + ok(false, "Something went wrong"); + } + }); + + dbg.postMessage("do magic"); + }); + } + + ]]> + </script> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <p id="display"></p> + <div id="content" style="display:none;"></div> + <pre id="test"></pre> + </body> + <label id="test-result"/> +</window> diff --git a/dom/workers/test/test_WorkerDebugger_frozen.xul b/dom/workers/test/test_WorkerDebugger_frozen.xul new file mode 100644 index 000000000..6b22e7702 --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger_frozen.xul @@ -0,0 +1,90 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Test for WorkerDebugger with frozen workers" + 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://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <script type="application/javascript"> + <![CDATA[ + + const CACHE_SUBFRAMES = "browser.sessionhistory.cache_subframes"; + const MAX_TOTAL_VIEWERS = "browser.sessionhistory.max_total_viewers"; + + const IFRAME1_URL = "WorkerDebugger_frozen_iframe1.html"; + const IFRAME2_URL = "WorkerDebugger_frozen_iframe2.html"; + + const WORKER1_URL = "WorkerDebugger_frozen_worker1.js"; + const WORKER2_URL = "WorkerDebugger_frozen_worker2.js"; + + function test() { + Task.spawn(function* () { + SimpleTest.waitForExplicitFinish(); + + var oldMaxTotalViewers = SpecialPowers.getIntPref(MAX_TOTAL_VIEWERS); + + SpecialPowers.setBoolPref(CACHE_SUBFRAMES, true); + SpecialPowers.setIntPref(MAX_TOTAL_VIEWERS, 10); + + let iframe = $("iframe"); + + let promise = waitForMultiple([ + waitForRegister(WORKER1_URL), + waitForWindowMessage(window, "ready"), + ]); + iframe.src = IFRAME1_URL; + let [dbg1] = yield promise; + is(dbg1.isClosed, false, + "debugger for worker on page 1 should not be closed"); + + promise = waitForMultiple([ + waitForUnregister(WORKER1_URL), + waitForDebuggerClose(dbg1), + waitForRegister(WORKER2_URL), + waitForWindowMessage(window, "ready"), + ]); + iframe.src = IFRAME2_URL; + let [,, dbg2] = yield promise; + is(dbg1.isClosed, true, + "debugger for worker on page 1 should be closed"); + is(dbg2.isClosed, false, + "debugger for worker on page 2 should not be closed"); + + promise = Promise.all([ + waitForUnregister(WORKER2_URL), + waitForDebuggerClose(dbg2), + waitForRegister(WORKER1_URL) + ]); + iframe.contentWindow.history.back(); + [,, dbg1] = yield promise; + is(dbg1.isClosed, false, + "debugger for worker on page 1 should not be closed"); + is(dbg2.isClosed, true, + "debugger for worker on page 2 should be closed"); + + SpecialPowers.clearUserPref(CACHE_SUBFRAMES); + SpecialPowers.setIntPref(MAX_TOTAL_VIEWERS, oldMaxTotalViewers); + + 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/test_WorkerDebugger_promise.xul b/dom/workers/test/test_WorkerDebugger_promise.xul new file mode 100644 index 000000000..24ed07133 --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger_promise.xul @@ -0,0 +1,70 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. ++ http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Test for WorkerDebugger with DOM Promises" + 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://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <script type="application/javascript"> + <![CDATA[ + + const WORKER_URL = "WorkerDebugger_promise_worker.js"; + const DEBUGGER_URL = BASE_URL + "WorkerDebugger_promise_debugger.js"; + + function test() { + Task.spawn(function* () { + SimpleTest.waitForExplicitFinish(); + + let promise = waitForRegister(WORKER_URL, DEBUGGER_URL); + let worker = new Worker(WORKER_URL); + let dbg = yield promise; + + info("Send a request to the worker. This should cause the worker " + + "to send a response."); + promise = waitForWorkerMessage(worker, "resolved"); + worker.postMessage("resolve"); + yield promise; + + info("Send a request to the debugger. This should cause the debugger " + + "to send a response."); + promise = waitForDebuggerMessage(dbg, "resolved"); + dbg.postMessage("resolve"); + yield promise; + + info("Send a request to the worker. This should cause the debugger " + + "to enter a nested event loop."); + promise = waitForDebuggerMessage(dbg, "paused"); + worker.postMessage("pause"); + yield promise; + + info("Send a request to the debugger. This should cause the debugger " + + "to leave the nested event loop."); + promise = waitForMultiple([ + waitForDebuggerMessage(dbg, "resumed"), + waitForWorkerMessage(worker, "resumed") + ]); + dbg.postMessage("resume"); + 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> + </body> + <label id="test-result"/> +</window> diff --git a/dom/workers/test/test_WorkerDebugger_suspended.xul b/dom/workers/test/test_WorkerDebugger_suspended.xul new file mode 100644 index 000000000..0ed8bb71a --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger_suspended.xul @@ -0,0 +1,75 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Test for WorkerDebugger with suspended workers" + 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://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <script type="application/javascript"> + <![CDATA[ + + const WORKER_URL = "WorkerDebugger_suspended_worker.js"; + const DEBUGGER_URL = BASE_URL + "WorkerDebugger_suspended_debugger.js"; + + function test() { + Task.spawn(function* () { + SimpleTest.waitForExplicitFinish(); + + info("Create a worker, wait for its debugger to be registered, and " + + "initialize it."); + let promise = waitForRegister(WORKER_URL, DEBUGGER_URL); + let worker = new Worker(WORKER_URL); + let dbg = yield promise; + + info("Send a request to the worker. This should cause both the " + + "worker and the worker debugger to send a response."); + promise = waitForMultiple([ + waitForWorkerMessage(worker, "worker"), + waitForDebuggerMessage(dbg, "debugger") + ]); + worker.postMessage("ping"); + yield promise; + + info("Suspend the workers for this window, and send another request " + + "to the worker. This should cause only the worker debugger to " + + "send a response."); + let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + windowUtils.suspendTimeouts(); + function onmessage() { + ok(false, "The worker should not send a response."); + }; + worker.addEventListener("message", onmessage); + promise = waitForDebuggerMessage(dbg, "debugger"); + worker.postMessage("ping"); + yield promise; + worker.removeEventListener("message", onmessage); + + info("Resume the workers for this window. This should cause the " + + "worker to send a response to the previous request."); + promise = waitForWorkerMessage(worker, "worker"); + windowUtils.resumeTimeouts(); + 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> + </body> + <label id="test-result"/> +</window> diff --git a/dom/workers/test/test_atob.html b/dom/workers/test/test_atob.html new file mode 100644 index 000000000..99419174c --- /dev/null +++ b/dom/workers/test/test_atob.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 for DOM Worker Threads</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"> +<script src="atob_worker.js" language="javascript"></script> +<script class="testbody" type="text/javascript"> + + var dataIndex = 0; + + var worker = new Worker("atob_worker.js"); + worker.onmessage = function(event) { + switch (event.data.type) { + case "done": + is(dataIndex, data.length, "Saw all values"); + SimpleTest.finish(); + return; + case "btoa": + is(btoa(data[dataIndex]), event.data.value, + "Good btoa value " + dataIndex); + break; + case "atob": + is(atob(btoa(data[dataIndex])) + "", event.data.value, + "Good round trip value " + dataIndex); + dataIndex++; + break; + default: + ok(false, "Worker posted a bad message: " + event.message); + worker.terminate(); + SimpleTest.finish(); + } + } + + worker.onerror = function(event) { + ok(false, "Worker threw an error: " + event.message); + worker.terminate(); + SimpleTest.finish(); + } + + worker.postMessage("go"); + + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_blobConstructor.html b/dom/workers/test/test_blobConstructor.html new file mode 100644 index 000000000..e8cfaf19d --- /dev/null +++ b/dom/workers/test/test_blobConstructor.html @@ -0,0 +1,60 @@ +<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<html>
+<!--
+Tests of DOM Worker Blob constructor
+-->
+<head>
+ <title>Test for DOM Worker Blob constructor</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">
+<script class="testbody" type="text/javascript">
+(function() {
+ onerror = function(e) {
+ ok(false, "Main Thread had an error: " + event.data);
+ SimpleTest.finish();
+ };
+ function f() {
+ onmessage = function(e) {
+ var b = new Blob([e.data, "World"],{type: "text/plain"});
+ var fr = new FileReaderSync();
+ postMessage({text: fr.readAsText(b), type: b.type});
+ };
+ }
+ var b = new Blob([f,"f();"]);
+ var u = URL.createObjectURL(b);
+ var w = new Worker(u);
+ w.onmessage = function(e) {
+ URL.revokeObjectURL(u);
+ is(e.data.text, fr.result);
+ is(e.data.type, "text/plain");
+ SimpleTest.finish();
+ };
+ w.onerror = function(e) {
+ is(e.target, w);
+ ok(false, "Worker had an error: " + e.message);
+ SimpleTest.finish();
+ };
+
+ b = new Blob(["Hello, "]);
+ var fr = new FileReader();
+ fr.readAsText(new Blob([b, "World"],{}));
+ fr.onload = function() {
+ w.postMessage(b);
+ };
+ SimpleTest.waitForExplicitFinish();
+})();
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/workers/test/test_blobWorkers.html b/dom/workers/test/test_blobWorkers.html new file mode 100644 index 000000000..6e2a83f53 --- /dev/null +++ b/dom/workers/test/test_blobWorkers.html @@ -0,0 +1,32 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> + <head> + <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"> + const message = "hi"; + + const workerScript = + "onmessage = function(event) {" + + " postMessage(event.data);" + + "};"; + + var worker = new Worker(URL.createObjectURL(new Blob([workerScript]))); + worker.onmessage = function(event) { + is(event.data, message, "Got correct message"); + SimpleTest.finish(); + }; + worker.postMessage(message); + + SimpleTest.waitForExplicitFinish(); + </script> + </body> +</html> + diff --git a/dom/workers/test/test_bug1002702.html b/dom/workers/test/test_bug1002702.html new file mode 100644 index 000000000..3db6d2580 --- /dev/null +++ b/dom/workers/test/test_bug1002702.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>Test for bug 1002702</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 port = new SharedWorker('data:application/javascript,1').port; +port.close(); +SpecialPowers.forceGC(); +ok(true, "No crash \\o/"); + +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/test_bug1010784.html b/dom/workers/test/test_bug1010784.html new file mode 100644 index 000000000..f746f35e6 --- /dev/null +++ b/dom/workers/test/test_bug1010784.html @@ -0,0 +1,35 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1010784 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1010784</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.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=1010784">Mozilla Bug 1010784</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + + var worker = new Worker("file_bug1010784_worker.js"); + + worker.onmessage = function(event) { + is(event.data, "done", "Got correct result"); + SimpleTest.finish(); + } + + worker.postMessage("testXHR.txt"); + + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_bug1014466.html b/dom/workers/test/test_bug1014466.html new file mode 100644 index 000000000..59f5a6185 --- /dev/null +++ b/dom/workers/test/test_bug1014466.html @@ -0,0 +1,42 @@ +<!-- +2 Any copyright is dedicated to the Public Domain. +3 http://creativecommons.org/publicdomain/zero/1.0/ +4 --> +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1014466 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1014466</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.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=1014466">Mozilla Bug 1014466</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + + var worker = new Worker("bug1014466_worker.js"); + + worker.onmessage = function(event) { + if (event.data.type == 'finish') { + SimpleTest.finish(); + } else if (event.data.type == 'status') { + ok(event.data.status, event.data.msg); + } + }; + + worker.postMessage(true); + + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_bug1020226.html b/dom/workers/test/test_bug1020226.html new file mode 100644 index 000000000..b6db2aeb4 --- /dev/null +++ b/dom/workers/test/test_bug1020226.html @@ -0,0 +1,38 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1020226 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1020226</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.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=1020226">Mozilla Bug 1020226</a> +<p id="display"></p> +<div id="content" style="display: none"> + +<iframe id="iframe" src="bug1020226_frame.html" onload="finishTest();"> +</iframe> +</div> +<pre id="test"> +<script type="application/javascript"> +function finishTest() { + document.getElementById("iframe").onload = null; + window.onmessage = function(e) { + info("Got message"); + document.getElementById("iframe").src = "about:blank"; + // We aren't really interested in the test, it shouldn't crash when the + // worker is GCed later. + ok(true, "Should not crash"); + SimpleTest.finish(); + }; +} + +SimpleTest.waitForExplicitFinish(); +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_bug1036484.html b/dom/workers/test/test_bug1036484.html new file mode 100644 index 000000000..17b9d490f --- /dev/null +++ b/dom/workers/test/test_bug1036484.html @@ -0,0 +1,54 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<!-- +Tests of DOM Worker Threads: bug 1036484 +--> +<head> + <title>Test for DOM Worker Threads: bug 1036484</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<pre id="test"> +<script class="testbody" type="text/javascript"> + +function test(script) { + var worker = new Worker(script); + + worker.onmessage = function(event) { + ok(false, "Shouldn't ever get a message!"); + } + + worker.onerror = function(event) { + is(event.target, worker); + ok(event.message.startsWith("NetworkError: Failed to load worker script")) + event.preventDefault(); + runTests(); + }; + + worker.postMessage("dummy"); +} + +var tests = [ '404_server.sjs', '404_server.sjs?js' ]; +function runTests() { + if (!tests.length) { + SimpleTest.finish(); + return; + } + + var script = tests.shift(); + test(script); +} + +SimpleTest.waitForExplicitFinish(); +runTests(); + +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/test_bug1060621.html b/dom/workers/test/test_bug1060621.html new file mode 100644 index 000000000..758bf996e --- /dev/null +++ b/dom/workers/test/test_bug1060621.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>Test for URLSearchParams object in 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"> + + var worker = new Worker("bug1060621_worker.js"); + + worker.onmessage = function(event) { + ok(true, "The operation is done. We should not leak."); + SimpleTest.finish(); + }; + + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_bug1062920.html b/dom/workers/test/test_bug1062920.html new file mode 100644 index 000000000..31061a2b1 --- /dev/null +++ b/dom/workers/test/test_bug1062920.html @@ -0,0 +1,70 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test for navigator property override</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 checkValues() { + var worker = new Worker("bug1062920_worker.js"); + + worker.onmessage = function(event) { + var ifr = document.createElement('IFRAME'); + ifr.src = "about:blank"; + + ifr.addEventListener('load', function() { + var nav = ifr.contentWindow.navigator; + is(event.data.appCodeName, nav.appCodeName, "appCodeName should match"); + is(event.data.appName, nav.appName, "appName should match"); + is(event.data.appVersion, nav.appVersion, "appVersion should match"); + is(event.data.platform, nav.platform, "platform should match"); + is(event.data.userAgent, nav.userAgent, "userAgent should match"); + is(event.data.product, nav.product, "product should match"); + runTests(); + }, false); + + document.getElementById('content').appendChild(ifr); + }; + } + + function replaceAndCheckValues() { + SpecialPowers.pushPrefEnv({"set": [ + ["general.appname.override", "appName overridden"], + ["general.appversion.override", "appVersion overridden"], + ["general.platform.override", "platform overridden"], + ["general.useragent.override", "userAgent overridden"] + ]}, checkValues); + } + + var tests = [ + checkValues, + replaceAndCheckValues + ]; + + function runTests() { + if (tests.length == 0) { + SimpleTest.finish(); + return; + } + + var test = tests.shift(); + test(); + } + + SimpleTest.waitForExplicitFinish(); + runTests(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_bug1062920.xul b/dom/workers/test/test_bug1062920.xul new file mode 100644 index 000000000..635c1b9f9 --- /dev/null +++ b/dom/workers/test/test_bug1062920.xul @@ -0,0 +1,69 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="DOM Worker Threads Test" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <script type="application/javascript"> + + function checkValues() { + var worker = new Worker("bug1062920_worker.js"); + + worker.onmessage = function(event) { + is(event.data.appCodeName, navigator.appCodeName, "appCodeName should match"); + is(event.data.appName, navigator.appName, "appName should match"); + isnot(event.data.appName, "appName overridden", "appName is not overridden"); + is(event.data.appVersion, navigator.appVersion, "appVersion should match"); + isnot(event.data.appVersion, "appVersion overridden", "appVersion is not overridden"); + is(event.data.platform, navigator.platform, "platform should match"); + isnot(event.data.platform, "platform overridden", "platform is not overridden"); + is(event.data.userAgent, navigator.userAgent, "userAgent should match"); + is(event.data.product, navigator.product, "product should match"); + runTests(); + }; + } + + function replaceAndCheckValues() { + SpecialPowers.pushPrefEnv({"set": [ + ["general.appname.override", "appName overridden"], + ["general.appversion.override", "appVersion overridden"], + ["general.platform.override", "platform overridden"], + ["general.useragent.override", "userAgent overridden"] + ]}, checkValues); + } + + var tests = [ + replaceAndCheckValues, + checkValues + ]; + + function runTests() { + if (tests.length == 0) { + SimpleTest.finish(); + return; + } + + var test = tests.shift(); + test(); + } + + SimpleTest.waitForExplicitFinish(); + runTests(); + + </script> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <p id="display"></p> + <div id="content" style="display:none;"></div> + <pre id="test"></pre> + </body> + <label id="test-result"/> +</window> diff --git a/dom/workers/test/test_bug1063538.html b/dom/workers/test/test_bug1063538.html new file mode 100644 index 000000000..7c32b8ed3 --- /dev/null +++ b/dom/workers/test/test_bug1063538.html @@ -0,0 +1,49 @@ +<!-- +2 Any copyright is dedicated to the Public Domain. +3 http://creativecommons.org/publicdomain/zero/1.0/ +4 --> +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1063538 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1063538</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.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=1063538">Mozilla Bug 1063538</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +function runTest() { + var worker = new Worker("bug1063538_worker.js"); + + worker.onmessage = function(e) { + if (e.data.type == 'finish') { + ok(e.data.progressFired, "Progress was fired."); + SimpleTest.finish(); + } + }; + + worker.postMessage(true); +} + +SimpleTest.waitForExplicitFinish(); + +addLoadEvent(function() { + SpecialPowers.pushPrefEnv({"set": [["network.jar.block-remote-files", false]]}, function() { + SpecialPowers.pushPermissions([{'type': 'systemXHR', 'allow': true, 'context': document}], runTest); + }); +}); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_bug1104064.html b/dom/workers/test/test_bug1104064.html new file mode 100644 index 000000000..9f83fd007 --- /dev/null +++ b/dom/workers/test/test_bug1104064.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>Test for bug 1104064</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 worker = new Worker("bug1104064_worker.js"); +worker.onmessage = function() { + ok(true, "setInterval has been called twice."); + SimpleTest.finish(); +} +worker.postMessage("go"); + +SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/test_bug1132395.html b/dom/workers/test/test_bug1132395.html new file mode 100644 index 000000000..30ca9b0ae --- /dev/null +++ b/dom/workers/test/test_bug1132395.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>Test for 1132395</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"> + +// This test is full of dummy debug messages. This is because I need to follow +// an hard-to-reproduce timeout failure. + +info("test started"); +var sw = new SharedWorker('bug1132395_sharedWorker.js'); +sw.port.onmessage = function(event) { + info("sw.onmessage received"); + ok(true, "We didn't crash."); + SimpleTest.finish(); +} + +sw.onerror = function(event) { + ok(false, "Failed to create a ServiceWorker"); + SimpleTest.finish(); +} + +info("sw.postmessage called"); +sw.port.postMessage('go'); + +SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_bug1132924.html b/dom/workers/test/test_bug1132924.html new file mode 100644 index 000000000..8c54813ef --- /dev/null +++ b/dom/workers/test/test_bug1132924.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>Test for 1132924</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"> + +SimpleTest.waitForExplicitFinish(); +var w = new Worker('bug1132924_worker.js'); +w.onmessage = function(event) { + ok(true, "We are still alive."); + SimpleTest.finish(); +} + +w.postMessage('go'); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_bug1278777.html b/dom/workers/test/test_bug1278777.html new file mode 100644 index 000000000..a91902d26 --- /dev/null +++ b/dom/workers/test/test_bug1278777.html @@ -0,0 +1,31 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1278777 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1278777</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.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=1278777">Mozilla Bug 1278777</a> + <script type="application/javascript"> + +SimpleTest.waitForExplicitFinish(); + +var worker = new Worker('worker_bug1278777.js'); +worker.onerror = function() { + ok(false, "We should not see any error."); + SimpleTest.finish(); +} + +worker.onmessage = function(e) { + ok(e.data, "Everything seems ok."); + SimpleTest.finish(); +} + + </script> +</body> +</html> diff --git a/dom/workers/test/test_bug1301094.html b/dom/workers/test/test_bug1301094.html new file mode 100644 index 000000000..ea396b32e --- /dev/null +++ b/dom/workers/test/test_bug1301094.html @@ -0,0 +1,69 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1301094 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1301094</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.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=1301094">Mozilla Bug 1301094</a> + <input id="file" type="file"></input> + <script type="application/javascript"> + +SimpleTest.waitForExplicitFinish(); + +var url = SimpleTest.getTestFileURL("script_createFile.js"); +script = SpecialPowers.loadChromeScript(url); + +var mainThreadOk, workerOk; + +function maybeFinish() { + if (mainThreadOk & workerOk) { + SimpleTest.finish(); + } +} + +function onOpened(message) { + var input = document.getElementById('file'); + SpecialPowers.wrap(input).mozSetDndFilesAndDirectories([message.data]); + + var worker = new Worker('worker_bug1301094.js'); + worker.onerror = function() { + ok(false, "We should not see any error."); + SimpleTest.finish(); + } + + worker.onmessage = function(e) { + ok(e.data, "Everything seems OK on the worker-side."); + + workerOk = true; + maybeFinish(); + } + + is(input.files.length, 1, "We have something"); + ok(input.files[0] instanceof Blob, "We have one Blob"); + worker.postMessage(input.files[0]); + + var xhr = new XMLHttpRequest(); + xhr.open("POST", 'worker_bug1301094.js', false); + xhr.onload = function() { + ok(xhr.responseText, "Everything seems OK on the main-thread-side."); + mainThreadOk = true; + maybeFinish(); + }; + + var fd = new FormData(); + fd.append('file', input.files[0]); + xhr.send(fd); +} + +script.addMessageListener("file.opened", onOpened); +script.sendAsyncMessage("file.open"); + + </script> +</body> +</html> diff --git a/dom/workers/test/test_bug1317725.html b/dom/workers/test/test_bug1317725.html new file mode 100644 index 000000000..c23587318 --- /dev/null +++ b/dom/workers/test/test_bug1317725.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>Test for bug 1317725</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> + +<input type="file" id="file" /> + +<script type="text/js-worker" id="worker-src"> +onmessage = function(e) { + var data = new FormData(); + data.append('Filedata', e.data.slice(0, 127), encodeURI(e.data.name)); + var xhr = new XMLHttpRequest(); + xhr.open('POST', location.href, false); + xhr.send(data); + postMessage("No crash \\o/"); +} +</script> + +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); + +var url = SimpleTest.getTestFileURL("script_createFile.js"); +script = SpecialPowers.loadChromeScript(url); + +function onOpened(message) { + var input = document.getElementById('file'); + SpecialPowers.wrap(input).mozSetFileArray([message.data]); + + var blob = new Blob([ document.getElementById("worker-src").textContent ], + { type: "text/javascript" }); + var worker = new Worker(URL.createObjectURL(blob)); + worker.onerror = function(e) { + ok(false, "We should not see any error."); + SimpleTest.finish(); + } + + worker.onmessage = function(e) { + ok(e.data, "Everything seems OK on the worker-side."); + SimpleTest.finish(); + } + + is(input.files.length, 1, "We have something"); + ok(input.files[0] instanceof Blob, "We have one Blob"); + worker.postMessage(input.files[0]); +} + +script.addMessageListener("file.opened", onOpened); +script.sendAsyncMessage("file.open"); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_bug949946.html b/dom/workers/test/test_bug949946.html new file mode 100644 index 000000000..547bdbda4 --- /dev/null +++ b/dom/workers/test/test_bug949946.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>Test for bug 949946</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"> + +new SharedWorker('sharedWorker_sharedWorker.js'); +new SharedWorker('sharedWorker_sharedWorker.js', ':'); +new SharedWorker('sharedWorker_sharedWorker.js', '|||'); +ok(true, "3 SharedWorkers created!"); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_bug978260.html b/dom/workers/test/test_bug978260.html new file mode 100644 index 000000000..49e84c659 --- /dev/null +++ b/dom/workers/test/test_bug978260.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>Test for DOM Worker Threads</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"> +<script class="testbody" type="text/javascript"> + + SimpleTest.waitForExplicitFinish(); + + var xhr = new XMLHttpRequest(); + xhr.onload = function () { + var worker = new Worker("bug978260_worker.js"); + worker.onmessage = function(event) { + is(event.data, "loaded"); + SimpleTest.finish(); + } + } + + xhr.open('GET', '/', false); + xhr.send(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_bug998474.html b/dom/workers/test/test_bug998474.html new file mode 100644 index 000000000..892b42ef9 --- /dev/null +++ b/dom/workers/test/test_bug998474.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>Test for bug 998474</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body onload="boom();"> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + +function boom() +{ + var worker = new SharedWorker("bug998474_worker.js"); + + setTimeout(function() { + port = worker.port; + port.postMessage(""); + + setTimeout(function() { + port.start(); + ok(true, "Still alive!"); + SimpleTest.finish(); + }, 150); + }, 150); +} + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestFlakyTimeout("untriaged"); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_chromeWorker.html b/dom/workers/test/test_chromeWorker.html new file mode 100644 index 000000000..644593949 --- /dev/null +++ b/dom/workers/test/test_chromeWorker.html @@ -0,0 +1,27 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Test for DOM Worker Threads</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<pre id="test"> +<script class="testbody" type="text/javascript"> + + try { + var worker = new ChromeWorker("simpleThread_worker.js"); + ok(false, "ChromeWorker constructor should be blocked!"); + } + catch (e) { + ok(true, "ChromeWorker constructor wasn't blocked!"); + } + +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/test_chromeWorker.xul b/dom/workers/test/test_chromeWorker.xul new file mode 100644 index 000000000..35df768be --- /dev/null +++ b/dom/workers/test/test_chromeWorker.xul @@ -0,0 +1,45 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="DOM Worker Threads Test" + 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://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <script type="application/javascript"> + <![CDATA[ + + function test() + { + waitForWorkerFinish(); + + var worker = new ChromeWorker("chromeWorker_worker.js"); + worker.onmessage = function(event) { + is(event.data, "Done!", "Wrong message!"); + finish(); + } + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.message); + worker.terminate(); + finish(); + } + worker.postMessage("go"); + } + + ]]> + </script> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <p id="display"></p> + <div id="content" style="display:none;"></div> + <pre id="test"></pre> + </body> + <label id="test-result"/> +</window> diff --git a/dom/workers/test/test_chromeWorkerJSM.xul b/dom/workers/test/test_chromeWorkerJSM.xul new file mode 100644 index 000000000..01a88bc2a --- /dev/null +++ b/dom/workers/test/test_chromeWorkerJSM.xul @@ -0,0 +1,56 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="DOM Worker Threads Test" + 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://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <script type="application/javascript"> + <![CDATA[ + + function test() + { + waitForWorkerFinish(); + + var worker; + + function done() + { + worker = null; + finish(); + } + + function messageCallback(event) { + is(event.data, "Done", "Correct message"); + done(); + } + + function errorCallback(event) { + ok(false, "Worker had an error: " + event.message); + done(); + } + + Components.utils.import("chrome://mochitests/content/chrome/dom/workers/test/WorkerTest.jsm"); + + worker = WorkerTest.go(window.location.href, messageCallback, + errorCallback); + } + + ]]> + </script> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <p id="display"></p> + <div id="content" style="display:none;"></div> + <pre id="test"></pre> + </body> + <label id="test-result"/> +</window> diff --git a/dom/workers/test/test_clearTimeouts.html b/dom/workers/test/test_clearTimeouts.html new file mode 100644 index 000000000..d9bc6a9f9 --- /dev/null +++ b/dom/workers/test/test_clearTimeouts.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>Test for DOM Worker Threads</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"> +<script class="testbody" type="text/javascript"> + + new Worker("clearTimeouts_worker.js").onmessage = function(event) { + event.target.terminate(); + + is(event.data, "ready", "Correct message"); + setTimeout(function() { SimpleTest.finish(); }, 1000); + } + + SimpleTest.waitForExplicitFinish(); + SimpleTest.requestFlakyTimeout("untriaged"); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_console.html b/dom/workers/test/test_console.html new file mode 100644 index 000000000..af82a7de0 --- /dev/null +++ b/dom/workers/test/test_console.html @@ -0,0 +1,44 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<!-- +Tests of DOM Worker Console +--> +<head> + <title>Test for DOM Worker Console</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"> +<script class="testbody" language="javascript"> + var worker = new Worker("console_worker.js"); + + worker.onmessage = function(event) { + is(event.target, worker, "Worker and target match!"); + ok(event.data.status, event.data.event); + + if (!event.data.status || event.data.last) + SimpleTest.finish(); + }; + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.message); + SimpleTest.finish(); + } + + worker.postMessage(true); + + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_consoleAndBlobs.html b/dom/workers/test/test_consoleAndBlobs.html new file mode 100644 index 000000000..e765500fa --- /dev/null +++ b/dom/workers/test/test_consoleAndBlobs.html @@ -0,0 +1,43 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> + <head> + <title>Test for console API and blobs</title> + <script src="/tests/SimpleTest/SimpleTest.js"> + </script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> + </head> + <body> + <script type="text/javascript"> + + function consoleListener() { + SpecialPowers.addObserver(this, "console-api-log-event", false); + } + + var order = 0; + consoleListener.prototype = { + observe: function(aSubject, aTopic, aData) { + ok(true, "Something has been received"); + is(aTopic, "console-api-log-event"); + + var obj = aSubject.wrappedJSObject; + if (obj.arguments[0] && obj.arguments[0].msg === 'consoleAndBlobs') { + SpecialPowers.removeObserver(this, "console-api-log-event"); + is(obj.arguments[0].blob.size, 3, "The size is correct"); + is(obj.arguments[0].blob.type, 'foo/bar', "The type is correct"); + SimpleTest.finish(); + } + } + } + + var cl = new consoleListener(); + + new Worker('worker_consoleAndBlobs.js'); + SimpleTest.waitForExplicitFinish(); + + </script> + </body> +</html> diff --git a/dom/workers/test/test_consoleReplaceable.html b/dom/workers/test/test_consoleReplaceable.html new file mode 100644 index 000000000..3886b679d --- /dev/null +++ b/dom/workers/test/test_consoleReplaceable.html @@ -0,0 +1,44 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<!-- +Tests of DOM Worker Console +--> +<head> + <title>Test for DOM Worker Console</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"> +<script class="testbody" language="javascript"> + var worker = new Worker("consoleReplaceable_worker.js"); + + worker.onmessage = function(event) { + is(event.target, worker, "Worker and target match!"); + ok(event.data.status, event.data.event); + + if (event.data.last) + SimpleTest.finish(); + }; + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.message); + SimpleTest.finish(); + } + + worker.postMessage(true); + + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_consoleSharedWorkers.html b/dom/workers/test/test_consoleSharedWorkers.html new file mode 100644 index 000000000..74e1ec742 --- /dev/null +++ b/dom/workers/test/test_consoleSharedWorkers.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>Test for console API in SharedWorker</title> + <script src="/tests/SimpleTest/SimpleTest.js"> + </script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> + </head> + <body> + <script type="text/javascript"> + + function consoleListener() { + SpecialPowers.addObserver(this, "console-api-log-event", false); + SpecialPowers.addObserver(this, "console-api-profiler", false); + } + + var order = 0; + consoleListener.prototype = { + observe: function(aSubject, aTopic, aData) { + ok(true, "Something has been received"); + + if (aTopic == "console-api-profiler") { + var obj = aSubject.wrappedJSObject; + is (obj.arguments[0], "Hello profiling from a SharedWorker!", "A message from a SharedWorker \\o/"); + is (order++, 0, "First a profiler message."); + + SpecialPowers.removeObserver(this, "console-api-profiler"); + return; + } + + if (aTopic == "console-api-log-event") { + var obj = aSubject.wrappedJSObject; + is (obj.arguments[0], "Hello world from a SharedWorker!", "A message from a SharedWorker \\o/"); + is (obj.ID, "http://mochi.test:8888/tests/dom/workers/test/sharedWorker_console.js", "The ID is SharedWorker"); + is (obj.innerID, "SharedWorker", "The ID is SharedWorker"); + is (order++, 1, "Then a log message."); + + SpecialPowers.removeObserver(this, "console-api-log-event"); + SimpleTest.finish(); + return; + } + } + } + + var cl = new consoleListener(); + new SharedWorker('sharedWorker_console.js'); + + SimpleTest.waitForExplicitFinish(); + + </script> + </body> +</html> diff --git a/dom/workers/test/test_contentWorker.html b/dom/workers/test/test_contentWorker.html new file mode 100644 index 000000000..e745ca4a0 --- /dev/null +++ b/dom/workers/test/test_contentWorker.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>Test for DOM Worker privileged 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"> +<script class="testbody" language="javascript"> + + var workerFilename = "content_worker.js"; + var worker = new Worker(workerFilename); + + var props = { + 'ctypes': 1, + 'OS': 1 + }; + + worker.onmessage = function(event) { + if (event.data.testfinished) { + SimpleTest.finish(); + return; + } + var prop = event.data.prop; + ok(prop in props, "checking " + prop); + is(event.data.value, undefined, prop + " should be undefined"); + }; + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.message); + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_csp.html b/dom/workers/test/test_csp.html new file mode 100644 index 000000000..a24217f04 --- /dev/null +++ b/dom/workers/test/test_csp.html @@ -0,0 +1,18 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test for DOM Worker + CSP</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> +</body> +<script type="text/javascript" src="test_csp.js"></script> +</html> diff --git a/dom/workers/test/test_csp.html^headers^ b/dom/workers/test/test_csp.html^headers^ new file mode 100644 index 000000000..1c9321079 --- /dev/null +++ b/dom/workers/test/test_csp.html^headers^ @@ -0,0 +1,2 @@ +Cache-Control: no-cache +Content-Security-Policy: default-src 'self' blob: diff --git a/dom/workers/test/test_csp.js b/dom/workers/test/test_csp.js new file mode 100644 index 000000000..dcbcd8c3a --- /dev/null +++ b/dom/workers/test/test_csp.js @@ -0,0 +1,48 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +var tests = 3; + +SimpleTest.waitForExplicitFinish(); + +testDone = function(event) { + if (!--tests) SimpleTest.finish(); +} + +// Workers don't inherit CSP +worker = new Worker("csp_worker.js"); +worker.postMessage({ do: "eval" }); +worker.onmessage = function(event) { + is(event.data, 42, "Eval succeeded!"); + testDone(); +} + +// blob: workers *do* inherit CSP +xhr = new XMLHttpRequest; +xhr.open("GET", "csp_worker.js"); +xhr.responseType = "blob"; +xhr.send(); +xhr.onload = (e) => { + uri = URL.createObjectURL(e.target.response); + worker = new Worker(uri); + worker.postMessage({ do: "eval" }) + worker.onmessage = function(event) { + is(event.data, "Error: call to eval() blocked by CSP", "Eval threw"); + testDone(); + } +} + +xhr = new XMLHttpRequest; +xhr.open("GET", "csp_worker.js"); +xhr.responseType = "blob"; +xhr.send(); +xhr.onload = (e) => { + uri = URL.createObjectURL(e.target.response); + worker = new Worker(uri); + worker.postMessage({ do: "nest", uri: uri, level: 3 }) + worker.onmessage = function(event) { + is(event.data, "Error: call to eval() blocked by CSP", "Eval threw in nested worker"); + testDone(); + } +} diff --git a/dom/workers/test/test_dataURLWorker.html b/dom/workers/test/test_dataURLWorker.html new file mode 100644 index 000000000..1ff72424f --- /dev/null +++ b/dom/workers/test/test_dataURLWorker.html @@ -0,0 +1,31 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> + <head> + <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"> + const message = "hi"; + const url = "DATA:text/plain," + + "onmessage = function(event) {" + + " postMessage(event.data);" + + "};"; + + var worker = new Worker(url); + worker.onmessage = function(event) { + is(event.data, message, "Got correct message"); + SimpleTest.finish(); + }; + worker.postMessage(message); + + SimpleTest.waitForExplicitFinish(); + </script> + </body> +</html> + diff --git a/dom/workers/test/test_errorPropagation.html b/dom/workers/test/test_errorPropagation.html new file mode 100644 index 000000000..7e1aafe25 --- /dev/null +++ b/dom/workers/test/test_errorPropagation.html @@ -0,0 +1,66 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> + <head> + <meta charset="utf-8"> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"> + </script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + </head> + <body> + <iframe id="workerFrame" src="errorPropagation_iframe.html" + onload="workerFrameLoaded();"></iframe> + <script type="text/javascript"> + const workerCount = 3; + + const errorMessage = "Error: expectedError"; + const errorFilename = "http://mochi.test:8888/tests/dom/workers/test/" + + "errorPropagation_worker.js"; + const errorLineno = 48; + + var workerFrame; + + scopeErrorCount = 0; + workerErrorCount = 0; + windowErrorCount = 0; + + function messageListener(event) { + if (event.type == "scope") { + scopeErrorCount++; + } + else if (event.type == "worker") { + workerErrorCount++; + } + else if (event.type == "window") { + windowErrorCount++; + } + else { + ok(false, "Bad event type: " + event.type); + } + + is(event.data.message, errorMessage, "Correct message event.message"); + is(event.data.filename, errorFilename, + "Correct message event.filename"); + is(event.data.lineno, errorLineno, "Correct message event.lineno"); + + if (windowErrorCount == 1) { + is(scopeErrorCount, workerCount, "Good number of scope errors"); + is(workerErrorCount, workerCount, "Good number of worker errors"); + workerFrame.stop(); + SimpleTest.finish(); + } + } + + function workerFrameLoaded() { + workerFrame = document.getElementById("workerFrame").contentWindow; + workerFrame.start(workerCount, messageListener); + } + + SimpleTest.waitForExplicitFinish(); + </script> + </body> +</html> + diff --git a/dom/workers/test/test_errorwarning.html b/dom/workers/test/test_errorwarning.html new file mode 100644 index 000000000..04523c839 --- /dev/null +++ b/dom/workers/test/test_errorwarning.html @@ -0,0 +1,95 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<!-- +Test javascript.options.strict in Workers +--> +<head> + <title>Test javascript.options.strict in 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"> +<script class="testbody" language="javascript"> + + var errors = 0; + function errorHandler(e) { + ok(true, "An error has been received!"); + errors++; + } + + function test_noErrors() { + errors = 0; + + var worker = new Worker('errorwarning_worker.js'); + worker.onerror = errorHandler; + worker.onmessage = function(e) { + if (e.data.type == 'ignore') + return; + + if (e.data.type == 'error') { + errorHandler(); + return; + } + + if (e.data.type == 'finish') { + ok(errors == 0, "Here we are with 0 errors!"); + runTests(); + return; + } + } + + onerror = errorHandler; + worker.postMessage({ loop: 5, errors: false }); + } + + function test_errors() { + errors = 0; + + var worker = new Worker('errorwarning_worker.js'); + worker.onerror = errorHandler; + worker.onmessage = function(e) { + if (e.data.type == 'ignore') + return; + + if (e.data.type == 'error') { + errorHandler(); + return; + } + + if (e.data.type == 'finish') { + ok(errors != 0, "Here we are with errors!"); + runTests(); + return; + } + } + + onerror = errorHandler; + worker.postMessage({ loop: 5, errors: true }); + } + + var tests = [ test_noErrors, test_errors ]; + function runTests() { + var test = tests.shift(); + if (test) { + test(); + } else { + SimpleTest.finish(); + } + } + + runTests(); + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_eventDispatch.html b/dom/workers/test/test_eventDispatch.html new file mode 100644 index 000000000..b3c3123f0 --- /dev/null +++ b/dom/workers/test/test_eventDispatch.html @@ -0,0 +1,33 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> + <head> + <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"> + const message = "Hi"; + + var messageCount = 0; + + var worker = new Worker("eventDispatch_worker.js"); + worker.onmessage = function(event) { + is(event.data, message, "Got correct data."); + if (!messageCount++) { + event.target.postMessage(event.data); + return; + } + SimpleTest.finish(); + } + worker.postMessage(message); + + SimpleTest.waitForExplicitFinish(); + </script> + </body> +</html> + diff --git a/dom/workers/test/test_extension.xul b/dom/workers/test/test_extension.xul new file mode 100644 index 000000000..ba68ef03a --- /dev/null +++ b/dom/workers/test/test_extension.xul @@ -0,0 +1,55 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="DOM Worker Threads Test" + 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://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <script type="application/javascript"> + <![CDATA[ + + function test() { + const message = "woohoo"; + + var workertest = + Cc["@mozilla.org/test/workertest;1"].createInstance(Ci.nsIWorkerTest); + + workertest.callback = { + onmessage: function(data) { + is(data, message, "Correct message"); + workertest.callback = null; + workertest = null; + SimpleTest.finish(); + }, + onerror: function(data) { + ok(false, "Worker had an error: " + data.message); + workertest.callback = null; + workertest = null; + SimpleTest.finish(); + }, + QueryInterface: XPCOMUtils.generateQI([Ci.nsIWorkerTestCallback]) + }; + + workertest.postMessage(message); + + SimpleTest.waitForExplicitFinish(); + } + + ]]> + </script> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <p id="display"></p> + <div id="content" style="display:none;"></div> + <pre id="test"></pre> + </body> + <label id="test-result"/> +</window> diff --git a/dom/workers/test/test_extensionBootstrap.xul b/dom/workers/test/test_extensionBootstrap.xul new file mode 100644 index 000000000..18bdde1d3 --- /dev/null +++ b/dom/workers/test/test_extensionBootstrap.xul @@ -0,0 +1,66 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="DOM Worker Threads Test" + 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://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <script type="application/javascript"> + <![CDATA[ + + function test() { + const message = "woohoo"; + + var observer = { + observe: function(subject, topic, data) { + is(topic, "message", "Correct type of event"); + is(data, message, "Correct message"); + + AddonManager.getAddonByID("workerbootstrap-test@mozilla.org", + function(addon) { + addon.uninstall(); + + const stages = [ "install", "startup", "shutdown", "uninstall" ]; + const symbols = [ "Worker", "ChromeWorker" ]; + + for (var stage of stages) { + for (var symbol of symbols) { + is(Services.prefs.getBoolPref("workertest.bootstrap." + stage + + "." + symbol), + true, + "Symbol '" + symbol + "' present during '" + stage + "'"); + } + } + + SimpleTest.finish(); + }); + }, + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]) + }; + + var workertestbootstrap = Cc["@mozilla.org/test/workertestbootstrap;1"]. + createInstance(Ci.nsIObserver); + + workertestbootstrap.observe(observer, "postMessage", message); + + SimpleTest.waitForExplicitFinish(); + } + + ]]> + </script> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <p id="display"></p> + <div id="content" style="display:none;"></div> + <pre id="test"></pre> + </body> + <label id="test-result"/> +</window> diff --git a/dom/workers/test/test_fibonacci.html b/dom/workers/test/test_fibonacci.html new file mode 100644 index 000000000..d93eb12d4 --- /dev/null +++ b/dom/workers/test/test_fibonacci.html @@ -0,0 +1,52 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<!-- +Tests of DOM Worker Threads with Fibonacci +--> +<head> + <title>Test for DOM Worker Threads with Fibonacci</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.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=450452">DOM Worker Threads Fibonacci</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> + + const seqNum = 5; + + function recursivefib(n) { + return n < 2 ? n : recursivefib(n - 1) + recursivefib(n - 2); + } + + var worker = new Worker("fibonacci_worker.js"); + + worker.onmessage = function(event) { + is(event.target, worker); + is(event.data, recursivefib(seqNum)); + SimpleTest.finish(); + }; + + worker.onerror = function(event) { + is(event.target, worker); + ok(false, "Worker had an error: " + event.message); + SimpleTest.finish(); + }; + + worker.postMessage(seqNum); + + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/test_file.xul b/dom/workers/test/test_file.xul new file mode 100644 index 000000000..800e88fbc --- /dev/null +++ b/dom/workers/test/test_file.xul @@ -0,0 +1,97 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=123456 +--> +<window title="Mozilla Bug 123456" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=123456" + target="_blank">Mozilla Bug 123456</a> + + <div id="content" style="display: none"> + <input id="fileList" type="file"></input> + </div> + + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 123456 **/ + + var fileNum = 0; + + /** + * Create a file which contains the given data and optionally adds the specified file extension. + */ + function createFileWithData(fileData, /** optional */ extension) { + var testFile = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("ProfD", Components.interfaces.nsIFile); + var fileExtension = (extension == undefined) ? "" : "." + extension; + testFile.append("workerFile" + fileNum++ + fileExtension); + + var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"] + .createInstance(Components.interfaces.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0666, 0); + outStream.write(fileData, fileData.length); + outStream.close(); + + var fileList = document.getElementById('fileList'); + fileList.value = testFile.path; + + return fileList.files[0]; + } + + /** + * Create a worker to access file properties. + */ + function accessFileProperties(file, expectedSize, expectedType) { + waitForWorkerFinish(); + + var worker = new Worker("file_worker.js"); + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.message); + finish(); + }; + + worker.onmessage = function(event) { + is(event.data.size, expectedSize, "size proproperty accessed from worker is not the same as on main thread."); + is(event.data.type, expectedType, "type proproperty accessed from worker is incorrect."); + is(event.data.name, file.name, "name proproperty accessed from worker is incorrect."); + is(event.data.lastModifiedDate.toString(), file.lastModifiedDate.toString(), "lastModifiedDate proproperty accessed from worker is incorrect."); + finish(); + }; + + worker.postMessage(file); + } + + // Empty file. + accessFileProperties(createFileWithData(""), 0, ""); + + // Typical use case. + accessFileProperties(createFileWithData("Hello"), 5, ""); + + // Longish file. + var text = ""; + for (var i = 0; i < 10000; ++i) { + text += "long"; + } + accessFileProperties(createFileWithData(text), 40000, ""); + + // Type detection based on extension. + accessFileProperties(createFileWithData("text", "txt"), 4, "text/plain"); + + ]]> + </script> +</window> diff --git a/dom/workers/test/test_fileBlobPosting.xul b/dom/workers/test/test_fileBlobPosting.xul new file mode 100644 index 000000000..358054598 --- /dev/null +++ b/dom/workers/test/test_fileBlobPosting.xul @@ -0,0 +1,86 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=664783 +--> +<window title="Mozilla Bug 664783" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783" + target="_blank">Mozilla Bug 664783</a> + + <div id="content" style="display: none"> + <input id="fileList" type="file"></input> + </div> + + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 664783 **/ + + var fileNum = 0; + + /** + * Create a file which contains the given data. + */ + function createFileWithData(fileData) { + var testFile = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("ProfD", Components.interfaces.nsIFile); + testFile.append("workerBlobPosting" + fileNum++); + + var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"] + .createInstance(Components.interfaces.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0666, 0); + outStream.write(fileData, fileData.length); + outStream.close(); + + var fileList = document.getElementById('fileList'); + fileList.value = testFile.path; + + return fileList.files[0]; + } + + /** + * Create a worker which posts the same blob given. Used to test cloning of blobs. + * Checks the size, type, name and path of the file posted from the worker to ensure it + * is the same as the original. + */ + function postBlob(file) { + var worker = new Worker("filePosting_worker.js"); + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.message); + finish(); + }; + + worker.onmessage = function(event) { + console.log(event.data); + is(event.data.size, file.size, "size of file posted from worker does not match file posted to worker."); + finish(); + }; + + var blob = file.slice(); + worker.postMessage(blob); + waitForWorkerFinish(); + } + + // Empty file. + postBlob(createFileWithData("")); + + // Typical use case. + postBlob(createFileWithData("Hello")); + + ]]> + </script> +</window> diff --git a/dom/workers/test/test_fileBlobSubWorker.xul b/dom/workers/test/test_fileBlobSubWorker.xul new file mode 100644 index 000000000..6a8dba636 --- /dev/null +++ b/dom/workers/test/test_fileBlobSubWorker.xul @@ -0,0 +1,98 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=664783 +--> +<window title="Mozilla Bug 664783" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783" + target="_blank">Mozilla Bug 664783</a> + + <div id="content" style="display: none"> + <input id="fileList" type="file"></input> + </div> + + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 664783 **/ + + var fileNum = 0; + + /** + * Create a file which contains the given data and optionally adds the specified file extension. + */ + function createFileWithData(fileData, /** optional */ extension) { + var testFile = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("ProfD", Components.interfaces.nsIFile); + var fileExtension = (extension == undefined) ? "" : "." + extension; + testFile.append("workerBlobSubWorker" + fileNum++ + fileExtension); + + var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"] + .createInstance(Components.interfaces.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0666, 0); + outStream.write(fileData, fileData.length); + outStream.close(); + + var fileList = document.getElementById('fileList'); + fileList.value = testFile.path; + + return fileList.files[0]; + } + + /** + * Create a worker to access blob properties. + */ + function accessFileProperties(file, expectedSize) { + var worker = new Worker("fileBlobSubWorker_worker.js"); + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.message); + finish(); + }; + + worker.onmessage = function(event) { + if (event.data == undefined) { + ok(false, "Worker had an error."); + } else { + is(event.data.size, expectedSize, "size proproperty accessed from worker is not the same as on main thread."); + } + finish(); + }; + + var blob = file.slice(); + worker.postMessage(blob); + waitForWorkerFinish(); + } + + // Empty file. + accessFileProperties(createFileWithData(""), 0); + + // Typical use case. + accessFileProperties(createFileWithData("Hello"), 5); + + // Longish file. + var text = ""; + for (var i = 0; i < 10000; ++i) { + text += "long"; + } + accessFileProperties(createFileWithData(text), 40000); + + // Type detection based on extension. + accessFileProperties(createFileWithData("text", "txt"), 4); + + ]]> + </script> +</window> diff --git a/dom/workers/test/test_filePosting.xul b/dom/workers/test/test_filePosting.xul new file mode 100644 index 000000000..ff8520d7e --- /dev/null +++ b/dom/workers/test/test_filePosting.xul @@ -0,0 +1,86 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=664783 +--> +<window title="Mozilla Bug 664783" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783" + target="_blank">Mozilla Bug 664783</a> + + <div id="content" style="display: none"> + <input id="fileList" type="file"></input> + </div> + + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 664783 **/ + + var fileNum = 0; + + /** + * Create a file which contains the given data. + */ + function createFileWithData(fileData) { + var testFile = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("ProfD", Components.interfaces.nsIFile); + testFile.append("workerFilePosting" + fileNum++); + + var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"] + .createInstance(Components.interfaces.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0666, 0); + outStream.write(fileData, fileData.length); + outStream.close(); + + var fileList = document.getElementById('fileList'); + fileList.value = testFile.path; + + return fileList.files[0]; + } + + /** + * Create a worker which posts the same file given. Used to test cloning of files. + * Checks the size, type, name and path of the file posted from the worker to ensure it + * is the same as the original. + */ + function postFile(file) { + var worker = new Worker("file_worker.js"); + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.message); + finish(); + }; + + worker.onmessage = function(event) { + is(event.data.size, file.size, "size of file posted from worker does not match file posted to worker."); + is(event.data.type, file.type, "type of file posted from worker does not match file posted to worker."); + is(event.data.name, file.name, "name of file posted from worker does not match file posted to worker."); + finish(); + }; + + worker.postMessage(file); + waitForWorkerFinish(); + } + + // Empty file. + postFile(createFileWithData("")); + + // Typical use case. + postFile(createFileWithData("Hello")); + + ]]> + </script> +</window> diff --git a/dom/workers/test/test_fileReadSlice.xul b/dom/workers/test/test_fileReadSlice.xul new file mode 100644 index 000000000..da2e16719 --- /dev/null +++ b/dom/workers/test/test_fileReadSlice.xul @@ -0,0 +1,94 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=664783 +--> +<window title="Mozilla Bug 664783" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783" + target="_blank">Mozilla Bug 664783</a> + + <div id="content" style="display: none"> + <input id="fileList" type="file"></input> + </div> + + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + if (navigator.platform.startsWith("Win")) { + SimpleTest.expectAssertions(0, 1); + } + + /** Test for Bug 664783 **/ + + var fileNum = 0; + + /** + * Create a file which contains the given data. + */ + function createFileWithData(fileData) { + var testFile = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("ProfD", Components.interfaces.nsIFile); + testFile.append("workerReadSlice" + fileNum++); + + var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"] + .createInstance(Components.interfaces.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0666, 0); + outStream.write(fileData, fileData.length); + outStream.close(); + + var fileList = document.getElementById('fileList'); + fileList.value = testFile.path; + + return fileList.files[0]; + } + + /** + * Creates a worker which slices a blob to the given start and end offset and + * reads the content as text. + */ + function readSlice(blob, start, end, expectedText) { + var worker = new Worker("fileReadSlice_worker.js"); + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.message); + finish(); + }; + + worker.onmessage = function(event) { + is(event.data, expectedText, "Text from sliced blob in worker is incorrect."); + finish(); + }; + + var params = {blob: blob, start: start, end: end}; + worker.postMessage(params); + waitForWorkerFinish(); + } + + // Empty file. + readSlice(createFileWithData(""), 0, 0, ""); + + // Typical use case. + readSlice(createFileWithData("HelloBye"), 5, 8, "Bye"); + + // End offset too large. + readSlice(createFileWithData("HelloBye"), 5, 9, "Bye"); + + // Start of file. + readSlice(createFileWithData("HelloBye"), 0, 5, "Hello"); + + ]]> + </script> +</window> diff --git a/dom/workers/test/test_fileReader.html b/dom/workers/test/test_fileReader.html new file mode 100644 index 000000000..26e73bdb6 --- /dev/null +++ b/dom/workers/test/test_fileReader.html @@ -0,0 +1,100 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for FileReader in 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> +<script type="text/javascript;version=1.7"> + +const minFileSize = 20000; +SimpleTest.waitForExplicitFinish(); + +// Create strings containing data we'll test with. We'll want long +// strings to ensure they span multiple buffers while loading +var testTextData = "asd b\tlah\u1234w\u00a0r"; +while (testTextData.length < minFileSize) { + testTextData = testTextData + testTextData; +} + +var testASCIIData = "abcdef 123456\n"; +while (testASCIIData.length < minFileSize) { + testASCIIData = testASCIIData + testASCIIData; +} + +var testBinaryData = ""; +for (var i = 0; i < 256; i++) { + testBinaryData += String.fromCharCode(i); +} +while (testBinaryData.length < minFileSize) { + testBinaryData = testBinaryData + testBinaryData; +} + +var dataurldata0 = testBinaryData.substr(0, testBinaryData.length - + testBinaryData.length % 3); +var dataurldata1 = testBinaryData.substr(0, testBinaryData.length - 2 - + testBinaryData.length % 3); +var dataurldata2 = testBinaryData.substr(0, testBinaryData.length - 1 - + testBinaryData.length % 3); + + +//Set up files for testing +var openerURL = SimpleTest.getTestFileURL("fileapi_chromeScript.js"); +var opener = SpecialPowers.loadChromeScript(openerURL); +opener.addMessageListener("files.opened", onFilesOpened); +opener.sendAsyncMessage("files.open", [ + testASCIIData, + testBinaryData, + null, + convertToUTF8(testTextData), + convertToUTF16(testTextData), + "", + dataurldata0, + dataurldata1, + dataurldata2, +]); + +function onFilesOpened(message) { + var worker = new Worker('worker_fileReader.js'); + worker.postMessage({ blobs: message, + testTextData: testTextData, + testASCIIData: testASCIIData, + testBinaryData: testBinaryData, + dataurldata0: dataurldata0, + dataurldata1: dataurldata1, + dataurldata2: dataurldata2 }); + + worker.onmessage = function(e) { + var msg = e.data; + if (msg.type == 'finish') { + SimpleTest.finish(); + return; + } + + if (msg.type == 'check') { + ok(msg.status, msg.msg); + return; + } + + ok(false, "Unknown message."); + } +} + +function convertToUTF16(s) { + res = ""; + for (var i = 0; i < s.length; ++i) { + c = s.charCodeAt(i); + res += String.fromCharCode(c & 255, c >>> 8); + } + return res; +} + +function convertToUTF8(s) { + return unescape(encodeURIComponent(s)); +} + +</script> +</body> +</html> diff --git a/dom/workers/test/test_fileReaderSync.xul b/dom/workers/test/test_fileReaderSync.xul new file mode 100644 index 000000000..de93c3ed7 --- /dev/null +++ b/dom/workers/test/test_fileReaderSync.xul @@ -0,0 +1,199 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=664783 +--> +<window title="Mozilla Bug 664783" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783" + target="_blank">Mozilla Bug 664783</a> + + <div id="content" style="display: none"> + <input id="fileList" type="file"></input> + </div> + + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 664783 **/ + + var fileNum = 0; + + /** + * Create a file which contains the given data and optionally adds the specified file extension. + */ + function createFileWithData(fileData, /** optional */ extension) { + var testFile = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("ProfD", Components.interfaces.nsIFile); + var fileExtension = (extension == undefined) ? "" : "." + extension; + testFile.append("workerFileReaderSync" + fileNum++ + fileExtension); + + var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"] + .createInstance(Components.interfaces.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0666, 0); + outStream.write(fileData, fileData.length); + outStream.close(); + + var fileList = document.getElementById('fileList'); + fileList.value = testFile.path; + + return fileList.files[0]; + } + + function convertToUTF16(s) { + res = ""; + for (var i = 0; i < s.length; ++i) { + c = s.charCodeAt(i); + res += String.fromCharCode(c & 255, c >>> 8); + } + return res; + } + + /** + * Converts the given string to a data URL of the specified mime type. + */ + function convertToDataURL(mime, s) { + return "data:" + mime + ";base64," + btoa(s); + } + + /** + * Create a worker to read a file containing fileData using FileReaderSync and + * checks the return type against the expected type. Optionally set an encoding + * for reading the file as text. + */ + function readFileData(fileData, expectedText, /** optional */ encoding) { + var worker = new Worker("fileReaderSync_worker.js"); + + worker.onmessage = function(event) { + is(event.data.text, expectedText, "readAsText in worker returned incorrect result."); + is(event.data.bin, fileData, "readAsBinaryString in worker returned incorrect result."); + is(event.data.url, convertToDataURL("application/octet-stream", fileData), "readAsDataURL in worker returned incorrect result."); + is(event.data.arrayBuffer.byteLength, fileData.length, "readAsArrayBuffer returned buffer of incorrect length."); + finish(); + }; + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.message); + finish(); + }; + + var params = {file: createFileWithData(fileData), encoding: encoding}; + + worker.postMessage(params); + + waitForWorkerFinish(); + } + + /** + * Create a worker which reuses a FileReaderSync to read multiple files as DataURLs. + */ + function reuseReaderForURL(files, expected) { + var worker = new Worker("fileReaderSync_worker.js"); + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.message); + finish(); + }; + + var k = 0; + worker.onmessage = function(event) { + is(event.data.url, expected[k], "readAsDataURL in worker returned incorrect result when reusing FileReaderSync."); + k++; + finish(); + }; + + for (var i = 0; i < files.length; ++i) { + var params = {file: files[i], encoding: undefined}; + worker.postMessage(params); + waitForWorkerFinish(); + } + } + + /** + * Create a worker which reuses a FileReaderSync to read multiple files as text. + */ + function reuseReaderForText(fileData, expected) { + var worker = new Worker("fileReaderSync_worker.js"); + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.message); + finish(); + }; + + var k = 0; + worker.onmessage = function(event) { + is(event.data.text, expected[k++], "readAsText in worker returned incorrect result when reusing FileReaderSync."); + finish(); + }; + + for (var i = 0; i < fileData.length; ++i) { + var params = {file: createFileWithData(fileData[i]), encoding: undefined}; + worker.postMessage(params); + waitForWorkerFinish(); + } + } + + + /** + * Creates a a worker which reads a file containing fileData as an ArrayBuffer. + * Verifies that the ArrayBuffer when interpreted as a string matches the original data. + */ + function readArrayBuffer(fileData) { + var worker = new Worker("fileReaderSync_worker.js"); + + worker.onmessage = function(event) { + var view = new Uint8Array(event.data.arrayBuffer); + is(event.data.arrayBuffer.byteLength, fileData.length, "readAsArrayBuffer returned buffer of incorrect length."); + is(String.fromCharCode.apply(String, view), fileData, "readAsArrayBuffer returned buffer containing incorrect data."); + finish(); + }; + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.message); + finish(); + }; + + var params = {file: createFileWithData(fileData), encoding: undefined}; + + worker.postMessage(params); + + waitForWorkerFinish(); + } + + // Empty file. + readFileData("", ""); + + // Typical use case. + readFileData("text", "text"); + + // Test reading UTF-16 characters. + readFileData(convertToUTF16("text"), "text", "UTF-16"); + + // First read a file of type "text/plain", then read a file of type "application/octet-stream". + reuseReaderForURL([createFileWithData("text", "txt"), createFileWithData("text")], + [convertToDataURL("text/plain", "text"), + convertToDataURL("application/octet-stream", "text")]); + + // First read UTF-16 characters marked using BOM, then read UTF-8 characters. + reuseReaderForText([convertToUTF16("\ufefftext"), "text"], + ["text", "text"]); + + // Reading data as ArrayBuffer. + readArrayBuffer(""); + readArrayBuffer("text"); + + ]]> + </script> +</window> diff --git a/dom/workers/test/test_fileReaderSyncErrors.xul b/dom/workers/test/test_fileReaderSyncErrors.xul new file mode 100644 index 000000000..9e416a603 --- /dev/null +++ b/dom/workers/test/test_fileReaderSyncErrors.xul @@ -0,0 +1,84 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=664783 +--> +<window title="Mozilla Bug 664783" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783" + target="_blank">Mozilla Bug 664783</a> + + <div id="content" style="display: none"> + <input id="fileList" type="file"></input> + </div> + + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 664783 **/ + + var fileNum = 0; + + /** + * Create a file which contains the given data. + */ + function createFileWithData(fileData) { + var testFile = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("ProfD", Components.interfaces.nsIFile); + testFile.append("workerFileReaderSyncErrors" + fileNum++); + + var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"] + .createInstance(Components.interfaces.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0666, 0); + outStream.write(fileData, fileData.length); + outStream.close(); + + var fileList = document.getElementById('fileList'); + fileList.value = testFile.path; + + return fileList.files[0]; + } + + /** + * Creates a worker which runs errors cases. + */ + function runWorkerErrors(file) { + var worker = new Worker("fileReaderSyncErrors_worker.js"); + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.message); + finish(); + }; + + worker.onmessage = function(event) { + if(event.data == undefined) { + // Worker returns undefined when tests have finished running. + finish(); + } else { + // Otherwise worker will return results of tests to be evaluated. + is(event.data.actual, event.data.expected, event.data.message); + } + }; + + worker.postMessage(file); + waitForWorkerFinish(); + } + + // Run worker which creates exceptions. + runWorkerErrors(createFileWithData("text")); + + ]]> + </script> +</window> diff --git a/dom/workers/test/test_fileSlice.xul b/dom/workers/test/test_fileSlice.xul new file mode 100644 index 000000000..31531da2e --- /dev/null +++ b/dom/workers/test/test_fileSlice.xul @@ -0,0 +1,106 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=664783 +--> +<window title="Mozilla Bug 664783" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783" + target="_blank">Mozilla Bug 664783</a> + + <div id="content" style="display: none"> + <input id="fileList" type="file"></input> + </div> + + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 664783 **/ + + var fileNum = 0; + + /** + * Create a file which contains the given data and optionally adds the specified file extension. + */ + function createFileWithData(fileData, /** optional */ extension) { + var testFile = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("ProfD", Components.interfaces.nsIFile); + var fileExtension = (extension == undefined) ? "" : "." + extension; + testFile.append("workerSlice" + fileNum++ + fileExtension); + + var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"] + .createInstance(Components.interfaces.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0666, 0); + outStream.write(fileData, fileData.length); + outStream.close(); + + var fileList = document.getElementById('fileList'); + fileList.value = testFile.path; + + return fileList.files[0]; + } + + /** + * Starts a worker which slices the blob to the given start offset and optional end offset and + * content type. It then verifies that the size and type of the sliced blob is correct. + */ + function createSlice(blob, start, expectedLength, /** optional */ end, /** optional */ contentType) { + var worker = new Worker("fileSlice_worker.js"); + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.message); + finish(); + }; + + worker.onmessage = function(event) { + is(event.data.size, expectedLength, "size property of slice is incorrect."); + is(event.data.type, contentType ? contentType : blob.type, "type property of slice is incorrect."); + finish(); + }; + + var params = {blob: blob, start: start, end: end, contentType: contentType}; + worker.postMessage(params); + waitForWorkerFinish(); + } + + // Empty file. + createSlice(createFileWithData(""), 0, 0, 0); + + // Typical use case. + createSlice(createFileWithData("Hello"), 1, 1, 2); + + // Longish file. + var text = ""; + for (var i = 0; i < 10000; ++i) { + text += "long"; + } + createSlice(createFileWithData(text), 2000, 2000, 4000); + + // Slice to different type. + createSlice(createFileWithData("text", "txt"), 0, 2, 2, "image/png"); + + // Length longer than blob. + createSlice(createFileWithData("text"), 0, 4, 20); + + // Start longer than blob. + createSlice(createFileWithData("text"), 20, 0, 4); + + // No optional arguments + createSlice(createFileWithData("text"), 0, 4); + createSlice(createFileWithData("text"), 2, 2); + + ]]> + </script> +</window> diff --git a/dom/workers/test/test_fileSubWorker.xul b/dom/workers/test/test_fileSubWorker.xul new file mode 100644 index 000000000..94b41704a --- /dev/null +++ b/dom/workers/test/test_fileSubWorker.xul @@ -0,0 +1,99 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=664783 +--> +<window title="Mozilla Bug 664783" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783" + target="_blank">Mozilla Bug 664783</a> + + <div id="content" style="display: none"> + <input id="fileList" type="file"></input> + </div> + + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 664783 **/ + + var fileNum = 0; + + /** + * Create a file which contains the given data and optionally adds the specified file extension. + */ + function createFileWithData(fileData, /** optional */ extension) { + var testFile = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("ProfD", Components.interfaces.nsIFile); + var fileExtension = (extension == undefined) ? "" : "." + extension; + testFile.append("workerSubWorker" + fileNum++ + fileExtension); + + var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"] + .createInstance(Components.interfaces.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0666, 0); + outStream.write(fileData, fileData.length); + outStream.close(); + + var fileList = document.getElementById('fileList'); + fileList.value = testFile.path; + + return fileList.files[0]; + } + + /** + * Create a worker to access file properties. + */ + function accessFileProperties(file, expectedSize, expectedType) { + var worker = new Worker("fileSubWorker_worker.js"); + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.message); + finish(); + }; + + worker.onmessage = function(event) { + if (event.data == undefined) { + ok(false, "Worker had an error."); + } else { + is(event.data.size, expectedSize, "size proproperty accessed from worker is not the same as on main thread."); + is(event.data.type, expectedType, "type proproperty accessed from worker is incorrect."); + is(event.data.name, file.name, "name proproperty accessed from worker is incorrect."); + } + finish(); + }; + + worker.postMessage(file); + waitForWorkerFinish(); + } + + // Empty file. + accessFileProperties(createFileWithData(""), 0, ""); + + // Typical use case. + accessFileProperties(createFileWithData("Hello"), 5, ""); + + // Longish file. + var text = ""; + for (var i = 0; i < 10000; ++i) { + text += "long"; + } + accessFileProperties(createFileWithData(text), 40000, ""); + + // Type detection based on extension. + accessFileProperties(createFileWithData("text", "txt"), 4, "text/plain"); + + ]]> + </script> +</window> diff --git a/dom/workers/test/test_importScripts.html b/dom/workers/test/test_importScripts.html new file mode 100644 index 000000000..718409ce3 --- /dev/null +++ b/dom/workers/test/test_importScripts.html @@ -0,0 +1,53 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<!-- +Tests of DOM Worker Threads (Bug 437152) +--> +<head> + <title>Test for DOM Worker Threads (Bug 437152)</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.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=437152">DOM Worker Threads Bug 437152</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> + + var worker = new Worker("importScripts_worker.js"); + + worker.onmessage = function(event) { + switch (event.data) { + case "started": + worker.postMessage("stop"); + break; + case "stopped": + ok(true, "worker correctly stopped"); + SimpleTest.finish(); + break; + default: + ok(false, "Unexpected message:" + event.data); + SimpleTest.finish(); + } + }; + + worker.onerror = function(event) { + ok(false, "Worker had an error:" + event.message); + SimpleTest.finish(); + } + + worker.postMessage("start"); + + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_importScripts_3rdparty.html b/dom/workers/test/test_importScripts_3rdparty.html new file mode 100644 index 000000000..a3d73c5b5 --- /dev/null +++ b/dom/workers/test/test_importScripts_3rdparty.html @@ -0,0 +1,134 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test for 3rd party imported script and muted errors</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"> + +const workerURL = 'http://mochi.test:8888/tests/dom/workers/test/importScripts_3rdParty_worker.js'; + +var tests = [ + function() { + var worker = new Worker("importScripts_3rdParty_worker.js"); + worker.onmessage = function(event) { + ok("result" in event.data && event.data.result, "It seems we don't share data!"); + next(); + }; + + worker.postMessage({ url: location.href, test: 'try', nested: false }); + }, + + function() { + var worker = new Worker("importScripts_3rdParty_worker.js"); + worker.onmessage = function(event) { + ok("result" in event.data && event.data.result, "It seems we don't share data in nested workers!"); + next(); + }; + + worker.postMessage({ url: location.href, test: 'try', nested: true }); + }, + + function() { + var worker = new Worker("importScripts_3rdParty_worker.js"); + worker.onmessage = function(event) { + ok("result" in event.data && event.data.result, "It seems we don't share data via eventListener!"); + next(); + }; + + worker.postMessage({ url: location.href, test: 'eventListener', nested: false }); + }, + + function() { + var worker = new Worker("importScripts_3rdParty_worker.js"); + worker.onmessage = function(event) { + ok("result" in event.data && event.data.result, "It seems we don't share data in nested workers via eventListener!"); + next(); + }; + + worker.postMessage({ url: location.href, test: 'eventListener', nested: true }); + }, + + function() { + var worker = new Worker("importScripts_3rdParty_worker.js"); + worker.onmessage = function(event) { + ok("result" in event.data && event.data.result, "It seems we don't share data via onerror!"); + next(); + }; + worker.onerror = function(event) { + event.preventDefault(); + } + + worker.postMessage({ url: location.href, test: 'onerror', nested: false }); + }, + + function() { + var worker = new Worker("importScripts_3rdParty_worker.js"); + worker.onerror = function(event) { + event.preventDefault(); + ok(event instanceof ErrorEvent, "ErrorEvent received."); + is(event.filename, workerURL, "ErrorEvent.filename is correct"); + next(); + }; + + worker.postMessage({ url: location.href, test: 'none', nested: false }); + }, + + function() { + var worker = new Worker("importScripts_3rdParty_worker.js"); + worker.addEventListener("error", function(event) { + event.preventDefault(); + ok(event instanceof ErrorEvent, "ErrorEvent received."); + is(event.filename, workerURL, "ErrorEvent.filename is correct"); + next(); + }); + + worker.postMessage({ url: location.href, test: 'none', nested: false }); + }, + + function() { + var worker = new Worker("importScripts_3rdParty_worker.js"); + worker.onerror = function(event) { + ok(false, "No error should be received!"); + }; + + worker.onmessage = function(event) { + ok("error" in event.data && event.data.error, "The error has been fully received from a nested worker"); + next(); + }; + worker.postMessage({ url: location.href, test: 'none', nested: true }); + }, + + function() { + var url = URL.createObjectURL(new Blob(["%&%^&%^"])); + var worker = new Worker(url); + worker.onerror = function(event) { + event.preventDefault(); + ok(event instanceof ErrorEvent, "ErrorEvent received."); + next(); + }; + } +]; + +function next() { + if (!tests.length) { + SimpleTest.finish(); + return; + } + + var test = tests.shift(); + test(); +} + +SimpleTest.waitForExplicitFinish(); +next(); + +</script> +</body> +</html> diff --git a/dom/workers/test/test_importScripts_mixedcontent.html b/dom/workers/test/test_importScripts_mixedcontent.html new file mode 100644 index 000000000..0a7ce005c --- /dev/null +++ b/dom/workers/test/test_importScripts_mixedcontent.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 1198078 - test that we respect mixed content blocking in importScript() inside 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> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1198078">DOM Worker Threads Bug 1198078</a> +<iframe></iframe> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> + + onmessage = function(event) { + switch (event.data.status) { + case "done": + SimpleTest.finish(); + break; + case "ok": + ok(event.data.data, event.data.msg); + break; + default: + ok(false, "Unexpected message:" + event.data); + SimpleTest.finish(); + } + }; + + SimpleTest.waitForExplicitFinish(); + onload = function() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.workers.sharedWorkers.enabled", true], + ["security.mixed_content.block_active_content", false], + ]}, function() { + var iframe = document.querySelector("iframe"); + iframe.src = "https://example.com/tests/dom/workers/test/importScripts_mixedcontent.html"; + }); + }; + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_instanceof.html b/dom/workers/test/test_instanceof.html new file mode 100644 index 000000000..f73b3b6a7 --- /dev/null +++ b/dom/workers/test/test_instanceof.html @@ -0,0 +1,40 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<!-- +Tests of DOM Worker JSON messages +--> +<head> + <title>Test for DOM Worker Navigator</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"> +<script src="json_worker.js" language="javascript"></script> +<script class="testbody" language="javascript"> + + var worker = new Worker("instanceof_worker.js"); + + worker.onmessage = function(event) { + ok(event.data.status, event.data.event); + + if (event.data.last) + SimpleTest.finish(); + }; + + worker.postMessage(42); + + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_json.html b/dom/workers/test/test_json.html new file mode 100644 index 000000000..3a495de09 --- /dev/null +++ b/dom/workers/test/test_json.html @@ -0,0 +1,89 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<!-- +Tests of DOM Worker JSON messages +--> +<head> + <title>Test for DOM Worker Navigator</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"> +<script src="json_worker.js" language="javascript"></script> +<script class="testbody" language="javascript"> + + ok(messages.length, "No messages to test!"); + + var worker = new Worker("json_worker.js"); + + var index = 0; + worker.onmessage = function(event) { + var key = messages[index++]; + + // Loop for the ones we shouldn't receive. + while (key.exception) { + key = messages[index++]; + } + + is(typeof event.data, key.type, "Bad type! " + messages.indexOf(key)); + + if (key.array) { + is(event.data instanceof Array, key.array, + "Array mismatch! " + messages.indexOf(key)); + } + + if (key.isNaN) { + ok(isNaN(event.data), "Should be NaN!" + messages.indexOf(key)); + } + + if (key.isInfinity) { + is(event.data, Infinity, "Should be Infinity!" + messages.indexOf(key)); + } + + if (key.isNegativeInfinity) { + is(event.data, -Infinity, "Should be -Infinity!" + messages.indexOf(key)); + } + + if (key.shouldCompare || key.shouldEqual) { + ok(event.data == key.compareValue, + "Values don't compare! " + messages.indexOf(key)); + } + + if (key.shouldEqual) { + ok(event.data === key.compareValue, + "Values don't equal! " + messages.indexOf(key)); + } + + if (key.jsonValue) { + is(JSON.stringify(event.data), key.jsonValue, + "Object stringification inconsistent!" + messages.indexOf(key)); + } + + if (event.data == "testFinished") { + is(index, messages.length, "Didn't see the right number of messages!"); + SimpleTest.finish(); + } + }; + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.message); + SimpleTest.finish(); + } + + worker.postMessage("start"); + + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_jsversion.html b/dom/workers/test/test_jsversion.html new file mode 100644 index 000000000..495b8a3fa --- /dev/null +++ b/dom/workers/test/test_jsversion.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>Test for JSVersion in workers - Bug 487070</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"> +<script class="testbody" language="javascript"> + + var gExpectedError = false; + + onerror = function(evt) { + ok(gExpectedError, "Error expected!"); + runTest(); + } + + function doMagic() { + var worker = new Worker('jsversion_worker.js'); + worker.onmessage = function(evt) { + ok(evt.data, 'All the tests passed'); + runTest(); + } + worker.postMessage(1); + } + + var tests = [ + // No custom version + function() { + gExpectedError = true; + SpecialPowers.pushPrefEnv({"set":[['dom.workers.latestJSVersion', false]]}, + function() { doMagic(true); }); + }, + + // Enable latest JS Version + function() { + gExpectedError = false; + SpecialPowers.pushPrefEnv({"set":[['dom.workers.latestJSVersion', true]]}, + function() { doMagic(false); }); + } + ]; + + function runTest() { + if (!tests.length) { + SimpleTest.finish(); + return; + } + + var test = tests.shift(); + test(); + } + + SimpleTest.waitForExplicitFinish(); + runTest(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_loadEncoding.html b/dom/workers/test/test_loadEncoding.html new file mode 100644 index 000000000..47e08f2f5 --- /dev/null +++ b/dom/workers/test/test_loadEncoding.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 484305 - Load workers as UTF-8</title> + <meta http-equiv="content-type" content="text/html; charset=KOI8-R"> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.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=484305">Bug 484305 - Load workers as UTF-8</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); + +var canonical = String.fromCharCode(0x41F, 0x440, 0x438, 0x432, 0x435, 0x442); +ok(document.inputEncoding === "KOI8-R", "Document encoding is KOI8-R"); + +// Worker sends two strings, one with `canonical` encoded in KOI8-R and one as UTF-8. +// Since Worker scripts should always be decoded using UTF-8, even if the owning document's charset is different, the UTF-8 decode should match, while KOI8-R should fail. +var counter = 0; +var worker = new Worker("loadEncoding_worker.js"); +worker.onmessage = function(e) { + if (e.data.encoding === "KOI8-R") { + ok(e.data.text !== canonical, "KOI8-R decoded text should not match"); + } else if (e.data.encoding === "UTF-8") { + ok(e.data.text === canonical, "UTF-8 decoded text should match"); + } + counter++; + if (counter === 2) + SimpleTest.finish(); +} + +worker.onerror = function(e) { + ok(false, "Worker error"); + SimpleTest.finish(); +} +</script> + +</pre> +</body> +</html> diff --git a/dom/workers/test/test_loadError.html b/dom/workers/test/test_loadError.html new file mode 100644 index 000000000..dc109b796 --- /dev/null +++ b/dom/workers/test/test_loadError.html @@ -0,0 +1,77 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Test for DOM Worker Threads</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<pre id="test"> +<script class="testbody" type="text/javascript"> +"use strict"; + +var loadErrorMessage = 'SecurityError: Failed to load worker script at "about:blank"'; + +function nextTest() { + (function(){ + function workerfunc() { + var subworker = new Worker("about:blank"); + subworker.onerror = function(e) { + e.preventDefault(); + postMessage(e.message); + } + } + var b = new Blob([workerfunc+'workerfunc();']); + var u = URL.createObjectURL(b); + function callworker(i) { + try { + var w = new Worker(u); + URL.revokeObjectURL(u); + is(i, 0, 'worker creation succeeded'); + } catch (e) { + is(i, 1, 'worker creation failed'); + SimpleTest.finish(); + return; + } + w.onmessage = function(e) { + is(e.data, loadErrorMessage, + "Should catch the error when loading inner script"); + if (++i < 2) callworker(i); + else SimpleTest.finish(); + }; + w.onerrror = function(e) { + ok(false, "Should not get any errors from this worker"); + } + } + callworker(0); + })(); +} + +try { + var worker = new Worker("about:blank"); + worker.onerror = function(e) { + e.preventDefault(); + is(e.message, loadErrorMessage, + "Should get the right error from the toplevel script"); + nextTest(); + } + + worker.onmessage = function(event) { + ok(false, "Shouldn't get a message!"); + SimpleTest.finish(); + } +} catch (e) { + ok(false, "This should not happen."); +} + + +SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/test_location.html b/dom/workers/test/test_location.html new file mode 100644 index 000000000..cbd605307 --- /dev/null +++ b/dom/workers/test/test_location.html @@ -0,0 +1,72 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<!-- +Tests of DOM Worker Location +--> +<head> + <title>Test for DOM Worker Location</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"> +<script class="testbody" language="javascript"> + + var thisFilename = "test_location.html"; + var workerFilename = "location_worker.js"; + + var href = window.location.href + var queryPos = href.lastIndexOf(window.location.search); + var baseHref = href.substr(0, href.substr(0, queryPos).lastIndexOf("/") + 1); + + var path = window.location.pathname; + var basePath = path.substr(0, path.lastIndexOf("/") + 1); + + var strings = { + "toString": baseHref + workerFilename, + "href": baseHref + workerFilename, + "protocol": window.location.protocol, + "host": window.location.host, + "hostname": window.location.hostname, + "port": window.location.port, + "pathname": basePath + workerFilename, + "search": "", + "hash": "", + "origin": "http://mochi.test:8888" + }; + + var lastSlash = href.substr(0, queryPos).lastIndexOf("/") + 1; + is(thisFilename, + href.substr(lastSlash, queryPos - lastSlash), + "Correct filename "); + + var worker = new Worker(workerFilename); + + worker.onmessage = function(event) { + if (event.data.string == "testfinished") { + SimpleTest.finish(); + return; + } + ok(event.data.string in strings, event.data.string); + is(event.data.value, strings[event.data.string], event.data.string); + }; + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.message); + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_longThread.html b/dom/workers/test/test_longThread.html new file mode 100644 index 000000000..d23989f8b --- /dev/null +++ b/dom/workers/test/test_longThread.html @@ -0,0 +1,59 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<!-- +Tests of DOM Worker Threads (Bug 437152) +--> +<head> + <title>Test for DOM Worker Threads (Bug 437152)</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.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=437152">DOM Worker Threads Bug 437152</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> + + const numThreads = 5; + var doneThreads = 0; + + function onmessage(event) { + switch (event.data) { + case "done": + if (++doneThreads == numThreads) { + ok(true, "All messages received from workers"); + SimpleTest.finish(); + } + break; + default: + ok(false, "Unexpected message"); + SimpleTest.finish(); + } + } + + function onerror(event) { + ok(false, "Worker had an error"); + SimpleTest.finish(); + } + + for (var i = 0; i < numThreads; i++) { + var worker = new Worker("longThread_worker.js"); + worker.onmessage = onmessage; + worker.onerror = onerror; + worker.postMessage("start"); + } + + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/test_multi_sharedWorker.html b/dom/workers/test/test_multi_sharedWorker.html new file mode 100644 index 000000000..ba028302f --- /dev/null +++ b/dom/workers/test/test_multi_sharedWorker.html @@ -0,0 +1,242 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> + <head> + <title>Test for SharedWorker</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> + <script class="testbody" type="text/javascript;version=1.7"> + "use strict"; + + const basePath = + location.pathname.substring(0, + location.pathname.lastIndexOf("/") + 1); + const baseURL = location.origin + basePath; + + const frameRelativeURL = "multi_sharedWorker_frame.html"; + const frameAbsoluteURL = baseURL + frameRelativeURL; + const workerAbsoluteURL = + baseURL + "multi_sharedWorker_sharedWorker.js"; + + const storedData = "0123456789abcdefghijklmnopqrstuvwxyz"; + const errorMessage = "Error: Expected"; + const errorLineno = 34; + + let testGenerator = (function() { + SimpleTest.waitForExplicitFinish(); + + window.addEventListener("message", function(event) { + if (typeof(event.data) == "string") { + info(event.data); + } else { + sendToGenerator(event); + } + }); + + let frame1 = document.getElementById("frame1"); + frame1.src = frameRelativeURL; + frame1.onload = sendToGenerator; + + yield undefined; + + frame1 = frame1.contentWindow; + + let frame2 = document.getElementById("frame2"); + frame2.src = frameAbsoluteURL; + frame2.onload = sendToGenerator; + + yield undefined; + + frame2 = frame2.contentWindow; + + let data = { + command: "start" + }; + + frame1.postMessage(data, "*"); + frame2.postMessage(data, "*"); + + let event = yield undefined; + ok(event instanceof MessageEvent, "Got a MessageEvent"); + is(event.source, frame1, "First window got the event"); + is(event.data.type, "connect", "Got a connect message"); + + data = { + command: "retrieve" + }; + frame1.postMessage(data, "*"); + + event = yield undefined; + ok(event instanceof MessageEvent, "Got a MessageEvent"); + is(event.source, frame1, "First window got the event"); + is(event.data.type, "result", "Got a result message"); + is(event.data.data, undefined, "No data stored yet"); + + frame2.postMessage(data, "*"); + + event = yield undefined; + ok(event instanceof MessageEvent, "Got a MessageEvent"); + is(event.source, frame2, "Second window got the event"); + is(event.data.type, "result", "Got a result message"); + is(event.data.data, undefined, "No data stored yet"); + + data = { + command: "store", + data: storedData + }; + frame2.postMessage(data, "*"); + + data = { + command: "retrieve" + }; + frame1.postMessage(data, "*"); + + event = yield undefined; + ok(event instanceof MessageEvent, "Got a MessageEvent"); + is(event.source, frame1, "First window got the event"); + is(event.data.type, "result", "Got a result message"); + is(event.data.data, storedData, "Got stored data"); + + // This will generate two MessageEvents, one for each window. + let sawFrame1Error = false; + let sawFrame2Error = false; + + data = { + command: "error" + }; + frame1.postMessage(data, "*"); + + // First event. + event = yield undefined; + + ok(event instanceof MessageEvent, "Got a MessageEvent"); + is(event.data.type, "worker-error", "Got an error message"); + is(event.data.message, errorMessage, "Got correct error message"); + is(event.data.filename, workerAbsoluteURL, "Got correct filename"); + is(event.data.lineno, errorLineno, "Got correct lineno"); + if (event.source == frame1) { + is(sawFrame1Error, false, "Haven't seen error for frame1 yet"); + sawFrame1Error = true; + } else if (event.source == frame2) { + is(sawFrame2Error, false, "Haven't seen error for frame1 yet"); + sawFrame2Error = true; + } else { + ok(false, "Saw error from unknown window"); + } + + // Second event + event = yield undefined; + + ok(event instanceof MessageEvent, "Got a MessageEvent"); + is(event.data.type, "worker-error", "Got an error message"); + is(event.data.message, errorMessage, "Got correct error message"); + is(event.data.filename, workerAbsoluteURL, "Got correct filename"); + is(event.data.lineno, errorLineno, "Got correct lineno"); + if (event.source == frame1) { + is(sawFrame1Error, false, "Haven't seen error for frame1 yet"); + sawFrame1Error = true; + } else if (event.source == frame2) { + is(sawFrame2Error, false, "Haven't seen error for frame1 yet"); + sawFrame2Error = true; + } else { + ok(false, "Saw error from unknown window"); + } + + is(sawFrame1Error, true, "Saw error for frame1"); + is(sawFrame2Error, true, "Saw error for frame2"); + + // This will generate two MessageEvents, one for each window. + sawFrame1Error = false; + sawFrame2Error = false; + + data = { + command: "error" + }; + frame1.postMessage(data, "*"); + + // First event. + event = yield undefined; + + ok(event instanceof MessageEvent, "Got a MessageEvent"); + is(event.data.type, "error", "Got an error message"); + is(event.data.message, errorMessage, "Got correct error message"); + is(event.data.filename, workerAbsoluteURL, "Got correct filename"); + is(event.data.lineno, errorLineno, "Got correct lineno"); + is(event.data.isErrorEvent, true, "Frame got an ErrorEvent"); + if (event.source == frame1) { + is(sawFrame1Error, false, "Haven't seen error for frame1 yet"); + sawFrame1Error = true; + } else if (event.source == frame2) { + is(sawFrame2Error, false, "Haven't seen error for frame1 yet"); + sawFrame2Error = true; + } else { + ok(false, "Saw error from unknown window"); + } + + // Second event + event = yield undefined; + + ok(event instanceof MessageEvent, "Got a MessageEvent"); + is(event.data.type, "error", "Got an error message"); + is(event.data.message, errorMessage, "Got correct error message"); + is(event.data.filename, workerAbsoluteURL, "Got correct filename"); + is(event.data.lineno, errorLineno, "Got correct lineno"); + is(event.data.isErrorEvent, true, "Frame got an ErrorEvent"); + if (event.source == frame1) { + is(sawFrame1Error, false, "Haven't seen error for frame1 yet"); + sawFrame1Error = true; + } else if (event.source == frame2) { + is(sawFrame2Error, false, "Haven't seen error for frame1 yet"); + sawFrame2Error = true; + } else { + ok(false, "Saw error from unknown window"); + } + + is(sawFrame1Error, true, "Saw error for frame1"); + is(sawFrame2Error, true, "Saw error for frame2"); + + // Try a shared worker in a different origin. + frame1 = document.getElementById("frame1"); + frame1.src = "http://example.org" + basePath + frameRelativeURL; + frame1.onload = sendToGenerator; + yield undefined; + + frame1 = frame1.contentWindow; + + data = { + command: "retrieve" + }; + frame1.postMessage(data, "*"); + + event = yield undefined; + ok(event instanceof MessageEvent, "Got a MessageEvent"); + is(event.source, frame1, "First window got the event"); + is(event.data.type, "result", "Got a result message"); + is(event.data.data, undefined, "No data stored yet"); + + frame2.postMessage(data, "*"); + + event = yield undefined; + ok(event instanceof MessageEvent, "Got a MessageEvent"); + is(event.source, frame2, "First window got the event"); + is(event.data.type, "result", "Got a result message"); + is(event.data.data, storedData, "Got stored data"); + + window.removeEventListener("message", sendToGenerator); + + SimpleTest.finish(); + yield undefined; + })(); + + let sendToGenerator = testGenerator.send.bind(testGenerator); + + </script> + </head> + <body onload="testGenerator.next();"> + <iframe id="frame1"></iframe> + <iframe id="frame2"></iframe> + </body> +</html> diff --git a/dom/workers/test/test_multi_sharedWorker_lifetimes.html b/dom/workers/test/test_multi_sharedWorker_lifetimes.html new file mode 100644 index 000000000..a3f4dc9b5 --- /dev/null +++ b/dom/workers/test/test_multi_sharedWorker_lifetimes.html @@ -0,0 +1,156 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> + <head> + <title>Test for SharedWorker</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> + <script class="testbody" type="text/javascript;version=1.7"> + "use strict"; + + const scrollbarPref = "layout.testing.overlay-scrollbars.always-visible"; + const bfCacheEnabledPref = "browser.sessionhistory.cache_subframes"; + const bfCacheDepthPref = "browser.sessionhistory.max_total_viewers"; + const bfCacheDepth = 10; + + const frameRelativeURL = "multi_sharedWorker_frame.html"; + const storedData = "0123456789abcdefghijklmnopqrstuvwxyz"; + + let testGenerator = (function() { + SimpleTest.waitForExplicitFinish(); + + // Force scrollbar to always be shown. The scrollbar setting is + // necessary to avoid the fade-in/fade-out from evicting our document + // from the BF cache below. If bug 1049277 is fixed, then we can + // stop setting the scrollbar pref here. + SpecialPowers.pushPrefEnv({ set: [[scrollbarPref, true]] }, + sendToGenerator); + yield undefined; + + window.addEventListener("message", function(event) { + if (typeof(event.data) == "string") { + info(event.data); + } else { + sendToGenerator(event); + } + }); + + let frame = document.getElementById("frame"); + frame.src = frameRelativeURL; + frame.onload = sendToGenerator; + + yield undefined; + + frame = frame.contentWindow; + frame.postMessage({ command: "retrieve" }, "*"); + + let event = yield undefined; + ok(event instanceof MessageEvent, "Got a MessageEvent"); + is(event.source, frame, "Correct window got the event"); + is(event.data.type, "result", "Got a result message"); + is(event.data.data, undefined, "No data stored yet"); + + frame.postMessage({ command: "store", data: storedData }, "*"); + frame.postMessage({ command: "retrieve" }, "*"); + + event = yield undefined; + ok(event instanceof MessageEvent, "Got a MessageEvent"); + is(event.source, frame, "Correct window got the event"); + is(event.data.type, "result", "Got a result message"); + is(event.data.data, storedData, "Got stored data"); + + // Navigate when the bfcache is disabled. + info("Navigating to about:blank"); + frame = document.getElementById("frame"); + frame.onload = sendToGenerator; + frame.src = "about:blank"; + frame.contentWindow.document.body.offsetTop; + + yield undefined; + + info("Navigating to " + frameRelativeURL); + frame.src = frameRelativeURL; + frame.contentWindow.document.body.offsetTop; + + yield undefined; + + frame = frame.contentWindow; + frame.postMessage({ command: "retrieve" }, "*"); + + event = yield undefined; + ok(event instanceof MessageEvent, "Got a MessageEvent"); + is(event.source, frame, "Correct window got the event"); + is(event.data.type, "result", "Got a result message"); + is(event.data.data, undefined, "No data stored"); + + frame.postMessage({ command: "store", data: storedData }, "*"); + frame.postMessage({ command: "retrieve" }, "*"); + + event = yield undefined; + ok(event instanceof MessageEvent, "Got a MessageEvent"); + is(event.source, frame, "Correct window got the event"); + is(event.data.type, "result", "Got a result message"); + is(event.data.data, storedData, "Got stored data"); + + info("Enabling '" + bfCacheEnabledPref + "' pref"); + SpecialPowers.pushPrefEnv({ set: [[bfCacheEnabledPref, true], + [bfCacheDepthPref, bfCacheDepth]] }, + sendToGenerator); + yield undefined; + + // Navigate when the bfcache is enabled. + frame = document.getElementById("frame"); + frame.onload = sendToGenerator; + + info("Navigating to about:blank"); + frame.src = "about:blank"; + frame.contentWindow.document.body.offsetTop; + + yield undefined; + + for (let i = 0; i < 3; i++) { + info("Running GC"); + SpecialPowers.exactGC(sendToGenerator); + yield undefined; + + info("Waiting the event queue to clear"); + SpecialPowers.executeSoon(sendToGenerator); + yield undefined; + } + + info("Navigating to " + frameRelativeURL); + frame.src = frameRelativeURL; + frame.contentWindow.document.body.offsetTop; + + yield undefined; + + frame = frame.contentWindow; + frame.postMessage({ command: "retrieve" }, "*"); + + event = yield undefined; + ok(event instanceof MessageEvent, "Got a MessageEvent"); + is(event.source, frame, "Correct window got the event"); + is(event.data.type, "result", "Got a result message"); + is(event.data.data, storedData, "Still have data stored"); + + info("Resetting '" + bfCacheEnabledPref + "' pref"); + SpecialPowers.popPrefEnv(sendToGenerator); + yield undefined; + + window.removeEventListener("message", sendToGenerator); + + SimpleTest.finish(); + yield undefined; + })(); + + let sendToGenerator = testGenerator.send.bind(testGenerator); + + </script> + </head> + <body onload="testGenerator.next();"> + <iframe id="frame"></iframe> + </body> +</html> diff --git a/dom/workers/test/test_navigator.html b/dom/workers/test/test_navigator.html new file mode 100644 index 000000000..49d320967 --- /dev/null +++ b/dom/workers/test/test_navigator.html @@ -0,0 +1,68 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<!-- +Tests of DOM Worker Navigator +--> +<head> + <title>Test for DOM Worker Navigator</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"> +<script class="testbody" language="javascript"> + + var worker = new Worker("navigator_worker.js"); + + worker.onmessage = function(event) { + var args = JSON.parse(event.data); + + if (args.name == "testFinished") { + SimpleTest.finish(); + return; + } + + if (typeof navigator[args.name] == "undefined") { + ok(false, "Navigator has no '" + args.name + "' property!"); + return; + } + + if (args.name === "languages") { + is(navigator.languages.toString(), args.value.toString(), "languages matches"); + return; + } + + if (args.name === "storage") { + is(typeof navigator.storage, typeof args.value, "storage type matches"); + return; + } + + is(navigator[args.name], args.value, + "Mismatched navigator string for " + args.name + "!"); + }; + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.message); + SimpleTest.finish(); + } + + var version = SpecialPowers.Cc["@mozilla.org/xre/app-info;1"].getService(SpecialPowers.Ci.nsIXULAppInfo).version; + var isNightly = version.endsWith("a1"); + var isRelease = !version.includes("a"); + + worker.postMessage({ isNightly, isRelease }); + + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_navigator_languages.html b/dom/workers/test/test_navigator_languages.html new file mode 100644 index 000000000..2111739d9 --- /dev/null +++ b/dom/workers/test/test_navigator_languages.html @@ -0,0 +1,53 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<!-- +Tests of DOM Worker Navigator +--> +<head> + <title>Test for DOM Worker Navigator.languages</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"> +<script class="testbody" language="javascript"> + + var tests = [ 'en,it', 'it,en,fr', '', 'en' ]; + var expectedLanguages; + function runTests() { + if (!tests.length) { + worker.postMessage('finish'); + SimpleTest.finish(); + return; + } + + expectedLanguages = tests.shift(); + SpecialPowers.pushPrefEnv({"set": [["intl.accept_languages", expectedLanguages]]}, function() { + worker.postMessage(true); + }); + } + + SimpleTest.waitForExplicitFinish(); + + var worker = new Worker("navigator_languages_worker.js"); + + worker.onmessage = function(event) { + is(event.data.toString(), navigator.languages.toString(), "The languages mach."); + is(event.data.toString(), expectedLanguages, "This is the correct result."); + runTests(); + } + + runTests(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_navigator_workers_hardwareConcurrency.html b/dom/workers/test/test_navigator_workers_hardwareConcurrency.html new file mode 100644 index 000000000..6fbbc75a9 --- /dev/null +++ b/dom/workers/test/test_navigator_workers_hardwareConcurrency.html @@ -0,0 +1,30 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test for Navigator.hardwareConcurrency</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + + SimpleTest.waitForExplicitFinish(); + var script = "postMessage(navigator.hardwareConcurrency)"; + var url = URL.createObjectURL(new Blob([script])); + var w = new Worker(url); + w.onmessage = function(e) { + var x = e.data; + is(typeof x, "number", "hardwareConcurrency should be a number."); + ok(x > 0, "hardwareConcurrency should be greater than 0."); + SimpleTest.finish(); + } + </script> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_newError.html b/dom/workers/test/test_newError.html new file mode 100644 index 000000000..51aeae6b0 --- /dev/null +++ b/dom/workers/test/test_newError.html @@ -0,0 +1,34 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>Test for DOM Worker Threads</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<pre id="test"> +<script class="testbody" type="text/javascript"> + + var worker = new Worker("newError_worker.js"); + + worker.onmessage = function(event) { + ok(false, "Shouldn't get a message!"); + SimpleTest.finish(); + } + + worker.onerror = function(event) { + is(event.message, "Error: foo!", "Got wrong error message!"); + event.preventDefault(); + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/test_notification.html b/dom/workers/test/test_notification.html new file mode 100644 index 000000000..409d9dfd1 --- /dev/null +++ b/dom/workers/test/test_notification.html @@ -0,0 +1,50 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=916893 +--> +<head> + <title>Bug 916893</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=916893">Bug 916893</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 runTest() { + MockServices.register(); + var w = new Worker("notification_worker.js"); + w.onmessage = function(e) { + if (e.data.type === 'finish') { + MockServices.unregister(); + SimpleTest.finish(); + } else if (e.data.type === 'ok') { + ok(e.data.test, e.data.message); + } else if (e.data.type === 'is') { + is(e.data.test1, e.data.test2, e.data.message); + } + } + + SimpleTest.waitForExplicitFinish(); + // turn on testing pref (used by notification.cpp, and mock the alerts + SpecialPowers.setBoolPref("notification.prompt.testing", true); + w.postMessage('start') + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv( + {"set": [["dom.webnotifications.workers.enabled", true]]}, + runTest + ); +</script> +</body> +</html> diff --git a/dom/workers/test/test_notification_child.html b/dom/workers/test/test_notification_child.html new file mode 100644 index 000000000..74a1e8e09 --- /dev/null +++ b/dom/workers/test/test_notification_child.html @@ -0,0 +1,49 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=916893 +--> +<head> + <title>Bug 916893 - Test Notifications in child workers.</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=916893">Bug 916893</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 event."); + function runTest() { + MockServices.register(); + var w = new Worker("notification_worker_child-parent.js"); + w.onmessage = function(e) { + if (e.data.type === 'finish') { + MockServices.unregister(); + SimpleTest.finish(); + } else if (e.data.type === 'ok') { + ok(e.data.test, e.data.message); + } else if (e.data.type === 'is') { + is(e.data.test1, e.data.test2, e.data.message); + } + } + + SimpleTest.waitForExplicitFinish(); + // turn on testing pref (used by notification.cpp, and mock the alerts + SpecialPowers.setBoolPref("notification.prompt.testing", true); + w.postMessage('start') + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv( + {"set": [["dom.webnotifications.workers.enabled", true]]}, + runTest + ); +</script> +</body> +</html> diff --git a/dom/workers/test/test_notification_permission.html b/dom/workers/test/test_notification_permission.html new file mode 100644 index 000000000..e51e060d3 --- /dev/null +++ b/dom/workers/test/test_notification_permission.html @@ -0,0 +1,51 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=916893 +--> +<head> + <title>Bug 916893 - Make sure error is fired on Notification if permission is denied.</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=916893">Bug 916893</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 event."); + function runTest() { + MockServices.register(); + var w = new Worker("notification_permission_worker.js"); + w.onmessage = function(e) { + if (e.data.type === 'finish') { + SpecialPowers.setBoolPref("notification.prompt.testing.allow", true); + MockServices.unregister(); + SimpleTest.finish(); + } else if (e.data.type === 'ok') { + ok(e.data.test, e.data.message); + } else if (e.data.type === 'is') { + is(e.data.test1, e.data.test2, e.data.message); + } + } + + SimpleTest.waitForExplicitFinish(); + // turn on testing pref (used by notification.cpp, and mock the alerts + SpecialPowers.setBoolPref("notification.prompt.testing", true); + SpecialPowers.setBoolPref("notification.prompt.testing.allow", false); + w.postMessage('start') + } + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv( + {"set": [["dom.webnotifications.workers.enabled", true]]}, + runTest + ); +</script> +</body> +</html> diff --git a/dom/workers/test/test_onLine.html b/dom/workers/test/test_onLine.html new file mode 100644 index 000000000..660835f82 --- /dev/null +++ b/dom/workers/test/test_onLine.html @@ -0,0 +1,64 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 925437: online/offline events tests. + +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/licenses/publicdomain/ +--> +<head> + <title>Test for Bug 925437 (worker online/offline events)</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=925437">Mozilla Bug 925437</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> + +<script class="testbody" type="text/javascript"> + +addLoadEvent(function() { + var w = new Worker("onLine_worker.js"); + + w.onmessage = function(e) { + if (e.data.type === 'ready') { + doTest(); + } else if (e.data.type === 'ok') { + ok(e.data.test, e.data.message); + } else if (e.data.type === 'finished') { + SimpleTest.finish(); + } + } + + function doTest() { + var iosvc = SpecialPowers.Cc["@mozilla.org/network/io-service;1"] + .getService(SpecialPowers.Ci.nsIIOService2); + iosvc.manageOfflineStatus = false; + + info("setting iosvc.offline = true"); + iosvc.offline = true; + + info("setting iosvc.offline = false"); + iosvc.offline = false; + + info("setting iosvc.offline = true"); + iosvc.offline = true; + + for (var i = 0; i < 10; ++i) { + iosvc.offline = !iosvc.offline; + } + + info("setting iosvc.offline = false"); + w.postMessage('lastTest'); + iosvc.offline = false; + } +}); + +SimpleTest.waitForExplicitFinish(); +</script> +</body> +</html> diff --git a/dom/workers/test/test_promise.html b/dom/workers/test/test_promise.html new file mode 100644 index 000000000..63f359f9d --- /dev/null +++ b/dom/workers/test/test_promise.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>Test for Promise object in 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 runTest() { + var worker = new Worker("promise_worker.js"); + + worker.onmessage = function(event) { + + if (event.data.type == 'finish') { + SimpleTest.finish(); + } else if (event.data.type == 'status') { + ok(event.data.status, event.data.msg); + } + } + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.message); + SimpleTest.finish(); + }; + + worker.postMessage(true); + } + + SimpleTest.waitForExplicitFinish(); + runTest(); +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/test_promise_resolved_with_string.html b/dom/workers/test/test_promise_resolved_with_string.html new file mode 100644 index 000000000..28caaaaa8 --- /dev/null +++ b/dom/workers/test/test_promise_resolved_with_string.html @@ -0,0 +1,41 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1027221 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1027221</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + + /** Test for Bug 1027221 **/ + // Set up a permanent atom + SimpleTest.waitForExplicitFinish(); + var x = "x"; + // Trigger some incremental gc + SpecialPowers.Cu.getJSTestingFunctions().gcslice(1); + + // Kick off a worker that uses this same atom + var w = new Worker("data:text/plain,Promise.resolve('x').then(function() { postMessage(1); });"); + // Maybe trigger some more incremental gc + SpecialPowers.Cu.getJSTestingFunctions().gcslice(1); + + w.onmessage = function() { + ok(true, "Got here"); + SimpleTest.finish(); + }; + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1027221">Mozilla Bug 1027221</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_recursion.html b/dom/workers/test/test_recursion.html new file mode 100644 index 000000000..888a607c4 --- /dev/null +++ b/dom/workers/test/test_recursion.html @@ -0,0 +1,69 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<!-- +Tests of DOM Worker Threads +--> +<head> + <title>Test for DOM Worker Threads Recursion</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"> +<script class="testbody" type="text/javascript"> + + // Intermittently triggers one assertion on Mac (bug 848098). + if (navigator.platform.indexOf("Mac") == 0) { + SimpleTest.expectAssertions(0, 1); + } + + const testCount = 2; + var errorCount = 0; + + var worker = new Worker("recursion_worker.js"); + + function done() { + worker.terminate(); + SimpleTest.finish(); + } + + worker.onmessage = function(event) { + if (event.data == "Done") { + ok(true, "correct message"); + } + else { + ok(false, "Bad message: " + event.data); + } + done(); + } + + worker.onerror = function(event) { + event.preventDefault(); + if (event.message == "too much recursion") { + ok(true, "got correct error message"); + ++errorCount; + } + else { + ok(false, "got bad error message: " + event.message); + done(); + } + } + + for (var i = 0; i < testCount; i++) { + worker.postMessage(""); + } + + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_recursiveOnerror.html b/dom/workers/test/test_recursiveOnerror.html new file mode 100644 index 000000000..06471d978 --- /dev/null +++ b/dom/workers/test/test_recursiveOnerror.html @@ -0,0 +1,44 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> + <head> + <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"> + const filename = "http://mochi.test:8888/tests/dom/workers/test/" + + "recursiveOnerror_worker.js"; + const errors = [ + { message: "Error: 2", lineno: 6 }, + { message: "Error: 1", lineno: 10 } + ] + + var errorCount = 0; + + var worker = new Worker("recursiveOnerror_worker.js"); + worker.postMessage("go"); + + worker.onerror = function(event) { + event.preventDefault(); + + ok(errorCount < errors.length, "Correct number of error events"); + const error = errors[errorCount++]; + + is(event.message, error.message, "Correct message"); + is(event.filename, filename, "Correct filename"); + is(event.lineno, error.lineno, "Correct lineno"); + + if (errorCount == errors.length) { + SimpleTest.finish(); + } + } + + SimpleTest.waitForExplicitFinish(); + </script> + </body> +</html> diff --git a/dom/workers/test/test_referrer.html b/dom/workers/test/test_referrer.html new file mode 100644 index 000000000..c3afec78e --- /dev/null +++ b/dom/workers/test/test_referrer.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 referrer of 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 test_mainScript() { + var worker = new Worker("referrer.sjs?worker"); + worker.onmessage = function() { + var xhr = new XMLHttpRequest(); + xhr.open('GET', 'referrer.sjs?result', true); + xhr.onload = function() { + is(xhr.responseText, location.href, "The referrer has been sent."); + next(); + } + xhr.send(); + } + worker.postMessage(42); + } + + function test_importScript() { + var worker = new Worker("worker_referrer.js"); + worker.onmessage = function(e) { + is(e.data, location.href.replace("test_referrer.html", "worker_referrer.js"), "The referrer has been sent."); + next(); + } + worker.postMessage(42); + } + + var tests = [ test_mainScript, test_importScript ]; + function next() { + if (!tests.length) { + SimpleTest.finish(); + return; + } + + var test = tests.shift(); + test(); + } + + SimpleTest.waitForExplicitFinish(); + next(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_referrer_header_worker.html b/dom/workers/test/test_referrer_header_worker.html new file mode 100644 index 000000000..d04c8b591 --- /dev/null +++ b/dom/workers/test/test_referrer_header_worker.html @@ -0,0 +1,39 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test the referrer of 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> + <script class="testbody" type="text/javascript"> + SimpleTest.waitForExplicitFinish(); + + SpecialPowers.pushPrefEnv( + {"set": [ + ['security.mixed_content.block_display_content', false], + ['security.mixed_content.block_active_content', false] + ]}, + function() { + SpecialPowers.pushPermissions([{'type': 'systemXHR', 'allow': true, 'context': document}], test); + }); + + function test() { + function messageListener(event) { + eval(event.data); + } + window.addEventListener("message", messageListener, false); + + var ifr = document.createElement('iframe'); + ifr.setAttribute('src', 'https://example.com/tests/dom/workers/test/referrer_worker.html'); + document.body.appendChild(ifr); + } + </script> +</body> +</html> + diff --git a/dom/workers/test/test_resolveWorker-assignment.html b/dom/workers/test/test_resolveWorker-assignment.html new file mode 100644 index 000000000..b6733e010 --- /dev/null +++ b/dom/workers/test/test_resolveWorker-assignment.html @@ -0,0 +1,30 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> + <head> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + </head> + <body> + <script type="application/javascript"> + window.Worker = 17; // resolve through assignment + + var desc = Object.getOwnPropertyDescriptor(window, "Worker"); + ok(typeof desc === "object" && desc !== null, "Worker property must exist"); + + is(desc.value, 17, "Overwrite didn't work correctly"); + is(desc.enumerable, false, + "Initial descriptor was non-enumerable, and [[Put]] changes the " + + "property value but not its enumerability"); + is(desc.configurable, true, + "Initial descriptor was configurable, and [[Put]] changes the " + + "property value but not its configurability"); + is(desc.writable, true, + "Initial descriptor was writable, and [[Put]] changes the " + + "property value but not its writability"); + </script> + </body> +</html> diff --git a/dom/workers/test/test_resolveWorker.html b/dom/workers/test/test_resolveWorker.html new file mode 100644 index 000000000..8e2ea5445 --- /dev/null +++ b/dom/workers/test/test_resolveWorker.html @@ -0,0 +1,31 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> + <head> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + </head> + <body> + <script type="application/javascript"> + window.Worker; // resolve not through assignment + Worker = 17; + + var desc = Object.getOwnPropertyDescriptor(window, "Worker"); + ok(typeof desc === "object" && desc !== null, "Worker property must exist"); + + is(desc.value, 17, "Overwrite didn't work correctly"); + is(desc.enumerable, false, + "Initial descriptor was non-enumerable, and [[Put]] changes the " + + "property value but not its enumerability"); + is(desc.configurable, true, + "Initial descriptor was configurable, and [[Put]] changes the " + + "property value but not its configurability"); + is(desc.writable, true, + "Initial descriptor was writable, and [[Put]] changes the " + + "property value but not its writability"); + </script> + </body> +</html> diff --git a/dom/workers/test/test_rvals.html b/dom/workers/test/test_rvals.html new file mode 100644 index 000000000..eba858928 --- /dev/null +++ b/dom/workers/test/test_rvals.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>Test for bug 911085</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 worker = new Worker("rvals_worker.js"); + + worker.onmessage = function(event) { + if (event.data == 'ignore') return; + + if (event.data == 'finished') { + is(worker.terminate(), undefined, "Terminate() returns 'undefined'"); + SimpleTest.finish(); + return; + } + + ok(event.data, "something good returns 'undefined' in workers"); + }; + + is(worker.postMessage(42), undefined, "PostMessage() returns 'undefined' on main thread"); + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> + + diff --git a/dom/workers/test/test_setTimeoutWith0.html b/dom/workers/test/test_setTimeoutWith0.html new file mode 100644 index 000000000..4c0dacafb --- /dev/null +++ b/dom/workers/test/test_setTimeoutWith0.html @@ -0,0 +1,20 @@ +<html> +<head> + <title>Test for DOM Worker setTimeout and strings containing 0</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> + +var a = new Worker('worker_setTimeoutWith0.js'); +a.onmessage = function(e) { + is(e.data, 2, "We want to see 2 here"); + SimpleTest.finish(); +} + +SimpleTest.waitForExplicitFinish(); +</script> +</body> +</html> + diff --git a/dom/workers/test/test_sharedWorker.html b/dom/workers/test/test_sharedWorker.html new file mode 100644 index 000000000..3d3d4e2c6 --- /dev/null +++ b/dom/workers/test/test_sharedWorker.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 for SharedWorker</title> + <script 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"> + <script class="testbody"> + "use strict"; + + const href = window.location.href; + const filename = "sharedWorker_sharedWorker.js"; + const sentMessage = "ping"; + const errorFilename = href.substring(0, href.lastIndexOf("/") + 1) + + filename; + const errorLine = 91; + const errorColumn = 0; + + var worker = new SharedWorker(filename); + + ok(worker instanceof SharedWorker, "Got SharedWorker instance"); + ok(!("postMessage" in worker), "SharedWorker has no 'postMessage'"); + ok(worker.port instanceof MessagePort, + "Shared worker has MessagePort"); + + var receivedMessage; + var receivedError; + + worker.port.onmessage = function(event) { + ok(event instanceof MessageEvent, "Got a MessageEvent"); + ok(event.target === worker.port, + "MessageEvent has correct 'target' property"); + is(event.data, sentMessage, "Got correct message"); + ok(receivedMessage === undefined, "Haven't gotten message yet"); + receivedMessage = event.data; + if (receivedError) { + SimpleTest.finish(); + } + }; + + worker.onerror = function(event) { + ok(event instanceof ErrorEvent, "Got an ErrorEvent"); + is(event.message, "Error: " + sentMessage, "Got correct error"); + is(event.filename, errorFilename, "Got correct filename"); + is(event.lineno, errorLine, "Got correct lineno"); + is(event.colno, errorColumn, "Got correct column"); + ok(receivedError === undefined, "Haven't gotten error yet"); + receivedError = event.message; + event.preventDefault(); + if (receivedMessage) { + SimpleTest.finish(); + } + }; + + worker.port.postMessage(sentMessage); + + SimpleTest.waitForExplicitFinish(); + + </script> + </pre> + </body> +</html> diff --git a/dom/workers/test/test_sharedWorker_lifetime.html b/dom/workers/test/test_sharedWorker_lifetime.html new file mode 100644 index 000000000..169746892 --- /dev/null +++ b/dom/workers/test/test_sharedWorker_lifetime.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>Test for MessagePort and SharedWorkers</title> + <meta http-equiv="content-type" content="text/html; charset=UTF-8"> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + </head> + <body> + <script class="testbody" type="text/javascript"> + +var gced = false; + +var sw = new SharedWorker('sharedWorker_lifetime.js'); +sw.port.onmessage = function(event) { + ok(gced, "The SW is still alive also after GC"); + SimpleTest.finish(); +} + +sw = null; +SpecialPowers.forceGC(); +gced = true; + +SimpleTest.waitForExplicitFinish(); + </script> + </body> +</html> diff --git a/dom/workers/test/test_sharedWorker_ports.html b/dom/workers/test/test_sharedWorker_ports.html new file mode 100644 index 000000000..32698ab52 --- /dev/null +++ b/dom/workers/test/test_sharedWorker_ports.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>Test for MessagePort and SharedWorkers</title> + <meta http-equiv="content-type" content="text/html; charset=UTF-8"> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + </head> + <body> + <script class="testbody" type="text/javascript"> + +var sw1 = new SharedWorker('sharedWorker_ports.js'); +sw1.port.onmessage = function(event) { + if (event.data.type == "connected") { + ok(true, "The SharedWorker is alive."); + + var sw2 = new SharedWorker('sharedWorker_ports.js'); + sw1.port.postMessage("Port from the main-thread!", [sw2.port]); + return; + } + + if (event.data.type == "status") { + ok(event.data.test, event.data.msg); + return; + } + + if (event.data.type == "finish") { + info("Finished!"); + ok(sw1.port, "The port still exists"); + sw1.port.foo = sw1; // Just a test to see if we leak. + SimpleTest.finish(); + } +} + +SimpleTest.waitForExplicitFinish(); + </script> + </body> +</html> + diff --git a/dom/workers/test/test_sharedWorker_privateBrowsing.html b/dom/workers/test/test_sharedWorker_privateBrowsing.html new file mode 100644 index 000000000..b0eac5831 --- /dev/null +++ b/dom/workers/test/test_sharedWorker_privateBrowsing.html @@ -0,0 +1,101 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <title>Test for SharedWorker - 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"; + +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 wP; + +function doTests() { + testOnWindow(false, function(aWin) { + wN = aWin; + + testOnWindow(true, function(aWin) { + wP = aWin; + + var sharedWorker1 = new wP.content.SharedWorker('sharedWorker_privateBrowsing.js'); + sharedWorker1.port.onmessage = function(event) { + is(event.data, 1, "Only 1 sharedworker expected in the private window"); + + var sharedWorker2 = new wN.content.SharedWorker('sharedWorker_privateBrowsing.js'); + sharedWorker2.port.onmessage = function(event) { + is(event.data, 1, "Only 1 sharedworker expected in the normal window"); + + var sharedWorker3 = new wP.content.SharedWorker('sharedWorker_privateBrowsing.js'); + sharedWorker3.port.onmessage = function(event) { + is(event.data, 2, "Only 2 sharedworker expected in the private window"); + runTest(); + } + } + } + }); + }); +} + +var steps = [ + setupWindow, + doTests +]; + +function runTest() { + if (!steps.length) { + wN.close(); + wP.close(); + + SimpleTest.finish(); + return; + } + + var step = steps.shift(); + step(); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPrefEnv({"set": [ + ["browser.startup.page", 0], + ["browser.startup.homepage_override.mstone", "ignore"], +]}, runTest); + +</script> +</body> +</html> diff --git a/dom/workers/test/test_simpleThread.html b/dom/workers/test/test_simpleThread.html new file mode 100644 index 000000000..0dad90312 --- /dev/null +++ b/dom/workers/test/test_simpleThread.html @@ -0,0 +1,75 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<!-- +Tests of DOM Worker Threads (Bug 437152) +--> +<head> + <title>Test for DOM Worker Threads (Bug 437152)</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.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=437152">DOM Worker Threads Bug 437152</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> + + var worker = new Worker("simpleThread_worker.js"); + + worker.addEventListener("message",function(event) { + is(event.target, worker); + switch (event.data) { + case "no-op": + break; + case "started": + is(gotErrors, true); + worker.postMessage("no-op"); + worker.postMessage("stop"); + break; + case "stopped": + worker.postMessage("no-op"); + SimpleTest.finish(); + break; + default: + ok(false, "Unexpected message:" + event.data); + SimpleTest.finish(); + } + }, false); + + var gotErrors = false; + worker.onerror = function(event) { + event.preventDefault(); + is(event.target, worker); + is(event.message, "uncaught exception: Bad message: asdf"); + + worker.onerror = function(otherEvent) { + otherEvent.preventDefault(); + is(otherEvent.target, worker); + is(otherEvent.message, "ReferenceError: Components is not defined"); + gotErrors = true; + + worker.onerror = function(oneMoreEvent) { + ok(false, "Worker had an error:" + oneMoreEvent.message); + SimpleTest.finish(); + }; + }; + }; + + worker.postMessage("asdf"); + worker.postMessage("components"); + worker.postMessage("start"); + + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/test_subworkers_suspended.html b/dom/workers/test/test_subworkers_suspended.html new file mode 100644 index 000000000..063ccaa64 --- /dev/null +++ b/dom/workers/test/test_subworkers_suspended.html @@ -0,0 +1,135 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test for sub workers+bfcache behavior</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + <script type="application/javascript"> + + const WORKER_URL = "worker_suspended.js"; + const SUB_WORKERS = 3 + + var testUrl1 = "window_suspended.html?page1Shown"; + var testUrl2 = "window_suspended.html?page2Shown"; + + var testWin; + var counter = 0; + + function cacheData() { + return caches.open("test") + .then(function(cache) { + return cache.match("http://mochi.test:888/foo"); + }) + .then(function(response) { + return response.text(); + }); + } + + function page1Shown(e) { + info("Page1Shown: " + testWin.location.href); + + // First time this page is shown. + if (counter == 0) { + ok(!e.persisted, "test page should have been persisted initially"); + + info("Create a worker and subworkers..."); + let worker = new e.target.defaultView.Worker(WORKER_URL); + + var promise = new Promise((resolve, reject) => { + info("Waiting until workers are ready..."); + worker.addEventListener("message", function onmessage(e) { + is(e.data, "ready", "We want to receive: -ready-"); + worker.removeEventListener("message", onmessage); + resolve(); + }); + worker.postMessage({ type: "page1", count: SUB_WORKERS }); + }); + + promise.then(function() { + info("Retrieving data from cache..."); + return cacheData(); + }) + + .then(function(content) { + is(content.indexOf("page1-"), 0, "We have data from the worker"); + }) + + .then(function() { + info("New location: " + testUrl2); + testWin.location.href = testUrl2; + }); + } else { + is(e.persisted, true, "test page should have been persisted in pageshow"); + + var promise = new Promise((resolve, reject) => { + info("Waiting a few seconds..."); + setTimeout(resolve, 5000); + }); + + promise.then(function() { + info("Retrieving data from cache..."); + return cacheData(); + }) + + .then(function(content) { + is(content.indexOf("page1-"), 0, "We have data from the worker"); + }) + + .then(function() { + testWin.close(); + SimpleTest.finish(); + }); + } + + counter++; + } + + function page2Shown(e) { + info("Page2Shown: " + testWin.location.href); + + info("Create a worker..."); + let worker = new e.target.defaultView.Worker(WORKER_URL); + + var promise = new Promise((resolve, reject) => { + info("Waiting until workers are ready..."); + worker.addEventListener("message", function onmessage(e) { + is(e.data, "ready", "We want to receive: -ready-"); + worker.removeEventListener("message", onmessage); + resolve(); + }); + worker.postMessage({ type: "page2" }); + }); + + promise.then(function() { + info("Retrieving data from cache..."); + return cacheData(); + }) + + .then(function(content) { + is(content, "page2-0", "We have data from the second worker"); + }) + + .then(function() { + info("Going back"); + testWin.history.back(); + }); + } + + SpecialPowers.pushPrefEnv({ set: [ + ["dom.caches.enabled", true], + ["dom.caches.testing.enabled", true], + ] }, + function() { + testWin = window.open(testUrl1); + }); + + SimpleTest.waitForExplicitFinish(); + SimpleTest.requestFlakyTimeout("untriaged"); + + </script> +</body> +</html> + diff --git a/dom/workers/test/test_suspend.html b/dom/workers/test/test_suspend.html new file mode 100644 index 000000000..806b97f6c --- /dev/null +++ b/dom/workers/test/test_suspend.html @@ -0,0 +1,138 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test for DOM Worker Threads</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"> +<iframe id="workerFrame" src="suspend_iframe.html" onload="subframeLoaded();"> +</iframe> +<script class="testbody" type="text/javascript"> + + SimpleTest.waitForExplicitFinish(); + + var iframe; + var lastCount; + + var suspended = false; + var resumed = false; + var finished = false; + + var interval; + var oldMessageCount; + var waitCount = 0; + + function finishTest() { + if (finished) { + return; + } + finished = true; + SpecialPowers.flushPrefEnv(function () { + iframe.terminateWorker(); + SimpleTest.finish(); + }); + } + + function waitInterval() { + if (finished) { + return; + } + is(String(iframe.location), "about:blank", "Wrong url!"); + is(suspended, true, "Not suspended?"); + is(resumed, false, "Already resumed?!"); + is(lastCount, oldMessageCount, "Received a message while suspended!"); + if (++waitCount == 5) { + clearInterval(interval); + resumed = true; + iframe.history.back(); + } + } + + function badOnloadCallback() { + if (finished) { + return; + } + ok(false, "We don't want suspend_iframe.html to fire a new load event, we want it to come out of the bfcache!"); + finishTest(); + } + + function suspendCallback() { + if (finished) { + return; + } + is(String(iframe.location), "about:blank", "Wrong url!"); + is(suspended, false, "Already suspended?"); + is(resumed, false, "Already resumed?"); + SpecialPowers.popPrefEnv(function () { + suspended = true; + var iframeElement = document.getElementById("workerFrame"); + iframeElement.onload = badOnloadCallback; + oldMessageCount = lastCount; + interval = setInterval(waitInterval, 1000); + }); + } + + function messageCallback(data) { + if (finished) { + return; + } + + if (!suspended) { + ok(lastCount === undefined || lastCount == data - 1, + "Got good data, lastCount = " + lastCount + ", data = " + data); + lastCount = data; + if (lastCount == 25) { + SpecialPowers.pushPrefEnv({"set": [["browser.sessionhistory.cache_subframes", true]]}, function () { + iframe.location = "about:blank"; + // We want suspend_iframe.html to go into bfcache, so we need to flush + // out all pending notifications. Otherwise, if they're flushed too + // late, they could kick us out of the bfcache again. + iframe.document.body.offsetTop; + }); + } + return; + } + + var newLocation = + window.location.toString().replace("test_suspend.html", + "suspend_iframe.html"); + is(newLocation.indexOf(iframe.location.toString()), 0, "Wrong url!"); + is(resumed, true, "Got message before resumed!"); + is(lastCount, data - 1, "Missed a message, suspend failed!"); + finishTest(); + } + + function errorCallback(data) { + if (finished) { + return; + } + ok(false, "Iframe had an error: '" + data + "'"); + finishTest(); + } + + function subframeLoaded() { + if (finished) { + return; + } + var iframeElement = document.getElementById("workerFrame"); + iframeElement.onload = suspendCallback; + + iframe = iframeElement.contentWindow; + ok(iframe, "No iframe?!"); + + iframe.startWorker(messageCallback, errorCallback); + } + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_terminate.html b/dom/workers/test/test_terminate.html new file mode 100644 index 000000000..5d31bd165 --- /dev/null +++ b/dom/workers/test/test_terminate.html @@ -0,0 +1,100 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<!-- +Tests of DOM Worker terminate feature +--> +<head> + <title>Test for DOM Worker Navigator</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"> +<script class="testbody" language="javascript"> + + + var messageCount = 0; + var intervalCount = 0; + + var interval; + + var worker; + + function messageListener(event) { + is(event.data, "Still alive!", "Correct message!"); + if (++messageCount == 20) { + ok(worker.onmessage === messageListener, + "Correct listener before terminate"); + + worker.terminate(); + + var exception = false; + try { + worker.addEventListener("message", messageListener, false); + } + catch (e) { + exception = true; + } + is(exception, false, "addEventListener didn't throw after terminate"); + + exception = false; + try { + worker.removeEventListener("message", messageListener, false); + } + catch (e) { + exception = true; + } + is(exception, false, "removeEventListener didn't throw after terminate"); + + exception = false; + try { + worker.postMessage("foo"); + } + catch (e) { + exception = true; + } + is(exception, false, "postMessage didn't throw after terminate"); + + exception = false; + try { + worker.terminate(); + } + catch (e) { + exception = true; + } + is(exception, false, "terminate didn't throw after terminate"); + + ok(worker.onmessage === messageListener, + "Correct listener after terminate"); + + worker.onmessage = function(event) { } + + interval = setInterval(testCount, 1000); + } + } + + function testCount() { + is(messageCount, 20, "Received another message after terminated!"); + if (intervalCount++ == 5) { + clearInterval(interval); + SimpleTest.finish(); + } + } + + worker = new Worker("terminate_worker.js"); + worker.onmessage = messageListener; + + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_threadErrors.html b/dom/workers/test/test_threadErrors.html new file mode 100644 index 000000000..034a443e7 --- /dev/null +++ b/dom/workers/test/test_threadErrors.html @@ -0,0 +1,64 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<!-- +Tests of DOM Worker Threads (Bug 437152) +--> +<head> + <title>Test for DOM Worker Threads (Bug 437152)</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.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=437152">DOM Worker Threads Bug 437152</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> + + const expectedErrorCount = 4; + + function messageListener(event) { + ok(false, "Unexpected message: " + event.data); + SimpleTest.finish(); + }; + + var actualErrorCount = 0; + var failedWorkers = []; + + function errorListener(event) { + event.preventDefault(); + + if (failedWorkers.indexOf(event.target) != -1) { + ok(false, "Seen an extra error from this worker"); + SimpleTest.finish(); + return; + } + + failedWorkers.push(event.target); + actualErrorCount++; + + if (actualErrorCount == expectedErrorCount) { + ok(true, "all errors correctly detected"); + SimpleTest.finish(); + } + }; + + for (var i = 1; i <= expectedErrorCount; i++) { + var worker = new Worker("threadErrors_worker" + i + ".js"); + worker.onmessage = messageListener; + worker.onerror = errorListener; + worker.postMessage("Hi"); + } + + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_threadTimeouts.html b/dom/workers/test/test_threadTimeouts.html new file mode 100644 index 000000000..0fa86935a --- /dev/null +++ b/dom/workers/test/test_threadTimeouts.html @@ -0,0 +1,61 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<!-- +Tests of DOM Worker Threads (Bug 437152) +--> +<head> + <title>Test for DOM Worker Threads (Bug 437152)</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.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=437152">DOM Worker Threads Bug 437152</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> + + var worker = new Worker("threadTimeouts_worker.js"); + + worker.onmessage = function(event) { + is(event.target, worker); + switch (event.data) { + case "timeoutFinished": + event.target.postMessage("startInterval"); + break; + case "intervalFinished": + event.target.postMessage("cancelInterval"); + break; + case "intervalCanceled": + worker.postMessage("startExpression"); + break; + case "expressionFinished": + SimpleTest.finish(); + break; + default: + ok(false, "Unexpected message"); + SimpleTest.finish(); + } + }; + + worker.onerror = function(event) { + is(event.target, worker); + ok(false, "Worker had an error: " + event.message); + SimpleTest.finish(); + }; + + worker.postMessage("startTimeout"); + + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/test_throwingOnerror.html b/dom/workers/test/test_throwingOnerror.html new file mode 100644 index 000000000..7676541f7 --- /dev/null +++ b/dom/workers/test/test_throwingOnerror.html @@ -0,0 +1,54 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<!-- +Tests of DOM Worker Threads +--> +<head> + <title>Test for DOM Worker Threads Recursion</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"> +<script class="testbody" type="text/javascript"> + + var worker = new Worker("throwingOnerror_worker.js"); + + var errors = ["foo", "bar"]; + + worker.onerror = function(event) { + event.preventDefault(); + var found = false; + for (var index in errors) { + if (event.message == "uncaught exception: " + errors[index]) { + errors.splice(index, 1); + found = true; + break; + } + } + is(found, true, "Unexpected error!"); + }; + + worker.onmessage = function(event) { + is(errors.length, 0, "Didn't see expected errors!"); + SimpleTest.finish(); + }; + + for (var i = 0; i < 2; i++) { + worker.postMessage(""); + } + + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_timeoutTracing.html b/dom/workers/test/test_timeoutTracing.html new file mode 100644 index 000000000..6770d36a1 --- /dev/null +++ b/dom/workers/test/test_timeoutTracing.html @@ -0,0 +1,48 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<!-- +Tests of DOM Worker Threads +--> +<head> + <title>Test for DOM Worker Threads</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<pre id="test"> +<script class="testbody" type="text/javascript"> + + var worker = new Worker("timeoutTracing_worker.js"); + + worker.onmessage = function(event) { + // begin + worker.onmessage = null; + + // 1 second should be enough to crash. + window.setTimeout(function(event) { + ok(true, "Didn't crash!"); + SimpleTest.finish(); + }, 1000); + + var os = SpecialPowers.Cc["@mozilla.org/observer-service;1"] + .getService(SpecialPowers.Ci.nsIObserverService); + os.notifyObservers(null, "memory-pressure", "heap-minimize"); + } + + worker.onerror = function(event) { + ok(false, "I was expecting a crash, not an error"); + SimpleTest.finish(); + }; + + SimpleTest.waitForExplicitFinish(); + SimpleTest.requestFlakyTimeout("untriaged"); + +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/test_transferable.html b/dom/workers/test/test_transferable.html new file mode 100644 index 000000000..a3deec12a --- /dev/null +++ b/dom/workers/test/test_transferable.html @@ -0,0 +1,123 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<!-- +Tests of DOM Worker transferable objects +--> +<head> + <title>Test for DOM Worker transferable objects</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"> +<script class="testbody" language="javascript"> + + function test1(sizes) { + if (!sizes.length) { + runTests(); + return; + } + + var size = sizes.pop(); + + var worker = new Worker("transferable_worker.js"); + worker.onmessage = function(event) { + ok(event.data.status, event.data.event); + if (!event.data.status) { + runTests(); + return; + } + + if ("notEmpty" in event.data && "byteLength" in event.data.notEmpty) { + ok(event.data.notEmpty.byteLength != 0, + "P: NotEmpty object received: " + event.data.notEmpty.byteLength); + } + + if (!event.data.last) + return; + + test1(sizes); + } + worker.onerror = function(event) { + ok(false, "No errors!"); + } + + try { + worker.postMessage(42, true); + ok(false, "P: PostMessage - Exception for wrong type"); + } catch(e) { + ok(true, "P: PostMessage - Exception for wrong type"); + } + + try { + ab = new ArrayBuffer(size); + worker.postMessage(42,[ab, ab]); + ok(false, "P: PostMessage - Exception for duplicate"); + } catch(e) { + ok(true, "P: PostMessage - Exception for duplicate"); + } + + var ab = new ArrayBuffer(size); + ok(ab.byteLength == size, "P: The size is: " + size + " == " + ab.byteLength); + worker.postMessage({ data: 0, timeout: 0, ab: ab, cb: ab, size: size }, [ab]); + ok(ab.byteLength == 0, "P: PostMessage - The size is: 0 == " + ab.byteLength) + } + + function test2() { + var worker = new Worker("transferable_worker.js"); + worker.onmessage = function(event) { + ok(event.data.status, event.data.event); + if (!event.data.status) { + runTests(); + return; + } + + if ("notEmpty" in event.data && "byteLength" in event.data.notEmpty) { + ok(event.data.notEmpty.byteLength != 0, + "P: NotEmpty object received: " + event.data.notEmpty.byteLength); + } + + if (event.data.last) { + runTests(); + } + } + worker.onerror = function(event) { + ok(false, "No errors!"); + } + + var f = new Float32Array([0,1,2,3]); + ok(f.byteLength != 0, "P: The size is: " + f.byteLength + " is not 0"); + worker.postMessage({ event: "P: postMessage with Float32Array", status: true, + size: 4, notEmpty: f, bc: [ f, f, { dd: f } ] }, [f.buffer]); + ok(f.byteLength == 0, "P: The size is: " + f.byteLength + " is 0"); + } + + var tests = [ + function() { test1([1024 * 1024 * 32, 128, 4]); }, + test2 + ]; + function runTests() { + if (!tests.length) { + SimpleTest.finish(); + return; + } + + var test = tests.shift(); + test(); + } + + SimpleTest.waitForExplicitFinish(); + runTests(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_webSocket_sharedWorker.html b/dom/workers/test/test_webSocket_sharedWorker.html new file mode 100644 index 000000000..7609a164b --- /dev/null +++ b/dom/workers/test/test_webSocket_sharedWorker.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>Test for bug 1090183</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 sw = new SharedWorker('webSocket_sharedWorker.js'); +sw.port.onmessage = function(event) { + if (event.data.type == 'finish') { + SimpleTest.finish(); + } else if (event.data.type == 'status') { + ok(event.data.status, event.data.msg); + } +} + +SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_websocket1.html b/dom/workers/test/test_websocket1.html new file mode 100644 index 000000000..6c74af03c --- /dev/null +++ b/dom/workers/test/test_websocket1.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 for WebSocket object in 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> +<pre id="feedback"></pre> +<script class="testbody" type="text/javascript"> + + var worker = new Worker("websocket_worker1.js"); + + worker.onmessage = function(event) { + is(event.target, worker, "event.target should be a worker!"); + + if (event.data.type == 'finish') { + info("All done!"); + SimpleTest.finish(); + } else if (event.data.type == 'status') { + ok(event.data.status, event.data.msg); + } else if (event.data.type == 'feedback') { + info(event.data.msg); + document.getElementById('feedback').innerHTML += event.data.msg + "\n"; + } + }; + + worker.onerror = function(event) { + is(event.target, worker); + info("error!"); + ok(false, "Worker had an error: " + event.data); + SimpleTest.finish(); + }; + + worker.postMessage('foobar'); + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_websocket2.html b/dom/workers/test/test_websocket2.html new file mode 100644 index 000000000..d774be5a2 --- /dev/null +++ b/dom/workers/test/test_websocket2.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 for WebSocket object in 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> +<pre id="feedback"></pre> +<script class="testbody" type="text/javascript"> + + var worker = new Worker("websocket_worker2.js"); + + worker.onmessage = function(event) { + is(event.target, worker, "event.target should be a worker!"); + + if (event.data.type == 'finish') { + info("All done!"); + SimpleTest.finish(); + } else if (event.data.type == 'status') { + ok(event.data.status, event.data.msg); + } else if (event.data.type == 'feedback') { + info(event.data.msg); + document.getElementById('feedback').innerHTML += event.data.msg + "\n"; + } + }; + + worker.onerror = function(event) { + is(event.target, worker); + info("error!"); + ok(false, "Worker had an error: " + event.data); + SimpleTest.finish(); + }; + + worker.postMessage('foobar'); + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_websocket3.html b/dom/workers/test/test_websocket3.html new file mode 100644 index 000000000..0882cf313 --- /dev/null +++ b/dom/workers/test/test_websocket3.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 for WebSocket object in 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> +<pre id="feedback"></pre> +<script class="testbody" type="text/javascript"> + + var worker = new Worker("websocket_worker3.js"); + + worker.onmessage = function(event) { + is(event.target, worker, "event.target should be a worker!"); + + if (event.data.type == 'finish') { + info("All done!"); + SimpleTest.finish(); + } else if (event.data.type == 'status') { + ok(event.data.status, event.data.msg); + } else if (event.data.type == 'feedback') { + info(event.data.msg); + document.getElementById('feedback').innerHTML += event.data.msg + "\n"; + } + }; + + worker.onerror = function(event) { + is(event.target, worker); + info("error!"); + ok(false, "Worker had an error: " + event.data); + SimpleTest.finish(); + }; + + worker.postMessage('foobar'); + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_websocket4.html b/dom/workers/test/test_websocket4.html new file mode 100644 index 000000000..8c6bef506 --- /dev/null +++ b/dom/workers/test/test_websocket4.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 for WebSocket object in 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> +<pre id="feedback"></pre> +<script class="testbody" type="text/javascript"> + + var worker = new Worker("websocket_worker4.js"); + + worker.onmessage = function(event) { + is(event.target, worker, "event.target should be a worker!"); + + if (event.data.type == 'finish') { + info("All done!"); + SimpleTest.finish(); + } else if (event.data.type == 'status') { + ok(event.data.status, event.data.msg); + } else if (event.data.type == 'feedback') { + info(event.data.msg); + document.getElementById('feedback').innerHTML += event.data.msg + "\n"; + } + }; + + worker.onerror = function(event) { + is(event.target, worker); + info("error!"); + ok(false, "Worker had an error: " + event.data); + SimpleTest.finish(); + }; + + worker.postMessage('foobar'); + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_websocket5.html b/dom/workers/test/test_websocket5.html new file mode 100644 index 000000000..cadae3782 --- /dev/null +++ b/dom/workers/test/test_websocket5.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 for WebSocket object in 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> +<pre id="feedback"></pre> +<script class="testbody" type="text/javascript"> + + var worker = new Worker("websocket_worker5.js"); + + worker.onmessage = function(event) { + is(event.target, worker, "event.target should be a worker!"); + + if (event.data.type == 'finish') { + info("All done!"); + SimpleTest.finish(); + } else if (event.data.type == 'status') { + ok(event.data.status, event.data.msg); + } else if (event.data.type == 'feedback') { + info(event.data.msg); + document.getElementById('feedback').innerHTML += event.data.msg + "\n"; + } + }; + + worker.onerror = function(event) { + is(event.target, worker); + info("error!"); + ok(false, "Worker had an error: " + event.data); + SimpleTest.finish(); + }; + + worker.postMessage('foobar'); + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_websocket_basic.html b/dom/workers/test/test_websocket_basic.html new file mode 100644 index 000000000..5c9f8bf10 --- /dev/null +++ b/dom/workers/test/test_websocket_basic.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 for WebSocket object in 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"> + + var worker = new Worker("websocket_basic_worker.js"); + + worker.onmessage = function(event) { + is(event.target, worker); + + if (event.data.type == 'finish') { + runTest(); + } else if (event.data.type == 'status') { + ok(event.data.status, event.data.msg); + } + }; + + worker.onerror = function(event) { + is(event.target, worker); + ok(false, "Worker had an error: " + event.data); + SimpleTest.finish(); + }; + + var tests = [ + function() { worker.postMessage(0); }, + function() { worker.postMessage(1); } + ]; + + function runTest() { + if (!tests.length) { + SimpleTest.finish(); + return; + } + + var test = tests.shift(); + test(); + } + + runTest(); + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_websocket_https.html b/dom/workers/test/test_websocket_https.html new file mode 100644 index 000000000..aa94141fd --- /dev/null +++ b/dom/workers/test/test_websocket_https.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>Test that creating insecure websockets from https workers is not possible</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"> +<script class="testbody" language="javascript"> + + onmessage = function(event) { + is(event.data, "not created", "WebSocket object must not be created"); + SimpleTest.finish(); + }; + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +<iframe src="https://example.com/tests/dom/workers/test/websocket_https.html"></iframe> +</body> +</html> diff --git a/dom/workers/test/test_websocket_loadgroup.html b/dom/workers/test/test_websocket_loadgroup.html new file mode 100644 index 000000000..3c65e262b --- /dev/null +++ b/dom/workers/test/test_websocket_loadgroup.html @@ -0,0 +1,61 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test for WebSocket object in 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"> + + var worker = new Worker("websocket_loadgroup_worker.js"); + + var stopped = false; + worker.onmessage = function(e) { + if (e.data == 'opened') { + stopped = true; + window.stop(); + } else if (e.data == 'closed') { + ok(stopped, "Good!"); + stopped = false; + runTest(); + } else { + ok(false, "An error has been received"); + } + }; + + worker.onerror = function(event) { + is(event.target, worker); + ok(false, "Worker had an error: " + event.data); + SimpleTest.finish(); + }; + + var tests = [ + function() { worker.postMessage(0); }, + function() { worker.postMessage(1); } + ]; + + function runTest() { + if (!tests.length) { + SimpleTest.finish(); + return; + } + + var test = tests.shift(); + test(); + } + + runTest(); + SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/dom/workers/test/test_worker_interfaces.html b/dom/workers/test/test_worker_interfaces.html new file mode 100644 index 000000000..26af63e51 --- /dev/null +++ b/dom/workers/test/test_worker_interfaces.html @@ -0,0 +1,16 @@ +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<!DOCTYPE HTML> +<html> +<head> + <title>Validate Interfaces Exposed to 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"> +workerTestExec("test_worker_interfaces.js"); +</script> +</body> +</html> diff --git a/dom/workers/test/test_worker_interfaces.js b/dom/workers/test/test_worker_interfaces.js new file mode 100644 index 000000000..e0647682c --- /dev/null +++ b/dom/workers/test/test_worker_interfaces.js @@ -0,0 +1,291 @@ +// 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! + "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! + "DedicatedWorkerGlobalScope", +// 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! + "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! + { name: "OffscreenCanvas", disabled: true }, +// 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! + {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! + "XMLHttpRequest", +// IMPORTANT: Do not change this list without review from a DOM peer! + "XMLHttpRequestEventTarget", +// IMPORTANT: Do not change this list without review from a DOM peer! + "XMLHttpRequestUpload", +// 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! + { name: "WebGLActiveInfo", disabled: true }, +// IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLBuffer", disabled: true }, +// IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLContextEvent", disabled: true }, +// IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLFramebuffer", disabled: true }, +// IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLProgram", disabled: true }, +// IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLRenderbuffer", disabled: true }, +// IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLRenderingContext", disabled: true }, +// IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLShader", disabled: true }, +// IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLShaderPrecisionFormat", disabled: true }, +// IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLTexture", disabled: true }, +// IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLUniformLocation", disabled: true }, +// 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! + "Worker", +// 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.desktop === !isDesktop) || + (entry.android === !isAndroid && !entry.nightlyAndroid) || + (entry.release === !isRelease) || + entry.disabled) { + 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 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/test_workersDisabled.html b/dom/workers/test/test_workersDisabled.html new file mode 100644 index 000000000..39a7fcc5b --- /dev/null +++ b/dom/workers/test/test_workersDisabled.html @@ -0,0 +1,54 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> + <head> + <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"> + SimpleTest.waitForExplicitFinish(); + + const enabledPref = "dom.workers.enabled"; + + is(SpecialPowers.getBoolPref(enabledPref), true, + "Workers should be enabled."); + + SpecialPowers.pushPrefEnv({"set": [[enabledPref, false]]}, test1); + + function test1() { + ok(!("Worker" in window), "Worker constructor should not be available."); + + var exception; + try { + var worker = new Worker("workersDisabled_worker.js"); + } + catch(e) { + exception = e; + } + + ok(exception, "Shouldn't be able to make a worker."); + + SpecialPowers.pushPrefEnv({"set": [[enabledPref, true]]}, test2); + } + + function test2() { + ok(("Worker" in window), "Worker constructor should be available."); + + const message = "Hi"; + + var worker = new Worker("workersDisabled_worker.js"); + worker.onmessage = function(event) { + is(event.data, message, "Good message."); + SimpleTest.finish(); + } + worker.postMessage(message); + } + </script> + </body> +</html> + diff --git a/dom/workers/test/test_workersDisabled.xul b/dom/workers/test/test_workersDisabled.xul new file mode 100644 index 000000000..1674f819f --- /dev/null +++ b/dom/workers/test/test_workersDisabled.xul @@ -0,0 +1,49 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="DOM Worker Threads Test" + 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="dom_worker_helper.js"/> + + <script type="application/javascript"> + <![CDATA[ + function test() + { + const enabledPref = "dom.workers.enabled"; + const message = "Hi"; + + var prefs = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefBranch); + is(prefs.getBoolPref(enabledPref), true, "Workers should be enabled."); + + prefs.setBoolPref(enabledPref, false); + + ok("Worker" in window, "Worker constructor should be available."); + ok("ChromeWorker" in window, + "ChromeWorker constructor should be available."); + + var worker = new ChromeWorker("workersDisabled_worker.js"); + worker.onmessage = function(event) { + is(event.data, message, "Good message."); + prefs.clearUserPref(enabledPref); + finish(); + } + worker.postMessage(message); + + waitForWorkerFinish(); + } + ]]> + </script> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <p id="display"></p> + <div id="content" style="display:none;"></div> + <pre id="test"></pre> + </body> + <label id="test-result"/> +</window> diff --git a/dom/workers/test/threadErrors_worker1.js b/dom/workers/test/threadErrors_worker1.js new file mode 100644 index 000000000..c0ddade82 --- /dev/null +++ b/dom/workers/test/threadErrors_worker1.js @@ -0,0 +1,8 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +// Syntax error +onmessage = function(event) { + for (var i = 0; i < 10) { } +} diff --git a/dom/workers/test/threadErrors_worker2.js b/dom/workers/test/threadErrors_worker2.js new file mode 100644 index 000000000..0462d9668 --- /dev/null +++ b/dom/workers/test/threadErrors_worker2.js @@ -0,0 +1,8 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +// Bad function error +onmessage = function(event) { + foopy(); +} diff --git a/dom/workers/test/threadErrors_worker3.js b/dom/workers/test/threadErrors_worker3.js new file mode 100644 index 000000000..151ffbe5e --- /dev/null +++ b/dom/workers/test/threadErrors_worker3.js @@ -0,0 +1,9 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +// Unhandled exception in body +onmessage = function(event) { +}; + +throw new Error("Bah!"); diff --git a/dom/workers/test/threadErrors_worker4.js b/dom/workers/test/threadErrors_worker4.js new file mode 100644 index 000000000..4518ce017 --- /dev/null +++ b/dom/workers/test/threadErrors_worker4.js @@ -0,0 +1,8 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +// Throwing message listener +onmessage = function(event) { + throw new Error("Bah!"); +}; diff --git a/dom/workers/test/threadTimeouts_worker.js b/dom/workers/test/threadTimeouts_worker.js new file mode 100644 index 000000000..7aaac03d2 --- /dev/null +++ b/dom/workers/test/threadTimeouts_worker.js @@ -0,0 +1,44 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +var gTimeoutId; +var gTimeoutCount = 0; +var gIntervalCount = 0; + +function timeoutFunc() { + if (++gTimeoutCount > 1) { + throw new Error("Timeout called more than once!"); + } + postMessage("timeoutFinished"); +} + +function intervalFunc() { + if (++gIntervalCount == 2) { + postMessage("intervalFinished"); + } +} + +function messageListener(event) { + switch (event.data) { + case "startTimeout": + gTimeoutId = setTimeout(timeoutFunc, 2000); + clearTimeout(gTimeoutId); + gTimeoutId = setTimeout(timeoutFunc, 2000); + break; + case "startInterval": + gTimeoutId = setInterval(intervalFunc, 2000); + break; + case "cancelInterval": + clearInterval(gTimeoutId); + postMessage("intervalCanceled"); + break; + case "startExpression": + setTimeout("this.postMessage('expressionFinished');", 2000); + break; + default: + throw "Bad message: " + event.data; + } +} + +addEventListener("message", messageListener, false); diff --git a/dom/workers/test/throwingOnerror_worker.js b/dom/workers/test/throwingOnerror_worker.js new file mode 100644 index 000000000..07e01787b --- /dev/null +++ b/dom/workers/test/throwingOnerror_worker.js @@ -0,0 +1,15 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onerror = function(event) { + throw "bar"; +}; + +var count = 0; +onmessage = function(event) { + if (!count++) { + throw "foo"; + } + postMessage(""); +}; diff --git a/dom/workers/test/timeoutTracing_worker.js b/dom/workers/test/timeoutTracing_worker.js new file mode 100644 index 000000000..d62cc50c5 --- /dev/null +++ b/dom/workers/test/timeoutTracing_worker.js @@ -0,0 +1,13 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +onmessage = function(event) { + throw "No messages should reach me!"; +} + +setInterval(function() { postMessage("Still alive!"); }, 20); +setInterval(";", 20); + +postMessage("Begin!"); diff --git a/dom/workers/test/transferable_worker.js b/dom/workers/test/transferable_worker.js new file mode 100644 index 000000000..3caf6121b --- /dev/null +++ b/dom/workers/test/transferable_worker.js @@ -0,0 +1,23 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +onmessage = function(event) { + if ("notEmpty" in event.data && "byteLength" in event.data.notEmpty) { + postMessage({ event: "W: NotEmpty object received: " + event.data.notEmpty.byteLength, + status: event.data.notEmpty.byteLength != 0, last: false }); + } + + var ab = new ArrayBuffer(event.data.size); + postMessage({ event: "W: The size is: " + event.data.size + " == " + ab.byteLength, + status: ab.byteLength == event.data.size, last: false }); + + postMessage({ event: "W: postMessage with arrayBuffer", status: true, + notEmpty: ab, ab: ab, bc: [ ab, ab, { dd: ab } ] }, [ab]); + + postMessage({ event: "W: The size is: 0 == " + ab.byteLength, + status: ab.byteLength == 0, last: false }); + + postMessage({ event: "W: last one!", status: true, last: true }); +} diff --git a/dom/workers/test/webSocket_sharedWorker.js b/dom/workers/test/webSocket_sharedWorker.js new file mode 100644 index 000000000..7c8fa0e12 --- /dev/null +++ b/dom/workers/test/webSocket_sharedWorker.js @@ -0,0 +1,20 @@ +onconnect = function(evt) { + var ws = new WebSocket("ws://mochi.test:8888/tests/dom/base/test/file_websocket_hello"); + + ws.onopen = function(e) { + evt.ports[0].postMessage({type: 'status', status: true, msg: 'OnOpen called' }); + ws.send("data"); + } + + ws.onclose = function(e) {} + + ws.onerror = function(e) { + evt.ports[0].postMessage({type: 'status', status: false, msg: 'onerror called!'}); + } + + ws.onmessage = function(e) { + evt.ports[0].postMessage({type: 'status', status: e.data == 'Hello world!', msg: 'Wrong data'}); + ws.close(); + evt.ports[0].postMessage({type: 'finish' }); + } +} diff --git a/dom/workers/test/websocket_basic_worker.js b/dom/workers/test/websocket_basic_worker.js new file mode 100644 index 000000000..256af569a --- /dev/null +++ b/dom/workers/test/websocket_basic_worker.js @@ -0,0 +1,39 @@ +onmessage = function(event) { + if (event.data != 0) { + var worker = new Worker('websocket_basic_worker.js'); + worker.onmessage = function(event) { + postMessage(event.data); + } + + worker.postMessage(event.data - 1); + return; + } + + status = false; + try { + if ((WebSocket instanceof Object)) { + status = true; + } + } catch(e) { + } + + postMessage({type: 'status', status: status, msg: 'WebSocket object:' + WebSocket}); + + var ws = new WebSocket("ws://mochi.test:8888/tests/dom/base/test/file_websocket_hello"); + ws.onopen = function(e) { + postMessage({type: 'status', status: true, msg: 'OnOpen called' }); + ws.send("data"); + } + + ws.onclose = function(e) {} + + ws.onerror = function(e) { + postMessage({type: 'status', status: false, msg: 'onerror called!'}); + } + + ws.onmessage = function(e) { + postMessage({type: 'status', status: e.data == 'Hello world!', msg: 'Wrong data'}); + ws.close(); + postMessage({type: 'finish' }); + } +} diff --git a/dom/workers/test/websocket_helpers.js b/dom/workers/test/websocket_helpers.js new file mode 100644 index 000000000..c2dc3f969 --- /dev/null +++ b/dom/workers/test/websocket_helpers.js @@ -0,0 +1,19 @@ +function feedback() { + postMessage({type: 'feedback', msg: "executing test: " + (current_test+1) + " of " + tests.length + " tests." }); +} + +function ok(status, msg) { + postMessage({type: 'status', status: !!status, msg: msg}); +} + +function is(a, b, msg) { + ok(a === b, msg); +} + +function isnot(a, b, msg) { + ok(a != b, msg); +} + +function finish() { + postMessage({type: 'finish'}); +} diff --git a/dom/workers/test/websocket_https.html b/dom/workers/test/websocket_https.html new file mode 100644 index 000000000..549147a33 --- /dev/null +++ b/dom/workers/test/websocket_https.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<script> + var worker = new Worker("https://example.com/tests/dom/workers/test/websocket_https_worker.js"); + + worker.onmessage = function(event) { + parent.postMessage(event.data, "*"); + }; + + worker.onerror = function(event) { + parent.postMessage("error", "*"); + }; + + worker.postMessage("start"); +</script> diff --git a/dom/workers/test/websocket_https_worker.js b/dom/workers/test/websocket_https_worker.js new file mode 100644 index 000000000..2592ed6d0 --- /dev/null +++ b/dom/workers/test/websocket_https_worker.js @@ -0,0 +1,9 @@ +onmessage = function() { + var wsCreated = true; + try { + new WebSocket("ws://mochi.test:8888/tests/dom/base/test/file_websocket_hello"); + } catch(e) { + wsCreated = false; + } + postMessage(wsCreated ? "created" : "not created"); +}; diff --git a/dom/workers/test/websocket_loadgroup_worker.js b/dom/workers/test/websocket_loadgroup_worker.js new file mode 100644 index 000000000..21cf248bc --- /dev/null +++ b/dom/workers/test/websocket_loadgroup_worker.js @@ -0,0 +1,24 @@ +onmessage = function(event) { + if (event.data != 0) { + var worker = new Worker('websocket_loadgroup_worker.js'); + worker.onmessage = function(event) { + postMessage(event.data); + } + + worker.postMessage(event.data - 1); + return; + } + + var ws = new WebSocket("ws://mochi.test:8888/tests/dom/base/test/file_websocket_hello"); + ws.onopen = function(e) { + postMessage('opened'); + } + + ws.onclose = function(e) { + postMessage('closed'); + } + + ws.onerror = function(e) { + postMessage('error'); + } +} diff --git a/dom/workers/test/websocket_worker1.js b/dom/workers/test/websocket_worker1.js new file mode 100644 index 000000000..cc2b6e825 --- /dev/null +++ b/dom/workers/test/websocket_worker1.js @@ -0,0 +1,19 @@ +importScripts('../../../dom/base/test/websocket_helpers.js'); +importScripts('../../../dom/base/test/websocket_tests.js'); +importScripts('websocket_helpers.js'); + +var tests = [ + test1, // client tries to connect to a http scheme location; + test2, // assure serialization of the connections; + test3, // client tries to connect to an non-existent ws server; + test4, // client tries to connect using a relative url; + test5, // client uses an invalid protocol value; + test6, // counter and encoding check; + test7, // onmessage event origin property check + test8, // client calls close() and the server sends the close frame (with no + // code or reason) in acknowledgement; + test9, // client closes the connection before the ws connection is established; + test10, // client sends a message before the ws connection is established; +]; + +doTest(); diff --git a/dom/workers/test/websocket_worker2.js b/dom/workers/test/websocket_worker2.js new file mode 100644 index 000000000..9dd4ae685 --- /dev/null +++ b/dom/workers/test/websocket_worker2.js @@ -0,0 +1,19 @@ +importScripts('../../../dom/base/test/websocket_helpers.js'); +importScripts('../../../dom/base/test/websocket_tests.js'); +importScripts('websocket_helpers.js'); + +var tests = [ + test11, // a simple hello echo; + test12, // client sends a message containing unpaired surrogates + test13, //server sends an invalid message; + test14, // server sends the close frame, it doesn't close the tcp connection + // and it keeps sending normal ws messages; + test15, // server closes the tcp connection, but it doesn't send the close + // frame; + test16, // client calls close() and tries to send a message; + test18, // client tries to connect to an http resource; + test19, // server closes the tcp connection before establishing the ws + // connection; +]; + +doTest(); diff --git a/dom/workers/test/websocket_worker3.js b/dom/workers/test/websocket_worker3.js new file mode 100644 index 000000000..d2811294f --- /dev/null +++ b/dom/workers/test/websocket_worker3.js @@ -0,0 +1,17 @@ +importScripts('../../../dom/base/test/websocket_helpers.js'); +importScripts('../../../dom/base/test/websocket_tests.js'); +importScripts('websocket_helpers.js'); + +var tests = [ + test24, // server rejects sub-protocol string + test25, // ctor with valid empty sub-protocol array + test26, // ctor with invalid sub-protocol array containing 1 empty element + test27, // ctor with invalid sub-protocol array containing an empty element in + // list + test28, // ctor using valid 1 element sub-protocol array + test29, // ctor using all valid 5 element sub-protocol array + test30, // ctor using valid 1 element sub-protocol array with element server + // will reject +]; + +doTest(); diff --git a/dom/workers/test/websocket_worker4.js b/dom/workers/test/websocket_worker4.js new file mode 100644 index 000000000..030ee67f5 --- /dev/null +++ b/dom/workers/test/websocket_worker4.js @@ -0,0 +1,19 @@ +importScripts('../../../dom/base/test/websocket_helpers.js'); +importScripts('../../../dom/base/test/websocket_tests.js'); +importScripts('websocket_helpers.js'); + +var tests = [ + test31, // ctor using valid 2 element sub-protocol array with 1 element server + // will reject and one server will accept + test32, // ctor using invalid sub-protocol array that contains duplicate items + test33, // test for sending/receiving custom close code (but no close reason) + test34, // test for receiving custom close code and reason + test35, // test for sending custom close code and reason + test36, // negative test for sending out of range close code + test37, // negative test for too long of a close reason + test38, // ensure extensions attribute is defined + test39, // a basic wss:// connectivity test + test40, // negative test for wss:// with no cert +]; + +doTest(); diff --git a/dom/workers/test/websocket_worker5.js b/dom/workers/test/websocket_worker5.js new file mode 100644 index 000000000..41d6e9be0 --- /dev/null +++ b/dom/workers/test/websocket_worker5.js @@ -0,0 +1,13 @@ +importScripts('../../../dom/base/test/websocket_helpers.js'); +importScripts('../../../dom/base/test/websocket_tests.js'); +importScripts('websocket_helpers.js'); + +var tests = [ + test42, // non-char utf-8 sequences + test43, // Test setting binaryType attribute + test44, // Test sending/receving binary ArrayBuffer + test46, // Test that we don't dispatch incoming msgs once in CLOSING state + test47, // Make sure onerror/onclose aren't called during close() +]; + +doTest(); diff --git a/dom/workers/test/window_suspended.html b/dom/workers/test/window_suspended.html new file mode 100644 index 000000000..b482cbe09 --- /dev/null +++ b/dom/workers/test/window_suspended.html @@ -0,0 +1,5 @@ +<script> +onpageshow = function(e) { + opener[location.search.split('?')[1]](e); +} +</script> diff --git a/dom/workers/test/worker_bug1278777.js b/dom/workers/test/worker_bug1278777.js new file mode 100644 index 000000000..c056d0254 --- /dev/null +++ b/dom/workers/test/worker_bug1278777.js @@ -0,0 +1,9 @@ +var xhr = new XMLHttpRequest(); +xhr.responseType = 'blob'; +xhr.open('GET', 'worker_bug1278777.js'); + +xhr.onload = function() { + postMessage(xhr.response instanceof Blob); +} + +xhr.send(); diff --git a/dom/workers/test/worker_bug1301094.js b/dom/workers/test/worker_bug1301094.js new file mode 100644 index 000000000..69cc25d23 --- /dev/null +++ b/dom/workers/test/worker_bug1301094.js @@ -0,0 +1,11 @@ +onmessage = function(e) { + var xhr = new XMLHttpRequest(); + xhr.open("POST", 'worker_bug1301094.js', false); + xhr.onload = function() { + self.postMessage("OK"); + }; + + var fd = new FormData(); + fd.append('file', e.data); + xhr.send(fd); +} diff --git a/dom/workers/test/worker_consoleAndBlobs.js b/dom/workers/test/worker_consoleAndBlobs.js new file mode 100644 index 000000000..41e8317ac --- /dev/null +++ b/dom/workers/test/worker_consoleAndBlobs.js @@ -0,0 +1,8 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +var b = new Blob(['123'], { type: 'foo/bar'}); +console.log({ msg: 'consoleAndBlobs', blob: b }); diff --git a/dom/workers/test/worker_driver.js b/dom/workers/test/worker_driver.js new file mode 100644 index 000000000..35eb17985 --- /dev/null +++ b/dom/workers/test/worker_driver.js @@ -0,0 +1,64 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/publicdomain/zero/1.0/ +// +// Utility script for writing worker tests. In your main document do: +// +// <script type="text/javascript" src="worker_driver.js"></script> +// <script type="text/javascript"> +// workerTestExec('myWorkerTestCase.js') +// </script> +// +// This will then spawn a worker, define some utility functions, and then +// execute the code in myWorkerTestCase.js. You can then use these +// functions in your worker-side test: +// +// ok() - like the SimpleTest assert +// is() - like the SimpleTest assert +// workerTestDone() - like SimpleTest.finish() indicating the test is complete +// +// There are also some functions for requesting information that requires +// SpecialPowers or other main-thread-only resources: +// +// workerTestGetVersion() - request the current version string from the MT +// workerTestGetUserAgent() - request the user agent string from the MT +// workerTestGetOSCPU() - request the navigator.oscpu string from the MT +// +// For an example see test_worker_interfaces.html and test_worker_interfaces.js. + +function workerTestExec(script) { + SimpleTest.waitForExplicitFinish(); + var worker = new Worker('worker_wrapper.js'); + worker.onmessage = function(event) { + if (event.data.type == 'finish') { + SimpleTest.finish(); + + } else if (event.data.type == 'status') { + ok(event.data.status, event.data.msg); + + } 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(); + }; + + worker.postMessage({ script: script }); +} diff --git a/dom/workers/test/worker_fileReader.js b/dom/workers/test/worker_fileReader.js new file mode 100644 index 000000000..f4e91b325 --- /dev/null +++ b/dom/workers/test/worker_fileReader.js @@ -0,0 +1,417 @@ +var testRanCounter = 0; +var expectedTestCount = 0; +var testSetupFinished = false; + +function ok(a, msg) { + postMessage({type: 'check', status: !!a, msg: msg }); +} + +function is(a, b, msg) { + ok(a === b, msg); +} + +function finish() { + postMessage({type: 'finish'}); +} + +function convertToUTF16(s) { + res = ""; + for (var i = 0; i < s.length; ++i) { + c = s.charCodeAt(i); + res += String.fromCharCode(c & 255, c >>> 8); + } + return res; +} + +function convertToUTF8(s) { + return unescape(encodeURIComponent(s)); +} + +function convertToDataURL(s) { + return "data:application/octet-stream;base64," + btoa(s); +} + +onmessage = function(message) { + is(FileReader.EMPTY, 0, "correct EMPTY value"); + is(FileReader.LOADING, 1, "correct LOADING value"); + is(FileReader.DONE, 2, "correct DONE value"); + + // List of blobs. + var asciiFile = message.data.blobs.shift(); + var binaryFile = message.data.blobs.shift(); + var nonExistingFile = message.data.blobs.shift(); + var utf8TextFile = message.data.blobs.shift(); + var utf16TextFile = message.data.blobs.shift(); + var emptyFile = message.data.blobs.shift(); + var dataUrlFile0 = message.data.blobs.shift(); + var dataUrlFile1 = message.data.blobs.shift(); + var dataUrlFile2 = message.data.blobs.shift(); + + // List of buffers for testing. + var testTextData = message.data.testTextData; + var testASCIIData = message.data.testASCIIData; + var testBinaryData = message.data.testBinaryData; + var dataurldata0 = message.data.dataurldata0; + var dataurldata1 = message.data.dataurldata1; + var dataurldata2 = message.data.dataurldata2; + + // Test that plain reading works and fires events as expected, both + // for text and binary reading + + var onloadHasRunText = false; + var onloadStartHasRunText = false; + r = new FileReader(); + is(r.readyState, FileReader.EMPTY, "correct initial text readyState"); + r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "plain reading"); + r.addEventListener("load", function() { onloadHasRunText = true }, false); + r.addEventListener("loadstart", function() { onloadStartHasRunText = true }, false); + r.readAsText(asciiFile); + is(r.readyState, FileReader.LOADING, "correct loading text readyState"); + is(onloadHasRunText, false, "text loading must be async"); + is(onloadStartHasRunText, true, "text loadstart should fire sync"); + expectedTestCount++; + + var onloadHasRunBinary = false; + var onloadStartHasRunBinary = false; + r = new FileReader(); + is(r.readyState, FileReader.EMPTY, "correct initial binary readyState"); + r.addEventListener("load", function() { onloadHasRunBinary = true }, false); + r.addEventListener("loadstart", function() { onloadStartHasRunBinary = true }, false); + r.readAsBinaryString(binaryFile); + r.onload = getLoadHandler(testBinaryData, testBinaryData.length, "binary reading"); + is(r.readyState, FileReader.LOADING, "correct loading binary readyState"); + is(onloadHasRunBinary, false, "binary loading must be async"); + is(onloadStartHasRunBinary, true, "binary loadstart should fire sync"); + expectedTestCount++; + + var onloadHasRunArrayBuffer = false; + var onloadStartHasRunArrayBuffer = false; + r = new FileReader(); + is(r.readyState, FileReader.EMPTY, "correct initial arrayBuffer readyState"); + r.addEventListener("load", function() { onloadHasRunArrayBuffer = true }, false); + r.addEventListener("loadstart", function() { onloadStartHasRunArrayBuffer = true }, false); + r.readAsArrayBuffer(binaryFile); + r.onload = getLoadHandlerForArrayBuffer(testBinaryData, testBinaryData.length, "array buffer reading"); + is(r.readyState, FileReader.LOADING, "correct loading arrayBuffer readyState"); + is(onloadHasRunArrayBuffer, false, "arrayBuffer loading must be async"); + is(onloadStartHasRunArrayBuffer, true, "arrayBuffer loadstart should fire sync"); + expectedTestCount++; + + // Test a variety of encodings, and make sure they work properly + r = new FileReader(); + r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "no encoding reading"); + r.readAsText(asciiFile, ""); + expectedTestCount++; + + r = new FileReader(); + r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "iso8859 reading"); + r.readAsText(asciiFile, "iso-8859-1"); + expectedTestCount++; + + r = new FileReader(); + r.onload = getLoadHandler(testTextData, + convertToUTF8(testTextData).length, + "utf8 reading"); + r.readAsText(utf8TextFile, "utf8"); + expectedTestCount++; + + r = new FileReader(); + r.readAsText(utf16TextFile, "utf-16"); + r.onload = getLoadHandler(testTextData, + convertToUTF16(testTextData).length, + "utf16 reading"); + expectedTestCount++; + + // Test get result without reading + r = new FileReader(); + is(r.readyState, FileReader.EMPTY, + "readyState in test reader get result without reading"); + is(r.error, null, + "no error in test reader get result without reading"); + is(r.result, null, + "result in test reader get result without reading"); + + // Test loading an empty file works (and doesn't crash!) + r = new FileReader(); + r.onload = getLoadHandler("", 0, "empty no encoding reading"); + r.readAsText(emptyFile, ""); + expectedTestCount++; + + r = new FileReader(); + r.onload = getLoadHandler("", 0, "empty utf8 reading"); + r.readAsText(emptyFile, "utf8"); + expectedTestCount++; + + r = new FileReader(); + r.onload = getLoadHandler("", 0, "empty utf16 reading"); + r.readAsText(emptyFile, "utf-16"); + expectedTestCount++; + + r = new FileReader(); + r.onload = getLoadHandler("", 0, "empty binary string reading"); + r.readAsBinaryString(emptyFile); + expectedTestCount++; + + r = new FileReader(); + r.onload = getLoadHandlerForArrayBuffer("", 0, "empty array buffer reading"); + r.readAsArrayBuffer(emptyFile); + expectedTestCount++; + + r = new FileReader(); + r.onload = getLoadHandler(convertToDataURL(""), 0, "empt binary string reading"); + r.readAsDataURL(emptyFile); + expectedTestCount++; + + // Test reusing a FileReader to read multiple times + r = new FileReader(); + r.onload = getLoadHandler(testASCIIData, + testASCIIData.length, + "to-be-reused reading text") + var makeAnotherReadListener = function(event) { + r = event.target; + r.removeEventListener("load", makeAnotherReadListener, false); + r.onload = getLoadHandler(testASCIIData, + testASCIIData.length, + "reused reading text"); + r.readAsText(asciiFile); + }; + r.addEventListener("load", makeAnotherReadListener, false); + r.readAsText(asciiFile); + expectedTestCount += 2; + + r = new FileReader(); + r.onload = getLoadHandler(testBinaryData, + testBinaryData.length, + "to-be-reused reading binary") + var makeAnotherReadListener2 = function(event) { + r = event.target; + r.removeEventListener("load", makeAnotherReadListener2, false); + r.onload = getLoadHandler(testBinaryData, + testBinaryData.length, + "reused reading binary"); + r.readAsBinaryString(binaryFile); + }; + r.addEventListener("load", makeAnotherReadListener2, false); + r.readAsBinaryString(binaryFile); + expectedTestCount += 2; + + r = new FileReader(); + r.onload = getLoadHandler(convertToDataURL(testBinaryData), + testBinaryData.length, + "to-be-reused reading data url") + var makeAnotherReadListener3 = function(event) { + r = event.target; + r.removeEventListener("load", makeAnotherReadListener3, false); + r.onload = getLoadHandler(convertToDataURL(testBinaryData), + testBinaryData.length, + "reused reading data url"); + r.readAsDataURL(binaryFile); + }; + r.addEventListener("load", makeAnotherReadListener3, false); + r.readAsDataURL(binaryFile); + expectedTestCount += 2; + + r = new FileReader(); + r.onload = getLoadHandlerForArrayBuffer(testBinaryData, + testBinaryData.length, + "to-be-reused reading arrayBuffer") + var makeAnotherReadListener4 = function(event) { + r = event.target; + r.removeEventListener("load", makeAnotherReadListener4, false); + r.onload = getLoadHandlerForArrayBuffer(testBinaryData, + testBinaryData.length, + "reused reading arrayBuffer"); + r.readAsArrayBuffer(binaryFile); + }; + r.addEventListener("load", makeAnotherReadListener4, false); + r.readAsArrayBuffer(binaryFile); + expectedTestCount += 2; + + // Test first reading as ArrayBuffer then read as something else + // (BinaryString) and doesn't crash + r = new FileReader(); + r.onload = getLoadHandlerForArrayBuffer(testBinaryData, + testBinaryData.length, + "to-be-reused reading arrayBuffer") + var makeAnotherReadListener5 = function(event) { + r = event.target; + r.removeEventListener("load", makeAnotherReadListener5, false); + r.onload = getLoadHandler(testBinaryData, + testBinaryData.length, + "reused reading binary string"); + r.readAsBinaryString(binaryFile); + }; + r.addEventListener("load", makeAnotherReadListener5, false); + r.readAsArrayBuffer(binaryFile); + expectedTestCount += 2; + + //Test data-URI encoding on differing file sizes + is(dataurldata0.length % 3, 0, "Want to test data with length % 3 == 0"); + r = new FileReader(); + r.onload = getLoadHandler(convertToDataURL(dataurldata0), + dataurldata0.length, + "dataurl reading, %3 = 0"); + r.readAsDataURL(dataUrlFile0); + expectedTestCount++; + + is(dataurldata1.length % 3, 1, "Want to test data with length % 3 == 1"); + r = new FileReader(); + r.onload = getLoadHandler(convertToDataURL(dataurldata1), + dataurldata1.length, + "dataurl reading, %3 = 1"); + r.readAsDataURL(dataUrlFile1); + expectedTestCount++; + + is(dataurldata2.length % 3, 2, "Want to test data with length % 3 == 2"); + r = new FileReader(); + r.onload = getLoadHandler(convertToDataURL(dataurldata2), + dataurldata2.length, + "dataurl reading, %3 = 2"); + r.readAsDataURL(dataUrlFile2), + expectedTestCount++; + + + // Test abort() + var abortHasRun = false; + var loadEndHasRun = false; + r = new FileReader(); + r.onabort = function (event) { + is(abortHasRun, false, "abort should only fire once"); + is(loadEndHasRun, false, "loadend shouldn't have fired yet"); + abortHasRun = true; + is(event.target.readyState, FileReader.DONE, "should be DONE while firing onabort"); + is(event.target.error.name, "AbortError", "error set to AbortError for aborted reads"); + is(event.target.result, null, "file data should be null on aborted reads"); + } + r.onloadend = function (event) { + is(abortHasRun, true, "abort should fire before loadend"); + is(loadEndHasRun, false, "loadend should only fire once"); + loadEndHasRun = true; + is(event.target.readyState, FileReader.DONE, "should be DONE while firing onabort"); + is(event.target.error.name, "AbortError", "error set to AbortError for aborted reads"); + is(event.target.result, null, "file data should be null on aborted reads"); + } + r.onload = function() { ok(false, "load should not fire for aborted reads") }; + r.onerror = function() { ok(false, "error should not fire for aborted reads") }; + r.onprogress = function() { ok(false, "progress should not fire for aborted reads") }; + var abortThrew = false; + try { + r.abort(); + } catch(e) { + abortThrew = true; + } + is(abortThrew, true, "abort() must throw if not loading"); + is(abortHasRun, false, "abort() is a no-op unless loading"); + r.readAsText(asciiFile); + r.abort(); + is(abortHasRun, true, "abort should fire sync"); + is(loadEndHasRun, true, "loadend should fire sync"); + + // Test calling readAsX to cause abort() + var reuseAbortHasRun = false; + r = new FileReader(); + r.onabort = function (event) { + is(reuseAbortHasRun, false, "abort should only fire once"); + reuseAbortHasRun = true; + is(event.target.readyState, FileReader.DONE, "should be DONE while firing onabort"); + is(event.target.error.name, "AbortError", "error set to AbortError for aborted reads"); + is(event.target.result, null, "file data should be null on aborted reads"); + } + r.onload = function() { ok(false, "load should not fire for aborted reads") }; + var abortThrew = false; + try { + r.abort(); + } catch(e) { + abortThrew = true; + } + is(abortThrew, true, "abort() must throw if not loading"); + is(reuseAbortHasRun, false, "abort() is a no-op unless loading"); + r.readAsText(asciiFile); + r.readAsText(asciiFile); + is(reuseAbortHasRun, true, "abort should fire sync"); + r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "reuse-as-abort reading"); + expectedTestCount++; + + + // Test reading from nonexistent files + r = new FileReader(); + var didThrow = false; + r.onerror = function (event) { + is(event.target.readyState, FileReader.DONE, "should be DONE while firing onerror"); + is(event.target.error.name, "NotFoundError", "error set to NotFoundError for nonexistent files"); + is(event.target.result, null, "file data should be null on aborted reads"); + testHasRun(); + }; + r.onload = function (event) { + is(false, "nonexistent file shouldn't load! (FIXME: bug 1122788)"); + testHasRun(); + }; + try { + r.readAsDataURL(nonExistingFile); + expectedTestCount++; + } catch(ex) { + didThrow = true; + } + // Once this test passes, we should test that onerror gets called and + // that the FileReader object is in the right state during that call. + is(didThrow, false, "shouldn't throw when opening nonexistent file, should fire error instead"); + + + function getLoadHandler(expectedResult, expectedLength, testName) { + return function (event) { + is(event.target.readyState, FileReader.DONE, + "readyState in test " + testName); + is(event.target.error, null, + "no error in test " + testName); + is(event.target.result, expectedResult, + "result in test " + testName); + is(event.lengthComputable, true, + "lengthComputable in test " + testName); + is(event.loaded, expectedLength, + "loaded in test " + testName); + is(event.total, expectedLength, + "total in test " + testName); + testHasRun(); + } + } + + function getLoadHandlerForArrayBuffer(expectedResult, expectedLength, testName) { + return function (event) { + is(event.target.readyState, FileReader.DONE, + "readyState in test " + testName); + is(event.target.error, null, + "no error in test " + testName); + is(event.lengthComputable, true, + "lengthComputable in test " + testName); + is(event.loaded, expectedLength, + "loaded in test " + testName); + is(event.total, expectedLength, + "total in test " + testName); + is(event.target.result.byteLength, expectedLength, + "array buffer size in test " + testName); + var u8v = new Uint8Array(event.target.result); + is(String.fromCharCode.apply(String, u8v), expectedResult, + "array buffer contents in test " + testName); + u8v = null; + is(event.target.result.byteLength, expectedLength, + "array buffer size after gc in test " + testName); + u8v = new Uint8Array(event.target.result); + is(String.fromCharCode.apply(String, u8v), expectedResult, + "array buffer contents after gc in test " + testName); + testHasRun(); + } + } + + function testHasRun() { + //alert(testRanCounter); + ++testRanCounter; + if (testRanCounter == expectedTestCount) { + is(testSetupFinished, true, "test setup should have finished; check for exceptions"); + is(onloadHasRunText, true, "onload text should have fired by now"); + is(onloadHasRunBinary, true, "onload binary should have fired by now"); + finish(); + } + } + + testSetupFinished = true; +} diff --git a/dom/workers/test/worker_referrer.js b/dom/workers/test/worker_referrer.js new file mode 100644 index 000000000..227a77caf --- /dev/null +++ b/dom/workers/test/worker_referrer.js @@ -0,0 +1,9 @@ +onmessage = function() { + importScripts(['referrer.sjs?import']); + var xhr = new XMLHttpRequest(); + xhr.open('GET', 'referrer.sjs?result', true); + xhr.onload = function() { + postMessage(xhr.responseText); + } + xhr.send(); +} diff --git a/dom/workers/test/worker_setTimeoutWith0.js b/dom/workers/test/worker_setTimeoutWith0.js new file mode 100644 index 000000000..2d8e5e6c2 --- /dev/null +++ b/dom/workers/test/worker_setTimeoutWith0.js @@ -0,0 +1,3 @@ +var x = 0; +setTimeout("x++; '\x00'; x++;"); +setTimeout("postMessage(x);"); diff --git a/dom/workers/test/worker_suspended.js b/dom/workers/test/worker_suspended.js new file mode 100644 index 000000000..3b53fcea2 --- /dev/null +++ b/dom/workers/test/worker_suspended.js @@ -0,0 +1,31 @@ +var count = 0; + +function do_magic(data) { + caches.open("test") + .then(function(cache) { + return cache.put("http://mochi.test:888/foo", new Response(data.type + "-" + count++)); + }) + .then(function() { + if (count == 1) { + postMessage("ready"); + } + + if (data.loop) { + setTimeout(function() {do_magic(data); }, 500); + } + }); +} + +onmessage = function(e) { + if (e.data.type == 'page1') { + if (e.data.count > 0) { + var a = new Worker("worker_suspended.js"); + a.postMessage({ type: "page1", count: e.data - 1 }); + a.onmessage = function() { postMessage("ready"); } + } else { + do_magic({ type: e.data.type, loop: true }); + } + } else if (e.data.type == 'page2') { + do_magic({ type: e.data.type, loop: false }); + } +} diff --git a/dom/workers/test/worker_wrapper.js b/dom/workers/test/worker_wrapper.js new file mode 100644 index 000000000..c385803c4 --- /dev/null +++ b/dom/workers/test/worker_wrapper.js @@ -0,0 +1,99 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/publicdomain/zero/1.0/ +// +// Worker-side wrapper script for the worker_driver.js helper code. See +// the comments at the top of worker_driver.js for more information. + +function ok(a, msg) { + dump("OK: " + !!a + " => " + a + ": " + msg + "\n"); + postMessage({type: 'status', status: !!a, msg: a + ": " + msg }); +} + +function is(a, b, msg) { + dump("IS: " + (a===b) + " => " + a + " | " + b + ": " + msg + "\n"); + 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() { + postMessage({ type: 'finish' }); +} + +function workerTestGetPermissions(permissions, cb) { + addEventListener('message', function workerTestGetPermissionsCB(e) { + if (e.data.type != 'returnPermissions' || + !workerTestArrayEquals(permissions, e.data.permissions)) { + return; + } + removeEventListener('message', workerTestGetPermissionsCB); + cb(e.data.result); + }); + postMessage({ + type: 'getPermissions', + permissions: permissions + }); +} + +function workerTestGetVersion(cb) { + addEventListener('message', function workerTestGetVersionCB(e) { + if (e.data.type !== 'returnVersion') { + return; + } + removeEventListener('message', workerTestGetVersionCB); + cb(e.data.result); + }); + postMessage({ + type: 'getVersion' + }); +} + +function workerTestGetUserAgent(cb) { + addEventListener('message', function workerTestGetUserAgentCB(e) { + if (e.data.type !== 'returnUserAgent') { + return; + } + removeEventListener('message', workerTestGetUserAgentCB); + cb(e.data.result); + }); + postMessage({ + type: 'getUserAgent' + }); +} + +function workerTestGetOSCPU(cb) { + addEventListener('message', function workerTestGetOSCPUCB(e) { + if (e.data.type !== 'returnOSCPU') { + return; + } + removeEventListener('message', workerTestGetOSCPUCB); + cb(e.data.result); + }); + postMessage({ + type: 'getOSCPU' + }); +} + +addEventListener('message', function workerWrapperOnMessage(e) { + removeEventListener('message', workerWrapperOnMessage); + var data = e.data; + try { + importScripts(data.script); + } catch(e) { + postMessage({ + type: 'status', + status: false, + msg: 'worker failed to import ' + data.script + "; error: " + e.message + }); + } +}); diff --git a/dom/workers/test/workersDisabled_worker.js b/dom/workers/test/workersDisabled_worker.js new file mode 100644 index 000000000..7346fc142 --- /dev/null +++ b/dom/workers/test/workersDisabled_worker.js @@ -0,0 +1,7 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function(event) { + postMessage(event.data); +} diff --git a/dom/workers/test/xpcshell/data/chrome.manifest b/dom/workers/test/xpcshell/data/chrome.manifest new file mode 100644 index 000000000..611e81fd4 --- /dev/null +++ b/dom/workers/test/xpcshell/data/chrome.manifest @@ -0,0 +1 @@ +content workers ./ diff --git a/dom/workers/test/xpcshell/data/worker.js b/dom/workers/test/xpcshell/data/worker.js new file mode 100644 index 000000000..9a83bb128 --- /dev/null +++ b/dom/workers/test/xpcshell/data/worker.js @@ -0,0 +1,7 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + + +self.onmessage = function(msg) { + self.postMessage("OK"); +}; diff --git a/dom/workers/test/xpcshell/data/worker_fileReader.js b/dom/workers/test/xpcshell/data/worker_fileReader.js new file mode 100644 index 000000000..b27366d17 --- /dev/null +++ b/dom/workers/test/xpcshell/data/worker_fileReader.js @@ -0,0 +1,8 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + + +self.onmessage = function(msg) { + var fr = new FileReader(); + self.postMessage("OK"); +}; diff --git a/dom/workers/test/xpcshell/test_fileReader.js b/dom/workers/test/xpcshell/test_fileReader.js new file mode 100644 index 000000000..0e283fbe0 --- /dev/null +++ b/dom/workers/test/xpcshell/test_fileReader.js @@ -0,0 +1,40 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +Components.utils.import("resource://gre/modules/Promise.jsm"); + +// Worker must be loaded from a chrome:// uri, not a file:// +// uri, so we first need to load it. +var WORKER_SOURCE_URI = "chrome://workers/content/worker_fileReader.js"; +do_load_manifest("data/chrome.manifest"); + +function run_test() { + run_next_test(); +} + +function talk_with_worker(worker) { + let deferred = Promise.defer(); + worker.onmessage = function(event) { + let success = true; + if (event.data == "OK") { + deferred.resolve(); + } else { + success = false; + deferred.reject(event); + } + do_check_true(success); + worker.terminate(); + }; + worker.onerror = function(event) { + let error = new Error(event.message, event.filename, event.lineno); + worker.terminate(); + deferred.reject(error); + }; + worker.postMessage("START"); + return deferred.promise; +} + + +add_task(function test_chrome_worker() { + return talk_with_worker(new ChromeWorker(WORKER_SOURCE_URI)); +}); diff --git a/dom/workers/test/xpcshell/test_workers.js b/dom/workers/test/xpcshell/test_workers.js new file mode 100644 index 000000000..c06e62af3 --- /dev/null +++ b/dom/workers/test/xpcshell/test_workers.js @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +Components.utils.import("resource://gre/modules/Promise.jsm"); + +// Worker must be loaded from a chrome:// uri, not a file:// +// uri, so we first need to load it. +var WORKER_SOURCE_URI = "chrome://workers/content/worker.js"; +do_load_manifest("data/chrome.manifest"); + +function run_test() { + run_next_test(); +} + +function talk_with_worker(worker) { + let deferred = Promise.defer(); + worker.onmessage = function(event) { + let success = true; + if (event.data == "OK") { + deferred.resolve(); + } else { + success = false; + deferred.reject(event); + } + do_check_true(success); + worker.terminate(); + }; + worker.onerror = function(event) { + let error = new Error(event.message, event.filename, event.lineno); + worker.terminate(); + deferred.reject(error); + }; + worker.postMessage("START"); + return deferred.promise; +} + + +add_task(function test_chrome_worker() { + return talk_with_worker(new ChromeWorker(WORKER_SOURCE_URI)); +}); + +add_task(function test_worker() { + return talk_with_worker(new Worker(WORKER_SOURCE_URI)); +}); diff --git a/dom/workers/test/xpcshell/xpcshell.ini b/dom/workers/test/xpcshell/xpcshell.ini new file mode 100644 index 000000000..917b842f5 --- /dev/null +++ b/dom/workers/test/xpcshell/xpcshell.ini @@ -0,0 +1,11 @@ +[DEFAULT] +head = +tail = +skip-if = toolkit == 'android' +support-files = + data/worker.js + data/worker_fileReader.js + data/chrome.manifest + +[test_workers.js] +[test_fileReader.js] |