diff --git a/toolkit/components/extensions/ExtensionStorage.jsm b/toolkit/components/extensions/ExtensionStorage.jsm index 02e496a56ad5..c09e58965409 100644 --- a/toolkit/components/extensions/ExtensionStorage.jsm +++ b/toolkit/components/extensions/ExtensionStorage.jsm @@ -81,12 +81,7 @@ this.ExtensionStorage = { extData[prop] = items[prop]; } - let listeners = this.listeners.get(extensionId); - if (listeners) { - for (let listener of listeners) { - listener(changes); - } - } + this.notifyListeners(extensionId, changes); return this.write(extensionId); }); @@ -106,13 +101,24 @@ this.ExtensionStorage = { delete extData[prop]; } - let listeners = this.listeners.get(extensionId); - if (listeners) { - for (let listener of listeners) { - listener(changes); + this.notifyListeners(extensionId, changes); + + return this.write(extensionId); + }); + }, + + clear(extensionId) { + return this.read(extensionId).then(extData => { + let changes = {}; + if (extData) { + for (let prop of Object.keys(extData)) { + changes[prop] = {oldValue: extData[prop]}; + delete extData[prop]; } } + this.notifyListeners(extensionId, changes); + return this.write(extensionId); }); }, @@ -158,6 +164,15 @@ this.ExtensionStorage = { listeners.delete(listener); }, + notifyListeners(extensionId, changes) { + let listeners = this.listeners.get(extensionId); + if (listeners) { + for (let listener of listeners) { + listener(changes); + } + } + }, + init() { Services.obs.addObserver(this, "extension-invalidate-storage-cache", false); Services.obs.addObserver(this, "xpcom-shutdown", false); diff --git a/toolkit/components/extensions/ext-storage.js b/toolkit/components/extensions/ext-storage.js index ae59b87f50ed..ed91dbd88fff 100644 --- a/toolkit/components/extensions/ext-storage.js +++ b/toolkit/components/extensions/ext-storage.js @@ -33,6 +33,13 @@ extensions.registerPrivilegedAPI("storage", (extension, context) => { } }); }, + clear: function(callback) { + ExtensionStorage.clear(extension.id).then(() => { + if (callback) { + runSafe(context, callback); + } + }); + }, }, onChanged: new EventManager(context, "storage.local.onChanged", fire => { diff --git a/toolkit/components/extensions/test/mochitest/test_ext_storage.html b/toolkit/components/extensions/test/mochitest/test_ext_storage.html index 180758489777..4b982da7442a 100644 --- a/toolkit/components/extensions/test/mochitest/test_ext_storage.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_storage.html @@ -31,6 +31,12 @@ function backgroundScript() { }); } + function clear(items) { + return new Promise(resolve => { + browser.storage.local.clear(resolve); + }); + } + function check(prop, value) { return get(null).then(data => { browser.test.assertEq(data[prop], value, "null getter worked for " + prop); @@ -110,6 +116,18 @@ function backgroundScript() { browser.test.assertFalse("test-prop1" in data, "prop1 absent"); browser.test.assertFalse("test-prop2" in data, "prop2 absent"); + // test storage.clear + }).then(() => { + return set({"test-prop1": "value1", "test-prop2": "value2"}); + }).then(() => { + return clear(); + }).then(() => { + checkChanges({"test-prop1": {oldValue: "value1"}, "test-prop2": {oldValue: "value2"}}); + return get(["test-prop1", "test-prop2"]); + }).then(data => { + browser.test.assertFalse("test-prop1" in data, "prop1 absent"); + browser.test.assertFalse("test-prop2" in data, "prop2 absent"); + // Test cache invalidation. }).then(() => { return set({"test-prop1": "value1", "test-prop2": "value2"});