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 107237 bd3192c8f2711f2bd4e80947e66a525df7b737ef
parent 107236 1c04d97d0f74288f9c160534b13ef077a45388f5
child 107238 e3ceadda1af182a86a8fd86b78117af12d8a3902
push id74
push usershu@rfrn.org
push dateTue, 18 Sep 2012 19:23:47 +0000
reviewersmconnor, biesi, jduell, mconnor
bugs770691
milestone18.0a1
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