Source code
Revision control
Copy as Markdown
Other Tools
"use strict";
const PROMPT_ALLOW_BUTTON = -1;
const PROMPT_NOT_NOW_BUTTON = 0;
const { HttpServer } = ChromeUtils.importESModule(
);
const LNA_BASE_URL = getRootDirectory(gTestPath).replace(
);
async function restorePermissions() {
info("Restoring permissions");
Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
Services.perms.removeAll();
}
function clickDoorhangerButton(buttonIndex, browser, notificationID) {
let popup = PopupNotifications.getNotification(notificationID, browser);
let notification = popup?.owner?.panel?.childNodes?.[0];
ok(notification, "Notification popup is available");
if (buttonIndex === PROMPT_ALLOW_BUTTON) {
ok(true, "Triggering main action (allow)");
notification.button.doCommand();
} else {
ok(true, "Triggering secondary action (deny)");
notification.secondaryButton.doCommand();
}
}
function observeAndCheck(testType, rand, expectedStatus, message) {
return new Promise(resolve => {
const url = `http://localhost:21555/?type=${testType}&rand=${rand}`;
const observer = {
observe(subject, topic) {
if (topic !== "http-on-stop-request") {
return;
}
let channel = subject.QueryInterface(Ci.nsIHttpChannel);
if (!channel || channel.URI.spec !== url) {
return;
}
is(channel.status, expectedStatus, message);
Services.obs.removeObserver(observer, "http-on-stop-request");
resolve();
},
};
Services.obs.addObserver(observer, "http-on-stop-request");
});
}
const testCases = [
{
type: "fetch",
allowStatus: Cr.NS_OK,
denyStatus: Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED,
},
{
type: "xhr",
allowStatus: Cr.NS_OK,
denyStatus: Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED,
},
{
type: "img",
allowStatus: Cr.NS_OK,
denyStatus: Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED,
},
{
type: "video",
allowStatus: Cr.NS_OK,
denyStatus: Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED,
},
{
type: "audio",
allowStatus: Cr.NS_OK,
denyStatus: Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED,
},
{
type: "iframe",
allowStatus: Cr.NS_OK,
denyStatus: Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED,
},
{
type: "script",
allowStatus: Cr.NS_OK,
denyStatus: Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED,
},
{
type: "font",
allowStatus: Cr.NS_OK,
denyStatus: Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED,
},
{
type: "websocket",
allowStatus: Cr.NS_ERROR_WEBSOCKET_CONNECTION_REFUSED,
denyStatus: Cr.NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED,
},
];
function registerServerHandlers(server) {
server.registerPathHandler("/", (request, response) => {
const params = new URLSearchParams(request.queryString);
const type = params.get("type");
response.setHeader("Access-Control-Allow-Origin", "*", false);
switch (type) {
case "img":
response.setHeader("Content-Type", "image/gif", false);
response.setStatusLine(request.httpVersion, 200, "OK");
response.write(
atob("R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==")
);
break;
case "audio":
response.setHeader("Content-Type", "audio/wav", false);
response.setStatusLine(request.httpVersion, 200, "OK");
response.write(
atob("UklGRhYAAABXQVZFZm10IBAAAAABAAEAIlYAAESsAAACABAAZGF0YQAAAAA=")
);
break;
case "video":
response.setHeader("Content-Type", "video/mp4", false);
response.setStatusLine(request.httpVersion, 200, "OK");
response.write(
atob(
"GkXfo0AgQoaBAUL3gQFC8oEEQvOBCEKCQAR3ZWJtQoeBAkKFgQIYU4BnQI0VSalmQCgq17FAAw9CQE2AQAZ3aGFtbXlXQUAGd2hhbW15RIlACECPQAAAAAAAFlSua0AxrkAu14EBY8WBAZyBACK1nEADdW5khkAFVl9WUDglhohAA1ZQOIOBAeBABrCBCLqBCB9DtnVAIueBAKNAHIEAAIAwAQCdASoIAAgAAUAmJaQAA3AA/vz0AAA="
)
);
break;
default:
response.setHeader("Content-Type", "text/plain", false);
response.setStatusLine(request.httpVersion, 200, "OK");
response.write("hello");
}
});
}
async function runSingleTestCase(
test,
rand,
expectedStatus,
description,
userAction = null,
notificationID = null
) {
info(description);
const promise = observeAndCheck(test.type, rand, expectedStatus, description);
const tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
`${LNA_BASE_URL}page_with_non_trackers.html?test=${test.type}&rand=${rand}`
);
if (userAction && notificationID) {
const buttonNum =
userAction === "allow" ? PROMPT_ALLOW_BUTTON : PROMPT_NOT_NOW_BUTTON;
await BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
clickDoorhangerButton(buttonNum, gBrowser.selectedBrowser, notificationID);
}
await promise;
gBrowser.removeTab(tab);
}
async function runPromptedLnaTest(test, overrideLabel, notificationID) {
const promptActions = ["allow", "deny"];
for (const userAction of promptActions) {
const rand = Math.random();
const expectedStatus =
userAction === "allow" ? test.allowStatus : test.denyStatus;
await runSingleTestCase(
test,
rand,
expectedStatus,
`LNA test (${overrideLabel}) for ${test.type} with user action: ${userAction}`,
userAction,
notificationID
);
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve => setTimeout(resolve, 300));
await runSingleTestCase(
test,
rand,
expectedStatus,
`LNA test (${overrideLabel}) for ${test.type} with user action: ${userAction}`,
userAction,
notificationID
);
}
}
var gLnaServer = null;
async function setupLnaServer() {
if (gLnaServer) {
return gLnaServer;
}
gLnaServer = new HttpServer();
gLnaServer.start(21555);
registerServerHandlers(gLnaServer);
registerCleanupFunction(async () => {
await restorePermissions();
if (gLnaServer) {
await new Promise(resolve => {
gLnaServer.stop(resolve);
});
gLnaServer = null;
}
});
return gLnaServer;
}
async function setupLnaPrefs() {
await SpecialPowers.pushPrefEnv({
set: [
["permissions.manager.defaultsUrl", ""],
["network.websocket.delay-failed-reconnects", false],
["network.websocket.max-connections", 1000],
["network.lna.block_trackers", true],
["network.lna.blocking", true],
["network.http.rcwn.enabled", false],
["network.lna.websocket.enabled", true],
["network.lna.local-network-to-localhost.skip-checks", false],
["browser.ipProtection.enabled", false],
],
});
Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
}