Bug 986056 - DataStoreService should delete dataStores only when apps are uninstalled. r=ehsan, a=1.4+
authorAndrea Marchesini <amarchesini@mozilla.com>
Fri, 28 Mar 2014 17:41:00 +0000
changeset 192445 12cd6611587f0ce36d9f7f58e38ff3514d467920
parent 192444 11f743514fbde2299fdec536b2ddfabf24767198
child 192446 57ded8c7e044551191940341a31b15dec63d6ebc
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan, 1
bugs986056
milestone30.0a2
Bug 986056 - DataStoreService should delete dataStores only when apps are uninstalled. r=ehsan, a=1.4+
dom/datastore/DataStoreService.js
dom/datastore/tests/file_bug986056.html
dom/datastore/tests/file_bug986056.template.webapp
dom/datastore/tests/mochitest.ini
dom/datastore/tests/test_bug986056.html
--- a/dom/datastore/DataStoreService.js
+++ b/dom/datastore/DataStoreService.js
@@ -373,39 +373,58 @@ DataStoreService.prototype = {
         !(aAppId in this.accessStores[aName])) {
       return null;
     }
 
     return this.accessStores[aName][aAppId];
   },
 
   observe: function observe(aSubject, aTopic, aData) {
-    debug('getDataStores - aTopic: ' + aTopic);
+    debug('observe - aTopic: ' + aTopic);
     if (aTopic != 'webapps-clear-data') {
       return;
     }
 
     let params =
       aSubject.QueryInterface(Ci.mozIApplicationClearPrivateDataParams);
 
     // DataStore is explosed to apps, not browser content.
     if (params.browserOnly) {
       return;
     }
 
+    function isEmpty(aMap) {
+      for (var key in aMap) {
+        if (aMap.hasOwnProperty(key)) {
+          return false;
+        }
+      }
+      return true;
+    }
+
     for (let key in this.stores) {
       if (params.appId in this.stores[key]) {
         this.deleteDatabase(key, this.stores[key][params.appId].owner);
         delete this.stores[key][params.appId];
       }
 
-      if (!this.stores[key].length) {
+      if (isEmpty(this.stores[key])) {
         delete this.stores[key];
       }
     }
+
+    for (let key in this.accessStores) {
+      if (params.appId in this.accessStores[key]) {
+        delete this.accessStores[key][params.appId];
+      }
+
+      if (isEmpty(this.accessStores[key])) {
+        delete this.accessStores[key];
+      }
+    }
   },
 
   deleteDatabase: function(aName, aOwner) {
     debug("delete database: " + aName);
 
     let db = new DataStoreDB();
     db.init(aOwner, aName);
     db.delete();
new file mode 100644
--- /dev/null
+++ b/dom/datastore/tests/file_bug986056.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for DataStore - bug 986056 app</title>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+  <script type="application/javascript;version=1.7">
+
+  function cbError() {
+    alert('KO error');
+  }
+
+  navigator.getDataStores('foo').then(function(stores) {
+    alert("OK " + stores.length);
+  }, cbError);
+  </script>
+</pre>
+</body>
+</html>
+
copy from dom/datastore/tests/file_bug976311.template.webapp
copy to dom/datastore/tests/file_bug986056.template.webapp
--- a/dom/datastore/tests/mochitest.ini
+++ b/dom/datastore/tests/mochitest.ini
@@ -12,16 +12,18 @@ support-files =
   file_arrays.html
   file_sync.html
   file_bug924104.html
   file_certifiedApp.html
   file_keys.html
   file_duplicate.html
   file_bug976311.html
   file_bug976311.template.webapp
+  file_bug986056.html
+  file_bug986056.template.webapp
   file_event_maker.html
   file_event_receiver.html
 
 [test_app_install.html]
 [test_readonly.html]
 skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) # b2g-debug(debug-only failure; time out) b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
 [test_basic.html]
 [test_changes.html]
@@ -33,9 +35,10 @@ skip-if = (buildapp == 'b2g' && (toolkit
 [test_sync.html]
 skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) # b2g-debug(debug-only failure; time out) b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
 [test_bug924104.html]
 [test_certifiedApp.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure; time out
 [test_keys.html]
 [test_duplicate.html]
 [test_bug976311.html]
+[test_bug986056.html]
 [test_oop_events.html]
new file mode 100644
--- /dev/null
+++ b/dom/datastore/tests/test_bug986056.html
@@ -0,0 +1,146 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for DataStore - bug 986056</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+  <script type="application/javascript;version=1.7">
+
+  var gHostedManifestURL = 'http://test/tests/dom/datastore/tests/file_app.sjs?testToken=file_bug986056.html&template=file_bug986056.template.webapp';
+  var gHostedManifestURL2 = 'http://example.com/tests/dom/datastore/tests/file_app.sjs?testToken=file_bug986056.html&template=file_bug986056.template.webapp';
+  var gApps = [];
+
+  function cbError() {
+    ok(false, "Error callback invoked");
+    finish();
+  }
+
+  function installApp(aApp) {
+    var request = navigator.mozApps.install(aApp);
+    request.onerror = cbError;
+    request.onsuccess = function() {
+      gApps.push(request.result);
+      runTest();
+    }
+  }
+
+  function uninstallApp(aApp) {
+    var request = navigator.mozApps.mgmt.uninstall(aApp);
+    request.onerror = cbError;
+    request.onsuccess = runTest;
+  }
+
+  function testApp(aCount) {
+    var ifr = document.createElement('iframe');
+    ifr.setAttribute('mozbrowser', 'true');
+    ifr.setAttribute('mozapp', gApps[0].manifestURL);
+    ifr.setAttribute('src', gApps[0].manifest.launch_path);
+    var domParent = document.getElementById('content');
+
+    // Set us up to listen for messages from the app.
+    var listener = function(e) {
+      var message = e.detail.message;
+      if (/KO/.exec(message)) {
+        ok(false, "Message from app: " + message);
+      } else if (/^OK/.exec(message)) {
+        is(message, "OK " + aCount, "Number of dataStore matches");
+        ifr.removeEventListener('mozbrowsershowmodalprompt', listener);
+        domParent.removeChild(ifr);
+        runTest();
+      }
+    }
+
+    // This event is triggered when the app calls "alert".
+    ifr.addEventListener('mozbrowsershowmodalprompt', listener, false);
+    domParent.appendChild(ifr);
+  }
+
+  var tests = [
+    // Permissions
+    function() {
+      SpecialPowers.pushPermissions(
+        [{ "type": "browser", "allow": 1, "context": document },
+         { "type": "embed-apps", "allow": 1, "context": document },
+         { "type": "webapps-manage", "allow": 1, "context": document }], runTest);
+    },
+
+    // Preferences
+    function() {
+      SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
+                                         ["dom.testing.ignore_ipc_principal", true],
+                                         ["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest);
+    },
+
+    // Enabling mozBrowser
+    function() {
+      SpecialPowers.setAllAppsLaunchable(true);
+      SpecialPowers.pushPrefEnv({"set": [["dom.mozBrowserFramesEnabled", true]]}, runTest);
+    },
+
+    // No confirmation needed when an app is installed
+    function() {
+      SpecialPowers.autoConfirmAppInstall(runTest);
+    },
+
+    // Installing the app1
+    function() { installApp(gHostedManifestURL); },
+
+    // Installing the app2
+    function() { installApp(gHostedManifestURL2); },
+
+    // Run tests in app. 2 apps are installed so we want to have 2 'foo'
+    // dataStore.
+    function() { testApp(2); },
+
+    // Uninstall the first app
+    function() { uninstallApp(gApps.shift()); },
+
+    // Run tests in app. 1 single apps is installed so we want to have 1 'foo'
+    // dataStore.
+    function() { testApp(1); },
+
+    // Installing the app1
+    function() { installApp(gHostedManifestURL); },
+
+    // Run tests in app. Back to 2 apps, 2 datastores.
+    function() { testApp(2); },
+
+    // Uninstall the first app
+    function() { uninstallApp(gApps.shift()); },
+
+    // Uninstall the second app
+    function() { uninstallApp(gApps.shift()); }
+  ];
+
+  function runTest() {
+    if (!tests.length) {
+      finish();
+      return;
+    }
+
+    var test = tests.shift();
+    test();
+  }
+
+  function finish() {
+    SimpleTest.finish();
+  }
+
+  if (SpecialPowers.isMainProcess()) {
+    SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  runTest();
+  </script>
+</pre>
+</body>
+</html>