Bug 1457071 - allow all extension-storage data to be deleted from the profile. r?glasserc
MozReview-Commit-ID: Dnb2kdcz1CH
--- a/services/sync/modules/engines/extension-storage.js
+++ b/services/sync/modules/engines/extension-storage.js
@@ -46,16 +46,21 @@ ExtensionStorageEngine.prototype = {
// can be set to true or false, if a power user wants to customize
// the behavior despite the lack of UI.
const forced = Svc.Prefs.get("engine." + this.prefName + ".force", undefined);
if (forced !== undefined) {
return forced;
}
return Svc.Prefs.get("engine.addons", false);
},
+
+ _wipeClient() {
+ return extensionStorageSync.clearAll();
+ },
+
};
function ExtensionStorageTracker(name, engine) {
Tracker.call(this, name, engine);
}
ExtensionStorageTracker.prototype = {
__proto__: Tracker.prototype,
--- a/services/sync/tests/unit/test_extension_storage_engine.js
+++ b/services/sync/tests/unit/test_extension_storage_engine.js
@@ -43,16 +43,27 @@ add_task(async function test_calling_syn
// first, which fails.
await engine.sync();
} finally {
ExtensionStorageEngine.prototype._sync = oldSync;
}
equal(syncMock.calls.length, 1);
});
+add_task(async function test_calling_wipeClient_calls_clearAll() {
+ let oldClearAll = extensionStorageSync.clearAll;
+ let clearMock = extensionStorageSync.clearAll = mock({returns: Promise.resolve()});
+ try {
+ await engine.wipeClient();
+ } finally {
+ extensionStorageSync.clearAll = oldClearAll;
+ }
+ equal(clearMock.calls.length, 1);
+});
+
add_task(async function test_calling_sync_calls_ext_storage_sync() {
const extension = {id: "my-extension"};
let oldSync = extensionStorageSync.syncAll;
let syncMock = extensionStorageSync.syncAll = mock({returns: Promise.resolve()});
try {
await withSyncContext(async function(context) {
// Set something so that everyone knows that we're using storage.sync
await extensionStorageSync.set(extension, {"a": "b"}, context);
--- a/toolkit/components/extensions/ExtensionStorageSync.jsm
+++ b/toolkit/components/extensions/ExtensionStorageSync.jsm
@@ -1158,16 +1158,35 @@ class ExtensionStorageSync {
}, {preloadIds: ids});
if (Object.keys(changes).length > 0) {
this.notifyListeners(extension, changes);
}
const histogram = this._telemetry.getKeyedHistogramById(HISTOGRAM_REMOVE_OPS);
histogram.add(extension.id, keys.length);
}
+ /* Wipe local data for all collections without causing the changes to be synced */
+ async clearAll() {
+ const extensions = extensionContexts.keys();
+ const extIds = Array.from(extensions, extension => extension.id);
+ log.debug(`Clearing extension data for ${JSON.stringify(extIds)}`);
+ if (extIds.length) {
+ const promises = Array.from(extensionContexts.keys(), extension => {
+ return openCollection(this.cryptoCollection, extension).then(coll => {
+ return coll.clear();
+ });
+ });
+ await Promise.all(promises);
+ }
+
+ // and clear the crypto collection.
+ const cc = await this.cryptoCollection.getCollection();
+ await cc.clear();
+ }
+
async clear(extension, context) {
// We can't call Collection#clear here, because that just clears
// the local database. We have to explicitly delete everything so
// that the deletions can be synced as well.
const coll = await this.getCollection(extension, context);
const res = await coll.list();
const records = res.data;
const keys = records.map(record => record.key);
--- a/toolkit/components/extensions/test/xpcshell/test_ext_storage_sync.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_storage_sync.js
@@ -571,16 +571,49 @@ add_task(async function test_extension_i
const cryptoCollection = new CryptoCollection(fxaService);
await cryptoCollection._setSalt(extensionId, salt);
equal(await cryptoCollection.extensionIdToCollectionId(extensionId),
"ext-0_QHA1P93_yJoj7ONisrR0lW6uN4PZ3Ii-rT-QOjtvo");
});
});
+add_task(async function ensureCanSync_clearAll() {
+ const extensionId = uuid();
+ const extension = {id: extensionId};
+
+ await withContextAndServer(async function(context, server) {
+ await withSignedInUser(loggedInUser, async function(extensionStorageSync, fxaService) {
+ server.installCollection("storage-sync-crypto");
+ server.etag = 1000;
+
+ let newKeys = await extensionStorageSync.ensureCanSync([extensionId]);
+ ok(newKeys.hasKeysFor([extensionId]), `key isn't present for ${extensionId}`);
+
+ let posts = server.getPosts();
+ equal(posts.length, 1);
+ const post = posts[0];
+ assertPostedNewRecord(post);
+
+ // Set data for an extension and sync.
+ await extensionStorageSync.set(extension, {"my-key": 5}, context);
+ let keyValue = await extensionStorageSync.get(extension, ["my-key"], context);
+ equal(keyValue["my-key"], 5, "should get back the data we set");
+
+ // clear everything.
+ await extensionStorageSync.clearAll();
+
+ keyValue = await extensionStorageSync.get(extension, ["my-key"], context);
+ deepEqual(keyValue, {}, "should have lost the data");
+ // should have been no posts caused by the clear.
+ equal(posts.length, 1);
+ });
+ });
+});
+
add_task(async function ensureCanSync_posts_new_keys() {
const extensionId = uuid();
await withContextAndServer(async function(context, server) {
await withSignedInUser(loggedInUser, async function(extensionStorageSync, fxaService) {
server.installCollection("storage-sync-crypto");
server.etag = 1000;
let newKeys = await extensionStorageSync.ensureCanSync([extensionId]);