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
--- 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,
});
}