Bug 770691 - Add plubming for per-site third party cookie blocking. r=mconnor sr=biesi,jduell,mconnor
authorMonica Chew <mmc@mozilla.com>
Fri, 14 Sep 2012 13:57:56 -0700
changeset 114009 bd3192c8f2711f2bd4e80947e66a525df7b737ef
parent 114008 1c04d97d0f74288f9c160534b13ef077a45388f5
child 114010 e3ceadda1af182a86a8fd86b78117af12d8a3902
push id239
push userakeybl@mozilla.com
push dateThu, 03 Jan 2013 21:54:43 +0000
treeherdermozilla-release@3a7b66445659 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmconnor, biesi, jduell, mconnor
bugs770691
milestone18.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 770691 - Add plubming for per-site third party cookie blocking. r=mconnor sr=biesi,jduell,mconnor
extensions/cookie/nsCookiePermission.cpp
extensions/cookie/nsCookiePermission.h
extensions/cookie/test/unit/test_cookies_thirdparty.js
extensions/cookie/test/unit/test_cookies_thirdparty_session.js
netwerk/cookie/nsCookieService.cpp
netwerk/cookie/nsCookieService.h
netwerk/cookie/nsICookiePermission.idl
--- a/extensions/cookie/nsCookiePermission.cpp
+++ b/extensions/cookie/nsCookiePermission.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 sw=2 et: */
 /* 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 "nsCookiePermission.h"
 
-#include "nsCookiePermission.h"
+#include "mozIThirdPartyUtil.h"
 #include "nsICookie2.h"
 #include "nsIServiceManager.h"
 #include "nsICookiePromptService.h"
 #include "nsICookieManager2.h"
 #include "nsNetUtil.h"
 #include "nsIURI.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
@@ -59,16 +60,18 @@ bool
 nsCookiePermission::Init()
 {
   // Initialize nsIPermissionManager and fetch relevant prefs. This is only
   // required for some methods on nsICookiePermission, so it should be done
   // lazily.
   nsresult rv;
   mPermMgr = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
   if (NS_FAILED(rv)) return false;
+  mThirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
+  if (NS_FAILED(rv)) return false;
 
   // failure to access the pref service is non-fatal...
   nsCOMPtr<nsIPrefBranch> prefBranch =
       do_GetService(NS_PREFSERVICE_CONTRACTID);
   if (prefBranch) {
     prefBranch->AddObserver(kCookiesLifetimePolicy, this, false);
     prefBranch->AddObserver(kCookiesLifetimeDays, this, false);
     prefBranch->AddObserver(kCookiesAlwaysAcceptSession, this, false);
@@ -162,39 +165,25 @@ nsCookiePermission::CanAccess(nsIURI    
 
   // Lazily initialize ourselves
   if (!EnsureInitialized())
     return NS_ERROR_UNEXPECTED;
 
   // finally, check with permission manager...
   rv = mPermMgr->TestPermission(aURI, kPermissionType, (uint32_t *) aResult);
   if (NS_SUCCEEDED(rv)) {
-    switch (*aResult) {
-    // if we have one of the publicly-available values, just return it
-    case nsIPermissionManager::UNKNOWN_ACTION: // ACCESS_DEFAULT
-    case nsIPermissionManager::ALLOW_ACTION:   // ACCESS_ALLOW
-    case nsIPermissionManager::DENY_ACTION:    // ACCESS_DENY
-      break;
-
-    // ACCESS_SESSION means the cookie can be accepted; the session 
-    // downgrade will occur in CanSetCookie().
-    case nsICookiePermission::ACCESS_SESSION:
-      *aResult = ACCESS_ALLOW;
-      break;
-
-    // ack, an unknown type! just use the defaults.
-    default:
-      *aResult = ACCESS_DEFAULT;
+    if (*aResult == nsICookiePermission::ACCESS_SESSION) {
+      *aResult = nsICookiePermission::ACCESS_ALLOW;
     }
   }
 
   return rv;
 }
 
-NS_IMETHODIMP 
+NS_IMETHODIMP
 nsCookiePermission::CanSetCookie(nsIURI     *aURI,
                                  nsIChannel *aChannel,
                                  nsICookie2 *aCookie,
                                  bool       *aIsSession,
                                  int64_t    *aExpiry,
                                  bool       *aResult)
 {
   NS_ASSERTION(aURI, "null uri");
@@ -202,55 +191,63 @@ nsCookiePermission::CanSetCookie(nsIURI 
   *aResult = kDefaultPolicy;
 
   // Lazily initialize ourselves
   if (!EnsureInitialized())
     return NS_ERROR_UNEXPECTED;
 
   uint32_t perm;
   mPermMgr->TestPermission(aURI, kPermissionType, &perm);
+  bool isThirdParty = false;
   switch (perm) {
   case nsICookiePermission::ACCESS_SESSION:
     *aIsSession = true;
 
-  case nsIPermissionManager::ALLOW_ACTION: // ACCESS_ALLOW
+  case nsICookiePermission::ACCESS_ALLOW:
     *aResult = true;
     break;
 
-  case nsIPermissionManager::DENY_ACTION:  // ACCESS_DENY
+  case nsICookiePermission::ACCESS_DENY:
     *aResult = false;
     break;
 
+  case nsICookiePermission::ACCESS_ALLOW_FIRST_PARTY_ONLY:
+    mThirdPartyUtil->IsThirdPartyChannel(aChannel, aURI, &isThirdParty);
+    // If it's third party, we can't set the cookie
+    if (isThirdParty)
+      *aResult = false;
+    break;
+
   default:
     // the permission manager has nothing to say about this cookie -
     // so, we apply the default prefs to it.
     NS_ASSERTION(perm == nsIPermissionManager::UNKNOWN_ACTION, "unknown permission");
-    
+
     // now we need to figure out what type of accept policy we're dealing with
     // if we accept cookies normally, just bail and return
     if (mCookiesLifetimePolicy == ACCEPT_NORMALLY) {
       *aResult = true;
       return NS_OK;
     }
-    
+
     // declare this here since it'll be used in all of the remaining cases
     int64_t currentTime = PR_Now() / PR_USEC_PER_SEC;
     int64_t delta = *aExpiry - currentTime;
-    
+
     // check whether the user wants to be prompted
     if (mCookiesLifetimePolicy == ASK_BEFORE_ACCEPT) {
       // if it's a session cookie and the user wants to accept these 
       // without asking, or if we are in private browsing mode, just
       // accept the cookie and return
       if ((*aIsSession && mCookiesAlwaysAcceptSession) ||
           InPrivateBrowsing()) {
         *aResult = true;
         return NS_OK;
       }
-      
+
       // default to rejecting, in case the prompting process fails
       *aResult = false;
 
       nsAutoCString hostPort;
       aURI->GetHostPort(hostPort);
 
       if (!aCookie) {
          return NS_ERROR_UNEXPECTED;
--- a/extensions/cookie/nsCookiePermission.h
+++ b/extensions/cookie/nsCookiePermission.h
@@ -6,43 +6,45 @@
 #define nsCookiePermission_h__
 
 #include "nsICookiePermission.h"
 #include "nsIPermissionManager.h"
 #include "nsIObserver.h"
 #include "nsCOMPtr.h"
 #include "prlong.h"
 #include "nsIPrivateBrowsingService.h"
+#include "mozIThirdPartyUtil.h"
 
 class nsIPrefBranch;
 
 class nsCookiePermission : public nsICookiePermission
                          , public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSICOOKIEPERMISSION
   NS_DECL_NSIOBSERVER
 
-  nsCookiePermission() 
+  nsCookiePermission()
     : mCookiesLifetimeSec(LL_MAXINT)
     , mCookiesLifetimePolicy(0) // ACCEPT_NORMALLY
     , mCookiesAlwaysAcceptSession(false)
     {}
   virtual ~nsCookiePermission() {}
 
   bool Init();
   void PrefChanged(nsIPrefBranch *, const char *);
 
 private:
-  bool EnsureInitialized() { return mPermMgr != NULL || Init(); };
-  bool InPrivateBrowsing();
+   bool EnsureInitialized() { return (mPermMgr != NULL && mThirdPartyUtil != NULL) || Init(); };
+   bool InPrivateBrowsing();
 
   nsCOMPtr<nsIPermissionManager> mPermMgr;
   nsCOMPtr<nsIPrivateBrowsingService> mPBService;
+  nsCOMPtr<mozIThirdPartyUtil> mThirdPartyUtil;
 
   int64_t      mCookiesLifetimeSec;            // lifetime limit specified in seconds
   uint8_t      mCookiesLifetimePolicy;         // pref for how long cookies are stored
   bool mCookiesAlwaysAcceptSession;    // don't prompt for session cookies
 };
 
 // {EF565D0A-AB9A-4A13-9160-0644CDFD859A}
 #define NS_COOKIEPERMISSION_CID \
--- a/extensions/cookie/test/unit/test_cookies_thirdparty.js
+++ b/extensions/cookie/test/unit/test_cookies_thirdparty.js
@@ -27,27 +27,49 @@ function run_test() {
   do_set_cookies(uri1, channel1, true, [0, 0, 0, 0]);
   Services.cookies.removeAll();
   do_set_cookies(uri1, channel2, true, [0, 0, 0, 0]);
   Services.cookies.removeAll();
 
   // Force the channel URI to be used when determining the originating URI of
   // the channel.
   var httpchannel1 = channel1.QueryInterface(Ci.nsIHttpChannelInternal);
-  var httpchannel2 = channel1.QueryInterface(Ci.nsIHttpChannelInternal);
+  var httpchannel2 = channel2.QueryInterface(Ci.nsIHttpChannelInternal);
   httpchannel1.forceAllowThirdPartyCookie = true;
   httpchannel2.forceAllowThirdPartyCookie = true;
 
   // test with cookies enabled
   Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
   do_set_cookies(uri1, channel1, true, [1, 2, 3, 4]);
   Services.cookies.removeAll();
   do_set_cookies(uri1, channel2, true, [1, 2, 3, 4]);
   Services.cookies.removeAll();
 
   // test with third party cookies blocked
   Services.prefs.setIntPref("network.cookie.cookieBehavior", 1);
   do_set_cookies(uri1, channel1, true, [0, 1, 1, 2]);
   Services.cookies.removeAll();
   do_set_cookies(uri1, channel2, true, [0, 0, 0, 0]);
   Services.cookies.removeAll();
+
+  // Test per-site 3rd party cookies with cookies enabled
+  Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
+  var kPermissionType = "cookie";
+  var ALLOW_FIRST_PARTY_ONLY = 9;
+  // ALLOW_FIRST_PARTY_ONLY overrides
+  Services.permissions.add(uri1, kPermissionType, ALLOW_FIRST_PARTY_ONLY);
+  do_set_cookies(uri1, channel1, true, [0, 1, 1, 2]);
+  Services.cookies.removeAll();
+  do_set_cookies(uri1, channel2, true, [0, 0, 0, 0]);
+  Services.cookies.removeAll();
+
+  // Test per-site 3rd party cookies with 3rd party cookies disabled
+  Services.prefs.setIntPref("network.cookie.cookieBehavior", 1);
+  do_set_cookies(uri1, channel1, true, [0, 1, 1, 2]);
+  Services.cookies.removeAll();
+  // No preference has been set for uri2, but it should act as if
+  // ALLOW_FIRST_PARTY_ONLY has been set
+  do_set_cookies(uri2, channel2, true, [0, 1, 1, 2]);
+  Services.cookies.removeAll();
+  do_set_cookies(uri1, channel2, true, [0, 0, 0, 0]);
+  Services.cookies.removeAll();
 }
 
--- a/extensions/cookie/test/unit/test_cookies_thirdparty_session.js
+++ b/extensions/cookie/test/unit/test_cookies_thirdparty_session.js
@@ -30,17 +30,17 @@ function do_run_test() {
   var uri1 = NetUtil.newURI(spec1);
   var uri2 = NetUtil.newURI(spec2);
   var channel1 = NetUtil.newChannel(uri1);
   var channel2 = NetUtil.newChannel(uri2);
 
   // Force the channel URI to be used when determining the originating URI of
   // the channel.
   var httpchannel1 = channel1.QueryInterface(Ci.nsIHttpChannelInternal);
-  var httpchannel2 = channel1.QueryInterface(Ci.nsIHttpChannelInternal);
+  var httpchannel2 = channel2.QueryInterface(Ci.nsIHttpChannelInternal);
   httpchannel1.forceAllowThirdPartyCookie = true;
   httpchannel2.forceAllowThirdPartyCookie = true;
 
   // test with cookies enabled, and third party cookies persistent.
   Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
   Services.prefs.setBoolPref("network.cookie.thirdparty.sessionOnly", false);
   do_set_cookies(uri1, channel2, false, [1, 2, 3, 4]);
   do_set_cookies(uri2, channel1, true, [1, 2, 3, 4]);
@@ -68,9 +68,8 @@ function do_run_test() {
   do_close_profile(test_generator);
   yield;
   do_load_profile();
   do_check_eq(Services.cookies.countCookiesFromHost(uri1.host), 0);
   do_check_eq(Services.cookies.countCookiesFromHost(uri2.host), 0);
 
   finish_test();
 }
-
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -602,16 +602,19 @@ nsCookieService::Init()
 
   nsresult rv;
   mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  mThirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   // init our pref and observer
   nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
   if (prefBranch) {
     prefBranch->AddObserver(kPrefCookieBehavior,     this, true);
     prefBranch->AddObserver(kPrefMaxNumberOfCookies, this, true);
     prefBranch->AddObserver(kPrefMaxCookiesPerHost,  this, true);
     prefBranch->AddObserver(kPrefCookiePurgeAge,     this, true);
     prefBranch->AddObserver(kPrefThirdPartySession,  this, true);
@@ -1412,18 +1415,17 @@ nsCookieService::GetCookieStringCommon(n
                                        bool aHttpBound,
                                        char** aCookie)
 {
   NS_ENSURE_ARG(aHostURI);
   NS_ENSURE_ARG(aCookie);
 
   // Determine whether the request is foreign. Failure is acceptable.
   bool isForeign = true;
-  if (RequireThirdPartyCheck())
-    mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
+  mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
 
   nsAutoCString result;
   GetCookieStringInternal(aHostURI, isForeign, aHttpBound, result);
   *aCookie = result.IsEmpty() ? nullptr : ToNewCString(result);
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -1454,18 +1456,17 @@ nsCookieService::SetCookieStringCommon(n
                                        nsIChannel *aChannel,
                                        bool aFromHttp) 
 {
   NS_ENSURE_ARG(aHostURI);
   NS_ENSURE_ARG(aCookieHeader);
 
   // Determine whether the request is foreign. Failure is acceptable.
   bool isForeign = true;
-  if (RequireThirdPartyCheck())
-    mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
+  mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
 
   nsDependentCString cookieString(aCookieHeader);
   nsDependentCString serverTime(aServerTime ? aServerTime : "");
   SetCookieStringInternal(aHostURI, isForeign, cookieString,
                           serverTime, aFromHttp);
   return NS_OK;
 }
 
@@ -1589,22 +1590,16 @@ nsCookieService::PrefChanged(nsIPrefBran
   if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefCookiePurgeAge, &val))) {
     mCookiePurgeAge =
       int64_t(LIMIT(val, 0, PR_INT32_MAX, PR_INT32_MAX)) * PR_USEC_PER_SEC;
   }
 
   bool boolval;
   if (NS_SUCCEEDED(aPrefBranch->GetBoolPref(kPrefThirdPartySession, &boolval)))
     mThirdPartySession = boolval;
-
-  // Lazily instantiate the third party service if necessary.
-  if (!mThirdPartyUtil && RequireThirdPartyCheck()) {
-    mThirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID);
-    NS_ABORT_IF_FALSE(mThirdPartyUtil, "require ThirdPartyUtil service");
-  }
 }
 
 /******************************************************************************
  * nsICookieManager impl:
  * nsICookieManager
  ******************************************************************************/
 
 NS_IMETHODIMP
@@ -3050,23 +3045,16 @@ static inline bool IsSubdomainOf(const n
 {
   if (a == b)
     return true;
   if (a.Length() > b.Length())
     return a[a.Length() - b.Length() - 1] == '.' && StringEndsWith(a, b);
   return false;
 }
 
-bool
-nsCookieService::RequireThirdPartyCheck()
-{
-  // 'true' iff we need to perform a third party test.
-  return mCookieBehavior == BEHAVIOR_REJECTFOREIGN || mThirdPartySession;
-}
-
 CookieStatus
 nsCookieService::CheckPrefs(nsIURI          *aHostURI,
                             bool             aIsForeign,
                             const nsCString &aBaseDomain,
                             bool             aRequireHostMatch,
                             const char      *aCookieHeader)
 {
   nsresult rv;
@@ -3085,38 +3073,52 @@ nsCookieService::CheckPrefs(nsIURI      
     // Not passing an nsIChannel here is probably OK; our implementation
     // doesn't do anything with it anyway.
     rv = mPermissionService->CanAccess(aHostURI, nullptr, &access);
 
     // if we found an entry, use it
     if (NS_SUCCEEDED(rv)) {
       switch (access) {
       case nsICookiePermission::ACCESS_DENY:
-        COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "cookies are blocked for this site");
+        COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI,
+                          aCookieHeader, "cookies are blocked for this site");
         return STATUS_REJECTED;
 
       case nsICookiePermission::ACCESS_ALLOW:
         return STATUS_ACCEPTED;
+
+      case nsICookiePermission::ACCESS_ALLOW_FIRST_PARTY_ONLY:
+        if (aIsForeign) {
+          COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI,
+                            aCookieHeader, "third party cookies are blocked "
+                            "for this site");
+          return STATUS_REJECTED;
+
+        }
+        return STATUS_ACCEPTED;
+
       }
     }
   }
 
   // check default prefs
   if (mCookieBehavior == BEHAVIOR_REJECT) {
     COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "cookies are disabled");
     return STATUS_REJECTED;
   }
 
-  if (RequireThirdPartyCheck() && aIsForeign) {
-    // check if cookie is foreign
+  // check if cookie is foreign
+  if (aIsForeign) {
     if (mCookieBehavior == BEHAVIOR_ACCEPT && mThirdPartySession)
       return STATUS_ACCEPT_SESSION;
 
-    COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "context is third party");
-    return STATUS_REJECTED;
+    if (mCookieBehavior == BEHAVIOR_REJECTFOREIGN) {
+      COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "context is third party");
+      return STATUS_REJECTED;
+    }
   }
 
   // if nothing has complained, accept cookie
   return STATUS_ACCEPTED;
 }
 
 // processes domain attribute, and returns true if host has permission to set for this domain.
 bool
--- a/netwerk/cookie/nsCookieService.h
+++ b/netwerk/cookie/nsCookieService.h
@@ -230,17 +230,16 @@ class nsCookieService : public nsICookie
     void                          SetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, nsDependentCString &aCookieHeader, const nsCString &aServerTime, bool aFromHttp);
     bool                          SetCookieInternal(nsIURI *aHostURI, const nsCString& aBaseDomain, bool aRequireHostMatch, CookieStatus aStatus, nsDependentCString &aCookieHeader, int64_t aServerTime, bool aFromHttp);
     void                          AddInternal(const nsCString& aBaseDomain, nsCookie *aCookie, int64_t aCurrentTimeInUsec, nsIURI *aHostURI, const char *aCookieHeader, bool aFromHttp);
     void                          RemoveCookieFromList(const nsListIter &aIter, mozIStorageBindingParamsArray *aParamsArray = NULL);
     void                          AddCookieToList(const nsCString& aBaseDomain, nsCookie *aCookie, DBState *aDBState, mozIStorageBindingParamsArray *aParamsArray, bool aWriteToDB = true);
     void                          UpdateCookieInList(nsCookie *aCookie, int64_t aLastAccessed, mozIStorageBindingParamsArray *aParamsArray);
     static bool                   GetTokenValue(nsASingleFragmentCString::const_char_iterator &aIter, nsASingleFragmentCString::const_char_iterator &aEndIter, nsDependentCSubstring &aTokenString, nsDependentCSubstring &aTokenValue, bool &aEqualsFound);
     static bool                   ParseAttributes(nsDependentCString &aCookieHeader, nsCookieAttributes &aCookie);
-    bool                          RequireThirdPartyCheck();
     CookieStatus                  CheckPrefs(nsIURI *aHostURI, bool aIsForeign, const nsCString &aBaseDomain, bool aRequireHostMatch, const char *aCookieHeader);
     bool                          CheckDomain(nsCookieAttributes &aCookie, nsIURI *aHostURI, const nsCString &aBaseDomain, bool aRequireHostMatch);
     static bool                   CheckPath(nsCookieAttributes &aCookie, nsIURI *aHostURI);
     static bool                   GetExpiry(nsCookieAttributes &aCookie, int64_t aServerTime, int64_t aCurrentTime);
     void                          RemoveAllFromMemory();
     already_AddRefed<nsIArray>    PurgeCookies(int64_t aCurrentTimeInUsec);
     bool                          FindCookie(const nsCString& aBaseDomain, const nsAFlatCString &aHost, const nsAFlatCString &aName, const nsAFlatCString &aPath, nsListIter &aIter);
     static void                   FindStaleCookie(nsCookieEntry *aEntry, int64_t aCurrentTime, nsListIter &aIter);
--- a/netwerk/cookie/nsICookiePermission.idl
+++ b/netwerk/cookie/nsICookiePermission.idl
@@ -19,22 +19,23 @@ interface nsICookiePermission : nsISuppo
   /**
    * nsCookieAccess values
    */
   const nsCookieAccess ACCESS_DEFAULT = 0;
   const nsCookieAccess ACCESS_ALLOW   = 1;
   const nsCookieAccess ACCESS_DENY    = 2;
 
   /**
-   * additional values for nsCookieAccess, which are not directly used by
-   * any methods on this interface, but are nevertheless convenient to define
-   * here. these may be relocated somewhere else if we ever consider freezing
-   * this interface.
+   * additional values for nsCookieAccess which may not match
+   * nsIPermissionManager. Keep 3-7 available to allow nsIPermissionManager to
+   * add values without colliding. ACCESS_SESSION is not directly returned by
+   * any methods on this interface.
    */
   const nsCookieAccess ACCESS_SESSION = 8;
+  const nsCookieAccess ACCESS_ALLOW_FIRST_PARTY_ONLY = 9;
 
   /**
    * setAccess
    *
    * this method is called to block cookie access for the given URI.  this
    * may result in other URIs being blocked as well (e.g., URIs which share
    * the same host name).
    *
@@ -53,17 +54,18 @@ interface nsICookiePermission : nsISuppo
    * access the cookie database, either to set or get cookies.
    *
    * @param aURI
    *        the URI trying to access cookies
    * @param aChannel
    *        the channel corresponding to aURI
    *
    * @return one of the following nsCookieAccess values:
-   *         ACCESS_DEFAULT, ACCESS_ALLOW, or ACCESS_DENY
+   *         ACCESS_DEFAULT, ACCESS_ALLOW, ACCESS_DENY, or
+   *         ACCESS_ALLOW_FIRST_PARTY_ONLY
    */
   nsCookieAccess canAccess(in nsIURI     aURI,
                            in nsIChannel aChannel);
 
   /**
    * canSetCookie
    *
    * this method is called to test whether or not the given URI/channel may