<!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); SimpleTest.registerCleanupFunction(() => { Services.prefs.clearUserPref("browser.send_pings"); Services.prefs.clearUserPref("browser.send_pings.max_per_link"); }); }, // 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://127.0.0.1:" + 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>