diff options
Diffstat (limited to 'dom/tests/mochitest/whatwg')
41 files changed, 4083 insertions, 0 deletions
diff --git a/dom/tests/mochitest/whatwg/chrome.toml b/dom/tests/mochitest/whatwg/chrome.toml new file mode 100644 index 0000000000..9ec68ac282 --- /dev/null +++ b/dom/tests/mochitest/whatwg/chrome.toml @@ -0,0 +1,3 @@ +[DEFAULT] + +["test_postMessage_chrome.html"] diff --git a/dom/tests/mochitest/whatwg/file_bug500328_1.html b/dom/tests/mochitest/whatwg/file_bug500328_1.html new file mode 100644 index 0000000000..941cb47c55 --- /dev/null +++ b/dom/tests/mochitest/whatwg/file_bug500328_1.html @@ -0,0 +1,51 @@ +<!DOCTYPE HTML> +<html> +<!-- +Inner frame for testing bug 500328. +https://bugzilla.mozilla.org/show_bug.cgi?id=500328 +--> +<head> +<title>test 1</title> + <style> + a { fill:blue; } + a:visited { fill:purple; } + </style> +</head> +<body onload="load(event);" onpopstate="popstate(event);" onpageshow="pageshow(event);"> +<script type="application/javascript"> + if (parent && parent.onChildScript) + parent.onChildScript(history.state); + if (opener && opener.onChildScript) + opener.onChildScript(history.state); + + function load(e) { + if(parent && parent.onChildLoad) + parent.onChildLoad(e); + if(opener && opener.onChildLoad) + opener.onChildLoad(e); + } + + function pageshow(e) { + if(parent && parent.onChildPageShow) + parent.onChildPageShow(e); + if(opener && opener.onChildPageShow) + opener.onChildPageShow(e); + } + + function popstate(e) { + if(parent && parent.onChildLoad) + parent.onChildPopState(e); + if(opener && opener.onChildLoad) + opener.onChildPopState(e); + } + + function navigateTo(loc) { + location = loc; + } + +</script> + +<a id="link-anchor1", href="#1">Link Anchor1</a> +<a id="link-self" href="file_bug500328_1.html">Self</a> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/file_bug500328_2.html b/dom/tests/mochitest/whatwg/file_bug500328_2.html new file mode 100644 index 0000000000..3ce66493dd --- /dev/null +++ b/dom/tests/mochitest/whatwg/file_bug500328_2.html @@ -0,0 +1,17 @@ +<!DOCTYPE HTML> +<html> +<!-- +Inner frame for testing bug 500328. +https://bugzilla.mozilla.org/show_bug.cgi?id=500328 +--> +<head> +</head> +<body> +<!-- + Yes, this page is basically blank. But no, about:blank wouldn't do as a + replacement, because we use this page to test that pushstate has correct + same-origin checks. +--> +file_bug500328_2.html +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/file_structuredCloneAndExposed.sjs b/dom/tests/mochitest/whatwg/file_structuredCloneAndExposed.sjs new file mode 100644 index 0000000000..e352078e2a --- /dev/null +++ b/dom/tests/mochitest/whatwg/file_structuredCloneAndExposed.sjs @@ -0,0 +1,22 @@ +"use strict"; + +function handleRequest(request, response) { + let query = new URLSearchParams(request.queryString); + + response.setHeader("Content-Type", "application/javascript"); + + let content = `function installListeners(input, target) { + input.addEventListener("message", () => { + target.postMessage(true, { targetOrigin: "*" }); + }); + input.addEventListener("messageerror", () => { + target.postMessage(false, { targetOrigin: "*" }); + }); + target.postMessage("Inited", { targetOrigin: "*" }); + } + + ${query.get("additionalScript")} + `; + + response.write(content); +} diff --git a/dom/tests/mochitest/whatwg/mochitest.toml b/dom/tests/mochitest/whatwg/mochitest.toml new file mode 100644 index 0000000000..fd20629a9c --- /dev/null +++ b/dom/tests/mochitest/whatwg/mochitest.toml @@ -0,0 +1,122 @@ +[DEFAULT] +support-files = [ + "postMessage_chrome_helper.html", + "postMessage_closed_helper.html", + "postMessage_hash.html", + "postMessage_helper.html", + "postMessage_idn_helper.html", + "postMessage_joined_helper2.html", + "postMessage_joined_helper.html", + "postMessage_onOther.html", + "postMessage_origin_helper.xhtml", + "postMessage_override_helper.html", + "postMessage_structured_clone_helper.html", + "postMessage_structured_clone_helper.js", + "postMessage_throw_helper.html", + "postMessage_transfer_helper.html", + "postMessage_userpass_helper.html", +] + +["test_MessageEvent.html"] + +["test_MessageEvent_dispatchToOther.html"] +skip-if = [ + "http3", + "http2", +] + +["test_bug477323.html"] + +["test_bug500328.html"] +skip-if = ["true"] # bug 696306 +support-files = [ + "file_bug500328_1.html", + "file_bug500328_2.html", +] + +["test_document_scripts.html"] + +["test_postMessage.html"] +skip-if = [ + "http3", + "http2", +] + +["test_postMessage_basehref.html"] +skip-if = [ + "http3", + "http2", +] + +["test_postMessage_closed.html"] +skip-if = [ + "os == 'android'", #bug 894914 - wrong data - got FAIL, expected message + "http3", + "http2", +] + +["test_postMessage_hash.html"] +skip-if = [ + "http3", + "http2", +] + +["test_postMessage_idn.xhtml"] +skip-if = [ + "http3", + "http2", +] + +["test_postMessage_joined.html"] +skip-if = [ + "http3", + "http2", +] + +["test_postMessage_onOther.html"] +skip-if = [ + "http3", + "http2", +] + +["test_postMessage_origin.xhtml"] +skip-if = [ + "http3", + "http2", +] + +["test_postMessage_override.html"] +skip-if = [ + "http3", + "http2", +] + +["test_postMessage_special.xhtml"] +skip-if = [ + "http3", + "http2", +] + +["test_postMessage_structured_clone.html"] +skip-if = [ + "http3", + "http2", +] + +["test_postMessage_throw.html"] + +["test_postMessage_transfer.html"] +skip-if = [ + "http3", + "http2", +] + +["test_postMessage_userpass.html"] +skip-if = [ + "http3", + "http2", +] + +["test_structuredCloneAndExposed.html"] +scheme = "https" +support-files = ["file_structuredCloneAndExposed.sjs"] diff --git a/dom/tests/mochitest/whatwg/postMessage_chrome_helper.html b/dom/tests/mochitest/whatwg/postMessage_chrome_helper.html new file mode 100644 index 0000000000..eea07f538c --- /dev/null +++ b/dom/tests/mochitest/whatwg/postMessage_chrome_helper.html @@ -0,0 +1,49 @@ +<!DOCTYPE html> +<html> +<head> + <title>postMessage chrome message receiver</title> + <script type="application/javascript"> + var gPrePath = ""; + + function receiveMessage(evt) + { + if (evt.data.substring(0,9) == "chrome://") { + gPrePath = evt.data; + respond("path-is-set"); + } else { + // Content cannot post to chrome without privileges + try { + window.parent.postMessage("SHOULD NOT GET THIS!", "*"); + } + catch (ex) { + } + + var msg = "post-to-content-response"; + + if (evt.source !== null) + msg += " wrong-source(" + evt.source + ")"; + if (!evt.isTrusted) + msg += " unexpected-untrusted-event"; + if (evt.type !== "message") + msg += " wrong-type(" + evt.type + ")"; + if (evt.origin !== gPrePath) + msg += " wrong-origin(" + evt.origin + ")"; + if (evt.data !== "post-to-content") + msg += " wrong-message(" + evt.data + ")"; + + respond(msg); + } + } + + function respond(msg) + { + SpecialPowers.wrap(window).parent.postMessage(msg, "*"); + } + + window.addEventListener("message", receiveMessage); + </script> +</head> +<body> +<h1 id="domain">example.org</h1> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/postMessage_closed_helper.html b/dom/tests/mochitest/whatwg/postMessage_closed_helper.html new file mode 100644 index 0000000000..984ed08e71 --- /dev/null +++ b/dom/tests/mochitest/whatwg/postMessage_closed_helper.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <title>postMessage closed page</title> + <script type="application/javascript"> +function receiveMessage(evt) +{ + evt.source.postMessage("FAIL", "*"); +} + +window.addEventListener("message", receiveMessage); + +function setup() +{ + var query = location.search.substring(1); + + if (query == "opener") + window.opener.postMessage("message", "http://mochi.test:8888"); +} + +window.addEventListener("load", setup); + </script> +</head> +<body> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/postMessage_hash.html b/dom/tests/mochitest/whatwg/postMessage_hash.html new file mode 100644 index 0000000000..a7225153be --- /dev/null +++ b/dom/tests/mochitest/whatwg/postMessage_hash.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<html> +<head> + <title>Hashed kid for test_postMessage_hash.html</title> + <script type="application/javascript"> +function receiveMessage(evt) +{ + var response = "response-message"; + + if (window.location.href !== "http://mochi.test:8888/tests/dom/tests/mochitest/whatwg/postMessage_hash.html#hash") + response += " kid-at-wrong-uri(" + window.location.href + ")"; + + if (evt.origin !== "http://mochi.test:8888") + response += " wrong-origin(" + evt.origin + ")"; + if (evt.source !== window.parent) + response += " wrong-source"; + if (evt.data !== "from-parent") + response += " wrong-data(" + evt.data + ")"; + if (evt.lastEventId !== "") + response += " wrong-lastEventId(" + evt.lastEventId + ")"; + + window.parent.postMessage(response, "http://mochi.test:8888"); +} + +window.addEventListener("message", receiveMessage); + </script> +</head> +<body> +<p>Kid iframe</p> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/postMessage_helper.html b/dom/tests/mochitest/whatwg/postMessage_helper.html new file mode 100644 index 0000000000..2d01214707 --- /dev/null +++ b/dom/tests/mochitest/whatwg/postMessage_helper.html @@ -0,0 +1,102 @@ +<!DOCTYPE html> +<html> +<head> + <title>postMessage message receiver</title> + <script type="application/javascript"> + function $(id) { return document.getElementById(id); } + + function setup() + { + var target = $("domain"); + target.textContent = location.hostname + ":" + (location.port || 80); + } + + function receiveMessage(evt) + { + var response = evt.data + "-response"; + + if (evt.lastEventId !== "") + response += " wrong-lastEventId(" + evt.lastEventId + ")"; + + if (evt.source !== window.parent) + { + response += " unexpected-source(" + evt.source + ")"; + response += " window-parent-is(" + window.parent + ")"; + response += " location(" + window.location.href + ")"; + } + + if (evt.type != "message") + response += " wrong-type(" + evt.type + ")"; + + var data = evt.data; + if (data == "post-to-other-same-domain") + { + receiveSame(evt, response); + } + else if (data == "post-to-other-cross-domain") + { + receiveCross(evt, response); + } + else + { + response += " unexpected-message-to(" + window.location.href + ")"; + window.parent.postMessage(response, "http://mochi.test:8888"); + } + } + + function receiveSame(evt, response) + { + var source = evt.source; + try + { + if (evt.origin != "http://mochi.test:8888") + response += " unexpected-origin(" + evt.origin + ")"; + + try + { + var threw = false; + var privateVariable = source.privateVariable; + } + catch (e) + { + threw = true; + } + if (threw || privateVariable !== window.parent.privateVariable) + response += " accessed-source!!!"; + + } + finally + { + source.postMessage(response, evt.origin); + } + } + + function receiveCross(evt, response) + { + var source = evt.source; + if (evt.origin != "http://mochi.test:8888") + response += " unexpected-origin(" + evt.origin + ")"; + + try + { + var threw = false; + var privateVariable = source.privateVariable; + } + catch (e) + { + threw = true; + } + if (!threw || privateVariable !== undefined) + response += " accessed-source!!!"; + + source.postMessage(response, evt.origin); + } + + window.addEventListener("load", setup); + window.addEventListener("message", receiveMessage); + </script> +</head> +<body> +<h1 id="domain"></h1> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/postMessage_idn_helper.html b/dom/tests/mochitest/whatwg/postMessage_idn_helper.html new file mode 100644 index 0000000000..b401fd9933 --- /dev/null +++ b/dom/tests/mochitest/whatwg/postMessage_idn_helper.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<html> +<head> + <title>postMessage IDN test page</title> + <script type="application/javascript"> + function receiveMessage(evt) + { + var response = "idn-response"; + + if (!(evt instanceof MessageEvent)) + response += " not-a-MessageEvent"; + if (evt.origin !== "http://mochi.test:8888") + response += " wrong-sender-origin(" + evt.origin + ")"; + if (evt.data !== "idn-message") + response += " wrong-data(" + evt.data + ")"; + if (evt.lastEventId !== "") + response += " wrong-lastEventId(" + evt.lastEventId + ")"; + if (evt.source !== window.parent) + response += " wrong-source"; + if (evt.target !== window) + response += " wrong-target"; + if (evt.type !== "message") + response += " wrong-type(" + evt.type + ")"; + + evt.source.postMessage(response, evt.origin); + } + window.addEventListener("message", receiveMessage); + + function setup() + { + var target = document.getElementById("location"); + target.textContent = location.hostname + ":" + (location.port || 80); + } + + window.addEventListener("load", setup); + </script> +</head> +<body> +<h1 id="location">No location!</h1> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/postMessage_joined_helper.html b/dom/tests/mochitest/whatwg/postMessage_joined_helper.html new file mode 100644 index 0000000000..aa40909017 --- /dev/null +++ b/dom/tests/mochitest/whatwg/postMessage_joined_helper.html @@ -0,0 +1,85 @@ +<!DOCTYPE html> +<html> +<!-- +http://sub1.test1.example.org/tests/dom/tests/mochitest/whatwg/postMessage_joined_helper.html +--> +<head> + <title>postMessage joined domains, inner frame</title> + <script type="application/javascript"> + function receiveMessage(evt) + { + var response, target, providedOrigin; + var data = evt.data; + if (data === "subframe-test-finished") + { + target = window.parent; + providedOrigin = "http://mochi.test:8888"; + response = "test-passed"; + } + else if (data === "start-test") + { + target = window.frames.innermost; + providedOrigin = "http://example.org"; + response = "start-test"; + } + else + { + target = window.parent; + providedOrigin = "http://mochi.test:8888"; + response = "not reached"; + } + + if (evt.lastEventId !== "") + response += " wrong-lastEventId(" + evt.lastEventId + ")"; + + if (evt.type !== "message") + response += " wrong-type(" + evt.type + ")"; + + if (evt.target !== window) + { + response += " wrong-target(" + evt.target + ")"; + response += " location(" + window.location.href + ")"; + } + + var origin; + if (data == "subframe-test-finished") + origin = "http://example.org"; + else if (data === "start-test") + origin = "http://mochi.test:8888"; + else + origin = "unreached"; + + if (evt.origin !== origin) + { + response += " wrong-origin(" + evt.origin + ")"; + response += " location(" + window.location.href + ")"; + } + + target.postMessage(response, providedOrigin); + } + + function setup() + { + var oldDomain = document.domain; + var newDomain = "example.org"; + + document.domain = newDomain; + + var target = document.getElementById("location"); + target.textContent = "Location: " + oldDomain + + ", effective domain: " + newDomain; + + window.addEventListener("message", receiveMessage); + } + + window.addEventListener("load", setup); + </script> +</head> +<body> +<p id="location">No location!</p> + +<iframe src="http://example.org/tests/dom/tests/mochitest/whatwg/postMessage_joined_helper2.html" + name="innermost"></iframe> + +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/postMessage_joined_helper2.html b/dom/tests/mochitest/whatwg/postMessage_joined_helper2.html new file mode 100644 index 0000000000..0f6c063cc4 --- /dev/null +++ b/dom/tests/mochitest/whatwg/postMessage_joined_helper2.html @@ -0,0 +1,71 @@ +<!DOCTYPE html> +<html> +<!-- +http://example.org/tests/dom/tests/mochitest/whatwg/postMessage_joined_helper2.html +--> +<head> + <title>postMessage joined domains, innermost frame</title> + <script type="application/javascript"> + function receiveMessage(evt) + { + var response = "subframe-test-finished"; + + if (evt.origin !== "http://sub1.test1.example.org") + { + response += " wrong-origin(" + evt.origin + ")"; + response += " location(" + window.location.href + ")"; + } + + if (evt.data !== "start-test") + response += " incorrect-subframe-data(" + evt.data + ")"; + if (evt.type !== "message") + response += " wrong-type(" + evt.type + ")"; + if (evt.target !== window) + { + response += " wrong-target(" + evt.target + ")"; + response += " location(" + window.location.href + ")"; + } + + if (evt.source !== window.parent) + { + response += " unexpected-source(" + evt.source + ")"; + response += " window-parent-is(" + window.parent + ")"; + response += " location(" + window.location.href + ")"; + } + + // verify that document.domain was actually joined with this domain + try + { + var passed = evt.source.document.domain === document.domain; + } + catch (e) + { + } + + if (!passed) + response += " expected-joined-domains"; + + window.parent.postMessage(response, "http://sub1.test1.example.org"); + } + + function setup() + { + var oldDomain = document.domain; + var newDomain = "example.org"; // join with parent + + document.domain = newDomain; + + var target = document.getElementById("location"); + target.textContent = "Location: " + oldDomain + + ", effective domain: " + newDomain; + + window.addEventListener("message", receiveMessage); + } + + window.addEventListener("load", setup); + </script> +</head> +<body> +<p id="location">No location!</p> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/postMessage_onOther.html b/dom/tests/mochitest/whatwg/postMessage_onOther.html new file mode 100644 index 0000000000..9d60d9ef24 --- /dev/null +++ b/dom/tests/mochitest/whatwg/postMessage_onOther.html @@ -0,0 +1,109 @@ +<!DOCTYPE html> +<html> +<head> + <title>postMessage called through another frame</title> + <script type="application/javascript"> + var PATH = "/tests/dom/tests/mochitest/whatwg/postMessage_onOther.html"; + + function receiveMessage(evt) + { + if (evt.lastEventId !== "") + { + fail("unexpected non-empty lastEventId"); + return; + } + + switch (window.location.href) + { + case "http://example.com" + PATH: + receiveTopDomain(evt); + break; + + case "http://test1.example.com" + PATH: + receiveSubDomain(evt); + break; + + default: + fail("unexpected location"); + } + } + + function fail(msg) + { + window.parent.postMessage("FAIL " + msg, "*"); + } + + // The parent frame sends "start-test" to the subdomain frame to start. + // The subdomain frame then sets document.domain to the top domain so that + // the top domain frame can access it. It then sends a message to the top + // domain frame to tell it to do likewise; once that happens, the top domain + // frame can then call a method on the subdomain frame window, which will + // call a method *on the top domain window* to send a message to the parent + // window. We thus expect to see an event whose source is the subdomain + // window -- *not* the top domain window. Therefore, its .origin should be: + // + // http://test1.example.com + // + // and not + // + // http://example.com + + function receiveSubDomain(evt) + { + if (evt.origin !== "http://mochi.test:8888") + { + fail("wrong top-domain origin: " + evt.origin); + return; + } + if (evt.data !== "start-test") + { + fail("wrong top-domain message: " + evt.origin); + return; + } + + document.domain = "example.com"; + window.parent.topDomainFrame.postMessage("domain-switch", + "http://example.com"); + } + + function receiveTopDomain(evt) + { + if (evt.origin !== "http://test1.example.com") + { + fail("wrong subdomain origin: " + evt.origin); + return; + } + if (evt.data !== "domain-switch") + { + fail("wrong subdomain message: " + evt.origin); + return; + } + if (evt.source !== window.parent.subDomainFrame) + { + fail("wrong source on message from subdomain"); + return; + } + + document.domain = "example.com"; + window.parent.subDomainFrame.testSiblingPostMessage(); + } + + function testSiblingPostMessage() + { + window.parent.postMessage("test-finished", "http://mochi.test:8888"); + } + + function setup() + { + var target = document.getElementById("location"); + target.textContent = location.hostname + ":" + (location.port || 80); + } + + window.addEventListener("message", receiveMessage); + window.addEventListener("load", setup); + </script> +</head> +<body> +<h1 id="location">No location!</h1> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/postMessage_origin_helper.xhtml b/dom/tests/mochitest/whatwg/postMessage_origin_helper.xhtml new file mode 100644 index 0000000000..955b2de83f --- /dev/null +++ b/dom/tests/mochitest/whatwg/postMessage_origin_helper.xhtml @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <title>postMessage origin-testing helper page</title> + <script type="application/javascript"><![CDATA[ +function receiveMessage(evt) +{ + if (event.data === "PING") { + window.parent.postMessage("PONG", "*"); + return; + } + + var response = "PASS"; + + if (evt.origin !== "http://mochi.test:8888") + response += " wrong-origin(" + evt.origin + ")"; + if (evt.source !== window.parent) + response += " wrong-source"; + if (evt.data !== "PASS") + response += " wrong-data(" + evt.data + ")"; + + window.parent.postMessage(response, "http://mochi.test:8888"); +} + +window.addEventListener("message", receiveMessage); + + +// Aids for identifying origins + +function setup() +{ + var target = document.getElementById("location"); + target.textContent = location.hostname + ":" + (location.port || 80); +} + +window.addEventListener("load", setup); + ]]></script> +</head> +<body> +<h1 id="location">No location!</h1> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/postMessage_override_helper.html b/dom/tests/mochitest/whatwg/postMessage_override_helper.html new file mode 100644 index 0000000000..901e3af7bb --- /dev/null +++ b/dom/tests/mochitest/whatwg/postMessage_override_helper.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html> +<head> + <title>Overriding postMessage and dispatchEvent bindings</title> + <script type="application/javascript"> + window.postMessage = function (evt) { + window.parent.postMessage("FAIL overridden postMessage called", "*"); + }; + + var count = 0; + + function receiveMessage(evt) { + count++; + if (count == 2) { + window.dispatchEvent = function(evt) { + window.parent.postMessage("FAIL", "*"); + throw "dispatchEvent threw"; + }; + } + + window.parent.postMessage(evt.data, "http://mochi.test:8888"); + } + + function setup() { + var target = document.getElementById("location"); + target.textContent = location.hostname + ":" + (location.port || 80); + } + + window.addEventListener("message", receiveMessage); + + window.addEventListener("load", setup); + </script> +</head> +<body> +<h1 id="location">No location!</h1> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/postMessage_structured_clone_helper.html b/dom/tests/mochitest/whatwg/postMessage_structured_clone_helper.html new file mode 100644 index 0000000000..2b6e9d1f46 --- /dev/null +++ b/dom/tests/mochitest/whatwg/postMessage_structured_clone_helper.html @@ -0,0 +1,106 @@ +<!DOCTYPE html> +<html> +<head> + <title>postMessage structured clone page</title> + <script type="application/javascript" + src="postMessage_structured_clone_helper.js"></script> + <script type="application/javascript"> + var generator = getTestContent() + + function isClone(a, b) { + window.dump("Object a: " + a + "\n"); + window.dump("Object b: " + b + "\n"); + var stack = [[a, b]]; + var memory = new WeakMap(); + var rmemory = new WeakMap(); + + while (stack.length) { + var pair = stack.pop(); + var x = pair[0], y = pair[1]; + if (typeof x !== "object" || x === null) { + // x is primitive. + if (x !== y) { + window.dump("Primitives not equal!\n"); + return false; + } + } else if (x instanceof Date) { + if (x.getTime() == y.getTime()) + return true; + window.dump("Dates not equal!\n"); + return false; + } else if (memory.has(x)) { + // x is an object we have seen before in a. + if (y !== memory.get(x)) { + window.dump("Already seen!?\n"); + return false; + } + if (!(rmemory.get(y) == x)) { + window.dump("Not equal??\n"); + return false; + } + } else { + // x is an object we have not seen before. + // Check that we have not seen y before either. + if (rmemory.has(y)) { + // If we have seen y before, the only possible outcome + // is that x and y are literally the same object. + if (y == x) + continue; + window.dump("Already seen y!?\n"); + window.dump(y.toString() + "\n"); + return false; + } + + // x and y must be of the same [[Class]]. + var xcls = Object.prototype.toString.call(x); + var ycls = Object.prototype.toString.call(y); + if (xcls !== ycls) { + window.dump("Failing on proto\n"); + return false; + } + + // This function is only designed to check Objects and Arrays. + if (!(xcls === "[object Object]" || xcls === "[object Array]")) { + window.dump("Not an object!\n"); + window.dump(xcls + "\n"); + return false; + } + + // Compare objects. + var xk = Object.keys(x), yk = Object.keys(y); + if (xk.length !== yk.length) { + window.dump("Length mismatch!\n"); + return false; + } + for (var i = 0; i < xk.length; i++) { + // We must see the same property names in the same order. + if (xk[i] !== yk[i]) { + window.dump("wrong order\n"); + return false; + } + + // Put the property values on the stack to compare later. + stack.push([x[xk[i]], y[yk[i]]]); + } + + // Record that we have seen this pair of objects. + memory.set(x, y); + rmemory.set(y, x); + } + } + return true; + } + + function receiveMessage(evt) + { + if (isClone(evt.data, generator.next().value)) + window.parent.postMessage("TEST-PASS", "*"); + else + window.parent.postMessage("TEST-FAIL", "*"); + } + window.addEventListener("message", receiveMessage); + </script> +</head> +<body> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/postMessage_structured_clone_helper.js b/dom/tests/mochitest/whatwg/postMessage_structured_clone_helper.js new file mode 100644 index 0000000000..b457fd2d2f --- /dev/null +++ b/dom/tests/mochitest/whatwg/postMessage_structured_clone_helper.js @@ -0,0 +1,62 @@ +function* getTestContent() { + yield "hello"; + yield 2 + 3; + yield 12; + yield null; + yield "complex" + "string"; + yield new Object(); + yield new Date(1306113544); + yield [1, 2, 3, 4, 5]; + let obj = new Object(); + obj.foo = 3; + obj.bar = "hi"; + obj.baz = new Date(1306113544); + obj.boo = obj; + yield obj; + + let recursiveobj = new Object(); + recursiveobj.a = recursiveobj; + recursiveobj.foo = new Object(); + recursiveobj.foo.bar = "bar"; + recursiveobj.foo.backref = recursiveobj; + recursiveobj.foo.baz = 84; + recursiveobj.foo.backref2 = recursiveobj; + recursiveobj.bar = new Object(); + recursiveobj.bar.foo = "foo"; + recursiveobj.bar.backref = recursiveobj; + recursiveobj.bar.baz = new Date(1306113544); + recursiveobj.bar.backref2 = recursiveobj; + recursiveobj.expando = recursiveobj; + yield recursiveobj; + + obj = new Object(); + obj.expando1 = 1; + obj.foo = new Object(); + obj.foo.bar = 2; + obj.bar = new Object(); + obj.bar.foo = obj.foo; + obj.expando = new Object(); + obj.expando.expando = new Object(); + obj.expando.expando.obj = obj; + obj.expando2 = 4; + obj.baz = obj.expando.expando; + obj.blah = obj.bar; + obj.foo.baz = obj.blah; + obj.foo.blah = obj.blah; + yield obj; + + let diamond = new Object(); + obj = new Object(); + obj.foo = "foo"; + obj.bar = 92; + obj.backref = diamond; + diamond.ref1 = obj; + diamond.ref2 = obj; + yield diamond; + + let doubleref = new Object(); + obj = new Object(); + doubleref.ref1 = obj; + doubleref.ref2 = obj; + yield doubleref; +} diff --git a/dom/tests/mochitest/whatwg/postMessage_throw_helper.html b/dom/tests/mochitest/whatwg/postMessage_throw_helper.html new file mode 100644 index 0000000000..b1d3ddce7d --- /dev/null +++ b/dom/tests/mochitest/whatwg/postMessage_throw_helper.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<html> +<head> + <title>postMessage throwing page</title> + <script type="application/javascript"> + function receiveMessage(evt) + { + throw 17; + } + window.addEventListener("message", receiveMessage); + + function setup() + { + var target = document.getElementById("location"); + target.textContent = location.hostname + ":" + (location.port || 80); + } + + window.addEventListener("load", setup); + </script> +</head> +<body> +<h1 id="location">No location!</h1> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/postMessage_transfer_helper.html b/dom/tests/mochitest/whatwg/postMessage_transfer_helper.html new file mode 100644 index 0000000000..03c4c1030b --- /dev/null +++ b/dom/tests/mochitest/whatwg/postMessage_transfer_helper.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> +<head><meta charset=utf-8> + <title>postMessage transferable tests helper</title> +</head> +<body> +<script> + +onmessage = function(e) { + parent.postMessage(e.data, "*"); +}; + +</script> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/postMessage_userpass_helper.html b/dom/tests/mochitest/whatwg/postMessage_userpass_helper.html new file mode 100644 index 0000000000..762cf52f9f --- /dev/null +++ b/dom/tests/mochitest/whatwg/postMessage_userpass_helper.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<html> +<head> + <title>Username/password page for postMessage tests</title> + <script type="application/javascript"> + +function sendMessage(evt) +{ + var msg = "child-message"; + + if (evt.origin !== "http://mochi.test:8888") + msg += " wrong-origin(" + evt.origin + ")"; + if (evt.data !== "parent-message") + msg += " wrong-data(" + evt.data + ")"; + if (evt.lastEventId !== "") + msg += " wrong-lastEventId(" + evt.lastEventId + ")"; + if (evt.source !== window.parent) + msg += " wrong-source"; + + // It would be good to guarantee that we've been opened with a userinfo of + // "bobhope:password", but Gecko elides that from the content-visible URL, + // and I can't find another way to actually detect this programmatically. + + window.parent.postMessage(msg, "http://mochi.test:8888"); +} + +window.addEventListener("message", sendMessage); + </script> +</head> +<body> +<p>Kid iframe</p> +</body> +</html> + diff --git a/dom/tests/mochitest/whatwg/test_MessageEvent.html b/dom/tests/mochitest/whatwg/test_MessageEvent.html new file mode 100644 index 0000000000..41e1a7bcb4 --- /dev/null +++ b/dom/tests/mochitest/whatwg/test_MessageEvent.html @@ -0,0 +1,102 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage +--> +<head> + <title>MessageEvent tests</title> + <script 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=postMessage">Mozilla Bug 387706</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> + +<button id="target">target</button> + +<pre id="test"> +<script class="testbody" type="application/javascript"> +/** Test for Bug 387706 **/ + +SimpleTest.waitForExplicitFinish(); + +var data = "foobar"; +var origin = "http://cool.example.com"; +var bubbles = true, cancelable = true; +var lastEventId = "lastEventId"; + +var target; + +var count = 0; + +function sendMsg() +{ + try + { + var evt = new MessageEvent('message', { + bubbles, cancelable, data, + origin, lastEventId, source: window}); + ok(evt instanceof MessageEvent, "I ordered a MessageEvent!"); + + is(evt.data, data, "unexpected data"); + is(evt.origin, origin, "unexpected origin"); + is(evt.lastEventId, lastEventId, "unexpected lastEventId"); + + is(evt.cancelable, cancelable, "wrong cancelable property"); + is(evt.bubbles, bubbles, "wrong bubbling property"); + is(evt.source, window, "wrong source"); + + return target.dispatchEvent(evt); + } + catch (e) + { + ok(false, "exception thrown: " + e); + return false; + } +} + +function recvMsg(evt) +{ + is(evt.data, data, "unexpected data"); + is(evt.origin, origin, "unexpected origin"); + is(evt.lastEventId, lastEventId, "unexpected lastEventId"); + + is(evt.cancelable, cancelable, "wrong cancelable property"); + is(evt.bubbles, bubbles, "wrong bubbling property"); + is(evt.source, window, "wrong source"); + + is(evt.target, target, "wrong target"); + + if (target == evt.currentTarget) + { + is(Event.AT_TARGET, evt.eventPhase, "this listener was on the target"); + } + else + { + is(evt.currentTarget, document, "should have gotten this at the window"); + is(Event.BUBBLING_PHASE, evt.eventPhase, "wrong phase"); + } + + count++; +} + +function setup() +{ + target = $("target"); + target.addEventListener("message", recvMsg); + document.addEventListener("message", recvMsg); + var res = sendMsg(); + ok(res === true, "nothing canceled this"); + is(count, 2, "listener not called twice"); + SimpleTest.finish(); +} + +addLoadEvent(setup); + +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/test_MessageEvent_dispatchToOther.html b/dom/tests/mochitest/whatwg/test_MessageEvent_dispatchToOther.html new file mode 100644 index 0000000000..d5d2f4c454 --- /dev/null +++ b/dom/tests/mochitest/whatwg/test_MessageEvent_dispatchToOther.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage +--> +<head> + <title>Dispatching MessageEvent across origins</title> + <script 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=postMessage">Mozilla Bug 387706</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe src="http://example.com/" name="otherDomain"></iframe> + +<pre id="test"> +<script class="testbody" type="application/javascript"> +/** Test for Bug 387706 **/ + +SimpleTest.waitForExplicitFinish(); + +function run() +{ + try + { + var msg = new MessageEvent('message', { bubbles: true, cancelable: true, + data: "foo", origin: "http://evil.com", + source: window }); + + try + { + var ex; + window.frames.otherDomain.dispatchEvent(msg); + ok(false, "should have thrown a security exception per HTML5"); + } + catch (e) + { + ok(true, "correctly threw an exception (security exception, but " + + "what that entails isn't yet defined in the spec)"); + } + } + catch (e) + { + ok(false, "threw exception during execution: " + e); + } + finally + { + SimpleTest.finish(); + } +} + +addLoadEvent(run); + +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/test_bug477323.html b/dom/tests/mochitest/whatwg/test_bug477323.html new file mode 100644 index 0000000000..66bad6288c --- /dev/null +++ b/dom/tests/mochitest/whatwg/test_bug477323.html @@ -0,0 +1,64 @@ +<!DOCTYPE html> +<html> +<head> + <title>Dynamically assigned drag and drop handlers</title> + <script type="text/javascript" src="/MochiKit/MochiKit.js"></script> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body onload="start()"> +<script class="testbody" type="application/javascript"> + +SimpleTest.waitForExplicitFinish(); + +function start() +{ + var element = document.getElementById("draggable_img"); + + var gotdragstart = false, gotdrag = false, + gotdragenter = false, gotdragover = false, gotdragleave = false, + gotdrop = false, gotdragend = false; + + element.ondragstart = function(event) {gotdragstart = true;} + element.ondrag = function(event) {gotdrag = true;} + element.ondragenter = function(event) {gotdragenter = true;} + element.ondragover = function(event) {gotdragover = true;} + element.ondragleave = function(event) {gotdragleave = true;} + element.ondrop = function(event) {gotdrop = true;} + element.ondragend = function(event) {gotdragend = true;} + + function dispatch(eventName) + { + var event = document.createEvent("DragEvent"); + event.initDragEvent(eventName, true, true, window, 0, 5, 5, 5, 5, + false, false, false, false, 0, null, null); + element.dispatchEvent(event); + } + + dispatch("dragstart"); + dispatch("drag"); + dispatch("dragenter"); + dispatch("dragover"); + dispatch("dragleave"); + dispatch("drop"); + dispatch("dragend"); + + ok(gotdragstart, "Got ondragstart event"); + ok(gotdrag, "Got ondrag event"); + ok(gotdragenter, "Got ondragenter event"); + ok(gotdragover, "Got ondragover event"); + ok(gotdragleave, "Got ondragleave event"); + ok(gotdrop, "Got ondrop event"); + ok(gotdragend, "Got ondragend event"); + + SimpleTest.finish(); +} + +</script> + +<img src="data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%18%00%00%00%18%02%03%00%00%00%9D%19%D5k%00%00%00%04gAMA%00%00%B1%8F%0B%FCa%05%00%00%00%0CPLTE%FF%FF%FF%FF%FF%FF%F7%DC%13%00%00%00%03%80%01X%00%00%00%01tRNS%08N%3DPT%00%00%00%01bKGD%00%88%05%1DH%00%00%00%09pHYs%00%00%0B%11%00%00%0B%11%01%7Fd_%91%00%00%00%07tIME%07%D2%05%0C%14%0C%0D%D8%3F%1FQ%00%00%00%5CIDATx%9C%7D%8E%CB%09%C0%20%10D%07r%B7%20%2F%E9wV0%15h%EA%D9%12D4%BB%C1x%CC%5C%1E%0C%CC%07%C0%9C0%9Dd7()%C0A%D3%8D%E0%B8%10%1DiCHM%D0%AC%D2d%C3M%F1%B4%E7%FF%10%0BY%AC%25%93%CD%CBF%B5%B2%C0%3Alh%CD%AE%13%DF%A5%F7%E0%03byW%09A%B4%F3%E2%00%00%00%00IEND%AEB%60%82" + draggable="true" id="draggable_img"/> + +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/test_bug500328.html b/dom/tests/mochitest/whatwg/test_bug500328.html new file mode 100644 index 0000000000..20b2c36761 --- /dev/null +++ b/dom/tests/mochitest/whatwg/test_bug500328.html @@ -0,0 +1,769 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=500328 +--> +<head> + <title>Test for Bug 500328</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/EventUtils.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=500328">Mozilla Bug 500328</a> +<p id="display"></p> +<div id="status"></div> +<div id="content"> + <iframe id="iframe"></iframe> + <iframe id="iframe2"></iframe> + <a id="link">link</a> +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 500328 **/ + +SimpleTest.waitForExplicitFinish(); + +var iframe = document.getElementById("iframe"); +var iframeCw = iframe.contentWindow; + +var iframe2 = document.getElementById("iframe2"); +var iframe2Cw = iframe2.contentWindow; + +const unvisitedColor = "rgb(0, 0, 238)"; +const visitedColor = "rgb(85, 26, 139)"; + +var gCallbackOnIframeLoad = false; +var gCallbackOnIframePageShow = false; +var gCallbackOnPopState = false; +var gNumPopStates = 0; +var gLastPopStateEvent; +var gLastScriptHistoryState; + +var gGen; + +function statusMsg(msg) { + var msgElem = document.createElement("p"); + msgElem.appendChild(document.createTextNode(msg)); + + document.getElementById("status").appendChild(msgElem); +} + +function longWait() { + function hitEventLoop(times, func) { + if (times > 0) { + setTimeout(hitEventLoop, 0, times - 1, func); + } else { + setTimeout(func, 0); + } + } + hitEventLoop(100, function() { gGen.next(); }); +} + +function shortWait() { + setTimeout(function() { gGen.next(); }, 0); +} + +function onChildPopState(e) { + gNumPopStates++; + gLastPopStateEvent = e; + if (gCallbackOnPopState) { + statusMsg("Popstate(" + JSON.stringify(e.state) + "). Calling gGen.next()."); + gCallbackOnPopState = false; + gGen.next(); + } + else { + statusMsg("Popstate(" + JSON.stringify(e.state) + "). NOT calling gGen.next()."); + } +} + +function onChildScript(state) { + gLastScriptHistoryState = state; +} + +function getURLFromEvent(e) { + try { + var target = e.target; + if ("contentWindow" in target) { + return target.contentWindow.location.toString(); + } + if ("ownerDocument" in target && target.ownerDocument) { + return target.ownerDocument.location.toString(); + } + if ("location" in target) { + return target.location.toString(); + } + return target.toString(); + } + catch(ex) { + return "<cross-site object>"; + } +} + +function onChildLoad(e) { + if(gCallbackOnIframeLoad) { + statusMsg("Got load for " + getURLFromEvent(e) + ". About to call gGen.next()."); + gCallbackOnIframeLoad = false; + gGen.next(); + } + else { + statusMsg("Got load for " + getURLFromEvent(e) + ", but not calling gGen.next() because gCallbackOnIframeLoad was false."); + } +} + +function onChildPageShow(e) { + if(gCallbackOnIframePageShow) { + statusMsg("Got pageshow for " + getURLFromEvent(e) + ". About to call gGen.next()."); + gCallbackOnIframePageShow = false; + SimpleTest.executeSoon(function() { gGen.next(); }); + } + else { + statusMsg("Got pageshow for " + getURLFromEvent(e) + ", but not calling gGen.next() because gCallbackOnIframePageShow was false."); + } +} + +function enableChildLoadCallback() { + gCallbackOnIframeLoad = true; +} + +function enableChildPageShowCallback() { + gCallbackOnIframePageShow = true; +} + +function enableChildPopStateCallback() { + gCallbackOnPopState = true; +} + +function clearPopStateCounter() { + gNumPopStates = 0; +} + +function noPopStateExpected(msg) { + is(gNumPopStates, 0, msg); + + // Even if there's an error, set gNumPopStates to 0 so other tests don't + // fail. + gNumPopStates = 0; +} + +function popstateExpected(msg) { + is(gNumPopStates, 1, msg); + gNumPopStates = 0; +} + +function getColor(elem) { + var utils = SpecialPowers.getDOMWindowUtils(document.defaultView); + return utils.getVisitedDependentComputedStyle(elem, "", "color"); +} + +function getSHistory(theWindow) +{ + const Ci = SpecialPowers.Ci; + var sh = SpecialPowers.wrap(theWindow).docShell + .QueryInterface(Ci.nsIWebNavigation) + .sessionHistory; + if (!sh || sh == null) + throw("Couldn't get shistory for window!"); + + return sh; +} + +function getSHTitle(sh, offset) +{ + if (!offset) + offset = 0; + + // False instructs the SHistory not to modify its current index. + return sh.legacySHistory.getEntryAtIndex(sh.index + offset).title; +} + +// Tests that win's location ends with str +function locationEndsWith(win, str) { + var exp = new RegExp(str + "$"); + ok(win.location.toString().match(exp), + "Wrong window location. Expected it to end with " + + str + ", but actuall was " + win.location); +} + +function expectException(func, msg) { + var failed = false; + try { + func(); + } catch(ex) { + failed = true; + } + + ok(failed, msg + " succeeded, but should have failed."); +} + +function* runTest() { + // We can't enable universal XPConnect privleges in this function, because + // test 5 needs to be running at normal privleges in order to test the + // same-origin policy. + + /** + * PRELIMINARY: + * 1. Clear the popstate counter + */ + + clearPopStateCounter(); + + // The URL of file_bug500328_1.html on http://localhost:8888 + var innerLoc; + + // Now we can start the tests + + /** + * TEST 1 tests basic pushState functionality + */ + enableChildLoadCallback(); + iframeCw.location = "file_bug500328_1.html"; + yield undefined; + innerLoc = iframeCw.location.toString(); + // No popstate during initial load. + shortWait(); + yield undefined; + noPopStateExpected("No initial popstate."); + is(JSON.stringify(gLastScriptHistoryState), "null", "null initial state."); + statusMsg("Awake after first load."); + + // Make sure that the pushstate below doesn't trigger a hashchange. + iframeCw.onhashchange = function() { + ok(false, "Pushstate shouldn't trigger a hashchange."); + }; + + var testObj1 = 42; + var testObj2 = { x: 4.2 }; + iframeCw.history.pushState(testObj1, "test 1"); + is(JSON.stringify(iframeCw.history.state), JSON.stringify(testObj1), + "correct state after pushState"); + is(iframeCw.location.search, "", + "First pushstate should leave us where we were."); + + iframeCw.history.pushState(testObj2, "test 1#foo", "?test1#foo"); + is(JSON.stringify(iframeCw.history.state), JSON.stringify(testObj2), + "correct state after pushState"); + isnot(iframeCw.history.state, testObj2, + "correct state object identity after pushState"); + is(iframeCw.location.search, "?test1", + "Second pushstate should push us to '?test1'."); + is(iframeCw.location.hash, "#foo", + "Second pushstate should push us to '#foo'"); + shortWait(); + yield undefined; + + // Let the hashchange event fire, if it's going to. + longWait(); + yield undefined; + iframeCw.onhashchange = null; + + statusMsg("About to go back to page 1."); + // We don't have to yield here because this back() and the resulting popstate + // are completely synchronous. In fact, if we did yield, JS would throw an + // error because we'd be calling gGen.next from within gGen.next. + iframeCw.history.back(); + + statusMsg("Awake after going back to page 1."); + popstateExpected("Going back to page 1 should trigger a popstate."); + is(gLastPopStateEvent.isTrusted, true, 'Popstate event should be trusted.'); + is(JSON.stringify(gLastPopStateEvent.state), JSON.stringify(testObj1), + "Wrong state object popped after going back to page 1."); + ok(gLastPopStateEvent.state === iframeCw.history.state, + "Wrong state object in document after going back to page 1."); + ok(iframeCw.location.toString().match(/file_bug500328_1.html$/), + "Going back to page 1 hould take us to original page."); + + iframeCw.history.back(); + popstateExpected("Going back to page 0 should trigger a popstate."); + is(gLastPopStateEvent.state, null, + "Going back to page 0 should pop a null state."); + is(iframeCw.history.state, null, + "Going back to page 0 should pop a null state."); + is(iframeCw.location.search, "", + "Going back to page 0 should clear the querystring."); + + iframeCw.history.forward(); + popstateExpected("Going forward to page 1 should trigger a popstate."); + is(JSON.stringify(gLastPopStateEvent.state), JSON.stringify(testObj1), + "Wrong state object popped after going forward to page 1."); + is(gLastPopStateEvent.state, iframeCw.history.state, + "Wrong state object in document after going forward to page 1."); + ok(iframeCw.location.toString().match(/file_bug500328_1.html$/), + "Going forward to page 1 should leave us at original page."); + + statusMsg("About to go forward to page 2."); + iframeCw.history.forward(); + statusMsg("Awake after going forward to page 2."); + popstateExpected("Going forward to page 2 should trigger a popstate."); + is(JSON.stringify(gLastPopStateEvent.state), JSON.stringify(testObj2), + "Wrong state object popped after going forward to page 2."); + is(iframeCw.history.state, gLastPopStateEvent.state, + "Wrong state object in document after going forward to page 2."); + ok(iframeCw.location.toString().match(/file_bug500328_1.html\?test1#foo$/), + "Going forward to page 2 took us to " + iframeCw.location.toString()); + + statusMsg("About to reload page 2."); + iframeCw.location.reload(); + enableChildLoadCallback(); + yield undefined; + statusMsg("Awake after reloading page 2."); + noPopStateExpected("Reloading page 2 should not trigger popstate."); + is(JSON.stringify(iframeCw.history.state), JSON.stringify(testObj2), + "Wrong state object after reloading page 2."); + is(JSON.stringify(gLastScriptHistoryState), JSON.stringify(testObj2), + "Wrong state object while reloading page 2."); + ok(iframeCw.location.toString().match(/file_bug500328_1.html\?test1#foo$/), + "Reloading page 2 took us to " + iframeCw.location.toString()); + + // The iframe's current location is file_bug500328_1.html?test1#foo. + // Clicking link1 should take us to file_bug500328_1.html?test1#1. + + enableChildPopStateCallback(); + sendMouseEvent({type:'click'}, 'link-anchor1', iframeCw); + yield undefined; + popstateExpected("Clicking on link-anchor1 should trigger a popstate."); + is(iframeCw.location.search, "?test1", + "search should be ?test1 after clicking link."); + is(iframeCw.location.hash, "#1", + "hash should be #1 after clicking link."); + is(iframeCw.history.state, null, + "Wrong state object in document after clicking link to hash '#1'."); + + /* + * Reload file_bug500328_1.html; we're now going to test that link hrefs + * and colors are updated correctly on push/popstates. + */ + + iframe.onload = onChildLoad; + enableChildLoadCallback(); + iframeCw.location = "about:blank"; + yield undefined; + enableChildLoadCallback(); + iframeCw.location = "file_bug500328_1.html"; + yield undefined; + noPopStateExpected("No popstate after re-loading file_bug500328_1.html"); + statusMsg("Done loading file_bug500328_1.html for the second time."); + + var ifLink = iframeCw.document.getElementById("link-anchor1"); + var rand = Date.now() + "-" + Math.random(); + ifLink.href = rand; + + // Poll the document until the link has the correct color, or this test times + // out. Unfortunately I can't come up with a more elegant way to do this. + // We could listen to MozAfterPaint, but that doesn't guarantee that we'll + // observe the new color. + while (getColor(ifLink) != unvisitedColor) { + // Dump so something shows up in the mochitest logs if we spin here. + dump("ifLink has wrong initial color. Spinning...\n"); + setTimeout(function() { gGen.next(); }, 0); + yield undefined; + } + + // Navigate iframe2 to dir/${rand} + iframe2.onload = onChildLoad; + enableChildLoadCallback(); + iframe2Cw.location = "mytestdir/" + rand; + yield undefined; + + // PushState the iframe into the mytestdir directory. This should cause + // ifLink to turn purple, since we just visited mytestdir/${rand} in iframe2. + iframeCw.history.pushState(null, "foo", "mytestdir/foo"); + + // Check that the link's color is now visitedColor + while (getColor(ifLink) != visitedColor) { + dump("ifLink has wrong color after pushstate. Spinning...\n"); + setTimeout(function() { gGen.next(); }, 0); + yield undefined; + } + + ok(ifLink.href.match("mytestdir\\/" + rand + "$"), + "inner frame's link should end with 'mytestdir/${rand}'"); + + // Navigate out of the mytestdir directory. This should cause ifLink to turn + // blue again. + iframeCw.history.pushState(null, "bar", "../file_bug500328_1.html"); + + // Check that the link's color is back to the unvisited color. + while (getColor(ifLink) != unvisitedColor) { + dump("ifLink has wrong color after pushstating out of dir. Spinning...\n"); + setTimeout(function() { gGen.next(); }, 0); + yield undefined; + } + + ok(!ifLink.href.match("mytestdir"), + "inner frame's link shouldn't contain 'mytestdir'."); + + /* + * TEST 2 tests that pushstate's same-origin checks are correct. + */ + var filename = 'file_bug500328_2.html'; + var dirname = document.location.pathname.replace(/[^\/]*$/, ''); + statusMsg("Dirname is: " + dirname); + iframeCw.location = filename; + iframe.onload = onChildLoad; + enableChildLoadCallback(); + yield undefined; + + // This function tries to pushstate and replacestate to the given URL and + // fails the test if the calls succeed. + var tryBadPushAndReplaceState = function(url) { + // XXX ex should be a SECURITY_ERR, not a plain Error. + + var hist = iframeCw.history; + var url2 = url + dirname + filename; + + expectException(function() { hist.pushState({}, "foo", url); }, + 'pushState to ' + url); + + expectException(function() { hist.pushState({}, "foo", url2); }, + 'pushState to ' + url2); + + expectException(function() { hist.replaceState({}, "foo", url); }, + 'replaceState to ' + url); + + expectException(function() { hist.replaceState({}, "foo", url2); }, + 'replaceState to ' + url2); + } + + // We're currently at http://example.com/[dirname]/[filename] + tryBadPushAndReplaceState("https://mochi.test:8888"); + tryBadPushAndReplaceState("http://foo.mochitest:8888"); + tryBadPushAndReplaceState("http://mochi.test:1234"); + tryBadPushAndReplaceState("http://mochi.test.a:8888"); + tryBadPushAndReplaceState("http://mochi.tes:8888"); + tryBadPushAndReplaceState("http://mmochi.test:8888"); + tryBadPushAndReplaceState("http://me@mochi.test:8888"); + + /** + * TEST 3 tests that the session history entries' titles are properly sync'ed + * after push/pop states. + * + * We have to run this test in a popup rather than an iframe because only the + * root docshell has a session history object. + */ + statusMsg("About to open popup."); + var popup = window.open("file_bug500328_1.html", "popup0", + "height=200,width=200,location=yes," + + "menubar=yes,status=yes,toolbar=yes,dependent=yes"); + + enableChildLoadCallback(); + var shistory = getSHistory(popup); + yield undefined; + shortWait(); + yield undefined; + noPopStateExpected("Shouldn't get popstate after opening window."); + + popup.history.pushState(null, "title 0"); + ok(SpecialPowers.isBackButtonEnabled(popup), + "Back button was not enabled after initial pushstate."); + + popup.document.title = "title 1"; + + // Yield to the event loop so listeners will be notified of the title change + // and so that the hash change we trigger below generates a new session + // history entry. + shortWait(); + yield undefined; + + // Check that the current session history entry's title has been updated to + // reflect the new document title. + is(getSHTitle(shistory), "title 1", "SHEntry title test 1"); + + // Change the page's hash to #1, which will trigger a popstate event. + // We don't have to wait, because this happens synchronously. + popup.location.hash = "#1"; + popstateExpected("Didn't get popstate after changing hash."); + + popup.document.title = "title 2"; + + // Yield so listeners will be notified of the title change we just performed. + shortWait(); + yield undefined; + + is(getSHTitle(shistory), "title 2", "SHEntry title test 2"); + + // Go back. Happens synchronously. We should get a popstate. + statusMsg("About to go back."); + popup.history.go(-1); + popstateExpected("Didn't get a popstate after going back."); + + // Even though we went back, we expect the SHEntry title to remain the same + // because the document didn't change. + is(getSHTitle(shistory), "title 2", "SHEntry title test 3"); + + popup.document.title = "Changed 1"; + shortWait(); + yield undefined; + + // This check is really a test of bug 509055. + is(getSHTitle(shistory), "Changed 1", "SHEntry title test 4"); + + popup.close(); + + /** + * TEST 4 tests replaceState and that we don't get double popstates on + * window.open. It also stress-tests the system and its interaction with + * bfcache by making many push/replace state calls. + */ + popup = window.open("file_bug500328_1.html", "popup1", + "height=200,width=200,location=yes," + + "menubar=yes,status=yes,toolbar=yes,dependent=yes"); + + // The initial about:blank load into the new window shouldn't result in us + // seeing a popstate. Once file_bug500328_1.html is loaded, it'll overwrite + // popup.onpopstate, and this assertion won't fire for that popstate and + // others after. + // + // If we fired the popstate event asynchronously, we'd expect this assert to + // fire. + popup.onpopstate = function() { + ok(false, "Initial load of popup shouldn't give us a popstate."); + }; + + shistory = getSHistory(popup); + + enableChildLoadCallback(); + yield undefined; + statusMsg("Awake after loading content into popup."); + + popup.history.replaceState({n:1, ok:true}, "state 1", "good1.html"); + locationEndsWith(popup, "good1.html"); + + // Even though we replaceState with title "state 1", the title should remain + // "test 1" because we ignore the title argument in push/replaceState. + // See bug 544535. + is(getSHTitle(shistory), "test 1", "SHEntry title 'state 1'"); + + // Flush the event loop so our next load creates a new session history entry. + shortWait(); + yield undefined; + + enableChildLoadCallback(); + popup.location = "file_bug500328_1.html"; + yield undefined; + + // Flush the event loop so nsDocShell::OnNewURI runs and our load is recorded + // properly. + shortWait(); + yield undefined; + + // Now go back and make sure everything is as it should be. + enableChildPageShowCallback(); + popup.history.back(); + yield undefined; + // Flush the event loop so the document's location is updated and any + // popstates fire. + shortWait(); + yield undefined; + noPopStateExpected("no popstate during initial load"); + + locationEndsWith(popup, "good1.html"); + is(JSON.stringify(popup.history.state), '{"n":1,"ok":true}', + "Wrong state popped after going back to initial state."); + + // We're back at state 0, which was replaceState-ed to state1.html. Let's do + // some push/pop/replaces to make sure everything works OK when we involve + // large numbers of SHEntries. + for(var i = 2; i <= 30; i++) { + if (i % 3 == 0) { + popup.history.pushState({n:i, ok:true}, "state " + i, "good" + i + ".html"); + } + else { + popup.history.pushState({n:i}, "state " + i, "state" + i + ".html"); + for(var j = 0; j < i % 4; j++) { + popup.history.replaceState({n:i, nn:j}, "state " + i + ", " + j); + } + popup.history.replaceState({n:i, ok:true}, "state " + i, "good" + i + ".html"); + } + } + + for(var i = 29; i >= 1; i--) { + popup.history.back(); + popstateExpected("Didn't get a popstate on iteration " + i); + locationEndsWith(popup, "good" + i + ".html"); + is(gLastPopStateEvent.state.n, i, "Bad counter on last popstate event."); + ok(gLastPopStateEvent.state.ok, + "Last popstate event should have 'ok' set to true."); + } + + popup.close(); + + /** + * TEST 5 tests misc security features and implementation details of + * Push/ReplaceState + */ + + /* + * Test that we can't push/replace an object with a large (over 640k + * characters) JSON representation. + */ + + // (In case you're curious, this loop generates an object which serializes to + // 694581 characters.) + var bigObject = new Object(); + for(var i = 0; i < 51200; i++) { + bigObject[i] = i; + } + // statusMsg("Big object has size " + JSON.stringify(bigObject).length); + + // We shouldn't be able to pushstate this large object, due to space + // constraints. + expectException( + function() { iframeCw.history.pushState(bigObject, "foo"); }, + "pushState-ing large object"); + + expectException( + function() { iframeCw.history.replaceState(bigObject, "foo"); }, + "replaceState-ing large object"); + + /* + * Make sure we can't push/replace state on an iframe of a different origin. + * This will work if this function has requested Universal XPConnect + * privileges, so any code which needs those privileges can't be in this + * function. + */ + enableChildLoadCallback(); + iframeCw.location = "http://example.com"; + iframe.onload = onChildLoad; + yield undefined; + iframe.onload = null; + + expectException( + function() { iframeCw.history.pushState({}, "foo"); }, + "pushState-ing in a different origin"); + + expectException( + function() { iframeCw.history.replaceState({}, "foo"); }, + "replaceState-ing in a different origin"); + + /* + * If we do the following: + * * Start at page A. + * * PushState to page B. + * * Refresh. The server responds with a 404 + * * Go back. + * Then at the end, page A should be displayed, not the 404 page. + */ + enableChildLoadCallback(); + iframe.onload = onChildLoad; + iframeCw.location = "about:blank"; + yield undefined; + iframe.onload = null; + + enableChildLoadCallback(); + // navigate to http://mochi.test:8888/[...]/file_bug500328_1.html + iframeCw.location = innerLoc; + yield undefined; + + // PushState to a URL which doesn't exist + iframeCw.history.pushState({}, "", rand); + + // Refresh. We'll end up a 404 page. + iframe.onload = onChildLoad; + enableChildLoadCallback(); + iframeCw.location.reload(true); + yield undefined; + iframe.onload = null; + + // Since the last page was a 404, going back should actually show the + // contents of the old page, instead of persisting the contents of the 404 + // page. + enableChildPageShowCallback(); + iframeCw.history.back(); + yield undefined; + + // Make sure that we're actually showing the contents of + // file_bug500328_1.html, as opposed to the 404 page. + var identifierElem = iframeCw.document.getElementById("link-anchor1"); + ok(identifierElem != undefined && identifierElem != null, + "iframe didn't contain file_bug500328_1.html's contents."); + + /** + * TEST 6 tests that the referrer is set properly after push/replace states. + */ + + /* + * First, a simple test: + * * Load file_bug500328_1.html into iframe + * * PushState to newpage1.html#foo + * * Instruct the iframe to load file_bug500328_1.html into itself. + * The referer should be newpage1.html, without the hash. + * + * This also tests that we can call pushState from within the onload handler. + */ + enableChildLoadCallback(); + iframeCw.location = "file_bug500328_1.html"; + yield undefined; + + // Run within the onload handler. This should work without issue. + iframeCw.history.pushState(null, "", "newpage1.html"); + + // iframeCw.navigateTo() causes the iframe to set its location on our + // behalf. We can't just set its location ourselves, because then *we* + // become the referrer. + enableChildLoadCallback(); + iframeCw.navigateTo("file_bug500328_1.html"); + yield undefined; + + ok(iframeCw.document.referrer.toString().match(/newpage1.html$/), + "Wrong referrer after pushState. Expected newpage1.html, but was " + + iframeCw.document.referrer); + + /* + * We're back at file_bug500328_1.html. Now do the following: + * * replaceState to newpage2.html#foo + * * Click a link back to file_bug500328_1.html + * The referrer should be newpage2.html, without the hash. + */ + iframeCw.history.replaceState(null, null, "newpage2.html#foo"); + enableChildLoadCallback(); + sendMouseEvent({type:'click'}, 'link-self', iframeCw); + yield undefined; + + ok(iframeCw.document.referrer.toString().match(/newpage2.html$/), + "Wrong referrer after replaceState. Expected newpage2.html, but was " + + iframeCw.document.referrer); + + /* + * Set up a cycle with the popstate event to make sure it's properly + * collected. + */ + var evt = document.createEvent("popstateevent"); + evt.initEvent("foo", false, false, evt); + + /* */ + SimpleTest.finish(); + statusMsg("********** Finished tests ***********"); + while(true) + { + yield undefined; + + // I don't think this will actually make the mochitest fail, but there's + // not much we can do about this. Realistically, though, regressions are + // not likely to fire extra events -- this trap is here mostly to catch + // errors made while wriring tests. + ok(false, "Got extra event!"); + } + + /* + statusMsg("XXXXXXXXXXXXXX"); + while(true) { + yield undefined; + statusMsg("Woken up."); + } + */ +} + +// Important: Wait to start the tests until the page has loaded. Otherwise, +// the test will occasionally fail when it begins running before the iframes +// have finished their initial load of about:blank. +window.addEventListener('load', function() { + gGen = runTest(); + gGen.next(); +}); + +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/test_document_scripts.html b/dom/tests/mochitest/whatwg/test_document_scripts.html new file mode 100644 index 0000000000..f0d8d27449 --- /dev/null +++ b/dom/tests/mochitest/whatwg/test_document_scripts.html @@ -0,0 +1,55 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=685774 +--> +<head> + <title>Test for document.scripts (Bug 685774)</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/EventUtils.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=685774">Mozilla Bug 685774</a> +<script type="application/javascript"> + +/** Test for Bug 685774 **/ + +function testSameCollection(a, b, c) { + is(a.length, c, "unexpected count of script elements"); + is(b.length, c, "unexpected count of script elements"); + for (var i = 0; i < a.length; i++) { + is(a[i], b[i], "document.scripts is not supported"); + } +} + +SimpleTest.waitForExplicitFinish(); + +testSameCollection(document.scripts, document.getElementsByTagName("script"), 3); + +</script> +<script type="application/javascript"> + +testSameCollection(document.scripts, document.getElementsByTagName("script"), 4); + +function start() { + testSameCollection(document.scripts, document.getElementsByTagName("script"), 5); + + var e = document.createElement("script"); + testSameCollection(document.scripts, document.getElementsByTagName("script"), 5); + document.body.appendChild(e); + testSameCollection(document.scripts, document.getElementsByTagName("script"), 6); + + SimpleTest.finish(); +} + +addLoadEvent(start); + +</script> +<script type="application/javascript"> + +testSameCollection(document.scripts, document.getElementsByTagName("script"), 5); + +</script> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/test_postMessage.html b/dom/tests/mochitest/whatwg/test_postMessage.html new file mode 100644 index 0000000000..a21abb9a00 --- /dev/null +++ b/dom/tests/mochitest/whatwg/test_postMessage.html @@ -0,0 +1,161 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage +--> +<head> + <title>Basic postMessage tests</title> + <script 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=postMessage">Mozilla Bug 387706</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe src="http://mochi.test:8888/tests/dom/tests/mochitest/whatwg/postMessage_helper.html" + name="otherSameDomain"></iframe> +<iframe src="http://example.org:8000/tests/dom/tests/mochitest/whatwg/postMessage_helper.html" + name="otherCrossDomain"></iframe> + + +<pre id="test"> +<script class="testbody" type="application/javascript"> +/** Test for Bug 387706 **/ + +SimpleTest.waitForExplicitFinish(); + +/** Variable for receivers to attempt to get. */ +window.privateVariable = 17; + +/** For sentinel finish, if necessary in deficient browsers. */ +var finished = false; + +/** Ends testing if it isn't already done. */ +function finish() +{ + if (!finished) + { + finished = true; + SimpleTest.finish(); + } +} + +/** Receives MessageEvents. */ +function messageReceiver(evt) +{ + try + { + ok(evt instanceof MessageEvent, "umm, how did we get this?"); + is(evt.lastEventId, "", + "postMessage creates events with empty lastEventId"); + is(evt.type, "message", "expected events of type 'message'"); + ok(evt.isTrusted === true, "should have been a trusted event"); + + var data = evt.data; + + // Check for the message we send to ourselves; it can't be + // counted as a test, and it's conceptually distinct from + // the other cases, so just return after handling it. + if (data === "post-to-self") + { + respondToSelf(evt); + return; + } + + switch (evt.data) + { + case "post-to-self-response": + receiveSelf(evt); + break; + + case "post-to-other-same-domain-response": + receiveOtherSameDomain(evt); + break; + + case "post-to-other-cross-domain-response": + receiveOtherCrossDomain(evt); + + // All the tests have executed, so we're done. + finish(); + break; + + default: + ok(false, "unexpected message: " + evt.data); + finish(); + break; + } + } + catch (e) + { + ok(false, "error processing event with data '" + evt.data + "': " + e); + finish(); + } +} + + +/****************** + * SELF-RESPONDER * + ******************/ + +function respondToSelf(evt) +{ + is(evt.origin, "http://mochi.test:8888", "event has wrong origin"); + is(evt.source, window, "we posted this message!"); + + evt.source.postMessage("post-to-self-response", evt.origin); +} + + +/************* + * RECEIVERS * + *************/ + +function receiveSelf(evt) +{ + is(evt.origin, "http://mochi.test:8888", "event has wrong origin"); + is(evt.source, window, "we posted this message!"); + + window.frames.otherSameDomain.postMessage("post-to-other-same-domain", + "http://mochi.test:8888"); +} + +function receiveOtherSameDomain(evt) +{ + is(evt.origin, "http://mochi.test:8888", + "same-domain response event has wrong origin"); + is(evt.source, window.frames.otherSameDomain, + "wrong source for same-domain message!"); + + window.frames.otherCrossDomain.postMessage("post-to-other-cross-domain", + "http://example.org:8000"); +} + +function receiveOtherCrossDomain(evt) +{ + is(evt.origin, "http://example.org:8000", + "same-domain response event has wrong origin"); + + // can't use |is| here, because ok tries to get properties on its arguments + // for creating a formatted logging message + ok(evt.source === window.frames.otherCrossDomain, + "wrong source for cross-domain message!"); +} + + +/************** + * TEST SETUP * + **************/ + +function start() +{ + window.postMessage("post-to-self", "http://mochi.test:8888"); +} + +window.addEventListener("load", start); +window.addEventListener("message", messageReceiver); + +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/test_postMessage_basehref.html b/dom/tests/mochitest/whatwg/test_postMessage_basehref.html new file mode 100644 index 0000000000..4a0a167d9f --- /dev/null +++ b/dom/tests/mochitest/whatwg/test_postMessage_basehref.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=414815 +--> +<head> + <title>postMessage's interaction with a <base> tag</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <base href="http://example.com/" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=414815">Mozilla Bug 414815</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<pre id="test"> +<script class="testbody" type="application/javascript"> +/** Test for Bug 414815 **/ + +SimpleTest.waitForExplicitFinish(); + +function receiveMessage(evt) +{ + is(evt.origin, "http://mochi.test:8888", "wrong sender"); + ok(evt.source === window, "wrong source"); + + is(evt.data, "generate-event", "wrong data"); + is(evt.lastEventId, "", "wrong lastEventId"); + + SimpleTest.finish(); +} + +window.addEventListener("message", receiveMessage); + +function run() +{ + window.postMessage("generate-event", "http://mochi.test:8888"); +} + +window.addEventListener("load", run); +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/test_postMessage_chrome.html b/dom/tests/mochitest/whatwg/test_postMessage_chrome.html new file mode 100644 index 0000000000..ceb19fc552 --- /dev/null +++ b/dom/tests/mochitest/whatwg/test_postMessage_chrome.html @@ -0,0 +1,114 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage +--> +<head> + <title>postMessage chrome tests</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script src="chrome://mochikit/content/chrome-harness.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> +</head> +<body> + +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage">Mozilla Bug 387706</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe src="http://example.org/tests/dom/tests/mochitest/whatwg/postMessage_chrome_helper.html" + name="contentDomain"></iframe> + + +<pre id="test"> +<script class="testbody" type="application/javascript"> +/** Test for Bug 387706 **/ + +SimpleTest.waitForExplicitFinish(); + +var finished = false; +function finish() +{ + if (!finished) + { + finished = true; + SimpleTest.finish(); + } +} + +/** Receives MessageEvents to this window. */ +function messageReceiver(evt) +{ + ok(evt instanceof MessageEvent, "umm, how did we get this?"); + is(evt.type, "message", "expected events of type 'message'"); + is(evt.lastEventId, "", "postMessage creates events with empty lastEventId"); + + switch (evt.data) + { + case "path-is-set": + chromePathIsSet(evt); + break; + + case "post-to-self": + checkSelf(evt); + break; + + case "post-to-content-response": + receiveContent(evt); + break; + + default: + ok(false, "unexpected message: " + evt.data); + finish(); + break; + } +} + + +/****************** + * SELF-RESPONDER * + ******************/ + +function checkSelf(evt) +{ + var prepath = getChromePrePath(window.location.href); + + is(evt.isTrusted, true, "should have sent a trusted event"); + is(evt.origin, prepath, "wrong origin for chrome: URL"); + is(evt.source, null, "chrome posters get a null source, for security"); + + window.frames.contentDomain.postMessage(prepath, "*"); +} + + +function chromePathIsSet(evt) +{ + window.frames.contentDomain.postMessage("post-to-content", + "http://example.org"); +} + +/************* + * RECEIVERS * + *************/ + +function receiveContent(evt) +{ + is(evt.isTrusted, true, "should have sent a trusted event"); + finish(); +} + + +/************** + * TEST SETUP * + **************/ + +function run() +{ + window.addEventListener("message", messageReceiver); + window.postMessage("post-to-self", "*"); +} + +window.addEventListener("load", run); +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/test_postMessage_closed.html b/dom/tests/mochitest/whatwg/test_postMessage_closed.html new file mode 100644 index 0000000000..01fc1294a3 --- /dev/null +++ b/dom/tests/mochitest/whatwg/test_postMessage_closed.html @@ -0,0 +1,77 @@ +<!DOCTYPE html> +<html> +<head> + <title>postMessage's interaction with closed windows</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p><a href="https://bugzilla.mozilla.org/show_bug.cgi?id=417075">Bug 417075</a></p> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<div id="holder"></div> + +<pre id="test"> +<script class="testbody" type="application/javascript"> + +SimpleTest.waitForExplicitFinish(); + +function receiveMessage(evt) +{ + is(evt.origin, "http://mochi.test:8888", "wrong origin"); + ok(evt.source === openedWindow, "wrong source"); + is(evt.lastEventId, "", "postMessage creates events with empty lastEventId"); + + is(evt.data, "message", "wrong data"); + if (evt.data !== "message") + return; // prevent recursion if bugs + + evt.source.close(); + + function afterClose() + { + document.removeEventListener("message", receiveMessage); + evt.source.postMessage("NOT-RECEIVED", "*"); + + var iframe = document.createElement("iframe"); + iframe.id = "insertedIframe"; + $("holder").appendChild(iframe); + iframe.addEventListener("load", iframeLoaded); + iframe.src = "postMessage_closed_helper.html?parent"; + } + + setTimeout(afterClose, 0); +} + +window.addEventListener("message", receiveMessage); + +function iframeLoaded(evt) +{ + var iframe = $("insertedIframe"); + iframe.removeEventListener("load", iframeLoaded); + + var iframeWindow = iframe.contentWindow; + $("holder").removeChild($("insertedIframe")); + iframeWindow.postMessage("NOT-RECEIVED", "*"); + + SimpleTest.finish(); +} + +var openedWindow; + +function run() +{ + openedWindow = window.open("postMessage_closed_helper.html?opener", "foobar"); + if (!openedWindow) + { + ok(false, "this test must be run with popup blocking disabled"); + SimpleTest.finish(); + } +} + +window.addEventListener("load", run); +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/test_postMessage_hash.html b/dom/tests/mochitest/whatwg/test_postMessage_hash.html new file mode 100644 index 0000000000..5942a200ca --- /dev/null +++ b/dom/tests/mochitest/whatwg/test_postMessage_hash.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<html> +<head> + <title>postMessage's interaction with hash URIs</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <base href="http://example.com/" /> +</head> +<body> +<p>(no bug; this is a preemptive test)</p> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe src="http://mochi.test:8888/tests/dom/tests/mochitest/whatwg/postMessage_hash.html#hash" + name="kid"></iframe> + +<pre id="test"> +<script class="testbody" type="application/javascript"> + +SimpleTest.waitForExplicitFinish(); + +function receiveMessage(evt) +{ + is(evt.origin, "http://mochi.test:8888", "wrong origin"); + ok(evt.source === window.frames.kid, "wrong source"); + is(evt.lastEventId, "", "postMessage creates events with empty lastEventId"); + + is(evt.data, "response-message", "wrong data"); + + SimpleTest.finish(); +} + +window.addEventListener("message", receiveMessage); + +function run() +{ + window.frames.kid.postMessage("from-parent", "http://mochi.test:8888"); +} + +window.addEventListener("load", run); +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/test_postMessage_idn.xhtml b/dom/tests/mochitest/whatwg/test_postMessage_idn.xhtml new file mode 100644 index 0000000000..79a6ede714 --- /dev/null +++ b/dom/tests/mochitest/whatwg/test_postMessage_idn.xhtml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage +--> +<head> + <title>postMessage uri/domain values and IDN encoding</title> + <script 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=postMessage">Mozilla Bug 387706</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe src="http://sub1.ält.example.org:8000/tests/dom/tests/mochitest/whatwg/postMessage_idn_helper.html"></iframe> + +<pre id="test"> +<script class="testbody" type="application/javascript"> +/** Test for Bug 387706 **/ + +SimpleTest.waitForExplicitFinish(); + +var responseReceived = false; +var idnWindow = null; + +function receiveMessage(evt) +{ + ok(evt instanceof MessageEvent, "umm, how did we get this?"); + is(evt.type, "message", "expected events of type 'message'"); + ok(evt.isTrusted === true, "should have been a trusted event"); + + is(evt.origin, "http://sub1.xn--lt-uia.example.org:8000", + "wrong origin -- IDN issue, perhaps?"); + + is(evt.data, "idn-response", "unexpected test result"); + is(evt.lastEventId, "", "postMessage creates events with empty lastEventId"); + ok(evt.source === idnWindow, "wrong source"); + + SimpleTest.finish(); +} +window.addEventListener("message", receiveMessage); + +var xhtmlns = "http://www.w3.org/1999/xhtml"; + +function setup() +{ + var idnFrame = document.getElementsByTagNameNS(xhtmlns, "iframe")[0]; + idnWindow = idnFrame.contentWindow; + try + { + idnWindow.postMessage("idn-message", "http://sub1.ält.example.org:8000"); + } + catch (e) + { + ok(false, "failed to post message: " + e); + SimpleTest.finish(); + } +} + +addLoadEvent(setup); +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/test_postMessage_joined.html b/dom/tests/mochitest/whatwg/test_postMessage_joined.html new file mode 100644 index 0000000000..560fbb2911 --- /dev/null +++ b/dom/tests/mochitest/whatwg/test_postMessage_joined.html @@ -0,0 +1,53 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage +--> +<head> + <title>postMessage with document.domain setting to join origins</title> + <script 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=postMessage">Mozilla Bug 387706</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe src="http://sub1.test1.example.org/tests/dom/tests/mochitest/whatwg/postMessage_joined_helper.html" + name="container"></iframe> + + +<pre id="test"> +<script class="testbody" type="application/javascript"> +/** Test for Bug 387706 **/ + +SimpleTest.waitForExplicitFinish(); + +function receiveTestResult(evt) +{ + ok(evt instanceof MessageEvent, "umm, how did we get this?"); + is(evt.type, "message", "expected events of type 'message'"); + ok(evt.isTrusted === true, "should have been a trusted event"); + + is(evt.lastEventId, "", "postMessage creates events with empty lastEventId"); + + // Either we passed the test or we failed it. The message's + // contents should help to diagnose the failure. Either way, + // consider this the end of the test. + is(evt.data, "test-passed", "unexpected test result"); + SimpleTest.finish(); +} + +function setup() +{ + window.addEventListener("message", receiveTestResult); + window.frames.container.postMessage("start-test", + "http://sub1.test1.example.org"); +} + +addLoadEvent(setup); + +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/test_postMessage_onOther.html b/dom/tests/mochitest/whatwg/test_postMessage_onOther.html new file mode 100644 index 0000000000..502ebca36c --- /dev/null +++ b/dom/tests/mochitest/whatwg/test_postMessage_onOther.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage +--> +<head> + <title>postMessage called through a different same-origin page</title> + <script 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=postMessage">Mozilla Bug 387706</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe src="http://example.com/tests/dom/tests/mochitest/whatwg/postMessage_onOther.html" + name="topDomainFrame"></iframe> +<iframe src="http://test1.example.com/tests/dom/tests/mochitest/whatwg/postMessage_onOther.html" + name="subDomainFrame"></iframe> + + +<pre id="test"> +<script class="testbody" type="application/javascript"> +/** Test for Bug 387706 **/ + +SimpleTest.waitForExplicitFinish(); + +/** Receives MessageEvents to this window. */ +function messageReceiver(evt) +{ + ok(evt instanceof MessageEvent, "wrong event type"); + is(evt.origin, "http://test1.example.com", "unexpected origin"); + is(evt.lastEventId, "", "postMessage creates events with empty lastEventId"); + is(evt.data, "test-finished", + "unexpected data in message"); + + SimpleTest.finish(); +} + +function run() +{ + window.frames.subDomainFrame.postMessage("start-test", + "http://test1.example.com"); +} + +window.addEventListener("message", messageReceiver); +window.addEventListener("load", run); +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/test_postMessage_origin.xhtml b/dom/tests/mochitest/whatwg/test_postMessage_origin.xhtml new file mode 100644 index 0000000000..dd027c09ff --- /dev/null +++ b/dom/tests/mochitest/whatwg/test_postMessage_origin.xhtml @@ -0,0 +1,465 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=417075 +--> +<head> + <title>postMessage from about:blank, data URLs</title> + <script 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=417075">Mozilla Bug 417075</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe src="http://mochi.test:8888/tests/dom/tests/mochitest/whatwg/postMessage_origin_helper.xhtml" + id="sameDomain"></iframe> +<iframe src="http://example.com/tests/dom/tests/mochitest/whatwg/postMessage_origin_helper.xhtml" + id="otherDomain"></iframe> +<iframe src="http://example.org:8000/tests/dom/tests/mochitest/whatwg/postMessage_origin_helper.xhtml" + id="otherDomainPort"></iframe> +<iframe src="ftp://mochi.test:27534/tests/dom/tests/mochitest/whatwg/postMessage_origin_helper.xhtml" + id="localNoExist"></iframe> + +<iframe src="http://sub1.παράδειγμα.δοκιμή/tests/dom/tests/mochitest/whatwg/postMessage_origin_helper.xhtml" + id="idnKidWhitelist"></iframe> + +<iframe src="http://sub1.exaмple.test/tests/dom/tests/mochitest/whatwg/postMessage_origin_helper.xhtml" + id="idnKidNoWhitelist"></iframe> + + +<pre id="test"> +<script class="testbody" type="application/javascript"><![CDATA[ +/** Test for Bug 417075 **/ + +SimpleTest.waitForExplicitFinish(); + +function errorCheck(i, called, error, actual) { + ok(!called, "receiver should not have been called for test #" + i); + is(actual, error, "wrong error thrown in test #" + i); +} + +// Sends a "PING" message to the given child frame content window and returns +// a promise which resolves when we receive a corresponding "PONG" message back. +function ping(win) { + if (win.parent !== window) { + throw new Error("Can only ping windows which are child frames of this window"); + } + return new Promise(resolve => { + window.addEventListener("message", function callback(event) { + if (event.source === win && event.data === "PONG") { + window.removeEventListener("message", callback); + resolve(); + } + }); + win.postMessage("PING", "*"); + }); +} + +var tests = + [ + // 0 + { + args: ["NOT-RECEIVED", ""], + source: "sameDomain", + name: "SyntaxError", + code: DOMException.SYNTAX_ERR + }, + { + args: ["NOT-RECEIVED", "null"], + source: "sameDomain", + name: "SyntaxError", + code: DOMException.SYNTAX_ERR + }, + { + args: ["NOT-RECEIVED", "a"], + source: "sameDomain", + name: "SyntaxError", + code: DOMException.SYNTAX_ERR + }, + { + args: ["NOT-RECEIVED", "http :"], + source: "sameDomain", + name: "SyntaxError", + code: DOMException.SYNTAX_ERR + }, + { + args: ["NOT-RECEIVED", "http: //"], + source: "sameDomain", + name: "SyntaxError", + code: DOMException.SYNTAX_ERR, + + hasThrowsNoExceptionBug: true + }, + // 5 + { + args: ["NOT-RECEIVED", "http ://"], + source: "sameDomain", + name: "SyntaxError", + code: DOMException.SYNTAX_ERR + }, + { + args: ["TODO", " http://localhost:8888"], + source: "sameDomain", + name: "SyntaxError", + code: DOMException.SYNTAX_ERR, + + returnOrigin: "http://mochi.test:8888", + hasThrowsNoExceptionBug: true + }, + { + args: ["NOT-RECEIVED", "hä"], + source: "sameDomain", + name: "SyntaxError", + code: DOMException.SYNTAX_ERR + }, + { + args: ["NOT-RECEIVED", "http://lo\0k.com"], + source: "sameDomain", + name: "SyntaxError", + code: DOMException.SYNTAX_ERR + }, + { + args: ["NOT-RECEIVED", "http: //localhost:8888"], + source: "sameDomain", + name: "SyntaxError", + code: DOMException.SYNTAX_ERR, + + hasThrowsNoExceptionBug: true + }, + // 10 + { + args: ["NOT-RECEIVED", "http://localhost :8888"], + source: "sameDomain", + name: "SyntaxError", + code: DOMException.SYNTAX_ERR + }, + { + args: ["NOT-RECEIVED", "http:// localhost:8888"], + source: "sameDomain", + name: "SyntaxError", + code: DOMException.SYNTAX_ERR, + + hasThrowsNoExceptionBug: true + }, + { + args: ["TODO", "http://\nlocalhost:8888"], + source: "sameDomain", + name: "SyntaxError", + code: DOMException.SYNTAX_ERR, + + returnOrigin: "http://mochi.test:8888", + hasThrowsNoExceptionBug: true + }, + { + args: ["TODO", "http://localhost:8888\0"], + source: "sameDomain", + name: "SyntaxError", + code: DOMException.SYNTAX_ERR, + + returnOrigin: "http://mochi.test:8888", + hasThrowsNoExceptionBug: true + }, + { + args: ["TODO", "http://localhost:8888\n"], + source: "sameDomain", + name: "SyntaxError", + code: DOMException.SYNTAX_ERR, + + returnOrigin: "http://mochi.test:8888", + hasThrowsNoExceptionBug: true + }, + // 15 + { + args: ["PASS", "*"], + source: "sameDomain", + returnOrigin: "http://mochi.test:8888" + }, + { + args: ["PASS", "http://mochi.test:8888"], + source: "sameDomain", + returnOrigin: "http://mochi.test:8888" + }, + { + args: ["PASS", "http://example.com"], + source: "otherDomain", + returnOrigin: "http://example.com" + }, + { + args: ["PASS", "http://example.com/"], + source: "otherDomain", + returnOrigin: "http://example.com" + }, + { + args: ["PASS", "http://example.com:80"], + source: "otherDomain", + returnOrigin: "http://example.com" + }, + // 20 + { + args: ["PASS", "http://example.com:80/"], + source: "otherDomain", + returnOrigin: "http://example.com" + }, + { + args: ["PASS", "http://example.com:80/foobar"], + source: "otherDomain", + returnOrigin: "http://example.com" + }, + { + args: ["PASS", "http://example.com/foobar"], + source: "otherDomain", + returnOrigin: "http://example.com" + }, + { + args: ["PASS", "http://example.com:8000"], + source: "otherDomain", + expectNoCallback: true + }, + { + args: ["PASS", "http://example.com:8000/"], + source: "otherDomain", + expectNoCallback: true + }, + // 25 + { + args: ["PASS", "http://example.org:8000"], + source: "otherDomainPort", + returnOrigin: "http://example.org:8000" + }, + { + args: ["PASS", "http://example.org:8000/"], + source: "otherDomainPort", + returnOrigin: "http://example.org:8000" + }, + { + args: ["PASS", "http://example.org:8000/tests/dom/test/mochitest/whatwg/postMessage_origin_helper.xhtml"], + source: "otherDomainPort", + returnOrigin: "http://example.org:8000" + }, + { + args: ["PASS", "http://example.org:8000/tests/dom/test/mochitest/whatwg/this_file_does_not_exist.xhtml"], + source: "otherDomainPort", + returnOrigin: "http://example.org:8000" + }, + { + args: ["PASS", "http://example.org"], + source: "otherDomainPort", + expectNoCallback: true + }, + // 30 + { + args: ["PASS", "http://example.org:80"], + source: "otherDomainPort", + expectNoCallback: true + }, + { + args: ["PASS", "http://example.org/"], + source: "otherDomainPort", + expectNoCallback: true + }, + { + args: ["PASS", "http://example.org"], + source: "otherDomain", + expectNoCallback: true + }, + { + args: ["PASS", "ftp://mochi.test:8888"], + source: "sameDomain", + expectNoCallback: true + }, + { + args: ["PASS", "http://mochi.test:8888"], + source: "sameDomain", + returnOrigin: "http://mochi.test:8888" + }, + // 35 + { + args: ["PASS", "http://mochi.test:27534"], + source: "sameDomain", + expectNoCallback: true + }, + { + args: ["PASS", "http://sub1.παράδειγμα.δοκιμή"], + source: "idnKidWhitelist", + returnOrigin: "http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp" + }, + { + args: ["PASS", "http://sub1.παράδειγμα.δοκιμή:80"], + source: "idnKidWhitelist", + returnOrigin: "http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp" + }, + { + args: ["PASS", "http://sub1.παράδειγμα.δοκιμή:80/"], + source: "idnKidWhitelist", + returnOrigin: "http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp" + }, + { + args: ["PASS", "http://sub1.παράδειγμα.δοκιμή:80/foobar"], + source: "idnKidWhitelist", + returnOrigin: "http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp" + }, + // 40 + { + args: ["PASS", "http://sub1.παράδειγμα.δοκιμή/foobar"], + source: "idnKidWhitelist", + returnOrigin: "http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp" + }, + { + args: ["PASS", "http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp"], + source: "idnKidWhitelist", + returnOrigin: "http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp" + }, + { + args: ["PASS", "http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp:80"], + source: "idnKidWhitelist", + returnOrigin: "http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp" + }, + { + args: ["PASS", "http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp:80/"], + source: "idnKidWhitelist", + returnOrigin: "http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp" + }, + { + args: ["PASS", "http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp:80/foo"], + source: "idnKidWhitelist", + returnOrigin: "http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp" + }, + // 45 + { + args: ["PASS", "http://sub1.exaмple.test"], + source: "idnKidNoWhitelist", + returnOrigin: "http://sub1.xn--exaple-kqf.test", + }, + { + args: ["PASS", "http://sub1.exaмple.test:80"], + source: "idnKidNoWhitelist", + returnOrigin: "http://sub1.xn--exaple-kqf.test", + }, + { + args: ["PASS", "http://sub1.exaмple.test:80/"], + source: "idnKidNoWhitelist", + returnOrigin: "http://sub1.xn--exaple-kqf.test", + }, + { + args: ["PASS", "http://sub1.exaмple.test/"], + source: "idnKidNoWhitelist", + returnOrigin: "http://sub1.xn--exaple-kqf.test", + }, + { + args: ["PASS", "http://sub1.exaмple.test/foobar"], + source: "idnKidNoWhitelist", + returnOrigin: "http://sub1.xn--exaple-kqf.test", + }, + // 50 + { + args: ["PASS", "http://sub1.xn--exaple-kqf.test"], + source: "idnKidNoWhitelist", + returnOrigin: "http://sub1.xn--exaple-kqf.test", + }, + { + args: ["PASS", "http://sub1.xn--exaple-kqf.test:80"], + source: "idnKidNoWhitelist", + returnOrigin: "http://sub1.xn--exaple-kqf.test", + }, + { + args: ["PASS", "http://sub1.xn--exaple-kqf.test:80/"], + source: "idnKidNoWhitelist", + returnOrigin: "http://sub1.xn--exaple-kqf.test", + }, + { + args: ["PASS", "http://sub1.xn--exaple-kqf.test/"], + source: "idnKidNoWhitelist", + returnOrigin: "http://sub1.xn--exaple-kqf.test", + }, + { + args: ["PASS", "http://sub1.xn--exaple-kqf.test/foobar"], + source: "idnKidNoWhitelist", + returnOrigin: "http://sub1.xn--exaple-kqf.test", + }, + ]; + +async function allTests() { + let target, called, test, i; + + let listener = evt => { + // Ignore "PONG" messages, which are used to ensure synchronization + // between scripts in parent and child frames. + if (evt.data === "PONG") { + return; + } + + is(test, tests[i], + "i and test are out of sync! async is hard, let's go shopping"); + + var originCheck = test.hasWrongReturnOriginBug ? todo_is : is; + originCheck(evt.origin, test.returnOrigin, "wrong origin for #" + i); + if (test.args[0] == "TODO") + todo_is(evt.data, "PASS", "wrong data"); + else + is(evt.data, "PASS", "wrong data"); + is(evt.lastEventId, "", + "postMessage creates events with empty lastEventId"); + ok(evt.source === target, "wrong source"); + called = true; + }; + window.addEventListener("message", listener); + + for ([i, test] of tests.entries()) { + called = false; + target = $(test.source).contentWindow; + try { + target.postMessage.apply(target, test.args); + } catch (e) { + // Since an exception was thrown, we know at this point that we're not + // waiting on anything else in the queue of script to run, and we can just + // continue immediately. + errorCheck(i, called, e.name, test.name); + errorCheck(i, called, e.code, test.code); + continue; + } + + // Perform a postMessage round-trip to ensure the target frame has + // processed our message and we've had time to receive its reply. + await ping(target); + + if (test.hasThrowsNoExceptionBug) { + todo(false, `should throw on test #${i}`); + } else { + is(!test.expectNoCallback, called, `should have been called #${i}`); + } + } + + window.removeEventListener("message", listener); +} + +async function oddballTests() { + for (let i = 0; i < 2; i++) { + let evt = await new Promise(resolve => { + window.addEventListener("message", function listener(evt) { + resolve(evt); + window.removeEventListener("message", listener); + }); + window.postMessage("PASS", "http://mochi.test:8888"); + }); + + is(evt.origin, "http://mochi.test:8888", "wrong sender"); + is(evt.data, "PASS", "wrong data"); + is(evt.lastEventId, "", + "postMessage creates events with empty lastEventId"); + ok(evt.source === window, "wrong source"); + } +} + +async function run() { + await oddballTests(); + await allTests(); + SimpleTest.finish(); +} + +window.addEventListener("load", run); +]]></script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/test_postMessage_override.html b/dom/tests/mochitest/whatwg/test_postMessage_override.html new file mode 100644 index 0000000000..8414344f8d --- /dev/null +++ b/dom/tests/mochitest/whatwg/test_postMessage_override.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage +--> +<head> + <title>postMessage override test</title> + <script 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=postMessage">Mozilla Bug 387706</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe src="http://example.org:8000/tests/dom/tests/mochitest/whatwg/postMessage_override_helper.html"></iframe> + + +<pre id="test"> +<script class="testbody" type="application/javascript"> +/** Test for Bug 387706 **/ + +SimpleTest.waitForExplicitFinish(); + +async function run() { + function ping(win, msg) { + return new Promise(resolve => { + window.addEventListener("message", evt => { + is(evt.source, win, "Response should come from the correct window"); + is(evt.data, msg, "Should get the correct response"); + resolve(); + }, { once: true }); + + win.postMessage(msg, "http://example.org:8000"); + }); + } + + await ping(frames[0], "PASS 1"); + await ping(frames[0], "PASS 2"); + SimpleTest.finish(); +} + +window.addEventListener("load", run); +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/test_postMessage_special.xhtml b/dom/tests/mochitest/whatwg/test_postMessage_special.xhtml new file mode 100644 index 0000000000..deb3930550 --- /dev/null +++ b/dom/tests/mochitest/whatwg/test_postMessage_special.xhtml @@ -0,0 +1,304 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage +--> +<head> + <title>postMessage from about:blank, data URLs</title> + <script 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=postMessage">Mozilla Bug 387706</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<pre id="test"> +<script class="testbody" type="application/javascript"><![CDATA[ +/** Test for Bug 387706 **/ + +SimpleTest.waitForExplicitFinish(); + +var B64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/** + * Encodes an array of bytes into a string using the base 64 encoding scheme. + * + * @param bytes + * An array of bytes to encode. + */ +function b64(str) +{ + var byteArray = new Array(str.length); + for (var i = 0, sz = str.length; i < sz; i++) + byteArray[i] = str.charCodeAt(i); + + var index = 0; + function get3Bytes() + { + if (byteArray.length - index < 3) + return null; // Less than three bytes remaining + + // Return the next three bytes in the array, and increment index for our + // next invocation + return byteArray.slice(index, index += 3); + } + + var out = ""; + var bytes = null; + while ((bytes = get3Bytes())) + { + var bits = 0; + for (var i = 0; i < 3; i++) + bits = (bits << 8) | bytes[i]; + for (var j = 18; j >= 0; j -= 6) + out += B64_CHARS[(bits>>j) & 0x3F]; + } + + // Get the remaining bytes + bytes = byteArray.slice(index); + + switch (bytes.length) + { + case 2: + out += B64_CHARS[(bytes[0]>>2) & 0x3F] + + B64_CHARS[((bytes[0] & 0x03) << 4) | ((bytes[1] >> 4) & 0x0F)] + + B64_CHARS[((bytes[1] & 0x0F) << 2)] + + "="; + break; + case 1: + out += B64_CHARS[(bytes[0]>>2) & 0x3F] + + B64_CHARS[(bytes[0] & 0x03) << 4] + + "=="; + break; + } + + return out; +} + + +var aboutBlankWindow = null; +var aboutBlank2Window = null; +var dataWindow = null; + +/** Convert a nullable string to a pretty representation */ +function sourceify(v) +{ + if (typeof v == "string") + return "'" + v + "'"; + return String(v); +} + +/** Receives MessageEvents to this window. */ +function messageReceiver(evt) +{ + // It's not clear what the security model is for data: URLs and whether they + // can access their parents; WebKit denies access, while Gecko currently + // allows it. We work around this problem by using postMessage (surprise!) + // to start the round of tests when each iframe loads. + if (evt.data === "next-test") + { + setTimeout(nextTest, 0); + return; + } + + + try + { + ok(evt instanceof MessageEvent, "umm, how did we get this?"); + is(evt.type, "message", "expected events of type 'message'"); + ok(evt.isTrusted === true, "should have been a trusted event"); + + if (evt.data === "about:blank-response") + { + // This isn't clarified in HTML5 yet, but the origin for a document which + // has been open()ed is the origin of the calling code, somewhat loosely + // speaking. For the specific case of about:blank it's also possible + // that the origin is determined by the code that opens the window. It's + // not codified yet which of these two causes the identifier tokens on + // the event generated by the new window to be those of this window, but + // in either case this is what they should be. + is(evt.origin, "http://mochi.test:8888", + "wrong origin for event from about:blank"); + is(evt.source, aboutBlankWindow, "wrong source"); + + // ...and onto the next test + setupBlank2(); + } + else if (evt.data === "about:blank2-response") + { + is(evt.origin, "http://mochi.test:8888", + "wrong origin for event from about:blank #2"); + is(evt.source, aboutBlank2Window, "wrong source"); + + setupData(); + } + else if (evt.data === "data-response") + { + is(evt.origin, "null", + "wrong origin for event from data URL (should be the origin of the " + + "window/script that opened the URL, in this case the origin of this " + + "file)"); + is(evt.source, dataWindow, "wrong source"); + + finish(); + } + else + { + ok(false, "unexpected message: " + evt.data); + } + } + catch (e) + { + ok(false, "error processing event with data '" + evt.data + "': " + e); + } +} + +function getContents(description, responseText) +{ + var contents = + "<!DOCTYPE html>\n" + + "<html>\n" + + "<head>\n" + + " <title>about:blank</title>\n" + + " <script type='application/javascript'>\n" + + "function receive(evt)\n" + + "{\n" + + " var response = '" + responseText + "';\n" + + "\n" + + " if (evt.source !== window.parent)\n" + + " response += ' wrong-source';\n" + + " if (evt.origin !== 'http://mochi.test:8888')\n" + + " response += ' wrong-origin(' + evt.origin + ')';\n" + + " if (evt.data !== 'from-opener')\n" + + " response += ' wrong-data(' + evt.data + ')';\n" + + "\n" + + " window.parent.postMessage(response, 'http://mochi.test:8888');\n" + + "}\n" + + "\n" + + "function ready()\n" + + "{\n" + + " window.parent.postMessage('next-test', 'http://mochi.test:8888');\n" + + "}\n" + + "\n" + + "window.addEventListener('load', ready, false);\n" + + "window.addEventListener('message', receive, false);\n" + + " </script>\n" + + "</head>\n" + + "<body><p>" + description + "</p></body>\n" + + "</html>"; + + return contents; +} + +function finish() +{ + SimpleTest.finish(); +} + +var xhtmlns = "http://www.w3.org/1999/xhtml"; + +function insert(el) +{ + var content = $("content"); + content.parentNode.insertBefore(el, content); +} + +function setupBlank() +{ + var aboutBlankFrame = document.createElementNS(xhtmlns, "iframe"); + aboutBlankFrame.setAttribute("src", "about:blank"); + insert(aboutBlankFrame); + + aboutBlankWindow = aboutBlankFrame.contentWindow; + var doc = aboutBlankWindow.document; + doc.open(); + doc.write(getContents("This was about:blank #1", "about:blank-response")); + doc.close(); + + // I don't believe anything guarantees sync parsing, so we have to wait for + // the new window to poke us to actually do the test. :-\ +} + +function setupBlank2() +{ + var aboutBlank2Frame = document.createElementNS(xhtmlns, "iframe"); + aboutBlank2Frame.addEventListener("load", nextTest); + aboutBlank2Frame.setAttribute("src", "about:blank"); + + insert(aboutBlank2Frame); +} + +// Could use window.btoa here, but that's not standardized, and we want to be +// able to run these tests against browsers that don't support it. +var dataURI = "data:text/html;base64," + + b64(getContents("A data: URL", "data-response")); + +function setupData() +{ + var dataFrame = document.createElementNS(xhtmlns, "iframe"); + dataFrame.setAttribute("src", dataURI); + insert(dataFrame); + + dataWindow = dataFrame.contentWindow; + + // ...and wait again for the window to load... +} + +var count = 0; +function nextTest() +{ + switch (count++) + { + case 0: + testBlank(); + break; + + case 1: + testBlank2(); + break; + + case 2: + testData(); + break; + + default: + ok(false, "unreached"); + break; + } +} + +function testBlank() +{ + aboutBlankWindow.postMessage("from-opener", "http://mochi.test:8888"); +} + +function testBlank2() +{ + // For some reason we can't access this across browsers prior to the iframe + // loading, so set its value here. + aboutBlank2Window = window.frames[1]; + + var doc = aboutBlank2Window.document; + + doc.body.textContent = "This was about:blank #2"; + + var script = doc.createElement("script"); + script.textContent = + "window.parent.postMessage('about:blank2-response', " + + " 'http://mochi.test:8888');"; + doc.body.appendChild(script); +} + +function testData() +{ + dataWindow.postMessage("from-opener", "*"); +} + +window.addEventListener("message", messageReceiver); + +addLoadEvent(setupBlank); +]]></script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/test_postMessage_structured_clone.html b/dom/tests/mochitest/whatwg/test_postMessage_structured_clone.html new file mode 100644 index 0000000000..9574dcacd4 --- /dev/null +++ b/dom/tests/mochitest/whatwg/test_postMessage_structured_clone.html @@ -0,0 +1,70 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=553125 +--> +<head> + <title>postMessage uses structured clone</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" + src="postMessage_structured_clone_helper.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=553125">Mozilla Bug 553125</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe src="http://mochi.test:8888/tests/dom/tests/mochitest/whatwg/postMessage_structured_clone_helper.html" + name="sameDomain"></iframe> +<iframe src="http://example.org:8000/tests/dom/tests/mochitest/whatwg/postMessage_structured_clone_helper.html" + name="crossDomain"></iframe> + + +<pre id="test"> +<script class="testbody" type="application/javascript"> +/** Test for Bug 553125 **/ + +SimpleTest.waitForExplicitFinish(); + +var lastMessage = null; +var crossOrigin = false; +var generator = getTestContent(); + +function runNextSameOrigin() { + let {done, value} = generator.next(); + if (done) { + generator = getTestContent(); + crossOrigin = true; + runNextCrossOrigin(); + return; + } + lastMessage = value; + window.frames.sameDomain.postMessage(lastMessage, "http://mochi.test:8888"); +} + +function runNextCrossOrigin() { + let {done, value} = generator.next(); + if (done) { + SimpleTest.finish(); + return; + } + lastMessage = value; + window.frames.crossDomain.postMessage(lastMessage, "http://example.org:8000"); +} + +function receiveMessage(evt) { + if (evt.data == "TEST-PASS") + SimpleTest.ok(true, "structured clone of | " + lastMessage + " | succeeded"); + else + SimpleTest.ok(false, "structured clone of | " + lastMessage + " | failed"); + setTimeout(crossOrigin ? runNextCrossOrigin : runNextSameOrigin, 0); +} + +window.addEventListener("message", receiveMessage); +window.addEventListener("load", runNextSameOrigin); + +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/test_postMessage_throw.html b/dom/tests/mochitest/whatwg/test_postMessage_throw.html new file mode 100644 index 0000000000..8d71ea38cf --- /dev/null +++ b/dom/tests/mochitest/whatwg/test_postMessage_throw.html @@ -0,0 +1,74 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage +--> +<head> + <title>postMessage with a thrown exception</title> + <script 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=postMessage">Mozilla Bug 387706</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe src="http://mochi.test:8888/tests/dom/tests/mochitest/whatwg/postMessage_throw_helper.html" + name="sameDomain"></iframe> +<iframe src="http://example.org:8000/tests/dom/tests/mochitest/whatwg/postMessage_throw_helper.html" + name="crossDomain"></iframe> + + +<pre id="test"> +<script class="testbody" type="application/javascript"> +/** Test for Bug 387706 **/ + +SimpleTest.waitForExplicitFinish(); + +function atLoad() +{ + try + { + sameDomain.postMessage("foo", "http://mochi.test:8888"); + ok(true, "should not have thrown for same-domain exception"); + } + catch (e) + { + ok(false, "uh-oh, threw a same-domain exception: " + e); + } + + setTimeout(next, 0); +} + +function next() +{ + ok(true, "no pending-exception wackiness for same-domain"); + setTimeout(next2, 0); +} + +function next2() +{ + try + { + crossDomain.postMessage("foo", "http://example.org:8000"); + ok(true, "should not have thrown for cross-domain exception"); + } + catch (e) + { + ok(false, "uh-oh, threw a cross-domain exception: " + e); + } + + setTimeout(next3, 0); +} + +function next3() +{ + ok(true, "no pending-exception wackiness for cross-domain"); + SimpleTest.finish(); +} + +addLoadEvent(atLoad); +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/test_postMessage_transfer.html b/dom/tests/mochitest/whatwg/test_postMessage_transfer.html new file mode 100644 index 0000000000..e7d8758bd6 --- /dev/null +++ b/dom/tests/mochitest/whatwg/test_postMessage_transfer.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=822094 +--> +<head><meta charset=utf-8> + <title>postMessage transferable tests</title> + <script 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=822094">Mozilla Bug 822094</a> +<p id="display"></p> +<div id="content" style="display: none"> + +<iframe src="postMessage_transfer_helper.html" + name="sameDomain"></iframe> +<iframe src="http://example.org:8000/tests/dom/tests/mochitest/whatwg/postMessage_transfer_helper.html" + name="crossDomain"></iframe> + +</div> +<pre id="test"> +<script class="testbody" type="application/javascript"> + +SimpleTest.waitForExplicitFinish(); + +var tests = [ + function() { testFunc(window, "http://mochi.test:8888"); }, + function() { testFunc(frames.sameDomain, "http://mochi.test:8888"); }, + function() { testFunc(frames.crossDomain, "http://example.org:8000"); }, + function() { SimpleTest.finish(); }, +]; + +function testFunc(target, origin) { + var ab = new ArrayBuffer(1); + var cd = new ArrayBuffer(1); + + target.postMessage([ab, cd], origin, [ab]); + is(ab.byteLength, 0, "ab should be detached"); + is(cd.byteLength, 1, "cd should not be detached"); + + onmessage = function(e) { + is(e.data[0].byteLength, 1, "ab should be transfered"); + is(e.data[1].byteLength, 1, "cd should be cloned"); + nextTest(); + }; +} + +function nextTest() { + var t = tests.shift(); + t(); +}; + +onload = function() { + nextTest(); +}; + +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/test_postMessage_userpass.html b/dom/tests/mochitest/whatwg/test_postMessage_userpass.html new file mode 100644 index 0000000000..b47266fc45 --- /dev/null +++ b/dom/tests/mochitest/whatwg/test_postMessage_userpass.html @@ -0,0 +1,46 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=417075 +--> +<head> + <title>postMessage from a page with username/password in its URI</title> + <script 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=417075">Mozilla Bug 417075</a> +<p id="display"></p> +<div id="content" style="display: none"></div> + +<iframe src="http://bobhope:password@example.org/tests/dom/tests/mochitest/whatwg/postMessage_userpass_helper.html" + name="userPassKid"></iframe> + + +<pre id="test"> +<script class="testbody" type="application/javascript"> +/** Test for Bug 417075 **/ + +SimpleTest.waitForExplicitFinish(); + +function receiveMessage(evt) +{ + is(evt.origin, "http://example.org", "wrong origin"); + is(evt.data, "child-message", "wrong data"); + is(evt.lastEventId, "", "postMessage creates events with empty lastEventId"); + ok(evt.source === window.frames.userPassKid, "wrong source"); + SimpleTest.finish(); +} + +window.addEventListener("message", receiveMessage); + +function sendMessage(evt) +{ + window.frames.userPassKid.postMessage("parent-message", "http://example.org"); +} + +window.addEventListener("load", sendMessage); +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/whatwg/test_structuredCloneAndExposed.html b/dom/tests/mochitest/whatwg/test_structuredCloneAndExposed.html new file mode 100644 index 0000000000..2bd15247af --- /dev/null +++ b/dom/tests/mochitest/whatwg/test_structuredCloneAndExposed.html @@ -0,0 +1,307 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1828264 +--> +<head> + <title>Basic structured clone tests</title> + <script 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=1828264">Mozilla Bug 1828264</a> +<p id="display"> +<iframe id="frame"></iframe> +<image href="four-colors.png" height="320" width="240"/> +</p> +<div id="content" style="display: none"></div> + + +<pre id="test"> +<script class="testbody" type="application/javascript"> + +SimpleTest.waitForExplicitFinish(); + +class WebIDLGlobal { + globalNames; + resolve; + #otherSide; + + constructor(globalNames, initSides) { + this.globalNames = globalNames; + let global = this; + this.#otherSide = initSides().then(([ourSide, otherSide, cleanup]) => { + if (cleanup) { + SimpleTest.registerCleanupFunction(cleanup); + } + ourSide.addEventListener("message", ({ data }) => { + global.resultReceived(data); + }); + return otherSide; + }); + } + + get otherSide() { + return this.#otherSide; + } + + waitForResult() { + ok(!this.resolve, "Still waiting on previous message?"); + return new Promise(resolve => { + this.resolve = resolve; + }); + } + resultReceived(value) { + this.resolve(value); + this.resolve = undefined; + } +} + +// Init functions return an array containing in order the object to use for +// sending a messages to the other global, the object to use to listen for +// messages from the other global and optioanlly a cleanup function. + +function waitForInitMessage(target) { + return new Promise(resolve => { + target.addEventListener("message", resolve, { once: true }); + }); +} + +function getModuleURL(additionalScript = "") { + return new URL( + `file_structuredCloneAndExposed.sjs?additionalScript=${encodeURIComponent( + additionalScript + )}`, + location.href + ); +} + +function initFrame() { + let callInstallListeners = ` + installListeners(globalThis, parent); + `; + frame.srcdoc = + `<script src="${getModuleURL(callInstallListeners)}" type="module"></` + + `script>`; + return waitForInitMessage(self).then(() => [self, frame.contentWindow]); +} + +function initDedicatedWorker() { + let callInstallListeners = ` + installListeners(globalThis, globalThis); + `; + const worker = new Worker(getModuleURL(callInstallListeners), { + type: "module", + }); + return waitForInitMessage(worker).then(() => [worker, worker]); +} + +function initSharedWorker() { + let callInstallListeners = ` + self.addEventListener("connect", (e) => { + const port = e.ports[0]; + port.start(); + installListeners(port, port); + }); + `; + const worker = new SharedWorker(getModuleURL(callInstallListeners), { + type: "module", + }); + worker.port.start(); + return waitForInitMessage(worker.port).then(() => [worker.port, worker.port]); +} + +function initServiceWorker() { + let callInstallListeners = ` + addEventListener("message", ({ source: client }) => { + installListeners(globalThis, client); + }, { once: true }); + `; + + return navigator.serviceWorker + .register(getModuleURL(callInstallListeners), { type: "module" }) + .then(registration => { + let worker = + registration.installing || registration.waiting || registration.active; + worker.postMessage("installListeners"); + return waitForInitMessage(navigator.serviceWorker).then(() => [ + navigator.serviceWorker, + worker, + () => registration.unregister(), + ]); + }); +} + +function initAudioWorklet() { + const exporter = ` + export { installListeners }; + `; + + const workletSource = ` + import { installListeners } from "${getModuleURL(exporter)}"; + + class CustomAudioWorkletProcessor extends AudioWorkletProcessor { + constructor() { + super(); + this.port.start(); + installListeners(this.port, this.port, "AudioWorklet"); + } + + process(inputs, outputs, params) { + // Do nothing, output silence + } + } + + registerProcessor("custom-audio-worklet-processor", CustomAudioWorkletProcessor); + `; + let workletURL = URL.createObjectURL( + new Blob([workletSource], { type: "text/javascript" }) + ); + + // We need to keep the context alive while we're using the message ports. + globalThis.context = new OfflineAudioContext(1, 64, 8000); + return context.audioWorklet.addModule(workletURL).then(() => { + let node = new AudioWorkletNode(context, "custom-audio-worklet-processor"); + node.port.start(); + return waitForInitMessage(node.port).then(() => [ + node.port, + node.port, + () => { + node.port.close(); + delete globalThis.context; + }, + ]); + }); +} + +const globals = [ + ["Window", ["Window"], initFrame], + [ + "DedicatedWorkerGlobalScope", + ["Worker", "DedicatedWorker"], + initDedicatedWorker, + ], + ["SharedWorkerGlobalScope", ["Worker", "SharedWorker"], initSharedWorker], + ["ServiceWorkerGlobalScope", ["Worker", "ServiceWorker"], initServiceWorker], + // WorkerDebuggerGlobalScope can only receive string messages. + ["AudioWorkletGlobalScope", ["Worklet", "AudioWorklet"], initAudioWorklet], + // PaintWorkletGlobalScope doesn't have a messaging mechanism +].map(([global, globalNames, init]) => [ + global, + new WebIDLGlobal(globalNames, init), +]); + + +function generateCertificate() { + return RTCPeerConnection.generateCertificate({ + name: "ECDSA", + namedCurve: "P-256", + }); +} + +function makeCanvas() { + const width = 20; + const height = 20; + let canvas = new OffscreenCanvas(width, height); + let ctx = canvas.getContext("2d"); + ctx.fillStyle = "rgba(50, 100, 150, 255)"; + ctx.fillRect(0, 0, width, height); + return canvas; +} + +function makeVideoFrame() { + return new VideoFrame(makeCanvas(), { timestamp: 1, alpha: "discard" }); +} + +function makeImageBitmap() { + return makeCanvas().transferToImageBitmap(); +} + +const serializable = [ + ["DOMException", ["Window", "Worker"], () => new DOMException()], + ["DOMMatrixReadOnly", ["Window", "Worker"], () => new DOMMatrixReadOnly()], + ["ImageBitmap", ["Window", "Worker"], makeImageBitmap], + ["RTCCertificate", ["Window"], generateCertificate], + ["VideoFrame", ["Window", "DedicatedWorker"], makeVideoFrame], +]; + +const transferable = [ + [ + "MessagePort", + ["Window", "Worker", "AudioWorklet"], + () => new MessageChannel().port1, + ], + ["ImageBitmap", ["Window", "Worker"], makeImageBitmap], + ["ReadableStream", ["*"], () => new ReadableStream()], + ["VideoFrame", ["Window", "DedicatedWorker"], makeVideoFrame], +]; + + +function isExposed(exposure, global) { + if (exposure.length === 1 && exposure[0] === "*") { + return true; + } + return !!global.globalNames.filter(v => exposure.includes(v)).length; +} + + +async function runTest() { + await SpecialPowers.pushPrefEnv({ set: [["dom.media.webcodecs.enabled", true]] }); + + async function testDOMClass(domClass, exposure, createObject, transferable) { + for ([globalName, webidlGlobal] of globals) { + info( + `Testing ${ + transferable ? "transfer" : "serialization" + } of ${domClass} with ${globalName}` + ); + + let object = await createObject(); + let otherSide = await webidlGlobal.otherSide; + let options = { targetOrigin: "*" }; + let message; + if (transferable) { + options.transfer = [object]; + } else { + message = object; + } + otherSide.postMessage(message, options); + + let result = await webidlGlobal.waitForResult(); + let expected = isExposed(exposure, webidlGlobal); + + if ( + domClass === "ImageBitmap" && + globalName === "ServiceWorkerGlobalScope" + ) { + // Service workers don't support transferring shared memory objects + // (see ServiceWorker::PostMessage). + expected = false; + } + + is( + result, + expected, + `Deserialization for ${domClass} in ${globalName} ${ + expected ? "should" : "shouldn't" + } succeed` + ); + } + } + + for ([domClass, exposure, createObject] of serializable) { + await testDOMClass(domClass, exposure, createObject, false); + } + for ([domClass, exposure, createObject] of transferable) { + await testDOMClass(domClass, exposure, createObject, true); + } + + SimpleTest.finish(); +} + +runTest(); + +</script> +</pre> +</body> +</html> |