1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "CookieCommons.h"
8 #include "CookieLogging.h"
9 #include "CookiePersistentStorage.h"
11 #include "mozilla/FileUtils.h"
12 #include "mozilla/glean/GleanMetrics.h"
13 #include "mozilla/ScopeExit.h"
14 #include "mozilla/Telemetry.h"
15 #include "mozIStorageAsyncStatement.h"
16 #include "mozIStorageError.h"
17 #include "mozIStorageFunction.h"
18 #include "mozIStorageService.h"
19 #include "mozStorageHelper.h"
20 #include "nsAppDirectoryServiceDefs.h"
21 #include "nsICookieNotification.h"
22 #include "nsICookieService.h"
23 #include "nsIEffectiveTLDService.h"
24 #include "nsILineInputStream.h"
25 #include "nsIURIMutator.h"
26 #include "nsNetUtil.h"
27 #include "nsVariant.h"
30 // XXX_hack. See bug 178993.
31 // This is a hack to hide HttpOnly cookies from older browsers
32 #define HTTP_ONLY_PREFIX "#HttpOnly_"
34 constexpr auto COOKIES_SCHEMA_VERSION
= 13;
36 // parameter indexes; see |Read|
37 constexpr auto IDX_NAME
= 0;
38 constexpr auto IDX_VALUE
= 1;
39 constexpr auto IDX_HOST
= 2;
40 constexpr auto IDX_PATH
= 3;
41 constexpr auto IDX_EXPIRY
= 4;
42 constexpr auto IDX_LAST_ACCESSED
= 5;
43 constexpr auto IDX_CREATION_TIME
= 6;
44 constexpr auto IDX_SECURE
= 7;
45 constexpr auto IDX_HTTPONLY
= 8;
46 constexpr auto IDX_ORIGIN_ATTRIBUTES
= 9;
47 constexpr auto IDX_SAME_SITE
= 10;
48 constexpr auto IDX_RAW_SAME_SITE
= 11;
49 constexpr auto IDX_SCHEME_MAP
= 12;
50 constexpr auto IDX_PARTITIONED_ATTRIBUTE_SET
= 13;
52 #define COOKIES_FILE "cookies.sqlite"
59 void BindCookieParameters(mozIStorageBindingParamsArray
* aParamsArray
,
60 const CookieKey
& aKey
, const Cookie
* aCookie
) {
61 NS_ASSERTION(aParamsArray
,
62 "Null params array passed to BindCookieParameters!");
63 NS_ASSERTION(aCookie
, "Null cookie passed to BindCookieParameters!");
65 // Use the asynchronous binding methods to ensure that we do not acquire the
67 nsCOMPtr
<mozIStorageBindingParams
> params
;
68 DebugOnly
<nsresult
> rv
=
69 aParamsArray
->NewBindingParams(getter_AddRefs(params
));
70 MOZ_ASSERT(NS_SUCCEEDED(rv
));
73 aKey
.mOriginAttributes
.CreateSuffix(suffix
);
74 rv
= params
->BindUTF8StringByName("originAttributes"_ns
, suffix
);
75 MOZ_ASSERT(NS_SUCCEEDED(rv
));
77 rv
= params
->BindUTF8StringByName("name"_ns
, aCookie
->Name());
78 MOZ_ASSERT(NS_SUCCEEDED(rv
));
80 rv
= params
->BindUTF8StringByName("value"_ns
, aCookie
->Value());
81 MOZ_ASSERT(NS_SUCCEEDED(rv
));
83 rv
= params
->BindUTF8StringByName("host"_ns
, aCookie
->Host());
84 MOZ_ASSERT(NS_SUCCEEDED(rv
));
86 rv
= params
->BindUTF8StringByName("path"_ns
, aCookie
->Path());
87 MOZ_ASSERT(NS_SUCCEEDED(rv
));
89 rv
= params
->BindInt64ByName("expiry"_ns
, aCookie
->Expiry());
90 MOZ_ASSERT(NS_SUCCEEDED(rv
));
92 rv
= params
->BindInt64ByName("lastAccessed"_ns
, aCookie
->LastAccessed());
93 MOZ_ASSERT(NS_SUCCEEDED(rv
));
95 rv
= params
->BindInt64ByName("creationTime"_ns
, aCookie
->CreationTime());
96 MOZ_ASSERT(NS_SUCCEEDED(rv
));
98 rv
= params
->BindInt32ByName("isSecure"_ns
, aCookie
->IsSecure());
99 MOZ_ASSERT(NS_SUCCEEDED(rv
));
101 rv
= params
->BindInt32ByName("isHttpOnly"_ns
, aCookie
->IsHttpOnly());
102 MOZ_ASSERT(NS_SUCCEEDED(rv
));
104 rv
= params
->BindInt32ByName("sameSite"_ns
, aCookie
->SameSite());
105 MOZ_ASSERT(NS_SUCCEEDED(rv
));
107 rv
= params
->BindInt32ByName("rawSameSite"_ns
, aCookie
->RawSameSite());
108 MOZ_ASSERT(NS_SUCCEEDED(rv
));
110 rv
= params
->BindInt32ByName("schemeMap"_ns
, aCookie
->SchemeMap());
111 MOZ_ASSERT(NS_SUCCEEDED(rv
));
113 rv
= params
->BindInt32ByName("isPartitionedAttributeSet"_ns
,
114 aCookie
->RawIsPartitioned());
115 MOZ_ASSERT(NS_SUCCEEDED(rv
));
117 // Bind the params to the array.
118 rv
= aParamsArray
->AddParams(params
);
119 MOZ_ASSERT(NS_SUCCEEDED(rv
));
122 class ConvertAppIdToOriginAttrsSQLFunction final
: public mozIStorageFunction
{
123 ~ConvertAppIdToOriginAttrsSQLFunction() = default;
126 NS_DECL_MOZISTORAGEFUNCTION
129 NS_IMPL_ISUPPORTS(ConvertAppIdToOriginAttrsSQLFunction
, mozIStorageFunction
);
132 ConvertAppIdToOriginAttrsSQLFunction::OnFunctionCall(
133 mozIStorageValueArray
* aFunctionArguments
, nsIVariant
** aResult
) {
135 OriginAttributes attrs
;
136 nsAutoCString suffix
;
137 attrs
.CreateSuffix(suffix
);
139 RefPtr
<nsVariant
> outVar(new nsVariant());
140 rv
= outVar
->SetAsAUTF8String(suffix
);
141 NS_ENSURE_SUCCESS(rv
, rv
);
143 outVar
.forget(aResult
);
147 class SetAppIdFromOriginAttributesSQLFunction final
148 : public mozIStorageFunction
{
149 ~SetAppIdFromOriginAttributesSQLFunction() = default;
152 NS_DECL_MOZISTORAGEFUNCTION
155 NS_IMPL_ISUPPORTS(SetAppIdFromOriginAttributesSQLFunction
, mozIStorageFunction
);
158 SetAppIdFromOriginAttributesSQLFunction::OnFunctionCall(
159 mozIStorageValueArray
* aFunctionArguments
, nsIVariant
** aResult
) {
161 nsAutoCString suffix
;
162 OriginAttributes attrs
;
164 rv
= aFunctionArguments
->GetUTF8String(0, suffix
);
165 NS_ENSURE_SUCCESS(rv
, rv
);
166 bool success
= attrs
.PopulateFromSuffix(suffix
);
167 NS_ENSURE_TRUE(success
, NS_ERROR_FAILURE
);
169 RefPtr
<nsVariant
> outVar(new nsVariant());
170 rv
= outVar
->SetAsInt32(0); // deprecated appId!
171 NS_ENSURE_SUCCESS(rv
, rv
);
173 outVar
.forget(aResult
);
177 class SetInBrowserFromOriginAttributesSQLFunction final
178 : public mozIStorageFunction
{
179 ~SetInBrowserFromOriginAttributesSQLFunction() = default;
182 NS_DECL_MOZISTORAGEFUNCTION
185 NS_IMPL_ISUPPORTS(SetInBrowserFromOriginAttributesSQLFunction
,
186 mozIStorageFunction
);
189 SetInBrowserFromOriginAttributesSQLFunction::OnFunctionCall(
190 mozIStorageValueArray
* aFunctionArguments
, nsIVariant
** aResult
) {
192 nsAutoCString suffix
;
193 OriginAttributes attrs
;
195 rv
= aFunctionArguments
->GetUTF8String(0, suffix
);
196 NS_ENSURE_SUCCESS(rv
, rv
);
197 bool success
= attrs
.PopulateFromSuffix(suffix
);
198 NS_ENSURE_TRUE(success
, NS_ERROR_FAILURE
);
200 RefPtr
<nsVariant
> outVar(new nsVariant());
201 rv
= outVar
->SetAsInt32(false);
202 NS_ENSURE_SUCCESS(rv
, rv
);
204 outVar
.forget(aResult
);
208 /******************************************************************************
209 * DBListenerErrorHandler impl:
210 * Parent class for our async storage listeners that handles the logging of
212 ******************************************************************************/
213 class DBListenerErrorHandler
: public mozIStorageStatementCallback
{
215 explicit DBListenerErrorHandler(CookiePersistentStorage
* dbState
)
216 : mStorage(dbState
) {}
217 RefPtr
<CookiePersistentStorage
> mStorage
;
218 virtual const char* GetOpType() = 0;
221 NS_IMETHOD
HandleError(mozIStorageError
* aError
) override
{
222 if (MOZ_LOG_TEST(gCookieLog
, LogLevel::Warning
)) {
224 aError
->GetResult(&result
);
226 nsAutoCString message
;
227 aError
->GetMessage(message
);
230 ("DBListenerErrorHandler::HandleError(): Error %d occurred while "
231 "performing operation '%s' with message '%s'; rebuilding database.",
232 result
, GetOpType(), message
.get()));
235 // Rebuild the database.
236 mStorage
->HandleCorruptDB();
242 /******************************************************************************
243 * InsertCookieDBListener impl:
244 * mozIStorageStatementCallback used to track asynchronous insertion operations.
245 ******************************************************************************/
246 class InsertCookieDBListener final
: public DBListenerErrorHandler
{
248 const char* GetOpType() override
{ return "INSERT"; }
250 ~InsertCookieDBListener() = default;
255 explicit InsertCookieDBListener(CookiePersistentStorage
* dbState
)
256 : DBListenerErrorHandler(dbState
) {}
257 NS_IMETHOD
HandleResult(mozIStorageResultSet
* /*aResultSet*/) override
{
258 MOZ_ASSERT_UNREACHABLE(
259 "Unexpected call to "
260 "InsertCookieDBListener::HandleResult");
263 NS_IMETHOD
HandleCompletion(uint16_t aReason
) override
{
264 // If we were rebuilding the db and we succeeded, make our mCorruptFlag say
266 if (mStorage
->GetCorruptFlag() == CookiePersistentStorage::REBUILDING
&&
267 aReason
== mozIStorageStatementCallback::REASON_FINISHED
) {
270 ("InsertCookieDBListener::HandleCompletion(): rebuild complete"));
271 mStorage
->SetCorruptFlag(CookiePersistentStorage::OK
);
274 // This notification is just for testing.
275 nsCOMPtr
<nsIObserverService
> os
= services::GetObserverService();
277 os
->NotifyObservers(nullptr, "cookie-saved-on-disk", nullptr);
284 NS_IMPL_ISUPPORTS(InsertCookieDBListener
, mozIStorageStatementCallback
)
286 /******************************************************************************
287 * UpdateCookieDBListener impl:
288 * mozIStorageStatementCallback used to track asynchronous update operations.
289 ******************************************************************************/
290 class UpdateCookieDBListener final
: public DBListenerErrorHandler
{
292 const char* GetOpType() override
{ return "UPDATE"; }
294 ~UpdateCookieDBListener() = default;
299 explicit UpdateCookieDBListener(CookiePersistentStorage
* dbState
)
300 : DBListenerErrorHandler(dbState
) {}
301 NS_IMETHOD
HandleResult(mozIStorageResultSet
* /*aResultSet*/) override
{
302 MOZ_ASSERT_UNREACHABLE(
303 "Unexpected call to "
304 "UpdateCookieDBListener::HandleResult");
307 NS_IMETHOD
HandleCompletion(uint16_t /*aReason*/) override
{ return NS_OK
; }
310 NS_IMPL_ISUPPORTS(UpdateCookieDBListener
, mozIStorageStatementCallback
)
312 /******************************************************************************
313 * RemoveCookieDBListener impl:
314 * mozIStorageStatementCallback used to track asynchronous removal operations.
315 ******************************************************************************/
316 class RemoveCookieDBListener final
: public DBListenerErrorHandler
{
318 const char* GetOpType() override
{ return "REMOVE"; }
320 ~RemoveCookieDBListener() = default;
325 explicit RemoveCookieDBListener(CookiePersistentStorage
* dbState
)
326 : DBListenerErrorHandler(dbState
) {}
327 NS_IMETHOD
HandleResult(mozIStorageResultSet
* /*aResultSet*/) override
{
328 MOZ_ASSERT_UNREACHABLE(
329 "Unexpected call to "
330 "RemoveCookieDBListener::HandleResult");
333 NS_IMETHOD
HandleCompletion(uint16_t /*aReason*/) override
{ return NS_OK
; }
336 NS_IMPL_ISUPPORTS(RemoveCookieDBListener
, mozIStorageStatementCallback
)
338 /******************************************************************************
339 * CloseCookieDBListener imp:
340 * Static mozIStorageCompletionCallback used to notify when the database is
341 * successfully closed.
342 ******************************************************************************/
343 class CloseCookieDBListener final
: public mozIStorageCompletionCallback
{
344 ~CloseCookieDBListener() = default;
347 explicit CloseCookieDBListener(CookiePersistentStorage
* dbState
)
348 : mStorage(dbState
) {}
349 RefPtr
<CookiePersistentStorage
> mStorage
;
352 NS_IMETHOD
Complete(nsresult
/*status*/, nsISupports
* /*value*/) override
{
353 mStorage
->HandleDBClosed();
358 NS_IMPL_ISUPPORTS(CloseCookieDBListener
, mozIStorageCompletionCallback
)
363 already_AddRefed
<CookiePersistentStorage
> CookiePersistentStorage::Create() {
364 RefPtr
<CookiePersistentStorage
> storage
= new CookiePersistentStorage();
368 return storage
.forget();
371 CookiePersistentStorage::CookiePersistentStorage()
372 : mMonitor("CookiePersistentStorage"),
376 void CookiePersistentStorage::NotifyChangedInternal(
377 nsICookieNotification
* aNotification
, bool aOldCookieIsSession
) {
378 MOZ_ASSERT(aNotification
);
379 // Notify for topic "session-cookie-changed" to update the copy of session
380 // cookies in session restore component.
382 nsICookieNotification::Action action
= aNotification
->GetAction();
384 // Filter out notifications for individual non-session cookies.
385 if (action
== nsICookieNotification::COOKIE_CHANGED
||
386 action
== nsICookieNotification::COOKIE_DELETED
||
387 action
== nsICookieNotification::COOKIE_ADDED
) {
388 nsCOMPtr
<nsICookie
> xpcCookie
;
389 DebugOnly
<nsresult
> rv
=
390 aNotification
->GetCookie(getter_AddRefs(xpcCookie
));
391 MOZ_ASSERT(NS_SUCCEEDED(rv
) && xpcCookie
);
392 const Cookie
& cookie
= xpcCookie
->AsCookie();
393 if (!cookie
.IsSession() && !aOldCookieIsSession
) {
398 nsCOMPtr
<nsIObserverService
> os
= services::GetObserverService();
400 os
->NotifyObservers(aNotification
, "session-cookie-changed", u
"");
404 void CookiePersistentStorage::RemoveAllInternal() {
405 // clear the cookie file
407 nsCOMPtr
<mozIStorageAsyncStatement
> stmt
;
408 nsresult rv
= mDBConn
->CreateAsyncStatement("DELETE FROM moz_cookies"_ns
,
409 getter_AddRefs(stmt
));
410 if (NS_SUCCEEDED(rv
)) {
411 nsCOMPtr
<mozIStoragePendingStatement
> handle
;
412 rv
= stmt
->ExecuteAsync(mRemoveListener
, getter_AddRefs(handle
));
413 MOZ_ASSERT(NS_SUCCEEDED(rv
));
415 // Recreate the database.
416 COOKIE_LOGSTRING(LogLevel::Debug
,
417 ("RemoveAll(): corruption detected with rv 0x%" PRIx32
,
418 static_cast<uint32_t>(rv
)));
424 void CookiePersistentStorage::HandleCorruptDB() {
425 COOKIE_LOGSTRING(LogLevel::Debug
,
426 ("HandleCorruptDB(): CookieStorage %p has mCorruptFlag %u",
427 this, mCorruptFlag
));
429 // Mark the database corrupt, so the close listener can begin reconstructing
431 switch (mCorruptFlag
) {
433 // Move to 'closing' state.
434 mCorruptFlag
= CLOSING_FOR_REBUILD
;
436 CleanupCachedStatements();
437 mDBConn
->AsyncClose(mCloseListener
);
438 CleanupDBConnection();
441 case CLOSING_FOR_REBUILD
: {
442 // We had an error while waiting for close completion. That's OK, just
443 // ignore it -- we're rebuilding anyway.
447 // We had an error while rebuilding the DB. Game over. Close the database
448 // and let the close handler do nothing; then we'll move it out of the
450 CleanupCachedStatements();
452 mDBConn
->AsyncClose(mCloseListener
);
454 CleanupDBConnection();
460 void CookiePersistentStorage::RemoveCookiesWithOriginAttributes(
461 const OriginAttributesPattern
& aPattern
, const nsACString
& aBaseDomain
) {
462 mozStorageTransaction
transaction(mDBConn
, false);
464 // XXX Handle the error, bug 1696130.
465 Unused
<< NS_WARN_IF(NS_FAILED(transaction
.Start()));
467 CookieStorage::RemoveCookiesWithOriginAttributes(aPattern
, aBaseDomain
);
469 DebugOnly
<nsresult
> rv
= transaction
.Commit();
470 MOZ_ASSERT(NS_SUCCEEDED(rv
));
473 void CookiePersistentStorage::RemoveCookiesFromExactHost(
474 const nsACString
& aHost
, const nsACString
& aBaseDomain
,
475 const OriginAttributesPattern
& aPattern
) {
476 mozStorageTransaction
transaction(mDBConn
, false);
478 // XXX Handle the error, bug 1696130.
479 Unused
<< NS_WARN_IF(NS_FAILED(transaction
.Start()));
481 CookieStorage::RemoveCookiesFromExactHost(aHost
, aBaseDomain
, aPattern
);
483 DebugOnly
<nsresult
> rv
= transaction
.Commit();
484 MOZ_ASSERT(NS_SUCCEEDED(rv
));
487 void CookiePersistentStorage::RemoveCookieFromDB(const Cookie
& aCookie
) {
488 // if it's a non-session cookie, remove it from the db
489 if (aCookie
.IsSession() || !mDBConn
) {
493 nsCOMPtr
<mozIStorageBindingParamsArray
> paramsArray
;
494 mStmtDelete
->NewBindingParamsArray(getter_AddRefs(paramsArray
));
496 PrepareCookieRemoval(aCookie
, paramsArray
);
498 DebugOnly
<nsresult
> rv
= mStmtDelete
->BindParameters(paramsArray
);
499 MOZ_ASSERT(NS_SUCCEEDED(rv
));
501 nsCOMPtr
<mozIStoragePendingStatement
> handle
;
502 rv
= mStmtDelete
->ExecuteAsync(mRemoveListener
, getter_AddRefs(handle
));
503 MOZ_ASSERT(NS_SUCCEEDED(rv
));
506 void CookiePersistentStorage::PrepareCookieRemoval(
507 const Cookie
& aCookie
, mozIStorageBindingParamsArray
* aParamsArray
) {
508 // if it's a non-session cookie, remove it from the db
509 if (aCookie
.IsSession() || !mDBConn
) {
513 nsCOMPtr
<mozIStorageBindingParams
> params
;
514 aParamsArray
->NewBindingParams(getter_AddRefs(params
));
516 DebugOnly
<nsresult
> rv
=
517 params
->BindUTF8StringByName("name"_ns
, aCookie
.Name());
518 MOZ_ASSERT(NS_SUCCEEDED(rv
));
520 rv
= params
->BindUTF8StringByName("host"_ns
, aCookie
.Host());
521 MOZ_ASSERT(NS_SUCCEEDED(rv
));
523 rv
= params
->BindUTF8StringByName("path"_ns
, aCookie
.Path());
524 MOZ_ASSERT(NS_SUCCEEDED(rv
));
526 nsAutoCString suffix
;
527 aCookie
.OriginAttributesRef().CreateSuffix(suffix
);
528 rv
= params
->BindUTF8StringByName("originAttributes"_ns
, suffix
);
529 MOZ_ASSERT(NS_SUCCEEDED(rv
));
531 rv
= aParamsArray
->AddParams(params
);
532 MOZ_ASSERT(NS_SUCCEEDED(rv
));
535 // Null out the statements.
536 // This must be done before closing the connection.
537 void CookiePersistentStorage::CleanupCachedStatements() {
538 mStmtInsert
= nullptr;
539 mStmtDelete
= nullptr;
540 mStmtUpdate
= nullptr;
543 // Null out the listeners, and the database connection itself. This
544 // will not null out the statements, cancel a pending read or
545 // asynchronously close the connection -- these must be done
546 // beforehand if necessary.
547 void CookiePersistentStorage::CleanupDBConnection() {
548 MOZ_ASSERT(!mStmtInsert
, "mStmtInsert has been cleaned up");
549 MOZ_ASSERT(!mStmtDelete
, "mStmtDelete has been cleaned up");
550 MOZ_ASSERT(!mStmtUpdate
, "mStmtUpdate has been cleaned up");
552 // Null out the database connections. If 'mDBConn' has not been used for any
553 // asynchronous operations yet, this will synchronously close it; otherwise,
554 // it's expected that the caller has performed an AsyncClose prior.
557 // Manually null out our listeners. This is necessary because they hold a
558 // strong ref to the CookieStorage itself. They'll stay alive until whatever
559 // statements are still executing complete.
560 mInsertListener
= nullptr;
561 mUpdateListener
= nullptr;
562 mRemoveListener
= nullptr;
563 mCloseListener
= nullptr;
566 void CookiePersistentStorage::Close() {
572 // Cleanup cached statements before we can close anything.
573 CleanupCachedStatements();
576 // Asynchronously close the connection. We will null it below.
577 mDBConn
->AsyncClose(mCloseListener
);
580 CleanupDBConnection();
582 mInitialized
= false;
583 mInitializedDBConn
= false;
586 void CookiePersistentStorage::StoreCookie(
587 const nsACString
& aBaseDomain
, const OriginAttributes
& aOriginAttributes
,
589 // if it's a non-session cookie and hasn't just been read from the db, write
591 if (aCookie
->IsSession() || !mDBConn
) {
595 nsCOMPtr
<mozIStorageBindingParamsArray
> paramsArray
;
596 mStmtInsert
->NewBindingParamsArray(getter_AddRefs(paramsArray
));
598 CookieKey
key(aBaseDomain
, aOriginAttributes
);
599 BindCookieParameters(paramsArray
, key
, aCookie
);
601 MaybeStoreCookiesToDB(paramsArray
);
604 void CookiePersistentStorage::MaybeStoreCookiesToDB(
605 mozIStorageBindingParamsArray
* aParamsArray
) {
611 aParamsArray
->GetLength(&length
);
616 DebugOnly
<nsresult
> rv
= mStmtInsert
->BindParameters(aParamsArray
);
617 MOZ_ASSERT(NS_SUCCEEDED(rv
));
619 nsCOMPtr
<mozIStoragePendingStatement
> handle
;
620 rv
= mStmtInsert
->ExecuteAsync(mInsertListener
, getter_AddRefs(handle
));
621 MOZ_ASSERT(NS_SUCCEEDED(rv
));
624 void CookiePersistentStorage::StaleCookies(const nsTArray
<Cookie
*>& aCookieList
,
625 int64_t aCurrentTimeInUsec
) {
626 // Create an array of parameters to bind to our update statement. Batching
627 // is OK here since we're updating cookies with no interleaved operations.
628 nsCOMPtr
<mozIStorageBindingParamsArray
> paramsArray
;
629 mozIStorageAsyncStatement
* stmt
= mStmtUpdate
;
631 stmt
->NewBindingParamsArray(getter_AddRefs(paramsArray
));
634 int32_t count
= aCookieList
.Length();
635 for (int32_t i
= 0; i
< count
; ++i
) {
636 Cookie
* cookie
= aCookieList
.ElementAt(i
);
638 if (cookie
->IsStale()) {
639 UpdateCookieInList(cookie
, aCurrentTimeInUsec
, paramsArray
);
642 // Update the database now if necessary.
645 paramsArray
->GetLength(&length
);
647 DebugOnly
<nsresult
> rv
= stmt
->BindParameters(paramsArray
);
648 MOZ_ASSERT(NS_SUCCEEDED(rv
));
650 nsCOMPtr
<mozIStoragePendingStatement
> handle
;
651 rv
= stmt
->ExecuteAsync(mUpdateListener
, getter_AddRefs(handle
));
652 MOZ_ASSERT(NS_SUCCEEDED(rv
));
657 void CookiePersistentStorage::UpdateCookieInList(
658 Cookie
* aCookie
, int64_t aLastAccessed
,
659 mozIStorageBindingParamsArray
* aParamsArray
) {
662 // udpate the lastAccessed timestamp
663 aCookie
->SetLastAccessed(aLastAccessed
);
665 // if it's a non-session cookie, update it in the db too
666 if (!aCookie
->IsSession() && aParamsArray
) {
667 // Create our params holder.
668 nsCOMPtr
<mozIStorageBindingParams
> params
;
669 aParamsArray
->NewBindingParams(getter_AddRefs(params
));
671 // Bind our parameters.
672 DebugOnly
<nsresult
> rv
=
673 params
->BindInt64ByName("lastAccessed"_ns
, aLastAccessed
);
674 MOZ_ASSERT(NS_SUCCEEDED(rv
));
676 rv
= params
->BindUTF8StringByName("name"_ns
, aCookie
->Name());
677 MOZ_ASSERT(NS_SUCCEEDED(rv
));
679 rv
= params
->BindUTF8StringByName("host"_ns
, aCookie
->Host());
680 MOZ_ASSERT(NS_SUCCEEDED(rv
));
682 rv
= params
->BindUTF8StringByName("path"_ns
, aCookie
->Path());
683 MOZ_ASSERT(NS_SUCCEEDED(rv
));
685 nsAutoCString suffix
;
686 aCookie
->OriginAttributesRef().CreateSuffix(suffix
);
687 rv
= params
->BindUTF8StringByName("originAttributes"_ns
, suffix
);
688 MOZ_ASSERT(NS_SUCCEEDED(rv
));
690 // Add our bound parameters to the array.
691 rv
= aParamsArray
->AddParams(params
);
692 MOZ_ASSERT(NS_SUCCEEDED(rv
));
696 void CookiePersistentStorage::DeleteFromDB(
697 mozIStorageBindingParamsArray
* aParamsArray
) {
699 aParamsArray
->GetLength(&length
);
701 DebugOnly
<nsresult
> rv
= mStmtDelete
->BindParameters(aParamsArray
);
702 MOZ_ASSERT(NS_SUCCEEDED(rv
));
704 nsCOMPtr
<mozIStoragePendingStatement
> handle
;
705 rv
= mStmtDelete
->ExecuteAsync(mRemoveListener
, getter_AddRefs(handle
));
706 MOZ_ASSERT(NS_SUCCEEDED(rv
));
710 void CookiePersistentStorage::Activate() {
711 MOZ_ASSERT(!mThread
, "already have a cookie thread");
713 mStorageService
= do_GetService("@mozilla.org/storage/service;1");
714 MOZ_ASSERT(mStorageService
);
716 mTLDService
= do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID
);
717 MOZ_ASSERT(mTLDService
);
719 // Get our cookie file.
720 nsresult rv
= NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
721 getter_AddRefs(mCookieFile
));
723 // We've already set up our CookieStorages appropriately; nothing more to
725 COOKIE_LOGSTRING(LogLevel::Warning
,
726 ("InitCookieStorages(): couldn't get cookie file"));
728 mInitializedDBConn
= true;
733 mCookieFile
->AppendNative(nsLiteralCString(COOKIES_FILE
));
735 NS_ENSURE_SUCCESS_VOID(NS_NewNamedThread("Cookie", getter_AddRefs(mThread
)));
737 RefPtr
<CookiePersistentStorage
> self
= this;
738 nsCOMPtr
<nsIRunnable
> runnable
=
739 NS_NewRunnableFunction("CookiePersistentStorage::Activate", [self
] {
740 MonitorAutoLock
lock(self
->mMonitor
);
742 // Attempt to open and read the database. If TryInitDB() returns
743 // RESULT_RETRY, do so.
744 OpenDBResult result
= self
->TryInitDB(false);
745 if (result
== RESULT_RETRY
) {
746 // Database may be corrupt. Synchronously close the connection, clean
747 // up the default CookieStorage, and try again.
748 COOKIE_LOGSTRING(LogLevel::Warning
,
749 ("InitCookieStorages(): retrying TryInitDB()"));
750 self
->CleanupCachedStatements();
751 self
->CleanupDBConnection();
752 result
= self
->TryInitDB(true);
753 if (result
== RESULT_RETRY
) {
754 // We're done. Change the code to failure so we clean up below.
755 result
= RESULT_FAILURE
;
759 if (result
== RESULT_FAILURE
) {
762 ("InitCookieStorages(): TryInitDB() failed, closing connection"));
764 // Connection failure is unrecoverable. Clean up our connection. We
765 // can run fine without persistent storage -- e.g. if there's no
767 self
->CleanupCachedStatements();
768 self
->CleanupDBConnection();
770 // No need to initialize mDBConn
771 self
->mInitializedDBConn
= true;
774 self
->mInitialized
= true;
776 NS_DispatchToMainThread(
777 NS_NewRunnableFunction("CookiePersistentStorage::InitDBConn",
778 [self
] { self
->InitDBConn(); }));
779 self
->mMonitor
.Notify();
782 mThread
->Dispatch(runnable
, NS_DISPATCH_NORMAL
);
785 /* Attempt to open and read the database. If 'aRecreateDB' is true, try to
786 * move the existing database file out of the way and create a new one.
788 * @returns RESULT_OK if opening or creating the database succeeded;
789 * RESULT_RETRY if the database cannot be opened, is corrupt, or some
790 * other failure occurred that might be resolved by recreating the
791 * database; or RESULT_FAILED if there was an unrecoverable error and
792 * we must run without a database.
794 * If RESULT_RETRY or RESULT_FAILED is returned, the caller should perform
795 * cleanup of the default CookieStorage.
797 CookiePersistentStorage::OpenDBResult
CookiePersistentStorage::TryInitDB(
799 NS_ASSERTION(!mDBConn
, "nonnull mDBConn");
800 NS_ASSERTION(!mStmtInsert
, "nonnull mStmtInsert");
801 NS_ASSERTION(!mInsertListener
, "nonnull mInsertListener");
802 NS_ASSERTION(!mSyncConn
, "nonnull mSyncConn");
803 NS_ASSERTION(NS_GetCurrentThread() == mThread
, "non cookie thread");
805 // Ditch an existing db, if we've been told to (i.e. it's corrupt). We don't
806 // want to delete it outright, since it may be useful for debugging purposes,
807 // so we move it out of the way.
810 nsCOMPtr
<nsIFile
> backupFile
;
811 mCookieFile
->Clone(getter_AddRefs(backupFile
));
812 rv
= backupFile
->MoveToNative(nullptr,
813 nsLiteralCString(COOKIES_FILE
".bak"));
814 NS_ENSURE_SUCCESS(rv
, RESULT_FAILURE
);
817 // This block provides scope for the Telemetry AutoTimer
819 Telemetry::AutoTimer
<Telemetry::MOZ_SQLITE_COOKIES_OPEN_READAHEAD_MS
>
821 ReadAheadFile(mCookieFile
);
823 // open a connection to the cookie database, and only cache our connection
824 // and statements upon success. The connection is opened unshared to
825 // eliminate cache contention between the main and background threads.
826 rv
= mStorageService
->OpenUnsharedDatabase(
827 mCookieFile
, mozIStorageService::CONNECTION_DEFAULT
,
828 getter_AddRefs(mSyncConn
));
829 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
832 auto guard
= MakeScopeExit([&] { mSyncConn
= nullptr; });
834 bool tableExists
= false;
835 mSyncConn
->TableExists("moz_cookies"_ns
, &tableExists
);
838 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
841 // table already exists; check the schema version before reading
842 int32_t dbSchemaVersion
;
843 rv
= mSyncConn
->GetSchemaVersion(&dbSchemaVersion
);
844 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
846 // Start a transaction for the whole migration block.
847 mozStorageTransaction
transaction(mSyncConn
, true);
849 // XXX Handle the error, bug 1696130.
850 Unused
<< NS_WARN_IF(NS_FAILED(transaction
.Start()));
852 switch (dbSchemaVersion
) {
854 // Every time you increment the database schema, you need to implement
855 // the upgrading code from the previous version to the new one. If
856 // migration fails for any reason, it's a bug -- so we return RESULT_RETRY
857 // such that the original database will be saved, in the hopes that we
858 // might one day see it and fix it.
860 // Add the lastAccessed column to the table.
861 rv
= mSyncConn
->ExecuteSimpleSQL(nsLiteralCString(
862 "ALTER TABLE moz_cookies ADD lastAccessed INTEGER"));
863 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
865 // Fall through to the next upgrade.
869 // Add the baseDomain column and index to the table.
870 rv
= mSyncConn
->ExecuteSimpleSQL(
871 "ALTER TABLE moz_cookies ADD baseDomain TEXT"_ns
);
872 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
874 // Compute the baseDomains for the table. This must be done eagerly
875 // otherwise we won't be able to synchronously read in individual
876 // domains on demand.
877 const int64_t SCHEMA2_IDX_ID
= 0;
878 const int64_t SCHEMA2_IDX_HOST
= 1;
879 nsCOMPtr
<mozIStorageStatement
> select
;
880 rv
= mSyncConn
->CreateStatement("SELECT id, host FROM moz_cookies"_ns
,
881 getter_AddRefs(select
));
882 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
884 nsCOMPtr
<mozIStorageStatement
> update
;
885 rv
= mSyncConn
->CreateStatement(
886 nsLiteralCString("UPDATE moz_cookies SET baseDomain = "
887 ":baseDomain WHERE id = :id"),
888 getter_AddRefs(update
));
889 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
891 nsCString baseDomain
;
895 rv
= select
->ExecuteStep(&hasResult
);
896 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
902 int64_t id
= select
->AsInt64(SCHEMA2_IDX_ID
);
903 select
->GetUTF8String(SCHEMA2_IDX_HOST
, host
);
905 rv
= CookieCommons::GetBaseDomainFromHost(mTLDService
, host
,
907 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
909 mozStorageStatementScoper
scoper(update
);
911 rv
= update
->BindUTF8StringByName("baseDomain"_ns
, baseDomain
);
912 MOZ_ASSERT(NS_SUCCEEDED(rv
));
913 rv
= update
->BindInt64ByName("id"_ns
, id
);
914 MOZ_ASSERT(NS_SUCCEEDED(rv
));
916 rv
= update
->ExecuteStep(&hasResult
);
917 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
920 // Create an index on baseDomain.
921 rv
= mSyncConn
->ExecuteSimpleSQL(nsLiteralCString(
922 "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain)"));
923 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
925 // Fall through to the next upgrade.
929 // Add the creationTime column to the table, and create a unique index
930 // on (name, host, path). Before we do this, we have to purge the table
931 // of expired cookies such that we know that the (name, host, path)
932 // index is truly unique -- otherwise we can't create the index. Note
933 // that we can't just execute a statement to delete all rows where the
934 // expiry column is in the past -- doing so would rely on the clock
935 // (both now and when previous cookies were set) being monotonic.
937 // Select the whole table, and order by the fields we're interested in.
938 // This means we can simply do a linear traversal of the results and
939 // check for duplicates as we go.
940 const int64_t SCHEMA3_IDX_ID
= 0;
941 const int64_t SCHEMA3_IDX_NAME
= 1;
942 const int64_t SCHEMA3_IDX_HOST
= 2;
943 const int64_t SCHEMA3_IDX_PATH
= 3;
944 nsCOMPtr
<mozIStorageStatement
> select
;
945 rv
= mSyncConn
->CreateStatement(
947 "SELECT id, name, host, path FROM moz_cookies "
948 "ORDER BY name ASC, host ASC, path ASC, expiry ASC"),
949 getter_AddRefs(select
));
950 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
952 nsCOMPtr
<mozIStorageStatement
> deleteExpired
;
953 rv
= mSyncConn
->CreateStatement(
954 "DELETE FROM moz_cookies WHERE id = :id"_ns
,
955 getter_AddRefs(deleteExpired
));
956 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
958 // Read the first row.
960 rv
= select
->ExecuteStep(&hasResult
);
961 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
967 int64_t id1
= select
->AsInt64(SCHEMA3_IDX_ID
);
968 select
->GetUTF8String(SCHEMA3_IDX_NAME
, name1
);
969 select
->GetUTF8String(SCHEMA3_IDX_HOST
, host1
);
970 select
->GetUTF8String(SCHEMA3_IDX_PATH
, path1
);
976 // Read the second row.
977 rv
= select
->ExecuteStep(&hasResult
);
978 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
984 int64_t id2
= select
->AsInt64(SCHEMA3_IDX_ID
);
985 select
->GetUTF8String(SCHEMA3_IDX_NAME
, name2
);
986 select
->GetUTF8String(SCHEMA3_IDX_HOST
, host2
);
987 select
->GetUTF8String(SCHEMA3_IDX_PATH
, path2
);
989 // If the two rows match in (name, host, path), we know the earlier
990 // row has an earlier expiry time. Delete it.
991 if (name1
== name2
&& host1
== host2
&& path1
== path2
) {
992 mozStorageStatementScoper
scoper(deleteExpired
);
994 rv
= deleteExpired
->BindInt64ByName("id"_ns
, id1
);
995 MOZ_ASSERT(NS_SUCCEEDED(rv
));
997 rv
= deleteExpired
->ExecuteStep(&hasResult
);
998 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1001 // Make the second row the first for the next iteration.
1009 // Add the creationTime column to the table.
1010 rv
= mSyncConn
->ExecuteSimpleSQL(nsLiteralCString(
1011 "ALTER TABLE moz_cookies ADD creationTime INTEGER"));
1012 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1014 // Copy the id of each row into the new creationTime column.
1015 rv
= mSyncConn
->ExecuteSimpleSQL(
1016 nsLiteralCString("UPDATE moz_cookies SET creationTime = "
1017 "(SELECT id WHERE id = moz_cookies.id)"));
1018 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1020 // Create a unique index on (name, host, path) to allow fast lookup.
1021 rv
= mSyncConn
->ExecuteSimpleSQL(
1022 nsLiteralCString("CREATE UNIQUE INDEX moz_uniqueid "
1023 "ON moz_cookies (name, host, path)"));
1024 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1026 // Fall through to the next upgrade.
1030 // We need to add appId/inBrowserElement, plus change a constraint on
1031 // the table (unique entries now include appId/inBrowserElement):
1032 // this requires creating a new table and copying the data to it. We
1033 // then rename the new table to the old name.
1035 // Why we made this change: appId/inBrowserElement allow "cookie jars"
1036 // for Firefox OS. We create a separate cookie namespace per {appId,
1037 // inBrowserElement}. When upgrading, we convert existing cookies
1038 // (which imply we're on desktop/mobile) to use {0, false}, as that is
1039 // the only namespace used by a non-Firefox-OS implementation.
1041 // Rename existing table
1042 rv
= mSyncConn
->ExecuteSimpleSQL(nsLiteralCString(
1043 "ALTER TABLE moz_cookies RENAME TO moz_cookies_old"));
1044 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1046 // Drop existing index (CreateTable will create new one for new table)
1047 rv
= mSyncConn
->ExecuteSimpleSQL("DROP INDEX moz_basedomain"_ns
);
1048 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1050 // Create new table (with new fields and new unique constraint)
1051 rv
= CreateTableForSchemaVersion5();
1052 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1054 // Copy data from old table, using appId/inBrowser=0 for existing rows
1055 rv
= mSyncConn
->ExecuteSimpleSQL(nsLiteralCString(
1056 "INSERT INTO moz_cookies "
1057 "(baseDomain, appId, inBrowserElement, name, value, host, path, "
1059 " lastAccessed, creationTime, isSecure, isHttpOnly) "
1060 "SELECT baseDomain, 0, 0, name, value, host, path, expiry,"
1061 " lastAccessed, creationTime, isSecure, isHttpOnly "
1062 "FROM moz_cookies_old"));
1063 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1066 rv
= mSyncConn
->ExecuteSimpleSQL("DROP TABLE moz_cookies_old"_ns
);
1067 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1069 COOKIE_LOGSTRING(LogLevel::Debug
,
1070 ("Upgraded database to schema version 5"));
1072 // Fall through to the next upgrade.
1076 // Change in the version: Replace the columns |appId| and
1077 // |inBrowserElement| by a single column |originAttributes|.
1079 // Why we made this change: FxOS new security model (NSec) encapsulates
1080 // "appId/inIsolatedMozBrowser" in nsIPrincipal::originAttributes to
1081 // make it easier to modify the contents of this structure in the
1084 // We do the migration in several steps:
1085 // 1. Rename the old table.
1086 // 2. Create a new table.
1087 // 3. Copy data from the old table to the new table; convert appId and
1088 // inBrowserElement to originAttributes in the meantime.
1090 // Rename existing table.
1091 rv
= mSyncConn
->ExecuteSimpleSQL(nsLiteralCString(
1092 "ALTER TABLE moz_cookies RENAME TO moz_cookies_old"));
1093 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1095 // Drop existing index (CreateTable will create new one for new table).
1096 rv
= mSyncConn
->ExecuteSimpleSQL("DROP INDEX moz_basedomain"_ns
);
1097 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1099 // Create new table with new fields and new unique constraint.
1100 rv
= CreateTableForSchemaVersion6();
1101 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1103 // Copy data from old table without the two deprecated columns appId and
1104 // inBrowserElement.
1105 nsCOMPtr
<mozIStorageFunction
> convertToOriginAttrs(
1106 new ConvertAppIdToOriginAttrsSQLFunction());
1107 NS_ENSURE_TRUE(convertToOriginAttrs
, RESULT_RETRY
);
1109 constexpr auto convertToOriginAttrsName
=
1110 "CONVERT_TO_ORIGIN_ATTRIBUTES"_ns
;
1112 rv
= mSyncConn
->CreateFunction(convertToOriginAttrsName
, 2,
1113 convertToOriginAttrs
);
1114 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1116 rv
= mSyncConn
->ExecuteSimpleSQL(nsLiteralCString(
1117 "INSERT INTO moz_cookies "
1118 "(baseDomain, originAttributes, name, value, host, path, expiry,"
1119 " lastAccessed, creationTime, isSecure, isHttpOnly) "
1120 "SELECT baseDomain, "
1121 " CONVERT_TO_ORIGIN_ATTRIBUTES(appId, inBrowserElement),"
1122 " name, value, host, path, expiry, lastAccessed, creationTime, "
1123 " isSecure, isHttpOnly "
1124 "FROM moz_cookies_old"));
1125 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1127 rv
= mSyncConn
->RemoveFunction(convertToOriginAttrsName
);
1128 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1131 rv
= mSyncConn
->ExecuteSimpleSQL("DROP TABLE moz_cookies_old"_ns
);
1132 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1134 COOKIE_LOGSTRING(LogLevel::Debug
,
1135 ("Upgraded database to schema version 6"));
1140 // We made a mistake in schema version 6. We cannot remove expected
1141 // columns of any version (checked in the default case) from cookie
1142 // database, because doing this would destroy the possibility of
1143 // downgrading database.
1145 // This version simply restores appId and inBrowserElement columns in
1146 // order to fix downgrading issue even though these two columns are no
1147 // longer used in the latest schema.
1148 rv
= mSyncConn
->ExecuteSimpleSQL(nsLiteralCString(
1149 "ALTER TABLE moz_cookies ADD appId INTEGER DEFAULT 0;"));
1150 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1152 rv
= mSyncConn
->ExecuteSimpleSQL(nsLiteralCString(
1153 "ALTER TABLE moz_cookies ADD inBrowserElement INTEGER DEFAULT 0;"));
1154 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1156 // Compute and populate the values of appId and inBrwoserElement from
1157 // originAttributes.
1158 nsCOMPtr
<mozIStorageFunction
> setAppId(
1159 new SetAppIdFromOriginAttributesSQLFunction());
1160 NS_ENSURE_TRUE(setAppId
, RESULT_RETRY
);
1162 constexpr auto setAppIdName
= "SET_APP_ID"_ns
;
1164 rv
= mSyncConn
->CreateFunction(setAppIdName
, 1, setAppId
);
1165 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1167 nsCOMPtr
<mozIStorageFunction
> setInBrowser(
1168 new SetInBrowserFromOriginAttributesSQLFunction());
1169 NS_ENSURE_TRUE(setInBrowser
, RESULT_RETRY
);
1171 constexpr auto setInBrowserName
= "SET_IN_BROWSER"_ns
;
1173 rv
= mSyncConn
->CreateFunction(setInBrowserName
, 1, setInBrowser
);
1174 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1176 rv
= mSyncConn
->ExecuteSimpleSQL(nsLiteralCString(
1177 "UPDATE moz_cookies SET appId = SET_APP_ID(originAttributes), "
1178 "inBrowserElement = SET_IN_BROWSER(originAttributes);"));
1179 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1181 rv
= mSyncConn
->RemoveFunction(setAppIdName
);
1182 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1184 rv
= mSyncConn
->RemoveFunction(setInBrowserName
);
1185 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1187 COOKIE_LOGSTRING(LogLevel::Debug
,
1188 ("Upgraded database to schema version 7"));
1193 // Remove the appId field from moz_cookies.
1195 // Unfortunately sqlite doesn't support dropping columns using ALTER
1196 // TABLE, so we need to go through the procedure documented in
1197 // https://www.sqlite.org/lang_altertable.html.
1199 // Drop existing index
1200 rv
= mSyncConn
->ExecuteSimpleSQL("DROP INDEX moz_basedomain"_ns
);
1201 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1203 // Create a new_moz_cookies table without the appId field.
1204 rv
= mSyncConn
->ExecuteSimpleSQL(
1205 nsLiteralCString("CREATE TABLE new_moz_cookies("
1206 "id INTEGER PRIMARY KEY, "
1208 "originAttributes TEXT NOT NULL DEFAULT '', "
1214 "lastAccessed INTEGER, "
1215 "creationTime INTEGER, "
1216 "isSecure INTEGER, "
1217 "isHttpOnly INTEGER, "
1218 "inBrowserElement INTEGER DEFAULT 0, "
1219 "CONSTRAINT moz_uniqueid UNIQUE (name, host, "
1220 "path, originAttributes)"
1222 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1224 // Move the data over.
1225 rv
= mSyncConn
->ExecuteSimpleSQL(
1226 nsLiteralCString("INSERT INTO new_moz_cookies ("
1229 "originAttributes, "
1243 "originAttributes, "
1254 "FROM moz_cookies;"));
1255 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1257 // Drop the old table
1258 rv
= mSyncConn
->ExecuteSimpleSQL("DROP TABLE moz_cookies;"_ns
);
1259 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1261 // Rename new_moz_cookies to moz_cookies.
1262 rv
= mSyncConn
->ExecuteSimpleSQL(nsLiteralCString(
1263 "ALTER TABLE new_moz_cookies RENAME TO moz_cookies;"));
1264 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1266 // Recreate our index.
1267 rv
= mSyncConn
->ExecuteSimpleSQL(
1268 nsLiteralCString("CREATE INDEX moz_basedomain ON moz_cookies "
1269 "(baseDomain, originAttributes)"));
1270 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1272 COOKIE_LOGSTRING(LogLevel::Debug
,
1273 ("Upgraded database to schema version 8"));
1278 // Add the sameSite column to the table.
1279 rv
= mSyncConn
->ExecuteSimpleSQL(
1280 "ALTER TABLE moz_cookies ADD sameSite INTEGER"_ns
);
1281 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1283 COOKIE_LOGSTRING(LogLevel::Debug
,
1284 ("Upgraded database to schema version 9"));
1289 // Add the rawSameSite column to the table.
1290 rv
= mSyncConn
->ExecuteSimpleSQL(nsLiteralCString(
1291 "ALTER TABLE moz_cookies ADD rawSameSite INTEGER"));
1292 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1294 // Copy the current sameSite value into rawSameSite.
1295 rv
= mSyncConn
->ExecuteSimpleSQL(
1296 "UPDATE moz_cookies SET rawSameSite = sameSite"_ns
);
1297 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1299 COOKIE_LOGSTRING(LogLevel::Debug
,
1300 ("Upgraded database to schema version 10"));
1305 // Rename existing table
1306 rv
= mSyncConn
->ExecuteSimpleSQL(nsLiteralCString(
1307 "ALTER TABLE moz_cookies RENAME TO moz_cookies_old"));
1308 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1310 // Create a new moz_cookies table without the baseDomain field.
1311 rv
= mSyncConn
->ExecuteSimpleSQL(
1312 nsLiteralCString("CREATE TABLE moz_cookies("
1313 "id INTEGER PRIMARY KEY, "
1314 "originAttributes TEXT NOT NULL DEFAULT '', "
1320 "lastAccessed INTEGER, "
1321 "creationTime INTEGER, "
1322 "isSecure INTEGER, "
1323 "isHttpOnly INTEGER, "
1324 "inBrowserElement INTEGER DEFAULT 0, "
1325 "sameSite INTEGER DEFAULT 0, "
1326 "rawSameSite INTEGER DEFAULT 0, "
1327 "CONSTRAINT moz_uniqueid UNIQUE (name, host, "
1328 "path, originAttributes)"
1330 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1332 // Move the data over.
1333 rv
= mSyncConn
->ExecuteSimpleSQL(
1334 nsLiteralCString("INSERT INTO moz_cookies ("
1336 "originAttributes, "
1346 "inBrowserElement, "
1351 "originAttributes, "
1361 "inBrowserElement, "
1364 "FROM moz_cookies_old "
1365 "WHERE baseDomain NOTNULL;"));
1366 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1368 // Drop the old table
1369 rv
= mSyncConn
->ExecuteSimpleSQL("DROP TABLE moz_cookies_old;"_ns
);
1370 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1372 // Drop the moz_basedomain index from the database (if it hasn't been
1373 // removed already by removing the table).
1374 rv
= mSyncConn
->ExecuteSimpleSQL(
1375 "DROP INDEX IF EXISTS moz_basedomain;"_ns
);
1376 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1378 COOKIE_LOGSTRING(LogLevel::Debug
,
1379 ("Upgraded database to schema version 11"));
1384 // Add the schemeMap column to the table.
1385 rv
= mSyncConn
->ExecuteSimpleSQL(nsLiteralCString(
1386 "ALTER TABLE moz_cookies ADD schemeMap INTEGER DEFAULT 0;"));
1387 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1389 COOKIE_LOGSTRING(LogLevel::Debug
,
1390 ("Upgraded database to schema version 12"));
1392 // No more upgrades. Update the schema version.
1393 rv
= mSyncConn
->SetSchemaVersion(COOKIES_SCHEMA_VERSION
);
1394 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1399 // Add the isPartitionedAttributeSet column to the table.
1400 rv
= mSyncConn
->ExecuteSimpleSQL(
1401 nsLiteralCString("ALTER TABLE moz_cookies ADD "
1402 "isPartitionedAttributeSet INTEGER DEFAULT 0;"));
1403 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1405 COOKIE_LOGSTRING(LogLevel::Debug
,
1406 ("Upgraded database to schema version 13"));
1408 // No more upgrades. Update the schema version.
1409 rv
= mSyncConn
->SetSchemaVersion(COOKIES_SCHEMA_VERSION
);
1410 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1415 case COOKIES_SCHEMA_VERSION
:
1419 NS_WARNING("couldn't get schema version!");
1421 // the table may be usable; someone might've just clobbered the schema
1422 // version. we can treat this case like a downgrade using the codepath
1423 // below, by verifying the columns we care about are all there. for now,
1424 // re-set the schema version in the db, in case the checks succeed (if
1425 // they don't, we're dropping the table anyway).
1426 rv
= mSyncConn
->SetSchemaVersion(COOKIES_SCHEMA_VERSION
);
1427 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1429 // fall through to downgrade check
1433 // if columns have been added to the table, we can still use the ones we
1434 // understand safely. if columns have been deleted or altered, just
1435 // blow away the table and start from scratch! if you change the way
1436 // a column is interpreted, make sure you also change its name so this
1437 // check will catch it.
1439 // check if all the expected columns exist
1440 nsCOMPtr
<mozIStorageStatement
> stmt
;
1441 rv
= mSyncConn
->CreateStatement(
1442 nsLiteralCString("SELECT "
1444 "originAttributes, "
1457 "isPartitionedAttributeSet "
1458 "FROM moz_cookies"),
1459 getter_AddRefs(stmt
));
1460 if (NS_SUCCEEDED(rv
)) {
1464 // our columns aren't there - drop the table!
1465 rv
= mSyncConn
->ExecuteSimpleSQL("DROP TABLE moz_cookies"_ns
);
1466 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1469 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1474 // if we deleted a corrupt db, don't attempt to import - return now
1479 // check whether to import or just read in the db
1487 void CookiePersistentStorage::RebuildCorruptDB() {
1488 NS_ASSERTION(!mDBConn
, "shouldn't have an open db connection");
1489 NS_ASSERTION(mCorruptFlag
== CookiePersistentStorage::CLOSING_FOR_REBUILD
,
1490 "should be in CLOSING_FOR_REBUILD state");
1492 nsCOMPtr
<nsIObserverService
> os
= services::GetObserverService();
1494 mCorruptFlag
= CookiePersistentStorage::REBUILDING
;
1496 COOKIE_LOGSTRING(LogLevel::Debug
,
1497 ("RebuildCorruptDB(): creating new database"));
1499 RefPtr
<CookiePersistentStorage
> self
= this;
1500 nsCOMPtr
<nsIRunnable
> runnable
=
1501 NS_NewRunnableFunction("RebuildCorruptDB.TryInitDB", [self
] {
1502 // The database has been closed, and we're ready to rebuild. Open a
1504 OpenDBResult result
= self
->TryInitDB(true);
1506 nsCOMPtr
<nsIRunnable
> innerRunnable
= NS_NewRunnableFunction(
1507 "RebuildCorruptDB.TryInitDBComplete", [self
, result
] {
1508 nsCOMPtr
<nsIObserverService
> os
= services::GetObserverService();
1509 if (result
!= RESULT_OK
) {
1510 // We're done. Reset our DB connection and statements, and
1511 // notify of closure.
1514 ("RebuildCorruptDB(): TryInitDB() failed with result %u",
1516 self
->CleanupCachedStatements();
1517 self
->CleanupDBConnection();
1518 self
->mCorruptFlag
= CookiePersistentStorage::OK
;
1520 os
->NotifyObservers(nullptr, "cookie-db-closed", nullptr);
1525 // Notify observers that we're beginning the rebuild.
1527 os
->NotifyObservers(nullptr, "cookie-db-rebuilding", nullptr);
1530 self
->InitDBConnInternal();
1532 // Enumerate the hash, and add cookies to the params array.
1533 mozIStorageAsyncStatement
* stmt
= self
->mStmtInsert
;
1534 nsCOMPtr
<mozIStorageBindingParamsArray
> paramsArray
;
1535 stmt
->NewBindingParamsArray(getter_AddRefs(paramsArray
));
1536 for (auto iter
= self
->mHostTable
.Iter(); !iter
.Done();
1538 CookieEntry
* entry
= iter
.Get();
1540 const CookieEntry::ArrayType
& cookies
= entry
->GetCookies();
1541 for (CookieEntry::IndexType i
= 0; i
< cookies
.Length(); ++i
) {
1542 Cookie
* cookie
= cookies
[i
];
1544 if (!cookie
->IsSession()) {
1545 BindCookieParameters(paramsArray
, CookieKey(entry
), cookie
);
1550 // Make sure we've got something to write. If we don't, we're
1553 paramsArray
->GetLength(&length
);
1557 ("RebuildCorruptDB(): nothing to write, rebuild complete"));
1558 self
->mCorruptFlag
= CookiePersistentStorage::OK
;
1562 self
->MaybeStoreCookiesToDB(paramsArray
);
1564 NS_DispatchToMainThread(innerRunnable
);
1566 mThread
->Dispatch(runnable
, NS_DISPATCH_NORMAL
);
1569 void CookiePersistentStorage::HandleDBClosed() {
1570 COOKIE_LOGSTRING(LogLevel::Debug
,
1571 ("HandleDBClosed(): CookieStorage %p closed", this));
1573 nsCOMPtr
<nsIObserverService
> os
= services::GetObserverService();
1575 switch (mCorruptFlag
) {
1576 case CookiePersistentStorage::OK
: {
1577 // Database is healthy. Notify of closure.
1579 os
->NotifyObservers(nullptr, "cookie-db-closed", nullptr);
1583 case CookiePersistentStorage::CLOSING_FOR_REBUILD
: {
1584 // Our close finished. Start the rebuild, and notify of db closure later.
1588 case CookiePersistentStorage::REBUILDING
: {
1589 // We encountered an error during rebuild, closed the database, and now
1590 // here we are. We already have a 'cookies.sqlite.bak' from the original
1591 // dead database; we don't want to overwrite it, so let's move this one to
1592 // 'cookies.sqlite.bak-rebuild'.
1593 nsCOMPtr
<nsIFile
> backupFile
;
1594 mCookieFile
->Clone(getter_AddRefs(backupFile
));
1595 nsresult rv
= backupFile
->MoveToNative(
1596 nullptr, nsLiteralCString(COOKIES_FILE
".bak-rebuild"));
1598 COOKIE_LOGSTRING(LogLevel::Warning
,
1599 ("HandleDBClosed(): CookieStorage %p encountered error "
1600 "rebuilding db; move to "
1601 "'cookies.sqlite.bak-rebuild' gave rv 0x%" PRIx32
,
1602 this, static_cast<uint32_t>(rv
)));
1604 os
->NotifyObservers(nullptr, "cookie-db-closed", nullptr);
1611 CookiePersistentStorage::OpenDBResult
CookiePersistentStorage::Read() {
1612 MOZ_ASSERT(NS_GetCurrentThread() == mThread
);
1614 // Read in the data synchronously.
1615 // see IDX_NAME, etc. for parameter indexes
1616 nsCOMPtr
<mozIStorageStatement
> stmt
;
1618 mSyncConn
->CreateStatement(nsLiteralCString("SELECT "
1628 "originAttributes, "
1632 "isPartitionedAttributeSet "
1633 "FROM moz_cookies"),
1634 getter_AddRefs(stmt
));
1636 NS_ENSURE_SUCCESS(rv
, RESULT_RETRY
);
1638 if (NS_WARN_IF(!mReadArray
.IsEmpty())) {
1641 mReadArray
.SetCapacity(kMaxNumberOfCookies
);
1643 nsCString baseDomain
;
1650 rv
= stmt
->ExecuteStep(&hasResult
);
1651 if (NS_WARN_IF(NS_FAILED(rv
))) {
1653 return RESULT_RETRY
;
1660 stmt
->GetUTF8String(IDX_HOST
, host
);
1662 rv
= CookieCommons::GetBaseDomainFromHost(mTLDService
, host
, baseDomain
);
1663 if (NS_FAILED(rv
)) {
1664 COOKIE_LOGSTRING(LogLevel::Debug
,
1665 ("Read(): Ignoring invalid host '%s'", host
.get()));
1669 nsAutoCString suffix
;
1670 OriginAttributes attrs
;
1671 stmt
->GetUTF8String(IDX_ORIGIN_ATTRIBUTES
, suffix
);
1672 // If PopulateFromSuffix failed we just ignore the OA attributes
1673 // that we don't support
1674 Unused
<< attrs
.PopulateFromSuffix(suffix
);
1676 CookieKey
key(baseDomain
, attrs
);
1677 CookieDomainTuple
* tuple
= mReadArray
.AppendElement();
1678 tuple
->key
= std::move(key
);
1679 tuple
->originAttributes
= attrs
;
1680 tuple
->cookie
= GetCookieFromRow(stmt
);
1683 COOKIE_LOGSTRING(LogLevel::Debug
,
1684 ("Read(): %zu cookies read", mReadArray
.Length()));
1689 // Extract data from a single result row and create an Cookie.
1690 UniquePtr
<CookieStruct
> CookiePersistentStorage::GetCookieFromRow(
1691 mozIStorageStatement
* aRow
) {
1696 DebugOnly
<nsresult
> rv
= aRow
->GetUTF8String(IDX_NAME
, name
);
1697 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1698 rv
= aRow
->GetUTF8String(IDX_VALUE
, value
);
1699 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1700 rv
= aRow
->GetUTF8String(IDX_HOST
, host
);
1701 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1702 rv
= aRow
->GetUTF8String(IDX_PATH
, path
);
1703 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1705 int64_t expiry
= aRow
->AsInt64(IDX_EXPIRY
);
1706 int64_t lastAccessed
= aRow
->AsInt64(IDX_LAST_ACCESSED
);
1707 int64_t creationTime
= aRow
->AsInt64(IDX_CREATION_TIME
);
1708 bool isSecure
= 0 != aRow
->AsInt32(IDX_SECURE
);
1709 bool isHttpOnly
= 0 != aRow
->AsInt32(IDX_HTTPONLY
);
1710 int32_t sameSite
= aRow
->AsInt32(IDX_SAME_SITE
);
1711 int32_t rawSameSite
= aRow
->AsInt32(IDX_RAW_SAME_SITE
);
1712 int32_t schemeMap
= aRow
->AsInt32(IDX_SCHEME_MAP
);
1713 bool isPartitionedAttributeSet
=
1714 0 != aRow
->AsInt32(IDX_PARTITIONED_ATTRIBUTE_SET
);
1716 // Create a new constCookie and assign the data.
1717 return MakeUnique
<CookieStruct
>(
1718 name
, value
, host
, path
, expiry
, lastAccessed
, creationTime
, isHttpOnly
,
1719 false, isSecure
, isPartitionedAttributeSet
, sameSite
, rawSameSite
,
1720 static_cast<nsICookie::schemeType
>(schemeMap
));
1723 void CookiePersistentStorage::EnsureInitialized() {
1724 MOZ_ASSERT(NS_IsMainThread());
1726 bool isAccumulated
= false;
1728 if (!mInitialized
) {
1729 TimeStamp startBlockTime
= TimeStamp::Now();
1730 MonitorAutoLock
lock(mMonitor
);
1732 while (!mInitialized
) {
1736 Telemetry::AccumulateTimeDelta(
1737 Telemetry::MOZ_SQLITE_COOKIES_BLOCK_MAIN_THREAD_MS_V2
, startBlockTime
);
1738 Telemetry::Accumulate(
1739 Telemetry::MOZ_SQLITE_COOKIES_TIME_TO_BLOCK_MAIN_THREAD_MS
, 0);
1740 isAccumulated
= true;
1741 } else if (!mEndInitDBConn
.IsNull()) {
1742 // We didn't block main thread, and here comes the first cookie request.
1743 // Collect how close we're going to block main thread.
1744 Telemetry::Accumulate(
1745 Telemetry::MOZ_SQLITE_COOKIES_TIME_TO_BLOCK_MAIN_THREAD_MS
,
1746 (TimeStamp::Now() - mEndInitDBConn
).ToMilliseconds());
1747 // Nullify the timestamp so wo don't accumulate this telemetry probe again.
1748 mEndInitDBConn
= TimeStamp();
1749 isAccumulated
= true;
1750 } else if (!mInitializedDBConn
) {
1751 // A request comes while we finished cookie thread task and InitDBConn is
1752 // on the way from cookie thread to main thread. We're very close to block
1754 Telemetry::Accumulate(
1755 Telemetry::MOZ_SQLITE_COOKIES_TIME_TO_BLOCK_MAIN_THREAD_MS
, 0);
1756 isAccumulated
= true;
1759 if (!mInitializedDBConn
) {
1761 if (isAccumulated
) {
1762 // Nullify the timestamp so wo don't accumulate this telemetry probe
1764 mEndInitDBConn
= TimeStamp();
1769 void CookiePersistentStorage::InitDBConn() {
1770 MOZ_ASSERT(NS_IsMainThread());
1772 // We should skip InitDBConn if we close profile during initializing
1773 // CookieStorages and then InitDBConn is called after we close the
1775 if (!mInitialized
|| mInitializedDBConn
) {
1779 nsCOMPtr
<nsIURI
> dummyUri
;
1780 nsresult rv
= NS_NewURI(getter_AddRefs(dummyUri
), "https://example.com");
1781 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1782 nsTArray
<RefPtr
<Cookie
>> cleanupCookies
;
1784 for (uint32_t i
= 0; i
< mReadArray
.Length(); ++i
) {
1785 CookieDomainTuple
& tuple
= mReadArray
[i
];
1786 MOZ_ASSERT(!tuple
.cookie
->isSession());
1788 // filter invalid non-ipv4 host ending in number from old db values
1789 nsCOMPtr
<nsIURIMutator
> outMut
;
1790 nsCOMPtr
<nsIURIMutator
> dummyMut
;
1791 rv
= dummyUri
->Mutate(getter_AddRefs(dummyMut
));
1792 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1793 rv
= dummyMut
->SetHost(tuple
.cookie
->host(), getter_AddRefs(outMut
));
1795 if (NS_FAILED(rv
)) {
1796 COOKIE_LOGSTRING(LogLevel::Debug
, ("Removing cookie from db with "
1797 "newly invalid hostname: '%s'",
1798 tuple
.cookie
->host().get()));
1799 RefPtr
<Cookie
> cookie
=
1800 Cookie::Create(*tuple
.cookie
, tuple
.originAttributes
);
1801 cleanupCookies
.AppendElement(cookie
);
1805 // CreateValidated fixes up the creation and lastAccessed times.
1806 // If the DB is corrupted and the timestaps are far away in the future
1807 // we don't want the creation timestamp to update gLastCreationTime
1808 // as that would contaminate all the next creation times.
1809 // We fix up these dates to not be later than the current time.
1810 // The downside is that if the user sets the date far away in the past
1811 // then back to the current date, those cookies will be stale,
1812 // but if we don't fix their dates, those cookies might never be
1814 RefPtr
<Cookie
> cookie
=
1815 Cookie::CreateValidated(*tuple
.cookie
, tuple
.originAttributes
);
1816 AddCookieToList(tuple
.key
.mBaseDomain
, tuple
.key
.mOriginAttributes
, cookie
);
1819 if (NS_FAILED(InitDBConnInternal())) {
1820 COOKIE_LOGSTRING(LogLevel::Warning
,
1821 ("InitDBConn(): retrying InitDBConnInternal()"));
1822 CleanupCachedStatements();
1823 CleanupDBConnection();
1824 if (NS_FAILED(InitDBConnInternal())) {
1827 ("InitDBConn(): InitDBConnInternal() failed, closing connection"));
1829 // Game over, clean the connections.
1830 CleanupCachedStatements();
1831 CleanupDBConnection();
1834 mInitializedDBConn
= true;
1836 COOKIE_LOGSTRING(LogLevel::Debug
,
1837 ("InitDBConn(): mInitializedDBConn = true"));
1838 mEndInitDBConn
= TimeStamp::Now();
1840 for (const auto& cookie
: cleanupCookies
) {
1841 RemoveCookieFromDB(*cookie
);
1844 nsCOMPtr
<nsIObserverService
> os
= services::GetObserverService();
1846 os
->NotifyObservers(nullptr, "cookie-db-read", nullptr);
1851 nsresult
CookiePersistentStorage::InitDBConnInternal() {
1852 MOZ_ASSERT(NS_IsMainThread());
1854 nsresult rv
= mStorageService
->OpenUnsharedDatabase(
1855 mCookieFile
, mozIStorageService::CONNECTION_DEFAULT
,
1856 getter_AddRefs(mDBConn
));
1857 NS_ENSURE_SUCCESS(rv
, rv
);
1859 // Set up our listeners.
1860 mInsertListener
= new InsertCookieDBListener(this);
1861 mUpdateListener
= new UpdateCookieDBListener(this);
1862 mRemoveListener
= new RemoveCookieDBListener(this);
1863 mCloseListener
= new CloseCookieDBListener(this);
1865 // Grow cookie db in 512KB increments
1866 mDBConn
->SetGrowthIncrement(512 * 1024, ""_ns
);
1868 // make operations on the table asynchronous, for performance
1869 mDBConn
->ExecuteSimpleSQL("PRAGMA synchronous = OFF"_ns
);
1871 // Use write-ahead-logging for performance. We cap the autocheckpoint limit at
1872 // 16 pages (around 500KB).
1873 mDBConn
->ExecuteSimpleSQL(nsLiteralCString(MOZ_STORAGE_UNIQUIFY_QUERY_STR
1874 "PRAGMA journal_mode = WAL"));
1875 mDBConn
->ExecuteSimpleSQL("PRAGMA wal_autocheckpoint = 16"_ns
);
1877 // cache frequently used statements (for insertion, deletion, and updating)
1878 rv
= mDBConn
->CreateAsyncStatement(
1879 nsLiteralCString("INSERT INTO moz_cookies ("
1880 "originAttributes, "
1893 "isPartitionedAttributeSet "
1895 ":originAttributes, "
1908 ":isPartitionedAttributeSet "
1910 getter_AddRefs(mStmtInsert
));
1911 NS_ENSURE_SUCCESS(rv
, rv
);
1913 rv
= mDBConn
->CreateAsyncStatement(
1914 nsLiteralCString("DELETE FROM moz_cookies "
1915 "WHERE name = :name AND host = :host AND path = :path "
1916 "AND originAttributes = :originAttributes"),
1917 getter_AddRefs(mStmtDelete
));
1918 NS_ENSURE_SUCCESS(rv
, rv
);
1920 rv
= mDBConn
->CreateAsyncStatement(
1921 nsLiteralCString("UPDATE moz_cookies SET lastAccessed = :lastAccessed "
1922 "WHERE name = :name AND host = :host AND path = :path "
1923 "AND originAttributes = :originAttributes"),
1924 getter_AddRefs(mStmtUpdate
));
1928 // Sets the schema version and creates the moz_cookies table.
1929 nsresult
CookiePersistentStorage::CreateTableWorker(const char* aName
) {
1930 // Create the table.
1931 // We default originAttributes to empty string: this is so if users revert to
1932 // an older Firefox version that doesn't know about this field, any cookies
1933 // set will still work once they upgrade back.
1934 nsAutoCString
command("CREATE TABLE ");
1935 command
.Append(aName
);
1936 command
.AppendLiteral(
1938 "id INTEGER PRIMARY KEY, "
1939 "originAttributes TEXT NOT NULL DEFAULT '', "
1945 "lastAccessed INTEGER, "
1946 "creationTime INTEGER, "
1947 "isSecure INTEGER, "
1948 "isHttpOnly INTEGER, "
1949 "inBrowserElement INTEGER DEFAULT 0, "
1950 "sameSite INTEGER DEFAULT 0, "
1951 "rawSameSite INTEGER DEFAULT 0, "
1952 "schemeMap INTEGER DEFAULT 0, "
1953 "isPartitionedAttributeSet INTEGER DEFAULT 0, "
1954 "CONSTRAINT moz_uniqueid UNIQUE (name, host, path, originAttributes)"
1956 return mSyncConn
->ExecuteSimpleSQL(command
);
1959 // Sets the schema version and creates the moz_cookies table.
1960 nsresult
CookiePersistentStorage::CreateTable() {
1961 // Set the schema version, before creating the table.
1962 nsresult rv
= mSyncConn
->SetSchemaVersion(COOKIES_SCHEMA_VERSION
);
1963 if (NS_FAILED(rv
)) {
1967 rv
= CreateTableWorker("moz_cookies");
1968 if (NS_FAILED(rv
)) {
1975 // Sets the schema version and creates the moz_cookies table.
1976 nsresult
CookiePersistentStorage::CreateTableForSchemaVersion6() {
1977 // Set the schema version, before creating the table.
1978 nsresult rv
= mSyncConn
->SetSchemaVersion(6);
1979 if (NS_FAILED(rv
)) {
1983 // Create the table.
1984 // We default originAttributes to empty string: this is so if users revert to
1985 // an older Firefox version that doesn't know about this field, any cookies
1986 // set will still work once they upgrade back.
1987 rv
= mSyncConn
->ExecuteSimpleSQL(nsLiteralCString(
1988 "CREATE TABLE moz_cookies ("
1989 "id INTEGER PRIMARY KEY, "
1991 "originAttributes TEXT NOT NULL DEFAULT '', "
1997 "lastAccessed INTEGER, "
1998 "creationTime INTEGER, "
1999 "isSecure INTEGER, "
2000 "isHttpOnly INTEGER, "
2001 "CONSTRAINT moz_uniqueid UNIQUE (name, host, path, originAttributes)"
2003 if (NS_FAILED(rv
)) {
2007 // Create an index on baseDomain.
2008 return mSyncConn
->ExecuteSimpleSQL(nsLiteralCString(
2009 "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain, "
2010 "originAttributes)"));
2013 // Sets the schema version and creates the moz_cookies table.
2014 nsresult
CookiePersistentStorage::CreateTableForSchemaVersion5() {
2015 // Set the schema version, before creating the table.
2016 nsresult rv
= mSyncConn
->SetSchemaVersion(5);
2017 if (NS_FAILED(rv
)) {
2021 // Create the table. We default appId/inBrowserElement to 0: this is so if
2022 // users revert to an older Firefox version that doesn't know about these
2023 // fields, any cookies set will still work once they upgrade back.
2024 rv
= mSyncConn
->ExecuteSimpleSQL(
2025 nsLiteralCString("CREATE TABLE moz_cookies ("
2026 "id INTEGER PRIMARY KEY, "
2028 "appId INTEGER DEFAULT 0, "
2029 "inBrowserElement INTEGER DEFAULT 0, "
2035 "lastAccessed INTEGER, "
2036 "creationTime INTEGER, "
2037 "isSecure INTEGER, "
2038 "isHttpOnly INTEGER, "
2039 "CONSTRAINT moz_uniqueid UNIQUE (name, host, path, "
2040 "appId, inBrowserElement)"
2042 if (NS_FAILED(rv
)) {
2046 // Create an index on baseDomain.
2047 return mSyncConn
->ExecuteSimpleSQL(nsLiteralCString(
2048 "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain, "
2050 "inBrowserElement)"));
2053 nsresult
CookiePersistentStorage::RunInTransaction(
2054 nsICookieTransactionCallback
* aCallback
) {
2055 if (NS_WARN_IF(!mDBConn
)) {
2056 return NS_ERROR_NOT_AVAILABLE
;
2059 mozStorageTransaction
transaction(mDBConn
, true);
2061 // XXX Handle the error, bug 1696130.
2062 Unused
<< NS_WARN_IF(NS_FAILED(transaction
.Start()));
2064 if (NS_FAILED(aCallback
->Callback())) {
2065 Unused
<< transaction
.Rollback();
2066 return NS_ERROR_FAILURE
;
2072 // purges expired and old cookies in a batch operation.
2073 already_AddRefed
<nsIArray
> CookiePersistentStorage::PurgeCookies(
2074 int64_t aCurrentTimeInUsec
, uint16_t aMaxNumberOfCookies
,
2075 int64_t aCookiePurgeAge
) {
2076 // Create a params array to batch the removals. This is OK here because
2077 // all the removals are in order, and there are no interleaved additions.
2078 nsCOMPtr
<mozIStorageBindingParamsArray
> paramsArray
;
2080 mStmtDelete
->NewBindingParamsArray(getter_AddRefs(paramsArray
));
2083 RefPtr
<CookiePersistentStorage
> self
= this;
2085 return PurgeCookiesWithCallbacks(
2086 aCurrentTimeInUsec
, aMaxNumberOfCookies
, aCookiePurgeAge
,
2087 [paramsArray
, self
](const CookieListIter
& aIter
) {
2088 self
->PrepareCookieRemoval(*aIter
.Cookie(), paramsArray
);
2089 self
->RemoveCookieFromListInternal(aIter
);
2091 [paramsArray
, self
]() {
2093 self
->DeleteFromDB(paramsArray
);
2098 void CookiePersistentStorage::CollectCookieJarSizeData() {
2099 COOKIE_LOGSTRING(LogLevel::Debug
,
2100 ("CookiePersistentStorage::CollectCookieJarSizeData"));
2102 uint32_t sumPartitioned
= 0;
2103 uint32_t sumUnpartitioned
= 0;
2104 for (const auto& cookieEntry
: mHostTable
) {
2105 if (cookieEntry
.IsPartitioned()) {
2106 uint16_t cePartitioned
= cookieEntry
.GetCookies().Length();
2107 sumPartitioned
+= cePartitioned
;
2108 mozilla::glean::networking::cookie_count_part_by_key
2109 .AccumulateSingleSample(cePartitioned
);
2111 uint16_t ceUnpartitioned
= cookieEntry
.GetCookies().Length();
2112 sumUnpartitioned
+= ceUnpartitioned
;
2113 mozilla::glean::networking::cookie_count_unpart_by_key
2114 .AccumulateSingleSample(ceUnpartitioned
);
2118 mozilla::glean::networking::cookie_count_total
.AccumulateSingleSample(
2120 mozilla::glean::networking::cookie_count_partitioned
.AccumulateSingleSample(
2122 mozilla::glean::networking::cookie_count_unpartitioned
.AccumulateSingleSample(
2127 } // namespace mozilla