Bug 976311 - DataStore checks if the messages are for itself, r=ehsan
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 26 Feb 2014 17:38:38 +0000
changeset 171046 0c8703fab0c991df30768f53ca2ca9c3c6d1e42f
parent 171045 fb4422a671dcd248a4a0d326143b96e2ff25dc9c
child 171047 960a7f055c95d6d304a2f2e82f7ffebedf36414d
child 171080 e840b5a70371d3776a6b2fa8c5e6bb6568e72780
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersehsan
bugs976311
milestone30.0a1
Bug 976311 - DataStore checks if the messages are for itself, r=ehsan
dom/datastore/DataStore.jsm
dom/datastore/DataStoreChangeNotifier.jsm
dom/datastore/tests/file_bug976311.html
dom/datastore/tests/file_bug976311.template.webapp
dom/datastore/tests/mochitest.ini
dom/datastore/tests/test_bug976311.html
--- a/dom/datastore/DataStore.jsm
+++ b/dom/datastore/DataStore.jsm
@@ -305,26 +305,33 @@ this.DataStore.prototype = {
   receiveMessage: function(aMessage) {
     debug("receiveMessage");
 
     if (aMessage.name != "DataStore:Changed:Return:OK") {
       debug("Wrong message: " + aMessage.name);
       return;
     }
 
+    // If this message is not for this DataStore, let's ignore it.
+    if (aMessage.data.owner != this._owner ||
+        aMessage.data.store != this._name) {
+      return;
+    }
+
     let self = this;
 
     this.retrieveRevisionId(
       function() {
         // If we have an active cursor we don't emit events.
         if (self._cursor) {
           return;
         }
 
-        let event = new self._window.DataStoreChangeEvent('change', aMessage.data);
+        let event = new self._window.DataStoreChangeEvent('change',
+                                                          aMessage.data.message);
         self.__DOM_IMPL__.dispatchEvent(event);
       }
     );
   },
 
   get exposedObject() {
     debug("get exposedObject");
     return this._exposedObject;
--- a/dom/datastore/DataStoreChangeNotifier.jsm
+++ b/dom/datastore/DataStoreChangeNotifier.jsm
@@ -51,42 +51,35 @@ this.DataStoreChangeNotifier = {
         break;
 
       default:
         debug("Wrong observer topic: " + aTopic);
         break;
     }
   },
 
-  broadcastMessage: function broadcastMessage(aMsgName, aData) {
-    debug("Broadast");
-    this.children.forEach(function(obj) {
-      if (obj.store == aData.store && obj.owner == aData.owner) {
-        obj.mm.sendAsyncMessage(aMsgName, aData.message);
-      }
-    });
-  },
-
   receiveMessage: function(aMessage) {
     debug("receiveMessage");
 
     // No check has to be done when the message is 'child-process-shutdown'
     // because at this point the target is already disconnected from
     // nsFrameMessageManager, so that assertAppHasStatus will always fail.
     let prefName = 'dom.testing.datastore_enabled_for_hosted_apps';
     if (aMessage.name != 'child-process-shutdown' &&
         (Services.prefs.getPrefType(prefName) == Services.prefs.PREF_INVALID ||
          !Services.prefs.getBoolPref(prefName)) &&
         !aMessage.target.assertAppHasStatus(Ci.nsIPrincipal.APP_STATUS_CERTIFIED)) {
       return;
     }
 
     switch (aMessage.name) {
       case "DataStore:Changed":
-        this.broadcastMessage("DataStore:Changed:Return:OK", aMessage.data);
+        debug("Broadasting message.");
+        let childMM = aMessage.target.QueryInterface(Ci.nsIMessageSender);
+        childMM.sendAsyncMessage("DataStore:Changed:Return:OK", aMessage.data);
         break;
 
       case "DataStore:RegisterForMessages":
         debug("Register!");
 
         for (let i = 0; i < this.children.length; ++i) {
           if (this.children[i].mm == aMessage.target &&
               this.children[i].store == aMessage.data.store &&
new file mode 100644
--- /dev/null
+++ b/dom/datastore/tests/file_bug976311.html
@@ -0,0 +1,88 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for DataStore - bug 976311 app1</title>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+  <script type="application/javascript;version=1.7">
+
+  var gStores = [];
+  var expectedWhere;
+
+  function is(a, b, msg) {
+    ok(a === b, msg);
+  }
+
+  function ok(a, msg) {
+    alert((a ? 'OK' : 'KO')+ ' ' + msg)
+  }
+
+  function cbError() {
+    alert('KO error');
+  }
+
+  function finish() {
+    alert('DONE');
+  }
+
+  function checkEvent(id) {
+    is(expectedWhere, id, "Message on the correct DS: " + id + " " + expectedWhere);
+    runTest();
+  }
+
+  function testGetDataStores() {
+    navigator.getDataStores('foo').then(function(stores) {
+      is(stores.length, 2, "getDataStores('foo') returns 1 element");
+
+      is(stores[0].name, 'foo', 'The dataStore.name is foo');
+      is(stores[0].readOnly, false, 'The dataStore foo is not in readonly');
+      stores[0].onchange = function(evt) { checkEvent(0); }
+      gStores.push(stores[0]);
+
+      is(stores[1].name, 'foo', 'The dataStore.name is foo');
+      is(stores[1].readOnly, false, 'The dataStore foo is not in readonly');
+      stores[1].onchange = function(evt) { checkEvent(1); }
+      gStores.push(stores[1]);
+
+      runTest();
+    }, cbError);
+  }
+
+  function testStoreAdd(where, value) {
+    expectedWhere = where;
+    dump("ADD TO: " + gStores[where].owner + "\n");
+    gStores[where].add({ a: value });
+  }
+
+  var tests = [
+    // Test for GetDataStore
+    testGetDataStores,
+
+    function() { testStoreAdd(0, 1); },
+    function() { testStoreAdd(1, 2); },
+    function() { testStoreAdd(0, 3); },
+    function() { testStoreAdd(1, 4); }
+  ];
+
+  function runTest() {
+    if (!tests.length) {
+      finish();
+      return;
+    }
+
+    var test = tests.shift();
+    test();
+  }
+
+  runTest();
+  </script>
+</pre>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/datastore/tests/file_bug976311.template.webapp
@@ -0,0 +1,12 @@
+{
+  "name": "Really Rapid Release (hosted)",
+  "description": "Updated even faster than <a href='http://mozilla.org'>Firefox</a>, just to annoy slashdotters.",
+  "launch_path": "/tests/dom/datastore/tests/TESTTOKEN",
+  "icons": { "128": "default_icon" },
+  "datastores-owned" : {
+    "foo" : { "access": "readwrite", "description" : "This store is called foo" }
+  },
+  "datastores-access" : {
+    "foo" : { "readonly": false, "description" : "This store is called foo" }
+  }
+}
--- a/dom/datastore/tests/mochitest.ini
+++ b/dom/datastore/tests/mochitest.ini
@@ -9,20 +9,23 @@ support-files =
   file_app.template.webapp
   file_app2.template.webapp
   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
 
 [test_app_install.html]
 [test_readonly.html]
 [test_basic.html]
 [test_changes.html]
 [test_arrays.html]
 [test_oop.html]
 [test_sync.html]
 [test_bug924104.html]
 [test_certifiedApp.html]
 [test_keys.html]
 [test_duplicate.html]
+[test_bug976311.html]
new file mode 100644
--- /dev/null
+++ b/dom/datastore/tests/test_bug976311.html
@@ -0,0 +1,135 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for DataStore - bug 976311</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<div id="container"></div>
+  <script type="application/javascript;version=1.7">
+
+  var gHostedManifestURL = 'http://test/tests/dom/datastore/tests/file_app.sjs?testToken=file_bug976311.html&template=file_bug976311.template.webapp';
+  var gHostedManifestURL2 = 'http://example.com/tests/dom/datastore/tests/file_app.sjs?testToken=file_bug976311.html&template=file_bug976311.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 uninstallApps() {
+    if (!gApps.length) {
+      ok(true, "All done!");
+      runTest();
+      return;
+    }
+
+    var app = gApps.pop();
+    var request = navigator.mozApps.mgmt.uninstall(app);
+    request.onerror = cbError;
+    request.onsuccess = uninstallApps;
+  }
+
+
+  function testApp() {
+   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('container');
+
+   // Set us up to listen for messages from the app.
+   var listener = function(e) {
+     var message = e.detail.message;
+     if (/^OK/.exec(message)) {
+       ok(true, "Message from app: " + message);
+     } else if (/KO/.exec(message)) {
+       ok(false, "Message from app: " + message);
+     } else if (/DONE/.exec(message)) {
+       ok(true, "Messaging from app complete");
+       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);
+    },
+
+    function() {
+      SpecialPowers.setAllAppsLaunchable(true);
+      SpecialPowers.setBoolPref("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
+    testApp,
+
+    // Uninstall the apps
+    uninstallApps
+  ];
+
+  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>
+</body>
+</html>