Bug 1612376 - P4. ShouldAllowAccessFor use ContentBlocking::HasStorageAccessGranted r=timhuang,baku
authorDimi Lee <dlee@mozilla.com>
Wed, 29 Apr 2020 14:48:35 +0000
changeset 526674 e2eb63c3ce5caf55069da424079d9ad293bf5999
parent 526673 747f90b27d59cd0e7e20131135570420e515fbfa
child 526675 6400cd18a2972f4eb48774b4dcc46325ad9ee109
push id37361
push usermalexandru@mozilla.com
push dateWed, 29 Apr 2020 21:55:39 +0000
treeherdermozilla-central@bc0932b38478 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstimhuang, baku
bugs1612376
milestone77.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 1612376 - P4. ShouldAllowAccessFor use ContentBlocking::HasStorageAccessGranted r=timhuang,baku There are two places where we save storage permission: 1. LoadInfo hasStoragePermission attribute 2. mStorageAccessGranted in nsPIDOMWindowInner For LoadInfo.hasStoragePermission, it is set during channel creation and its value remains the same even when the storage permission is granted afterward. The updated storage permission for a window is saved in mStorageAccessGranted, which has a different meaning for fission and non-fission mode. In non-fission mode, mStorageAccessGranted is saved in the top-level window and it is an array containing all tracking subframes that are allowed to access storage. In fission mode, mStorageAccessGranted is set in individual tracking windows that we have granted its storage permission. Although it works like a boolean flag in fission, we still keep using an array to compatible with the use case in non-fission mode. Depends on D71984 Differential Revision: https://phabricator.services.mozilla.com/D71985
dom/base/Document.cpp
dom/base/Document.h
toolkit/components/antitracking/ContentBlocking.cpp
toolkit/components/antitracking/ContentBlocking.h
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -1358,17 +1358,16 @@ Document::Document(const char* aContentT
       mScrollAnchorAdjustmentCount(0),
       mServoRestyleRootDirtyBits(0),
       mThrowOnDynamicMarkupInsertionCounter(0),
       mIgnoreOpensDuringUnloadCounter(0),
       mDocLWTheme(Doc_Theme_Uninitialized),
       mSavedResolution(1.0f),
       mSavedResolutionBeforeMVM(1.0f),
       mPendingInitialTranslation(false),
-      mHasStoragePermission(false),
       mGeneration(0),
       mCachedTabSizeGeneration(0),
       mNextFormNumber(0),
       mNextControlNumber(0) {
   MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p created", this));
 
   SetIsInDocument();
   SetIsConnected(true);
@@ -3181,18 +3180,16 @@ nsresult Document::StartDocumentLoad(con
 
   // Initialize FeaturePolicy
   rv = InitFeaturePolicy(aChannel);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = loadInfo->GetCookieJarSettings(getter_AddRefs(mCookieJarSettings));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mHasStoragePermission = loadInfo->GetHasStoragePermission();
-
   return NS_OK;
 }
 
 nsIContentSecurityPolicy* Document::GetCsp() const { return mCSP; }
 
 void Document::SetCsp(nsIContentSecurityPolicy* aCSP) { mCSP = aCSP; }
 
 nsIContentSecurityPolicy* Document::GetPreloadCsp() const {
@@ -16163,16 +16160,33 @@ nsICookieJarSettings* Document::CookieJa
             "WindowGlobalParent");
       }
     }
   }
 
   return mCookieJarSettings;
 }
 
+bool Document::HasStoragePermission() {
+  // The HasStoragePermission flag in LoadInfo remains fixed when
+  // it is set in the parent process, so we need to check the cache
+  // to see if the permission is granted afterwards.
+  nsPIDOMWindowInner* inner = GetInnerWindow();
+  if (ContentBlocking::HasStorageAccessGranted(inner)) {
+    return true;
+  }
+
+  if (!mChannel) {
+    return false;
+  }
+
+  nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
+  return loadInfo->GetHasStoragePermission();
+}
+
 nsIPrincipal* Document::EffectiveStoragePrincipal() const {
   nsPIDOMWindowInner* inner = GetInnerWindow();
   if (!inner) {
     return NodePrincipal();
   }
 
   // Return our cached storage principal if one exists.
   if (mActiveStoragePrincipal) {
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -1428,17 +1428,17 @@ class Document : public nsINode,
   // Helper method that returns true if storage access API is enabled and
   // the passed flag has storage-access sandbox flag.
   static bool StorageAccessSandboxed(uint32_t aSandboxFlags);
 
   // Returns the cookie jar settings for this and sub contexts.
   nsICookieJarSettings* CookieJarSettings();
 
   // Returns whether this document has the storage permission.
-  bool HasStoragePermission() { return mHasStoragePermission; }
+  bool HasStoragePermission();
 
   // Increments the document generation.
   inline void Changed() { ++mGeneration; }
 
   // Returns the current generation.
   inline int32_t GetGeneration() const { return mGeneration; }
 
   // Adds cached sizes values to aSizes if there's any
--- a/toolkit/components/antitracking/ContentBlocking.cpp
+++ b/toolkit/components/antitracking/ContentBlocking.cpp
@@ -670,16 +670,35 @@ ContentBlocking::SaveAccessForOriginOnPa
     TemporaryAccessGrantObserver::Create(permManager, aParentPrincipal, type);
   }
 
   LOG(("Result: %s", NS_SUCCEEDED(rv) ? "success" : "failure"));
   return ParentAccessGrantPromise::CreateAndResolve(rv, __func__);
 }
 
 /* static */
+bool ContentBlocking::HasStorageAccessGranted(nsPIDOMWindowInner* aWindow) {
+  if (!aWindow) {
+    return false;
+  }
+
+  nsAutoCString trackingOrigin;
+  if (!GetTrackingOrigin(nsGlobalWindowInner::Cast(aWindow), trackingOrigin)) {
+    LOG(("Failed to obtain the the tracking origin"));
+    return false;
+  }
+
+  nsAutoCString permissionKey;
+  AntiTrackingUtils::CreateStoragePermissionKey(trackingOrigin, permissionKey);
+
+  return ContentBlocking::HasStorageAccessGranted(aWindow->GetBrowsingContext(),
+                                                  permissionKey);
+}
+
+/* static */
 bool ContentBlocking::HasStorageAccessGranted(
     BrowsingContext* aBrowsingContext, const nsACString& aPermissionKey) {
   MOZ_ASSERT(aBrowsingContext);
 
   bool useRemoteSubframes;
   aBrowsingContext->GetUseRemoteSubframes(&useRemoteSubframes);
 
   // For non-fission, the permission is stored in the top-level window.
@@ -830,54 +849,16 @@ bool ContentBlocking::ShouldAllowAccessF
 
   nsGlobalWindowInner* innerWindow = nsGlobalWindowInner::Cast(aWindow);
   Document* document = innerWindow->GetExtantDoc();
   if (!document) {
     LOG(("Our window has no document"));
     return false;
   }
 
-  BrowsingContext* topBC = aWindow->GetBrowsingContext()->Top();
-  nsGlobalWindowOuter* topWindow = nullptr;
-  if (topBC->IsInProcess()) {
-    topWindow = nsGlobalWindowOuter::Cast(topBC->GetDOMWindow());
-  } else {
-    // For out-of-process top frames, we need to be able to access three things
-    // from the top BrowsingContext in order to be able to port this code to
-    // Fission successfully:
-    //   * The CookieJarSettings of the top BrowsingContext.
-    //   * The HasStorageAccessGranted() API on BrowsingContext.
-    // For now, if we face an out-of-process top frame, instead of failing here,
-    // we revert back to looking at the in-process top frame.  This is of course
-    // the wrong thing to do, but we seem to have a number of tests in the tree
-    // which are depending on this incorrect behaviour.  This path is intended
-    // to temporarily keep those tests working...
-    nsGlobalWindowOuter* outerWindow =
-        nsGlobalWindowOuter::Cast(aWindow->GetOuterWindow());
-    if (!outerWindow) {
-      LOG(("Our window has no outer window"));
-      return false;
-    }
-
-    nsCOMPtr<nsPIDOMWindowOuter> topOuterWindow =
-        outerWindow->GetInProcessTop();
-    topWindow = nsGlobalWindowOuter::Cast(topOuterWindow);
-  }
-
-  if (NS_WARN_IF(!topWindow)) {
-    LOG(("No top outer window"));
-    return false;
-  }
-
-  nsPIDOMWindowInner* topInnerWindow = topWindow->GetCurrentInnerWindow();
-  if (NS_WARN_IF(!topInnerWindow)) {
-    LOG(("No top inner window."));
-    return false;
-  }
-
   uint32_t cookiePermission = CheckCookiePermissionForPrincipal(
       document->CookieJarSettings(), document->NodePrincipal());
   if (cookiePermission != nsICookiePermission::ACCESS_DEFAULT) {
     LOG(
         ("CheckCookiePermissionForPrincipal() returned a non-default access "
          "code (%d) for window's principal, returning %s",
          int(cookiePermission),
          cookiePermission != nsICookiePermission::ACCESS_DENY ? "success"
@@ -1003,42 +984,28 @@ bool ContentBlocking::ShouldAllowAccessF
   // here since we only get here if the window is not a top.
   if (behavior == nsICookieService::BEHAVIOR_REJECT_TRACKER &&
       !AntiTrackingUtils::IsFirstLevelSubContext(
           aWindow->GetBrowsingContext())) {
     *aRejectedReason = blockedReason;
     return false;
   }
 
-  nsAutoCString trackingOrigin;
-  if (!GetTrackingOrigin(nsGlobalWindowInner::Cast(aWindow), trackingOrigin)) {
-    LOG(("Failed to obtain the the tracking origin"));
-    *aRejectedReason = blockedReason;
-    return false;
-  }
-
-  nsAutoCString type;
-  AntiTrackingUtils::CreateStoragePermissionKey(trackingOrigin, type);
-
-  if (topInnerWindow->HasStorageAccessGranted(type)) {
-    LOG(("Permission stored in the window. All good."));
-    return true;
-  }
-
-  RefPtr<WindowContext> wc = aWindow->GetWindowContext();
-  if (!wc) {
-    LOG(("Failed to obtain the window context from the window."));
-    *aRejectedReason = blockedReason;
-    return false;
-  }
-
-  bool allowed = wc->GetHasStoragePermission();
+  bool allowed = document->HasStoragePermission();
 
   if (!allowed) {
     *aRejectedReason = blockedReason;
+  } else {
+    // Document::HasStoragePermission already checks HasStorageAccessGranted,
+    // we just show log here to know whether the permission is granted because
+    // of permission update.
+    if (MOZ_LOG_TEST(gAntiTrackingLog, mozilla::LogLevel::Debug) &&
+        ContentBlocking::HasStorageAccessGranted(aWindow)) {
+      LOG(("Permission stored in the window. All good."));
+    }
   }
 
   return allowed;
 }
 
 bool ContentBlocking::ShouldAllowAccessFor(nsIChannel* aChannel, nsIURI* aURI,
                                            uint32_t* aRejectedReason) {
   MOZ_ASSERT(aURI);
@@ -1208,62 +1175,43 @@ bool ContentBlocking::ShouldAllowAccessF
 
   nsAutoCString trackingOrigin;
   rv = nsContentUtils::GetASCIIOrigin(trackingURI, trackingOrigin);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     LOG_SPEC(("Failed to compute the origin from %s", _spec), trackingURI);
     return false;
   }
 
-  nsAutoCString type;
-  AntiTrackingUtils::CreateStoragePermissionKey(trackingOrigin, type);
-
-  auto checkPermission = [loadInfo, aRejectedReason, blockedReason]() -> bool {
-    bool allowed = loadInfo->GetHasStoragePermission();
-
-    if (!allowed) {
-      *aRejectedReason = blockedReason;
-    }
+  // HasStorageAccessGranted only applies to channels that load documents,
+  // for sub-resources loads, just returns the result from loadInfo.
+  bool isDocument = false;
+  aChannel->GetIsDocument(&isDocument);
 
-    return allowed;
-  };
+  if (isDocument) {
+    RefPtr<BrowsingContext> browsingContext;
+    rv = loadInfo->GetTargetBrowsingContext(getter_AddRefs(browsingContext));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      LOG(("Failed to get the channel's target browsing context"));
+    } else {
+      nsAutoCString type;
+      AntiTrackingUtils::CreateStoragePermissionKey(trackingOrigin, type);
 
-  // Call HasStorageAccessGranted() in the top-level inner window to check
-  // if the storage permission has been granted by the heuristic or the
-  // StorageAccessAPI. Note that calling the HasStorageAccessGranted() is still
-  // not fission-compatible. This would be modified in Bug 1612376.
-  RefPtr<BrowsingContext> bc;
-  loadInfo->GetBrowsingContext(getter_AddRefs(bc));
-  if (!bc) {
-    return checkPermission();
+      if (ContentBlocking::HasStorageAccessGranted(browsingContext, type)) {
+        LOG(("Permission stored in the window. All good."));
+        return true;
+      }
+    }
   }
 
-  bc = bc->Top();
-  if (!bc || !bc->IsInProcess()) {
-    return checkPermission();
-  }
-
-  nsGlobalWindowOuter* topWindow =
-      nsGlobalWindowOuter::Cast(bc->GetDOMWindow());
-
-  if (!topWindow) {
-    return checkPermission();
+  bool allowed = loadInfo->GetHasStoragePermission();
+  if (!allowed) {
+    *aRejectedReason = blockedReason;
   }
 
-  nsPIDOMWindowInner* topInnerWindow = topWindow->GetCurrentInnerWindow();
-  // We use the 'hasStoragePermission' flag to check the storage permission.
-  // However, this flag won't get updated once the permission is granted by
-  // the heuristic or the StorageAccessAPI. So, we need to check the
-  // HasStorageAccessGranted() in order to get the correct storage access before
-  // we check the 'hasStoragePermission' flag.
-  if (topInnerWindow && topInnerWindow->HasStorageAccessGranted(type)) {
-    return true;
-  }
-
-  return checkPermission();
+  return allowed;
 }
 
 bool ContentBlocking::ShouldAllowAccessFor(
     nsIPrincipal* aPrincipal, nsICookieJarSettings* aCookieJarSettings) {
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(aCookieJarSettings);
 
   uint32_t access = nsICookiePermission::ACCESS_DEFAULT;
--- a/toolkit/components/antitracking/ContentBlocking.h
+++ b/toolkit/components/antitracking/ContentBlocking.h
@@ -122,16 +122,19 @@ class ContentBlocking final {
   // processes with an in-process browsing context.
   static MOZ_MUST_USE RefPtr<StorageAccessGrantPromise> CompleteAllowAccessFor(
       dom::BrowsingContext* aParentContext, uint64_t aTopLevelWindowId,
       nsIPrincipal* aTrackingPrincipal, const nsCString& aTrackingOrigin,
       uint32_t aCookieBehavior,
       ContentBlockingNotifier::StorageAccessGrantedReason aReason,
       const PerformFinalChecks& aPerformFinalChecks = nullptr);
 
+  friend class dom::Document;
+  static bool HasStorageAccessGranted(nsPIDOMWindowInner* aWindow);
+
   static bool HasStorageAccessGranted(dom::BrowsingContext* aBrowsingContext,
                                       const nsACString& aPermissionKey);
 
   static void UpdateAllowAccessOnCurrentProcess(
       dom::BrowsingContext* aParentContext, const nsACString& aTrackingOrigin);
 
   static void UpdateAllowAccessOnParentProcess(
       dom::BrowsingContext* aParentContext, const nsACString& aTrackingOrigin);