Bug 1517057 - Part 2: Avoid minting a new codebase principal in the channel-based version of AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor() in the common case to make things faster; r=baku
authorEhsan Akhgari <ehsan@mozilla.com>
Wed, 02 Jan 2019 15:41:59 +0000
changeset 509376 1da34ff3bc95118f93db519784d9e60368e7aade
parent 509375 23a0332b18a1ed07e9ad20eaf1920c79dcf58849
child 509377 f13ad4df31b23f50ad6b1644f4c10490a21552f7
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1517057
milestone66.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 1517057 - Part 2: Avoid minting a new codebase principal in the channel-based version of AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor() in the common case to make things faster; r=baku Depends on D15547 Differential Revision: https://phabricator.services.mozilla.com/D15548
extensions/cookie/nsCookiePermission.cpp
netwerk/cookie/nsICookiePermission.idl
toolkit/components/antitracking/AntiTrackingCommon.cpp
--- a/extensions/cookie/nsCookiePermission.cpp
+++ b/extensions/cookie/nsCookiePermission.cpp
@@ -124,16 +124,33 @@ nsCookiePermission::CanAccess(nsIPrincip
       *aResult = nsICookiePermission::ACCESS_ALLOW;
     }
   }
 
   return rv;
 }
 
 NS_IMETHODIMP
+nsCookiePermission::CanAccessURI(nsIURI *aURI, nsCookieAccess *aResult) {
+  // Lazily initialize ourselves
+  if (!EnsureInitialized()) return NS_ERROR_UNEXPECTED;
+
+  // finally, check with permission manager...
+  nsresult rv =
+      mPermMgr->TestPermission(aURI, kPermissionType, (uint32_t *)aResult);
+  if (NS_SUCCEEDED(rv)) {
+    if (*aResult == nsICookiePermission::ACCESS_SESSION) {
+      *aResult = nsICookiePermission::ACCESS_ALLOW;
+    }
+  }
+
+  return rv;
+}
+
+NS_IMETHODIMP
 nsCookiePermission::CanSetCookie(nsIURI *aURI, nsIChannel *aChannel,
                                  nsICookie2 *aCookie, bool *aIsSession,
                                  int64_t *aExpiry, bool *aResult) {
   NS_ASSERTION(aURI, "null uri");
 
   *aResult = kDefaultPolicy;
 
   // Lazily initialize ourselves
--- a/netwerk/cookie/nsICookiePermission.idl
+++ b/netwerk/cookie/nsICookiePermission.idl
@@ -60,16 +60,34 @@ interface nsICookiePermission : nsISuppo
    *
    * @return one of the following nsCookieAccess values:
    *         ACCESS_DEFAULT, ACCESS_ALLOW, ACCESS_DENY, or
    *         ACCESS_ALLOW_FIRST_PARTY_ONLY
    */
   nsCookieAccess canAccess(in nsIPrincipal aPrincipal);
 
   /**
+   * canAccessURI
+   *
+   * this method is called to test whether or not the given principal may
+   * access the cookie database, either to set or get cookies.
+   *
+   * Be careful when calling this function, you probably want the principal
+   * based version instead of this one unless if performance is an issue.
+   *
+   * @param aURI
+   *        the URI trying to access cookies.
+   *
+   * @return one of the following nsCookieAccess values:
+   *         ACCESS_DEFAULT, ACCESS_ALLOW, ACCESS_DENY, or
+   *         ACCESS_ALLOW_FIRST_PARTY_ONLY
+   */
+  nsCookieAccess canAccessURI(in nsIURI aURI);
+
+  /**
    * canSetCookie
    *
    * this method is called to test whether or not the given URI/channel may
    * set a specific cookie.  this method is always preceded by a call to
    * canAccess. it may modify the isSession and expiry attributes of the
    * cookie via the aIsSession and aExpiry parameters, in order to limit
    * or extend the lifetime of the cookie. this is useful, for instance, to
    * downgrade a cookie to session-only if it fails to meet certain criteria.
--- a/toolkit/components/antitracking/AntiTrackingCommon.cpp
+++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp
@@ -134,17 +134,33 @@ nsCookieAccess CheckCookiePermissionForP
   if (!aPrincipal->GetIsCodebasePrincipal()) {
     return access;
   }
 
   nsCOMPtr<nsICookiePermission> cps = nsCookiePermission::GetOrCreate();
 
   nsresult rv = cps->CanAccess(aPrincipal, &access);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    return access;
+    return nsICookiePermission::ACCESS_DEFAULT;
+  }
+
+  // If we have a custom cookie permission, let's use it.
+  return access;
+}
+
+// This internal method returns ACCESS_DENY if the access is denied,
+// ACCESS_DEFAULT if unknown, some other access code if granted.
+nsCookieAccess CheckCookiePermissionForURI(nsIURI* aURI) {
+  nsCookieAccess access = nsICookiePermission::ACCESS_DEFAULT;
+
+  nsCOMPtr<nsICookiePermission> cps = nsCookiePermission::GetOrCreate();
+
+  nsresult rv = cps->CanAccessURI(aURI, &access);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return nsICookiePermission::ACCESS_DEFAULT;
   }
 
   // If we have a custom cookie permission, let's use it.
   return access;
 }
 
 int32_t CookiesBehavior(nsIPrincipal* aTopLevelPrincipal,
                         nsIPrincipal* a3rdPartyPrincipal) {
@@ -157,16 +173,36 @@ int32_t CookiesBehavior(nsIPrincipal* aT
   if (a3rdPartyPrincipal &&
       BasePrincipal::Cast(a3rdPartyPrincipal)->AddonPolicy()) {
     return nsICookieService::BEHAVIOR_ACCEPT;
   }
 
   return StaticPrefs::network_cookie_cookieBehavior();
 }
 
+int32_t CookiesBehavior(nsIPrincipal* aTopLevelPrincipal,
+                        nsIURI* a3rdPartyURI) {
+  // WebExtensions principals always get BEHAVIOR_ACCEPT as cookieBehavior
+  // (See Bug 1406675 for rationale).
+  if (BasePrincipal::Cast(aTopLevelPrincipal)->AddonPolicy()) {
+    return nsICookieService::BEHAVIOR_ACCEPT;
+  }
+
+  // This is semantically equivalent to the principal having a AddonPolicy().
+  bool is3rdPartyMozExt = false;
+  if (a3rdPartyURI &&
+      NS_SUCCEEDED(
+          a3rdPartyURI->SchemeIs("moz-extension", &is3rdPartyMozExt)) &&
+      is3rdPartyMozExt) {
+    return nsICookieService::BEHAVIOR_ACCEPT;
+  }
+
+  return StaticPrefs::network_cookie_cookieBehavior();
+}
+
 bool CheckContentBlockingAllowList(nsIURI* aTopWinURI,
                                    bool aIsPrivateBrowsing) {
   bool isAllowed = false;
   nsresult rv = AntiTrackingCommon::IsOnContentBlockingAllowList(
       aTopWinURI, aIsPrivateBrowsing, AntiTrackingCommon::eStorageChecks,
       isAllowed);
   if (NS_SUCCEEDED(rv) && isAllowed) {
     LOG_SPEC(
@@ -988,59 +1024,66 @@ bool AntiTrackingCommon::IsFirstPartySto
 
   // Let's avoid a null check on aRejectedReason everywhere else.
   uint32_t rejectedReason = 0;
   if (!aRejectedReason) {
     aRejectedReason = &rejectedReason;
   }
 
   nsCOMPtr<nsIURI> channelURI;
-  Unused << aChannel->GetURI(getter_AddRefs(channelURI));
+  nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
+  if (NS_FAILED(rv)) {
+    LOG(("Failed to get the channel final URI, bail out early"));
+    return true;
+  }
   LOG_SPEC(
       ("Computing whether channel %p has access to URI %s", aChannel, _spec),
       channelURI);
 
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
   if (!loadInfo) {
     LOG(("No loadInfo, bail out early"));
     return true;
   }
 
   // We need to find the correct principal to check the cookie permission. For
   // third-party contexts, we want to check if the top-level window has a custom
   // cookie permission.
-  nsIPrincipal* toplevelPrincipal = loadInfo->GetTopLevelPrincipal();
+  nsCOMPtr<nsIPrincipal> toplevelPrincipal = loadInfo->GetTopLevelPrincipal();
 
   // If this is already the top-level window, we should use the loading
   // principal.
   if (!toplevelPrincipal) {
     LOG(
         ("Our loadInfo lacks a top-level principal, use the loadInfo's loading "
          "principal instead"));
     toplevelPrincipal = loadInfo->LoadingPrincipal();
   }
 
-  nsCOMPtr<nsIPrincipal> channelPrincipal;
-  nsIScriptSecurityManager* ssm =
-      nsScriptSecurityManager::GetScriptSecurityManager();
-  nsresult rv = ssm->GetChannelResultPrincipal(
-      aChannel, getter_AddRefs(channelPrincipal));
-
   // If we don't have a loading principal and this is a document channel, we are
   // a top-level window!
   if (!toplevelPrincipal) {
     LOG(
         ("We don't have a loading principal, let's see if this is a document "
          "channel"
          " that belongs to a top-level window"));
     bool isDocument = false;
-    nsresult rv2 = aChannel->GetIsMainDocumentChannel(&isDocument);
-    if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(rv2) && isDocument) {
-      toplevelPrincipal = channelPrincipal;
-      LOG(("Yes, we guessed right!"));
+    rv = aChannel->GetIsMainDocumentChannel(&isDocument);
+    if (NS_SUCCEEDED(rv) && isDocument) {
+      nsIScriptSecurityManager* ssm =
+          nsScriptSecurityManager::GetScriptSecurityManager();
+      rv = ssm->GetChannelResultPrincipal(aChannel,
+                                          getter_AddRefs(toplevelPrincipal));
+      if (NS_SUCCEEDED(rv)) {
+        LOG(("Yes, we guessed right!"));
+      } else {
+        LOG(
+            ("Yes, we guessed right, but minting the channel result principal "
+             "failed"));
+      }
     } else {
       LOG(("No, we guessed wrong!"));
     }
   }
 
   // Let's use the triggering principal then.
   if (!toplevelPrincipal) {
     LOG(
@@ -1065,38 +1108,38 @@ bool AntiTrackingCommon::IsFirstPartySto
       return true;
     }
 
     *aRejectedReason =
         nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION;
     return false;
   }
 
-  if (NS_WARN_IF(NS_FAILED(rv) || !channelPrincipal)) {
+  if (NS_WARN_IF(NS_FAILED(rv) || !channelURI)) {
     LOG(("No channel principal, bail out early"));
     return false;
   }
 
-  access = CheckCookiePermissionForPrincipal(channelPrincipal);
+  access = CheckCookiePermissionForURI(channelURI);
   if (access != nsICookiePermission::ACCESS_DEFAULT) {
     LOG(
         ("CheckCookiePermissionForPrincipal() returned a non-default access "
          "code (%d) for channel's principal, returning %s",
          int(access),
          access != nsICookiePermission::ACCESS_DENY ? "success" : "failure"));
     if (access != nsICookiePermission::ACCESS_DENY) {
       return true;
     }
 
     *aRejectedReason =
         nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION;
     return false;
   }
 
-  int32_t behavior = CookiesBehavior(toplevelPrincipal, channelPrincipal);
+  int32_t behavior = CookiesBehavior(toplevelPrincipal, channelURI);
   if (behavior == nsICookieService::BEHAVIOR_ACCEPT) {
     LOG(("The cookie behavior pref mandates accepting all cookies!"));
     return true;
   }
 
   if (CheckContentBlockingAllowList(aChannel)) {
     return true;
   }
@@ -1226,17 +1269,18 @@ bool AntiTrackingCommon::IsFirstPartySto
     nsIPrincipal* aPrincipal) {
   MOZ_ASSERT(aPrincipal);
 
   nsCookieAccess access = CheckCookiePermissionForPrincipal(aPrincipal);
   if (access != nsICookiePermission::ACCESS_DEFAULT) {
     return access != nsICookiePermission::ACCESS_DENY;
   }
 
-  int32_t behavior = CookiesBehavior(aPrincipal, nullptr);
+  int32_t behavior =
+      CookiesBehavior(aPrincipal, static_cast<nsIPrincipal*>(nullptr));
   return behavior != nsICookieService::BEHAVIOR_REJECT;
 }
 
 /* static */ bool AntiTrackingCommon::MaybeIsFirstPartyStorageAccessGrantedFor(
     nsPIDOMWindowInner* aFirstPartyWindow, nsIURI* aURI) {
   MOZ_ASSERT(aFirstPartyWindow);
   MOZ_ASSERT(aURI);