зеркало из https://github.com/mozilla/pjs.git
Bug 572223 - too much cookies.sqlite io. Part 2: async read. r=sdwilsh
This commit is contained in:
Родитель
22ce2adbdf
Коммит
8741cf7c86
|
@ -357,9 +357,8 @@ LogSuccess(PRBool aSetCookie, nsIURI *aHostURI, const nsAFlatCString &aCookieStr
|
|||
#define NS_ASSERT_SUCCESS(res) PR_BEGIN_MACRO /* nothing */ PR_END_MACRO
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
/******************************************************************************
|
||||
* DBListenerErrorHandler imp:
|
||||
* DBListenerErrorHandler impl:
|
||||
* Parent class for our async storage listeners that handles the logging of
|
||||
* errors.
|
||||
******************************************************************************/
|
||||
|
@ -389,9 +388,8 @@ public:
|
|||
NS_IMPL_ISUPPORTS1(DBListenerErrorHandler, mozIStorageStatementCallback)
|
||||
|
||||
/******************************************************************************
|
||||
* InsertCookieDBListener imp:
|
||||
* Static mozIStorageStatementCallback used to track asynchronous insertion
|
||||
* operations.
|
||||
* InsertCookieDBListener impl:
|
||||
* mozIStorageStatementCallback used to track asynchronous insertion operations.
|
||||
******************************************************************************/
|
||||
class InsertCookieDBListener : public DBListenerErrorHandler
|
||||
{
|
||||
|
@ -411,9 +409,8 @@ public:
|
|||
};
|
||||
|
||||
/******************************************************************************
|
||||
* UpdateCookieDBListener imp:
|
||||
* Static mozIStorageStatementCallback used to track asynchronous update
|
||||
* operations.
|
||||
* UpdateCookieDBListener impl:
|
||||
* mozIStorageStatementCallback used to track asynchronous update operations.
|
||||
******************************************************************************/
|
||||
class UpdateCookieDBListener : public DBListenerErrorHandler
|
||||
{
|
||||
|
@ -433,9 +430,8 @@ public:
|
|||
};
|
||||
|
||||
/******************************************************************************
|
||||
* RemoveCookieDBListener imp:
|
||||
* Static mozIStorageStatementCallback used to track asynchronous removal
|
||||
* operations.
|
||||
* RemoveCookieDBListener impl:
|
||||
* mozIStorageStatementCallback used to track asynchronous removal operations.
|
||||
******************************************************************************/
|
||||
class RemoveCookieDBListener : public DBListenerErrorHandler
|
||||
{
|
||||
|
@ -454,6 +450,77 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* ReadCookieDBListener impl:
|
||||
* mozIStorageStatementCallback used to track asynchronous removal operations.
|
||||
******************************************************************************/
|
||||
class ReadCookieDBListener : public DBListenerErrorHandler
|
||||
{
|
||||
protected:
|
||||
virtual const char *GetOpType() { return "READ"; }
|
||||
bool mCanceled;
|
||||
|
||||
public:
|
||||
ReadCookieDBListener() : mCanceled(PR_FALSE) { }
|
||||
|
||||
void Cancel() { mCanceled = true; }
|
||||
|
||||
NS_IMETHOD HandleResult(mozIStorageResultSet *aResult)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<mozIStorageRow> row;
|
||||
nsTArray<CookieDomainTuple> &cookieArray =
|
||||
nsCookieService::gCookieService->mDefaultDBState.hostArray;
|
||||
|
||||
while (1) {
|
||||
rv = aResult->GetNextRow(getter_AddRefs(row));
|
||||
NS_ASSERT_SUCCESS(rv);
|
||||
|
||||
if (!row)
|
||||
break;
|
||||
|
||||
CookieDomainTuple *tuple = cookieArray.AppendElement();
|
||||
nsCookieService::gCookieService->ReadRow(row, *tuple);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
NS_IMETHOD HandleCompletion(PRUint16 aReason)
|
||||
{
|
||||
// Process the completion of the read operation. If we have been canceled,
|
||||
// we cannot assume that the cookieservice still has an open connection
|
||||
// or that it even refers to the same database, so we must return early.
|
||||
// Conversely, the cookieservice guarantees that if we have not been
|
||||
// canceled, the database connection is still alive and we can safely
|
||||
// operate on it.
|
||||
|
||||
if (mCanceled) {
|
||||
// We may receive a REASON_FINISHED after being canceled;
|
||||
// tweak the reason accordingly.
|
||||
aReason = mozIStorageStatementCallback::REASON_CANCELED;
|
||||
}
|
||||
|
||||
switch (aReason) {
|
||||
case mozIStorageStatementCallback::REASON_FINISHED:
|
||||
nsCookieService::gCookieService->ReadComplete();
|
||||
break;
|
||||
case mozIStorageStatementCallback::REASON_CANCELED:
|
||||
// Nothing more to do here. The partially read data has already been
|
||||
// thrown away.
|
||||
COOKIE_LOGSTRING(PR_LOG_DEBUG, ("Read canceled"));
|
||||
break;
|
||||
case mozIStorageStatementCallback::REASON_ERROR:
|
||||
// Nothing more to do here. DBListenerErrorHandler::HandleError()
|
||||
// can take handle it.
|
||||
COOKIE_LOGSTRING(PR_LOG_DEBUG, ("Read error"));
|
||||
break;
|
||||
default:
|
||||
NS_NOTREACHED("invalid reason");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* CloseCookieDBListener imp:
|
||||
* Static mozIStorageCompletionCallback used to notify when the database is
|
||||
|
@ -478,8 +545,6 @@ public:
|
|||
|
||||
NS_IMPL_ISUPPORTS1(CloseCookieDBListener, mozIStorageCompletionCallback)
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
/******************************************************************************
|
||||
* nsCookieService impl:
|
||||
* singleton instance ctor/dtor methods
|
||||
|
@ -901,6 +966,20 @@ nsCookieService::CloseDB()
|
|||
mDefaultDBState.stmtDelete = nsnull;
|
||||
mDefaultDBState.stmtUpdate = nsnull;
|
||||
if (mDefaultDBState.dbConn) {
|
||||
// Cancel any pending read. No further results will be received by our
|
||||
// read listener.
|
||||
if (mDefaultDBState.pendingRead) {
|
||||
NS_ASSERTION(mDefaultDBState.readListener, "no read listener");
|
||||
|
||||
mDefaultDBState.readListener->Cancel();
|
||||
nsresult rv = mDefaultDBState.pendingRead->Cancel();
|
||||
NS_ASSERT_SUCCESS(rv);
|
||||
|
||||
mDefaultDBState.pendingRead = nsnull;
|
||||
mDefaultDBState.readListener = nsnull;
|
||||
mDefaultDBState.hostArray.Clear();
|
||||
}
|
||||
|
||||
mDefaultDBState.dbConn->AsyncClose(mCloseListener);
|
||||
mDefaultDBState.dbConn = nsnull;
|
||||
}
|
||||
|
@ -1329,28 +1408,9 @@ nsCookieService::Remove(const nsACString &aHost,
|
|||
nsresult
|
||||
nsCookieService::Read()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// delete expired cookies, before we read in the db
|
||||
{
|
||||
// scope the deletion, so the write lock is released when finished
|
||||
nsCOMPtr<mozIStorageStatement> stmtDeleteExpired;
|
||||
rv = mDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"DELETE FROM moz_cookies WHERE expiry <= ?1"),
|
||||
getter_AddRefs(stmtDeleteExpired));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = stmtDeleteExpired->BindInt64ByIndex(0, PR_Now() / PR_USEC_PER_SEC);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRBool hasResult;
|
||||
rv = stmtDeleteExpired->ExecuteStep(&hasResult);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// let the reading begin!
|
||||
// Let the reading begin!
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
rv = mDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
nsresult rv = mDefaultDBState.dbConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT "
|
||||
"id, "
|
||||
"name, "
|
||||
|
@ -1365,49 +1425,58 @@ nsCookieService::Read()
|
|||
"FROM moz_cookies"), getter_AddRefs(stmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCAutoString baseDomain, name, value, host, path;
|
||||
PRBool hasResult;
|
||||
while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&hasResult)) && hasResult) {
|
||||
PRInt64 creationID = stmt->AsInt64(0);
|
||||
nsRefPtr<ReadCookieDBListener> readListener = new ReadCookieDBListener;
|
||||
rv = stmt->ExecuteAsync(readListener,
|
||||
getter_AddRefs(mDefaultDBState.pendingRead));
|
||||
NS_ASSERT_SUCCESS(rv);
|
||||
|
||||
stmt->GetUTF8String(1, name);
|
||||
stmt->GetUTF8String(2, value);
|
||||
stmt->GetUTF8String(3, host);
|
||||
stmt->GetUTF8String(4, path);
|
||||
mDefaultDBState.readListener = readListener;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRInt64 expiry = stmt->AsInt64(5);
|
||||
PRInt64 lastAccessed = stmt->AsInt64(6);
|
||||
PRBool isSecure = 0 != stmt->AsInt32(7);
|
||||
PRBool isHttpOnly = 0 != stmt->AsInt32(8);
|
||||
void
|
||||
nsCookieService::ReadRow(mozIStorageRow *aRow, CookieDomainTuple &aTuple)
|
||||
{
|
||||
PRInt64 creationID = aRow->AsInt64(0);
|
||||
|
||||
stmt->GetUTF8String(9, baseDomain);
|
||||
nsCString name, value, host, path;
|
||||
aRow->GetUTF8String(1, name);
|
||||
aRow->GetUTF8String(2, value);
|
||||
aRow->GetUTF8String(3, host);
|
||||
aRow->GetUTF8String(4, path);
|
||||
|
||||
// create a new nsCookie and assign the data.
|
||||
nsCookie* newCookie =
|
||||
nsCookie::Create(name, value, host, path,
|
||||
expiry,
|
||||
lastAccessed,
|
||||
creationID,
|
||||
PR_FALSE,
|
||||
isSecure,
|
||||
isHttpOnly);
|
||||
if (!newCookie)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
PRInt64 expiry = aRow->AsInt64(5);
|
||||
PRInt64 lastAccessed = aRow->AsInt64(6);
|
||||
PRBool isSecure = 0 != aRow->AsInt32(7);
|
||||
PRBool isHttpOnly = 0 != aRow->AsInt32(8);
|
||||
|
||||
if (!AddCookieToList(baseDomain, newCookie, NULL, PR_FALSE))
|
||||
// It is purpose that created us; purpose that connects us;
|
||||
// purpose that pulls us; that guides us; that drives us.
|
||||
// It is purpose that defines us; purpose that binds us.
|
||||
// When a cookie no longer has purpose, it has a choice:
|
||||
// it can return to the source to be deleted, or it can go
|
||||
// into exile, and stay hidden inside the Matrix.
|
||||
// Let's choose deletion.
|
||||
delete newCookie;
|
||||
aRow->GetUTF8String(9, aTuple.baseDomain);
|
||||
|
||||
// Create a new nsCookie and assign the data.
|
||||
aTuple.cookie =
|
||||
nsCookie::Create(name, value, host, path,
|
||||
expiry,
|
||||
lastAccessed,
|
||||
creationID,
|
||||
PR_FALSE,
|
||||
isSecure,
|
||||
isHttpOnly);
|
||||
}
|
||||
|
||||
void
|
||||
nsCookieService::ReadComplete()
|
||||
{
|
||||
for (PRUint32 i = 0; i < mDefaultDBState.hostArray.Length(); ++i) {
|
||||
const CookieDomainTuple &tuple = mDefaultDBState.hostArray[i];
|
||||
AddCookieToList(tuple.baseDomain, tuple.cookie, NULL, PR_FALSE);
|
||||
}
|
||||
|
||||
COOKIE_LOGSTRING(PR_LOG_DEBUG, ("Read(): %ld cookies read", mDBState->cookieCount));
|
||||
mDefaultDBState.hostArray.Clear();
|
||||
mDefaultDBState.pendingRead = nsnull;
|
||||
mDefaultDBState.readListener = nsnull;
|
||||
|
||||
return rv;
|
||||
COOKIE_LOGSTRING(PR_LOG_DEBUG, ("Read(): %ld cookies read",
|
||||
mDefaultDBState.cookieCount));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -3015,7 +3084,7 @@ bindCookieParameters(mozIStorageBindingParamsArray *aParamsArray,
|
|||
NS_ASSERT_SUCCESS(rv);
|
||||
}
|
||||
|
||||
PRBool
|
||||
void
|
||||
nsCookieService::AddCookieToList(const nsCString &aBaseDomain,
|
||||
nsCookie *aCookie,
|
||||
mozIStorageBindingParamsArray *aParamsArray,
|
||||
|
@ -3027,10 +3096,7 @@ nsCookieService::AddCookieToList(const nsCString &aBaseDomain,
|
|||
"Do not have a DB connection but have a params array?");
|
||||
|
||||
nsCookieEntry *entry = mDBState->hostTable.PutEntry(aBaseDomain);
|
||||
if (!entry) {
|
||||
NS_ERROR("can't insert element into a null entry!");
|
||||
return PR_FALSE;
|
||||
}
|
||||
NS_ASSERTION(entry, "can't insert element into a null entry!");
|
||||
|
||||
entry->GetCookies().AppendElement(aCookie);
|
||||
++mDBState->cookieCount;
|
||||
|
@ -3058,8 +3124,6 @@ nsCookieService::AddCookieToList(const nsCString &aBaseDomain,
|
|||
NS_ASSERT_SUCCESS(rv);
|
||||
}
|
||||
}
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -54,7 +54,9 @@
|
|||
#include "nsHashKeys.h"
|
||||
#include "nsTHashtable.h"
|
||||
#include "mozIStorageStatement.h"
|
||||
#include "mozIStoragePendingStatement.h"
|
||||
#include "mozIStorageConnection.h"
|
||||
#include "mozIStorageRow.h"
|
||||
|
||||
class nsICookiePermission;
|
||||
class nsIEffectiveTLDService;
|
||||
|
@ -63,9 +65,9 @@ class nsIPrefBranch;
|
|||
class nsIObserverService;
|
||||
class nsIURI;
|
||||
class nsIChannel;
|
||||
class DBListenerErrorHandler;
|
||||
class mozIStorageStatementCallback;
|
||||
class mozIStorageCompletionCallback;
|
||||
class ReadCookieDBListener;
|
||||
|
||||
struct nsCookieAttributes;
|
||||
struct nsListIter;
|
||||
|
@ -135,6 +137,13 @@ class nsCookieEntry : public PLDHashEntryHdr
|
|||
ArrayType mCookies;
|
||||
};
|
||||
|
||||
// encapsulates a (baseDomain, nsCookie) tuple for temporary storage purposes.
|
||||
struct CookieDomainTuple
|
||||
{
|
||||
nsCString baseDomain;
|
||||
nsRefPtr<nsCookie> cookie;
|
||||
};
|
||||
|
||||
// encapsulates in-memory and on-disk DB states, so we can
|
||||
// conveniently switch state when entering or exiting private browsing.
|
||||
struct DBState
|
||||
|
@ -148,6 +157,10 @@ struct DBState
|
|||
nsCOMPtr<mozIStorageStatement> stmtInsert;
|
||||
nsCOMPtr<mozIStorageStatement> stmtDelete;
|
||||
nsCOMPtr<mozIStorageStatement> stmtUpdate;
|
||||
|
||||
nsCOMPtr<mozIStoragePendingStatement> pendingRead;
|
||||
ReadCookieDBListener* readListener; // weak ref
|
||||
nsTArray<CookieDomainTuple> hostArray;
|
||||
};
|
||||
|
||||
// these constants represent a decision about a cookie based on user prefs.
|
||||
|
@ -193,6 +206,8 @@ class nsCookieService : public nsICookieService
|
|||
nsresult CreateTable();
|
||||
void CloseDB();
|
||||
nsresult Read();
|
||||
void ReadRow(mozIStorageRow *aRow, CookieDomainTuple &aCookeTuple);
|
||||
void ReadComplete();
|
||||
nsresult NormalizeHost(nsCString &aHost);
|
||||
nsresult GetBaseDomain(nsIURI *aHostURI, nsCString &aBaseDomain, PRBool &aRequireHostMatch);
|
||||
nsresult GetBaseDomainFromHost(const nsACString &aHost, nsCString &aBaseDomain);
|
||||
|
@ -201,7 +216,7 @@ class nsCookieService : public nsICookieService
|
|||
PRBool SetCookieInternal(nsIURI *aHostURI, const nsCString& aBaseDomain, PRBool aRequireHostMatch, CookieStatus aStatus, nsDependentCString &aCookieHeader, PRInt64 aServerTime, PRBool aFromHttp);
|
||||
void AddInternal(const nsCString& aBaseDomain, nsCookie *aCookie, PRInt64 aCurrentTimeInUsec, nsIURI *aHostURI, const char *aCookieHeader, PRBool aFromHttp);
|
||||
void RemoveCookieFromList(const nsListIter &aIter, mozIStorageBindingParamsArray *aParamsArray = NULL);
|
||||
PRBool AddCookieToList(const nsCString& aBaseDomain, nsCookie *aCookie, mozIStorageBindingParamsArray *aParamsArray, PRBool aWriteToDB = PR_TRUE);
|
||||
void AddCookieToList(const nsCString& aBaseDomain, nsCookie *aCookie, mozIStorageBindingParamsArray *aParamsArray, PRBool aWriteToDB = PR_TRUE);
|
||||
void UpdateCookieInList(nsCookie *aCookie, PRInt64 aLastAccessed, mozIStorageBindingParamsArray *aParamsArray);
|
||||
static PRBool GetTokenValue(nsASingleFragmentCString::const_char_iterator &aIter, nsASingleFragmentCString::const_char_iterator &aEndIter, nsDependentCSubstring &aTokenString, nsDependentCSubstring &aTokenValue, PRBool &aEqualsFound);
|
||||
static PRBool ParseAttributes(nsDependentCString &aCookieHeader, nsCookieAttributes &aCookie);
|
||||
|
@ -247,8 +262,9 @@ class nsCookieService : public nsICookieService
|
|||
PRUint16 mMaxCookiesPerHost;
|
||||
PRInt64 mCookiePurgeAge;
|
||||
|
||||
// this callback needs access to member functions
|
||||
// friends!
|
||||
friend PLDHashOperator purgeCookiesCallback(nsCookieEntry *aEntry, void *aArg);
|
||||
friend class ReadCookieDBListener;
|
||||
|
||||
static nsCookieService* GetSingleton();
|
||||
#ifdef MOZ_IPC
|
||||
|
|
Загрузка…
Ссылка в новой задаче