<!
DOCTYPE HTML>
<
html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=786347
-->
<
head>
<
meta charset=
"utf-8">
<
title>Test for Bug 786347</
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"/>
<
script type=
"application/javascript">
/** Test for Bug 786347 **/
SimpleTest.waitForExplicitFinish();
const {NetUtil} = ChromeUtils.importESModule(
"resource://gre/modules/NetUtil.sys.mjs"
);
const {HttpServer} = ChromeUtils.importESModule(
"resource://testing-common/httpd.sys.mjs"
);
addLoadEvent(function () {
(async function run_tests() {
while (tests.length) {
let test = tests.shift();
info(
"-- running " + test.name);
await 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.
async 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.
await Promise.all([promiseHref, promisePing]);
// Cleanup.
await 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.
async 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.
await Promise.all([promiseHref, promisePing]);
// Cleanup.
await stopServer(server);
await 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.
async 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.
await promisePing;
// Cleanup.
await stopServer(server);
},
// Test that the <a ping> attribute is properly tokenized using ASCII white
// space characters as separators.
async 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.
await Promise.all([promiseHref, ...pingPathHandlers]);
// Cleanup.
await 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 =
'' +
'';
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) {
return new Promise(resolve => {
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");
resolve();
});
});
}
// 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) {
return new Promise(resolve => {
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) {
let expectedReferrer = headers.referrer.match(/https?:\/\/[^\/]+\/?/i)[0];
is(request.getHeader(
"Referer"), expectedReferrer,
"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");
resolve();
});
});
}
// Returns a promise that is resolved when the given http server instance has
// been stopped.
function stopServer(server) {
return new Promise(resolve => {
server.stop(resolve);
});
}
</
script>
</
head>
<
body>
<a target=
"_blank" href=
"https://bugzilla.mozilla.org/show_bug.cgi?id=786347">Mozill
a Bug 786347</a>
<p id="display"></p>
<iframe id="frame" />
</body>
</html>