summaryrefslogtreecommitdiffstats
path: root/dom/html/test/test_anchor_ping.html
diff options
context:
space:
mode:
Diffstat (limited to 'dom/html/test/test_anchor_ping.html')
-rw-r--r--dom/html/test/test_anchor_ping.html309
1 files changed, 309 insertions, 0 deletions
diff --git a/dom/html/test/test_anchor_ping.html b/dom/html/test/test_anchor_ping.html
new file mode 100644
index 000000000..4a39bcefe
--- /dev/null
+++ b/dom/html/test/test_anchor_ping.html
@@ -0,0 +1,309 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=786347
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 786347</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript;version=1.8">
+
+ /** Test for Bug 786347 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://gre/modules/Promise.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
+
+addLoadEvent(function () {
+ Task.spawn(function run_tests() {
+ while (tests.length) {
+ let test = tests.shift();
+ info("-- running " + test.name);
+ yield Task.spawn(test);
+ }
+
+ SimpleTest.finish();
+ });
+});
+
+let tests = [
+
+ // Ensure that sending pings is enabled.
+ function* setup() {
+ Services.prefs.setBoolPref("browser.send_pings", true);
+ Services.prefs.setIntPref("browser.send_pings.max_per_link", -1);
+ Services.prefs.setBoolPref("security.mixed_content.block_active_content", false);
+ // The server we create can't handle the priming HEAD requests
+ Services.prefs.setBoolPref("security.mixed_content.send_hsts_priming", false);
+
+ SimpleTest.registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("browser.send_pings");
+ Services.prefs.clearUserPref("browser.send_pings.max_per_link");
+ Services.prefs.clearUserPref("security.mixed_content.block_active_content");
+ Services.prefs.clearUserPref("security.mixed_content.send_hsts_priming");
+ });
+ },
+
+ // If both the address of the document containing the hyperlink being audited
+ // and ping URL have the same origin then the request must include a Ping-From
+ // HTTP header with, as its value, the address of the document containing the
+ // hyperlink, and a Ping-To HTTP header with, as its value, the target URL.
+ // The request must not include a Referer (sic) HTTP header.
+ function* same_origin() {
+ let from = "/ping-from/" + Math.random();
+ let to = "/ping-to/" + Math.random();
+ let ping = "/ping/" + Math.random();
+
+ let base;
+ let server = new HttpServer();
+
+ // The page that contains the link.
+ createFromPathHandler(server, from, to, () => ping);
+
+ // The page that the link's href points to.
+ let promiseHref = createToPathHandler(server, to);
+
+ // The ping we want to receive.
+ let promisePing = createPingPathHandler(server, ping, () => {
+ return {from: base + from, to: base + to};
+ });
+
+ // Start the server, get its base URL and run the test.
+ server.start(-1);
+ base = "http://localhost:" + server.identity.primaryPort;
+ navigate(base + from);
+
+ // Wait until the target and ping url have loaded.
+ yield Promise.all([promiseHref, promisePing]);
+
+ // Cleanup.
+ yield stopServer(server);
+ },
+
+ // If the origins are different, but the document containing the hyperlink
+ // being audited was not retrieved over an encrypted connection then the
+ // request must include a Referer (sic) HTTP header with, as its value, the
+ // address of the document containing the hyperlink, a Ping-From HTTP header
+ // with the same value, and a Ping-To HTTP header with, as its value, target
+ // URL.
+ function* diff_origin() {
+ let from = "/ping-from/" + Math.random();
+ let to = "/ping-to/" + Math.random();
+ let ping = "/ping/" + Math.random();
+
+ // We will use two servers to simulate two different origins.
+ let base, base2;
+ let server = new HttpServer();
+ let server2 = new HttpServer();
+
+ // The page that contains the link.
+ createFromPathHandler(server, from, to, () => base2 + ping);
+
+ // The page that the link's href points to.
+ let promiseHref = createToPathHandler(server, to);
+
+ // Start the first server and get its base URL.
+ server.start(-1);
+ base = "http://localhost:" + server.identity.primaryPort;
+
+ // The ping we want to receive.
+ let promisePing = createPingPathHandler(server2, ping, () => {
+ return {referrer: base + from, from: base + from, to: base + to};
+ });
+
+ // Start the second server, get its base URL and run the test.
+ server2.start(-1);
+ base2 = "http://localhost:" + server2.identity.primaryPort;
+ navigate(base + from);
+
+ // Wait until the target and ping url have loaded.
+ yield Promise.all([promiseHref, promisePing]);
+
+ // Cleanup.
+ yield stopServer(server);
+ yield stopServer(server2);
+ },
+
+ // If the origins are different and the document containing the hyperlink
+ // being audited was retrieved over an encrypted connection then the request
+ // must include a Ping-To HTTP header with, as its value, target URL. The
+ // request must neither include a Referer (sic) HTTP header nor include a
+ // Ping-From HTTP header.
+ function* diff_origin_secure_referrer() {
+ let ping = "/ping/" + Math.random();
+ let server = new HttpServer();
+
+ // The ping we want to receive.
+ let promisePing = createPingPathHandler(server, ping, () => {
+ return {to: "https://example.com/"};
+ });
+
+ // Start the server and run the test.
+ server.start(-1);
+
+ // The referrer will be loaded using a secure channel.
+ navigate("https://example.com/chrome/dom/html/test/" +
+ "file_anchor_ping.html?" + "http://localhost:" +
+ server.identity.primaryPort + ping);
+
+ // Wait until the ping has been sent.
+ yield promisePing;
+
+ // Cleanup.
+ yield stopServer(server);
+ },
+
+ // Test that the <a ping> attribute is properly tokenized using ASCII white
+ // space characters as separators.
+ function* tokenize_white_space() {
+ let from = "/ping-from/" + Math.random();
+ let to = "/ping-to/" + Math.random();
+
+ let base;
+ let server = new HttpServer();
+
+ let pings = [
+ "/ping1/" + Math.random(),
+ "/ping2/" + Math.random(),
+ "/ping3/" + Math.random(),
+ "/ping4/" + Math.random()
+ ];
+
+ // The page that contains the link.
+ createFromPathHandler(server, from, to, () => {
+ return " " + pings[0] + " \r " + pings[1] + " \t " +
+ pings[2] + " \n " + pings[3] + " ";
+ });
+
+ // The page that the link's href points to.
+ let promiseHref = createToPathHandler(server, to);
+
+ // The pings we want to receive.
+ let pingPathHandlers = createPingPathHandlers(server, pings, () => {
+ return {from: base + from, to: base + to};
+ });
+
+ // Start the server, get its base URL and run the test.
+ server.start(-1);
+ base = "http://localhost:" + server.identity.primaryPort;
+ navigate(base + from);
+
+ // Wait until the target and ping url have loaded.
+ yield Promise.all([promiseHref, ...pingPathHandlers]);
+
+ // Cleanup.
+ yield stopServer(server);
+ }
+];
+
+// Navigate the iframe used for testing to a new URL.
+function navigate(uri) {
+ document.getElementById("frame").src = uri;
+}
+
+// Registers a path handler for the given server that will serve a page
+// containing an <a ping> element. The page will automatically simulate
+// clicking the link after it has loaded.
+function createFromPathHandler(server, path, href, lazyPing) {
+ server.registerPathHandler(path, function (request, response) {
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.setHeader("Content-Type", "text/html;charset=utf-8", false);
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ let body = '<body onload="document.body.firstChild.click()">' +
+ '<a href="' + href + '" ping="' + lazyPing() + '"></a></body>';
+ response.write(body);
+ });
+}
+
+// Registers a path handler for the given server that will serve a simple empty
+// page we can use as the href attribute for links. It returns a promise that
+// will be resolved once the page has been requested.
+function createToPathHandler(server, path) {
+ let deferred = Promise.defer();
+
+ server.registerPathHandler(path, function (request, response) {
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.setHeader("Content-Type", "text/html;charset=utf-8", false);
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.write("OK");
+
+ deferred.resolve();
+ });
+
+ return deferred.promise;
+}
+
+// Register multiple path handlers for the given server that will receive
+// pings as sent when an <a ping> element is clicked. This method uses
+// createPingPathHandler() defined below to ensure all headers are sent
+// and received as expected.
+function createPingPathHandlers(server, paths, lazyHeaders) {
+ return Array.from(paths, (path) => createPingPathHandler(server, path, lazyHeaders));
+}
+
+// Registers a path handler for the given server that will receive pings as
+// sent when an <a ping> element has been clicked. It will check that the
+// correct http method has been used, the post data is correct and all headers
+// are given as expected. It returns a promise that will be resolved once the
+// ping has been received.
+function createPingPathHandler(server, path, lazyHeaders) {
+ let deferred = Promise.defer();
+
+ server.registerPathHandler(path, function (request, response) {
+ let headers = lazyHeaders();
+
+ is(request.method, "POST", "correct http method used");
+ is(request.getHeader("Ping-To"), headers.to, "valid ping-to header");
+
+ if ("from" in headers) {
+ is(request.getHeader("Ping-From"), headers.from, "valid ping-from header");
+ } else {
+ ok(!request.hasHeader("Ping-From"), "no ping-from header");
+ }
+
+ if ("referrer" in headers) {
+ is(request.getHeader("Referer"), headers.referrer, "valid referer header");
+ } else {
+ ok(!request.hasHeader("Referer"), "no referer header");
+ }
+
+ let bs = request.bodyInputStream;
+ let body = NetUtil.readInputStreamToString(bs, bs.available());
+ is(body, "PING", "correct body sent");
+
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.setHeader("Content-Type", "text/html;charset=utf-8", false);
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.write("OK");
+
+ deferred.resolve();
+ });
+
+ return deferred.promise;
+}
+
+// Returns a promise that is resolved when the given http server instance has
+// been stopped.
+function stopServer(server) {
+ let deferred = Promise.defer();
+ server.stop(deferred.resolve);
+ return deferred.promise;
+}
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=786347">Mozilla Bug 786347</a>
+<p id="display"></p>
+<iframe id="frame" />
+</body>
+</html>