Bug 756648 - Implement "cookie jars" for apps. r=biesi,smaug

This commit is contained in:
Jason Duell
2012-09-18 12:04:04 -04:00
parent b40842ee59
commit 8b5e83d050
15 changed files with 522 additions and 120 deletions

View File

@@ -18,6 +18,11 @@ SerializedLoadContext::SerializedLoadContext(nsILoadContext* aLoadContext)
SerializedLoadContext::SerializedLoadContext(nsIChannel* aChannel) SerializedLoadContext::SerializedLoadContext(nsIChannel* aChannel)
{ {
if (!aChannel) {
Init(nullptr);
return;
}
nsCOMPtr<nsILoadContext> loadContext; nsCOMPtr<nsILoadContext> loadContext;
NS_QueryNotificationCallbacks(aChannel, loadContext); NS_QueryNotificationCallbacks(aChannel, loadContext);
Init(loadContext); Init(loadContext);
@@ -40,7 +45,9 @@ SerializedLoadContext::SerializedLoadContext(nsIChannel* aChannel)
SerializedLoadContext::SerializedLoadContext(nsIWebSocketChannel* aChannel) SerializedLoadContext::SerializedLoadContext(nsIWebSocketChannel* aChannel)
{ {
nsCOMPtr<nsILoadContext> loadContext; nsCOMPtr<nsILoadContext> loadContext;
if (aChannel) {
NS_QueryNotificationCallbacks(aChannel, loadContext); NS_QueryNotificationCallbacks(aChannel, loadContext);
}
Init(loadContext); Init(loadContext);
} }

View File

@@ -18,6 +18,8 @@
// c) Schema 3: the 'creationTime' column already exists; or the // c) Schema 3: the 'creationTime' column already exists; or the
// 'moz_uniqueid' index already exists. // 'moz_uniqueid' index already exists.
let COOKIE_DATABASE_SCHEMA_CURRENT = 5;
let test_generator = do_run_test(); let test_generator = do_run_test();
function run_test() { function run_test() {
@@ -62,6 +64,10 @@ function do_run_test() {
sub_generator.next(); sub_generator.next();
yield; yield;
this.sub_generator = run_test_3(test_generator, COOKIE_DATABASE_SCHEMA_CURRENT);
sub_generator.next();
yield;
this.sub_generator = run_test_3(test_generator, 4); this.sub_generator = run_test_3(test_generator, 4);
sub_generator.next(); sub_generator.next();
yield; yield;
@@ -206,7 +212,7 @@ function run_test_3(generator, schema)
// Check that the schema version has been reset. // Check that the schema version has been reset.
let db = Services.storage.openDatabase(cookieFile); let db = Services.storage.openDatabase(cookieFile);
do_check_eq(db.schemaVersion, 4); do_check_eq(db.schemaVersion, COOKIE_DATABASE_SCHEMA_CURRENT);
db.close(); db.close();
// Clean up. // Clean up.
@@ -233,7 +239,7 @@ function run_test_4_exists(generator, schema, stmt)
// Check that the schema version has been reset and the backup file exists. // Check that the schema version has been reset and the backup file exists.
db = Services.storage.openDatabase(cookieFile); db = Services.storage.openDatabase(cookieFile);
do_check_eq(db.schemaVersion, 4); do_check_eq(db.schemaVersion, COOKIE_DATABASE_SCHEMA_CURRENT);
db.close(); db.close();
do_check_true(backupFile.exists()); do_check_true(backupFile.exists());
@@ -264,7 +270,7 @@ function run_test_4_baseDomain(generator)
// Check that the schema version has been reset and the backup file exists. // Check that the schema version has been reset and the backup file exists.
db = Services.storage.openDatabase(cookieFile); db = Services.storage.openDatabase(cookieFile);
do_check_eq(db.schemaVersion, 4); do_check_eq(db.schemaVersion, COOKIE_DATABASE_SCHEMA_CURRENT);
db.close(); db.close();
do_check_true(backupFile.exists()); do_check_true(backupFile.exists());

View File

@@ -1339,6 +1339,12 @@ NS_UsePrivateBrowsing(nsIChannel *channel)
return loadContext && loadContext->UsePrivateBrowsing(); return loadContext && loadContext->UsePrivateBrowsing();
} }
// Constants duplicated from nsIScriptSecurityManager so we avoid having necko
// know about script security manager.
#define NECKO_NO_APP_ID 0
// Note: UNKNOWN also equals PR_UINT32_MAX
#define NECKO_UNKNOWN_APP_ID 4294967295
/** /**
* Gets AppId and isInBrowserElement from channel's nsILoadContext. * Gets AppId and isInBrowserElement from channel's nsILoadContext.
* Returns false if error or channel's callbacks don't implement nsILoadContext. * Returns false if error or channel's callbacks don't implement nsILoadContext.

View File

@@ -117,7 +117,8 @@ CookieServiceChild::GetCookieStringInternal(nsIURI *aHostURI,
// Synchronously call the parent. // Synchronously call the parent.
nsAutoCString result; nsAutoCString result;
SendGetCookieString(uriParams, !!isForeign, aFromHttp, &result); SendGetCookieString(uriParams, !!isForeign, aFromHttp,
IPC::SerializedLoadContext(aChannel), &result);
if (!result.IsEmpty()) if (!result.IsEmpty())
*aCookieString = ToNewCString(result); *aCookieString = ToNewCString(result);
@@ -149,7 +150,7 @@ CookieServiceChild::SetCookieStringInternal(nsIURI *aHostURI,
// Synchronously call the parent. // Synchronously call the parent.
SendSetCookieString(uriParams, !!isForeign, cookieString, serverTime, SendSetCookieString(uriParams, !!isForeign, cookieString, serverTime,
aFromHttp); aFromHttp, IPC::SerializedLoadContext(aChannel));
return NS_OK; return NS_OK;
} }

View File

@@ -34,6 +34,8 @@ bool
CookieServiceParent::RecvGetCookieString(const URIParams& aHost, CookieServiceParent::RecvGetCookieString(const URIParams& aHost,
const bool& aIsForeign, const bool& aIsForeign,
const bool& aFromHttp, const bool& aFromHttp,
const IPC::SerializedLoadContext&
aLoadContext,
nsCString* aResult) nsCString* aResult)
{ {
if (!mCookieService) if (!mCookieService)
@@ -45,8 +47,12 @@ CookieServiceParent::RecvGetCookieString(const URIParams& aHost,
if (!hostURI) if (!hostURI)
return false; return false;
mCookieService->GetCookieStringInternal(hostURI, aIsForeign, uint32_t appId;
aFromHttp, *aResult); bool isInBrowserElement;
GetAppInfoFromLoadContext(aLoadContext, appId, isInBrowserElement);
mCookieService->GetCookieStringInternal(hostURI, aIsForeign, aFromHttp, appId,
isInBrowserElement, *aResult);
return true; return true;
} }
@@ -55,7 +61,9 @@ CookieServiceParent::RecvSetCookieString(const URIParams& aHost,
const bool& aIsForeign, const bool& aIsForeign,
const nsCString& aCookieString, const nsCString& aCookieString,
const nsCString& aServerTime, const nsCString& aServerTime,
const bool& aFromHttp) const bool& aFromHttp,
const IPC::SerializedLoadContext&
aLoadContext)
{ {
if (!mCookieService) if (!mCookieService)
return true; return true;
@@ -66,13 +74,34 @@ CookieServiceParent::RecvSetCookieString(const URIParams& aHost,
if (!hostURI) if (!hostURI)
return false; return false;
uint32_t appId;
bool isInBrowserElement;
GetAppInfoFromLoadContext(aLoadContext, appId, isInBrowserElement);
nsDependentCString cookieString(aCookieString, 0); nsDependentCString cookieString(aCookieString, 0);
mCookieService->SetCookieStringInternal(hostURI, aIsForeign, mCookieService->SetCookieStringInternal(hostURI, aIsForeign, cookieString,
cookieString, aServerTime, aServerTime, aFromHttp, appId,
aFromHttp); isInBrowserElement);
return true; return true;
} }
void
CookieServiceParent::GetAppInfoFromLoadContext(
const IPC::SerializedLoadContext &aLoadContext,
uint32_t& aAppId,
bool& aIsInBrowserElement)
{
// TODO: bug 782542: what to do when we get null loadContext? For now assume
// NECKO_NO_APP_ID.
aAppId = NECKO_NO_APP_ID;
aIsInBrowserElement = false;
if (aLoadContext.IsNotNull()) {
aAppId = aLoadContext.mAppId;
aIsInBrowserElement = aLoadContext.mIsInBrowserElement;
}
}
} }
} }

View File

@@ -7,6 +7,7 @@
#define mozilla_net_CookieServiceParent_h #define mozilla_net_CookieServiceParent_h
#include "mozilla/net/PCookieServiceParent.h" #include "mozilla/net/PCookieServiceParent.h"
#include "SerializedLoadContext.h"
class nsCookieService; class nsCookieService;
class nsIIOService; class nsIIOService;
@@ -24,13 +25,20 @@ protected:
virtual bool RecvGetCookieString(const URIParams& aHost, virtual bool RecvGetCookieString(const URIParams& aHost,
const bool& aIsForeign, const bool& aIsForeign,
const bool& aFromHttp, const bool& aFromHttp,
const IPC::SerializedLoadContext&
loadContext,
nsCString* aResult); nsCString* aResult);
virtual bool RecvSetCookieString(const URIParams& aHost, virtual bool RecvSetCookieString(const URIParams& aHost,
const bool& aIsForeign, const bool& aIsForeign,
const nsCString& aCookieString, const nsCString& aCookieString,
const nsCString& aServerTime, const nsCString& aServerTime,
const bool& aFromHttp); const bool& aFromHttp,
const IPC::SerializedLoadContext&
loadContext);
void GetAppInfoFromLoadContext(const IPC::SerializedLoadContext& aLoadContext,
uint32_t& aAppId, bool& aIsInBrowserElement);
nsRefPtr<nsCookieService> mCookieService; nsRefPtr<nsCookieService> mCookieService;
}; };

View File

@@ -8,6 +8,10 @@
include protocol PNecko; include protocol PNecko;
include URIParams; include URIParams;
include "SerializedLoadContext.h";
using IPC::SerializedLoadContext;
namespace mozilla { namespace mozilla {
namespace net { namespace net {
@@ -47,6 +51,9 @@ parent:
* Whether the result is for an HTTP request header. This should be * Whether the result is for an HTTP request header. This should be
* true for nsICookieService.getCookieStringFromHttp calls, false * true for nsICookieService.getCookieStringFromHttp calls, false
* otherwise. * otherwise.
* @param loadContext
* The loadContext from the HTTP channel or document that the cookie is
* being set on.
* *
* @see nsICookieService.getCookieString * @see nsICookieService.getCookieString
* @see nsICookieService.getCookieStringFromHttp * @see nsICookieService.getCookieStringFromHttp
@@ -56,7 +63,8 @@ parent:
*/ */
sync GetCookieString(URIParams host, sync GetCookieString(URIParams host,
bool isForeign, bool isForeign,
bool fromHttp) bool fromHttp,
SerializedLoadContext loadContext)
returns (nsCString result); returns (nsCString result);
/* /*
@@ -80,6 +88,9 @@ parent:
* Whether the result is for an HTTP request header. This should be * Whether the result is for an HTTP request header. This should be
* true for nsICookieService.setCookieStringFromHttp calls, false * true for nsICookieService.setCookieStringFromHttp calls, false
* otherwise. * otherwise.
* @param loadContext
* The loadContext from the HTTP channel or document that the cookie is
* being set on.
* *
* @see nsICookieService.setCookieString * @see nsICookieService.setCookieString
* @see nsICookieService.setCookieStringFromHttp * @see nsICookieService.setCookieStringFromHttp
@@ -89,7 +100,8 @@ parent:
bool isForeign, bool isForeign,
nsCString cookieString, nsCString cookieString,
nsCString serverTime, nsCString serverTime,
bool fromHttp); bool fromHttp,
SerializedLoadContext loadContext);
__delete__(); __delete__();
}; };

View File

@@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public /* 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 * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -52,6 +53,13 @@
using namespace mozilla; using namespace mozilla;
using namespace mozilla::net; using namespace mozilla::net;
// Create key from baseDomain that will access the default cookie namespace.
// TODO: When we figure out what the API will look like for nsICookieManager{2}
// on content processes (see bug 777620), change to use the appropriate app
// namespace. For now those IDLs aren't supported on child processes.
#define DEFAULT_APP_KEY(baseDomain) \
nsCookieKey(baseDomain, NECKO_NO_APP_ID, false)
/****************************************************************************** /******************************************************************************
* nsCookieService impl: * nsCookieService impl:
* useful types & constants * useful types & constants
@@ -64,7 +72,7 @@ static nsCookieService *gCookieService;
static const char kHttpOnlyPrefix[] = "#HttpOnly_"; static const char kHttpOnlyPrefix[] = "#HttpOnly_";
#define COOKIES_FILE "cookies.sqlite" #define COOKIES_FILE "cookies.sqlite"
#define COOKIES_SCHEMA_VERSION 4 #define COOKIES_SCHEMA_VERSION 5
static const int64_t kCookieStaleThreshold = 60 * PR_USEC_PER_SEC; // 1 minute in microseconds static const int64_t kCookieStaleThreshold = 60 * PR_USEC_PER_SEC; // 1 minute in microseconds
static const int64_t kCookiePurgeAge = static const int64_t kCookiePurgeAge =
@@ -99,7 +107,7 @@ static const char kPrefThirdPartySession[] = "network.cookie.thirdparty.session
static void static void
bindCookieParameters(mozIStorageBindingParamsArray *aParamsArray, bindCookieParameters(mozIStorageBindingParamsArray *aParamsArray,
const nsCString &aBaseDomain, const nsCookieKey &aKey,
const nsCookie *aCookie); const nsCookie *aCookie);
// struct for temporarily storing cookie attributes during header parsing // struct for temporarily storing cookie attributes during header parsing
@@ -467,7 +475,9 @@ public:
break; break;
CookieDomainTuple *tuple = mDBState->hostArray.AppendElement(); CookieDomainTuple *tuple = mDBState->hostArray.AppendElement();
row->GetUTF8String(9, tuple->baseDomain); row->GetUTF8String(9, tuple->key.mBaseDomain);
tuple->key.mAppId = static_cast<uint32_t>(row->AsInt32(10));
tuple->key.mInBrowserElement = static_cast<bool>(row->AsInt32(11));
tuple->cookie = gCookieService->GetCookieFromRow(row); tuple->cookie = gCookieService->GetCookieFromRow(row);
} }
@@ -930,6 +940,52 @@ nsCookieService::TryInitDB(bool aRecreateDB)
} }
// Fall through to the next upgrade. // Fall through to the next upgrade.
case 4:
{
// We need to add appId/inBrowserElement, plus change a constraint on
// the table (unique entries now include appId/inBrowserElement):
// this requires creating a new table and copying the data to it. We
// then rename the new table to the old name.
//
// Why we made this change: appId/inBrowserElement allow "cookie jars"
// for Firefox OS. We create a separate cookie namespace per {appId,
// inBrowserElement}. When upgrading, we convert existing cookies
// (which imply we're on desktop/mobile) to use {0, false}, as that is
// the only namespace used by a non-Firefox-OS implementation.
// Rename existing table
rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"ALTER TABLE moz_cookies RENAME TO moz_cookies_old"));
NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
// Drop existing index (CreateTable will create new one for new table)
rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"DROP INDEX moz_basedomain"));
NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
// Create new table (with new fields and new unique constraint)
rv = CreateTable();
NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
// Copy data from old table, using appId/inBrowser=0 for existing rows
rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"INSERT INTO moz_cookies "
"(baseDomain, appId, inBrowserElement, name, value, host, path, expiry,"
" lastAccessed, creationTime, isSecure, isHttpOnly) "
"SELECT baseDomain, 0, 0, name, value, host, path, expiry,"
" lastAccessed, creationTime, isSecure, isHttpOnly "
"FROM moz_cookies_old"));
NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
// Drop old table
rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"DROP TABLE moz_cookies_old"));
NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
COOKIE_LOGSTRING(PR_LOG_DEBUG,
("Upgraded database to schema version 5"));
}
// No more upgrades. Update the schema version. // No more upgrades. Update the schema version.
rv = mDefaultDBState->dbConn->SetSchemaVersion(COOKIES_SCHEMA_VERSION); rv = mDefaultDBState->dbConn->SetSchemaVersion(COOKIES_SCHEMA_VERSION);
NS_ENSURE_SUCCESS(rv, RESULT_RETRY); NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
@@ -965,6 +1021,8 @@ nsCookieService::TryInitDB(bool aRecreateDB)
"SELECT " "SELECT "
"id, " "id, "
"baseDomain, " "baseDomain, "
"appId, "
"inBrowserElement, "
"name, " "name, "
"value, " "value, "
"host, " "host, "
@@ -1005,6 +1063,8 @@ nsCookieService::TryInitDB(bool aRecreateDB)
rv = mDefaultDBState->dbConn->CreateAsyncStatement(NS_LITERAL_CSTRING( rv = mDefaultDBState->dbConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
"INSERT INTO moz_cookies (" "INSERT INTO moz_cookies ("
"baseDomain, " "baseDomain, "
"appId, "
"inBrowserElement, "
"name, " "name, "
"value, " "value, "
"host, " "host, "
@@ -1016,6 +1076,8 @@ nsCookieService::TryInitDB(bool aRecreateDB)
"isHttpOnly" "isHttpOnly"
") VALUES (" ") VALUES ("
":baseDomain, " ":baseDomain, "
":appId, "
":inBrowserElement, "
":name, " ":name, "
":value, " ":value, "
":host, " ":host, "
@@ -1076,11 +1138,15 @@ nsCookieService::CreateTable()
COOKIES_SCHEMA_VERSION); COOKIES_SCHEMA_VERSION);
if (NS_FAILED(rv)) return rv; if (NS_FAILED(rv)) return rv;
// Create the table. // Create the table. We default appId/inBrowserElement to 0: this is so if
// users revert to an older Firefox version that doesn't know about these
// fields, any cookies set will still work once they upgrade back.
rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"CREATE TABLE moz_cookies (" "CREATE TABLE moz_cookies ("
"id INTEGER PRIMARY KEY, " "id INTEGER PRIMARY KEY, "
"baseDomain TEXT, " "baseDomain TEXT, "
"appId INTEGER DEFAULT 0, "
"inBrowserElement INTEGER DEFAULT 0, "
"name TEXT, " "name TEXT, "
"value TEXT, " "value TEXT, "
"host TEXT, " "host TEXT, "
@@ -1090,13 +1156,15 @@ nsCookieService::CreateTable()
"creationTime INTEGER, " "creationTime INTEGER, "
"isSecure INTEGER, " "isSecure INTEGER, "
"isHttpOnly INTEGER, " "isHttpOnly INTEGER, "
"CONSTRAINT moz_uniqueid UNIQUE (name, host, path)" "CONSTRAINT moz_uniqueid UNIQUE (name, host, path, appId, inBrowserElement)"
")")); ")"));
if (NS_FAILED(rv)) return rv; if (NS_FAILED(rv)) return rv;
// Create an index on baseDomain. // Create an index on baseDomain.
return mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( return mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"CREATE INDEX moz_basedomain ON moz_cookies (baseDomain)")); "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain, "
"appId, "
"inBrowserElement)"));
} }
void void
@@ -1254,7 +1322,7 @@ RebuildDBCallback(nsCookieEntry *aEntry,
nsCookie* cookie = cookies[i]; nsCookie* cookie = cookies[i];
if (!cookie->IsSession()) { if (!cookie->IsSession()) {
bindCookieParameters(paramsArray, aEntry->GetKey(), cookie); bindCookieParameters(paramsArray, aEntry, cookie);
} }
} }
@@ -1422,8 +1490,16 @@ nsCookieService::GetCookieStringCommon(nsIURI *aHostURI,
bool isForeign = true; bool isForeign = true;
mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign); mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
// Get app info, if channel is present. Else assume default namespace.
uint32_t appId = NECKO_NO_APP_ID;
bool inBrowserElement = false;
if (aChannel) {
NS_GetAppInfo(aChannel, &appId, &inBrowserElement);
}
nsAutoCString result; nsAutoCString result;
GetCookieStringInternal(aHostURI, isForeign, aHttpBound, result); GetCookieStringInternal(aHostURI, isForeign, aHttpBound, appId,
inBrowserElement, result);
*aCookie = result.IsEmpty() ? nullptr : ToNewCString(result); *aCookie = result.IsEmpty() ? nullptr : ToNewCString(result);
return NS_OK; return NS_OK;
} }
@@ -1463,10 +1539,17 @@ nsCookieService::SetCookieStringCommon(nsIURI *aHostURI,
bool isForeign = true; bool isForeign = true;
mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign); mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
// Get app info, if channel is present. Else assume default namespace.
uint32_t appId = NECKO_NO_APP_ID;
bool inBrowserElement = false;
if (aChannel) {
NS_GetAppInfo(aChannel, &appId, &inBrowserElement);
}
nsDependentCString cookieString(aCookieHeader); nsDependentCString cookieString(aCookieHeader);
nsDependentCString serverTime(aServerTime ? aServerTime : ""); nsDependentCString serverTime(aServerTime ? aServerTime : "");
SetCookieStringInternal(aHostURI, isForeign, cookieString, SetCookieStringInternal(aHostURI, isForeign, cookieString,
serverTime, aFromHttp); serverTime, aFromHttp, appId, inBrowserElement);
return NS_OK; return NS_OK;
} }
@@ -1475,7 +1558,9 @@ nsCookieService::SetCookieStringInternal(nsIURI *aHostURI,
bool aIsForeign, bool aIsForeign,
nsDependentCString &aCookieHeader, nsDependentCString &aCookieHeader,
const nsCString &aServerTime, const nsCString &aServerTime,
bool aFromHttp) bool aFromHttp,
uint32_t aAppId,
bool aInBrowserElement)
{ {
NS_ASSERTION(aHostURI, "null host!"); NS_ASSERTION(aHostURI, "null host!");
@@ -1498,9 +1583,11 @@ nsCookieService::SetCookieStringInternal(nsIURI *aHostURI,
return; return;
} }
nsCookieKey key(baseDomain, aAppId, aInBrowserElement);
// check default prefs // check default prefs
CookieStatus cookieStatus = CheckPrefs(aHostURI, aIsForeign, baseDomain, CookieStatus cookieStatus = CheckPrefs(aHostURI, aIsForeign, requireHostMatch,
requireHostMatch, aCookieHeader.get()); aCookieHeader.get());
// fire a notification if cookie was rejected (but not if there was an error) // fire a notification if cookie was rejected (but not if there was an error)
switch (cookieStatus) { switch (cookieStatus) {
case STATUS_REJECTED: case STATUS_REJECTED:
@@ -1528,7 +1615,7 @@ nsCookieService::SetCookieStringInternal(nsIURI *aHostURI,
} }
// process each cookie in the header // process each cookie in the header
while (SetCookieInternal(aHostURI, baseDomain, requireHostMatch, cookieStatus, while (SetCookieInternal(aHostURI, key, requireHostMatch, cookieStatus,
aCookieHeader, serverTime, aFromHttp)) { aCookieHeader, serverTime, aFromHttp)) {
// document.cookie can only set one cookie at a time // document.cookie can only set one cookie at a time
if (!aFromHttp) if (!aFromHttp)
@@ -1712,7 +1799,7 @@ nsCookieService::Add(const nsACString &aHost,
return NS_ERROR_OUT_OF_MEMORY; return NS_ERROR_OUT_OF_MEMORY;
} }
AddInternal(baseDomain, cookie, currentTimeInUsec, nullptr, nullptr, true); AddInternal(DEFAULT_APP_KEY(baseDomain), cookie, currentTimeInUsec, nullptr, nullptr, true);
return NS_OK; return NS_OK;
} }
@@ -1738,7 +1825,7 @@ nsCookieService::Remove(const nsACString &aHost,
nsListIter matchIter; nsListIter matchIter;
nsRefPtr<nsCookie> cookie; nsRefPtr<nsCookie> cookie;
if (FindCookie(baseDomain, if (FindCookie(DEFAULT_APP_KEY(baseDomain),
host, host,
PromiseFlatCString(aName), PromiseFlatCString(aName),
PromiseFlatCString(aPath), PromiseFlatCString(aPath),
@@ -1793,7 +1880,9 @@ nsCookieService::Read()
"creationTime, " "creationTime, "
"isSecure, " "isSecure, "
"isHttpOnly, " "isHttpOnly, "
"baseDomain " "baseDomain, "
"appId, "
"inBrowserElement "
"FROM moz_cookies " "FROM moz_cookies "
"WHERE baseDomain NOTNULL"), getter_AddRefs(stmtRead)); "WHERE baseDomain NOTNULL"), getter_AddRefs(stmtRead));
NS_ENSURE_SUCCESS(rv, RESULT_RETRY); NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
@@ -1885,11 +1974,10 @@ nsCookieService::AsyncReadComplete()
// Tiebreak: if the given base domain has already been read in, ignore // Tiebreak: if the given base domain has already been read in, ignore
// the background data. Note that readSet may contain domains that were // the background data. Note that readSet may contain domains that were
// queried but found not to be in the db -- that's harmless. // queried but found not to be in the db -- that's harmless.
if (mDefaultDBState->readSet.GetEntry(tuple.baseDomain)) if (mDefaultDBState->readSet.GetEntry(tuple.key))
continue; continue;
AddCookieToList(tuple.baseDomain, tuple.cookie, mDefaultDBState, NULL, AddCookieToList(tuple.key, tuple.cookie, mDefaultDBState, NULL, false);
false);
} }
mDefaultDBState->stmtReadDomain = nullptr; mDefaultDBState->stmtReadDomain = nullptr;
@@ -1933,7 +2021,7 @@ nsCookieService::CancelAsyncRead(bool aPurgeReadSet)
} }
void void
nsCookieService::EnsureReadDomain(const nsCString &aBaseDomain) nsCookieService::EnsureReadDomain(const nsCookieKey &aKey)
{ {
NS_ASSERTION(!mDBState->dbConn || mDBState == mDefaultDBState, NS_ASSERTION(!mDBState->dbConn || mDBState == mDefaultDBState,
"not in default db state"); "not in default db state");
@@ -1943,7 +2031,7 @@ nsCookieService::EnsureReadDomain(const nsCString &aBaseDomain)
return; return;
// Fast path 2: already read in this particular domain. // Fast path 2: already read in this particular domain.
if (NS_LIKELY(mDefaultDBState->readSet.GetEntry(aBaseDomain))) if (NS_LIKELY(mDefaultDBState->readSet.GetEntry(aKey)))
return; return;
// Read in the data synchronously. // Read in the data synchronously.
@@ -1962,7 +2050,9 @@ nsCookieService::EnsureReadDomain(const nsCString &aBaseDomain)
"isSecure, " "isSecure, "
"isHttpOnly " "isHttpOnly "
"FROM moz_cookies " "FROM moz_cookies "
"WHERE baseDomain = :baseDomain"), "WHERE baseDomain = :baseDomain "
" AND appId = :appId "
" AND inBrowserElement = :inBrowserElement"),
getter_AddRefs(mDefaultDBState->stmtReadDomain)); getter_AddRefs(mDefaultDBState->stmtReadDomain));
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
@@ -1980,8 +2070,15 @@ nsCookieService::EnsureReadDomain(const nsCString &aBaseDomain)
mozStorageStatementScoper scoper(mDefaultDBState->stmtReadDomain); mozStorageStatementScoper scoper(mDefaultDBState->stmtReadDomain);
rv = mDefaultDBState->stmtReadDomain->BindUTF8StringByName( rv = mDefaultDBState->stmtReadDomain->BindUTF8StringByName(
NS_LITERAL_CSTRING("baseDomain"), aBaseDomain); NS_LITERAL_CSTRING("baseDomain"), aKey.mBaseDomain);
NS_ASSERT_SUCCESS(rv); NS_ASSERT_SUCCESS(rv);
rv = mDefaultDBState->stmtReadDomain->BindInt32ByName(
NS_LITERAL_CSTRING("appId"), aKey.mAppId);
NS_ASSERT_SUCCESS(rv);
rv = mDefaultDBState->stmtReadDomain->BindInt32ByName(
NS_LITERAL_CSTRING("inBrowserElement"), aKey.mInBrowserElement ? 1 : 0);
NS_ASSERT_SUCCESS(rv);
bool hasResult; bool hasResult;
nsCString name, value, host, path; nsCString name, value, host, path;
@@ -2006,15 +2103,16 @@ nsCookieService::EnsureReadDomain(const nsCString &aBaseDomain)
// Add the cookies to the table in a single operation. This makes sure that // Add the cookies to the table in a single operation. This makes sure that
// either all the cookies get added, or in the case of corruption, none. // either all the cookies get added, or in the case of corruption, none.
for (uint32_t i = 0; i < array.Length(); ++i) { for (uint32_t i = 0; i < array.Length(); ++i) {
AddCookieToList(aBaseDomain, array[i], mDefaultDBState, NULL, false); AddCookieToList(aKey, array[i], mDefaultDBState, NULL, false);
} }
// Add it to the hashset of read entries, so we don't read it again. // Add it to the hashset of read entries, so we don't read it again.
mDefaultDBState->readSet.PutEntry(aBaseDomain); mDefaultDBState->readSet.PutEntry(aKey);
COOKIE_LOGSTRING(PR_LOG_DEBUG, COOKIE_LOGSTRING(PR_LOG_DEBUG,
("EnsureReadDomain(): %ld cookies read for base domain %s", ("EnsureReadDomain(): %ld cookies read for base domain %s, "
array.Length(), aBaseDomain.get())); " appId=%u, inBrowser=%d", array.Length(), aKey.mBaseDomain.get(),
(unsigned)aKey.mAppId, (int)aKey.mInBrowserElement));
} }
void void
@@ -2043,7 +2141,9 @@ nsCookieService::EnsureReadComplete()
"creationTime, " "creationTime, "
"isSecure, " "isSecure, "
"isHttpOnly, " "isHttpOnly, "
"baseDomain " "baseDomain, "
"appId, "
"inBrowserElement "
"FROM moz_cookies " "FROM moz_cookies "
"WHERE baseDomain NOTNULL"), getter_AddRefs(stmt)); "WHERE baseDomain NOTNULL"), getter_AddRefs(stmt));
@@ -2057,7 +2157,8 @@ nsCookieService::EnsureReadComplete()
} }
nsCString baseDomain, name, value, host, path; nsCString baseDomain, name, value, host, path;
bool hasResult; uint32_t appId;
bool inBrowserElement, hasResult;
nsAutoTArray<CookieDomainTuple, kMaxNumberOfCookies> array; nsAutoTArray<CookieDomainTuple, kMaxNumberOfCookies> array;
while (1) { while (1) {
rv = stmt->ExecuteStep(&hasResult); rv = stmt->ExecuteStep(&hasResult);
@@ -2075,11 +2176,14 @@ nsCookieService::EnsureReadComplete()
// Make sure we haven't already read the data. // Make sure we haven't already read the data.
stmt->GetUTF8String(9, baseDomain); stmt->GetUTF8String(9, baseDomain);
if (mDefaultDBState->readSet.GetEntry(baseDomain)) appId = static_cast<uint32_t>(stmt->AsInt32(10));
inBrowserElement = static_cast<bool>(stmt->AsInt32(11));
nsCookieKey key(baseDomain, appId, inBrowserElement);
if (mDefaultDBState->readSet.GetEntry(key))
continue; continue;
CookieDomainTuple* tuple = array.AppendElement(); CookieDomainTuple* tuple = array.AppendElement();
tuple->baseDomain = baseDomain; tuple->key = key;
tuple->cookie = GetCookieFromRow(stmt); tuple->cookie = GetCookieFromRow(stmt);
} }
@@ -2087,7 +2191,7 @@ nsCookieService::EnsureReadComplete()
// either all the cookies get added, or in the case of corruption, none. // either all the cookies get added, or in the case of corruption, none.
for (uint32_t i = 0; i < array.Length(); ++i) { for (uint32_t i = 0; i < array.Length(); ++i) {
CookieDomainTuple& tuple = array[i]; CookieDomainTuple& tuple = array[i];
AddCookieToList(tuple.baseDomain, tuple.cookie, mDefaultDBState, NULL, AddCookieToList(tuple.key, tuple.cookie, mDefaultDBState, NULL,
false); false);
} }
@@ -2218,6 +2322,9 @@ nsCookieService::ImportCookies(nsIFile *aCookieFile)
if (NS_FAILED(rv)) if (NS_FAILED(rv))
continue; continue;
// pre-existing cookies have appId=0, inBrowser=false
nsCookieKey key = DEFAULT_APP_KEY(baseDomain);
// Create a new nsCookie and assign the data. We don't know the cookie // Create a new nsCookie and assign the data. We don't know the cookie
// creation time, so just use the current time to generate a unique one. // creation time, so just use the current time to generate a unique one.
nsRefPtr<nsCookie> newCookie = nsRefPtr<nsCookie> newCookie =
@@ -2240,10 +2347,11 @@ nsCookieService::ImportCookies(nsIFile *aCookieFile)
lastAccessedCounter--; lastAccessedCounter--;
if (originalCookieCount == 0) { if (originalCookieCount == 0) {
AddCookieToList(baseDomain, newCookie, mDefaultDBState, paramsArray); AddCookieToList(key, newCookie, mDefaultDBState, paramsArray);
} }
else { else {
AddInternal(baseDomain, newCookie, currentTimeInUsec, NULL, NULL, true); AddInternal(key, newCookie, currentTimeInUsec,
NULL, NULL, true);
} }
} }
@@ -2305,6 +2413,8 @@ void
nsCookieService::GetCookieStringInternal(nsIURI *aHostURI, nsCookieService::GetCookieStringInternal(nsIURI *aHostURI,
bool aIsForeign, bool aIsForeign,
bool aHttpBound, bool aHttpBound,
uint32_t aAppId,
bool aInBrowserElement,
nsCString &aCookieString) nsCString &aCookieString)
{ {
NS_ASSERTION(aHostURI, "null host!"); NS_ASSERTION(aHostURI, "null host!");
@@ -2332,8 +2442,8 @@ nsCookieService::GetCookieStringInternal(nsIURI *aHostURI,
} }
// check default prefs // check default prefs
CookieStatus cookieStatus = CheckPrefs(aHostURI, aIsForeign, baseDomain, CookieStatus cookieStatus = CheckPrefs(aHostURI, aIsForeign, requireHostMatch,
requireHostMatch, nullptr); nullptr);
// for GetCookie(), we don't fire rejection notifications. // for GetCookie(), we don't fire rejection notifications.
switch (cookieStatus) { switch (cookieStatus) {
case STATUS_REJECTED: case STATUS_REJECTED:
@@ -2357,10 +2467,11 @@ nsCookieService::GetCookieStringInternal(nsIURI *aHostURI,
int64_t currentTime = currentTimeInUsec / PR_USEC_PER_SEC; int64_t currentTime = currentTimeInUsec / PR_USEC_PER_SEC;
bool stale = false; bool stale = false;
EnsureReadDomain(baseDomain); nsCookieKey key(baseDomain, aAppId, aInBrowserElement);
EnsureReadDomain(key);
// perform the hash lookup // perform the hash lookup
nsCookieEntry *entry = mDBState->hostTable.GetEntry(baseDomain); nsCookieEntry *entry = mDBState->hostTable.GetEntry(key);
if (!entry) if (!entry)
return; return;
@@ -2489,7 +2600,7 @@ nsCookieService::GetCookieStringInternal(nsIURI *aHostURI,
// to be processed // to be processed
bool bool
nsCookieService::SetCookieInternal(nsIURI *aHostURI, nsCookieService::SetCookieInternal(nsIURI *aHostURI,
const nsCString &aBaseDomain, const nsCookieKey &aKey,
bool aRequireHostMatch, bool aRequireHostMatch,
CookieStatus aStatus, CookieStatus aStatus,
nsDependentCString &aCookieHeader, nsDependentCString &aCookieHeader,
@@ -2536,7 +2647,7 @@ nsCookieService::SetCookieInternal(nsIURI *aHostURI,
} }
// domain & path checks // domain & path checks
if (!CheckDomain(cookieAttributes, aHostURI, aBaseDomain, aRequireHostMatch)) { if (!CheckDomain(cookieAttributes, aHostURI, aKey.mBaseDomain, aRequireHostMatch)) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader, "failed the domain tests"); COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader, "failed the domain tests");
return newCookie; return newCookie;
} }
@@ -2586,7 +2697,7 @@ nsCookieService::SetCookieInternal(nsIURI *aHostURI,
// add the cookie to the list. AddInternal() takes care of logging. // add the cookie to the list. AddInternal() takes care of logging.
// we get the current time again here, since it may have changed during prompting // we get the current time again here, since it may have changed during prompting
AddInternal(aBaseDomain, cookie, PR_Now(), aHostURI, savedCookieHeader.get(), AddInternal(aKey, cookie, PR_Now(), aHostURI, savedCookieHeader.get(),
aFromHttp); aFromHttp);
return newCookie; return newCookie;
} }
@@ -2597,7 +2708,7 @@ nsCookieService::SetCookieInternal(nsIURI *aHostURI,
// and deletes a cookie (if maximum number of cookies has been // and deletes a cookie (if maximum number of cookies has been
// reached). also performs list maintenance by removing expired cookies. // reached). also performs list maintenance by removing expired cookies.
void void
nsCookieService::AddInternal(const nsCString &aBaseDomain, nsCookieService::AddInternal(const nsCookieKey &aKey,
nsCookie *aCookie, nsCookie *aCookie,
int64_t aCurrentTimeInUsec, int64_t aCurrentTimeInUsec,
nsIURI *aHostURI, nsIURI *aHostURI,
@@ -2614,7 +2725,7 @@ nsCookieService::AddInternal(const nsCString &aBaseDomain,
} }
nsListIter matchIter; nsListIter matchIter;
bool foundCookie = FindCookie(aBaseDomain, aCookie->Host(), bool foundCookie = FindCookie(aKey, aCookie->Host(),
aCookie->Name(), aCookie->Path(), matchIter); aCookie->Name(), aCookie->Path(), matchIter);
nsRefPtr<nsCookie> oldCookie; nsRefPtr<nsCookie> oldCookie;
@@ -2679,7 +2790,7 @@ nsCookieService::AddInternal(const nsCString &aBaseDomain,
} }
// check if we have to delete an old cookie. // check if we have to delete an old cookie.
nsCookieEntry *entry = mDBState->hostTable.GetEntry(aBaseDomain); nsCookieEntry *entry = mDBState->hostTable.GetEntry(aKey);
if (entry && entry->GetCookies().Length() >= mMaxCookiesPerHost) { if (entry && entry->GetCookies().Length() >= mMaxCookiesPerHost) {
nsListIter iter; nsListIter iter;
FindStaleCookie(entry, currentTime, iter); FindStaleCookie(entry, currentTime, iter);
@@ -2707,7 +2818,7 @@ nsCookieService::AddInternal(const nsCString &aBaseDomain,
// Add the cookie to the db. We do not supply a params array for batching // Add the cookie to the db. We do not supply a params array for batching
// because this might result in removals and additions being out of order. // because this might result in removals and additions being out of order.
AddCookieToList(aBaseDomain, aCookie, mDBState, NULL); AddCookieToList(aKey, aCookie, mDBState, NULL);
COOKIE_LOGSUCCESS(SET_COOKIE, aHostURI, aCookieHeader, aCookie, foundCookie); COOKIE_LOGSUCCESS(SET_COOKIE, aHostURI, aCookieHeader, aCookie, foundCookie);
// Now that list mutations are complete, notify observers. We do it here // Now that list mutations are complete, notify observers. We do it here
@@ -3053,7 +3164,6 @@ static inline bool IsSubdomainOf(const nsCString &a, const nsCString &b)
CookieStatus CookieStatus
nsCookieService::CheckPrefs(nsIURI *aHostURI, nsCookieService::CheckPrefs(nsIURI *aHostURI,
bool aIsForeign, bool aIsForeign,
const nsCString &aBaseDomain,
bool aRequireHostMatch, bool aRequireHostMatch,
const char *aCookieHeader) const char *aCookieHeader)
{ {
@@ -3511,7 +3621,7 @@ nsCookieService::CookieExists(nsICookie2 *aCookie,
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
nsListIter iter; nsListIter iter;
*aFoundCookie = FindCookie(baseDomain, host, name, path, iter); *aFoundCookie = FindCookie(DEFAULT_APP_KEY(baseDomain), host, name, path, iter);
return NS_OK; return NS_OK;
} }
@@ -3565,10 +3675,11 @@ nsCookieService::CountCookiesFromHost(const nsACString &aHost,
rv = GetBaseDomainFromHost(host, baseDomain); rv = GetBaseDomainFromHost(host, baseDomain);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
EnsureReadDomain(baseDomain); nsCookieKey key = DEFAULT_APP_KEY(baseDomain);
EnsureReadDomain(key);
// Return a count of all cookies, including expired. // Return a count of all cookies, including expired.
nsCookieEntry *entry = mDBState->hostTable.GetEntry(baseDomain); nsCookieEntry *entry = mDBState->hostTable.GetEntry(key);
*aCountFromHost = entry ? entry->GetCookies().Length() : 0; *aCountFromHost = entry ? entry->GetCookies().Length() : 0;
return NS_OK; return NS_OK;
} }
@@ -3593,9 +3704,10 @@ nsCookieService::GetCookiesFromHost(const nsACString &aHost,
rv = GetBaseDomainFromHost(host, baseDomain); rv = GetBaseDomainFromHost(host, baseDomain);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
EnsureReadDomain(baseDomain); nsCookieKey key = DEFAULT_APP_KEY(baseDomain);
EnsureReadDomain(key);
nsCookieEntry *entry = mDBState->hostTable.GetEntry(baseDomain); nsCookieEntry *entry = mDBState->hostTable.GetEntry(key);
if (!entry) if (!entry)
return NS_NewEmptyEnumerator(aEnumerator); return NS_NewEmptyEnumerator(aEnumerator);
@@ -3610,15 +3722,15 @@ nsCookieService::GetCookiesFromHost(const nsACString &aHost,
// find an exact cookie specified by host, name, and path that hasn't expired. // find an exact cookie specified by host, name, and path that hasn't expired.
bool bool
nsCookieService::FindCookie(const nsCString &aBaseDomain, nsCookieService::FindCookie(const nsCookieKey &aKey,
const nsAFlatCString &aHost, const nsAFlatCString &aHost,
const nsAFlatCString &aName, const nsAFlatCString &aName,
const nsAFlatCString &aPath, const nsAFlatCString &aPath,
nsListIter &aIter) nsListIter &aIter)
{ {
EnsureReadDomain(aBaseDomain); EnsureReadDomain(aKey);
nsCookieEntry *entry = mDBState->hostTable.GetEntry(aBaseDomain); nsCookieEntry *entry = mDBState->hostTable.GetEntry(aKey);
if (!entry) if (!entry)
return false; return false;
@@ -3697,7 +3809,7 @@ nsCookieService::RemoveCookieFromList(const nsListIter &aIter,
void void
bindCookieParameters(mozIStorageBindingParamsArray *aParamsArray, bindCookieParameters(mozIStorageBindingParamsArray *aParamsArray,
const nsCString &aBaseDomain, const nsCookieKey &aKey,
const nsCookie *aCookie) const nsCookie *aCookie)
{ {
NS_ASSERTION(aParamsArray, "Null params array passed to bindCookieParameters!"); NS_ASSERTION(aParamsArray, "Null params array passed to bindCookieParameters!");
@@ -3712,7 +3824,15 @@ bindCookieParameters(mozIStorageBindingParamsArray *aParamsArray,
// Bind our values to params // Bind our values to params
rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("baseDomain"), rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("baseDomain"),
aBaseDomain); aKey.mBaseDomain);
NS_ASSERT_SUCCESS(rv);
rv = params->BindInt32ByName(NS_LITERAL_CSTRING("appId"),
aKey.mAppId);
NS_ASSERT_SUCCESS(rv);
rv = params->BindInt32ByName(NS_LITERAL_CSTRING("inBrowserElement"),
aKey.mInBrowserElement ? 1 : 0);
NS_ASSERT_SUCCESS(rv); NS_ASSERT_SUCCESS(rv);
rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("name"), rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("name"),
@@ -3757,7 +3877,7 @@ bindCookieParameters(mozIStorageBindingParamsArray *aParamsArray,
} }
void void
nsCookieService::AddCookieToList(const nsCString &aBaseDomain, nsCookieService::AddCookieToList(const nsCookieKey &aKey,
nsCookie *aCookie, nsCookie *aCookie,
DBState *aDBState, DBState *aDBState,
mozIStorageBindingParamsArray *aParamsArray, mozIStorageBindingParamsArray *aParamsArray,
@@ -3768,7 +3888,7 @@ nsCookieService::AddCookieToList(const nsCString &aBaseDomain,
NS_ASSERTION(!(!aDBState->dbConn && aParamsArray), NS_ASSERTION(!(!aDBState->dbConn && aParamsArray),
"Do not have a DB connection but have a params array?"); "Do not have a DB connection but have a params array?");
nsCookieEntry *entry = aDBState->hostTable.PutEntry(aBaseDomain); nsCookieEntry *entry = aDBState->hostTable.PutEntry(aKey);
NS_ASSERTION(entry, "can't insert element into a null entry!"); NS_ASSERTION(entry, "can't insert element into a null entry!");
entry->GetCookies().AppendElement(aCookie); entry->GetCookies().AppendElement(aCookie);
@@ -3785,7 +3905,7 @@ nsCookieService::AddCookieToList(const nsCString &aBaseDomain,
if (!paramsArray) { if (!paramsArray) {
stmt->NewBindingParamsArray(getter_AddRefs(paramsArray)); stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
} }
bindCookieParameters(paramsArray, aBaseDomain, aCookie); bindCookieParameters(paramsArray, aKey, aCookie);
// If we were supplied an array to store parameters, we shouldn't call // If we were supplied an array to store parameters, we shouldn't call
// executeAsync - someone up the stack will do this for us. // executeAsync - someone up the stack will do this for us.

View File

@@ -47,41 +47,42 @@ class CookieServiceParent;
} }
} }
// hash entry class // hash key class
class nsCookieEntry : public PLDHashEntryHdr class nsCookieKey : public PLDHashEntryHdr
{ {
public: public:
// Hash methods typedef const nsCookieKey& KeyType;
typedef const nsCString& KeyType; typedef const nsCookieKey* KeyTypePointer;
typedef const nsCString* KeyTypePointer;
typedef nsTArray< nsRefPtr<nsCookie> > ArrayType;
typedef ArrayType::index_type IndexType;
explicit nsCookieKey()
nsCookieEntry(KeyTypePointer aBaseDomain) {}
: mBaseDomain(*aBaseDomain)
{
}
nsCookieEntry(const nsCookieEntry& toCopy) nsCookieKey(const nsCString &baseDomain, uint32_t appId, bool inBrowser)
{ : mBaseDomain(baseDomain)
// if we end up here, things will break. nsTHashtable shouldn't , mAppId(appId)
// allow this, since we set ALLOW_MEMMOVE to true. , mInBrowserElement(inBrowser)
NS_NOTREACHED("nsCookieEntry copy constructor is forbidden!"); {}
}
~nsCookieEntry() nsCookieKey(const KeyTypePointer other)
{ : mBaseDomain(other->mBaseDomain)
} , mAppId(other->mAppId)
, mInBrowserElement(other->mInBrowserElement)
{}
KeyType GetKey() const nsCookieKey(const KeyType &other)
{ : mBaseDomain(other.mBaseDomain)
return mBaseDomain; , mAppId(other.mAppId)
} , mInBrowserElement(other.mInBrowserElement)
{}
bool KeyEquals(KeyTypePointer aKey) const ~nsCookieKey()
{}
bool KeyEquals(KeyTypePointer other) const
{ {
return mBaseDomain == *aKey; return mBaseDomain == other->mBaseDomain &&
mAppId == other->mAppId &&
mInBrowserElement == other->mInBrowserElement;
} }
static KeyTypePointer KeyToPointer(KeyType aKey) static KeyTypePointer KeyToPointer(KeyType aKey)
@@ -91,22 +92,55 @@ class nsCookieEntry : public PLDHashEntryHdr
static PLDHashNumber HashKey(KeyTypePointer aKey) static PLDHashNumber HashKey(KeyTypePointer aKey)
{ {
return mozilla::HashString(*aKey); // TODO: more efficient way to generate hash?
nsAutoCString temp(aKey->mBaseDomain);
temp.Append("#");
temp.Append(aKey->mAppId);
temp.Append("#");
temp.Append(aKey->mInBrowserElement ? 1 : 0);
return mozilla::HashString(temp);
} }
enum { ALLOW_MEMMOVE = true }; enum { ALLOW_MEMMOVE = true };
nsCString mBaseDomain;
uint32_t mAppId;
bool mInBrowserElement;
};
// Inherit from nsCookieKey so this can be stored in nsTHashTable
// TODO: why aren't we using nsClassHashTable<nsCookieKey, ArrayType>?
class nsCookieEntry : public nsCookieKey
{
public:
// Hash methods
typedef nsTArray< nsRefPtr<nsCookie> > ArrayType;
typedef ArrayType::index_type IndexType;
nsCookieEntry(KeyTypePointer aKey)
: nsCookieKey(aKey)
{}
nsCookieEntry(const nsCookieEntry& toCopy)
{
// if we end up here, things will break. nsTHashtable shouldn't
// allow this, since we set ALLOW_MEMMOVE to true.
NS_NOTREACHED("nsCookieEntry copy constructor is forbidden!");
}
~nsCookieEntry()
{}
inline ArrayType& GetCookies() { return mCookies; } inline ArrayType& GetCookies() { return mCookies; }
private: private:
nsCString mBaseDomain;
ArrayType mCookies; ArrayType mCookies;
}; };
// encapsulates a (baseDomain, nsCookie) tuple for temporary storage purposes. // encapsulates a (key, nsCookie) tuple for temporary storage purposes.
struct CookieDomainTuple struct CookieDomainTuple
{ {
nsCString baseDomain; nsCookieKey key;
nsRefPtr<nsCookie> cookie; nsRefPtr<nsCookie> cookie;
}; };
@@ -152,7 +186,7 @@ struct DBState
// A hashset of baseDomains read in synchronously, while the async read is // A hashset of baseDomains read in synchronously, while the async read is
// in flight. This is used to keep track of which data in hostArray is stale // in flight. This is used to keep track of which data in hostArray is stale
// when the time comes to merge. // when the time comes to merge.
nsTHashtable<nsCStringHashKey> readSet; nsTHashtable<nsCookieKey> readSet;
// DB completion handlers. // DB completion handlers.
nsCOMPtr<mozIStorageStatementCallback> insertListener; nsCOMPtr<mozIStorageStatementCallback> insertListener;
@@ -219,29 +253,30 @@ class nsCookieService : public nsICookieService
template<class T> nsCookie* GetCookieFromRow(T &aRow); template<class T> nsCookie* GetCookieFromRow(T &aRow);
void AsyncReadComplete(); void AsyncReadComplete();
void CancelAsyncRead(bool aPurgeReadSet); void CancelAsyncRead(bool aPurgeReadSet);
void EnsureReadDomain(const nsCString &aBaseDomain); void EnsureReadDomain(const nsCookieKey &aKey);
void EnsureReadComplete(); void EnsureReadComplete();
nsresult NormalizeHost(nsCString &aHost); nsresult NormalizeHost(nsCString &aHost);
nsresult GetBaseDomain(nsIURI *aHostURI, nsCString &aBaseDomain, bool &aRequireHostMatch); nsresult GetBaseDomain(nsIURI *aHostURI, nsCString &aBaseDomain, bool &aRequireHostMatch);
nsresult GetBaseDomainFromHost(const nsACString &aHost, nsCString &aBaseDomain); nsresult GetBaseDomainFromHost(const nsACString &aHost, nsCString &aBaseDomain);
nsresult GetCookieStringCommon(nsIURI *aHostURI, nsIChannel *aChannel, bool aHttpBound, char** aCookie); nsresult GetCookieStringCommon(nsIURI *aHostURI, nsIChannel *aChannel, bool aHttpBound, char** aCookie);
void GetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, bool aHttpBound, nsCString &aCookie); void GetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, bool aHttpBound, uint32_t aAppId, bool aInBrowserElement, nsCString &aCookie);
nsresult SetCookieStringCommon(nsIURI *aHostURI, const char *aCookieHeader, const char *aServerTime, nsIChannel *aChannel, bool aFromHttp); nsresult SetCookieStringCommon(nsIURI *aHostURI, const char *aCookieHeader, const char *aServerTime, nsIChannel *aChannel, bool aFromHttp);
void SetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, nsDependentCString &aCookieHeader, const nsCString &aServerTime, bool aFromHttp); void SetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, nsDependentCString &aCookieHeader, const nsCString &aServerTime, bool aFromHttp, uint32_t aAppId, bool aInBrowserElement);
bool SetCookieInternal(nsIURI *aHostURI, const nsCString& aBaseDomain, bool aRequireHostMatch, CookieStatus aStatus, nsDependentCString &aCookieHeader, int64_t aServerTime, bool aFromHttp); bool SetCookieInternal(nsIURI *aHostURI, const nsCookieKey& aKey, 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 AddInternal(const nsCookieKey& aKey, nsCookie *aCookie, int64_t aCurrentTimeInUsec, nsIURI *aHostURI, const char *aCookieHeader, bool aFromHttp);
void RemoveCookieFromList(const nsListIter &aIter, mozIStorageBindingParamsArray *aParamsArray = NULL); void RemoveCookieFromList(const nsListIter &aIter, mozIStorageBindingParamsArray *aParamsArray = NULL);
void AddCookieToList(const nsCString& aBaseDomain, nsCookie *aCookie, DBState *aDBState, mozIStorageBindingParamsArray *aParamsArray, bool aWriteToDB = true); void AddCookieToList(const nsCookieKey& aKey, nsCookie *aCookie, DBState *aDBState, mozIStorageBindingParamsArray *aParamsArray, bool aWriteToDB = true);
void UpdateCookieInList(nsCookie *aCookie, int64_t aLastAccessed, mozIStorageBindingParamsArray *aParamsArray); 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 GetTokenValue(nsASingleFragmentCString::const_char_iterator &aIter, nsASingleFragmentCString::const_char_iterator &aEndIter, nsDependentCSubstring &aTokenString, nsDependentCSubstring &aTokenValue, bool &aEqualsFound);
static bool ParseAttributes(nsDependentCString &aCookieHeader, nsCookieAttributes &aCookie); static bool ParseAttributes(nsDependentCString &aCookieHeader, nsCookieAttributes &aCookie);
CookieStatus CheckPrefs(nsIURI *aHostURI, bool aIsForeign, const nsCString &aBaseDomain, bool aRequireHostMatch, const char *aCookieHeader); bool RequireThirdPartyCheck();
CookieStatus CheckPrefs(nsIURI *aHostURI, bool aIsForeign, bool aRequireHostMatch, const char *aCookieHeader);
bool CheckDomain(nsCookieAttributes &aCookie, nsIURI *aHostURI, const nsCString &aBaseDomain, bool aRequireHostMatch); bool CheckDomain(nsCookieAttributes &aCookie, nsIURI *aHostURI, const nsCString &aBaseDomain, bool aRequireHostMatch);
static bool CheckPath(nsCookieAttributes &aCookie, nsIURI *aHostURI); static bool CheckPath(nsCookieAttributes &aCookie, nsIURI *aHostURI);
static bool GetExpiry(nsCookieAttributes &aCookie, int64_t aServerTime, int64_t aCurrentTime); static bool GetExpiry(nsCookieAttributes &aCookie, int64_t aServerTime, int64_t aCurrentTime);
void RemoveAllFromMemory(); void RemoveAllFromMemory();
already_AddRefed<nsIArray> PurgeCookies(int64_t aCurrentTimeInUsec); already_AddRefed<nsIArray> PurgeCookies(int64_t aCurrentTimeInUsec);
bool FindCookie(const nsCString& aBaseDomain, const nsAFlatCString &aHost, const nsAFlatCString &aName, const nsAFlatCString &aPath, nsListIter &aIter); bool FindCookie(const nsCookieKey& aKey, const nsAFlatCString &aHost, const nsAFlatCString &aName, const nsAFlatCString &aPath, nsListIter &aIter);
static void FindStaleCookie(nsCookieEntry *aEntry, int64_t aCurrentTime, nsListIter &aIter); static void FindStaleCookie(nsCookieEntry *aEntry, int64_t aCurrentTime, nsListIter &aIter);
void NotifyRejected(nsIURI *aHostURI); void NotifyRejected(nsIURI *aHostURI);
void NotifyChanged(nsISupports *aSubject, const PRUnichar *aData); void NotifyChanged(nsISupports *aSubject, const PRUnichar *aData);

View File

@@ -184,3 +184,37 @@ ChannelEventSink.prototype = {
callback.onRedirectVerifyCallback(Cr.NS_OK); callback.onRedirectVerifyCallback(Cr.NS_OK);
} }
}; };
/**
* Class that implements nsILoadContext. Use it as callbacks for channel when
* test needs it.
*/
function LoadContextCallback(appId, inBrowserElement, isPrivate, isContent) {
this.appId = appId;
this.isInBrowserElement = inBrowserElement;
this.usePrivateBrowsing = isPrivate;
this.isContent = isContent;
}
LoadContextCallback.prototype = {
associatedWindow: null,
topWindow : null,
isAppOfType: function(appType) {
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
QueryInterface: function(iid) {
if (iid == Ci.nsILoadContext ||
Ci.nsIInterfaceRequestor ||
Ci.nsISupports) {
return this;
}
throw Cr.NS_ERROR_NO_INTERFACE;
},
getInterface: function(iid) {
if (iid.equals(Ci.nsILoadContext))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
},
}

View File

@@ -0,0 +1,139 @@
/* 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/. */
/*
* Test that channels with different
* AppIds/inBrowserElements/usePrivateBrowsing (from nsILoadContext callback)
* are stored in separate namespaces ("cookie jars")
*/
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
Cu.import("resource://testing-common/httpd.js");
var httpserver = new HttpServer();
var cookieSetPath = "/setcookie";
var cookieCheckPath = "/checkcookie";
// Test array:
// - element 0: name for cookie, used both to set and later to check
// - element 1: loadContext (determines cookie namespace)
//
// TODO: bug 722850: make private browsing work per-app, and add tests. For now
// all values are 'false' for PB.
var tests = [
{ cookieName: 'LCC_App0_BrowF_PrivF',
loadContext: new LoadContextCallback(0, false, false, 1) },
{ cookieName: 'LCC_App0_BrowT_PrivF',
loadContext: new LoadContextCallback(0, true, false, 1) },
{ cookieName: 'LCC_App1_BrowF_PrivF',
loadContext: new LoadContextCallback(1, false, false, 1) },
{ cookieName: 'LCC_App1_BrowT_PrivF',
loadContext: new LoadContextCallback(1, true, false, 1) },
];
// test number: index into 'tests' array
var i = 0;
function setupChannel(path)
{
var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
var chan = ios.newChannel("http://localhost:4444" + path, "", null);
chan.notificationCallbacks = tests[i].loadContext;
chan.QueryInterface(Ci.nsIHttpChannel);
return chan;
}
function setCookie() {
var channel = setupChannel(cookieSetPath);
channel.setRequestHeader("foo-set-cookie", tests[i].cookieName, false);
channel.asyncOpen(new ChannelListener(setNextCookie, null), null);
}
function setNextCookie(request, data, context)
{
if (++i == tests.length) {
// all cookies set: switch to checking them
i = 0;
checkCookie();
} else {
do_print("setNextCookie:i=" + i);
setCookie();
}
}
// Open channel that should send one and only one correct Cookie: header to
// server, corresponding to it's namespace
function checkCookie()
{
var channel = setupChannel(cookieCheckPath);
channel.asyncOpen(new ChannelListener(completeCheckCookie, null), null);
}
function completeCheckCookie(request, data, context) {
// Look for all cookies in what the server saw: fail if we see any besides the
// one expected cookie for each namespace;
var expectedCookie = tests[i].cookieName;
request.QueryInterface(Ci.nsIHttpChannel);
var cookiesSeen = request.getResponseHeader("foo-saw-cookies");
var j;
for (j = 0; j < tests.length; j++) {
var cookieToCheck = tests[j].cookieName;
found = (cookiesSeen.indexOf(cookieToCheck) != -1);
if (found && expectedCookie != cookieToCheck) {
do_throw("test index " + i + ": found unexpected cookie '"
+ cookieToCheck + "': in '" + cookiesSeen + "'");
} else if (!found && expectedCookie == cookieToCheck) {
do_throw("test index " + i + ": missing expected cookie '"
+ expectedCookie + "': in '" + cookiesSeen + "'");
}
}
// If we get here we're good.
do_print("Saw only correct cookie '" + expectedCookie + "'");
do_check_true(true);
if (++i == tests.length) {
// end of tests
httpserver.stop(do_test_finished);
} else {
checkCookie();
}
}
function run_test()
{
httpserver.registerPathHandler(cookieSetPath, cookieSetHandler);
httpserver.registerPathHandler(cookieCheckPath, cookieCheckHandler);
httpserver.start(4444);
setCookie();
do_test_pending();
}
function cookieSetHandler(metadata, response)
{
var cookieName = metadata.getHeader("foo-set-cookie");
response.setStatusLine(metadata.httpVersion, 200, "Ok");
response.setHeader("Set-Cookie", cookieName + "=1; Path=/", false);
response.setHeader("Content-Type", "text/plain");
response.bodyOutputStream.write("Ok", "Ok".length);
}
function cookieCheckHandler(metadata, response)
{
var cookies = metadata.getHeader("Cookie");
response.setStatusLine(metadata.httpVersion, 200, "Ok");
response.setHeader("foo-saw-cookies", cookies, false);
response.setHeader("Content-Type", "text/plain");
response.bodyOutputStream.write("Ok", "Ok".length);
}

View File

@@ -95,6 +95,7 @@ fail-if = os == "android"
[test_content_encoding_gzip.js] [test_content_encoding_gzip.js]
[test_content_sniffer.js] [test_content_sniffer.js]
[test_cookie_header.js] [test_cookie_header.js]
[test_cookiejars.js]
[test_data_protocol.js] [test_data_protocol.js]
[test_dns_service.js] [test_dns_service.js]
[test_dns_localredirect.js] [test_dns_localredirect.js]

View File

@@ -0,0 +1,3 @@
function run_test() {
run_test_in_child("../unit/test_cookiejars.js");
}

View File

@@ -4,7 +4,8 @@ tail =
[test_cacheflags_wrap.js] [test_cacheflags_wrap.js]
[test_channel_close_wrap.js] [test_channel_close_wrap.js]
[test_cookie_wrap.js] [test_cookie_header_wrap.js]
[test_cookiejars_wrap.js]
[test_duplicate_headers_wrap.js] [test_duplicate_headers_wrap.js]
[test_event_sink_wrap.js] [test_event_sink_wrap.js]
[test_head_wrap.js] [test_head_wrap.js]