Bug 1406181 - Test storage.local cleanup on uninstall on file and indexedDB backends. draft
authorLuca Greco <lgreco@mozilla.com>
Thu, 19 Oct 2017 16:00:52 +0200
changeset 802152 932ffddab02569442961667272bfdc485d236715
parent 802151 30900289c6bf259bb258dc23638ee062fa5135f0
child 802153 545d8d48c4b148c9e5554e92f1fb2ddf78890b24
push id111840
push userluca.greco@alcacoop.it
push dateThu, 31 May 2018 15:14:50 +0000
bugs1406181
milestone62.0a1
Bug 1406181 - Test storage.local cleanup on uninstall on file and indexedDB backends. MozReview-Commit-ID: H8W8ry1dQIt
toolkit/components/extensions/test/mochitest/test_ext_storage_cleanup.html
--- a/toolkit/components/extensions/test/mochitest/test_ext_storage_cleanup.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_storage_cleanup.html
@@ -8,157 +8,229 @@
   <script type="text/javascript" src="head.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 
 <script type="text/javascript">
 "use strict";
 
-// Test that storage used by a webextension (through localStorage,
-// indexedDB, and browser.storage.local) gets cleaned up when the
-// extension is uninstalled.
-add_task(async function test_uninstall() {
-  function writeData() {
-    localStorage.setItem("hello", "world");
+const {
+  ExtensionStorageIDB,
+} = SpecialPowers.Cu.import("resource://gre/modules/ExtensionStorageIDB.jsm");
 
-    let idbPromise = new Promise((resolve, reject) => {
-      let req = indexedDB.open("test");
-      req.onerror = e => {
-        reject(new Error(`indexedDB open failed with ${e.errorCode}`));
-      };
+const storageTestHelpers = {
+  storageLocal: {
+    async writeData() {
+      await browser.storage.local.set({hello: "world"});
+      browser.test.sendMessage("finished");
+    },
 
-      req.onupgradeneeded = e => {
-        let db = e.target.result;
-        db.createObjectStore("store", {keyPath: "name"});
-      };
+    async readData() {
+      const matchBrowserStorage = await browser.storage.local.get("hello").then(result => {
+        return (Object.keys(result).length == 1 && result.hello == "world");
+      });
+
+      browser.test.sendMessage("results", {matchBrowserStorage});
+    },
 
-      req.onsuccess = e => {
-        let db = e.target.result;
-        let transaction = db.transaction("store", "readwrite");
-        let addreq = transaction.objectStore("store")
-                                .add({name: "hello", value: "world"});
-        addreq.onerror = addreqError => {
-          reject(new Error(`add to indexedDB failed with ${addreqError.errorCode}`));
-        };
-        addreq.onsuccess = () => {
-          resolve();
+    assertResults({results, keepOnUninstall}) {
+      if (keepOnUninstall) {
+        is(results.matchBrowserStorage, true, "browser.storage.local data is still present");
+      } else {
+        is(results.matchBrowserStorage, false, "browser.storage.local data was cleared");
+      }
+    },
+  },
+  webAPIs: {
+    async readData() {
+      let matchLocalStorage = (localStorage.getItem("hello") == "world");
+
+      let idbPromise = new Promise((resolve, reject) => {
+        let req = indexedDB.open("test");
+        req.onerror = e => {
+          reject(new Error(`indexedDB open failed with ${e.errorCode}`));
         };
-      };
-    });
 
-    let browserStoragePromise = browser.storage.local.set({hello: "world"});
-
-    Promise.all([idbPromise, browserStoragePromise]).then(() => {
-      browser.test.sendMessage("finished");
-    });
-  }
-
-  function readData() {
-    let matchLocalStorage = (localStorage.getItem("hello") == "world");
+        req.onupgradeneeded = e => {
+          // no database, data is not present
+          resolve(false);
+        };
 
-    let idbPromise = new Promise((resolve, reject) => {
-      let req = indexedDB.open("test");
-      req.onerror = e => {
-        reject(new Error(`indexedDB open failed with ${e.errorCode}`));
-      };
-
-      req.onupgradeneeded = e => {
-        // no database, data is not present
-        resolve(false);
-      };
+        req.onsuccess = e => {
+          let db = e.target.result;
+          let transaction = db.transaction("store", "readwrite");
+          let addreq = transaction.objectStore("store").get("hello");
+          addreq.onerror = addreqError => {
+            reject(new Error(`read from indexedDB failed with ${addreqError.errorCode}`));
+          };
+          addreq.onsuccess = () => {
+            let match = (addreq.result.value == "world");
+            resolve(match);
+          };
+        };
+      });
 
-      req.onsuccess = e => {
-        let db = e.target.result;
-        let transaction = db.transaction("store", "readwrite");
-        let addreq = transaction.objectStore("store").get("hello");
-        addreq.onerror = addreqError => {
-          reject(new Error(`read from indexedDB failed with ${addreqError.errorCode}`));
+      await idbPromise.then(matchIDB => {
+        let result = {matchLocalStorage, matchIDB};
+        browser.test.sendMessage("results", result);
+      });
+    },
+
+    async writeData() {
+      localStorage.setItem("hello", "world");
+
+      let idbPromise = new Promise((resolve, reject) => {
+        let req = indexedDB.open("test");
+        req.onerror = e => {
+          reject(new Error(`indexedDB open failed with ${e.errorCode}`));
         };
-        addreq.onsuccess = () => {
-          let match = (addreq.result.value == "world");
-          resolve(match);
+
+        req.onupgradeneeded = e => {
+          let db = e.target.result;
+          db.createObjectStore("store", {keyPath: "name"});
         };
-      };
-    });
 
-    let browserStoragePromise = browser.storage.local.get("hello").then(result => {
-      return (Object.keys(result).length == 1 && result.hello == "world");
-    });
+        req.onsuccess = e => {
+          let db = e.target.result;
+          let transaction = db.transaction("store", "readwrite");
+          let addreq = transaction.objectStore("store")
+                                  .add({name: "hello", value: "world"});
+          addreq.onerror = addreqError => {
+            reject(new Error(`add to indexedDB failed with ${addreqError.errorCode}`));
+          };
+          addreq.onsuccess = () => {
+            resolve();
+          };
+        };
+      });
 
-    Promise.all([idbPromise, browserStoragePromise])
-           .then(([matchIDB, matchBrowserStorage]) => {
-             let result = {matchLocalStorage, matchIDB, matchBrowserStorage};
-             browser.test.sendMessage("results", result);
-           });
-  }
-
-  const ID = "storage.cleanup@tests.mozilla.org";
+      await idbPromise.then(() => {
+        browser.test.sendMessage("finished");
+      });
+    },
 
-  // Use a test-only pref to leave the addonid->uuid mapping around after
-  // uninstall so that we can re-attach to the same storage.  Also set
-  // the pref to prevent cleaning up storage on uninstall so we can test
-  // that the "keep uuid" logic works correctly.  Do the storage flag in
-  // a separate prefEnv so we can pop it below, leaving the uuid flag set.
-  await SpecialPowers.pushPrefEnv({
-    set: [["extensions.webextensions.keepUuidOnUninstall", true]],
-  });
+    assertResults({results, keepOnUninstall}) {
+      if (keepOnUninstall) {
+        is(results.matchLocalStorage, true, "localStorage data is still present");
+        is(results.matchIDB, true, "indexedDB data is still present");
+      } else {
+        is(results.matchLocalStorage, false, "localStorage data was cleared");
+        is(results.matchIDB, false, "indexedDB data was cleared");
+      }
+    },
+  },
+};
+
+async function test_uninstall({extensionId, writeData, readData, assertResults}) {
+  // Set the pref to prevent cleaning up storage on uninstall in a separate prefEnv
+  // so we can pop it below, leaving flags set in the previous prefEnvs unmodified.
   await SpecialPowers.pushPrefEnv({
     set: [["extensions.webextensions.keepStorageOnUninstall", true]],
   });
 
   let extension = ExtensionTestUtils.loadExtension({
     background: writeData,
     manifest: {
-      applications: {gecko: {id: ID}},
+      applications: {gecko: {id: extensionId}},
       permissions: ["storage"],
     },
     useAddonManager: "temporary",
   });
 
   await extension.startup();
   await extension.awaitMessage("finished");
   await extension.unload();
 
   // Check that we can still see data we wrote to storage but clear the
-  // "leave storage" flag so our storaged gets cleared on uninstall.
+  // "leave storage" flag so our storaged gets cleared on the next uninstall.
   // This effectively tests the keepUuidOnUninstall logic, which ensures
   // that when we read storage again and check that it is cleared, that
   // it is actually a meaningful test!
   await SpecialPowers.popPrefEnv();
+
   extension = ExtensionTestUtils.loadExtension({
     background: readData,
     manifest: {
-      applications: {gecko: {id: ID}},
+      applications: {gecko: {id: extensionId}},
       permissions: ["storage"],
     },
     useAddonManager: "temporary",
   });
 
   await extension.startup();
   let results = await extension.awaitMessage("results");
-  is(results.matchLocalStorage, true, "localStorage data is still present");
-  is(results.matchIDB, true, "indexedDB data is still present");
-  is(results.matchBrowserStorage, true, "browser.storage.local data is still present");
+
+  assertResults({results, keepOnUninstall: true});
 
   await extension.unload();
 
   // Read again.  This time, our data should be gone.
   extension = ExtensionTestUtils.loadExtension({
     background: readData,
     manifest: {
-      applications: {gecko: {id: ID}},
+      applications: {gecko: {id: extensionId}},
       permissions: ["storage"],
     },
     useAddonManager: "temporary",
   });
 
   await extension.startup();
   results = await extension.awaitMessage("results");
-  is(results.matchLocalStorage, false, "localStorage data was cleared");
-  is(results.matchIDB, false, "indexedDB data was cleared");
-  is(results.matchBrowserStorage, false, "browser.storage.local data was cleared");
+
+  assertResults({results, keepOnUninstall: false});
+
   await extension.unload();
+}
+
+
+add_task(async function test_setup_keep_uuid_on_uninstall() {
+  // Use a test-only pref to leave the addonid->uuid mapping around after
+  // uninstall so that we can re-attach to the same storage (this prefEnv
+  // is kept for this entire file and cleared automatically once all the
+  // tests in this file have been executed).
+  await SpecialPowers.pushPrefEnv({
+    set: [["extensions.webextensions.keepUuidOnUninstall", true]],
+  });
 });
+
+// Test extension indexedDB and localStorage storages get cleaned up when the
+// extension is uninstalled.
+add_task(async function test_uninstall_with_webapi_storages() {
+  await test_uninstall({
+    extensionId: "storage.cleanup-WebAPIStorages@tests.mozilla.org",
+    ...(storageTestHelpers.webAPIs),
+  });
+});
+
+// Test browser.storage.local with JSONFile backend gets cleaned up when the
+// extension is uninstalled.
+add_task(async function test_uninistall_with_storage_local_file_backend() {
+  await SpecialPowers.pushPrefEnv({
+    set: [[ExtensionStorageIDB.BACKEND_ENABLED_PREF, false]],
+  });
+
+  await test_uninstall({
+    extensionId: "storage.cleanup-JSONFileBackend@tests.mozilla.org",
+    ...(storageTestHelpers.storageLocal),
+  });
+
+  await SpecialPowers.pushPrefEnv();
+});
+
+// Repeat the cleanup test when the storage.local IndexedDB backend is enabled.
+add_task(async function test_uninistall_with_storage_local_idb_backend() {
+  await SpecialPowers.pushPrefEnv({
+    set: [[ExtensionStorageIDB.BACKEND_ENABLED_PREF, true]],
+  });
+
+  await test_uninstall({
+    extensionId: "storage.cleanup-IDBBackend@tests.mozilla.org",
+    ...(storageTestHelpers.storageLocal),
+  });
+
+  await SpecialPowers.pushPrefEnv();
+});
+
 </script>
 
 </body>
 </html>