<!DOCTYPE HTML> <html> <head> <title>Test for postMessages cloning/transferring 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> <input id="fileList" type="file"></input> <script type="application/javascript;version=1.7"> function setup_tests() { SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true], ["javascript.options.wasm", true]]}, next); } function getType(a) { if (a === null || a === undefined) return 'null'; if (Array.isArray(a)) return 'array'; if (typeof a == 'object') return 'object'; if (SpecialPowers.Cu.getJSTestingFunctions().wasmIsSupported() && a instanceof WebAssembly.Module) return "wasm"; return 'primitive'; } function compare(a, b) { is (getType(a), getType(b), 'Type matches'); var type = getType(a); if (type == 'array') { is (a.length, b.length, 'Array.length matches'); for (var i = 0; i < a.length; ++i) { compare(a[i], b[i]); } return; } if (type == 'object') { ok (a !== b, 'They should not match'); var aProps = []; for (var p in a) aProps.push(p); var bProps = []; for (var p in b) bProps.push(p); is (aProps.length, bProps.length, 'Props match'); is (aProps.sort().toSource(), bProps.sort().toSource(), 'Props match - using toSource()'); for (var p in a) { compare(a[p], b[p]); } return; } if (type == 'wasm') { var wasmA = new WebAssembly.Instance(a); ok(wasmA instanceof WebAssembly.Instance, "got an instance"); var wasmB = new WebAssembly.Instance(b); ok(wasmB instanceof WebAssembly.Instance, "got an instance"); ok(wasmA.exports.foo() === wasmB.exports.foo(), "Same result!"); ok(wasmB.exports.foo() === 42, "We want 42"); } if (type != 'null') { is (a.toSource(), b.toSource(), 'Matching using toSource()'); } } var clonableObjects = [ { target: 'all', data: 'hello world' }, { target: 'all', data: 123 }, { target: 'all', data: null }, { target: 'all', data: true }, { target: 'all', data: new Date() }, { target: 'all', data: [ 1, 'test', true, new Date() ] }, { target: 'all', data: { a: true, b: null, c: new Date(), d: [ true, false, {} ] } }, { target: 'all', data: new Blob([123], { type: 'plain/text' }) }, { target: 'all', data: new ImageData(2, 2) }, ]; function create_fileList() { var url = SimpleTest.getTestFileURL("script_postmessages_fileList.js"); var script = SpecialPowers.loadChromeScript(url); function onOpened(message) { var fileList = document.getElementById('fileList'); SpecialPowers.wrap(fileList).mozSetFileArray([message.file]); // Just a simple test var domFile = fileList.files[0]; is(domFile.name, "prefs.js", "fileName should be prefs.js"); clonableObjects.push({ target: 'all', data: fileList.files }); script.destroy(); next(); } script.addMessageListener("file.opened", onOpened); script.sendAsyncMessage("file.open"); } function create_directory() { if (navigator.userAgent.toLowerCase().indexOf('Android') != -1) { next(); return; } var url = SimpleTest.getTestFileURL("script_postmessages_fileList.js"); var script = SpecialPowers.loadChromeScript(url); function onOpened(message) { var fileList = document.getElementById('fileList'); SpecialPowers.wrap(fileList).mozSetDirectory(message.dir); fileList.getFilesAndDirectories().then(function(list) { // Just a simple test is(list.length, 1, "This list has 1 element"); ok(list[0] instanceof Directory, "We have a directory."); clonableObjects.push({ target: 'all', data: list[0] }); script.destroy(); next(); }); } script.addMessageListener("dir.opened", onOpened); script.sendAsyncMessage("dir.open"); } function create_wasmModule() { info("Checking if we can play with WebAssembly..."); if (!SpecialPowers.Cu.getJSTestingFunctions().wasmIsSupported()) { next(); return; } ok(WebAssembly, "WebAssembly object should exist"); ok(WebAssembly.compile, "WebAssembly.compile function should exist"); const wasmTextToBinary = SpecialPowers.unwrap(SpecialPowers.Cu.getJSTestingFunctions().wasmTextToBinary); const fooModuleCode = wasmTextToBinary(`(module (func $foo (result i32) (i32.const 42)) (export "foo" $foo) )`, 'new-format'); WebAssembly.compile(fooModuleCode).then((m) => { ok(m instanceof WebAssembly.Module, "The WasmModule has been compiled."); clonableObjects.push({ target: 'sameProcess', data: m }); next(); }, () => { ok(false, "The compilation of the wasmModule failed."); }); } function runTests(obj) { ok(('clonableObjectsEveryWhere' in obj) && ('clonableObjectsSameProcess' in obj) && ('transferableObjects' in obj) && (obj.clonableObjectsEveryWhere || obj.clonableObjectsSameProcess || obj.transferableObjects), "We must run some test!"); // cloning tests - everyWhere new Promise(function(resolve, reject) { if (!obj.clonableObjectsEveryWhere) { resolve(); return; } var clonableObjectsId = 0; function runClonableTest() { if (clonableObjectsId >= clonableObjects.length) { resolve(); return; } var object = clonableObjects[clonableObjectsId++]; if (object.target != 'all') { runClonableTest(); return; } obj.send(object.data, []).then(function(received) { compare(received.data, object.data); runClonableTest(); }); } runClonableTest(); }) // clonable same process .then(function() { return new Promise(function(resolve, reject) { if (!obj.clonableObjectsSameProcess) { resolve(); return; } var clonableObjectsId = 0; function runClonableTest() { if (clonableObjectsId >= clonableObjects.length) { resolve(); return; } var object = clonableObjects[clonableObjectsId++]; if (object.target != 'sameProcess') { runClonableTest(); return; } obj.send(object.data, []).then(function(received) { compare(received.data, object.data); runClonableTest(); }); } runClonableTest(); }); }) // transfering tests .then(function() { if (!obj.transferableObjects) { return; } // MessagePort return new Promise(function(r, rr) { var mc = new MessageChannel(); obj.send(42, [mc.port1]).then(function(received) { ok(received.ports.length, 1, "MessagePort has been transferred"); mc.port2.postMessage("hello world"); received.ports[0].onmessage = function(e) { ok(e.data, "hello world", "Ports are connected!"); r(); } }); }); }) // no dup transfering .then(function() { if (!obj.transferableObjects) { return; } // MessagePort return new Promise(function(r, rr) { var mc = new MessageChannel(); obj.send(42, [mc.port1, mc.port1]).then(function(received) { ok(false, "Duplicate ports should throw!"); }, function() { ok(true, "Duplicate ports should throw!"); }) .then(r); }); }) // non transfering tests .then(function() { if (obj.transferableObjects) { return; } // MessagePort return new Promise(function(r, rr) { var mc = new MessageChannel(); obj.send(42, [mc.port1]).then(function(received) { ok(false, "This object should not support port transferring"); }, function() { ok(true, "This object should not support port transferring"); }) .then(r); }); }) // done. .then(function() { obj.finished(); }); } // PostMessage to the same window. function test_windowToWindow() { info("Testing window to window"); var resolve; onmessage = function(e) { if (!resolve) { ok(false, "Unexpected message!"); return; } let tmp = resolve; resolve = null; tmp({ data: e.data, ports: e.ports }); } runTests({ clonableObjectsEveryWhere: true, clonableObjectsSameProcess: true, transferableObjects: true, send: function(what, ports) { return new Promise(function(r, rr) { resolve = r; try { postMessage(what, '*', ports); } catch(e) { resolve = null; rr(); } }); }, finished: function() { onmessage = null; next(); } }); } // PostMessage to iframe function test_windowToIframe() { info("Testing window to iframe"); test_windowToIframeURL('iframe_postMessages.html'); } // PostMessage to cross-origin iframe function test_windowToCrossOriginIframe() { info("Testing window to cross-origin iframe"); test_windowToIframeURL('http://example.com/tests/dom/base/test/iframe_postMessages.html'); } // iframe helper class function test_windowToIframeURL(url) { var resolve; onmessage = function(e) { if (!resolve) { ok(false, "Unexpected message!"); return; } let tmp = resolve; resolve = null; tmp({ data: e.data, ports: e.ports }); } var ifr = document.createElement('iframe'); ifr.src = url; ifr.onload = function() { runTests({ clonableObjectsEveryWhere: true, clonableObjectsSameProcess: true, transferableObjects: true, send: function(what, ports) { return new Promise(function(r, rr) { resolve = r; try { ifr.contentWindow.postMessage(what, '*', ports); } catch(e) { resolve = null; rr(); } }); }, finished: function() { document.body.removeChild(ifr); onmessage = null; next(); } }); } document.body.appendChild(ifr); } // PostMessage for Workers function test_workers() { info("Testing Workers"); var resolve; var w = new Worker('worker_postMessages.js'); w.postMessage('workers'); w.onmessage = function(e) { is(e.data, 'ok', "Worker ready!"); w.onmessage = function(e) { if (!resolve) { ok(false, "Unexpected message!"); return; } let tmp = resolve; resolve = null; tmp({ data: e.data, ports: e.ports }); } runTests({ clonableObjectsEveryWhere: true, clonableObjectsSameProcess: true, transferableObjects: true, send: function(what, ports) { return new Promise(function(r, rr) { resolve = r; try { w.postMessage(what, ports); } catch(e) { resolve = null; rr(); } }); }, finished: function() { onmessage = null; next(); } }); } } // PostMessage for BroadcastChannel function test_broadcastChannel() { info("Testing broadcastChannel"); var bc1 = new BroadcastChannel('postMessagesTest'); var bc2 = new BroadcastChannel('postMessagesTest'); var resolve; bc2.onmessage = function(e) { if (!resolve) { ok(false, "Unexpected message!"); return; } let tmp = resolve; resolve = null; tmp({ data: e.data, ports: [] }); } runTests({ clonableObjectsEveryWhere: true, clonableObjectsSameProcess: false, transferableObjects: false, send: function(what, ports) { return new Promise(function(r, rr) { if (ports.length) { rr(); return; } resolve = r; bc1.postMessage(what); }); }, finished: function() { onmessage = null; next(); } }); } // PostMessage for BroadcastChannel in workers function test_broadcastChannel_inWorkers() { info("Testing broadcastChannel in Workers"); var bc = new BroadcastChannel('postMessagesTest_inWorkers'); var resolve; var w = new Worker('worker_postMessages.js'); w.postMessage('broadcastChannel'); w.onmessage = function(e) { is(e.data, 'ok', "Worker ready!"); w.onmessage = function(e) { if (!resolve) { ok(false, "Unexpected message!"); return; } let tmp = resolve; resolve = null; tmp({ data: e.data, ports: e.ports }); } runTests({ clonableObjectsEveryWhere: true, clonableObjectsSameProcess: false, transferableObjects: false, send: function(what, ports) { return new Promise(function(r, rr) { if (ports.length) { rr(); return; } resolve = r; bc.postMessage(what); }); }, finished: function() { onmessage = null; next(); } }); } } // PostMessage for MessagePort function test_messagePort() { info("Testing messagePort"); var mc = new MessageChannel(); var resolve; mc.port2.onmessage = function(e) { if (!resolve) { ok(false, "Unexpected message!"); return; } let tmp = resolve; resolve = null; tmp({ data: e.data, ports: e.ports }); } runTests({ clonableObjectsEveryWhere: true, clonableObjectsSameProcess: false, transferableObjects: true, send: function(what, ports) { return new Promise(function(r, rr) { resolve = r; try { mc.port1.postMessage(what, ports); } catch(e) { resolve = null; rr(); } }); }, finished: function() { onmessage = null; next(); } }); } // PostMessage for MessagePort in Workers function test_messagePort_inWorkers() { info("Testing messagePort in workers"); var mc = new MessageChannel(); var resolve; var w = new Worker('worker_postMessages.js'); w.postMessage('messagePort', [ mc.port2 ]); w.onmessage = function(e) { is(e.data, 'ok', "Worker ready!"); w.onmessage = function(e) { if (!resolve) { ok(false, "Unexpected message!"); return; } let tmp = resolve; resolve = null; tmp({ data: e.data, ports: e.ports }); } runTests({ clonableObjectsEveryWhere: true, clonableObjectsSameProcess: false, transferableObjects: true, send: function(what, ports) { return new Promise(function(r, rr) { resolve = r; try { mc.port1.postMessage(what, ports); } catch(e) { resolve = null; rr(); } }); }, finished: function() { onmessage = null; next(); } }); } } var tests = [ setup_tests, create_fileList, create_directory, create_wasmModule, test_windowToWindow, test_windowToIframe, test_windowToCrossOriginIframe, test_workers, test_broadcastChannel, test_broadcastChannel_inWorkers, test_messagePort, test_messagePort_inWorkers, ]; function next() { if (!tests.length) { SimpleTest.finish(); return; } var test = tests.shift(); test(); } SimpleTest.waitForExplicitFinish(); next(); </script> </body> </html>