Bug 1860888 - [devtools] Fix logging SharedArrayBuffer from Worklet/Workers. r=devtools-reviewers,smaug

This custom CloneDataPolicy was already using while writing the structured clone,
but not when reading it.

Differential Revision: https://phabricator.services.mozilla.com/D191783
This commit is contained in:
Alexandre Poirot 2023-11-03 09:39:41 +00:00
parent c7b7af4656
commit bd6e530d90
10 changed files with 147 additions and 13 deletions

View File

@ -64,6 +64,7 @@ support-files = [
"test-console-stacktrace-mapped.html",
"test-console-table.html",
"test-console-workers.html",
"test-console-worklet.html",
"test-console.html",
"test-data.json",
"test-data.json^headers^",
@ -827,4 +828,6 @@ skip-if = ["true"] # Bug 1765369
["browser_webconsole_worker_promise_error.js"]
["browser_webconsole_worklet_console.js"]
["browser_webconsole_worklet_error.js"]

View File

@ -10,6 +10,12 @@ const TEST_URI =
"test/browser/test-console-workers.html";
add_task(async function () {
// Allow using SharedArrayBuffer in the test without special HTTP Headers
await pushPref(
"dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled",
true
);
info("Run the test with worker events dispatched to main thread");
await pushPref("dom.worker.console.dispatch_events_to_main_thread", true);
await testWorkerMessage();
@ -91,6 +97,11 @@ async function testWorkerMessage(directConnectionToWorkerThread = false) {
ok(symbolMessage, "Symbol logged from worker is visible in the console");
}
const sabMessage = await waitFor(() =>
findConsoleAPIMessage(hud, "sab-from-worker")
);
ok(sabMessage.textContent.includes("SharedArrayBuffer"));
info("Click on the clear button and wait for messages to be removed");
const onMessagesCacheCleared = hud.ui.once("messages-cache-cleared");
hud.ui.window.document.querySelector(".devtools-clear-icon").click();

View File

@ -0,0 +1,34 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// Tests that console api usage in worklet show in the console
"use strict";
const TEST_URI =
"https://example.com/browser/devtools/client/webconsole/" +
"test/browser/test-console-worklet.html";
add_task(async function () {
// Allow using SharedArrayBuffer in the test without special HTTP Headers
await pushPref(
"dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled",
true
);
const hud = await openNewTabAndConsole(TEST_URI);
await waitFor(() => findConsoleAPIMessage(hud, "string"));
await waitFor(() => findConsoleAPIMessage(hud, "42"));
const objectMessage = await waitFor(() =>
findConsoleAPIMessage(hud, "object")
);
ok(
objectMessage
.querySelector(".message-body")
.textContent.includes(`Object { object: true }`),
"The simple object is logged as expected"
);
await waitFor(() => findConsoleAPIMessage(hud, "SharedArrayBuffer"));
ok(true, "SharedArrayBuffer object is logged");
});

View File

@ -17,11 +17,13 @@
onmessage = function (e) {
console.log("log-from-worker", e.data.msg, globalThis);
console.log(Symbol("logged-symbol-from-worker"));
console.log("sab-from-worker", e.data.sab);
};
`);
function logFromWorker(msg) {
worker.postMessage({type: "log", msg});
const sab = new SharedArrayBuffer(1024);
worker.postMessage({type: "log", msg, sab});
}
</script>
</body>

View File

@ -0,0 +1,41 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Worklet console generator</title>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
</head>
<script type="worklet">
registerProcessor('test-param', class param extends AudioWorkletProcessor {
constructor() {
super();
this.port.onmessage = e => {
console.log("worklet", "string");
console.log("worklet", 42);
console.log("worklet", { object: true });
console.log("worklet", e.data.data); // Log the SharedArrayBuffer
};
}
process(input, output, parameters) {
return true;
}
});
</script>
<script>
"use strict";
const ac = new AudioContext();
const e = document.querySelector("script[type=worklet]")
const blob = new Blob([e.textContent], {type: "application/javascript"});
const url = URL.createObjectURL(blob);
ac.audioWorklet.addModule(url).then(() => {
const nodea = new AudioWorkletNode(ac, 'test-param');
// Instantiate and pass a SharedArrayBuffer to the Worklet
const normal_sab = new SharedArrayBuffer(1024);
nodea.port.postMessage({data: normal_sab });
});
</script>

View File

@ -1,6 +1,7 @@
"use strict";
console.log("script evaluation");
console.log("Here is a SAB", new SharedArrayBuffer(1024));
addEventListener("install", function (evt) {
console.log("install event");

View File

@ -79,6 +79,12 @@ const expectedConsoleCalls = [
filename: /helper_serviceworker/,
arguments: ['script evaluation'],
},
{
level: "log",
filename: /helper_serviceworker/,
// Note that the second argument isn't a SharedArrayBuffer, but a DevTools "Object Front" instance.
arguments: ['Here is a SAB', { class : "SharedArrayBuffer" }],
},
{
level: "log",
filename: /helper_serviceworker/,
@ -108,7 +114,11 @@ const startTest = async function () {
await new Promise(resolve => {
SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.enabled", true],
["devtools.webconsole.filter.serviceworkers", true]
["devtools.webconsole.filter.serviceworkers", true],
[
"dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled",
true
],
]}, resolve);
});

View File

@ -328,8 +328,14 @@ class ConsoleRunnable : public StructuredCloneHolderBase {
ConsoleCommon::ClearException ce(aCx);
// This is the same policy as when writing from the other side, in
// WriteData.
JS::CloneDataPolicy cloneDataPolicy;
cloneDataPolicy.allowIntraClusterClonableSharedObjects();
cloneDataPolicy.allowSharedMemoryObjects();
JS::Rooted<JS::Value> argumentsValue(aCx);
if (!Read(aCx, &argumentsValue)) {
if (!Read(aCx, &argumentsValue, cloneDataPolicy)) {
return;
}

View File

@ -3,6 +3,15 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
add_task(async function test() {
await SpecialPowers.pushPrefEnv({
set: [
[
"dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled",
true,
],
],
});
const testURL = getRootDirectory(gTestPath) + "empty.html";
let tab = BrowserTestUtils.addTab(gBrowser, testURL);
gBrowser.selectedTab = tab;
@ -27,17 +36,33 @@ add_task(async function test() {
consoleListener.prototype = {
onConsoleLogEvent(aSubject) {
var obj = aSubject.wrappedJSObject;
is(
obj.arguments[0],
"Hello world from a SharedWorker!",
"A message from a SharedWorker \\o/"
);
is(obj.ID, "sharedWorker_console.js", "The ID is SharedWorker");
is(obj.innerID, "SharedWorker", "The ID is SharedWorker");
is(order++, 1, "Then a log message.");
if (order == 1) {
is(
obj.arguments[0],
"Hello world from a SharedWorker!",
"A message from a SharedWorker \\o/"
);
is(obj.ID, "sharedWorker_console.js", "The ID is SharedWorker");
is(obj.innerID, "SharedWorker", "The ID is SharedWorker");
is(order++, 1, "Then a first log message.");
} else {
is(
obj.arguments[0],
"Here is a SAB",
"A message from a SharedWorker \\o/"
);
is(
obj.arguments[1].constructor.name,
"SharedArrayBuffer",
"We got a direct reference to the SharedArrayBuffer coming from the worker thread"
);
is(obj.ID, "sharedWorker_console.js", "The ID is SharedWorker");
is(obj.innerID, "SharedWorker", "The ID is SharedWorker");
is(order++, 2, "Then a second log message.");
ConsoleAPIStorage.removeLogEventListener(this.onConsoleLogEvent);
resolve();
ConsoleAPIStorage.removeLogEventListener(this.onConsoleLogEvent);
resolve();
}
},
observe: (aSubject, aTopic) => {

View File

@ -7,5 +7,6 @@
onconnect = function (evt) {
console.profile("Hello profiling from a SharedWorker!");
console.log("Hello world from a SharedWorker!");
console.log("Here is a SAB", new SharedArrayBuffer(1024));
evt.ports[0].postMessage("ok!");
};