michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // This file tests message ports and semantics of the frameworker which aren't michael@0: // directly related to the sandbox. See also browser_frameworker_sandbox.js. michael@0: michael@0: function makeWorkerUrl(runner) { michael@0: let prefix = "http://example.com/browser/toolkit/components/social/test/browser/echo.sjs?"; michael@0: if (typeof runner == "function") { michael@0: runner = "var run=" + runner.toSource() + ";run();"; michael@0: } michael@0: return prefix + encodeURI(runner); michael@0: } michael@0: michael@0: var getFrameWorkerHandle; michael@0: function test() { michael@0: waitForExplicitFinish(); michael@0: michael@0: let scope = {}; michael@0: Cu.import("resource://gre/modules/FrameWorker.jsm", scope); michael@0: getFrameWorkerHandle = scope.getFrameWorkerHandle; michael@0: michael@0: runTests(tests); michael@0: } michael@0: michael@0: let tests = { michael@0: testSimple: function(cbnext) { michael@0: let run = function() { michael@0: onconnect = function(e) { michael@0: let port = e.ports[0]; michael@0: port.onmessage = function(e) { michael@0: if (e.data.topic == "ping") { michael@0: port.postMessage({topic: "pong"}); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testSimple"); michael@0: michael@0: worker.port.onmessage = function(e) { michael@0: if (e.data.topic == "pong") { michael@0: worker.terminate(); michael@0: cbnext(); michael@0: } michael@0: } michael@0: worker.port.postMessage({topic: "ping"}) michael@0: }, michael@0: michael@0: // when the client closes early but the worker tries to send anyway... michael@0: // XXX - disabled due to bug 919878 - we close the frameworker before the michael@0: // remote browser has completed initializing, leading to failures. Given michael@0: // this can realistically only happen in this synthesized test environment, michael@0: // disabling just this test seems OK for now. michael@0: /*** michael@0: testEarlyClose: function(cbnext) { michael@0: let run = function() { michael@0: onconnect = function(e) { michael@0: let port = e.ports[0]; michael@0: port.postMessage({topic: "oh hai"}); michael@0: } michael@0: } michael@0: michael@0: let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testEarlyClose"); michael@0: worker.port.close(); michael@0: worker.terminate(); michael@0: cbnext(); michael@0: }, michael@0: ***/ michael@0: michael@0: // Check we do get a social.port-closing message as the port is closed. michael@0: testPortClosingMessage: function(cbnext) { michael@0: // We use 2 ports - we close the first and report success via the second. michael@0: let run = function() { michael@0: let firstPort, secondPort; michael@0: onconnect = function(e) { michael@0: let port = e.ports[0]; michael@0: if (firstPort === undefined) { michael@0: firstPort = port; michael@0: port.onmessage = function(e) { michael@0: if (e.data.topic == "social.port-closing") { michael@0: secondPort.postMessage({topic: "got-closing"}); michael@0: } michael@0: } michael@0: } else { michael@0: secondPort = port; michael@0: // now both ports are connected we can trigger the client side michael@0: // closing the first. michael@0: secondPort.postMessage({topic: "connected"}); michael@0: } michael@0: } michael@0: } michael@0: let workerurl = makeWorkerUrl(run); michael@0: let worker1 = getFrameWorkerHandle(workerurl, undefined, "testPortClosingMessage worker1"); michael@0: let worker2 = getFrameWorkerHandle(workerurl, undefined, "testPortClosingMessage worker2"); michael@0: worker2.port.onmessage = function(e) { michael@0: if (e.data.topic == "connected") { michael@0: // both ports connected, so close the first. michael@0: worker1.port.close(); michael@0: } else if (e.data.topic == "got-closing") { michael@0: worker2.terminate(); michael@0: cbnext(); michael@0: } michael@0: } michael@0: }, michael@0: michael@0: // Tests that prototypes added to core objects work with data sent over michael@0: // the message ports. michael@0: testPrototypes: function(cbnext) { michael@0: let run = function() { michael@0: // Modify the Array prototype... michael@0: Array.prototype.customfunction = function() {}; michael@0: onconnect = function(e) { michael@0: let port = e.ports[0]; michael@0: port.onmessage = function(e) { michael@0: // Check the data we get via the port has the prototype modification michael@0: if (e.data.topic == "hello" && e.data.data.customfunction) { michael@0: port.postMessage({topic: "hello", data: [1,2,3]}); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: // hrmph - this kinda sucks as it is really just testing the actual michael@0: // implementation rather than the end result, but is OK for now. michael@0: // Really we are just testing that JSON.parse in the client window michael@0: // is called. michael@0: let fakeWindow = { michael@0: JSON: { michael@0: parse: function(s) { michael@0: let data = JSON.parse(s); michael@0: data.data.somextrafunction = function() {}; michael@0: return data; michael@0: } michael@0: } michael@0: } michael@0: let worker = getFrameWorkerHandle(makeWorkerUrl(run), fakeWindow, "testPrototypes"); michael@0: worker.port.onmessage = function(e) { michael@0: if (e.data.topic == "hello") { michael@0: ok(e.data.data.somextrafunction, "have someextrafunction") michael@0: worker.terminate(); michael@0: cbnext(); michael@0: } michael@0: } michael@0: worker.port.postMessage({topic: "hello", data: [1,2,3]}); michael@0: }, michael@0: michael@0: testSameOriginImport: function(cbnext) { michael@0: let run = function() { michael@0: onconnect = function(e) { michael@0: let port = e.ports[0]; michael@0: port.onmessage = function(e) { michael@0: if (e.data.topic == "ping") { michael@0: try { michael@0: importScripts("http://mochi.test:8888/error"); michael@0: } catch(ex) { michael@0: port.postMessage({topic: "pong", data: ex}); michael@0: return; michael@0: } michael@0: port.postMessage({topic: "pong", data: null}); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testSameOriginImport"); michael@0: worker.port.onmessage = function(e) { michael@0: if (e.data.topic == "pong") { michael@0: isnot(e.data.data, null, "check same-origin applied to importScripts"); michael@0: worker.terminate(); michael@0: cbnext(); michael@0: } michael@0: } michael@0: worker.port.postMessage({topic: "ping"}) michael@0: }, michael@0: michael@0: testRelativeImport: function(cbnext) { michael@0: let url = "https://example.com/browser/toolkit/components/social/test/browser/worker_relative.js"; michael@0: let worker = getFrameWorkerHandle(url, undefined, "testSameOriginImport"); michael@0: worker.port.onmessage = function(e) { michael@0: if (e.data.topic == "done") { michael@0: is(e.data.result, "ok", "check relative url in importScripts"); michael@0: worker.terminate(); michael@0: cbnext(); michael@0: } michael@0: } michael@0: }, michael@0: michael@0: testNavigator: function(cbnext) { michael@0: let run = function() { michael@0: let port; michael@0: ononline = function() { michael@0: port.postMessage({topic: "ononline", data: navigator.onLine}); michael@0: } michael@0: onoffline = function() { michael@0: port.postMessage({topic: "onoffline", data: navigator.onLine}); michael@0: } michael@0: onconnect = function(e) { michael@0: port = e.ports[0]; michael@0: port.postMessage({topic: "ready", michael@0: data: { michael@0: appName: navigator.appName, michael@0: appVersion: navigator.appVersion, michael@0: platform: navigator.platform, michael@0: userAgent: navigator.userAgent, michael@0: } michael@0: }); michael@0: } michael@0: } michael@0: let ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService2); michael@0: let oldManage = ioService.manageOfflineStatus; michael@0: let oldOffline = ioService.offline; michael@0: michael@0: ioService.manageOfflineStatus = false; michael@0: let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testNavigator"); michael@0: let expected_topic = "onoffline"; michael@0: let expected_data = false; michael@0: worker.port.onmessage = function(e) { michael@0: is(e.data.topic, "ready"); michael@0: for each (let attr in ['appName', 'appVersion', 'platform', 'userAgent']) { michael@0: // each attribute must be a string with length > 0. michael@0: is(typeof e.data.data[attr], "string"); michael@0: ok(e.data.data[attr].length > 0); michael@0: } michael@0: michael@0: worker.port.onmessage = function(e) { michael@0: // a handler specifically for the offline notification. michael@0: is(e.data.topic, "onoffline"); michael@0: is(e.data.data, false); michael@0: michael@0: // add another handler specifically for the 'online' case. michael@0: worker.port.onmessage = function(e) { michael@0: is(e.data.topic, "ononline"); michael@0: is(e.data.data, true); michael@0: // all good! michael@0: ioService.manageOfflineStatus = oldManage; michael@0: ioService.offline = oldOffline; michael@0: worker.terminate(); michael@0: cbnext(); michael@0: } michael@0: ioService.offline = false; michael@0: } michael@0: ioService.offline = true; michael@0: } michael@0: }, michael@0: michael@0: testMissingWorker: function(cbnext) { michael@0: // don't ever create this file! We want a 404. michael@0: let url = "https://example.com/browser/toolkit/components/social/test/browser/worker_is_missing.js"; michael@0: let worker = getFrameWorkerHandle(url, undefined, "testMissingWorker"); michael@0: Services.obs.addObserver(function handleError(subj, topic, data) { michael@0: Services.obs.removeObserver(handleError, "social:frameworker-error"); michael@0: is(data, worker._worker.origin, "social:frameworker-error was handled"); michael@0: worker.terminate(); michael@0: cbnext(); michael@0: }, 'social:frameworker-error', false); michael@0: worker.port.onmessage = function(e) { michael@0: ok(false, "social:frameworker-error was handled"); michael@0: cbnext(); michael@0: } michael@0: }, michael@0: michael@0: testNoConnectWorker: function(cbnext) { michael@0: let worker = getFrameWorkerHandle(makeWorkerUrl(function () {}), michael@0: undefined, "testNoConnectWorker"); michael@0: Services.obs.addObserver(function handleError(subj, topic, data) { michael@0: Services.obs.removeObserver(handleError, "social:frameworker-error"); michael@0: is(data, worker._worker.origin, "social:frameworker-error was handled"); michael@0: worker.terminate(); michael@0: cbnext(); michael@0: }, 'social:frameworker-error', false); michael@0: worker.port.onmessage = function(e) { michael@0: ok(false, "social:frameworker-error was handled"); michael@0: cbnext(); michael@0: } michael@0: }, michael@0: michael@0: testEmptyWorker: function(cbnext) { michael@0: let worker = getFrameWorkerHandle(makeWorkerUrl(''), michael@0: undefined, "testEmptyWorker"); michael@0: Services.obs.addObserver(function handleError(subj, topic, data) { michael@0: Services.obs.removeObserver(handleError, "social:frameworker-error"); michael@0: is(data, worker._worker.origin, "social:frameworker-error was handled"); michael@0: worker.terminate(); michael@0: cbnext(); michael@0: }, 'social:frameworker-error', false); michael@0: worker.port.onmessage = function(e) { michael@0: ok(false, "social:frameworker-error was handled"); michael@0: cbnext(); michael@0: } michael@0: }, michael@0: michael@0: testWorkerConnectError: function(cbnext) { michael@0: let run = function () { michael@0: onconnect = function(e) { michael@0: throw new Error("worker failure"); michael@0: } michael@0: } michael@0: let worker = getFrameWorkerHandle(makeWorkerUrl(run), michael@0: undefined, "testWorkerConnectError"); michael@0: Services.obs.addObserver(function handleError(subj, topic, data) { michael@0: Services.obs.removeObserver(handleError, "social:frameworker-error"); michael@0: is(data, worker._worker.origin, "social:frameworker-error was handled"); michael@0: worker.terminate(); michael@0: cbnext(); michael@0: }, 'social:frameworker-error', false); michael@0: worker.port.onmessage = function(e) { michael@0: ok(false, "social:frameworker-error was handled"); michael@0: cbnext(); michael@0: } michael@0: }, michael@0: michael@0: // This will create the worker, then send a message to the port, then close michael@0: // the port - all before the worker has actually initialized. michael@0: testCloseFirstSend: function(cbnext) { michael@0: let run = function() { michael@0: let numPings = 0, numCloses = 0; michael@0: onconnect = function(e) { michael@0: let port = e.ports[0]; michael@0: port.onmessage = function(e) { michael@0: if (e.data.topic == "ping") { michael@0: numPings += 1; michael@0: } else if (e.data.topic == "social.port-closing") { michael@0: numCloses += 1; michael@0: } else if (e.data.topic == "get-counts") { michael@0: port.postMessage({topic: "result", michael@0: result: {ping: numPings, close: numCloses}}); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testSendAndClose"); michael@0: worker.port.postMessage({topic: "ping"}); michael@0: worker.port.close(); michael@0: let newPort = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testSendAndClose").port; michael@0: newPort.onmessage = function(e) { michael@0: if (e.data.topic == "result") { michael@0: is(e.data.result.ping, 1, "the worker got the ping"); michael@0: is(e.data.result.close, 1, "the worker got 1 close message"); michael@0: worker.terminate(); michael@0: cbnext(); michael@0: } michael@0: } michael@0: newPort.postMessage({topic: "get-counts"}); michael@0: }, michael@0: michael@0: // Like testCloseFirstSend, although in this test the worker has already michael@0: // initialized (so the "connect pending ports" part of the worker isn't michael@0: // what needs to handle this case.) michael@0: testCloseAfterInit: function(cbnext) { michael@0: let run = function() { michael@0: let numPings = 0, numCloses = 0; michael@0: onconnect = function(e) { michael@0: let port = e.ports[0]; michael@0: port.onmessage = function(e) { michael@0: if (e.data.topic == "ping") { michael@0: numPings += 1; michael@0: } else if (e.data.topic == "social.port-closing") { michael@0: numCloses += 1; michael@0: } else if (e.data.topic == "get-counts") { michael@0: port.postMessage({topic: "result", michael@0: result: {ping: numPings, close: numCloses}}); michael@0: } else if (e.data.topic == "get-ready") { michael@0: port.postMessage({topic: "ready"}); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testSendAndClose"); michael@0: worker.port.onmessage = function(e) { michael@0: if (e.data.topic == "ready") { michael@0: let newPort = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testSendAndClose").port; michael@0: newPort.postMessage({topic: "ping"}); michael@0: newPort.close(); michael@0: worker.port.postMessage({topic: "get-counts"}); michael@0: } else if (e.data.topic == "result") { michael@0: is(e.data.result.ping, 1, "the worker got the ping"); michael@0: is(e.data.result.close, 1, "the worker got 1 close message"); michael@0: worker.terminate(); michael@0: cbnext(); michael@0: } michael@0: } michael@0: worker.port.postMessage({topic: "get-ready"}); michael@0: }, michael@0: }