summaryrefslogtreecommitdiffstats
path: root/dom/tests/mochitest/whatwg
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /dom/tests/mochitest/whatwg
parentInitial commit. (diff)
downloadfirefox-upstream/124.0.1.tar.xz
firefox-upstream/124.0.1.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/tests/mochitest/whatwg')
-rw-r--r--dom/tests/mochitest/whatwg/chrome.toml3
-rw-r--r--dom/tests/mochitest/whatwg/file_bug500328_1.html51
-rw-r--r--dom/tests/mochitest/whatwg/file_bug500328_2.html17
-rw-r--r--dom/tests/mochitest/whatwg/file_structuredCloneAndExposed.sjs22
-rw-r--r--dom/tests/mochitest/whatwg/mochitest.toml122
-rw-r--r--dom/tests/mochitest/whatwg/postMessage_chrome_helper.html49
-rw-r--r--dom/tests/mochitest/whatwg/postMessage_closed_helper.html26
-rw-r--r--dom/tests/mochitest/whatwg/postMessage_hash.html31
-rw-r--r--dom/tests/mochitest/whatwg/postMessage_helper.html102
-rw-r--r--dom/tests/mochitest/whatwg/postMessage_idn_helper.html41
-rw-r--r--dom/tests/mochitest/whatwg/postMessage_joined_helper.html85
-rw-r--r--dom/tests/mochitest/whatwg/postMessage_joined_helper2.html71
-rw-r--r--dom/tests/mochitest/whatwg/postMessage_onOther.html109
-rw-r--r--dom/tests/mochitest/whatwg/postMessage_origin_helper.xhtml42
-rw-r--r--dom/tests/mochitest/whatwg/postMessage_override_helper.html37
-rw-r--r--dom/tests/mochitest/whatwg/postMessage_structured_clone_helper.html106
-rw-r--r--dom/tests/mochitest/whatwg/postMessage_structured_clone_helper.js62
-rw-r--r--dom/tests/mochitest/whatwg/postMessage_throw_helper.html24
-rw-r--r--dom/tests/mochitest/whatwg/postMessage_transfer_helper.html15
-rw-r--r--dom/tests/mochitest/whatwg/postMessage_userpass_helper.html34
-rw-r--r--dom/tests/mochitest/whatwg/test_MessageEvent.html102
-rw-r--r--dom/tests/mochitest/whatwg/test_MessageEvent_dispatchToOther.html59
-rw-r--r--dom/tests/mochitest/whatwg/test_bug477323.html64
-rw-r--r--dom/tests/mochitest/whatwg/test_bug500328.html769
-rw-r--r--dom/tests/mochitest/whatwg/test_document_scripts.html55
-rw-r--r--dom/tests/mochitest/whatwg/test_postMessage.html161
-rw-r--r--dom/tests/mochitest/whatwg/test_postMessage_basehref.html45
-rw-r--r--dom/tests/mochitest/whatwg/test_postMessage_chrome.html114
-rw-r--r--dom/tests/mochitest/whatwg/test_postMessage_closed.html77
-rw-r--r--dom/tests/mochitest/whatwg/test_postMessage_hash.html44
-rw-r--r--dom/tests/mochitest/whatwg/test_postMessage_idn.xhtml66
-rw-r--r--dom/tests/mochitest/whatwg/test_postMessage_joined.html53
-rw-r--r--dom/tests/mochitest/whatwg/test_postMessage_onOther.html51
-rw-r--r--dom/tests/mochitest/whatwg/test_postMessage_origin.xhtml465
-rw-r--r--dom/tests/mochitest/whatwg/test_postMessage_override.html47
-rw-r--r--dom/tests/mochitest/whatwg/test_postMessage_special.xhtml304
-rw-r--r--dom/tests/mochitest/whatwg/test_postMessage_structured_clone.html70
-rw-r--r--dom/tests/mochitest/whatwg/test_postMessage_throw.html74
-rw-r--r--dom/tests/mochitest/whatwg/test_postMessage_transfer.html61
-rw-r--r--dom/tests/mochitest/whatwg/test_postMessage_userpass.html46
-rw-r--r--dom/tests/mochitest/whatwg/test_structuredCloneAndExposed.html307
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 &lt;base&gt; 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>