diff options
Diffstat (limited to 'dom/tests/mochitest/beacon')
18 files changed, 1028 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..c675049225 --- /dev/null +++ b/dom/tests/mochitest/beacon/beacon-preflight-handler.sjs @@ -0,0 +1,37 @@ +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"); + } +} 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.toml b/dom/tests/mochitest/beacon/chrome.toml new file mode 100644 index 0000000000..92a95b0515 --- /dev/null +++ b/dom/tests/mochitest/beacon/chrome.toml @@ -0,0 +1,15 @@ +[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.toml b/dom/tests/mochitest/beacon/mochitest.toml new file mode 100644 index 0000000000..84e85fdbf2 --- /dev/null +++ b/dom/tests/mochitest/beacon/mochitest.toml @@ -0,0 +1,37 @@ +[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_beaconContentPolicy.html"] + +["test_beaconFrame.html"] +skip-if = [ + "http3", + "http2", +] + +["test_beaconOriginHeader.html"] +skip-if = [ + "verify", + "http3", + "http2", +] + +["test_beaconPreflightWithCustomContentType.html"] +skip-if = [ + "http3", + "http2", +] + +["test_beaconRedirect.html"] +skip-if = [ + "http3", + "http2", +] 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..07f1080681 --- /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) { + // 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) { + 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..5e634664eb --- /dev/null +++ b/dom/tests/mochitest/beacon/test_beaconCookies.html @@ -0,0 +1,84 @@ +<!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.importESModule( + "resource://testing-common/BrowserTestUtils.sys.mjs" +); + +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.startLoadingURIString(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.startLoadingURIString(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..054732c8fd --- /dev/null +++ b/dom/tests/mochitest/beacon/test_beaconWithSafelistedContentType.html @@ -0,0 +1,101 @@ +<!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.importESModule( + "resource://testing-common/BrowserTestUtils.sys.mjs" +); + +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.startLoadingURIString(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> |