Bug 1457071 - allow all extension-storage data to be deleted from the profile. r?glasserc draft
authorMark Hammond <mhammond@skippinet.com.au>
Thu, 26 Apr 2018 17:53:56 +1000
changeset 788750 8d18f3a5ff2c431c3550dc0a1e1b5d1470153532
parent 788735 63a0e2f626febb98d87d2543955ab99a653654ff
push id108083
push userbmo:markh@mozilla.com
push dateThu, 26 Apr 2018 23:25:05 +0000
reviewersglasserc
bugs1457071
milestone61.0a1
Bug 1457071 - allow all extension-storage data to be deleted from the profile. r?glasserc MozReview-Commit-ID: Dnb2kdcz1CH
services/sync/modules/engines/extension-storage.js
services/sync/tests/unit/test_extension_storage_engine.js
toolkit/components/extensions/ExtensionStorageSync.jsm
toolkit/components/extensions/test/xpcshell/test_ext_storage_sync.js
--- 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]);