Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Errors

/* Any copyright is dedicated to the Public Domain.
"use strict";
/**
* !! AFTER MOVING OR RENAMING THIS METHOD, UPDATE `EXPECTED` CONSTANTS BELOW !!
*/
const createParentProcessRequests = async () => {
info("Do some requests from the parent process");
// The line:column for `fetch` should be EXPECTED_REQUEST_LINE_1/COL_1
await fetch(FETCH_URI);
const img = new Image();
const onLoad = new Promise(r => img.addEventListener("load", r));
// The line:column for `img` below should be EXPECTED_REQUEST_LINE_2/COL_2
img.src = IMAGE_URI;
await onLoad;
};
const EXPECTED_METHOD_NAME = "createParentProcessRequests";
const EXPECTED_REQUEST_LINE_1 = 12;
const EXPECTED_REQUEST_COL_1 = 9;
const EXPECTED_REQUEST_LINE_2 = 17;
const EXPECTED_REQUEST_COL_2 = 3;
// Test the ResourceCommand API around NETWORK_EVENT for the parent process
// The img.src request gets cached regardless of `devtools.cache.disabled`.
// Add a random parameter to the request to bypass the cache.
const uuid = `${Date.now()}-${Math.random()}`;
const IMAGE_URI = URL_ROOT_SSL + "test_image.png?" + uuid;
// Loading the content page might also trigger priviledge image requests from the firefox UI, this seems to
// happen when a new tab is created for the page.
const ignoreRequestPatterns = "file:///";
add_task(async function testParentProcessRequests() {
// The test expects the main process commands instance to receive resources
// for content process requests.
await pushPref("devtools.browsertoolbox.scope", "everything");
const commands = await CommandsFactory.forMainProcess();
await commands.targetCommand.startListening();
const { resourceCommand } = commands;
const receivedNetworkEvents = [];
const receivedStacktraces = [];
const onAvailable = resources => {
for (const resource of resources) {
if (resource.resourceType == resourceCommand.TYPES.NETWORK_EVENT) {
if (resource.url.startsWith(ignoreRequestPatterns)) {
return;
}
receivedNetworkEvents.push(resource);
} else if (
resource.resourceType == resourceCommand.TYPES.NETWORK_EVENT_STACKTRACE
) {
receivedStacktraces.push(resource);
}
}
};
const onUpdated = updates => {
for (const { resource } of updates) {
is(
resource.resourceType,
resourceCommand.TYPES.NETWORK_EVENT,
"Received a network update event resource"
);
}
};
await resourceCommand.watchResources(
[
resourceCommand.TYPES.NETWORK_EVENT,
resourceCommand.TYPES.NETWORK_EVENT_STACKTRACE,
],
{
ignoreExistingResources: true,
onAvailable,
onUpdated,
}
);
await createParentProcessRequests();
const img2 = new Image();
img2.src = IMAGE_URI;
info("Wait for the network events");
await waitFor(() => receivedNetworkEvents.length == 3);
info("Wait for the network events stack traces");
// Note that we aren't getting any stacktrace for the second cached request
await waitFor(() => receivedStacktraces.length == 2);
info("Assert the fetch request");
const fetchRequest = receivedNetworkEvents[0];
is(
fetchRequest.url,
FETCH_URI,
"The first resource is for the fetch request"
);
ok(fetchRequest.chromeContext, "The fetch request is privileged");
const fetchStacktrace = receivedStacktraces[0].lastFrame;
is(receivedStacktraces[0].resourceId, fetchRequest.stacktraceResourceId);
is(fetchStacktrace.filename, gTestPath);
is(fetchStacktrace.lineNumber, EXPECTED_REQUEST_LINE_1);
is(fetchStacktrace.columnNumber, EXPECTED_REQUEST_COL_1);
is(fetchStacktrace.functionName, EXPECTED_METHOD_NAME);
is(fetchStacktrace.asyncCause, null);
async function getResponseContent(networkEvent) {
const packet = {
to: networkEvent.actor,
type: "getResponseContent",
};
const response = await commands.client.request(packet);
return response.content.text;
}
const fetchContent = await getResponseContent(fetchRequest);
is(fetchContent, "foo");
info("Assert the first image request");
const firstImageRequest = receivedNetworkEvents[1];
is(
firstImageRequest.url,
IMAGE_URI,
"The second resource is for the first image request"
);
ok(!firstImageRequest.fromCache, "The first image request isn't cached");
ok(firstImageRequest.chromeContext, "The first image request is privileged");
const firstImageStacktrace = receivedStacktraces[1].lastFrame;
is(receivedStacktraces[1].resourceId, firstImageRequest.stacktraceResourceId);
is(firstImageStacktrace.filename, gTestPath);
is(firstImageStacktrace.lineNumber, EXPECTED_REQUEST_LINE_2);
is(firstImageStacktrace.columnNumber, EXPECTED_REQUEST_COL_2);
is(firstImageStacktrace.functionName, EXPECTED_METHOD_NAME);
is(firstImageStacktrace.asyncCause, null);
info("Assert the second image request");
const secondImageRequest = receivedNetworkEvents[2];
is(
secondImageRequest.url,
IMAGE_URI,
"The third resource is for the second image request"
);
ok(secondImageRequest.fromCache, "The second image request is cached");
ok(
secondImageRequest.chromeContext,
"The second image request is privileged"
);
info(
"Open a content page to ensure we also receive request from content processes"
);
const tab = await addTab(pageUrl);
await waitFor(() => receivedNetworkEvents.length == 4);
const tabRequest = receivedNetworkEvents[3];
is(tabRequest.url, pageUrl, "The 4th resource is for the tab request");
ok(!tabRequest.chromeContext, "The 4th request is content");
info(
"Also spawn a privileged request from the content process, not bound to any WindowGlobal"
);
await SpecialPowers.spawn(
tab.linkedBrowser,
[requestUrl],
async function (uri) {
const { NetUtil } = ChromeUtils.importESModule(
);
const channel = NetUtil.newChannel({
uri,
loadUsingSystemPrincipal: true,
});
channel.open();
}
);
await removeTab(tab);
await waitFor(() => receivedNetworkEvents.length == 5);
const privilegedContentRequest = receivedNetworkEvents[4];
is(
privilegedContentRequest.url,
requestUrl,
"The 5th resource is for the privileged content process request"
);
ok(privilegedContentRequest.chromeContext, "The 5th request is privileged");
info("Now focus only on parent process resources");
await pushPref("devtools.browsertoolbox.scope", "parent-process");
info(
"Retrigger the two last requests. The tab document request and a privileged request. Both happening in the tab's content process."
);
const secondTab = await addTab(pageUrl);
await SpecialPowers.spawn(
secondTab.linkedBrowser,
[requestUrl],
async function (uri) {
const { NetUtil } = ChromeUtils.importESModule(
);
const channel = NetUtil.newChannel({
uri,
loadUsingSystemPrincipal: true,
});
channel.open();
}
);
await waitFor(() => receivedNetworkEvents.length == 6);
// nsIHttpChannel doesn't expose any attribute allowing to identify
// privileged requests done in content processes.
// Thus, preventing us from filtering them out correctly.
// Ideally, we would need some new attribute to know from which (content) process
// any channel originates from.
info(
"For now, we are still notified about the privileged content process request"
);
const secondPrivilegedContentRequest = receivedNetworkEvents[5];
is(
secondPrivilegedContentRequest.url,
requestUrl,
"The 6th resource is for the second privileged content process request"
);
ok(privilegedContentRequest.chromeContext, "The 6th request is privileged");
// Let some time to receive the tab request if that's not correctly filtered out
await wait(1000);
is(
receivedNetworkEvents.length,
6,
"But we don't receive the request for the tab request"
);
await removeTab(secondTab);
await resourceCommand.unwatchResources(
[resourceCommand.TYPES.NETWORK_EVENT],
{
onAvailable,
onUpdated,
}
);
await commands.destroy();
});