tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 45290297179b465ddf1a2d1cd098f3946ecb56af
parent 198fc64bac829ec18d3de6832ebc7fc59d21ed23
Author: Rob Wu <rob@robwu.nl>
Date:   Mon,  5 Jan 2026 16:09:00 +0000

Bug 2007587 - Gracefully handle clone errors in StructuredLogger r=ahal

Differential Revision: https://phabricator.services.mozilla.com/D277476

Diffstat:
Mtesting/modules/StructuredLog.sys.mjs | 14+++++++++++---
Mtesting/modules/tests/xpcshell/test_structuredlog.js | 67++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
2 files changed, 73 insertions(+), 8 deletions(-)

diff --git a/testing/modules/StructuredLog.sys.mjs b/testing/modules/StructuredLog.sys.mjs @@ -215,10 +215,18 @@ export class StructuredLogger { } if (this.#dumpScope) { - this.#dumpFun(Cu.cloneInto(allData, this.#dumpScope)); - } else { - this.#dumpFun(allData); + try { + allData = Cu.cloneInto(allData, this.#dumpScope); + } catch (e) { + try { + this.error(`Failed to cloneInto: ${e}`); + this.warning(`Tried to clone: ${uneval(allData)}`); + } catch (e2) { + console.error("Failed to handle clone error", e, e2); + } + } } + this.#dumpFun(allData); } #testId(test) { diff --git a/testing/modules/tests/xpcshell/test_structuredlog.js b/testing/modules/tests/xpcshell/test_structuredlog.js @@ -1,11 +1,11 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -function run_test() { - const { StructuredLogger } = ChromeUtils.importESModule( - "resource://testing-common/StructuredLog.sys.mjs" - ); +const { StructuredLogger } = ChromeUtils.importESModule( + "resource://testing-common/StructuredLog.sys.mjs" +); +add_task(function test_StructuredLogger() { let testBuffer = []; let appendBuffer = function (msg) { @@ -144,4 +144,61 @@ function run_test() { assertLastMsg({ action: "suite_end", }); -} +}); + +add_task(function test_StructuredLogger_with_dumpScope() { + let testBuffer = []; + let appendBuffer2 = msg => { + testBuffer.push(msg); + }; + + let assertSeenMsg = (expected, checkExtra) => { + const { action, message, level, extra } = testBuffer.shift(); + deepEqual({ action, message, level }, expected); + ok(checkExtra(extra), `Extra: ${uneval(extra)}`); + }; + + // Regression test for bug 2007587: Logger gracefully handles clone failures. + let logger2 = new StructuredLogger("test_dump", appendBuffer2, globalThis); + const extraNotCloneable = { notCloneable: () => {} }; + logger2.info("Test non-cloneable", extraNotCloneable); + + assertSeenMsg( + { + action: "log", + message: + "Failed to cloneInto: Error: Permission denied to pass a Function via structured clone", + level: "ERROR", + }, + extra => extra === undefined + ); + + testBuffer[0].message = testBuffer[0].message.replace(/time:\d+/, "time:123"); + assertSeenMsg( + { + action: "log", + message: `Tried to clone: ({action:"log", time:123, thread:null, pid:null, source:"test_dump", level:"INFO", message:"Test non-cloneable", extra:{notCloneable:() => {}}})`, + level: "WARNING", + }, + extra => extra === undefined + ); + + assertSeenMsg( + { action: "log", message: "Test non-cloneable", level: "INFO" }, + extra => extra === extraNotCloneable + ); + + // Now verify the most common / desired behavior: clone where possible. + const extraCloneable = { cloneable: "Yes I am" }; + logger2.info("Test cloneable", extraCloneable); + + assertSeenMsg( + { action: "log", message: "Test cloneable", level: "INFO" }, + extra => { + deepEqual(extra, extraCloneable); + return extra !== extraCloneable; + } + ); + + equal(testBuffer.length, 0, "No messages unaccounted for"); +});