Bug 1311707 - dom-private-storage2-changed notification, r=janv
authorAndrea Marchesini <amarchesini@mozilla.com>
Tue, 29 Nov 2016 07:19:08 +0100
changeset 324638 a7623a66ba0873bff655f5957b39909f2927aa6b
parent 324515 2411fdf8a816414051e84cb2de43498d9e648d18
child 324639 d40f29aab1e9b06dd81b80afcfc988f70209e636
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersjanv
bugs1311707
milestone53.0a1
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"