Bug 1498370 - Update the storage access flag when storage access is granted/denied r=baku
authorEhsan Akhgari <ehsan@mozilla.com>
Mon, 15 Oct 2018 10:29:33 +0000
changeset 441275 e362c18e1830925ac30a83c45eccd16257cb58b9
parent 441274 ad329ec90b6eedfdc428f8dcb08d85aed08af61d
child 441276 8efab497c2e6f710481d215e360650b220865269
push id70992
push usereakhgari@mozilla.com
push dateMon, 15 Oct 2018 13:41:50 +0000
treeherderautoland@e362c18e1830 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1498370
milestone64.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 1498370 - Update the storage access flag when storage access is granted/denied r=baku Differential Revision: https://phabricator.services.mozilla.com/D8550
dom/base/nsDocument.cpp
dom/base/nsGlobalWindowOuter.cpp
dom/base/nsGlobalWindowOuter.h
toolkit/components/antitracking/AntiTrackingCommon.cpp
toolkit/components/antitracking/AntiTrackingCommon.h
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -13664,41 +13664,16 @@ nsIDocument::HasStorageAccess(mozilla::E
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
   if (topLevelDoc->NodePrincipal()->Equals(NodePrincipal())) {
     promise->MaybeResolve(true);
     return promise.forget();
   }
 
-  if (AntiTrackingCommon::ShouldHonorContentBlockingCookieRestrictions() &&
-      StaticPrefs::network_cookie_cookieBehavior() ==
-        nsICookieService::BEHAVIOR_REJECT_TRACKER) {
-    // If we need to abide by Content Blocking cookie restrictions, ensure to
-    // first do all of our storage access checks.  If storage access isn't
-    // disabled in our document, given that we're a third-party, we must either
-    // not be a tracker, or be whitelisted for some reason (e.g. a storage
-    // access permission being granted).  In that case, resolve the promise and
-    // say we have obtained storage access.
-    if (!nsContentUtils::StorageDisabledByAntiTracking(this, nullptr)) {
-      // Note, storage might be allowed because the top-level document is on
-      // the content blocking allowlist!  In that case, don't provide special
-      // treatment here.
-      bool isOnAllowList = false;
-      if (NS_SUCCEEDED(AntiTrackingCommon::IsOnContentBlockingAllowList(
-                         topLevelDoc->GetDocumentURI(),
-                         AntiTrackingCommon::eStorageChecks,
-                         isOnAllowList)) &&
-          !isOnAllowList) {
-        promise->MaybeResolve(true);
-        return promise.forget();
-      }
-    }
-  }
-
   nsPIDOMWindowInner* inner = GetInnerWindow();
   nsGlobalWindowOuter* outer = nullptr;
   if (inner) {
     outer = nsGlobalWindowOuter::Cast(inner->GetOuterWindow());
     promise->MaybeResolve(outer->HasStorageAccess());
   } else {
     promise->MaybeRejectWithUndefined();
   }
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -13,16 +13,18 @@
 // Local Includes
 #include "Navigator.h"
 #include "nsContentSecurityManager.h"
 #include "nsScreen.h"
 #include "nsHistory.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsICookieService.h"
 #include "nsIDOMStorageManager.h"
+#include "nsIPermission.h"
+#include "nsIPermissionManager.h"
 #include "nsISecureBrowserUI.h"
 #include "nsIWebProgressListener.h"
 #include "mozilla/AntiTrackingCommon.h"
 #include "mozilla/dom/ContentFrameMessageManager.h"
 #include "mozilla/dom/EventTarget.h"
 #include "mozilla/dom/LocalStorage.h"
 #include "mozilla/dom/Storage.h"
 #include "mozilla/dom/IdleRequest.h"
@@ -973,16 +975,21 @@ nsGlobalWindowOuter::~nsGlobalWindowOute
   // Outer windows are always supposed to call CleanUp before letting themselves
   // be destroyed.
   MOZ_ASSERT(mCleanedUp);
 
   nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
   if (ac)
     ac->RemoveWindowAsListener(this);
 
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    obs->RemoveObserver(this, PERM_CHANGE_NOTIFICATION);
+  }
+
   nsLayoutStatics::Release();
 }
 
 // static
 void
 nsGlobalWindowOuter::ShutDown()
 {
   AssertIsOnMainThread();
@@ -1084,16 +1091,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject)
   NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
   NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
   NS_INTERFACE_MAP_ENTRY(nsPIDOMWindowOuter)
   NS_INTERFACE_MAP_ENTRY(mozIDOMWindowProxy)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMChromeWindow, IsChromeWindow())
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
+  NS_INTERFACE_MAP_ENTRY(nsIObserver)
 NS_INTERFACE_MAP_END
 
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindowOuter)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindowOuter)
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindowOuter)
   if (tmp->IsBlackForCC(false)) {
@@ -6797,16 +6805,66 @@ nsGlobalWindowOuter::GetInterface(const 
 {
   nsresult rv = GetInterfaceInternal(aIID, aSink);
   if (rv == NS_ERROR_NO_INTERFACE) {
     return QueryInterface(aIID, aSink);
   }
   return rv;
 }
 
+//*****************************************************************************
+// nsGlobalWindowOuter::nsIObserver
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsGlobalWindowOuter::Observe(nsISupports* aSupports, const char* aTopic,
+                             const char16_t* aData)
+{
+  if (!nsCRT::strcmp(aTopic, PERM_CHANGE_NOTIFICATION)) {
+    if (!nsCRT::strcmp(aData, u"cleared") && !aSupports) {
+      // All permissions have been cleared.
+      mHasStorageAccess = false;
+      return NS_OK;
+    }
+    nsCOMPtr<nsIPermission> permission = do_QueryInterface(aSupports);
+    if (!permission) {
+      return NS_OK;
+    }
+    nsIPrincipal* principal = GetPrincipal();
+    if (!principal) {
+      return NS_OK;
+    }
+    if (!AntiTrackingCommon::IsStorageAccessPermission(permission, principal)) {
+      return NS_OK;
+    }
+    if (!nsCRT::strcmp(aData, u"deleted")) {
+      // The storage access permission was deleted.
+      mHasStorageAccess = false;
+      return NS_OK;
+    }
+    if (!nsCRT::strcmp(aData, u"added") ||
+        !nsCRT::strcmp(aData, u"changed")) {
+      // The storage access permission was granted or modified.
+      uint32_t expireType = 0;
+      int64_t expireTime = 0;
+      MOZ_ALWAYS_SUCCEEDS(permission->GetExpireType(&expireType));
+      MOZ_ALWAYS_SUCCEEDS(permission->GetExpireTime(&expireTime));
+      if ((expireType == nsIPermissionManager::EXPIRE_TIME &&
+           expireTime >= PR_Now() / 1000) ||
+          (expireType == nsIPermissionManager::EXPIRE_SESSION &&
+           expireTime != 0)) {
+        // Permission hasn't expired yet.
+        mHasStorageAccess = true;
+        return NS_OK;
+      }
+    }
+  }
+  return NS_OK;
+}
+
 bool
 nsGlobalWindowOuter::IsSuspended() const
 {
   MOZ_ASSERT(NS_IsMainThread());
   // No inner means we are effectively suspended
   if (!mInnerWindow) {
     return true;
   }
@@ -7668,16 +7726,20 @@ nsGlobalWindowOuter::Create(nsIDocShell*
   uint64_t outerWindowID = aDocShell->GetOuterWindowID();
   RefPtr<nsGlobalWindowOuter> window = new nsGlobalWindowOuter(outerWindowID);
   if (aIsChrome) {
     window->mIsChrome = true;
   }
   window->SetDocShell(aDocShell);
 
   window->InitWasOffline();
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    obs->AddObserver(window, PERM_CHANGE_NOTIFICATION, true);
+  }
   return window.forget();
 }
 
 nsIURI*
 nsPIDOMWindowOuter::GetDocumentURI() const
 {
   return mDoc ? mDoc->GetDocumentURI() : mDocumentURI.get();
 }
--- a/dom/base/nsGlobalWindowOuter.h
+++ b/dom/base/nsGlobalWindowOuter.h
@@ -22,16 +22,17 @@
 #include "nsDataHashtable.h"
 #include "nsJSThingHashtable.h"
 #include "nsCycleCollectionParticipant.h"
 
 // Interfaces Needed
 #include "nsIBrowserDOMWindow.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIDOMChromeWindow.h"
+#include "nsIObserver.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsITimer.h"
 #include "mozilla/EventListenerManager.h"
 #include "nsIPrincipal.h"
 #include "nsSize.h"
 #include "mozilla/FlushType.h"
 #include "prclist.h"
@@ -167,16 +168,17 @@ class nsGlobalWindowOuter final
     // NOTE: This interface is private, as it's only
     // implemented on chrome windows.
   , private nsIDOMChromeWindow
   , public nsIScriptGlobalObject
   , public nsIScriptObjectPrincipal
   , public nsSupportsWeakReference
   , public nsIInterfaceRequestor
   , public PRCListStr
+  , public nsIObserver
 {
 public:
   typedef nsDataHashtable<nsUint64HashKey, nsGlobalWindowOuter*> OuterWindowByIdTable;
 
   static void
   AssertIsOnMainThread()
 #ifdef DEBUG
   ;
@@ -356,16 +358,19 @@ public:
   void FinishFullscreenChange(bool aIsFullscreen) final;
   bool SetWidgetFullscreen(FullscreenReason aReason, bool aIsFullscreen,
                            nsIWidget* aWidget, nsIScreen* aScreen);
   bool Fullscreen() const;
 
   // nsIInterfaceRequestor
   NS_DECL_NSIINTERFACEREQUESTOR
 
+  // nsIObserver
+  NS_DECL_NSIOBSERVER
+
   already_AddRefed<nsPIDOMWindowOuter> IndexedGetterOuter(uint32_t aIndex);
 
   already_AddRefed<nsPIDOMWindowOuter> GetTop() override;
   nsPIDOMWindowOuter* GetScriptableTop() override;
   inline nsGlobalWindowOuter *GetTopInternal();
 
   inline nsGlobalWindowOuter* GetScriptableTopInternal();
 
--- a/toolkit/components/antitracking/AntiTrackingCommon.cpp
+++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp
@@ -559,16 +559,52 @@ AntiTrackingCommon::SaveFirstPartyStorag
                             nsIPermissionManager::ALLOW_ACTION,
                             expirationType, when);
   Unused << NS_WARN_IF(NS_FAILED(rv));
   aResolver(NS_SUCCEEDED(rv));
 
   LOG(("Result: %s", NS_SUCCEEDED(rv) ? "success" : "failure"));
 }
 
+// static
+bool
+AntiTrackingCommon::IsStorageAccessPermission(nsIPermission* aPermission,
+                                              nsIPrincipal* aPrincipal)
+{
+  MOZ_ASSERT(aPermission);
+  MOZ_ASSERT(aPrincipal);
+
+  nsAutoCString origin;
+  nsresult rv = aPrincipal->GetOriginNoSuffix(origin);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  // The permission key may belong either to a tracking origin on the same
+  // origin as the granted origin, or on another origin as the granted origin
+  // (for example when a tracker in a third-party context uses window.open to
+  // open another origin where that second origin would be the granted origin.)
+  // But even in the second case, the type of the permission would still be
+  // formed by concatenating the granted origin to the end of the type name
+  // (see CreatePermissionKey).  Therefore, we pass in the same argument to
+  // both tracking origin and granted origin here in order to compute the
+  // shorter permission key and will then do a prefix match on the type of the
+  // input permission to see if it is a storage access permission or not.
+  nsAutoCString permissionKey;
+  CreatePermissionKey(origin, origin, permissionKey);
+
+  nsAutoCString type;
+  rv = aPermission->GetType(type);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  return StringBeginsWith(type, permissionKey);
+}
+
 bool
 AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(nsPIDOMWindowInner* aWindow,
                                                         nsIURI* aURI,
                                                         uint32_t* aRejectedReason)
 {
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(aURI);
 
--- a/toolkit/components/antitracking/AntiTrackingCommon.h
+++ b/toolkit/components/antitracking/AntiTrackingCommon.h
@@ -8,16 +8,17 @@
 #define mozilla_antitrackingservice_h
 
 #include "nsString.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/RefPtr.h"
 
 class nsIChannel;
 class nsIHttpChannel;
+class nsIPermission;
 class nsIPrincipal;
 class nsIURI;
 class nsPIDOMWindowInner;
 
 namespace mozilla {
 
 class AntiTrackingCommon final
 {
@@ -103,16 +104,21 @@ public:
   //   the user interacts with it. tracker.com is allowed when loaded by
   //   example.net.
   typedef MozPromise<bool, bool, false> StorageAccessGrantPromise;
   static MOZ_MUST_USE RefPtr<StorageAccessGrantPromise>
   AddFirstPartyStorageAccessGrantedFor(nsIPrincipal* aPrincipal,
                                        nsPIDOMWindowInner* aParentWindow,
                                        StorageAccessGrantedReason aReason);
 
+  // Returns true if the permission passed in is a storage access permission
+  // for the passed in principal argument.
+  static bool
+  IsStorageAccessPermission(nsIPermission* aPermission, nsIPrincipal* aPrincipal);
+
   static void
   StoreUserInteractionFor(nsIPrincipal* aPrincipal);
 
   static bool
   HasUserInteraction(nsIPrincipal* aPrincipal);
 
   // For IPC only.
   static void