Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

"use strict";
let consoleTesterExperimentalAPIS = {
consoleTester: {
schema: "schema.json",
parent: {
scopes: ["addon_parent"],
script: "parent.js",
paths: [["consoleTester"]],
},
child: {
scopes: ["addon_child"],
script: "child.js",
paths: [["consoleTester"]],
},
},
};
let consoleTesterFiles = {
"schema.json": JSON.stringify([
{
namespace: "consoleTester",
functions: [
{
name: "testConsoleChild",
type: "function",
parameters: [],
returns: { type: "array", items: { type: "string" } },
},
{
name: "testConsoleParent",
type: "function",
parameters: [
{
type: "function",
name: "callback",
parameters: [{ type: "array", items: { type: "string" } }],
},
],
async: "callback",
},
{
name: "testStringifyGlobalInChild",
type: "function",
parameters: [],
returns: { type: "string" },
},
],
},
]),
"common.js": () => {
// Common test logic shared between parent.js and child.js
globalThis.consoleTester_testConsole = context => {
let capturedLogs = [];
// Intercept console.log call handled by ConsoleAPI at:
let origDump = console.dump;
console.dump = function (msg) {
capturedLogs.push(msg);
return origDump.apply(this, arguments);
};
try {
console.log("testConsole_Hello");
// This used to trigger a crash, regression test for: bug 1977694
console.log(context);
console.log("testConsole_Bye");
} finally {
console.dump = origDump;
}
return capturedLogs;
};
},
"parent.js": () => {
/* globals ExtensionAPI */ // (also defined for use in child.js below)
this.consoleTester = class extends ExtensionAPI {
getAPI(context) {
if (!globalThis.consoleTester_testConsole) {
const mozExtJsFile = context.extension.getURL("common.js");
Services.scriptloader.loadSubScript(mozExtJsFile, globalThis);
}
return {
consoleTester: {
testConsoleParent() {
return globalThis.consoleTester_testConsole(context);
},
},
};
}
};
},
"child.js": () => {
this.consoleTester = class extends ExtensionAPI {
getAPI(context) {
if (!globalThis.consoleTester_testConsole) {
const mozExtJsFile = context.extension.getURL("common.js");
Services.scriptloader.loadSubScript(mozExtJsFile, globalThis);
}
return {
consoleTester: {
testConsoleChild() {
let logs = globalThis.consoleTester_testConsole(context);
return Cu.cloneInto(logs, context.cloneScope);
},
testStringifyGlobalInChild() {
try {
// global is _createExtGlobal() in ExtensionCommon.sys.mjs.
// eslint-disable-next-line no-undef -- global is defined ^
return JSON.stringify(global);
} catch (e) {
return String(e);
}
},
},
};
}
};
},
};
// Test that console.log of context works instead of causing a crash.
add_task(async function test_console_log_context_in_experiment_sandbox() {
let extension = ExtensionTestUtils.loadExtension({
isPrivileged: true,
manifest: {
experiment_apis: consoleTesterExperimentalAPIS,
},
files: consoleTesterFiles,
background() {
browser.test.onMessage.addListener(async msg => {
browser.test.assertEq("start", msg, "Expected message");
function assertStartsWith(actual, expectedStart, message) {
browser.test.assertTrue(
actual.startsWith(expectedStart),
`${message} - Starts with ${expectedStart} given actual: ${actual}`
);
}
// Test 1/2: test console.log behavior in parent:
let logs = await browser.consoleTester.testConsoleParent();
browser.test.assertEq(3, logs.length, "Expected 3 log entries");
browser.test.assertEq(
logs[0],
"console.log: WebExtensions: testConsole_Hello\n",
"First message in parent"
);
assertStartsWith(
logs[1],
"console.log: WebExtensions: ExtensionPageContextParent",
"Second message in parent, serialization of context"
);
browser.test.assertEq(
logs[2],
"console.log: WebExtensions: testConsole_Bye\n",
"Last message in parent"
);
// Test 2/2: test console.log behavior in child:
logs = browser.consoleTester.testConsoleChild();
browser.test.assertEq(3, logs.length, "Expected 3 log entries");
browser.test.assertEq(
logs[0],
"console.log: WebExtensions: testConsole_Hello\n",
"First message in child"
);
assertStartsWith(
logs[1],
"console.log: WebExtensions: ExtensionPageContextChild",
"Second message in child, serialization of context"
);
browser.test.assertEq(
logs[2],
"console.log: WebExtensions: testConsole_Bye\n",
"Last message in child"
);
// Bonus: test that JSON.stringify on the API global throws gracefully.
// This previously caused a crash, before the fix to bug 1977694.
browser.test.assertEq(
browser.consoleTester.testStringifyGlobalInChild(),
"TypeError: cyclic object value",
"JSON.stringify(global) should throw"
);
browser.test.sendMessage("done");
});
},
});
await extension.startup();
extension.sendMessage("start");
await extension.awaitMessage("done");
await extension.unload();
});