Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: http3 OR http2
- Manifest: devtools/client/webconsole/test/browser/_browser_console.toml
/* Any copyright is dedicated to the Public Domain.
// Test the basic features of the Browser Console.
"use strict";
requestLongerTimeout(2);
const TEST_URI =
"test/browser/test-console.html?" +
Date.now();
const TEST_IMAGE =
"test/test-image.png";
add_task(async function () {
// Needed for the execute() call in `testMessages`.
await pushPref("security.allow_parent_unrestricted_js_loads", true);
await pushPref("devtools.browserconsole.enableNetworkMonitoring", true);
await pushPref("devtools.browsertoolbox.scope", "everything");
// Open a parent process tab to check it doesn't have impact
const aboutRobotsTab = await addTab("about:robots");
// And open the "actual" test tab
const tab = await addTab(TEST_URI);
await testMessages();
info("Close tab");
await removeTab(tab);
await removeTab(aboutRobotsTab);
});
async function testMessages() {
const opened = waitForBrowserConsole();
let hud = BrowserConsoleManager.getBrowserConsole();
ok(!hud, "browser console is not open");
// The test harness does override the global's console property to replace it with
// a Console.sys.mjs instance (https://searchfox.org/mozilla-central/rev/c5c002f81f08a73e04868e0c2bf0eb113f200b03/testing/mochitest/api.js#75-78)
// So here we reset the console property with the native console (which is luckily
// stored in `nativeConsole`).
const overriddenConsole = globalThis.console;
globalThis.console = globalThis.nativeConsole;
info("wait for the browser console to open with ctrl-shift-j");
EventUtils.synthesizeKey("j", { accelKey: true, shiftKey: true }, window);
hud = await opened;
ok(hud, "browser console opened");
info("Check that we don't display the non-native console API warning");
// Wait a bit to let room for the message to be displayed
await wait(1000);
is(
await findMessageVirtualizedByType({
hud,
text: "The Web Console logging API",
typeSelector: ".warn",
}),
undefined,
"The message about disabled console API is not displayed"
);
// Set the overidden console back.
globalThis.console = overriddenConsole;
await clearOutput(hud);
await setFilterState(hud, {
netxhr: true,
css: true,
});
executeSoon(() => {
expectUncaughtException();
// eslint-disable-next-line no-undef
foobarException();
});
// Add a message from a chrome window.
hud.iframeWindow.console.log("message from chrome window");
// Spawn worker from a chrome window and log a message and an error
const workerCode = `console.log("message in parent worker");
throw new Error("error in parent worker");`;
const blob = new hud.iframeWindow.Blob([workerCode], {
type: "application/javascript",
});
const chromeSpawnedWorker = new hud.iframeWindow.Worker(
URL.createObjectURL(blob)
);
// Spawn Chrome worker from a chrome window and log a message
// It's important to use the browser console global so the message gets assigned
// a non-numeric innerID in Console.cpp
const browserConsoleGlobal = Cu.getGlobalForObject(hud);
const chromeWorker = new browserConsoleGlobal.ChromeWorker(
URL.createObjectURL(
new browserConsoleGlobal.Blob(
[`console.log("message in chrome worker")`],
{
type: "application/javascript",
}
)
)
);
const sandbox = new Cu.Sandbox(null, {
wantComponents: false,
wantGlobalProperties: ["URL", "URLSearchParams"],
});
const error = Cu.evalInSandbox(
`new Error("error from nuked globals");`,
sandbox
);
console.error(error);
Cu.nukeSandbox(sandbox);
const componentsException = new Components.Exception("Components.Exception");
console.error(componentsException);
// Check privileged error message from a content process
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
(async function () {
throw new Error("privileged content process error message");
})();
});
// Add a message from a content window.
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
content.wrappedJSObject.console.log("message from content window");
content.wrappedJSObject.throwError("error from content window");
content.testWorker = new content.Worker("./test-worker.js");
content.testWorker.postMessage({
type: "log",
message: "message in content worker",
});
content.testWorker.postMessage({
type: "error",
message: "error in content worker",
});
});
// Test eval.
execute(hud, "`Parent Process Location: ${document.location.href}`");
// Test eval frame script
gBrowser.selectedBrowser.messageManager.loadFrameScript(
`data:application/javascript,console.log("framescript-message")`,
false
);
// Check for network requests.
const xhr = new XMLHttpRequest();
xhr.onload = () => console.log("xhr loaded, status is: " + xhr.status);
xhr.open("get", TEST_URI, true);
xhr.send();
// Check for xhr error.
const xhrErr = new XMLHttpRequest();
xhrErr.onload = () => {
console.log("xhr error loaded, status is: " + xhrErr.status);
};
xhrErr.open("get", TEST_XHR_ERROR_URI, true);
xhrErr.send();
// Check that Fetch requests are categorized as "XHR".
await fetch(TEST_IMAGE);
console.log("fetch loaded");
// Check messages logged with Services.console.logMessage
const scriptErrorMessage = Cc["@mozilla.org/scripterror;1"].createInstance(
Ci.nsIScriptError
);
scriptErrorMessage.initWithWindowID(
"Error from Services.console.logMessage",
gBrowser.currentURI.prePath,
0,
0,
Ci.nsIScriptError.warningFlag,
"chrome javascript",
gBrowser.selectedBrowser.innerWindowID
);
Services.console.logMessage(scriptErrorMessage);
// Check messages logged in content with Log.sys.mjs
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
const { Log } = ChromeUtils.importESModule(
"resource://gre/modules/Log.sys.mjs"
);
const logger = Log.repository.getLogger("TEST_LOGGER_" + Date.now());
logger.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));
logger.level = Log.Level.Info;
logger.info("Log.sys.mjs content process messsage");
});
// Check CSS warnings in parent process
await execute(hud, `document.body.style.backgroundColor = "rainbow"`);
// Wait enough so any duplicated message would have the time to be rendered
await wait(1000);
await checkUniqueMessageExists(
hud,
"message from chrome window",
".console-api"
);
await checkUniqueMessageExists(hud, "error from nuked globals", ".error");
await checkUniqueMessageExists(
hud,
"privileged content process error message",
".error"
);
await checkUniqueMessageExists(
hud,
"message from content window",
".console-api"
);
await checkUniqueMessageExists(hud, "error from content window", ".error");
await checkUniqueMessageExists(
hud,
".result"
);
await checkUniqueMessageExists(hud, "framescript-message", ".console-api");
await checkUniqueMessageExists(
hud,
"Error from Services.console.logMessage",
".warn"
);
await checkUniqueMessageExists(hud, "foobarException", ".error");
await checkUniqueMessageExists(hud, "test-console.html", ".network");
await checkUniqueMessageExists(hud, "404.html", ".network");
await checkUniqueMessageExists(hud, "test-image.png", ".network");
await checkUniqueMessageExists(
hud,
"Log.sys.mjs content process messsage",
".console-api"
);
await checkUniqueMessageExists(
hud,
"message in content worker",
".console-api"
);
await checkUniqueMessageExists(hud, "error in content worker", ".error");
await checkUniqueMessageExists(
hud,
"message in parent worker",
".console-api"
);
await checkUniqueMessageExists(hud, "error in parent worker", ".error");
await checkUniqueMessageExists(
hud,
"message in chrome worker",
".console-api"
);
await checkUniqueMessageExists(
hud,
"Expected color but found ‘rainbow’",
".warn"
);
await checkUniqueMessageExists(
hud,
"Expected color but found ‘bled’",
".warn"
);
await checkComponentExceptionMessage(hud, componentsException);
await resetFilters(hud);
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
content.testWorker.terminate();
delete content.testWorker;
});
chromeSpawnedWorker.terminate();
chromeWorker.terminate();
info("Close the Browser Console");
await safeCloseBrowserConsole();
}
async function checkComponentExceptionMessage(hud, exception) {
const msgNode = await checkUniqueMessageExists(
hud,
"Components.Exception",
".error"
);
const framesNode = await waitFor(() => msgNode.querySelector(".pane.frames"));
ok(framesNode, "The Components.Exception stack is displayed right away");
const frameNodes = framesNode.querySelectorAll(".frame");
Assert.greater(frameNodes.length, 1, "Got at least one frame in the stack");
is(
frameNodes[0].querySelector(".line").textContent,
String(exception.lineNumber),
"The stack displayed by default refers to Components.Exception passed as argument"
);
const [, line] = msgNode
.querySelector(".frame-link-line")
.textContent.split(":");
is(
line,
String(exception.lineNumber + 1),
"The link on the top right refers to the console.error callsite"
);
}