Bug 1457071 - allow all extension-storage data to be deleted from the profile. r=glasserc
authorMark Hammond <mhammond@skippinet.com.au>
Thu, 26 Apr 2018 17:53:56 +1000
changeset 415931 bea414aba9b9e74cd5e1feecf63b1cf0bc240279
parent 415930 8141b60030b908c1f291979d72e0f21db8206c5c
child 415932 3f80d2aaef435081f05551eae1a5c9b42302d56b
push id33911
push usercsabou@mozilla.com
push dateFri, 27 Apr 2018 10:01:39 +0000
treeherdermozilla-central@822936017145 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersglasserc
bugs1457071
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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]);