Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* Any copyright is dedicated to the Public Domain.
"use strict";
const { JSONHandler } = ChromeUtils.importESModule(
"chrome://remote/content/cdp/JSONHandler.sys.mjs"
);
// Get list of supported routes from JSONHandler
const routes = new JSONHandler().routes;
add_task(async function json_version() {
const { userAgent } = Cc[
"@mozilla.org/network/protocol;1?name=http"
].getService(Ci.nsIHttpProtocolHandler);
const json = await requestJSON("/json/version");
is(
json.Browser,
`${Services.appinfo.name}/${Services.appinfo.version}`,
"Browser name and version found"
);
is(json["Protocol-Version"], "1.3", "Protocol version found");
is(json["User-Agent"], userAgent, "User agent found");
is(json["V8-Version"], "1.0", "V8 version found");
is(json["WebKit-Version"], "1.0", "Webkit version found");
is(
json.webSocketDebuggerUrl,
RemoteAgent.cdp.targetList.getMainProcessTarget().wsDebuggerURL,
"Websocket URL for main process target found"
);
});
add_task(async function check_routes() {
for (const route in routes) {
const { parameter, method } = routes[route];
// Skip routes expecting parameter
if (parameter) {
continue;
}
// Check request succeeded (200) and responded with valid JSON
info(`Checking ${route}`);
await requestJSON(route, { method });
// Check with trailing slash
info(`Checking ${route + "/"}`);
await requestJSON(route + "/", { method });
// Test routes expecting a certain method
if (method) {
const responseText = await requestJSON(route, {
method: "DELETE",
status: 405,
json: false,
});
is(
responseText,
`Using unsafe HTTP verb DELETE to invoke ${route}. This action supports only ${method} verb.`,
"/json/new fails with 405 when using GET"
);
}
}
});
add_task(async function json_list({ client }) {
const { Target } = client;
const { targetInfos } = await Target.getTargets();
const json = await requestJSON("/json/list");
const jsonAlias = await requestJSON("/json");
Assert.deepEqual(json, jsonAlias, "/json/list and /json return the same");
ok(Array.isArray(json), "Target list is an array");
is(
json.length,
targetInfos.length,
"Targets as listed on /json/list are equal to Target.getTargets"
);
for (let i = 0; i < json.length; i++) {
const jsonTarget = json[i];
const wsTarget = targetInfos[i];
is(
jsonTarget.id,
wsTarget.targetId,
"Target id matches between HTTP and Target.getTargets"
);
is(
jsonTarget.type,
wsTarget.type,
"Target type matches between HTTP and Target.getTargets"
);
is(
jsonTarget.url,
wsTarget.url,
"Target url matches between HTTP and Target.getTargets"
);
// Ensure expected values specifically for JSON endpoint
// and that type is always "page" as main process target should not be included
is(
jsonTarget.type,
"page",
`Target (${jsonTarget.id}) from list has expected type (page)`
);
is(
jsonTarget.webSocketDebuggerUrl,
`ws://${RemoteAgent.debuggerAddress}/devtools/page/${wsTarget.targetId}`,
`Target (${jsonTarget.id}) from list has expected webSocketDebuggerUrl value`
);
}
});
add_task(async function json_new_target({ client }) {
const newUrl = "https://example.com";
let getError = await requestJSON("/json/new?" + newUrl, {
status: 405,
json: false,
});
is(
getError,
"Using unsafe HTTP verb GET to invoke /json/new. This action supports only PUT verb.",
"/json/new fails with 405 when using GET"
);
const newTarget = await requestJSON("/json/new?" + newUrl, { method: "PUT" });
is(newTarget.type, "page", "Returned target type is 'page'");
is(newTarget.url, newUrl, "Returned target URL matches");
ok(!!newTarget.id, "Returned target has id");
ok(
!!newTarget.webSocketDebuggerUrl,
"Returned target has webSocketDebuggerUrl"
);
const { Target } = client;
const targets = await getDiscoveredTargets(Target);
const foundTarget = targets.find(target => target.targetId === newTarget.id);
ok(!!foundTarget, "Returned target id was found");
});
add_task(async function json_activate_target({ client, tab }) {
const { Target, target } = client;
const currentTargetId = target.id;
const targets = await getDiscoveredTargets(Target);
const initialTarget = targets.find(
target => target.targetId === currentTargetId
);
ok(!!initialTarget, "The current target has been found");
// open some more tabs in the initial window
await openTab(Target);
await openTab(Target);
const lastTabFirstWindow = await openTab(Target);
is(
gBrowser.selectedTab,
lastTabFirstWindow.newTab,
"Selected tab has changed to a new tab"
);
const activateResponse = await requestJSON(
"/json/activate/" + initialTarget.targetId,
{ json: false }
);
is(
activateResponse,
"Target activated",
"Activate endpoint returned expected string"
);
is(gBrowser.selectedTab, tab, "Selected tab is the initial tab again");
const invalidResponse = await requestJSON("/json/activate/does-not-exist", {
status: 404,
json: false,
});
is(invalidResponse, "No such target id: does-not-exist");
});
add_task(async function json_close_target({ CDP, client }) {
const { Target } = client;
const { targetInfo, newTab } = await openTab(Target);
const targetListBefore = await CDP.List();
const beforeTarget = targetListBefore.find(
target => target.id === targetInfo.targetId
);
ok(!!beforeTarget, "New target has been found");
const tabClosed = BrowserTestUtils.waitForEvent(newTab, "TabClose");
const targetDestroyed = Target.targetDestroyed();
const activateResponse = await requestJSON(
"/json/close/" + targetInfo.targetId,
{ json: false }
);
is(
activateResponse,
"Target is closing",
"Close endpoint returned expected string"
);
await tabClosed;
info("Tab was closed");
await targetDestroyed;
info("Received the Target.targetDestroyed event");
const targetListAfter = await CDP.List();
const afterTarget = targetListAfter.find(
target => target.id === targetInfo.targetId
);
Assert.equal(afterTarget, null, "New target is gone");
const invalidResponse = await requestJSON("/json/close/does-not-exist", {
status: 404,
json: false,
});
is(invalidResponse, "No such target id: does-not-exist");
});
add_task(async function json_prevent_load_in_iframe({ client }) {
const { Page } = client;
const PAGE = `https://example.com/document-builder.sjs?html=${encodeURIComponent(
'<iframe src="http://localhost:9222/json/version"></iframe>`'
)}`;
await Page.enable();
const NAVIGATED = "Page.frameNavigated";
const history = new RecordEvents(2);
history.addRecorder({
event: Page.frameNavigated,
eventName: NAVIGATED,
messageFn: payload => {
return `Received ${NAVIGATED} for frame id ${payload.frame.id}`;
},
});
await loadURL(PAGE);
const frameNavigatedEvents = await history.record();
const frames = frameNavigatedEvents
.map(({ payload }) => payload.frame)
.filter(frame => frame.parentId !== undefined);
const windowGlobal = BrowsingContext.get(frames[0].id).currentWindowGlobal;
ok(
windowGlobal.documentURI.spec.startsWith("about:neterror?e=cspBlocked"),
"Expected page not be loaded within an iframe"
);
});
async function requestJSON(path, options = {}) {
const { method = "GET", status = 200, json = true } = options;
info(`${method} http://${RemoteAgent.debuggerAddress}${path}`);
const response = await fetch(`http://${RemoteAgent.debuggerAddress}${path}`, {
method,
});
is(response.status, status, `JSON response is ${status}`);
if (json) {
return response.json();
}
return response.text();
}