From eb86cac0c7f6761cf48517170cd47db2f0f7f68f Mon Sep 17 00:00:00 2001 From: Sean Lin Date: Fri, 27 Jun 2014 15:58:47 +0800 Subject: [PATCH] Bug 1014023 - [Datastore] Notify apps of changes in datastore without being opened. r=baku, r=gene --- b2g/app/b2g.js | 4 + dom/datastore/DataStoreChangeNotifier.jsm | 88 ++++++++++- dom/datastore/DataStoreService.cpp | 45 ++++++ dom/datastore/nsIDataStoreService.idl | 5 +- .../tests/file_notify_system_message.html | 122 ++++++++++++++++ dom/datastore/tests/mochitest.ini | 3 + dom/datastore/tests/test_app_install.html | 10 +- dom/datastore/tests/test_arrays.html | 10 +- dom/datastore/tests/test_basic.html | 10 +- dom/datastore/tests/test_basic_worker.html | 10 +- dom/datastore/tests/test_bug1008044.html | 10 +- dom/datastore/tests/test_bug924104.html | 10 +- dom/datastore/tests/test_bug957086.html | 10 +- dom/datastore/tests/test_bug976311.html | 10 +- dom/datastore/tests/test_bug986056.html | 11 +- dom/datastore/tests/test_certifiedApp.html | 10 +- dom/datastore/tests/test_changes.html | 11 +- dom/datastore/tests/test_duplicate.html | 10 +- dom/datastore/tests/test_keys.html | 10 +- .../tests/test_notify_system_message.html | 138 ++++++++++++++++++ dom/datastore/tests/test_oop.html | 10 +- dom/datastore/tests/test_oop_events.html | 10 +- dom/datastore/tests/test_readonly.html | 10 +- dom/datastore/tests/test_sync.html | 10 +- dom/datastore/tests/test_sync_worker.html | 10 +- dom/datastore/tests/test_transactions.html | 10 +- dom/messages/SystemMessageInternal.js | 61 +++++--- .../SystemMessagePermissionsChecker.jsm | 35 ++++- .../interfaces/nsISystemMessagesInternal.idl | 6 +- 29 files changed, 589 insertions(+), 110 deletions(-) create mode 100644 dom/datastore/tests/file_notify_system_message.html create mode 100644 dom/datastore/tests/test_notify_system_message.html diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index 9e46b6dcc3e..0080a4bf21d 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -886,6 +886,10 @@ pref("network.sntp.timeout", 30); // In seconds. // Enable dataStore pref("dom.datastore.enabled", true); +// When an entry is changed, use two timers to fire system messages in a more +// moderate pattern. +pref("dom.datastore.sysMsgOnChangeShortTimeoutSec", 10); +pref("dom.datastore.sysMsgOnChangeLongTimeoutSec", 60); // DOM Inter-App Communication API. pref("dom.inter-app-communication-api.enabled", true); diff --git a/dom/datastore/DataStoreChangeNotifier.jsm b/dom/datastore/DataStoreChangeNotifier.jsm index 64951f3ca43..bbf44a1f7ce 100644 --- a/dom/datastore/DataStoreChangeNotifier.jsm +++ b/dom/datastore/DataStoreChangeNotifier.jsm @@ -23,12 +23,33 @@ XPCOMUtils.defineLazyServiceGetter(this, "dataStoreService", "@mozilla.org/datastore-service;1", "nsIDataStoreService"); +XPCOMUtils.defineLazyServiceGetter(this, "systemMessenger", + "@mozilla.org/system-message-internal;1", + "nsISystemMessagesInternal"); + +var kSysMsgOnChangeShortTimeoutSec = + Services.prefs.getIntPref("dom.datastore.sysMsgOnChangeShortTimeoutSec"); +var kSysMsgOnChangeLongTimeoutSec = + Services.prefs.getIntPref("dom.datastore.sysMsgOnChangeLongTimeoutSec"); + this.DataStoreChangeNotifier = { children: [], messages: [ "DataStore:Changed", "DataStore:RegisterForMessages", "DataStore:UnregisterForMessages", "child-process-shutdown" ], + // These hashes are used for storing the mapping between the datastore + // identifiers (name | owner manifest URL) and their correspondent timers. + // The object literal is defined as below: + // + // { + // "datastore name 1|owner manifest URL 1": timer1, + // "datastore name 2|owner manifest URL 2": timer2, + // ... + // } + sysMsgOnChangeShortTimers: {}, + sysMsgOnChangeLongTimers: {}, + init: function() { debug("init"); @@ -59,7 +80,8 @@ this.DataStoreChangeNotifier = { }, broadcastMessage: function broadcastMessage(aData) { - debug("Broadast"); + debug("broadcast"); + this.children.forEach(function(obj) { if (obj.store == aData.store && obj.owner == aData.owner) { obj.mm.sendAsyncMessage("DataStore:Changed:Return:OK", aData); @@ -67,6 +89,69 @@ this.DataStoreChangeNotifier = { }); }, + broadcastSystemMessage: function(aStore, aOwner) { + debug("broadcastSystemMessage"); + + // Clear relevant timers. + var storeKey = aStore + "|" + aOwner; + var shortTimer = this.sysMsgOnChangeShortTimers[storeKey]; + if (shortTimer) { + shortTimer.cancel(); + delete this.sysMsgOnChangeShortTimers[storeKey]; + } + var longTimer = this.sysMsgOnChangeLongTimers[storeKey]; + if (longTimer) { + longTimer.cancel(); + delete this.sysMsgOnChangeLongTimers[storeKey]; + } + + // Get all the manifest URLs of the apps which can access the datastore. + var manifestURLs = dataStoreService.getAppManifestURLsForDataStore(aStore); + var enumerate = manifestURLs.enumerate(); + while (enumerate.hasMoreElements()) { + var manifestURL = enumerate.getNext().QueryInterface(Ci.nsISupportsString); + debug("Notify app " + manifestURL + " of datastore updates"); + // Send the system message 'datastore-update-{store name}' to all the + // pages for these apps. With the manifest URL of the owner in the message + // payload, it notifies the consumer a sync operation should be performed. + systemMessenger.sendMessage("datastore-update-" + aStore, + { owner: aOwner }, + null, + Services.io.newURI(manifestURL, null, null)); + } + }, + + // Use the following logic to broadcast system messages in a moderate pattern. + // 1. When an entry is changed, start a short timer and a long timer. + // 2. If an entry is changed while the short timer is running, reset it. + // Do not reset the long timer. + // 3. Once either fires, broadcast the system message and cancel both timers. + setSystemMessageTimeout: function(aStore, aOwner) { + debug("setSystemMessageTimeout"); + + var storeKey = aStore + "|" + aOwner; + + // Reset the short timer. + var shortTimer = this.sysMsgOnChangeShortTimers[storeKey]; + if (!shortTimer) { + shortTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + this.sysMsgOnChangeShortTimers[storeKey] = shortTimer; + } else { + shortTimer.cancel(); + } + shortTimer.initWithCallback({ notify: this.broadcastSystemMessage.bind(this, aStore, aOwner) }, + kSysMsgOnChangeShortTimeoutSec * 1000, + Ci.nsITimer.TYPE_ONE_SHOT); + + // Set the long timer if necessary. + if (!this.sysMsgOnChangeLongTimers[storeKey]) { + var longTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + this.sysMsgOnChangeLongTimers[storeKey] = longTimer; + longTimer.initWithCallback({ notify: this.broadcastSystemMessage.bind(this, aStore, aOwner) }, + kSysMsgOnChangeLongTimeoutSec * 1000, + Ci.nsITimer.TYPE_ONE_SHOT); + } + }, receiveMessage: function(aMessage) { debug("receiveMessage "); @@ -89,6 +174,7 @@ this.DataStoreChangeNotifier = { switch (aMessage.name) { case "DataStore:Changed": this.broadcastMessage(aMessage.data); + this.setSystemMessageTimeout(aMessage.data.store, aMessage.data.owner); break; case "DataStore:RegisterForMessages": diff --git a/dom/datastore/DataStoreService.cpp b/dom/datastore/DataStoreService.cpp index a30a513a8d7..580246bc54b 100644 --- a/dom/datastore/DataStoreService.cpp +++ b/dom/datastore/DataStoreService.cpp @@ -33,9 +33,11 @@ #include "nsIDocument.h" #include "nsIDOMGlobalPropertyInitializer.h" #include "nsIIOService.h" +#include "nsIMutableArray.h" #include "nsIObserverService.h" #include "nsIPermissionManager.h" #include "nsIScriptSecurityManager.h" +#include "nsISupportsPrimitives.h" #include "nsIUUIDGenerator.h" #include "nsPIDOMWindow.h" #include "nsIURI.h" @@ -375,6 +377,24 @@ GetDataStoreInfosEnumerator(const uint32_t& aAppId, return PL_DHASH_NEXT; } +PLDHashOperator +GetAppManifestURLsEnumerator(const uint32_t& aAppId, + DataStoreInfo* aInfo, + void* aUserData) +{ + AssertIsInMainProcess(); + MOZ_ASSERT(NS_IsMainThread()); + + auto* manifestURLs = static_cast(aUserData); + nsCOMPtr manifestURL(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID)); + if (manifestURL) { + manifestURL->SetData(aInfo->mManifestURL); + manifestURLs->AppendElement(manifestURL, false); + } + + return PL_DHASH_NEXT; +} + // This class is useful to enumerate the add permissions for each app. class MOZ_STACK_CLASS AddPermissionsData { @@ -1057,6 +1077,31 @@ DataStoreService::GetDataStoreInfos(const nsAString& aName, return NS_OK; } +NS_IMETHODIMP +DataStoreService::GetAppManifestURLsForDataStore(const nsAString& aName, + nsIArray** aManifestURLs) +{ + ASSERT_PARENT_PROCESS() + MOZ_ASSERT(NS_IsMainThread()); + + nsCOMPtr manifestURLs = do_CreateInstance(NS_ARRAY_CONTRACTID); + if (!manifestURLs) { + return NS_ERROR_OUT_OF_MEMORY; + } + + HashApp* apps = nullptr; + if (mStores.Get(aName, &apps)) { + apps->EnumerateRead(GetAppManifestURLsEnumerator, manifestURLs.get()); + } + if (mAccessStores.Get(aName, &apps)) { + apps->EnumerateRead(GetAppManifestURLsEnumerator, manifestURLs.get()); + } + + *aManifestURLs = manifestURLs; + NS_ADDREF(*aManifestURLs); + return NS_OK; +} + bool DataStoreService::CheckPermission(nsIPrincipal* aPrincipal) { diff --git a/dom/datastore/nsIDataStoreService.idl b/dom/datastore/nsIDataStoreService.idl index b2b3d658630..adb2b2f50ff 100644 --- a/dom/datastore/nsIDataStoreService.idl +++ b/dom/datastore/nsIDataStoreService.idl @@ -7,8 +7,9 @@ interface nsIDOMWindow; interface nsIPrincipal; +interface nsIArray; -[scriptable, uuid(9b59c49a-0cd7-11e4-b096-74d02b97e723)] +[scriptable, uuid(79944b1c-187d-11e4-abb6-74d02b97e723)] interface nsIDataStoreService : nsISupports { void installDataStore(in unsigned long appId, @@ -27,5 +28,7 @@ interface nsIDataStoreService : nsISupports in DOMString name, in DOMString owner); + nsIArray getAppManifestURLsForDataStore(in DOMString name); + boolean checkPermission(in nsIPrincipal principal); }; diff --git a/dom/datastore/tests/file_notify_system_message.html b/dom/datastore/tests/file_notify_system_message.html new file mode 100644 index 00000000000..05e9153cd07 --- /dev/null +++ b/dom/datastore/tests/file_notify_system_message.html @@ -0,0 +1,122 @@ + + + + + Test for DataStore - notify updates with system messages + + +

+ +
+  
+
+ + diff --git a/dom/datastore/tests/mochitest.ini b/dom/datastore/tests/mochitest.ini index fd3972597db..e1ca2818206 100644 --- a/dom/datastore/tests/mochitest.ini +++ b/dom/datastore/tests/mochitest.ini @@ -30,6 +30,7 @@ support-files = file_sync_common.js file_bug1008044.html file_bug957086.html + file_notify_system_message.html [test_app_install.html] [test_readonly.html] @@ -50,3 +51,5 @@ support-files = [test_transactions.html] [test_bug1008044.html] [test_bug957086.html] +[test_notify_system_message.html] +skip-if = toolkit == 'gonk' # b2g diff --git a/dom/datastore/tests/test_app_install.html b/dom/datastore/tests/test_app_install.html index c03f249326d..8c326d33704 100644 --- a/dom/datastore/tests/test_app_install.html +++ b/dom/datastore/tests/test_app_install.html @@ -10,10 +10,6 @@
diff --git a/dom/datastore/tests/test_basic.html b/dom/datastore/tests/test_basic.html index 25d6637ef50..c774b146c9f 100644 --- a/dom/datastore/tests/test_basic.html +++ b/dom/datastore/tests/test_basic.html @@ -77,11 +77,17 @@ // Preferences function() { SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true], + ["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1], + ["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3], ["dom.testing.ignore_ipc_principal", true], ["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest); }, function() { + if (SpecialPowers.isMainProcess()) { + SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + } + SpecialPowers.setAllAppsLaunchable(true); SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); runTest(); @@ -116,10 +122,6 @@ SimpleTest.finish(); } - if (SpecialPowers.isMainProcess()) { - SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); - } - SimpleTest.waitForExplicitFinish(); runTest(); diff --git a/dom/datastore/tests/test_basic_worker.html b/dom/datastore/tests/test_basic_worker.html index be940e8771b..70fadecd8fa 100644 --- a/dom/datastore/tests/test_basic_worker.html +++ b/dom/datastore/tests/test_basic_worker.html @@ -77,11 +77,17 @@ // Preferences function() { SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true], + ["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1], + ["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3], ["dom.testing.ignore_ipc_principal", true], ["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest); }, function() { + if (SpecialPowers.isMainProcess()) { + SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + } + SpecialPowers.setAllAppsLaunchable(true); SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); runTest(); @@ -116,10 +122,6 @@ SimpleTest.finish(); } - if (SpecialPowers.isMainProcess()) { - SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); - } - SimpleTest.waitForExplicitFinish(); runTest(); diff --git a/dom/datastore/tests/test_bug1008044.html b/dom/datastore/tests/test_bug1008044.html index 97164c396ca..c22131f2379 100644 --- a/dom/datastore/tests/test_bug1008044.html +++ b/dom/datastore/tests/test_bug1008044.html @@ -77,12 +77,18 @@ // Preferences function() { SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true], + ["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1], + ["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3], ["dom.ipc.browser_frames.oop_by_default", true], ["dom.testing.ignore_ipc_principal", true], ["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest); }, function() { + if (SpecialPowers.isMainProcess()) { + SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + } + SpecialPowers.setAllAppsLaunchable(true); SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); runTest(); @@ -117,10 +123,6 @@ SimpleTest.finish(); } - if (SpecialPowers.isMainProcess()) { - SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); - } - SimpleTest.waitForExplicitFinish(); runTest(); diff --git a/dom/datastore/tests/test_bug924104.html b/dom/datastore/tests/test_bug924104.html index 5b22acf11db..3896a108b30 100644 --- a/dom/datastore/tests/test_bug924104.html +++ b/dom/datastore/tests/test_bug924104.html @@ -77,11 +77,17 @@ // Preferences function() { SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true], + ["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1], + ["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3], ["dom.testing.ignore_ipc_principal", true], ["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest); }, function() { + if (SpecialPowers.isMainProcess()) { + SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + } + SpecialPowers.setAllAppsLaunchable(true); SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); runTest(); @@ -116,10 +122,6 @@ SimpleTest.finish(); } - if (SpecialPowers.isMainProcess()) { - SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); - } - SimpleTest.waitForExplicitFinish(); runTest(); diff --git a/dom/datastore/tests/test_bug957086.html b/dom/datastore/tests/test_bug957086.html index 1abb1f4c153..ac8f31ee09b 100644 --- a/dom/datastore/tests/test_bug957086.html +++ b/dom/datastore/tests/test_bug957086.html @@ -81,12 +81,18 @@ // Preferences function() { SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true], + ["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1], + ["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3], ["dom.ipc.browser_frames.oop_by_default", true], ["dom.testing.ignore_ipc_principal", true], ["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest); }, function() { + if (SpecialPowers.isMainProcess()) { + SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + } + SpecialPowers.setAllAppsLaunchable(true); SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); runTest(); @@ -124,10 +130,6 @@ SimpleTest.finish(); } - if (SpecialPowers.isMainProcess()) { - SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); - } - SimpleTest.waitForExplicitFinish(); runTest(); diff --git a/dom/datastore/tests/test_bug976311.html b/dom/datastore/tests/test_bug976311.html index 4922dd2db35..35f9aba480a 100644 --- a/dom/datastore/tests/test_bug976311.html +++ b/dom/datastore/tests/test_bug976311.html @@ -82,11 +82,17 @@ // Preferences function() { SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true], + ["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1], + ["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3], ["dom.testing.ignore_ipc_principal", true], ["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest); }, function() { + if (SpecialPowers.isMainProcess()) { + SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + } + SpecialPowers.setAllAppsLaunchable(true); SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); runTest(); @@ -124,10 +130,6 @@ SimpleTest.finish(); } - if (SpecialPowers.isMainProcess()) { - SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); - } - SimpleTest.waitForExplicitFinish(); runTest(); diff --git a/dom/datastore/tests/test_bug986056.html b/dom/datastore/tests/test_bug986056.html index 2376f71483c..3d14cb666c5 100644 --- a/dom/datastore/tests/test_bug986056.html +++ b/dom/datastore/tests/test_bug986056.html @@ -75,12 +75,17 @@ // Preferences function() { SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true], + ["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1], + ["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3], ["dom.testing.ignore_ipc_principal", true], ["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest); }, - // Enabling mozBrowser function() { + if (SpecialPowers.isMainProcess()) { + SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + } + SpecialPowers.setAllAppsLaunchable(true); SpecialPowers.pushPrefEnv({"set": [["dom.mozBrowserFramesEnabled", true]]}, runTest); }, @@ -134,10 +139,6 @@ SimpleTest.finish(); } - if (SpecialPowers.isMainProcess()) { - SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); - } - SimpleTest.waitForExplicitFinish(); runTest(); diff --git a/dom/datastore/tests/test_certifiedApp.html b/dom/datastore/tests/test_certifiedApp.html index 0b78ccbc814..e4a328c2d0d 100644 --- a/dom/datastore/tests/test_certifiedApp.html +++ b/dom/datastore/tests/test_certifiedApp.html @@ -77,6 +77,8 @@ // Preferences function() { SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true], + ["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1], + ["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3], ["dom.testing.ignore_ipc_principal", true], ["dom.testing.datastore_enabled_for_hosted_apps", false]]}, runTest); }, @@ -89,6 +91,10 @@ }, function() { + if (SpecialPowers.isMainProcess()) { + SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + } + SpecialPowers.setAllAppsLaunchable(true); SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); runTest(); @@ -123,10 +129,6 @@ SimpleTest.finish(); } - if (SpecialPowers.isMainProcess()) { - SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); - } - SimpleTest.waitForExplicitFinish(); runTest(); diff --git a/dom/datastore/tests/test_changes.html b/dom/datastore/tests/test_changes.html index 434c4c0f4c7..c2a67b1addd 100644 --- a/dom/datastore/tests/test_changes.html +++ b/dom/datastore/tests/test_changes.html @@ -121,12 +121,17 @@ // Preferences function() { SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true], + ["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1], + ["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3], ["dom.testing.ignore_ipc_principal", true], ["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest); }, - // Enabling mozBrowser function() { + if (SpecialPowers.isMainProcess()) { + SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + } + SpecialPowers.setAllAppsLaunchable(true); SpecialPowers.pushPrefEnv({"set": [["dom.mozBrowserFramesEnabled", true]]}, runTest); }, @@ -169,10 +174,6 @@ SimpleTest.finish(); } - if (SpecialPowers.isMainProcess()) { - SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); - } - SimpleTest.waitForExplicitFinish(); runTest(); diff --git a/dom/datastore/tests/test_duplicate.html b/dom/datastore/tests/test_duplicate.html index e9ecca2ab9a..8589d440826 100644 --- a/dom/datastore/tests/test_duplicate.html +++ b/dom/datastore/tests/test_duplicate.html @@ -77,11 +77,17 @@ // Preferences function() { SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true], + ["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1], + ["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3], ["dom.testing.ignore_ipc_principal", true], ["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest); }, function() { + if (SpecialPowers.isMainProcess()) { + SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + } + SpecialPowers.setAllAppsLaunchable(true); SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); runTest(); @@ -116,10 +122,6 @@ SimpleTest.finish(); } - if (SpecialPowers.isMainProcess()) { - SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); - } - SimpleTest.waitForExplicitFinish(); runTest(); diff --git a/dom/datastore/tests/test_keys.html b/dom/datastore/tests/test_keys.html index dced0354ef3..9bc2ac2b88b 100644 --- a/dom/datastore/tests/test_keys.html +++ b/dom/datastore/tests/test_keys.html @@ -77,11 +77,17 @@ // Preferences function() { SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true], + ["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1], + ["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3], ["dom.testing.ignore_ipc_principal", true], ["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest); }, function() { + if (SpecialPowers.isMainProcess()) { + SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + } + SpecialPowers.setAllAppsLaunchable(true); SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); runTest(); @@ -116,10 +122,6 @@ SimpleTest.finish(); } - if (SpecialPowers.isMainProcess()) { - SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); - } - SimpleTest.waitForExplicitFinish(); runTest(); diff --git a/dom/datastore/tests/test_notify_system_message.html b/dom/datastore/tests/test_notify_system_message.html new file mode 100644 index 00000000000..dc32f79f5a5 --- /dev/null +++ b/dom/datastore/tests/test_notify_system_message.html @@ -0,0 +1,138 @@ + + + + + Test for DataStore - notify updates with system messages + + + + +
+ + + diff --git a/dom/datastore/tests/test_oop.html b/dom/datastore/tests/test_oop.html index 989904341e6..e275b2d1b3d 100644 --- a/dom/datastore/tests/test_oop.html +++ b/dom/datastore/tests/test_oop.html @@ -77,12 +77,18 @@ // Preferences function() { SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true], + ["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1], + ["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3], ["dom.ipc.browser_frames.oop_by_default", true], ["dom.testing.ignore_ipc_principal", true], ["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest); }, function() { + if (SpecialPowers.isMainProcess()) { + SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + } + SpecialPowers.setAllAppsLaunchable(true); SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); runTest(); @@ -117,10 +123,6 @@ SimpleTest.finish(); } - if (SpecialPowers.isMainProcess()) { - SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); - } - SimpleTest.waitForExplicitFinish(); runTest(); diff --git a/dom/datastore/tests/test_oop_events.html b/dom/datastore/tests/test_oop_events.html index d97d674c8a9..c57f93de657 100644 --- a/dom/datastore/tests/test_oop_events.html +++ b/dom/datastore/tests/test_oop_events.html @@ -105,12 +105,18 @@ // Preferences function() { SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true], + ["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1], + ["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3], ["dom.ipc.browser_frames.oop_by_default", true], ["dom.testing.ignore_ipc_principal", true], ["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest); }, function() { + if (SpecialPowers.isMainProcess()) { + SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + } + SpecialPowers.setAllAppsLaunchable(true); SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); runTest(); @@ -147,10 +153,6 @@ SimpleTest.finish(); } - if (SpecialPowers.isMainProcess()) { - SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); - } - SimpleTest.waitForExplicitFinish(); runTest(); diff --git a/dom/datastore/tests/test_readonly.html b/dom/datastore/tests/test_readonly.html index 559b8d0f3eb..52541cd2618 100644 --- a/dom/datastore/tests/test_readonly.html +++ b/dom/datastore/tests/test_readonly.html @@ -30,6 +30,10 @@ } function runTest() { + if (SpecialPowers.isMainProcess()) { + SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + } + SpecialPowers.setAllAppsLaunchable(true); SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); @@ -95,12 +99,10 @@ SimpleTest.finish(); } - if (SpecialPowers.isMainProcess()) { - SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); - } - SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true], + ["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1], + ["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3], ["dom.testing.ignore_ipc_principal", true], ["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest); diff --git a/dom/datastore/tests/test_sync.html b/dom/datastore/tests/test_sync.html index b45472c88ba..15ab46c910c 100644 --- a/dom/datastore/tests/test_sync.html +++ b/dom/datastore/tests/test_sync.html @@ -77,11 +77,17 @@ // Preferences function() { SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true], + ["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1], + ["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3], ["dom.testing.ignore_ipc_principal", true], ["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest); }, function() { + if (SpecialPowers.isMainProcess()) { + SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + } + SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); runTest(); }, @@ -115,10 +121,6 @@ SimpleTest.finish(); } - if (SpecialPowers.isMainProcess()) { - SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); - } - SimpleTest.waitForExplicitFinish(); runTest(); diff --git a/dom/datastore/tests/test_sync_worker.html b/dom/datastore/tests/test_sync_worker.html index 904edea96e8..4e1d5d0507f 100644 --- a/dom/datastore/tests/test_sync_worker.html +++ b/dom/datastore/tests/test_sync_worker.html @@ -77,11 +77,17 @@ // Preferences function() { SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true], + ["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1], + ["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3], ["dom.testing.ignore_ipc_principal", true], ["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest); }, function() { + if (SpecialPowers.isMainProcess()) { + SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + } + SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); runTest(); }, @@ -115,10 +121,6 @@ SimpleTest.finish(); } - if (SpecialPowers.isMainProcess()) { - SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); - } - SimpleTest.waitForExplicitFinish(); runTest(); diff --git a/dom/datastore/tests/test_transactions.html b/dom/datastore/tests/test_transactions.html index 7607b11fa5c..26d974aa1cb 100644 --- a/dom/datastore/tests/test_transactions.html +++ b/dom/datastore/tests/test_transactions.html @@ -77,11 +77,17 @@ // Preferences function() { SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true], + ["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1], + ["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3], ["dom.testing.ignore_ipc_principal", true], ["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest); }, function() { + if (SpecialPowers.isMainProcess()) { + SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + } + SpecialPowers.setAllAppsLaunchable(true); SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); runTest(); @@ -116,10 +122,6 @@ SimpleTest.finish(); } - if (SpecialPowers.isMainProcess()) { - SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); - } - SimpleTest.waitForExplicitFinish(); runTest(); diff --git a/dom/messages/SystemMessageInternal.js b/dom/messages/SystemMessageInternal.js index 72708689626..74e24e38129 100644 --- a/dom/messages/SystemMessageInternal.js +++ b/dom/messages/SystemMessageInternal.js @@ -192,31 +192,48 @@ SystemMessageInternal.prototype = { // clean it up from the pending message queue when apps receive it. let messageID = gUUIDGenerator.generateUUID().toString(); - debug("Sending " + aType + " " + JSON.stringify(aMessage) + - " for " + aPageURI.spec + " @ " + aManifestURI.spec + - '; extra: ' + JSON.stringify(aExtra)); - - let result = this._sendMessageCommon(aType, - aMessage, - messageID, - aPageURI.spec, - aManifestURI.spec, - aExtra); - debug("Returned status of sending message: " + result); - - // Don't need to open the pages and queue the system message - // which was not allowed to be sent. - if (result === MSG_SENT_FAILURE_PERM_DENIED) { - return; + let manifestURL = aManifestURI.spec; + let pageURLs = []; + if (aPageURI) { + pageURLs.push(aPageURI.spec); + } else { + // Send this message to all the registered pages of the app if |aPageURI| + // is not specified. + for (let i = 0; i < this._pages.length; i++) { + let page = this._pages[i]; + if (page.type === aType && page.manifestURL === manifestURL) { + pageURLs.push(page.pageURL); + } + } } - let page = this._findPage(aType, aPageURI.spec, aManifestURI.spec); - if (page) { - // Queue this message in the corresponding pages. - this._queueMessage(page, aMessage, messageID); + pageURLs.forEach(function(aPageURL) { + debug("Sending " + aType + " " + JSON.stringify(aMessage) + + " for " + aPageURL + " @ " + manifestURL + + '; extra: ' + JSON.stringify(aExtra)); - this._openAppPage(page, aMessage, aExtra, result); - } + let result = this._sendMessageCommon(aType, + aMessage, + messageID, + aPageURL, + manifestURL, + aExtra); + debug("Returned status of sending message: " + result); + + // Don't need to open the pages and queue the system message + // which was not allowed to be sent. + if (result === MSG_SENT_FAILURE_PERM_DENIED) { + return; + } + + let page = this._findPage(aType, aPageURL, manifestURL); + if (page) { + // Queue this message in the corresponding pages. + this._queueMessage(page, aMessage, messageID); + + this._openAppPage(page, aMessage, aExtra, result); + } + }, this); }, broadcastMessage: function(aType, aMessage, aExtra) { diff --git a/dom/messages/SystemMessagePermissionsChecker.jsm b/dom/messages/SystemMessagePermissionsChecker.jsm index 433c8759875..89bcf675734 100644 --- a/dom/messages/SystemMessagePermissionsChecker.jsm +++ b/dom/messages/SystemMessagePermissionsChecker.jsm @@ -15,7 +15,8 @@ Cu.import("resource://gre/modules/PermissionsTable.jsm"); Cu.import("resource://gre/modules/PermissionSettings.jsm"); this.EXPORTED_SYMBOLS = ["SystemMessagePermissionsChecker", - "SystemMessagePermissionsTable"]; + "SystemMessagePermissionsTable", + "SystemMessagePrefixPermissionsTable"]; function debug(aStr) { // dump("SystemMessagePermissionsChecker.jsm: " + aStr + "\n"); @@ -122,6 +123,18 @@ this.SystemMessagePermissionsTable = { } }; +// This table maps system message prefix to permission(s), indicating only +// the system messages with specified prefixes granted by the page's permissions +// are allowed to be registered or sent to that page. Note the empty permission +// set means this type of system message is always permitted. +// +// Note that this table is only used when the permission checker can't find a +// match in SystemMessagePermissionsTable listed above. + +this.SystemMessagePrefixPermissionsTable = { + "datastore-update-": { }, +}; + this.SystemMessagePermissionsChecker = { /** * Return all the needed permission names for the given system message. @@ -140,16 +153,26 @@ this.SystemMessagePermissionsChecker = { let permNames = SystemMessagePermissionsTable[aSysMsgName]; if (permNames === undefined) { - debug("'" + aSysMsgName + "' is not associated with permissions. " + - "Please add them to the SystemMessagePermissionsTable."); - return null; + // Try to look up in the prefix table. + for (let sysMsgPrefix in SystemMessagePrefixPermissionsTable) { + if (aSysMsgName.indexOf(sysMsgPrefix) === 0) { + permNames = SystemMessagePrefixPermissionsTable[sysMsgPrefix]; + break; + } + } + + if (permNames === undefined) { + debug("'" + aSysMsgName + "' is not associated with permissions. " + + "Please add them to the SystemMessage[Prefix]PermissionsTable."); + return null; + } } let object = { }; for (let permName in permNames) { if (PermissionsTable[permName] === undefined) { debug("'" + permName + "' for '" + aSysMsgName + "' is invalid. " + - "Please correct it in the SystemMessagePermissionsTable."); + "Please correct it in the SystemMessage[Prefix]PermissionsTable."); return null; } @@ -157,7 +180,7 @@ this.SystemMessagePermissionsChecker = { let access = permNames[permName]; if (!access || !Array.isArray(access)) { debug("'" + permName + "' is not associated with access array. " + - "Please correct it in the SystemMessagePermissionsTable."); + "Please correct it in the SystemMessage[Prefix]PermissionsTable."); return null; } object[permName] = appendAccessToPermName(permName, access); diff --git a/dom/messages/interfaces/nsISystemMessagesInternal.idl b/dom/messages/interfaces/nsISystemMessagesInternal.idl index 8df78278f07..450206b2ba5 100644 --- a/dom/messages/interfaces/nsISystemMessagesInternal.idl +++ b/dom/messages/interfaces/nsISystemMessagesInternal.idl @@ -13,10 +13,12 @@ interface nsIDOMWindow; interface nsISystemMessagesInternal : nsISupports { /* - * Allow any internal user to broadcast a message of a given type. + * Allow any internal user to send a message of a given type to a given page + * of an app. The message will be sent to all the registered pages of the app + * when |pageURI| is not specified. * @param type The type of the message to be sent. * @param message The message payload. - * @param pageURI The URI of the page that will be opened. + * @param pageURI The URI of the page that will be opened. Nullable. * @param manifestURI The webapp's manifest URI. * @param extra Extra opaque information that will be passed around in the observer * notification to open the page.