Bug 1354229 - use private cookie DB in nsICookieManager depending on OriginAttributes draft
authorRob Wu <rob@robwu.nl>
Fri, 09 Jun 2017 18:00:06 +0200
changeset 597726 2fef3a693c580bfe8c65a00114c5d00b538b181f
parent 597455 464b2a3c25aa1065760d9ecbb0870bca4a66c62e
child 597727 381d9ca843b2111ee31b411037f4569b421faf88
push id65002
push userbmo:rob@robwu.nl
push dateTue, 20 Jun 2017 21:02:31 +0000
bugs1354229, 777620, 1309637
milestone56.0a1
Bug 1354229 - use private cookie DB in nsICookieManager depending on OriginAttributes Evaluated every method exposed via nsICookieManager and nsICookieManager2, and ensured that every method uses the right cookie database depending on whether the mPrivateBrowsingId flag is set in OriginAttributes. The usePrivateMode method has been removed since it was introduced in bugzil.la/777620 and not used anywhere else. This should be covered by toolkit/components/extensions/test/mochitest/test_ext_cookies.html, but due to bugzil.la/1309637, the relevant private browsing tests have been disabled. These tests will be enabled in one of the next commits. MozReview-Commit-ID: DMhE42LGu9S
netwerk/cookie/nsCookieService.cpp
netwerk/cookie/nsICookieManager.idl
toolkit/components/extensions/ext-cookies.js
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -2489,16 +2489,19 @@ nsCookieService::AddNative(const nsACStr
     return NS_ERROR_FAILURE;
   }
 
   if (!mDBState) {
     NS_WARNING("No DBState! Profile already closed?");
     return NS_ERROR_NOT_AVAILABLE;
   }
 
+  AutoRestore<DBState*> savePrevDBState(mDBState);
+  mDBState = (aOriginAttributes->mPrivateBrowsingId > 0) ? mPrivateDBState : mDefaultDBState;
+
   // first, normalize the hostname, and fail if it contains illegal characters.
   nsAutoCString host(aHost);
   nsresult rv = NormalizeHost(host);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // get the base domain for the host URI.
   // e.g. for "www.bbc.co.uk", this would be "bbc.co.uk".
   nsAutoCString baseDomain;
@@ -2531,16 +2534,19 @@ nsCookieService::Remove(const nsACString
                         const nsACString& aName, const nsACString& aPath,
                         bool aBlocked)
 {
   if (!mDBState) {
     NS_WARNING("No DBState! Profile already closed?");
     return NS_ERROR_NOT_AVAILABLE;
   }
 
+  AutoRestore<DBState*> savePrevDBState(mDBState);
+  mDBState = (aAttrs.mPrivateBrowsingId > 0) ? mPrivateDBState : mDefaultDBState;
+
   // first, normalize the hostname, and fail if it contains illegal characters.
   nsAutoCString host(aHost);
   nsresult rv = NormalizeHost(host);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString baseDomain;
   rv = GetBaseDomainFromHost(host, baseDomain);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -2616,35 +2622,16 @@ nsCookieService::RemoveNative(const nsAC
   nsresult rv = Remove(aHost, *aOriginAttributes, aName, aPath, aBlocked);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsCookieService::UsePrivateMode(bool aIsPrivate,
-                                nsIPrivateModeCallback* aCallback)
-{
-  if (!aCallback) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  if (!mDBState) {
-    NS_WARNING("No DBState! Profile already closed?");
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  AutoRestore<DBState*> savePrevDBState(mDBState);
-  mDBState = aIsPrivate ? mPrivateDBState : mDefaultDBState;
-
-  return aCallback->Callback();
-}
-
 /******************************************************************************
  * nsCookieService impl:
  * private file I/O functions
  ******************************************************************************/
 
 // Begin an asynchronous read from the database.
 OpenDBResult
 nsCookieService::Read()
@@ -4636,16 +4623,19 @@ nsCookieService::CookieExistsNative(nsIC
   NS_ENSURE_ARG_POINTER(aOriginAttributes);
   NS_ENSURE_ARG_POINTER(aFoundCookie);
 
   if (!mDBState) {
     NS_WARNING("No DBState! Profile already closed?");
     return NS_ERROR_NOT_AVAILABLE;
   }
 
+  AutoRestore<DBState*> savePrevDBState(mDBState);
+  mDBState = (aOriginAttributes->mPrivateBrowsingId > 0) ? mPrivateDBState : mDefaultDBState;
+
   nsAutoCString host, name, path;
   nsresult rv = aCookie->GetHost(host);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = aCookie->GetName(name);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = aCookie->GetPath(path);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -4826,16 +4816,19 @@ nsCookieService::GetCookiesFromHost(cons
   rv = InitializeOriginAttributes(&attrs,
                                   aOriginAttributes,
                                   aCx,
                                   aArgc,
                                   u"nsICookieManager2.getCookiesFromHost()",
                                   u"2");
   NS_ENSURE_SUCCESS(rv, rv);
 
+  AutoRestore<DBState*> savePrevDBState(mDBState);
+  mDBState = (attrs.mPrivateBrowsingId > 0) ? mPrivateDBState : mDefaultDBState;
+
   nsCookieKey key = nsCookieKey(baseDomain, attrs);
   EnsureReadDomain(key);
 
   nsCookieEntry *entry = mDBState->hostTable.GetEntry(key);
   if (!entry)
     return NS_NewEmptyEnumerator(aEnumerator);
 
   nsCOMArray<nsICookie> cookieList(mMaxCookiesPerHost);
@@ -4874,16 +4867,20 @@ nsCookieService::GetCookiesWithOriginAtt
     const nsCString& aBaseDomain,
     nsISimpleEnumerator **aEnumerator)
 {
   if (!mDBState) {
     NS_WARNING("No DBState! Profile already closed?");
     return NS_ERROR_NOT_AVAILABLE;
   }
 
+  AutoRestore<DBState*> savePrevDBState(mDBState);
+  mDBState = (aPattern.mPrivateBrowsingId.WasPassed() &&
+      aPattern.mPrivateBrowsingId.Value() > 0) ? mPrivateDBState : mDefaultDBState;
+
   nsCOMArray<nsICookie> cookies;
   for (auto iter = mDBState->hostTable.Iter(); !iter.Done(); iter.Next()) {
     nsCookieEntry* entry = iter.Get();
 
     if (!aBaseDomain.IsEmpty() && !aBaseDomain.Equals(entry->mBaseDomain)) {
       continue;
     }
 
@@ -4928,16 +4925,20 @@ nsCookieService::RemoveCookiesWithOrigin
     const mozilla::OriginAttributesPattern& aPattern,
     const nsCString& aBaseDomain)
 {
   if (!mDBState) {
     NS_WARNING("No DBState! Profile already close?");
     return NS_ERROR_NOT_AVAILABLE;
   }
 
+  AutoRestore<DBState*> savePrevDBState(mDBState);
+  mDBState = (aPattern.mPrivateBrowsingId.WasPassed() &&
+      aPattern.mPrivateBrowsingId.Value() > 0) ? mPrivateDBState : mDefaultDBState;
+
   mozStorageTransaction transaction(mDBState->dbConn, false);
   // Iterate the hash table of nsCookieEntry.
   for (auto iter = mDBState->hostTable.Iter(); !iter.Done(); iter.Next()) {
     nsCookieEntry* entry = iter.Get();
 
     if (!aBaseDomain.IsEmpty() && !aBaseDomain.Equals(entry->mBaseDomain)) {
       continue;
     }
--- a/netwerk/cookie/nsICookieManager.idl
+++ b/netwerk/cookie/nsICookieManager.idl
@@ -33,22 +33,26 @@ interface nsICookieManager : nsISupports
   /**
    * Called to remove all cookies from the cookie list
    */
   void removeAll();
 
   /**
    * Called to enumerate through each cookie in the cookie list.
    * The objects enumerated over are of type nsICookie
+   * This enumerator should only be used for non-private browsing cookies.
+   * To retrieve an enumerator for private browsing cookies, use
+   * getCookiesWithOriginAttributes from nsICookieManager2.
    */
   readonly attribute nsISimpleEnumerator enumerator;
 
   /**
    * Called to enumerate through each session cookie in the cookie list.
    * The objects enumerated over are of type nsICookie
+   * This enumerator should only be used for non-private browsing cookies.
    */
   readonly attribute nsISimpleEnumerator sessionEnumerator;
 
   /**
    * Called to remove an individual cookie from the cookie list, specified
    * by host, name, and path. If the cookie cannot be found, no exception
    * is thrown. Typically, the arguments to this method will be obtained
    * directly from the desired nsICookie object.
@@ -74,21 +78,9 @@ interface nsICookieManager : nsISupports
               [optional] in jsval aOriginAttributes);
 
   [notxpcom]
   nsresult removeNative(in AUTF8String   aHost,
                         in ACString      aName,
                         in AUTF8String   aPath,
                         in boolean       aBlocked,
                         in OriginAttributesPtr aOriginAttributes);
-
-  /**
-   * Set the cookie manager to work on private or non-private cookies for the
-   * duration of the callback.
-   *
-   * @param aIsPrivate True to work on private cookies, false to work on
-   *                   non-private cookies.
-   * @param aCallback Methods on the cookie manager interface will work on
-   *                  private or non-private cookies for the duration of this
-   *                  callback.
-   */
-  void usePrivateMode(in boolean aIsPrivate, in nsIPrivateModeCallback aCallback);
 };
--- a/toolkit/components/extensions/ext-cookies.js
+++ b/toolkit/components/extensions/ext-cookies.js
@@ -170,34 +170,32 @@ function* query(detailsIn, props, contex
     storeId = PRIVATE_STORE;
   } else if ("storeId" in details) {
     storeId = details.storeId;
   }
 
   // We can use getCookiesFromHost for faster searching.
   let enumerator;
   let uri;
+  let originAttributes = {
+    userContextId,
+    privateBrowsingId: isPrivate ? 1 : 0,
+  };
   if ("url" in details) {
     try {
       uri = NetUtil.newURI(details.url).QueryInterface(Ci.nsIURL);
-      Services.cookies.usePrivateMode(isPrivate, () => {
-        enumerator = Services.cookies.getCookiesFromHost(uri.host, {userContextId});
-      });
+      enumerator = Services.cookies.getCookiesFromHost(uri.host, originAttributes);
     } catch (ex) {
       // This often happens for about: URLs
       return;
     }
   } else if ("domain" in details) {
-    Services.cookies.usePrivateMode(isPrivate, () => {
-      enumerator = Services.cookies.getCookiesFromHost(details.domain, {userContextId});
-    });
+    enumerator = Services.cookies.getCookiesFromHost(details.domain, originAttributes);
   } else {
-    Services.cookies.usePrivateMode(isPrivate, () => {
-      enumerator = Services.cookies.enumerator;
-    });
+    enumerator = Services.cookies.getCookiesWithOriginAttributes(JSON.stringify(originAttributes));
   }
 
   // Based on nsCookieService::GetCookieStringInternal
   function matches(cookie) {
     function domainMatches(host) {
       return cookie.rawHost == host || (cookie.isDomain && host.endsWith(cookie.host));
     }
 
@@ -233,20 +231,16 @@ function* query(detailsIn, props, contex
         return false;
       }
     }
 
     if ("name" in details && details.name != cookie.name) {
       return false;
     }
 
-    if (userContextId != cookie.originAttributes.userContextId) {
-      return false;
-    }
-
     // "Restricts the retrieved cookies to those whose domains match or are subdomains of this one."
     if ("domain" in details && !isSubdomain(cookie.rawHost, details.domain)) {
       return false;
     }
 
     // "Restricts the retrieved cookies to those whose path exactly matches this string.""
     if ("path" in details && details.path != cookie.path) {
       return false;
@@ -335,31 +329,32 @@ this.cookies = class extends ExtensionAP
             return Promise.reject({message: "Unknown storeId"});
           }
 
           let cookieAttrs = {host: details.domain, path: path, isSecure: secure};
           if (!checkSetCookiePermissions(extension, uri, cookieAttrs)) {
             return Promise.reject({message: `Permission denied to set cookie ${JSON.stringify(details)}`});
           }
 
+          let originAttributes = {
+            userContextId,
+            privateBrowsingId: isPrivate ? 1 : 0,
+          };
+
           // The permission check may have modified the domain, so use
           // the new value instead.
-          Services.cookies.usePrivateMode(isPrivate, () => {
-            Services.cookies.add(cookieAttrs.host, path, name, value,
-                                 secure, httpOnly, isSession, expiry, {userContextId});
-          });
+          Services.cookies.add(cookieAttrs.host, path, name, value,
+                               secure, httpOnly, isSession, expiry, originAttributes);
 
           return self.cookies.get(details);
         },
 
         remove: function(details) {
-          for (let {cookie, isPrivate, storeId} of query(details, ["url", "name", "storeId"], context)) {
-            Services.cookies.usePrivateMode(isPrivate, () => {
-              Services.cookies.remove(cookie.host, cookie.name, cookie.path, false, cookie.originAttributes);
-            });
+          for (let {cookie, storeId} of query(details, ["url", "name", "storeId"], context)) {
+            Services.cookies.remove(cookie.host, cookie.name, cookie.path, false, cookie.originAttributes);
 
             // Todo: could there be multiple per subdomain?
             return Promise.resolve({
               url: details.url,
               name: details.name,
               storeId,
             });
           }