summaryrefslogtreecommitdiffstats
path: root/dom/tests/mochitest/beacon
diff options
context:
space:
mode:
Diffstat (limited to 'dom/tests/mochitest/beacon')
-rw-r--r--dom/tests/mochitest/beacon/beacon-frame.html24
-rw-r--r--dom/tests/mochitest/beacon/beacon-handler.sjs149
-rw-r--r--dom/tests/mochitest/beacon/beacon-originheader-handler.sjs45
-rw-r--r--dom/tests/mochitest/beacon/beacon-preflight-handler.sjs38
-rw-r--r--dom/tests/mochitest/beacon/beacon-redirect-handler.sjs46
-rw-r--r--dom/tests/mochitest/beacon/beacon-set-cookie.sjs10
-rw-r--r--dom/tests/mochitest/beacon/chrome.ini10
-rw-r--r--dom/tests/mochitest/beacon/file_beaconCookies.html8
-rw-r--r--dom/tests/mochitest/beacon/file_beaconSafelist.html11
-rw-r--r--dom/tests/mochitest/beacon/mochitest.ini22
-rw-r--r--dom/tests/mochitest/beacon/test_beacon.html60
-rw-r--r--dom/tests/mochitest/beacon/test_beaconContentPolicy.html103
-rw-r--r--dom/tests/mochitest/beacon/test_beaconCookies.html82
-rw-r--r--dom/tests/mochitest/beacon/test_beaconFrame.html118
-rw-r--r--dom/tests/mochitest/beacon/test_beaconOriginHeader.html66
-rw-r--r--dom/tests/mochitest/beacon/test_beaconPreflightWithCustomContentType.html57
-rw-r--r--dom/tests/mochitest/beacon/test_beaconRedirect.html57
-rw-r--r--dom/tests/mochitest/beacon/test_beaconWithSafelistedContentType.html102
18 files changed, 1008 insertions, 0 deletions
diff --git a/dom/tests/mochitest/beacon/beacon-frame.html b/dom/tests/mochitest/beacon/beacon-frame.html
new file mode 100644
index 0000000000..f50e04e90a
--- /dev/null
+++ b/dom/tests/mochitest/beacon/beacon-frame.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Inner frame performing a basic sendBeacon from within an iframe</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<script type="text/javascript">
+
+function sendBeacon()
+{
+ var frame = window.parent.document.getElementById("frame");
+ var data = window.parent.beaconConvert(frame.getAttribute("data"));
+
+ var result = navigator.sendBeacon("http://mochi.test:8888/tests/dom/tests/mochitest/beacon/beacon-handler.sjs", data);
+ window.parent.beaconSent(result);
+}
+
+window.addEventListener("load", function() { setTimeout(sendBeacon, 0); });
+
+</script>
+</body>
+</html>
+
diff --git a/dom/tests/mochitest/beacon/beacon-handler.sjs b/dom/tests/mochitest/beacon/beacon-handler.sjs
new file mode 100644
index 0000000000..a1e851ac5b
--- /dev/null
+++ b/dom/tests/mochitest/beacon/beacon-handler.sjs
@@ -0,0 +1,149 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const CC = Components.Constructor;
+const BinaryInputStream = CC(
+ "@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream"
+);
+
+function DEBUG(str) {
+ // dump("********** " + str + "\n");
+}
+
+function setOurState(data) {
+ x = {
+ data,
+ QueryInterface(iid) {
+ return this;
+ },
+ };
+ x.wrappedJSObject = x;
+ setObjectState("beacon-handler", x);
+ DEBUG("our state is " + data);
+}
+
+function getOurState() {
+ var data;
+ getObjectState("beacon-handler", function (x) {
+ // x can be null if no one has set any state yet
+ if (x) {
+ data = x.wrappedJSObject.data;
+ }
+ });
+ return data;
+}
+
+function handleRequest(request, response) {
+ DEBUG("Entered request handler");
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ function finishControlResponse(response) {
+ DEBUG("********* sending out the control GET response");
+ var data = getState("beaconData");
+ var mimetype = getState("beaconMimetype");
+ DEBUG("GET was sending : " + data + "\n");
+ DEBUG("GET was sending : " + mimetype + "\n");
+ var result = {
+ data,
+ mimetype,
+ };
+ response.write(JSON.stringify(result));
+ setOurState(null);
+ }
+
+ if (request.method == "GET") {
+ DEBUG(" ------------ GET --------------- ");
+ response.setHeader("Content-Type", "application/json", false);
+ switch (request.queryString) {
+ case "getLastBeaconCors":
+ // Allow CORS responses of the last beacon
+ var originHeader = request.getHeader("origin");
+ response.setHeader(
+ "Access-Control-Allow-Headers",
+ "content-type",
+ false
+ );
+ response.setHeader("Access-Control-Allow-Methods", "POST, GET", false);
+ response.setHeader("Access-Control-Allow-Origin", originHeader, false);
+ response.setHeader("Access-Control-Allow-Credentials", "true", false);
+ // fallthrough
+ case "getLastBeacon":
+ var state = getOurState();
+ if (state === "unblocked") {
+ finishControlResponse(response);
+ } else {
+ DEBUG("GET has arrived, but POST has not, blocking response!");
+ setOurState(response);
+ response.processAsync();
+ }
+ break;
+ default:
+ response.setStatusLine(request.httpVersion, 400, "Bad Request");
+ break;
+ }
+ return;
+ }
+
+ if (request.method == "POST") {
+ DEBUG(" ------------ POST --------------- ");
+ var body = new BinaryInputStream(request.bodyInputStream);
+ var avail;
+ var bytes = [];
+
+ while ((avail = body.available()) > 0) {
+ Array.prototype.push.apply(bytes, body.readByteArray(avail));
+ }
+
+ var data = "";
+ for (var i = 0; i < bytes.length; i++) {
+ // We are only passing strings at this point.
+ if (bytes[i] < 32) {
+ continue;
+ }
+ var charcode = String.fromCharCode(bytes[i]);
+ data += charcode;
+ }
+
+ var mimetype = "";
+ if (request.hasHeader("Content-Type")) {
+ mimetype = request.getHeader("Content-Type");
+ }
+
+ // check to see if this is form data.
+ if (mimetype.indexOf("multipart/form-data") != -1) {
+ // trim the mime type to make testing easier.
+ mimetype = "multipart/form-data";
+ // Extract only the form-data name.
+
+ var pattern = /; name=\"(.+)\";/;
+ data = data.split(pattern)[1];
+ }
+
+ DEBUG("********** POST was sending : " + data + "\n");
+ DEBUG("********** POST was sending : " + mimetype + "\n");
+ setState("beaconData", data);
+ setState("beaconMimetype", mimetype);
+
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write("ok");
+
+ var blockedResponse = getOurState();
+ if (typeof blockedResponse == "object" && blockedResponse) {
+ DEBUG("GET is already pending, finishing!");
+ finishControlResponse(blockedResponse);
+ blockedResponse.finish();
+ } else {
+ DEBUG("GET has not arrived, marking it as unblocked");
+ setOurState("unblocked");
+ }
+
+ return;
+ }
+
+ response.setStatusLine(request.httpVersion, 402, "Bad Request");
+}
diff --git a/dom/tests/mochitest/beacon/beacon-originheader-handler.sjs b/dom/tests/mochitest/beacon/beacon-originheader-handler.sjs
new file mode 100644
index 0000000000..96304187a8
--- /dev/null
+++ b/dom/tests/mochitest/beacon/beacon-originheader-handler.sjs
@@ -0,0 +1,45 @@
+/*
+ * TestSever customized specifically for the needs of:
+ * Bug 1280692 - navigator.sendBeacon() should not send origin header
+ */
+
+function handleRequest(request, response) {
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/plain", false);
+
+ // case XHR-REQUEST: the xhr-request tries to query the
+ // stored header from the beacon request.
+ if (request.queryString == "queryheader") {
+ var header = getState("originHeader");
+ // if the beacon already stored the header - return.
+ if (header) {
+ response.write(header);
+ setState("originHeader", "");
+ return;
+ }
+ // otherwise wait for the beacon request
+ response.processAsync();
+ setObjectState("xhr-response", response);
+ return;
+ }
+
+ // case BEACON-REQUEST: get the beacon header and
+ // store the header on the server.
+ var header = "reset";
+ try {
+ header = request.getHeader("origin");
+ } catch (e) {
+ header = "no-header";
+ }
+ setState("originHeader", header);
+
+ // if there is an xhr-request waiting, return the header now.
+ getObjectState("xhr-response", function (xhrResponse) {
+ if (!xhrResponse) {
+ return;
+ }
+ setState("originHeader", "");
+ xhrResponse.write(header);
+ xhrResponse.finish();
+ });
+}
diff --git a/dom/tests/mochitest/beacon/beacon-preflight-handler.sjs b/dom/tests/mochitest/beacon/beacon-preflight-handler.sjs
new file mode 100644
index 0000000000..f4a89b9828
--- /dev/null
+++ b/dom/tests/mochitest/beacon/beacon-preflight-handler.sjs
@@ -0,0 +1,38 @@
+function handleRequest(request, response) {
+ response.setHeader("Cache-Control", "no-cache, must-revalidate", false);
+
+ if (request.queryString === "verify") {
+ var preflightState = getState("preflight");
+ response.write(preflightState === "done" ? "green" : "red");
+ return;
+ }
+
+ var originHeader = request.getHeader("origin");
+ response.setHeader("Access-Control-Allow-Headers", "content-type", false);
+ response.setHeader("Access-Control-Allow-Methods", "POST, GET", false);
+ response.setHeader("Access-Control-Allow-Origin", originHeader, false);
+ response.setHeader("Access-Control-Allow-Credentials", "true", false);
+
+ if (request.queryString === "beacon") {
+ if (request.method == "OPTIONS") {
+ setState("preflight", "done");
+ response.setStatusLine(null, 200, "OK");
+ return;
+ }
+ response.setStatusLine(null, 200, "OK");
+ response.write("DONE");
+ return;
+ }
+
+ if (request.queryString === "fail") {
+ if (request.method == "OPTIONS") {
+ setState("preflight", "done");
+ response.setStatusLine(null, 400, "Bad Request");
+ return;
+ }
+ setState("preflight", "oops");
+ response.setStatusLine(null, 200, "OK");
+ response.write("DONE");
+ return;
+ }
+}
diff --git a/dom/tests/mochitest/beacon/beacon-redirect-handler.sjs b/dom/tests/mochitest/beacon/beacon-redirect-handler.sjs
new file mode 100644
index 0000000000..5496353588
--- /dev/null
+++ b/dom/tests/mochitest/beacon/beacon-redirect-handler.sjs
@@ -0,0 +1,46 @@
+/*
+ * TestSever customized specifically for the needs of:
+ * Bug 1280692 - sendBeacon() should follow 30x redirect
+ *
+ * Here is a sequence of the test:
+ * [1] sendBeacon (identified by the queryString 'beacon') which gets redirected
+ * [2] redirected sendBeacon (identified by the queryString 'redirected') which
+ * updates the state idniciating that redirected sendBeacon succeeds.
+ * [3] xhr request (identified by the queryString 'verifyRedirectDidSucceed')
+ * which checks if the state was not changed from 'reset' to 'gree'. If the channel
+ * woulnd't be blocked correctly the redirected channel would set the state to 'red'.
+ *
+ */
+
+function handleRequest(request, response) {
+ response.setHeader("Cache-Control", "no-cache, must-revalidate", false);
+
+ // [Sequence 3]
+ if (request.queryString === "verifyRedirectDidSucceed") {
+ var redirectState = getState("redirectState");
+ response.write(redirectState);
+ return;
+ }
+
+ // [Sequence 1]
+ if (request.queryString === "beacon") {
+ setState("redirectState", "reset");
+ var newLocation =
+ "http://mochi.test:8888/tests/dom/tests/mochitest/beacon/beacon-redirect-handler.sjs?redirected";
+ response.setStatusLine("1.1", 302, "Found");
+ response.setHeader("Location", newLocation, false);
+ return;
+ }
+
+ // [Sequence 2]
+ if (request.queryString === "redirected") {
+ setState("redirectState", "green");
+ response.setStatusLine(null, 200, "OK");
+ return;
+ }
+
+ // we should never get here, but just in case let's
+ // set the state to something unexpected
+ setState("redirectState", "red");
+ response.setStatusLine(null, 200, "OK");
+}
diff --git a/dom/tests/mochitest/beacon/beacon-set-cookie.sjs b/dom/tests/mochitest/beacon/beacon-set-cookie.sjs
new file mode 100644
index 0000000000..e2dce0b3c6
--- /dev/null
+++ b/dom/tests/mochitest/beacon/beacon-set-cookie.sjs
@@ -0,0 +1,10 @@
+function handleRequest(request, response) {
+ response.setHeader(
+ "Set-Cookie",
+ "cookie=" + request.host + "~" + Math.random()
+ );
+ response.setHeader("Content-Type", "text/plain", false);
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ response.setStatusLine(request.httpVersion, 200, "OK");
+}
diff --git a/dom/tests/mochitest/beacon/chrome.ini b/dom/tests/mochitest/beacon/chrome.ini
new file mode 100644
index 0000000000..27f8f93952
--- /dev/null
+++ b/dom/tests/mochitest/beacon/chrome.ini
@@ -0,0 +1,10 @@
+[DEFAULT]
+skip-if = os == 'android'
+
+[test_beaconCookies.html]
+skip-if = (verify && !debug && (os == 'win'))
+support-files = beacon-set-cookie.sjs
+ file_beaconCookies.html
+[test_beaconWithSafelistedContentType.html]
+support-files = beacon-handler.sjs
+ file_beaconSafelist.html
diff --git a/dom/tests/mochitest/beacon/file_beaconCookies.html b/dom/tests/mochitest/beacon/file_beaconCookies.html
new file mode 100644
index 0000000000..aeecb2263e
--- /dev/null
+++ b/dom/tests/mochitest/beacon/file_beaconCookies.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<script class="testbody" type="text/javascript">
+
+var beaconUrl = "http://mochi.test:8888/chrome/dom/tests/mochitest/beacon/beacon-set-cookie.sjs";
+
+navigator.sendBeacon(beaconUrl, "ceci n'est pas une demande");
+
+</script>
diff --git a/dom/tests/mochitest/beacon/file_beaconSafelist.html b/dom/tests/mochitest/beacon/file_beaconSafelist.html
new file mode 100644
index 0000000000..0079ed5074
--- /dev/null
+++ b/dom/tests/mochitest/beacon/file_beaconSafelist.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<script class="testbody" type="text/javascript">
+
+var beaconUrl = "http://example.com/tests/dom/tests/mochitest/beacon/beacon-handler.sjs?beacon";
+
+var value = ["text"];
+var blob = new Blob(value, {type: "application/x-www-form-urlencoded"});
+navigator.sendBeacon(beaconUrl, blob);
+var intervalID = null;
+
+</script>
diff --git a/dom/tests/mochitest/beacon/mochitest.ini b/dom/tests/mochitest/beacon/mochitest.ini
new file mode 100644
index 0000000000..9f51091c52
--- /dev/null
+++ b/dom/tests/mochitest/beacon/mochitest.ini
@@ -0,0 +1,22 @@
+[DEFAULT]
+support-files = beacon-frame.html
+ beacon-handler.sjs
+ beacon-preflight-handler.sjs
+ beacon-originheader-handler.sjs
+ beacon-redirect-handler.sjs
+
+[test_beacon.html]
+[test_beaconFrame.html]
+skip-if =
+ http3
+[test_beaconPreflightWithCustomContentType.html]
+skip-if =
+ http3
+[test_beaconContentPolicy.html]
+[test_beaconOriginHeader.html]
+skip-if =
+ verify
+ http3
+[test_beaconRedirect.html]
+skip-if =
+ http3
diff --git a/dom/tests/mochitest/beacon/test_beacon.html b/dom/tests/mochitest/beacon/test_beacon.html
new file mode 100644
index 0000000000..4df99cfe8e
--- /dev/null
+++ b/dom/tests/mochitest/beacon/test_beacon.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=936340
+-->
+<head>
+ <title>Test whether sendBeacon fails for non-HTTP URIs and syntactically incorrect calls</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=936340">Mozilla Bug 936340</a>
+<p id="display"></p>
+
+<div id="content">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, beginTest);
+
+function beginTest() {
+ var threw;
+ for(let scheme of ["bad", "ftp", "data"]) {
+ try {
+ is(false, navigator.sendBeacon(`${scheme}://example.com`, "0"));
+ threw = false;
+ } catch (ex) {
+ threw = true;
+ }
+ ok(threw, `sendBeacon not supported for ${scheme} scheme.`);
+ }
+
+ for(let scheme of ["http", "https"]) {
+ try {
+ is(false, navigator.sendBeacon(`${scheme}://invalid:URL`, "0"));
+ threw = false;
+ } catch (ex) {
+ threw = true;
+ }
+ ok(threw, `sendBeacon not supported for invalid ${scheme} URLs.`);
+ }
+
+ try {
+ is(false, navigator.sendBeacon());
+ threw = false;
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw, "sendBeacon needs more parameters.");
+
+ SimpleTest.finish()
+}
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/tests/mochitest/beacon/test_beaconContentPolicy.html b/dom/tests/mochitest/beacon/test_beaconContentPolicy.html
new file mode 100644
index 0000000000..0e1bce574e
--- /dev/null
+++ b/dom/tests/mochitest/beacon/test_beaconContentPolicy.html
@@ -0,0 +1,103 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=936340
+-->
+<head>
+ <title>Test that sendBeacon obeys content policy directives</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=936340">Mozilla Bug 936340</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+var beaconUrl = "http://mochi.test:8888/tests/dom/tests/mochitest/beacon/beacon-handler.sjs";
+
+const Cc = SpecialPowers.Cc;
+const Ci = SpecialPowers.Ci;
+
+// not enabled by default yet.
+SimpleTest.waitForExplicitFinish();
+
+var policy;
+
+SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, beginTest);
+
+function setupPolicy() {
+ var policyID = SpecialPowers.wrap(SpecialPowers.Components).ID("{b80e19d0-878f-d41b-2654-194714a4115c}");
+ var policyName = "@mozilla.org/testpolicy;1";
+ var policy = {
+ // nsISupports implementation
+ QueryInterface(iid) {
+ iid = SpecialPowers.wrap(iid);
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIFactory) ||
+ iid.equals(Ci.nsIContentPolicy))
+ return this;
+ throw SpecialPowers.Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+ // nsIFactory implementation
+ createInstance(iid) {
+ return this.QueryInterface(iid);
+ },
+
+ // nsIContentPolicy implementation
+ shouldLoad(contentLocation, loadInfo, mimeTypeGuess) {
+ // Remember last content type seen for the test url
+ let contentType = loadInfo.externalContentPolicyType;
+
+ if (SpecialPowers.wrap(contentLocation).spec == beaconUrl) {
+ is(contentType, Ci.nsIContentPolicy.TYPE_BEACON, "Beacon content type should match expected. is: " + contentType + " should be: " + Ci.nsIContentPolicy.TYPE_BEACON);
+ teardownPolicy();
+ SimpleTest.finish();
+ }
+
+ return Ci.nsIContentPolicy.ACCEPT;
+ },
+
+ shouldProcess(contentLocation, loadInfo, mimeTypeGuess) {
+ return Ci.nsIContentPolicy.ACCEPT;
+ }
+ }
+ policy = SpecialPowers.wrapCallbackObject(policy);
+
+ // Register content policy
+ var componentManager = SpecialPowers.wrap(SpecialPowers.Components).manager.QueryInterface(Ci.nsIComponentRegistrar);
+ componentManager.registerFactory(policyID, "Test content policy", policyName, policy);
+
+ var categoryManager = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
+ categoryManager.addCategoryEntry("content-policy", policyName, policyName, false, true);
+
+ return { 'policy': policy, 'policyID': policyID, 'policyName': policyName };
+}
+
+function teardownPolicy() {
+ setTimeout(function() {
+ // policy will not be removed from the category correctly
+ var componentManager = SpecialPowers.wrap(SpecialPowers.Components).manager.QueryInterface(Ci.nsIComponentRegistrar);
+ componentManager.unregisterFactory(policy.policyID, policy.policy);
+ var categoryManager = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
+ categoryManager.deleteCategoryEntry("content-policy", policy.policyName, false);
+ }, 0);
+}
+
+function beginTest() {
+ policy = setupPolicy();
+ // Make sure to hit the event loop here in order to ensure that nsContentPolicy
+ // has been notified of the newly registered policy.
+ SimpleTest.executeSoon(function() {
+ navigator.sendBeacon(beaconUrl, "bacon would have been a better name than beacon");
+ });
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/tests/mochitest/beacon/test_beaconCookies.html b/dom/tests/mochitest/beacon/test_beaconCookies.html
new file mode 100644
index 0000000000..192e1395f5
--- /dev/null
+++ b/dom/tests/mochitest/beacon/test_beaconCookies.html
@@ -0,0 +1,82 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=936340
+-->
+<head>
+ <title>Test whether sendBeacon sets cookies</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=936340">Mozilla Bug 936340</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+// not enabled by default yet.
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, beginTest);
+
+const {BrowserTestUtils} = ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm");
+
+var baseURL = "http://mochi.test:8888/chrome/dom/tests/mochitest/beacon/";
+
+function whenDelayedStartupFinished(aWindow, aCallback) {
+ Services.obs.addObserver(function observer(aSubject, aTopic) {
+ if (aWindow == aSubject) {
+ Services.obs.removeObserver(observer, aTopic);
+ setTimeout(aCallback, 0);
+ }
+ }, "browser-delayed-startup-finished");
+}
+
+function testOnWindow(options, callback) {
+ var mainWindow = window.browsingContext.topChromeWindow;
+
+ var win = mainWindow.OpenBrowserWindow(options);
+ windowsToClose.push(win);
+ whenDelayedStartupFinished(win, function() {
+ callback(win);
+ });
+};
+
+var windowsToClose = [];
+var next;
+
+function beginTest() {
+ testOnWindow({}, function(aNormalWindow) {
+ Services.obs.addObserver(function waitCookie() {
+ Services.obs.removeObserver(waitCookie, "cookie-changed");
+ ok(true, "cookie set by beacon request in normal window");
+ testOnPrivateWindow();
+ }, "cookie-changed");
+ BrowserTestUtils.loadURIString(aNormalWindow.gBrowser.selectedBrowser, baseURL + "file_beaconCookies.html");
+ });
+}
+
+function testOnPrivateWindow() {
+ testOnWindow({private: true}, function(aPrivateWindow) {
+ Services.obs.addObserver(function waitCookie() {
+ Services.obs.removeObserver(waitCookie, "private-cookie-changed");
+ ok(true, "private cookie set by beacon request in private window");
+ cleanup();
+ }, "private-cookie-changed");
+ BrowserTestUtils.loadURIString(aPrivateWindow.gBrowser.selectedBrowser, baseURL + "file_beaconCookies.html");
+ });
+}
+
+function cleanup() {
+ for (var i = 0; i < windowsToClose.length; ++i) {
+ windowsToClose[i].close();
+ }
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/tests/mochitest/beacon/test_beaconFrame.html b/dom/tests/mochitest/beacon/test_beaconFrame.html
new file mode 100644
index 0000000000..940cb9ecf6
--- /dev/null
+++ b/dom/tests/mochitest/beacon/test_beaconFrame.html
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=936340
+-->
+<head>
+ <title>Test for beacon</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=936340">Mozilla Bug 936340</a>
+<p id="display"></p>
+
+<div id="content">
+</div>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+// not enabled by default yet.
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, runNextTest);
+
+function getBeaconServerStatus(callback) {
+ var request = new XMLHttpRequest();
+ request.open("GET", "http://mochi.test:8888/tests/dom/tests/mochitest/beacon/beacon-handler.sjs?getLastBeacon", true);
+ request.onload = function() {
+ if (request.readyState === request.DONE) {
+ callback(request.responseText);
+ }
+ };
+ request.send(null);
+}
+
+function createIframeWithData(data, mimetype, convert) {
+ beaconConvert = convert;
+
+ var frame = document.createElement("IFRAME");
+ frame.setAttribute("src", "beacon-frame.html");
+ frame.id = "frame";
+ frame.setAttribute("data", data.toString());
+ frame.setAttribute("mimetype", mimetype);
+ var c = document.getElementById("content");
+ c.appendChild(frame);
+}
+
+function beaconSent(result) {
+ // This function gets called from beacon-frame.html in the inner frame
+ // Check that the beacon was actually sent
+ ok(result, "Beacon was not sent")
+
+ // remove the frame.
+ var frame = document.getElementById("frame");
+ var data = frame.getAttribute("data");
+ var mimetype = frame.getAttribute("mimetype");
+
+ var c = document.getElementById("content");
+ c.removeChild(frame);
+
+ getBeaconServerStatus( function(response) {
+ console.log(response);
+ var result = JSON.parse(response);
+
+ is(result.data, data, "Beacon status should match expected. is: " + result.data + " should be: " + data);
+ is(result.mimetype, mimetype, "Beacon mimetype should match expected. is: " + result.mimetype + " should be: " + mimetype);
+
+ runNextTest();
+ });
+}
+
+function runNextTest() {
+ var test = tests.shift();
+ setTimeout(test, 0);
+}
+
+var beaconConvert = function() {};
+
+function stringToArrayBuffer(input) {
+
+ var buffer = new ArrayBuffer(input.length * 2);
+ var array = new Uint16Array(buffer);
+
+ // dumbly copy over the bytes
+ for (var i = 0, len = input.length; i < len; i++) {
+ array[i] = input.charCodeAt(i);
+ }
+ return array;
+}
+
+function stringToBlob(input) {
+ var blob = new Blob([input], {type : 'text/html'});
+ return blob;
+}
+
+function stringToFormData(input) {
+ var formdata = new FormData();
+ formdata.append(input, new Blob(['hi']));
+ return formdata;
+}
+
+function identity(data) {
+ return data;
+}
+
+var tests = [
+ function() { createIframeWithData("hi!", "text/plain;charset=UTF-8", identity); },
+ function() { createIframeWithData("123", "", stringToArrayBuffer); },
+ function() { createIframeWithData("abc", "text/html", stringToBlob); },
+ function() { createIframeWithData("qwerty", "multipart/form-data", stringToFormData); },
+ function() { SimpleTest.finish(); },
+];
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/tests/mochitest/beacon/test_beaconOriginHeader.html b/dom/tests/mochitest/beacon/test_beaconOriginHeader.html
new file mode 100644
index 0000000000..177ca94dba
--- /dev/null
+++ b/dom/tests/mochitest/beacon/test_beaconOriginHeader.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1280692 - navigator.sendBeacon() should send origin header</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="visibility: hidden">
+ <iframe style="width:100%;" id="testframe"></iframe>
+ </div>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+const BEACON_URL = "http://example.com/tests/dom/tests/mochitest/beacon/beacon-originheader-handler.sjs";
+// server returns any origin-header or 'no-header' if there is no header sent.
+const ORIGIN_HEADER = "http://mochi.test:8888";
+
+/* Description of the test:
+ * We call sendBeacon() cross origin and make sure that the
+ * origin header is actually set in the request.
+ *
+ * Since sendBeacon() does not expect any response, we are storing any
+ * header on the server (*.sjs) and use an XMLHttpRequest to actually
+ * retrieve the potentially set header back from the server. We assert
+ * that the header is indeed *not* sent with the request. Since sendBeacon()
+ * and also the XMLHttpRequest() are performed in an asynchronous fashion,
+ * there is no guarantee that the sendBeacon() is actually executed before
+ * the XMLHttpRequest().
+ * Hence the xhr-response might be processed asynchronously.
+ */
+
+SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, runTest);
+
+function queryHeaderFromServer() {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", "beacon-originheader-handler.sjs?queryheader", true);
+ xhr.onload = function() {
+ is(xhr.responseText, ORIGIN_HEADER, "SendBeacon should send origin header");
+ SimpleTest.finish();
+ };
+ xhr.onerror = function() {
+ ok(false, "xhr request returned error");
+ SimpleTest.finish();
+ };
+ xhr.send();
+}
+
+function runTest() {
+ // generate data and send beacon
+ var formData = new FormData();
+ formData.append('name', 'value');
+ navigator.sendBeacon(BEACON_URL, formData);
+
+ // start quering the result from the server
+ queryHeaderFromServer();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/tests/mochitest/beacon/test_beaconPreflightWithCustomContentType.html b/dom/tests/mochitest/beacon/test_beaconPreflightWithCustomContentType.html
new file mode 100644
index 0000000000..d1007b78c5
--- /dev/null
+++ b/dom/tests/mochitest/beacon/test_beaconPreflightWithCustomContentType.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1210302
+-->
+<head>
+ <title>Test for Bug 1210302</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=1210302">Mozilla Bug 936340</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+var beaconUrl = "http://example.com/tests/dom/tests/mochitest/beacon/beacon-preflight-handler.sjs?beacon";
+
+var intervalID = null;
+
+function queryIfBeaconSucceeded() {
+ clearInterval(intervalID);
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", "beacon-preflight-handler.sjs?verify", true);
+ xhr.onload = function() {
+ is(xhr.responseText, "green", "SendBeacon should have succeeded after preflight!");
+ SimpleTest.finish();
+ };
+ xhr.onerror = function() {
+ ok(false, "xhr request returned error");
+ SimpleTest.finish();
+ };
+ xhr.send();
+}
+
+// not enabled by default yet.
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, beginTest);
+
+function beginTest() {
+ var abv = new Uint8Array([0,1,2,3]);
+ var blob = new Blob(abv, {type: "application/badness, text/plain"});
+ var sent = navigator.sendBeacon(beaconUrl, blob);
+ ok(sent, "sending the beacon should start successfully");
+
+ // we have to make sure sending the beacon did not fail, so
+ // we have to wait for 2 seconds before we can query the result.
+ intervalID = setInterval(queryIfBeaconSucceeded, 2000);
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/tests/mochitest/beacon/test_beaconRedirect.html b/dom/tests/mochitest/beacon/test_beaconRedirect.html
new file mode 100644
index 0000000000..210b7a1e98
--- /dev/null
+++ b/dom/tests/mochitest/beacon/test_beaconRedirect.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1280692 - sendBeacon() should follow 30x redirect</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="visibility: hidden">
+ <iframe style="width:100%;" id="testframe"></iframe>
+ </div>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * We do perform a non simple sendBeacon request which should not use CORS and should follow
+ * a 30x cross origin redirect, which is allowed by the spec.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+const BEACON_URL = "http://example.com/tests/dom/tests/mochitest/beacon/beacon-redirect-handler.sjs?beacon";
+
+SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, runTest);
+
+var intervalID = null;
+
+function queryIfRedirectSucceeded() {
+ clearInterval(intervalID);
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", "beacon-redirect-handler.sjs?verifyRedirectDidSucceed", true);
+ xhr.onload = function() {
+ is(xhr.responseText, "green", "SendBeacon should follow cross origin redirects!");
+ SimpleTest.finish();
+ };
+ xhr.onerror = function() {
+ ok(false, "xhr request returned error");
+ SimpleTest.finish();
+ };
+ xhr.send();
+}
+
+function runTest() {
+ var data = new Uint8Array([0,1,2,3]);
+ navigator.sendBeacon(BEACON_URL, data);
+
+ // we have to make sure the channel did follow the redirect hence
+ // we have to wait for 4 seconds before we can query the result.
+ intervalID = setInterval(queryIfRedirectSucceeded, 4000);
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/tests/mochitest/beacon/test_beaconWithSafelistedContentType.html b/dom/tests/mochitest/beacon/test_beaconWithSafelistedContentType.html
new file mode 100644
index 0000000000..d2348d7892
--- /dev/null
+++ b/dom/tests/mochitest/beacon/test_beaconWithSafelistedContentType.html
@@ -0,0 +1,102 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1557386
+-->
+<head>
+ <title>Test for Bug 1557386</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1557386">Mozilla Bug 1557386</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+// not enabled by default yet.
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, beginTest);
+
+const {BrowserTestUtils} = ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm");
+const { clearInterval, setInterval } = ChromeUtils.import(
+ "resource://gre/modules/Timer.jsm"
+);
+
+function whenDelayedStartupFinished(aWindow, aCallback) {
+ Services.obs.addObserver(function observer(aSubject, aTopic) {
+ if (aWindow == aSubject) {
+ Services.obs.removeObserver(observer, aTopic);
+ setTimeout(aCallback, 0);
+ }
+ }, "browser-delayed-startup-finished");
+}
+
+let baseURL = "http://mochi.test:8888/chrome/dom/tests/mochitest/beacon/";
+let windowsToClose = [];
+let listener = msg => {
+ if (!(msg instanceof Ci.nsIConsoleMessage)) {
+ return;
+ }
+ if (msg.message.match("Cross-Origin Request Blocked:")) {
+ ok(false, "CORS response console warning found");
+ }
+};
+
+function beginTest() {
+ Services.console.registerListener(listener);
+
+ let mainWindow = window.docShell.rootTreeItem.domWindow;
+
+ let win = mainWindow.OpenBrowserWindow({});
+ windowsToClose.push(win);
+ whenDelayedStartupFinished(win, async function() {
+ Services.obs.addObserver(function waitCookie() {
+ Services.obs.removeObserver(waitCookie, "cookie-changed");
+ ok(true, "cookie set by beacon request in normal window");
+ testOnPrivateWindow();
+ }, "cookie-changed");
+ let testURL = baseURL + "file_beaconSafelist.html";
+ BrowserTestUtils.loadURIString(win.gBrowser.selectedBrowser, testURL);
+ await BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser, false, testURL);
+ await SpecialPowers.spawn(win.gBrowser.selectedBrowser, [], async () => {
+ await new Promise((resolve, reject) => {
+ let intervalId;
+ function queryIfBeaconSucceeded() {
+ content.clearInterval(intervalId);
+ let xhr = new content.XMLHttpRequest();
+ let baseURL = "http://example.com/tests/dom/tests/mochitest/beacon/";
+ let checkUrl = baseURL + "beacon-handler.sjs?getLastBeaconCors";
+ xhr.open("GET", checkUrl, true);
+ xhr.onload = function() {
+ let res = JSON.parse(xhr.responseText);
+ is(res.data, "text", "Got correct data");
+ resolve();
+ };
+ xhr.onerror = function() {
+ ok(false, "Error getting last beacon");
+ reject();
+ };
+ xhr.send();
+ }
+ intervalID = content.setInterval(queryIfBeaconSucceeded, 2000);
+ });
+ });
+ cleanup();
+ });
+}
+
+function cleanup() {
+ Services.console.unregisterListener(listener);
+ for (let i = 0; i < windowsToClose.length; ++i) {
+ windowsToClose[i].close();
+ }
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>