Bug 756648 - Implement "cookie jars" for apps. r=biesi,smaug
This commit is contained in:
@@ -18,6 +18,11 @@ SerializedLoadContext::SerializedLoadContext(nsILoadContext* aLoadContext)
|
||||
|
||||
SerializedLoadContext::SerializedLoadContext(nsIChannel* aChannel)
|
||||
{
|
||||
if (!aChannel) {
|
||||
Init(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadContext> loadContext;
|
||||
NS_QueryNotificationCallbacks(aChannel, loadContext);
|
||||
Init(loadContext);
|
||||
@@ -40,7 +45,9 @@ SerializedLoadContext::SerializedLoadContext(nsIChannel* aChannel)
|
||||
SerializedLoadContext::SerializedLoadContext(nsIWebSocketChannel* aChannel)
|
||||
{
|
||||
nsCOMPtr<nsILoadContext> loadContext;
|
||||
if (aChannel) {
|
||||
NS_QueryNotificationCallbacks(aChannel, loadContext);
|
||||
}
|
||||
Init(loadContext);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
// c) Schema 3: the 'creationTime' column already exists; or the
|
||||
// 'moz_uniqueid' index already exists.
|
||||
|
||||
let COOKIE_DATABASE_SCHEMA_CURRENT = 5;
|
||||
|
||||
let test_generator = do_run_test();
|
||||
|
||||
function run_test() {
|
||||
@@ -62,6 +64,10 @@ function do_run_test() {
|
||||
sub_generator.next();
|
||||
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);
|
||||
sub_generator.next();
|
||||
yield;
|
||||
@@ -206,7 +212,7 @@ function run_test_3(generator, schema)
|
||||
|
||||
// Check that the schema version has been reset.
|
||||
let db = Services.storage.openDatabase(cookieFile);
|
||||
do_check_eq(db.schemaVersion, 4);
|
||||
do_check_eq(db.schemaVersion, COOKIE_DATABASE_SCHEMA_CURRENT);
|
||||
db.close();
|
||||
|
||||
// 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.
|
||||
db = Services.storage.openDatabase(cookieFile);
|
||||
do_check_eq(db.schemaVersion, 4);
|
||||
do_check_eq(db.schemaVersion, COOKIE_DATABASE_SCHEMA_CURRENT);
|
||||
db.close();
|
||||
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.
|
||||
db = Services.storage.openDatabase(cookieFile);
|
||||
do_check_eq(db.schemaVersion, 4);
|
||||
do_check_eq(db.schemaVersion, COOKIE_DATABASE_SCHEMA_CURRENT);
|
||||
db.close();
|
||||
do_check_true(backupFile.exists());
|
||||
|
||||
|
||||
@@ -1339,6 +1339,12 @@ NS_UsePrivateBrowsing(nsIChannel *channel)
|
||||
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.
|
||||
* Returns false if error or channel's callbacks don't implement nsILoadContext.
|
||||
|
||||
@@ -117,7 +117,8 @@ CookieServiceChild::GetCookieStringInternal(nsIURI *aHostURI,
|
||||
|
||||
// Synchronously call the parent.
|
||||
nsAutoCString result;
|
||||
SendGetCookieString(uriParams, !!isForeign, aFromHttp, &result);
|
||||
SendGetCookieString(uriParams, !!isForeign, aFromHttp,
|
||||
IPC::SerializedLoadContext(aChannel), &result);
|
||||
if (!result.IsEmpty())
|
||||
*aCookieString = ToNewCString(result);
|
||||
|
||||
@@ -149,7 +150,7 @@ CookieServiceChild::SetCookieStringInternal(nsIURI *aHostURI,
|
||||
|
||||
// Synchronously call the parent.
|
||||
SendSetCookieString(uriParams, !!isForeign, cookieString, serverTime,
|
||||
aFromHttp);
|
||||
aFromHttp, IPC::SerializedLoadContext(aChannel));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ bool
|
||||
CookieServiceParent::RecvGetCookieString(const URIParams& aHost,
|
||||
const bool& aIsForeign,
|
||||
const bool& aFromHttp,
|
||||
const IPC::SerializedLoadContext&
|
||||
aLoadContext,
|
||||
nsCString* aResult)
|
||||
{
|
||||
if (!mCookieService)
|
||||
@@ -45,8 +47,12 @@ CookieServiceParent::RecvGetCookieString(const URIParams& aHost,
|
||||
if (!hostURI)
|
||||
return false;
|
||||
|
||||
mCookieService->GetCookieStringInternal(hostURI, aIsForeign,
|
||||
aFromHttp, *aResult);
|
||||
uint32_t appId;
|
||||
bool isInBrowserElement;
|
||||
GetAppInfoFromLoadContext(aLoadContext, appId, isInBrowserElement);
|
||||
|
||||
mCookieService->GetCookieStringInternal(hostURI, aIsForeign, aFromHttp, appId,
|
||||
isInBrowserElement, *aResult);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -55,7 +61,9 @@ CookieServiceParent::RecvSetCookieString(const URIParams& aHost,
|
||||
const bool& aIsForeign,
|
||||
const nsCString& aCookieString,
|
||||
const nsCString& aServerTime,
|
||||
const bool& aFromHttp)
|
||||
const bool& aFromHttp,
|
||||
const IPC::SerializedLoadContext&
|
||||
aLoadContext)
|
||||
{
|
||||
if (!mCookieService)
|
||||
return true;
|
||||
@@ -66,13 +74,34 @@ CookieServiceParent::RecvSetCookieString(const URIParams& aHost,
|
||||
if (!hostURI)
|
||||
return false;
|
||||
|
||||
uint32_t appId;
|
||||
bool isInBrowserElement;
|
||||
GetAppInfoFromLoadContext(aLoadContext, appId, isInBrowserElement);
|
||||
|
||||
nsDependentCString cookieString(aCookieString, 0);
|
||||
mCookieService->SetCookieStringInternal(hostURI, aIsForeign,
|
||||
cookieString, aServerTime,
|
||||
aFromHttp);
|
||||
mCookieService->SetCookieStringInternal(hostURI, aIsForeign, cookieString,
|
||||
aServerTime, aFromHttp, appId,
|
||||
isInBrowserElement);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#define mozilla_net_CookieServiceParent_h
|
||||
|
||||
#include "mozilla/net/PCookieServiceParent.h"
|
||||
#include "SerializedLoadContext.h"
|
||||
|
||||
class nsCookieService;
|
||||
class nsIIOService;
|
||||
@@ -24,13 +25,20 @@ protected:
|
||||
virtual bool RecvGetCookieString(const URIParams& aHost,
|
||||
const bool& aIsForeign,
|
||||
const bool& aFromHttp,
|
||||
const IPC::SerializedLoadContext&
|
||||
loadContext,
|
||||
nsCString* aResult);
|
||||
|
||||
virtual bool RecvSetCookieString(const URIParams& aHost,
|
||||
const bool& aIsForeign,
|
||||
const nsCString& aCookieString,
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
include protocol PNecko;
|
||||
include URIParams;
|
||||
|
||||
include "SerializedLoadContext.h";
|
||||
|
||||
using IPC::SerializedLoadContext;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
@@ -47,6 +51,9 @@ parent:
|
||||
* Whether the result is for an HTTP request header. This should be
|
||||
* true for nsICookieService.getCookieStringFromHttp calls, false
|
||||
* otherwise.
|
||||
* @param loadContext
|
||||
* The loadContext from the HTTP channel or document that the cookie is
|
||||
* being set on.
|
||||
*
|
||||
* @see nsICookieService.getCookieString
|
||||
* @see nsICookieService.getCookieStringFromHttp
|
||||
@@ -56,7 +63,8 @@ parent:
|
||||
*/
|
||||
sync GetCookieString(URIParams host,
|
||||
bool isForeign,
|
||||
bool fromHttp)
|
||||
bool fromHttp,
|
||||
SerializedLoadContext loadContext)
|
||||
returns (nsCString result);
|
||||
|
||||
/*
|
||||
@@ -80,6 +88,9 @@ parent:
|
||||
* Whether the result is for an HTTP request header. This should be
|
||||
* true for nsICookieService.setCookieStringFromHttp calls, false
|
||||
* otherwise.
|
||||
* @param loadContext
|
||||
* The loadContext from the HTTP channel or document that the cookie is
|
||||
* being set on.
|
||||
*
|
||||
* @see nsICookieService.setCookieString
|
||||
* @see nsICookieService.setCookieStringFromHttp
|
||||
@@ -89,7 +100,8 @@ parent:
|
||||
bool isForeign,
|
||||
nsCString cookieString,
|
||||
nsCString serverTime,
|
||||
bool fromHttp);
|
||||
bool fromHttp,
|
||||
SerializedLoadContext loadContext);
|
||||
|
||||
__delete__();
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* -*- 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
|
||||
* 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/. */
|
||||
@@ -52,6 +53,13 @@
|
||||
using namespace mozilla;
|
||||
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:
|
||||
* useful types & constants
|
||||
@@ -64,7 +72,7 @@ static nsCookieService *gCookieService;
|
||||
static const char kHttpOnlyPrefix[] = "#HttpOnly_";
|
||||
|
||||
#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 kCookiePurgeAge =
|
||||
@@ -99,7 +107,7 @@ static const char kPrefThirdPartySession[] = "network.cookie.thirdparty.session
|
||||
|
||||
static void
|
||||
bindCookieParameters(mozIStorageBindingParamsArray *aParamsArray,
|
||||
const nsCString &aBaseDomain,
|
||||
const nsCookieKey &aKey,
|
||||
const nsCookie *aCookie);
|
||||
|
||||
// struct for temporarily storing cookie attributes during header parsing
|
||||
@@ -467,7 +475,9 @@ public:
|
||||
break;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -930,6 +940,52 @@ nsCookieService::TryInitDB(bool aRecreateDB)
|
||||
}
|
||||
// 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.
|
||||
rv = mDefaultDBState->dbConn->SetSchemaVersion(COOKIES_SCHEMA_VERSION);
|
||||
NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
|
||||
@@ -965,6 +1021,8 @@ nsCookieService::TryInitDB(bool aRecreateDB)
|
||||
"SELECT "
|
||||
"id, "
|
||||
"baseDomain, "
|
||||
"appId, "
|
||||
"inBrowserElement, "
|
||||
"name, "
|
||||
"value, "
|
||||
"host, "
|
||||
@@ -1005,6 +1063,8 @@ nsCookieService::TryInitDB(bool aRecreateDB)
|
||||
rv = mDefaultDBState->dbConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
|
||||
"INSERT INTO moz_cookies ("
|
||||
"baseDomain, "
|
||||
"appId, "
|
||||
"inBrowserElement, "
|
||||
"name, "
|
||||
"value, "
|
||||
"host, "
|
||||
@@ -1016,6 +1076,8 @@ nsCookieService::TryInitDB(bool aRecreateDB)
|
||||
"isHttpOnly"
|
||||
") VALUES ("
|
||||
":baseDomain, "
|
||||
":appId, "
|
||||
":inBrowserElement, "
|
||||
":name, "
|
||||
":value, "
|
||||
":host, "
|
||||
@@ -1076,11 +1138,15 @@ nsCookieService::CreateTable()
|
||||
COOKIES_SCHEMA_VERSION);
|
||||
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(
|
||||
"CREATE TABLE moz_cookies ("
|
||||
"id INTEGER PRIMARY KEY, "
|
||||
"baseDomain TEXT, "
|
||||
"appId INTEGER DEFAULT 0, "
|
||||
"inBrowserElement INTEGER DEFAULT 0, "
|
||||
"name TEXT, "
|
||||
"value TEXT, "
|
||||
"host TEXT, "
|
||||
@@ -1090,13 +1156,15 @@ nsCookieService::CreateTable()
|
||||
"creationTime INTEGER, "
|
||||
"isSecure 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;
|
||||
|
||||
// Create an index on baseDomain.
|
||||
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
|
||||
@@ -1254,7 +1322,7 @@ RebuildDBCallback(nsCookieEntry *aEntry,
|
||||
nsCookie* cookie = cookies[i];
|
||||
|
||||
if (!cookie->IsSession()) {
|
||||
bindCookieParameters(paramsArray, aEntry->GetKey(), cookie);
|
||||
bindCookieParameters(paramsArray, aEntry, cookie);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1422,8 +1490,16 @@ nsCookieService::GetCookieStringCommon(nsIURI *aHostURI,
|
||||
bool isForeign = true;
|
||||
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;
|
||||
GetCookieStringInternal(aHostURI, isForeign, aHttpBound, result);
|
||||
GetCookieStringInternal(aHostURI, isForeign, aHttpBound, appId,
|
||||
inBrowserElement, result);
|
||||
*aCookie = result.IsEmpty() ? nullptr : ToNewCString(result);
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -1463,10 +1539,17 @@ nsCookieService::SetCookieStringCommon(nsIURI *aHostURI,
|
||||
bool isForeign = true;
|
||||
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 serverTime(aServerTime ? aServerTime : "");
|
||||
SetCookieStringInternal(aHostURI, isForeign, cookieString,
|
||||
serverTime, aFromHttp);
|
||||
serverTime, aFromHttp, appId, inBrowserElement);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -1475,7 +1558,9 @@ nsCookieService::SetCookieStringInternal(nsIURI *aHostURI,
|
||||
bool aIsForeign,
|
||||
nsDependentCString &aCookieHeader,
|
||||
const nsCString &aServerTime,
|
||||
bool aFromHttp)
|
||||
bool aFromHttp,
|
||||
uint32_t aAppId,
|
||||
bool aInBrowserElement)
|
||||
{
|
||||
NS_ASSERTION(aHostURI, "null host!");
|
||||
|
||||
@@ -1498,9 +1583,11 @@ nsCookieService::SetCookieStringInternal(nsIURI *aHostURI,
|
||||
return;
|
||||
}
|
||||
|
||||
nsCookieKey key(baseDomain, aAppId, aInBrowserElement);
|
||||
|
||||
// check default prefs
|
||||
CookieStatus cookieStatus = CheckPrefs(aHostURI, aIsForeign, baseDomain,
|
||||
requireHostMatch, aCookieHeader.get());
|
||||
CookieStatus cookieStatus = CheckPrefs(aHostURI, aIsForeign, requireHostMatch,
|
||||
aCookieHeader.get());
|
||||
// fire a notification if cookie was rejected (but not if there was an error)
|
||||
switch (cookieStatus) {
|
||||
case STATUS_REJECTED:
|
||||
@@ -1528,7 +1615,7 @@ nsCookieService::SetCookieStringInternal(nsIURI *aHostURI,
|
||||
}
|
||||
|
||||
// process each cookie in the header
|
||||
while (SetCookieInternal(aHostURI, baseDomain, requireHostMatch, cookieStatus,
|
||||
while (SetCookieInternal(aHostURI, key, requireHostMatch, cookieStatus,
|
||||
aCookieHeader, serverTime, aFromHttp)) {
|
||||
// document.cookie can only set one cookie at a time
|
||||
if (!aFromHttp)
|
||||
@@ -1712,7 +1799,7 @@ nsCookieService::Add(const nsACString &aHost,
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1738,7 +1825,7 @@ nsCookieService::Remove(const nsACString &aHost,
|
||||
|
||||
nsListIter matchIter;
|
||||
nsRefPtr<nsCookie> cookie;
|
||||
if (FindCookie(baseDomain,
|
||||
if (FindCookie(DEFAULT_APP_KEY(baseDomain),
|
||||
host,
|
||||
PromiseFlatCString(aName),
|
||||
PromiseFlatCString(aPath),
|
||||
@@ -1793,7 +1880,9 @@ nsCookieService::Read()
|
||||
"creationTime, "
|
||||
"isSecure, "
|
||||
"isHttpOnly, "
|
||||
"baseDomain "
|
||||
"baseDomain, "
|
||||
"appId, "
|
||||
"inBrowserElement "
|
||||
"FROM moz_cookies "
|
||||
"WHERE baseDomain NOTNULL"), getter_AddRefs(stmtRead));
|
||||
NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
|
||||
@@ -1885,11 +1974,10 @@ nsCookieService::AsyncReadComplete()
|
||||
// Tiebreak: if the given base domain has already been read in, ignore
|
||||
// the background data. Note that readSet may contain domains that were
|
||||
// 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;
|
||||
|
||||
AddCookieToList(tuple.baseDomain, tuple.cookie, mDefaultDBState, NULL,
|
||||
false);
|
||||
AddCookieToList(tuple.key, tuple.cookie, mDefaultDBState, NULL, false);
|
||||
}
|
||||
|
||||
mDefaultDBState->stmtReadDomain = nullptr;
|
||||
@@ -1933,7 +2021,7 @@ nsCookieService::CancelAsyncRead(bool aPurgeReadSet)
|
||||
}
|
||||
|
||||
void
|
||||
nsCookieService::EnsureReadDomain(const nsCString &aBaseDomain)
|
||||
nsCookieService::EnsureReadDomain(const nsCookieKey &aKey)
|
||||
{
|
||||
NS_ASSERTION(!mDBState->dbConn || mDBState == mDefaultDBState,
|
||||
"not in default db state");
|
||||
@@ -1943,7 +2031,7 @@ nsCookieService::EnsureReadDomain(const nsCString &aBaseDomain)
|
||||
return;
|
||||
|
||||
// Fast path 2: already read in this particular domain.
|
||||
if (NS_LIKELY(mDefaultDBState->readSet.GetEntry(aBaseDomain)))
|
||||
if (NS_LIKELY(mDefaultDBState->readSet.GetEntry(aKey)))
|
||||
return;
|
||||
|
||||
// Read in the data synchronously.
|
||||
@@ -1962,7 +2050,9 @@ nsCookieService::EnsureReadDomain(const nsCString &aBaseDomain)
|
||||
"isSecure, "
|
||||
"isHttpOnly "
|
||||
"FROM moz_cookies "
|
||||
"WHERE baseDomain = :baseDomain"),
|
||||
"WHERE baseDomain = :baseDomain "
|
||||
" AND appId = :appId "
|
||||
" AND inBrowserElement = :inBrowserElement"),
|
||||
getter_AddRefs(mDefaultDBState->stmtReadDomain));
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
@@ -1980,8 +2070,15 @@ nsCookieService::EnsureReadDomain(const nsCString &aBaseDomain)
|
||||
mozStorageStatementScoper scoper(mDefaultDBState->stmtReadDomain);
|
||||
|
||||
rv = mDefaultDBState->stmtReadDomain->BindUTF8StringByName(
|
||||
NS_LITERAL_CSTRING("baseDomain"), aBaseDomain);
|
||||
NS_LITERAL_CSTRING("baseDomain"), aKey.mBaseDomain);
|
||||
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;
|
||||
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
|
||||
// either all the cookies get added, or in the case of corruption, none.
|
||||
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.
|
||||
mDefaultDBState->readSet.PutEntry(aBaseDomain);
|
||||
mDefaultDBState->readSet.PutEntry(aKey);
|
||||
|
||||
COOKIE_LOGSTRING(PR_LOG_DEBUG,
|
||||
("EnsureReadDomain(): %ld cookies read for base domain %s",
|
||||
array.Length(), aBaseDomain.get()));
|
||||
("EnsureReadDomain(): %ld cookies read for base domain %s, "
|
||||
" appId=%u, inBrowser=%d", array.Length(), aKey.mBaseDomain.get(),
|
||||
(unsigned)aKey.mAppId, (int)aKey.mInBrowserElement));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2043,7 +2141,9 @@ nsCookieService::EnsureReadComplete()
|
||||
"creationTime, "
|
||||
"isSecure, "
|
||||
"isHttpOnly, "
|
||||
"baseDomain "
|
||||
"baseDomain, "
|
||||
"appId, "
|
||||
"inBrowserElement "
|
||||
"FROM moz_cookies "
|
||||
"WHERE baseDomain NOTNULL"), getter_AddRefs(stmt));
|
||||
|
||||
@@ -2057,7 +2157,8 @@ nsCookieService::EnsureReadComplete()
|
||||
}
|
||||
|
||||
nsCString baseDomain, name, value, host, path;
|
||||
bool hasResult;
|
||||
uint32_t appId;
|
||||
bool inBrowserElement, hasResult;
|
||||
nsAutoTArray<CookieDomainTuple, kMaxNumberOfCookies> array;
|
||||
while (1) {
|
||||
rv = stmt->ExecuteStep(&hasResult);
|
||||
@@ -2075,11 +2176,14 @@ nsCookieService::EnsureReadComplete()
|
||||
|
||||
// Make sure we haven't already read the data.
|
||||
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;
|
||||
|
||||
CookieDomainTuple* tuple = array.AppendElement();
|
||||
tuple->baseDomain = baseDomain;
|
||||
tuple->key = key;
|
||||
tuple->cookie = GetCookieFromRow(stmt);
|
||||
}
|
||||
|
||||
@@ -2087,7 +2191,7 @@ nsCookieService::EnsureReadComplete()
|
||||
// either all the cookies get added, or in the case of corruption, none.
|
||||
for (uint32_t i = 0; i < array.Length(); ++i) {
|
||||
CookieDomainTuple& tuple = array[i];
|
||||
AddCookieToList(tuple.baseDomain, tuple.cookie, mDefaultDBState, NULL,
|
||||
AddCookieToList(tuple.key, tuple.cookie, mDefaultDBState, NULL,
|
||||
false);
|
||||
}
|
||||
|
||||
@@ -2218,6 +2322,9 @@ nsCookieService::ImportCookies(nsIFile *aCookieFile)
|
||||
if (NS_FAILED(rv))
|
||||
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
|
||||
// creation time, so just use the current time to generate a unique one.
|
||||
nsRefPtr<nsCookie> newCookie =
|
||||
@@ -2240,10 +2347,11 @@ nsCookieService::ImportCookies(nsIFile *aCookieFile)
|
||||
lastAccessedCounter--;
|
||||
|
||||
if (originalCookieCount == 0) {
|
||||
AddCookieToList(baseDomain, newCookie, mDefaultDBState, paramsArray);
|
||||
AddCookieToList(key, newCookie, mDefaultDBState, paramsArray);
|
||||
}
|
||||
else {
|
||||
AddInternal(baseDomain, newCookie, currentTimeInUsec, NULL, NULL, true);
|
||||
AddInternal(key, newCookie, currentTimeInUsec,
|
||||
NULL, NULL, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2305,6 +2413,8 @@ void
|
||||
nsCookieService::GetCookieStringInternal(nsIURI *aHostURI,
|
||||
bool aIsForeign,
|
||||
bool aHttpBound,
|
||||
uint32_t aAppId,
|
||||
bool aInBrowserElement,
|
||||
nsCString &aCookieString)
|
||||
{
|
||||
NS_ASSERTION(aHostURI, "null host!");
|
||||
@@ -2332,8 +2442,8 @@ nsCookieService::GetCookieStringInternal(nsIURI *aHostURI,
|
||||
}
|
||||
|
||||
// check default prefs
|
||||
CookieStatus cookieStatus = CheckPrefs(aHostURI, aIsForeign, baseDomain,
|
||||
requireHostMatch, nullptr);
|
||||
CookieStatus cookieStatus = CheckPrefs(aHostURI, aIsForeign, requireHostMatch,
|
||||
nullptr);
|
||||
// for GetCookie(), we don't fire rejection notifications.
|
||||
switch (cookieStatus) {
|
||||
case STATUS_REJECTED:
|
||||
@@ -2357,10 +2467,11 @@ nsCookieService::GetCookieStringInternal(nsIURI *aHostURI,
|
||||
int64_t currentTime = currentTimeInUsec / PR_USEC_PER_SEC;
|
||||
bool stale = false;
|
||||
|
||||
EnsureReadDomain(baseDomain);
|
||||
nsCookieKey key(baseDomain, aAppId, aInBrowserElement);
|
||||
EnsureReadDomain(key);
|
||||
|
||||
// perform the hash lookup
|
||||
nsCookieEntry *entry = mDBState->hostTable.GetEntry(baseDomain);
|
||||
nsCookieEntry *entry = mDBState->hostTable.GetEntry(key);
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
@@ -2489,7 +2600,7 @@ nsCookieService::GetCookieStringInternal(nsIURI *aHostURI,
|
||||
// to be processed
|
||||
bool
|
||||
nsCookieService::SetCookieInternal(nsIURI *aHostURI,
|
||||
const nsCString &aBaseDomain,
|
||||
const nsCookieKey &aKey,
|
||||
bool aRequireHostMatch,
|
||||
CookieStatus aStatus,
|
||||
nsDependentCString &aCookieHeader,
|
||||
@@ -2536,7 +2647,7 @@ nsCookieService::SetCookieInternal(nsIURI *aHostURI,
|
||||
}
|
||||
|
||||
// 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");
|
||||
return newCookie;
|
||||
}
|
||||
@@ -2586,7 +2697,7 @@ nsCookieService::SetCookieInternal(nsIURI *aHostURI,
|
||||
|
||||
// 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
|
||||
AddInternal(aBaseDomain, cookie, PR_Now(), aHostURI, savedCookieHeader.get(),
|
||||
AddInternal(aKey, cookie, PR_Now(), aHostURI, savedCookieHeader.get(),
|
||||
aFromHttp);
|
||||
return newCookie;
|
||||
}
|
||||
@@ -2597,7 +2708,7 @@ nsCookieService::SetCookieInternal(nsIURI *aHostURI,
|
||||
// and deletes a cookie (if maximum number of cookies has been
|
||||
// reached). also performs list maintenance by removing expired cookies.
|
||||
void
|
||||
nsCookieService::AddInternal(const nsCString &aBaseDomain,
|
||||
nsCookieService::AddInternal(const nsCookieKey &aKey,
|
||||
nsCookie *aCookie,
|
||||
int64_t aCurrentTimeInUsec,
|
||||
nsIURI *aHostURI,
|
||||
@@ -2614,7 +2725,7 @@ nsCookieService::AddInternal(const nsCString &aBaseDomain,
|
||||
}
|
||||
|
||||
nsListIter matchIter;
|
||||
bool foundCookie = FindCookie(aBaseDomain, aCookie->Host(),
|
||||
bool foundCookie = FindCookie(aKey, aCookie->Host(),
|
||||
aCookie->Name(), aCookie->Path(), matchIter);
|
||||
|
||||
nsRefPtr<nsCookie> oldCookie;
|
||||
@@ -2679,7 +2790,7 @@ nsCookieService::AddInternal(const nsCString &aBaseDomain,
|
||||
}
|
||||
|
||||
// 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) {
|
||||
nsListIter 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
|
||||
// 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);
|
||||
|
||||
// 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
|
||||
nsCookieService::CheckPrefs(nsIURI *aHostURI,
|
||||
bool aIsForeign,
|
||||
const nsCString &aBaseDomain,
|
||||
bool aRequireHostMatch,
|
||||
const char *aCookieHeader)
|
||||
{
|
||||
@@ -3511,7 +3621,7 @@ nsCookieService::CookieExists(nsICookie2 *aCookie,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsListIter iter;
|
||||
*aFoundCookie = FindCookie(baseDomain, host, name, path, iter);
|
||||
*aFoundCookie = FindCookie(DEFAULT_APP_KEY(baseDomain), host, name, path, iter);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -3565,10 +3675,11 @@ nsCookieService::CountCookiesFromHost(const nsACString &aHost,
|
||||
rv = GetBaseDomainFromHost(host, baseDomain);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
EnsureReadDomain(baseDomain);
|
||||
nsCookieKey key = DEFAULT_APP_KEY(baseDomain);
|
||||
EnsureReadDomain(key);
|
||||
|
||||
// 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;
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -3593,9 +3704,10 @@ nsCookieService::GetCookiesFromHost(const nsACString &aHost,
|
||||
rv = GetBaseDomainFromHost(host, baseDomain);
|
||||
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)
|
||||
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.
|
||||
bool
|
||||
nsCookieService::FindCookie(const nsCString &aBaseDomain,
|
||||
nsCookieService::FindCookie(const nsCookieKey &aKey,
|
||||
const nsAFlatCString &aHost,
|
||||
const nsAFlatCString &aName,
|
||||
const nsAFlatCString &aPath,
|
||||
nsListIter &aIter)
|
||||
{
|
||||
EnsureReadDomain(aBaseDomain);
|
||||
EnsureReadDomain(aKey);
|
||||
|
||||
nsCookieEntry *entry = mDBState->hostTable.GetEntry(aBaseDomain);
|
||||
nsCookieEntry *entry = mDBState->hostTable.GetEntry(aKey);
|
||||
if (!entry)
|
||||
return false;
|
||||
|
||||
@@ -3697,7 +3809,7 @@ nsCookieService::RemoveCookieFromList(const nsListIter &aIter,
|
||||
|
||||
void
|
||||
bindCookieParameters(mozIStorageBindingParamsArray *aParamsArray,
|
||||
const nsCString &aBaseDomain,
|
||||
const nsCookieKey &aKey,
|
||||
const nsCookie *aCookie)
|
||||
{
|
||||
NS_ASSERTION(aParamsArray, "Null params array passed to bindCookieParameters!");
|
||||
@@ -3712,7 +3824,15 @@ bindCookieParameters(mozIStorageBindingParamsArray *aParamsArray,
|
||||
|
||||
// Bind our values to params
|
||||
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);
|
||||
|
||||
rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("name"),
|
||||
@@ -3757,7 +3877,7 @@ bindCookieParameters(mozIStorageBindingParamsArray *aParamsArray,
|
||||
}
|
||||
|
||||
void
|
||||
nsCookieService::AddCookieToList(const nsCString &aBaseDomain,
|
||||
nsCookieService::AddCookieToList(const nsCookieKey &aKey,
|
||||
nsCookie *aCookie,
|
||||
DBState *aDBState,
|
||||
mozIStorageBindingParamsArray *aParamsArray,
|
||||
@@ -3768,7 +3888,7 @@ nsCookieService::AddCookieToList(const nsCString &aBaseDomain,
|
||||
NS_ASSERTION(!(!aDBState->dbConn && aParamsArray),
|
||||
"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!");
|
||||
|
||||
entry->GetCookies().AppendElement(aCookie);
|
||||
@@ -3785,7 +3905,7 @@ nsCookieService::AddCookieToList(const nsCString &aBaseDomain,
|
||||
if (!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
|
||||
// executeAsync - someone up the stack will do this for us.
|
||||
|
||||
@@ -47,41 +47,42 @@ class CookieServiceParent;
|
||||
}
|
||||
}
|
||||
|
||||
// hash entry class
|
||||
class nsCookieEntry : public PLDHashEntryHdr
|
||||
// hash key class
|
||||
class nsCookieKey : public PLDHashEntryHdr
|
||||
{
|
||||
public:
|
||||
// Hash methods
|
||||
typedef const nsCString& KeyType;
|
||||
typedef const nsCString* KeyTypePointer;
|
||||
typedef nsTArray< nsRefPtr<nsCookie> > ArrayType;
|
||||
typedef ArrayType::index_type IndexType;
|
||||
typedef const nsCookieKey& KeyType;
|
||||
typedef const nsCookieKey* KeyTypePointer;
|
||||
|
||||
explicit
|
||||
nsCookieEntry(KeyTypePointer aBaseDomain)
|
||||
: mBaseDomain(*aBaseDomain)
|
||||
{
|
||||
}
|
||||
nsCookieKey()
|
||||
{}
|
||||
|
||||
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!");
|
||||
}
|
||||
nsCookieKey(const nsCString &baseDomain, uint32_t appId, bool inBrowser)
|
||||
: mBaseDomain(baseDomain)
|
||||
, mAppId(appId)
|
||||
, mInBrowserElement(inBrowser)
|
||||
{}
|
||||
|
||||
~nsCookieEntry()
|
||||
{
|
||||
}
|
||||
nsCookieKey(const KeyTypePointer other)
|
||||
: mBaseDomain(other->mBaseDomain)
|
||||
, mAppId(other->mAppId)
|
||||
, mInBrowserElement(other->mInBrowserElement)
|
||||
{}
|
||||
|
||||
KeyType GetKey() const
|
||||
{
|
||||
return mBaseDomain;
|
||||
}
|
||||
nsCookieKey(const KeyType &other)
|
||||
: mBaseDomain(other.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)
|
||||
@@ -91,22 +92,55 @@ class nsCookieEntry : public PLDHashEntryHdr
|
||||
|
||||
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 };
|
||||
|
||||
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; }
|
||||
|
||||
private:
|
||||
nsCString mBaseDomain;
|
||||
ArrayType mCookies;
|
||||
};
|
||||
|
||||
// encapsulates a (baseDomain, nsCookie) tuple for temporary storage purposes.
|
||||
// encapsulates a (key, nsCookie) tuple for temporary storage purposes.
|
||||
struct CookieDomainTuple
|
||||
{
|
||||
nsCString baseDomain;
|
||||
nsCookieKey key;
|
||||
nsRefPtr<nsCookie> cookie;
|
||||
};
|
||||
|
||||
@@ -152,7 +186,7 @@ struct DBState
|
||||
// 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
|
||||
// when the time comes to merge.
|
||||
nsTHashtable<nsCStringHashKey> readSet;
|
||||
nsTHashtable<nsCookieKey> readSet;
|
||||
|
||||
// DB completion handlers.
|
||||
nsCOMPtr<mozIStorageStatementCallback> insertListener;
|
||||
@@ -219,29 +253,30 @@ class nsCookieService : public nsICookieService
|
||||
template<class T> nsCookie* GetCookieFromRow(T &aRow);
|
||||
void AsyncReadComplete();
|
||||
void CancelAsyncRead(bool aPurgeReadSet);
|
||||
void EnsureReadDomain(const nsCString &aBaseDomain);
|
||||
void EnsureReadDomain(const nsCookieKey &aKey);
|
||||
void EnsureReadComplete();
|
||||
nsresult NormalizeHost(nsCString &aHost);
|
||||
nsresult GetBaseDomain(nsIURI *aHostURI, nsCString &aBaseDomain, bool &aRequireHostMatch);
|
||||
nsresult GetBaseDomainFromHost(const nsACString &aHost, nsCString &aBaseDomain);
|
||||
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);
|
||||
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 SetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, nsDependentCString &aCookieHeader, const nsCString &aServerTime, bool aFromHttp, uint32_t aAppId, bool aInBrowserElement);
|
||||
bool SetCookieInternal(nsIURI *aHostURI, const nsCookieKey& aKey, bool aRequireHostMatch, CookieStatus aStatus, nsDependentCString &aCookieHeader, int64_t aServerTime, 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 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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
void NotifyRejected(nsIURI *aHostURI);
|
||||
void NotifyChanged(nsISupports *aSubject, const PRUnichar *aData);
|
||||
|
||||
@@ -184,3 +184,37 @@ ChannelEventSink.prototype = {
|
||||
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;
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
139
netwerk/test/unit/test_cookiejars.js
Normal file
139
netwerk/test/unit/test_cookiejars.js
Normal 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);
|
||||
}
|
||||
|
||||
@@ -95,6 +95,7 @@ fail-if = os == "android"
|
||||
[test_content_encoding_gzip.js]
|
||||
[test_content_sniffer.js]
|
||||
[test_cookie_header.js]
|
||||
[test_cookiejars.js]
|
||||
[test_data_protocol.js]
|
||||
[test_dns_service.js]
|
||||
[test_dns_localredirect.js]
|
||||
|
||||
3
netwerk/test/unit_ipc/test_cookiejars_wrap.js
Normal file
3
netwerk/test/unit_ipc/test_cookiejars_wrap.js
Normal file
@@ -0,0 +1,3 @@
|
||||
function run_test() {
|
||||
run_test_in_child("../unit/test_cookiejars.js");
|
||||
}
|
||||
@@ -4,7 +4,8 @@ tail =
|
||||
|
||||
[test_cacheflags_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_event_sink_wrap.js]
|
||||
[test_head_wrap.js]
|
||||
|
||||
Reference in New Issue
Block a user