commit 13aa8ab1d77ff5c42b93c8b1ac06353adf939de3
parent 04b66025cf4f6065f50fbac3da5e7fed42ca7640
Author: Rob Wu <rob@robwu.nl>
Date: Wed, 1 Oct 2025 17:21:12 +0000
Bug 1990529 - Log unsanitized error on internal storage.sync r=rpl
Differential Revision: https://phabricator.services.mozilla.com/D266019
Diffstat:
3 files changed, 63 insertions(+), 9 deletions(-)
diff --git a/toolkit/components/extensions/ExtensionStorageSync.sys.mjs b/toolkit/components/extensions/ExtensionStorageSync.sys.mjs
@@ -70,13 +70,17 @@ export class ExtensionStorageSync {
} catch (ex) {
// The only "public" exception here is for quota failure - all others
// are sanitized.
- let sanitized =
- ex instanceof lazy.QuotaError
- ? // The same message as the local IDB implementation
- "QuotaExceededError: storage.sync API call exceeded its quota limitations."
- : // The standard, generic extension error.
- "An unexpected error occurred";
- throw new lazy.ExtensionUtils.ExtensionError(sanitized);
+ if (ex instanceof lazy.QuotaError) {
+ // The same message as the local IDB implementation
+ throw new lazy.ExtensionUtils.ExtensionError(
+ "QuotaExceededError: storage.sync API call exceeded its quota limitations."
+ );
+ }
+ Cu.reportError(ex);
+ // The standard, generic extension error.
+ throw new lazy.ExtensionUtils.ExtensionError(
+ "An unexpected error occurred"
+ );
}
}
diff --git a/toolkit/components/extensions/test/xpcshell/head_storage.js b/toolkit/components/extensions/test/xpcshell/head_storage.js
@@ -788,7 +788,7 @@ async function check_storage_sync_getBytesInUse(area, expectQuota) {
if (expectQuota) {
await browser.test.assertRejects(
impl.set({ x: value + "x" }),
- /QuotaExceededError/,
+ "QuotaExceededError: storage.sync API call exceeded its quota limitations.",
"Got a rejection with the expected error message"
);
// MAX_ITEMS
@@ -800,7 +800,7 @@ async function check_storage_sync_getBytesInUse(area, expectQuota) {
await impl.set(ob); // should work.
await browser.test.assertRejects(
impl.set({ straw: "camel's back" }), // exceeds MAX_ITEMS
- /QuotaExceededError/,
+ "QuotaExceededError: storage.sync API call exceeded its quota limitations.",
"Got a rejection with the expected error message"
);
// QUOTA_BYTES is being already tested for the underlying StorageSyncService
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_storage_sync.js b/toolkit/components/extensions/test/xpcshell/test_ext_storage_sync.js
@@ -34,3 +34,53 @@ add_task(async function test_storage_onChanged_event_page() {
add_task(async function test_storage_session_getBytesInUse() {
await test_get_bytes_in_use("sync");
});
+
+add_task(async function test_storage_sync_sanitizes_internal_error() {
+ const extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ permissions: ["storage"],
+ browser_specific_settings: { gecko: { id: "test@storage-sync-err" } },
+ },
+ background() {
+ browser.test.onMessage.addListener(async msg => {
+ browser.test.assertEq(msg, "call_storage_sync", "Expected message");
+ await browser.test.assertRejects(
+ browser.storage.sync.get(null),
+ "An unexpected error occurred",
+ "Internal error from storage.sync implementation is sanitized"
+ );
+ browser.test.sendMessage("done");
+ });
+ // Call any storage.sync API, to make sure that recordSyncQuotaTelemetry
+ // in parent/ext-storage.js is called, so that its logic is skipped when
+ // we call storage.sync.get(null) again in this test. Otherwise the mock
+ // below that fakes an error will be tripped and cause
+ // recordSyncQuotaTelemetry to raise an unrejected promise rejection,
+ // which causes the test to fail.
+ browser.storage.sync.get(null).then(() => {
+ browser.test.sendMessage("ready_to_call_sync");
+ });
+ },
+ });
+ await extension.startup();
+ await extension.awaitMessage("ready_to_call_sync");
+ const { messages } = await promiseConsoleOutput(async () => {
+ const { storageSyncService } = ChromeUtils.importESModule(
+ "resource://gre/modules/ExtensionStorageComponents.sys.mjs"
+ );
+ const orig = storageSyncService._storageAreaPromise;
+ storageSyncService._storageAreaPromise = Promise.reject(
+ new Error("Some fake internal error")
+ );
+ try {
+ extension.sendMessage("call_storage_sync");
+ await extension.awaitMessage("done");
+ } finally {
+ storageSyncService._storageAreaPromise = orig;
+ }
+ });
+ AddonTestUtils.checkMessages(messages, {
+ expected: [{ message: /Some fake internal error/ }],
+ });
+ await extension.unload();
+});