mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 14:22:01 +00:00
Revert bug 572223 to fix creation of duplicate cookies.
This commit is contained in:
parent
dbaecaed6a
commit
55339a9932
@ -17,33 +17,36 @@ XPCOMUtils.defineLazyServiceGetter(Services, "cookiemgr",
|
||||
"@mozilla.org/cookiemanager;1",
|
||||
"nsICookieManager2");
|
||||
|
||||
function _observer(generator, service, topic) {
|
||||
Services.obs.addObserver(this, topic, false);
|
||||
// Close and reload the cookie database.
|
||||
function do_reload_profile(generator, profile, cleanse) {
|
||||
function _observer(generator, service, topic) {
|
||||
Services.obs.addObserver(this, topic, false);
|
||||
|
||||
this.service = service;
|
||||
this.generator = generator;
|
||||
this.topic = topic;
|
||||
}
|
||||
this.service = service;
|
||||
this.generator = generator;
|
||||
this.topic = topic;
|
||||
}
|
||||
|
||||
_observer.prototype = {
|
||||
observe: function (subject, topic, data) {
|
||||
do_check_eq(this.topic, topic);
|
||||
_observer.prototype = {
|
||||
observe: function (subject, topic, data) {
|
||||
do_check_eq(this.topic, topic);
|
||||
|
||||
Services.obs.removeObserver(this, this.topic);
|
||||
Services.obs.removeObserver(this, this.topic);
|
||||
|
||||
// Continue executing the generator function.
|
||||
if (this.generator)
|
||||
// Fire the notification to reload the database, and continue executing
|
||||
// the generator function.
|
||||
this.service.observe(null, "profile-do-change", "");
|
||||
this.generator.next();
|
||||
|
||||
this.generator = null;
|
||||
this.service = null;
|
||||
this.topic = null;
|
||||
this.generator = null;
|
||||
this.service = null;
|
||||
this.topic = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close the cookie database. If a generator is supplied, it will be invoked
|
||||
// once the close is complete.
|
||||
function do_close_profile(generator, cleanse) {
|
||||
let dbfile = profile.QueryInterface(Ci.nsILocalFile).clone();
|
||||
dbfile.append("cookies.sqlite");
|
||||
|
||||
// Register an observer for db close.
|
||||
let service = Services.cookies.QueryInterface(Ci.nsIObserver);
|
||||
let obs = new _observer(generator, service, "cookie-db-closed");
|
||||
@ -52,17 +55,6 @@ function do_close_profile(generator, cleanse) {
|
||||
service.observe(null, "profile-before-change", cleanse ? cleanse : "");
|
||||
}
|
||||
|
||||
// Load the cookie database. If a generator is supplied, it will be invoked
|
||||
// once the load is complete.
|
||||
function do_load_profile(generator) {
|
||||
// Register an observer for read completion.
|
||||
let service = Services.cookies.QueryInterface(Ci.nsIObserver);
|
||||
let obs = new _observer(generator, service, "cookie-db-read");
|
||||
|
||||
// Load the profile.
|
||||
service.observe(null, "profile-do-change", "");
|
||||
}
|
||||
|
||||
// Set four cookies; with & without channel, http and non-http; and test
|
||||
// the cookie count against 'expected' after each set.
|
||||
function do_set_cookies(uri, channel, session, expected) {
|
||||
@ -81,17 +73,3 @@ function do_set_cookies(uri, channel, session, expected) {
|
||||
Services.cookies.setCookieStringFromHttp(uri, null, null, "hot=dog" + suffix, null, channel);
|
||||
do_check_eq(Services.cookiemgr.countCookiesFromHost(uri.host), expected[3]);
|
||||
}
|
||||
|
||||
function do_count_enumerator(enumerator) {
|
||||
let i = 0;
|
||||
while (enumerator.hasMoreElements()) {
|
||||
enumerator.getNext();
|
||||
++i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
function do_count_cookies() {
|
||||
return do_count_enumerator(Services.cookiemgr.enumerator);
|
||||
}
|
||||
|
||||
|
@ -47,24 +47,14 @@ function do_run_test() {
|
||||
do_set_cookies(uri2, channel2, true, [1, 2, 3, 4]);
|
||||
|
||||
// fake a profile change
|
||||
do_close_profile(test_generator);
|
||||
do_reload_profile(test_generator, profile);
|
||||
yield;
|
||||
do_load_profile();
|
||||
do_check_eq(Services.cookies.countCookiesFromHost(uri1.host), 4);
|
||||
do_check_eq(Services.cookies.countCookiesFromHost(uri2.host), 0);
|
||||
|
||||
// Again, but don't wait for the async close to complete. This should always
|
||||
// work, since we blocked on close above and haven't kicked off any writes
|
||||
// since then.
|
||||
do_close_profile();
|
||||
do_load_profile();
|
||||
do_check_eq(Services.cookies.countCookiesFromHost(uri1.host), 4);
|
||||
do_check_eq(Services.cookies.countCookiesFromHost(uri2.host), 0);
|
||||
|
||||
// cleanse them
|
||||
do_close_profile(test_generator, "shutdown-cleanse");
|
||||
do_reload_profile(test_generator, profile, "shutdown-cleanse");
|
||||
yield;
|
||||
do_load_profile();
|
||||
do_check_eq(Services.cookies.countCookiesFromHost(uri1.host), 0);
|
||||
do_check_eq(Services.cookies.countCookiesFromHost(uri2.host), 0);
|
||||
|
||||
@ -74,9 +64,8 @@ function do_run_test() {
|
||||
do_set_cookies(uri2, channel2, true, [1, 2, 3, 4]);
|
||||
|
||||
// fake a profile change
|
||||
do_close_profile(test_generator);
|
||||
do_reload_profile(test_generator, profile);
|
||||
yield;
|
||||
do_load_profile();
|
||||
do_check_eq(Services.cookies.countCookiesFromHost(uri1.host), 0);
|
||||
do_check_eq(Services.cookies.countCookiesFromHost(uri2.host), 0);
|
||||
|
||||
|
@ -1,67 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// test cookie database asynchronous read operation.
|
||||
|
||||
let test_generator = do_run_test();
|
||||
|
||||
function run_test() {
|
||||
do_test_pending();
|
||||
test_generator.next();
|
||||
}
|
||||
|
||||
function finish_test() {
|
||||
do_execute_soon(function() {
|
||||
test_generator.close();
|
||||
do_test_finished();
|
||||
});
|
||||
}
|
||||
|
||||
function do_run_test() {
|
||||
// Set up a profile.
|
||||
let profile = do_get_profile();
|
||||
|
||||
for (let i = 0; i < 3000; ++i) {
|
||||
let uri = NetUtil.newURI("http://" + i + ".com/");
|
||||
Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null);
|
||||
}
|
||||
|
||||
do_check_eq(do_count_cookies(), 3000);
|
||||
|
||||
// fake a profile change
|
||||
do_close_profile(test_generator);
|
||||
yield;
|
||||
do_load_profile();
|
||||
|
||||
// test a few random cookies
|
||||
do_check_eq(Services.cookiemgr.countCookiesFromHost("2000.com"), 1);
|
||||
do_check_eq(Services.cookiemgr.countCookiesFromHost("abc.com"), 0);
|
||||
do_check_eq(Services.cookiemgr.countCookiesFromHost("100.com"), 1);
|
||||
do_check_eq(Services.cookiemgr.countCookiesFromHost("1400.com"), 1);
|
||||
do_check_eq(Services.cookiemgr.countCookiesFromHost("xyz.com"), 0);
|
||||
|
||||
// force synchronous load of everything
|
||||
do_check_eq(do_count_cookies(), 3000);
|
||||
|
||||
// check that everything's precisely correct
|
||||
for (let i = 0; i < 3000; ++i) {
|
||||
let host = i.toString() + ".com";
|
||||
do_check_eq(Services.cookiemgr.countCookiesFromHost(host), 1);
|
||||
}
|
||||
|
||||
// reload again, but wait for async read completion
|
||||
do_close_profile(test_generator);
|
||||
yield;
|
||||
do_load_profile(test_generator);
|
||||
yield;
|
||||
|
||||
// check that everything's precisely correct
|
||||
for (let i = 0; i < 3000; ++i) {
|
||||
let host = i.toString() + ".com";
|
||||
do_check_eq(Services.cookiemgr.countCookiesFromHost(host), 1);
|
||||
}
|
||||
|
||||
finish_test();
|
||||
}
|
||||
|
@ -47,16 +47,14 @@ function do_run_test() {
|
||||
do_set_cookies(uri2, channel1, true, [1, 2, 3, 4]);
|
||||
|
||||
// fake a profile change
|
||||
do_close_profile(test_generator);
|
||||
do_reload_profile(test_generator, profile);
|
||||
yield;
|
||||
do_load_profile();
|
||||
do_check_eq(Services.cookies.countCookiesFromHost(uri1.host), 4);
|
||||
do_check_eq(Services.cookies.countCookiesFromHost(uri2.host), 0);
|
||||
|
||||
// cleanse them
|
||||
do_close_profile(test_generator, "shutdown-cleanse");
|
||||
do_reload_profile(test_generator, profile, "shutdown-cleanse");
|
||||
yield;
|
||||
do_load_profile();
|
||||
do_check_eq(Services.cookies.countCookiesFromHost(uri1.host), 0);
|
||||
do_check_eq(Services.cookies.countCookiesFromHost(uri2.host), 0);
|
||||
|
||||
@ -66,9 +64,8 @@ function do_run_test() {
|
||||
do_set_cookies(uri2, channel1, true, [1, 2, 3, 4]);
|
||||
|
||||
// fake a profile change
|
||||
do_close_profile(test_generator);
|
||||
do_reload_profile(test_generator, profile);
|
||||
yield;
|
||||
do_load_profile();
|
||||
do_check_eq(Services.cookies.countCookiesFromHost(uri1.host), 0);
|
||||
do_check_eq(Services.cookies.countCookiesFromHost(uri2.host), 0);
|
||||
|
||||
|
@ -87,8 +87,6 @@ using namespace mozilla::net;
|
||||
* useful types & constants
|
||||
******************************************************************************/
|
||||
|
||||
static nsCookieService *gCookieService;
|
||||
|
||||
// XXX_hack. See bug 178993.
|
||||
// This is a hack to hide HttpOnly cookies from older browsers
|
||||
static const char kHttpOnlyPrefix[] = "#HttpOnly_";
|
||||
@ -359,8 +357,9 @@ LogSuccess(PRBool aSetCookie, nsIURI *aHostURI, const nsAFlatCString &aCookieStr
|
||||
#define NS_ASSERT_SUCCESS(res) PR_BEGIN_MACRO /* nothing */ PR_END_MACRO
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
/******************************************************************************
|
||||
* DBListenerErrorHandler impl:
|
||||
* DBListenerErrorHandler imp:
|
||||
* Parent class for our async storage listeners that handles the logging of
|
||||
* errors.
|
||||
******************************************************************************/
|
||||
@ -374,7 +373,6 @@ public:
|
||||
|
||||
NS_IMETHOD HandleError(mozIStorageError* aError)
|
||||
{
|
||||
// XXX Ignore corruption handling for now. See bug 547031.
|
||||
#ifdef PR_LOGGING
|
||||
PRInt32 result = -1;
|
||||
aError->GetResult(&result);
|
||||
@ -391,8 +389,9 @@ public:
|
||||
NS_IMPL_ISUPPORTS1(DBListenerErrorHandler, mozIStorageStatementCallback)
|
||||
|
||||
/******************************************************************************
|
||||
* InsertCookieDBListener impl:
|
||||
* mozIStorageStatementCallback used to track asynchronous insertion operations.
|
||||
* InsertCookieDBListener imp:
|
||||
* Static mozIStorageStatementCallback used to track asynchronous insertion
|
||||
* operations.
|
||||
******************************************************************************/
|
||||
class InsertCookieDBListener : public DBListenerErrorHandler
|
||||
{
|
||||
@ -412,8 +411,9 @@ public:
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* UpdateCookieDBListener impl:
|
||||
* mozIStorageStatementCallback used to track asynchronous update operations.
|
||||
* UpdateCookieDBListener imp:
|
||||
* Static mozIStorageStatementCallback used to track asynchronous update
|
||||
* operations.
|
||||
******************************************************************************/
|
||||
class UpdateCookieDBListener : public DBListenerErrorHandler
|
||||
{
|
||||
@ -433,8 +433,9 @@ public:
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* RemoveCookieDBListener impl:
|
||||
* mozIStorageStatementCallback used to track asynchronous removal operations.
|
||||
* RemoveCookieDBListener imp:
|
||||
* Static mozIStorageStatementCallback used to track asynchronous removal
|
||||
* operations.
|
||||
******************************************************************************/
|
||||
class RemoveCookieDBListener : public DBListenerErrorHandler
|
||||
{
|
||||
@ -453,78 +454,6 @@ 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(false) { }
|
||||
|
||||
void Cancel() { mCanceled = true; }
|
||||
|
||||
NS_IMETHOD HandleResult(mozIStorageResultSet *aResult)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<mozIStorageRow> row;
|
||||
nsTArray<CookieDomainTuple> &cookieArray =
|
||||
gCookieService->mDefaultDBState.hostArray;
|
||||
|
||||
while (1) {
|
||||
rv = aResult->GetNextRow(getter_AddRefs(row));
|
||||
NS_ASSERT_SUCCESS(rv);
|
||||
|
||||
if (!row)
|
||||
break;
|
||||
|
||||
CookieDomainTuple *tuple = cookieArray.AppendElement();
|
||||
row->GetUTF8String(9, tuple->baseDomain);
|
||||
tuple->cookie = gCookieService->GetCookieFromRow(row);
|
||||
}
|
||||
|
||||
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:
|
||||
gCookieService->AsyncReadComplete();
|
||||
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
|
||||
@ -549,11 +478,15 @@ public:
|
||||
|
||||
NS_IMPL_ISUPPORTS1(CloseCookieDBListener, mozIStorageCompletionCallback)
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
/******************************************************************************
|
||||
* nsCookieService impl:
|
||||
* singleton instance ctor/dtor methods
|
||||
******************************************************************************/
|
||||
|
||||
static nsCookieService *gCookieService;
|
||||
|
||||
nsICookieService*
|
||||
nsCookieService::GetXPCOMSingleton()
|
||||
{
|
||||
@ -643,9 +576,6 @@ nsCookieService::Init()
|
||||
PrefChanged(prefBranch);
|
||||
}
|
||||
|
||||
mStorageService = do_GetService("@mozilla.org/storage/service;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// failure here is non-fatal (we can run fine without
|
||||
// persistent storage - e.g. if there's no profile)
|
||||
rv = InitDB();
|
||||
@ -724,10 +654,13 @@ nsCookieService::TryInitDB(PRBool aDeleteExistingDB)
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
nsCOMPtr<mozIStorageService> storage = do_GetService("@mozilla.org/storage/service;1");
|
||||
if (!storage)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
// open a connection to the cookie database, and only cache our connection
|
||||
// and statements upon success. The connection is opened shared such that
|
||||
// the main and background threads can operate on the db concurrently.
|
||||
rv = mStorageService->OpenDatabase(cookieFile, getter_AddRefs(mDBState->dbConn));
|
||||
// and statements upon success.
|
||||
rv = storage->OpenUnsharedDatabase(cookieFile, getter_AddRefs(mDBState->dbConn));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRBool tableExists = PR_FALSE;
|
||||
@ -968,13 +901,6 @@ 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) {
|
||||
CancelAsyncRead(PR_TRUE);
|
||||
mDefaultDBState.syncConn = nsnull;
|
||||
}
|
||||
|
||||
mDefaultDBState.dbConn->AsyncClose(mCloseListener);
|
||||
mDefaultDBState.dbConn = nsnull;
|
||||
}
|
||||
@ -1001,7 +927,9 @@ nsCookieService::Observe(nsISupports *aSubject,
|
||||
if (mDBState->dbConn) {
|
||||
if (!nsCRT::strcmp(aData, NS_LITERAL_STRING("shutdown-cleanse").get())) {
|
||||
// clear the cookie file
|
||||
RemoveAll();
|
||||
nsresult rv = mDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_cookies"));
|
||||
if (NS_FAILED(rv))
|
||||
NS_WARNING("db delete failed");
|
||||
}
|
||||
|
||||
// Close the DB connection before changing
|
||||
@ -1254,15 +1182,16 @@ nsCookieService::RemoveAll()
|
||||
if (mDBState->dbConn) {
|
||||
NS_ASSERTION(mDBState == &mDefaultDBState, "not in default DB state");
|
||||
|
||||
// XXX Ignore corruption for now. See bug 547031.
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
nsresult rv = mDefaultDBState.dbConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"DELETE FROM moz_cookies"), getter_AddRefs(stmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsresult rv = mDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_cookies"));
|
||||
if (NS_FAILED(rv)) {
|
||||
// Database must be corrupted, so remove it completely.
|
||||
nsCOMPtr<nsIFile> dbFile;
|
||||
mDBState->dbConn->GetDatabaseFile(getter_AddRefs(dbFile));
|
||||
CloseDB();
|
||||
dbFile->Remove(PR_FALSE);
|
||||
|
||||
nsCOMPtr<mozIStoragePendingStatement> handle;
|
||||
rv = stmt->ExecuteAsync(mRemoveListener, getter_AddRefs(handle));
|
||||
NS_ASSERT_SUCCESS(rv);
|
||||
InitDB();
|
||||
}
|
||||
}
|
||||
|
||||
NotifyChanged(nsnull, NS_LITERAL_STRING("cleared").get());
|
||||
@ -1303,8 +1232,6 @@ nsCookieService::GetEnumerator(nsISimpleEnumerator **aEnumerator)
|
||||
nsCOMArray<nsICookie> cookieList(mDBState->cookieCount);
|
||||
nsGetEnumeratorData data(&cookieList, PR_Now() / PR_USEC_PER_SEC);
|
||||
|
||||
EnsureReadComplete();
|
||||
|
||||
mDBState->hostTable.EnumerateEntries(COMArrayCallback, &data);
|
||||
|
||||
return NS_NewArrayEnumerator(aEnumerator, cookieList);
|
||||
@ -1402,9 +1329,28 @@ nsCookieService::Remove(const nsACString &aHost,
|
||||
nsresult
|
||||
nsCookieService::Read()
|
||||
{
|
||||
// Let the reading begin!
|
||||
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!
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
nsresult rv = mDefaultDBState.dbConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
rv = mDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT "
|
||||
"id, "
|
||||
"name, "
|
||||
@ -1419,261 +1365,49 @@ nsCookieService::Read()
|
||||
"FROM moz_cookies"), getter_AddRefs(stmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsRefPtr<ReadCookieDBListener> readListener = new ReadCookieDBListener;
|
||||
rv = stmt->ExecuteAsync(readListener,
|
||||
getter_AddRefs(mDefaultDBState.pendingRead));
|
||||
NS_ASSERT_SUCCESS(rv);
|
||||
|
||||
mDefaultDBState.readListener = readListener;
|
||||
if (!mDefaultDBState.readSet.IsInitialized())
|
||||
mDefaultDBState.readSet.Init();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Extract data from a single result row and create an nsCookie.
|
||||
// This is templated since 'T' is different for sync vs async results.
|
||||
template<class T> nsCookie*
|
||||
nsCookieService::GetCookieFromRow(T &aRow)
|
||||
{
|
||||
// Skip reading 'baseDomain' -- up to the caller.
|
||||
PRInt64 creationID = aRow->AsInt64(0);
|
||||
|
||||
nsCString name, value, host, path;
|
||||
nsresult rv = aRow->GetUTF8String(1, name);
|
||||
NS_ASSERT_SUCCESS(rv);
|
||||
rv = aRow->GetUTF8String(2, value);
|
||||
NS_ASSERT_SUCCESS(rv);
|
||||
rv = aRow->GetUTF8String(3, host);
|
||||
NS_ASSERT_SUCCESS(rv);
|
||||
rv = aRow->GetUTF8String(4, path);
|
||||
NS_ASSERT_SUCCESS(rv);
|
||||
|
||||
PRInt64 expiry = aRow->AsInt64(5);
|
||||
PRInt64 lastAccessed = aRow->AsInt64(6);
|
||||
PRBool isSecure = 0 != aRow->AsInt32(7);
|
||||
PRBool isHttpOnly = 0 != aRow->AsInt32(8);
|
||||
|
||||
// create a new nsCookie and assign the data.
|
||||
return nsCookie::Create(name, value, host, path,
|
||||
expiry,
|
||||
lastAccessed,
|
||||
creationID,
|
||||
PR_FALSE,
|
||||
isSecure,
|
||||
isHttpOnly);
|
||||
}
|
||||
|
||||
void
|
||||
nsCookieService::AsyncReadComplete()
|
||||
{
|
||||
NS_ASSERTION(mDBState == &mDefaultDBState, "not in default db state");
|
||||
NS_ASSERTION(mDBState->pendingRead, "no pending read");
|
||||
NS_ASSERTION(mDBState->readListener, "no read listener");
|
||||
|
||||
// Merge the data read on the background thread with the data synchronously
|
||||
// read on the main thread. Note that transactions on the cookie table may
|
||||
// have occurred on the main thread since, making the background data stale.
|
||||
for (PRUint32 i = 0; i < mDefaultDBState.hostArray.Length(); ++i) {
|
||||
const CookieDomainTuple &tuple = mDefaultDBState.hostArray[i];
|
||||
|
||||
// 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))
|
||||
continue;
|
||||
|
||||
AddCookieToList(tuple.baseDomain, tuple.cookie, NULL, PR_FALSE);
|
||||
}
|
||||
|
||||
mDefaultDBState.stmtReadDomain = nsnull;
|
||||
mDefaultDBState.pendingRead = nsnull;
|
||||
mDefaultDBState.readListener = nsnull;
|
||||
mDefaultDBState.syncConn = nsnull;
|
||||
mDefaultDBState.hostArray.Clear();
|
||||
mDefaultDBState.readSet.Clear();
|
||||
|
||||
mObserverService->NotifyObservers(nsnull, "cookie-db-read", nsnull);
|
||||
|
||||
COOKIE_LOGSTRING(PR_LOG_DEBUG, ("Read(): %ld cookies read",
|
||||
mDefaultDBState.cookieCount));
|
||||
}
|
||||
|
||||
void
|
||||
nsCookieService::CancelAsyncRead(PRBool aPurgeReadSet)
|
||||
{
|
||||
NS_ASSERTION(mDBState == &mDefaultDBState, "not in default db state");
|
||||
NS_ASSERTION(mDBState->pendingRead, "no pending read");
|
||||
NS_ASSERTION(mDBState->readListener, "no read listener");
|
||||
|
||||
// Cancel the pending read, kill the read listener, and empty the array
|
||||
// of data already read in on the background thread.
|
||||
mDefaultDBState.readListener->Cancel();
|
||||
nsresult rv = mDefaultDBState.pendingRead->Cancel();
|
||||
NS_ASSERT_SUCCESS(rv);
|
||||
|
||||
mDefaultDBState.stmtReadDomain = nsnull;
|
||||
mDefaultDBState.pendingRead = nsnull;
|
||||
mDefaultDBState.readListener = nsnull;
|
||||
mDefaultDBState.hostArray.Clear();
|
||||
|
||||
// Only clear the 'readSet' table if we no longer need to know what set of
|
||||
// data is already accounted for.
|
||||
if (aPurgeReadSet)
|
||||
mDefaultDBState.readSet.Clear();
|
||||
}
|
||||
|
||||
mozIStorageConnection*
|
||||
nsCookieService::GetSyncDBConn()
|
||||
{
|
||||
NS_ASSERTION(!mDefaultDBState.syncConn, "already have sync db connection");
|
||||
|
||||
// Start a new connection for sync reads to reduce contention with the
|
||||
// background thread.
|
||||
nsCOMPtr<nsIFile> cookieFile;
|
||||
mDefaultDBState.dbConn->GetDatabaseFile(getter_AddRefs(cookieFile));
|
||||
NS_ASSERTION(cookieFile, "no cookie file on connection");
|
||||
|
||||
mStorageService->OpenDatabase(cookieFile,
|
||||
getter_AddRefs(mDefaultDBState.syncConn));
|
||||
NS_ASSERTION(mDefaultDBState.syncConn, "can't open sync db connection");
|
||||
return mDefaultDBState.syncConn;
|
||||
}
|
||||
|
||||
void
|
||||
nsCookieService::EnsureReadDomain(const nsCString &aBaseDomain)
|
||||
{
|
||||
NS_ASSERTION(!mDBState->dbConn || mDBState == &mDefaultDBState,
|
||||
"not in default db state");
|
||||
|
||||
// Fast path 1: nothing to read, or we've already finished reading.
|
||||
if (NS_LIKELY(!mDBState->dbConn || !mDefaultDBState.pendingRead))
|
||||
return;
|
||||
|
||||
// Fast path 2: already read in this particular domain.
|
||||
if (NS_LIKELY(mDefaultDBState.readSet.GetEntry(aBaseDomain)))
|
||||
return;
|
||||
|
||||
// Read in the data synchronously.
|
||||
nsresult rv;
|
||||
if (!mDefaultDBState.stmtReadDomain) {
|
||||
if (!GetSyncDBConn())
|
||||
return;
|
||||
|
||||
// Cache the statement, since it's likely to be used again.
|
||||
rv = mDefaultDBState.syncConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT "
|
||||
"id, "
|
||||
"name, "
|
||||
"value, "
|
||||
"host, "
|
||||
"path, "
|
||||
"expiry, "
|
||||
"lastAccessed, "
|
||||
"isSecure, "
|
||||
"isHttpOnly "
|
||||
"FROM moz_cookies "
|
||||
"WHERE baseDomain = ?1"),
|
||||
getter_AddRefs(mDefaultDBState.stmtReadDomain));
|
||||
|
||||
// XXX Ignore corruption for now. See bug 547031.
|
||||
if (NS_FAILED(rv)) return;
|
||||
}
|
||||
|
||||
NS_ASSERTION(mDefaultDBState.syncConn, "should have a sync db connection");
|
||||
|
||||
mozStorageStatementScoper scoper(mDefaultDBState.stmtReadDomain);
|
||||
|
||||
rv = mDefaultDBState.stmtReadDomain->BindUTF8StringByIndex(0, aBaseDomain);
|
||||
NS_ASSERT_SUCCESS(rv);
|
||||
|
||||
nsCAutoString baseDomain, name, value, host, path;
|
||||
PRBool hasResult;
|
||||
PRUint32 readCount = 0;
|
||||
nsCString name, value, host, path;
|
||||
while (1) {
|
||||
rv = mDefaultDBState.stmtReadDomain->ExecuteStep(&hasResult);
|
||||
// XXX Ignore corruption for now. See bug 547031.
|
||||
if (NS_FAILED(rv)) return;
|
||||
while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&hasResult)) && hasResult) {
|
||||
PRInt64 creationID = stmt->AsInt64(0);
|
||||
|
||||
if (!hasResult)
|
||||
break;
|
||||
stmt->GetUTF8String(1, name);
|
||||
stmt->GetUTF8String(2, value);
|
||||
stmt->GetUTF8String(3, host);
|
||||
stmt->GetUTF8String(4, path);
|
||||
|
||||
nsCookie* newCookie = GetCookieFromRow(mDefaultDBState.stmtReadDomain);
|
||||
AddCookieToList(aBaseDomain, newCookie, NULL, PR_FALSE);
|
||||
++readCount;
|
||||
}
|
||||
PRInt64 expiry = stmt->AsInt64(5);
|
||||
PRInt64 lastAccessed = stmt->AsInt64(6);
|
||||
PRBool isSecure = 0 != stmt->AsInt32(7);
|
||||
PRBool isHttpOnly = 0 != stmt->AsInt32(8);
|
||||
|
||||
// Add it to the hashset of read entries, so we don't read it again.
|
||||
mDefaultDBState.readSet.PutEntry(aBaseDomain);
|
||||
|
||||
COOKIE_LOGSTRING(PR_LOG_DEBUG,
|
||||
("EnsureReadDomain(): %ld cookies read for base domain %s",
|
||||
readCount, aBaseDomain.get()));
|
||||
}
|
||||
|
||||
void
|
||||
nsCookieService::EnsureReadComplete()
|
||||
{
|
||||
NS_ASSERTION(!mDBState->dbConn || mDBState == &mDefaultDBState,
|
||||
"not in default db state");
|
||||
|
||||
// Fast path 1: nothing to read, or we've already finished reading.
|
||||
if (NS_LIKELY(!mDBState->dbConn || !mDefaultDBState.pendingRead))
|
||||
return;
|
||||
|
||||
// Cancel the pending read, so we don't get any more results.
|
||||
CancelAsyncRead(PR_FALSE);
|
||||
|
||||
if (!mDefaultDBState.syncConn && !GetSyncDBConn())
|
||||
return;
|
||||
|
||||
// Read in the data synchronously.
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
nsresult rv = mDefaultDBState.syncConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT "
|
||||
"id, "
|
||||
"name, "
|
||||
"value, "
|
||||
"host, "
|
||||
"path, "
|
||||
"expiry, "
|
||||
"lastAccessed, "
|
||||
"isSecure, "
|
||||
"isHttpOnly, "
|
||||
"baseDomain "
|
||||
"FROM moz_cookies"), getter_AddRefs(stmt));
|
||||
|
||||
// XXX Ignore corruption for now. See bug 547031.
|
||||
if (NS_FAILED(rv)) return;
|
||||
|
||||
nsCString baseDomain, name, value, host, path;
|
||||
PRBool hasResult;
|
||||
PRUint32 readCount = 0;
|
||||
while (1) {
|
||||
rv = stmt->ExecuteStep(&hasResult);
|
||||
// XXX Ignore corruption for now. See bug 547031.
|
||||
if (NS_FAILED(rv)) return;
|
||||
|
||||
if (!hasResult)
|
||||
break;
|
||||
|
||||
// Make sure we haven't already read the data.
|
||||
stmt->GetUTF8String(9, baseDomain);
|
||||
if (mDefaultDBState.readSet.GetEntry(baseDomain))
|
||||
continue;
|
||||
|
||||
nsCookie* newCookie = GetCookieFromRow(stmt);
|
||||
AddCookieToList(baseDomain, newCookie, NULL, PR_FALSE);
|
||||
++readCount;
|
||||
// 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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
mDefaultDBState.syncConn = nsnull;
|
||||
mDefaultDBState.readSet.Clear();
|
||||
COOKIE_LOGSTRING(PR_LOG_DEBUG, ("Read(): %ld cookies read", mDBState->cookieCount));
|
||||
|
||||
mObserverService->NotifyObservers(nsnull, "cookie-db-read", nsnull);
|
||||
|
||||
COOKIE_LOGSTRING(PR_LOG_DEBUG,
|
||||
("EnsureReadComplete(): %ld cookies read", readCount));
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -1727,9 +1461,6 @@ nsCookieService::ImportCookies(nsIFile *aCookieFile)
|
||||
*
|
||||
*/
|
||||
|
||||
// First, ensure we've read in everything from the database, if we have one.
|
||||
EnsureReadComplete();
|
||||
|
||||
// We will likely be adding a bunch of cookies to the DB, so we use async
|
||||
// batching with storage to make this super fast.
|
||||
nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
|
||||
@ -1926,8 +1657,6 @@ nsCookieService::GetCookieInternal(nsIURI *aHostURI,
|
||||
PRInt64 currentTime = currentTimeInUsec / PR_USEC_PER_SEC;
|
||||
PRBool stale = PR_FALSE;
|
||||
|
||||
EnsureReadDomain(baseDomain);
|
||||
|
||||
// perform the hash lookup
|
||||
nsCookieEntry *entry = mDBState->hostTable.GetEntry(baseDomain);
|
||||
if (!entry)
|
||||
@ -2987,8 +2716,6 @@ nsCookieService::PurgeCookies(PRInt64 aCurrentTimeInUsec)
|
||||
stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
|
||||
}
|
||||
|
||||
EnsureReadComplete();
|
||||
|
||||
nsPurgeData data(aCurrentTimeInUsec / PR_USEC_PER_SEC,
|
||||
aCurrentTimeInUsec - mCookiePurgeAge, purgeList, removedList, paramsArray);
|
||||
mDBState->hostTable.EnumerateEntries(purgeCookiesCallback, &data);
|
||||
@ -3081,8 +2808,6 @@ PRUint32
|
||||
nsCookieService::CountCookiesFromHostInternal(const nsCString &aBaseDomain,
|
||||
nsEnumerationData &aData)
|
||||
{
|
||||
EnsureReadDomain(aBaseDomain);
|
||||
|
||||
nsCookieEntry *entry = mDBState->hostTable.GetEntry(aBaseDomain);
|
||||
if (!entry)
|
||||
return 0;
|
||||
@ -3146,8 +2871,6 @@ nsCookieService::GetCookiesFromHost(const nsACString &aHost,
|
||||
nsCOMArray<nsICookie> cookieList(mMaxCookiesPerHost);
|
||||
PRInt64 currentTime = PR_Now() / PR_USEC_PER_SEC;
|
||||
|
||||
EnsureReadDomain(baseDomain);
|
||||
|
||||
nsCookieEntry *entry = mDBState->hostTable.GetEntry(baseDomain);
|
||||
if (!entry)
|
||||
return NS_NewEmptyEnumerator(aEnumerator);
|
||||
@ -3173,8 +2896,6 @@ nsCookieService::FindCookie(const nsCString &aBaseDomain,
|
||||
nsListIter &aIter,
|
||||
PRInt64 aCurrentTime)
|
||||
{
|
||||
EnsureReadDomain(aBaseDomain);
|
||||
|
||||
nsCookieEntry *entry = mDBState->hostTable.GetEntry(aBaseDomain);
|
||||
if (!entry)
|
||||
return PR_FALSE;
|
||||
@ -3294,7 +3015,7 @@ bindCookieParameters(mozIStorageBindingParamsArray *aParamsArray,
|
||||
NS_ASSERT_SUCCESS(rv);
|
||||
}
|
||||
|
||||
void
|
||||
PRBool
|
||||
nsCookieService::AddCookieToList(const nsCString &aBaseDomain,
|
||||
nsCookie *aCookie,
|
||||
mozIStorageBindingParamsArray *aParamsArray,
|
||||
@ -3306,7 +3027,10 @@ nsCookieService::AddCookieToList(const nsCString &aBaseDomain,
|
||||
"Do not have a DB connection but have a params array?");
|
||||
|
||||
nsCookieEntry *entry = mDBState->hostTable.PutEntry(aBaseDomain);
|
||||
NS_ASSERTION(entry, "can't insert element into a null entry!");
|
||||
if (!entry) {
|
||||
NS_ERROR("can't insert element into a null entry!");
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
entry->GetCookies().AppendElement(aCookie);
|
||||
++mDBState->cookieCount;
|
||||
@ -3334,6 +3058,8 @@ nsCookieService::AddCookieToList(const nsCString &aBaseDomain,
|
||||
NS_ASSERT_SUCCESS(rv);
|
||||
}
|
||||
}
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -54,9 +54,7 @@
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsTHashtable.h"
|
||||
#include "mozIStorageStatement.h"
|
||||
#include "mozIStoragePendingStatement.h"
|
||||
#include "mozIStorageConnection.h"
|
||||
#include "mozIStorageRow.h"
|
||||
|
||||
class nsICookiePermission;
|
||||
class nsIEffectiveTLDService;
|
||||
@ -65,10 +63,9 @@ class nsIPrefBranch;
|
||||
class nsIObserverService;
|
||||
class nsIURI;
|
||||
class nsIChannel;
|
||||
class mozIStorageService;
|
||||
class DBListenerErrorHandler;
|
||||
class mozIStorageStatementCallback;
|
||||
class mozIStorageCompletionCallback;
|
||||
class ReadCookieDBListener;
|
||||
|
||||
struct nsCookieAttributes;
|
||||
struct nsListIter;
|
||||
@ -138,13 +135,6 @@ 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
|
||||
@ -158,22 +148,6 @@ struct DBState
|
||||
nsCOMPtr<mozIStorageStatement> stmtInsert;
|
||||
nsCOMPtr<mozIStorageStatement> stmtDelete;
|
||||
nsCOMPtr<mozIStorageStatement> stmtUpdate;
|
||||
|
||||
// Various parts representing asynchronous read state. These are useful
|
||||
// while the background read is taking place.
|
||||
nsCOMPtr<mozIStorageConnection> syncConn;
|
||||
nsCOMPtr<mozIStorageStatement> stmtReadDomain;
|
||||
nsCOMPtr<mozIStoragePendingStatement> pendingRead;
|
||||
// The asynchronous read listener. This is a weak ref (storage has ownership)
|
||||
// since it may need to outlive the DBState's database connection.
|
||||
ReadCookieDBListener* readListener;
|
||||
// An array of (baseDomain, cookie) tuples representing data read in
|
||||
// asynchronously. This is merged into hostTable once read is complete.
|
||||
nsTArray<CookieDomainTuple> hostArray;
|
||||
// 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;
|
||||
};
|
||||
|
||||
// these constants represent a decision about a cookie based on user prefs.
|
||||
@ -219,12 +193,6 @@ class nsCookieService : public nsICookieService
|
||||
nsresult CreateTable();
|
||||
void CloseDB();
|
||||
nsresult Read();
|
||||
template<class T> nsCookie* GetCookieFromRow(T &aRow);
|
||||
void AsyncReadComplete();
|
||||
void CancelAsyncRead(PRBool aPurgeReadSet);
|
||||
mozIStorageConnection* GetSyncDBConn();
|
||||
void EnsureReadDomain(const nsCString &aBaseDomain);
|
||||
void EnsureReadComplete();
|
||||
nsresult NormalizeHost(nsCString &aHost);
|
||||
nsresult GetBaseDomain(nsIURI *aHostURI, nsCString &aBaseDomain, PRBool &aRequireHostMatch);
|
||||
nsresult GetBaseDomainFromHost(const nsACString &aHost, nsCString &aBaseDomain);
|
||||
@ -233,7 +201,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);
|
||||
void AddCookieToList(const nsCString& aBaseDomain, nsCookie *aCookie, mozIStorageBindingParamsArray *aParamsArray, PRBool aWriteToDB = PR_TRUE);
|
||||
PRBool 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);
|
||||
@ -256,7 +224,6 @@ class nsCookieService : public nsICookieService
|
||||
nsCOMPtr<nsICookiePermission> mPermissionService;
|
||||
nsCOMPtr<nsIEffectiveTLDService> mTLDService;
|
||||
nsCOMPtr<nsIIDNService> mIDNService;
|
||||
nsCOMPtr<mozIStorageService> mStorageService;
|
||||
|
||||
// we have two separate DB states: one for normal browsing and one for
|
||||
// private browsing, switching between them as appropriate. this state
|
||||
@ -280,9 +247,8 @@ class nsCookieService : public nsICookieService
|
||||
PRUint16 mMaxCookiesPerHost;
|
||||
PRInt64 mCookiePurgeAge;
|
||||
|
||||
// friends!
|
||||
// this callback needs access to member functions
|
||||
friend PLDHashOperator purgeCookiesCallback(nsCookieEntry *aEntry, void *aArg);
|
||||
friend class ReadCookieDBListener;
|
||||
|
||||
static nsCookieService* GetSingleton();
|
||||
#ifdef MOZ_IPC
|
||||
|
@ -783,7 +783,7 @@ Statement::GetUTF8String(PRUint32 aIndex,
|
||||
nsresult rv = GetTypeOfIndex(aIndex, &type);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (type == mozIStorageStatement::VALUE_TYPE_NULL) {
|
||||
// NULL columns should have IsVoid set to distinguish them from the empty
|
||||
// NULL columns should have IsVod set to distinguis them from an empty
|
||||
// string.
|
||||
_value.Truncate(0);
|
||||
_value.SetIsVoid(PR_TRUE);
|
||||
@ -806,7 +806,7 @@ Statement::GetString(PRUint32 aIndex,
|
||||
nsresult rv = GetTypeOfIndex(aIndex, &type);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (type == mozIStorageStatement::VALUE_TYPE_NULL) {
|
||||
// NULL columns should have IsVoid set to distinguish them from the empty
|
||||
// NULL columns should have IsVod set to distinguis them from an empty
|
||||
// string.
|
||||
_value.Truncate(0);
|
||||
_value.SetIsVoid(PR_TRUE);
|
||||
|
Loading…
Reference in New Issue
Block a user