Bug 1311707 - dom-private-storage2-changed notification, r=janv
authorAndrea Marchesini <amarchesini@mozilla.com>
Tue, 29 Nov 2016 07:19:08 +0100
changeset 324482 a7623a66ba0873bff655f5957b39909f2927aa6b
parent 324481 2411fdf8a816414051e84cb2de43498d9e648d18
child 324483 d40f29aab1e9b06dd81b80afcfc988f70209e636
push id84432
push useramarchesini@mozilla.com
push dateTue, 29 Nov 2016 06:20:28 +0000
treeherdermozilla-inbound@a7623a66ba08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjanv
bugs1311707
milestone53.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 1311707 - dom-private-storage2-changed notification, r=janv
devtools/server/actors/storage.js
dom/base/nsGlobalWindow.cpp
dom/storage/DOMStorage.cpp
dom/tests/mochitest/storageevent/test_storageNotifications.html
netwerk/ipc/NeckoParent.cpp
--- a/devtools/server/actors/storage.js
+++ b/devtools/server/actors/storage.js
@@ -101,20 +101,20 @@ var StorageActors = {};
  *                             objects for it
  *   - getFields: Given a subType(optional), get an array of objects containing
  *                column field info. The info includes,
  *                "name" is name of colume key.
  *                "editable" is 1 means editable field; 0 means uneditable.
  *
  * @param {string} typeName
  *        The typeName of the actor.
- * @param {string} observationTopic
- *        The topic which this actor listens to via Notification Observers.
+ * @param {array} observationTopics
+ *        An array of topics which this actor listens to via Notification Observers.
  */
-StorageActors.defaults = function (typeName, observationTopic) {
+StorageActors.defaults = function (typeName, observationTopics) {
   return {
     typeName: typeName,
 
     get conn() {
       return this.storageActor.conn;
     },
 
     /**
@@ -145,28 +145,32 @@ StorageActors.defaults = function (typeN
     },
 
     initialize(storageActor) {
       protocol.Actor.prototype.initialize.call(this, null);
 
       this.storageActor = storageActor;
 
       this.populateStoresForHosts();
-      if (observationTopic) {
-        Services.obs.addObserver(this, observationTopic, false);
+      if (observationTopics) {
+        observationTopics.forEach((observationTopic) => {
+          Services.obs.addObserver(this, observationTopic, false);
+        });
       }
       this.onWindowReady = this.onWindowReady.bind(this);
       this.onWindowDestroyed = this.onWindowDestroyed.bind(this);
       events.on(this.storageActor, "window-ready", this.onWindowReady);
       events.on(this.storageActor, "window-destroyed", this.onWindowDestroyed);
     },
 
     destroy() {
-      if (observationTopic) {
-        Services.obs.removeObserver(this, observationTopic, false);
+      if (observationTopics) {
+        observationTopics.forEach((observationTopic) => {
+          Services.obs.removeObserver(this, observationTopic, false);
+        });
       }
       events.off(this.storageActor, "window-ready", this.onWindowReady);
       events.off(this.storageActor, "window-destroyed", this.onWindowDestroyed);
 
       this.hostVsStores.clear();
 
       protocol.Actor.prototype.destroy.call(this);
 
@@ -364,27 +368,27 @@ StorageActors.defaults = function (typeN
  * Actor.
  *
  * @See StorageActors.defaults()
  *
  * @param {object} options
  *        Options required by StorageActors.defaults method which are :
  *         - typeName {string}
  *                    The typeName of the actor.
- *         - observationTopic {string}
- *                            The topic which this actor listens to via
+ *         - observationTopics {array}
+ *                            The topics which this actor listens to via
  *                            Notification Observers.
  * @param {object} overrides
  *        All the methods which you want to be different from the ones in
  *        StorageActors.defaults method plus the required ones described there.
  */
 StorageActors.createActor = function (options = {}, overrides = {}) {
   let actorObject = StorageActors.defaults(
     options.typeName,
-    options.observationTopic || null
+    options.observationTopics || null
   );
   for (let key in overrides) {
     actorObject[key] = overrides[key];
   }
 
   let actorSpec = specs.childSpecs[options.typeName];
   let actor = protocol.ActorClassWithSpec(actorSpec, actorObject);
   storageTypePool.set(actorObject.typeName, actor);
@@ -1104,17 +1108,19 @@ function getObjectForLocalOrSessionStora
       let storage = this.hostVsStores.get(host);
       if (!storage) {
         return;
       }
       storage.clear();
     }),
 
     observe(subject, topic, data) {
-      if (topic != "dom-storage2-changed" || data != type) {
+      if ((topic != "dom-storage2-changed" &&
+           topic != "dom-private-storage2-changed") ||
+          data != type) {
         return null;
       }
 
       let host = this.getSchemaAndHost(subject.url);
 
       if (!this.hostVsStores.has(host)) {
         return null;
       }
@@ -1156,25 +1162,25 @@ function getObjectForLocalOrSessionStora
   };
 }
 
 /**
  * The Local Storage actor and front.
  */
 StorageActors.createActor({
   typeName: "localStorage",
-  observationTopic: "dom-storage2-changed"
+  observationTopics: ["dom-storage2-changed", "dom-private-storage2-changed"]
 }, getObjectForLocalOrSessionStorage("localStorage"));
 
 /**
  * The Session Storage actor and front.
  */
 StorageActors.createActor({
   typeName: "sessionStorage",
-  observationTopic: "dom-storage2-changed"
+  observationTopics: ["dom-storage2-changed", "dom-private-storage2-changed"]
 }, getObjectForLocalOrSessionStorage("sessionStorage"));
 
 StorageActors.createActor({
   typeName: "Cache"
 }, {
   getCachesForHost: Task.async(function* (host) {
     let uri = Services.io.newURI(host, null, null);
     let principal =
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1303,19 +1303,20 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
     if (mObserver) {
       nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
       if (os) {
         // Watch for online/offline status changes so we can fire events. Use
         // a strong reference.
         os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
                         false);
 
-        // Watch for dom-storage2-changed so we can fire storage
-        // events. Use a strong reference.
+        // Watch for dom-storage2-changed and dom-private-storage2-changed so we
+        // can fire storage events. Use a strong reference.
         os->AddObserver(mObserver, "dom-storage2-changed", false);
+        os->AddObserver(mObserver, "dom-private-storage2-changed", false);
       }
 
       Preferences::AddStrongObserver(mObserver, "intl.accept_languages");
     }
   } else {
     // |this| is an outer window. Outer windows start out frozen and
     // remain frozen until they get an inner window.
     MOZ_ASSERT(IsFrozen());
@@ -1606,16 +1607,17 @@ nsGlobalWindow::CleanUp()
 
   DisconnectEventTargetObjects();
 
   if (mObserver) {
     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     if (os) {
       os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
       os->RemoveObserver(mObserver, "dom-storage2-changed");
+      os->RemoveObserver(mObserver, "dom-private-storage2-changed");
     }
 
 #ifdef MOZ_B2G
     DisableNetworkEvent(eNetworkUpload);
     DisableNetworkEvent(eNetworkDownload);
 #endif // MOZ_B2G
 
     if (mIdleService) {
@@ -11670,17 +11672,19 @@ nsGlobalWindow::Observe(nsISupports* aSu
       mNotifyIdleObserversIdleOnThaw = false;
     } else if (AsInner()->IsCurrentInnerWindow()) {
       MOZ_ASSERT(IsInnerWindow());
       ScheduleActiveTimerCallback();
     }
     return NS_OK;
   }
 
-  if (!nsCRT::strcmp(aTopic, "dom-storage2-changed")) {
+  bool isPrivateBrowsing = IsPrivateBrowsing();
+  if ((!nsCRT::strcmp(aTopic, "dom-storage2-changed") && !isPrivateBrowsing) ||
+      (!nsCRT::strcmp(aTopic, "dom-private-storage2-changed") && isPrivateBrowsing)) {
     if (!IsInnerWindow() || !AsInner()->IsCurrentInnerWindow()) {
       return NS_OK;
     }
 
     nsIPrincipal *principal;
     nsresult rv;
 
     RefPtr<StorageEvent> event = static_cast<StorageEvent*>(aSubject);
@@ -11705,19 +11709,17 @@ nsGlobalWindow::Observe(nsISupports* aSu
 
     uint32_t privateBrowsingId = 0;
     nsIPrincipal *storagePrincipal = changingStorage->GetPrincipal();
     rv = storagePrincipal->GetPrivateBrowsingId(&privateBrowsingId);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
-    if ((privateBrowsingId > 0) != IsPrivateBrowsing()) {
-      return NS_OK;
-    }
+    MOZ_ASSERT((privateBrowsingId > 0) == isPrivateBrowsing);
 
     switch (changingStorage->GetType())
     {
     case DOMStorage::SessionStorage:
     {
       bool check = false;
 
       nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(GetDocShell());
@@ -12274,16 +12276,17 @@ nsGlobalWindow::CallOnChildren(Method aM
 
 nsresult
 nsGlobalWindow::FireDelayedDOMEvents()
 {
   FORWARD_TO_INNER(FireDelayedDOMEvents, (), NS_ERROR_UNEXPECTED);
 
   for (uint32_t i = 0, len = mPendingStorageEvents.Length(); i < len; ++i) {
     Observe(mPendingStorageEvents[i], "dom-storage2-changed", nullptr);
+    Observe(mPendingStorageEvents[i], "dom-private-storage2-changed", nullptr);
   }
 
   if (mApplicationCache) {
     static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->FirePendingEvents();
   }
 
   // Fires an offline status event if the offline status has changed
   FireOfflineStatusEventIfChanged();
--- a/dom/storage/DOMStorage.cpp
+++ b/dom/storage/DOMStorage.cpp
@@ -8,17 +8,16 @@
 #include "DOMStorageCache.h"
 #include "DOMStorageManager.h"
 
 #include "nsIObserverService.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsICookiePermission.h"
-#include "nsPIDOMWindow.h"
 
 #include "mozilla/dom/StorageBinding.h"
 #include "mozilla/dom/StorageEvent.h"
 #include "mozilla/dom/StorageEventBinding.h"
 #include "mozilla/Services.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/EnumSet.h"
 #include "nsThreadUtils.h"
@@ -174,34 +173,40 @@ DOMStorage::Clear(nsIPrincipal& aSubject
   }
 }
 
 namespace {
 
 class StorageNotifierRunnable : public Runnable
 {
 public:
-  StorageNotifierRunnable(nsISupports* aSubject, const char16_t* aType)
-    : mSubject(aSubject), mType(aType)
+  StorageNotifierRunnable(nsISupports* aSubject, const char16_t* aType,
+                          bool aPrivateBrowsing)
+    : mSubject(aSubject), mType(aType), mPrivateBrowsing(aPrivateBrowsing)
   { }
 
   NS_DECL_NSIRUNNABLE
 
 private:
   nsCOMPtr<nsISupports> mSubject;
   const char16_t* mType;
+  const bool mPrivateBrowsing;
 };
 
 NS_IMETHODIMP
 StorageNotifierRunnable::Run()
 {
   nsCOMPtr<nsIObserverService> observerService =
     mozilla::services::GetObserverService();
   if (observerService) {
-    observerService->NotifyObservers(mSubject, "dom-storage2-changed", mType);
+    observerService->NotifyObservers(mSubject,
+                                     mPrivateBrowsing
+                                       ? "dom-private-storage2-changed"
+                                       : "dom-storage2-changed",
+                                     mType);
   }
   return NS_OK;
 }
 
 } // namespace
 
 void
 DOMStorage::BroadcastChangeNotification(const nsSubstring& aKey,
@@ -221,17 +226,18 @@ DOMStorage::BroadcastChangeNotification(
   // nsGlobalWindow.
   RefPtr<StorageEvent> event =
     StorageEvent::Constructor(nullptr, NS_LITERAL_STRING("storage"), dict);
 
   RefPtr<StorageNotifierRunnable> r =
     new StorageNotifierRunnable(event,
                                 GetType() == LocalStorage
                                   ? u"localStorage"
-                                  : u"sessionStorage");
+                                  : u"sessionStorage",
+                                IsPrivate());
   NS_DispatchToMainThread(r);
 }
 
 static const char kPermissionType[] = "cookie";
 static const char kStorageEnabled[] = "dom.storage.enabled";
 
 bool
 DOMStorage::CanUseStorage(nsIPrincipal& aSubjectPrincipal)
--- a/dom/tests/mochitest/storageevent/test_storageNotifications.html
+++ b/dom/tests/mochitest/storageevent/test_storageNotifications.html
@@ -39,17 +39,17 @@ function Tests()
   // Initially check the both storages are empty
   is(sessionStorage.length, 0, "Session storage is empty [1]");
   is(localStorage.length, 0, "Local storage is empty [1]");
 
   var onStorageChanged = {
     observe: function(subject, topic, type) {
       if (topic == "dom-storage2-changed") {
         ok(expectedTypes.length > 0, "Not more then expected events encountered");
-        is(type, expectedTypes.shift(), "Expected type of the storage notificaiton");
+        is(type, expectedTypes.shift(), "Expected type of the storage notification");
         tests.next();
       }
     }
   }
 
   // Listen for dom-storage2-changed notification
   SpecialPowers.Services.obs.addObserver(onStorageChanged,
     "dom-storage2-changed", false);
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -2,16 +2,17 @@
 /* vim: set sw=2 ts=8 et tw=80 : */
 
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "necko-config.h"
 #include "nsHttp.h"
+#include "mozilla/BasePrincipal.h"
 #include "mozilla/net/NeckoParent.h"
 #include "mozilla/net/HttpChannelParent.h"
 #include "mozilla/net/CookieServiceParent.h"
 #include "mozilla/net/WyciwygChannelParent.h"
 #include "mozilla/net/FTPChannelParent.h"
 #include "mozilla/net/WebSocketChannelParent.h"
 #include "mozilla/net/WebSocketEventListenerParent.h"
 #include "mozilla/net/DataChannelParent.h"