commit 0303dcecad6003c2b682c16f7a9002a0f19c361d
parent c16d30d0e6c9bfe1a961bf67ecad24e6b171c948
Author: Ben Dean-Kawamura <bdk@mozilla.com>
Date: Thu, 2 Oct 2025 15:37:57 +0000
Bug 1989840 - Adding JS tests, r=markh,robwu
This was fixed in the app-services code, but I wanted to add some JS
tests to make sure we don't regress.
Differential Revision: https://phabricator.services.mozilla.com/D265971
Diffstat:
2 files changed, 88 insertions(+), 0 deletions(-)
diff --git a/toolkit/components/extensions/test/xpcshell/head_storage.js b/toolkit/components/extensions/test/xpcshell/head_storage.js
@@ -209,6 +209,40 @@ async function test_background_page_storage(testAreaName) {
);
}
+ // Regression test https://bugzilla.mozilla.org/show_bug.cgi?id=1989840
+ async function testDeeplyNestedObject(areaName) {
+ const obj = {};
+ let current = obj;
+ for (let i = 0; i < 127; i++) {
+ const next = {};
+ current.foo = next;
+ current = next;
+ }
+ let storage = browser.storage[areaName];
+ if (areaName == "sync") {
+ // TODO(bug 1990313)
+ // Storing a deeply nested object currently fails for the Rust sync storage, but not Kinto.
+ // Let's make sure it doesn't crash in any case.
+ let errorMessage = null;
+ try {
+ await storage.set(obj);
+ } catch (e) {
+ errorMessage = e.toString();
+ // The caller expects one storage event. To make sure that the event is
+ // fired, trigger a dummy change.
+ storage.set({ foo: "dummy" });
+ }
+ browser.test.assertTrue(
+ errorMessage === null ||
+ errorMessage.includes("An unexpected error occurred"),
+ `Invalid exception message when storing deeply nested object: ${errorMessage}`
+ );
+ } else {
+ // Storing a deeply nested object should succeed for non-sync engines.
+ await storage.set(obj);
+ }
+ }
+
async function testFalseyValues(areaName) {
let storage = browser.storage[areaName];
const dataInitial = {
@@ -625,6 +659,13 @@ async function test_background_page_storage(testAreaName) {
clearGlobalChanges();
await storage.clear();
await globalChanges;
+
+ clearGlobalChanges();
+ await testDeeplyNestedObject(areaName);
+ await globalChanges;
+ clearGlobalChanges();
+ await storage.clear();
+ await globalChanges;
} catch (e) {
browser.test.fail(`Error: ${e} :: ${e.stack}`);
browser.test.notifyFail("storage");
diff --git a/toolkit/components/extensions/test/xpcshell/test_StorageSyncService.js b/toolkit/components/extensions/test/xpcshell/test_StorageSyncService.js
@@ -260,3 +260,50 @@ add_task(async function test_storage_sync_quota() {
"should reject with QuotaError"
);
});
+
+// Test trying to storing invalid JSON
+//
+// The main test is that this doesn't crash the browser:
+// https://bugzilla.mozilla.org/show_bug.cgi?id=1989840
+add_task(async function test_invalid_json() {
+ let engine = new ExtensionStorageEngineBridge(Service);
+ await engine.initialize();
+ let service = engine._rustStore;
+
+ await engine._bridge.wipe();
+ await Assert.rejects(
+ service.set("ext-1", "{invalid-json"),
+ /Failed to convert arg 'val'/,
+ "should reject invalid JSON"
+ );
+});
+
+// Test sending deeply-nested objects
+//
+// Right now this fails, although it doesn't need to.
+// For example, the sync engine only cares about top-level object members,
+// so we could call `JSON.stringify` on each value
+// and send the raw JSON string across the FFI.
+//
+// Let's wait for getting more info before doing anything though:
+// https://bugzilla.mozilla.org/show_bug.cgi?id=1990313
+add_task(async function test_storage_sync_recursion_limit() {
+ let engine = new ExtensionStorageEngineBridge(Service);
+ await engine.initialize();
+ let service = engine._rustStore;
+
+ await engine._bridge.wipe();
+ // Construct an object with lots of nested fields
+ const obj = {};
+ let current = obj;
+ for (let i = 0; i < 127; i++) {
+ const next = {};
+ current.foo = next;
+ current = next;
+ }
+ await Assert.rejects(
+ service.set("ext-1", JSON.stringify(obj)),
+ /Failed to convert arg 'val'/,
+ "should reject deeply-nested JSON"
+ );
+});