1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "BounceTrackingProtectionStorage.h"
8 #include "BounceTrackingState.h"
9 #include "BounceTrackingStateGlobal.h"
10 #include "ErrorList.h"
11 #include "MainThreadUtils.h"
12 #include "mozIStorageConnection.h"
13 #include "mozIStorageService.h"
14 #include "mozIStorageStatement.h"
15 #include "mozStorageCID.h"
16 #include "mozilla/Components.h"
17 #include "mozilla/Monitor.h"
18 #include "mozilla/IntegerPrintfMacros.h"
19 #include "mozilla/Services.h"
21 #include "nsDirectoryServiceUtils.h"
22 #include "nsIBounceTrackingProtection.h"
23 #include "nsIObserverService.h"
24 #include "nsIPrincipal.h"
25 #include "nsIScriptSecurityManager.h"
26 #include "nsStringFwd.h"
27 #include "nsVariant.h"
29 #include "nsAppDirectoryServiceDefs.h"
32 #define BOUNCE_TRACKING_PROTECTION_DB_FILENAME \
33 "bounce-tracking-protection.sqlite"_ns
34 #define SCHEMA_VERSION 1
38 NS_IMPL_ISUPPORTS(BounceTrackingProtectionStorage
, nsIAsyncShutdownBlocker
,
41 RefPtr
<BounceTrackingStateGlobal
>
42 BounceTrackingProtectionStorage::GetStateGlobal(nsIPrincipal
* aPrincipal
) {
43 MOZ_ASSERT(aPrincipal
);
44 return GetStateGlobal(aPrincipal
->OriginAttributesRef());
47 RefPtr
<BounceTrackingStateGlobal
>
48 BounceTrackingProtectionStorage::GetStateGlobal(
49 const OriginAttributes
& aOriginAttributes
) {
50 return mStateGlobal
.Get(aOriginAttributes
);
53 RefPtr
<BounceTrackingStateGlobal
>
54 BounceTrackingProtectionStorage::GetOrCreateStateGlobal(
55 nsIPrincipal
* aPrincipal
) {
56 MOZ_ASSERT(aPrincipal
);
57 return GetOrCreateStateGlobal(aPrincipal
->OriginAttributesRef());
60 RefPtr
<BounceTrackingStateGlobal
>
61 BounceTrackingProtectionStorage::GetOrCreateStateGlobal(
62 BounceTrackingState
* aBounceTrackingState
) {
63 MOZ_ASSERT(aBounceTrackingState
);
64 return GetOrCreateStateGlobal(aBounceTrackingState
->OriginAttributesRef());
67 RefPtr
<BounceTrackingStateGlobal
>
68 BounceTrackingProtectionStorage::GetOrCreateStateGlobal(
69 const OriginAttributes
& aOriginAttributes
) {
70 return mStateGlobal
.GetOrInsertNew(aOriginAttributes
, this,
74 nsresult
BounceTrackingProtectionStorage::ClearByType(
75 BounceTrackingProtectionStorage::EntryType aType
) {
76 for (auto iter
= mStateGlobal
.Iter(); !iter
.Done(); iter
.Next()) {
77 BounceTrackingStateGlobal
* stateGlobal
= iter
.Data();
78 MOZ_ASSERT(stateGlobal
);
79 // Update in memory state. Skip storage so we can batch the writes later.
80 nsresult rv
= stateGlobal
->ClearByType(aType
, true);
81 NS_ENSURE_SUCCESS(rv
, rv
);
84 // Clear on-disk state for all OriginAttributes by type.
85 return DeleteDBEntriesByType(nullptr, aType
);
88 nsresult
BounceTrackingProtectionStorage::ClearBySiteHost(
89 const nsACString
& aSiteHost
, OriginAttributes
* aOriginAttributes
) {
90 NS_ENSURE_TRUE(!aSiteHost
.IsEmpty(), NS_ERROR_INVALID_ARG
);
92 // If OriginAttributes are passed only clear the matching state global.
93 if (aOriginAttributes
) {
94 RefPtr
<BounceTrackingStateGlobal
> stateGlobal
=
95 mStateGlobal
.Get(*aOriginAttributes
);
97 nsresult rv
= stateGlobal
->ClearSiteHost(aSiteHost
, true);
98 NS_ENSURE_SUCCESS(rv
, rv
);
101 // Otherwise we need to clear the host across all state globals.
102 for (auto iter
= mStateGlobal
.Iter(); !iter
.Done(); iter
.Next()) {
103 BounceTrackingStateGlobal
* stateGlobal
= iter
.Data();
104 MOZ_ASSERT(stateGlobal
);
105 // Update in memory state. Skip storage so we can batch the writes later.
106 nsresult rv
= stateGlobal
->ClearSiteHost(aSiteHost
, true);
107 NS_ENSURE_SUCCESS(rv
, rv
);
111 // Update the database.
112 // Private browsing data is not written to disk.
113 if (aOriginAttributes
&& aOriginAttributes
->IsPrivateBrowsing()) {
116 return DeleteDBEntries(aOriginAttributes
, aSiteHost
);
119 nsresult
BounceTrackingProtectionStorage::ClearByTimeRange(PRTime aFrom
,
121 for (auto iter
= mStateGlobal
.Iter(); !iter
.Done(); iter
.Next()) {
122 BounceTrackingStateGlobal
* stateGlobal
= iter
.Data();
123 MOZ_ASSERT(stateGlobal
);
124 // Update in memory state. Skip storage so we can batch the writes later.
126 stateGlobal
->ClearByTimeRange(aFrom
, Some(aTo
), Nothing(), true);
127 NS_ENSURE_SUCCESS(rv
, rv
);
130 // Update the database.
131 return DeleteDBEntriesInTimeRange(nullptr, aFrom
, Some(aTo
));
134 nsresult
BounceTrackingProtectionStorage::ClearByOriginAttributesPattern(
135 const OriginAttributesPattern
& aOriginAttributesPattern
,
136 const Maybe
<nsCString
>& aSiteHost
) {
137 // Clear in memory state.
138 for (auto iter
= mStateGlobal
.Iter(); !iter
.Done(); iter
.Next()) {
139 BounceTrackingStateGlobal
* stateGlobal
= iter
.Data();
140 MOZ_ASSERT(stateGlobal
);
142 if (!aOriginAttributesPattern
.Matches(stateGlobal
->OriginAttributesRef())) {
146 // If there is no site host filtering we can remove the entire state global.
147 if (aSiteHost
.isNothing()) {
152 // Otherwise we need to clear just the entries that match the site host.
153 // Update in memory state. Skip storage so we can batch the writes later.
154 nsresult rv
= stateGlobal
->ClearSiteHost(aSiteHost
.ref(), true);
155 NS_ENSURE_SUCCESS(rv
, rv
);
158 // Update the database.
159 // Private browsing data is not written to disk.
160 if (aOriginAttributesPattern
.mPrivateBrowsingId
.WasPassed() &&
161 aOriginAttributesPattern
.mPrivateBrowsingId
.Value() !=
162 nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID
) {
165 return DeleteDBEntriesByOriginAttributesPattern(aOriginAttributesPattern
,
169 nsresult
BounceTrackingProtectionStorage::UpdateDBEntry(
170 const OriginAttributes
& aOriginAttributes
, const nsACString
& aSiteHost
,
171 EntryType aEntryType
, PRTime aTimeStamp
) {
172 MOZ_ASSERT(NS_IsMainThread());
173 MOZ_ASSERT(!aSiteHost
.IsEmpty());
174 MOZ_ASSERT(aTimeStamp
);
176 nsresult rv
= WaitForInitialization();
177 NS_ENSURE_SUCCESS(rv
, rv
);
179 if (MOZ_LOG_TEST(gBounceTrackingProtectionLog
, LogLevel::Debug
)) {
180 nsAutoCString originAttributeSuffix
;
181 aOriginAttributes
.CreateSuffix(originAttributeSuffix
);
182 MOZ_LOG(gBounceTrackingProtectionLog
, LogLevel::Debug
,
183 ("%s: originAttributes: %s, siteHost=%s, entryType=%d, "
184 "timeStamp=%" PRId64
,
185 __FUNCTION__
, originAttributeSuffix
.get(),
186 PromiseFlatCString(aSiteHost
).get(),
187 static_cast<uint8_t>(aEntryType
), aTimeStamp
));
190 IncrementPendingWrites();
192 RefPtr
<BounceTrackingProtectionStorage
> self
= this;
193 nsCString
siteHost(aSiteHost
);
195 mBackgroundThread
->Dispatch(
196 NS_NewRunnableFunction(
197 "BounceTrackingProtectionStorage::UpdateEntry",
198 [self
, aOriginAttributes
, siteHost
, aEntryType
, aTimeStamp
]() {
200 UpsertData(self
->mDatabaseConnection
, aOriginAttributes
,
201 siteHost
, aEntryType
, aTimeStamp
);
202 self
->DecrementPendingWrites();
203 NS_ENSURE_SUCCESS_VOID(rv
);
205 NS_DISPATCH_EVENT_MAY_BLOCK
);
210 nsresult
BounceTrackingProtectionStorage::DeleteDBEntries(
211 OriginAttributes
* aOriginAttributes
, const nsACString
& aSiteHost
) {
212 MOZ_ASSERT(NS_IsMainThread());
213 MOZ_ASSERT(!aSiteHost
.IsEmpty());
214 MOZ_ASSERT(!aOriginAttributes
||
215 aOriginAttributes
->mPrivateBrowsingId
==
216 nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID
,
217 "Must not write private browsing data to the table.");
219 nsresult rv
= WaitForInitialization();
220 NS_ENSURE_SUCCESS(rv
, rv
);
222 if (MOZ_LOG_TEST(gBounceTrackingProtectionLog
, LogLevel::Debug
)) {
223 nsAutoCString
originAttributeSuffix("*");
224 if (aOriginAttributes
) {
225 aOriginAttributes
->CreateSuffix(originAttributeSuffix
);
227 MOZ_LOG(gBounceTrackingProtectionLog
, LogLevel::Debug
,
228 ("%s: originAttributes: %s, siteHost=%s", __FUNCTION__
,
229 originAttributeSuffix
.get(), PromiseFlatCString(aSiteHost
).get()));
232 RefPtr
<BounceTrackingProtectionStorage
> self
= this;
233 nsCString
siteHost(aSiteHost
);
234 Maybe
<OriginAttributes
> originAttributes
;
235 if (aOriginAttributes
) {
236 originAttributes
.emplace(*aOriginAttributes
);
239 IncrementPendingWrites();
240 mBackgroundThread
->Dispatch(
241 NS_NewRunnableFunction("BounceTrackingProtectionStorage::DeleteEntry",
242 [self
, originAttributes
, siteHost
]() {
244 DeleteData(self
->mDatabaseConnection
,
245 originAttributes
, siteHost
);
246 self
->DecrementPendingWrites();
247 NS_ENSURE_SUCCESS_VOID(rv
);
249 NS_DISPATCH_EVENT_MAY_BLOCK
);
254 nsresult
BounceTrackingProtectionStorage::Clear() {
255 MOZ_ASSERT(NS_IsMainThread());
256 // Clear in memory data.
257 mStateGlobal
.Clear();
259 // Clear on disk data.
260 nsresult rv
= WaitForInitialization();
261 NS_ENSURE_SUCCESS(rv
, rv
);
263 IncrementPendingWrites();
264 RefPtr
<BounceTrackingProtectionStorage
> self
= this;
265 mBackgroundThread
->Dispatch(
266 NS_NewRunnableFunction("BounceTrackingProtectionStorage::Clear",
269 ClearData(self
->mDatabaseConnection
);
270 self
->DecrementPendingWrites();
271 NS_ENSURE_SUCCESS_VOID(rv
);
273 NS_DISPATCH_EVENT_MAY_BLOCK
);
277 nsresult
BounceTrackingProtectionStorage::DeleteDBEntriesInTimeRange(
278 OriginAttributes
* aOriginAttributes
, PRTime aFrom
, Maybe
<PRTime
> aTo
,
279 Maybe
<BounceTrackingProtectionStorage::EntryType
> aEntryType
) {
280 MOZ_ASSERT(NS_IsMainThread());
281 NS_ENSURE_ARG_MIN(aFrom
, 0);
282 NS_ENSURE_TRUE(aTo
.isNothing() || aTo
.value() > aFrom
, NS_ERROR_INVALID_ARG
);
284 nsresult rv
= WaitForInitialization();
285 NS_ENSURE_SUCCESS(rv
, rv
);
287 RefPtr
<BounceTrackingProtectionStorage
> self
= this;
288 Maybe
<OriginAttributes
> originAttributes
;
289 if (aOriginAttributes
) {
290 originAttributes
.emplace(*aOriginAttributes
);
293 IncrementPendingWrites();
294 mBackgroundThread
->Dispatch(
295 NS_NewRunnableFunction(
296 "BounceTrackingProtectionStorage::DeleteDBEntriesInTimeRange",
297 [self
, originAttributes
, aFrom
, aTo
, aEntryType
]() {
299 DeleteDataInTimeRange(self
->mDatabaseConnection
,
300 originAttributes
, aFrom
, aTo
, aEntryType
);
301 self
->DecrementPendingWrites();
302 NS_ENSURE_SUCCESS_VOID(rv
);
304 NS_DISPATCH_EVENT_MAY_BLOCK
);
308 nsresult
BounceTrackingProtectionStorage::DeleteDBEntriesByType(
309 OriginAttributes
* aOriginAttributes
,
310 BounceTrackingProtectionStorage::EntryType aEntryType
) {
311 MOZ_ASSERT(NS_IsMainThread());
313 nsresult rv
= WaitForInitialization();
314 NS_ENSURE_SUCCESS(rv
, rv
);
316 RefPtr
<BounceTrackingProtectionStorage
> self
= this;
317 Maybe
<OriginAttributes
> originAttributes
;
318 if (aOriginAttributes
) {
319 originAttributes
.emplace(*aOriginAttributes
);
322 IncrementPendingWrites();
323 mBackgroundThread
->Dispatch(
324 NS_NewRunnableFunction(
325 "BounceTrackingProtectionStorage::DeleteDBEntriesByType",
326 [self
, originAttributes
, aEntryType
]() {
327 nsresult rv
= self
->DeleteDataByType(self
->mDatabaseConnection
,
328 originAttributes
, aEntryType
);
329 self
->DecrementPendingWrites();
330 NS_ENSURE_SUCCESS_VOID(rv
);
332 NS_DISPATCH_EVENT_MAY_BLOCK
);
337 BounceTrackingProtectionStorage::DeleteDBEntriesByOriginAttributesPattern(
338 const OriginAttributesPattern
& aOriginAttributesPattern
,
339 const Maybe
<nsCString
>& aSiteHost
) {
340 MOZ_ASSERT(NS_IsMainThread());
341 MOZ_ASSERT(!aOriginAttributesPattern
.mPrivateBrowsingId
.WasPassed() ||
342 aOriginAttributesPattern
.mPrivateBrowsingId
.Value() ==
343 nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID
,
344 "Must not clear private browsing data from the table.");
345 if (aSiteHost
.isSome()) {
346 MOZ_ASSERT(!aSiteHost
->IsEmpty(), "aSiteHost must not be empty.");
349 nsresult rv
= WaitForInitialization();
350 NS_ENSURE_SUCCESS(rv
, rv
);
352 IncrementPendingWrites();
353 RefPtr
<BounceTrackingProtectionStorage
> self
= this;
355 mBackgroundThread
->Dispatch(
356 NS_NewRunnableFunction(
357 "BounceTrackingProtectionStorage::"
358 "DeleteEntriesByOriginAttributesPattern",
359 [self
, aOriginAttributesPattern
, siteHost
= aSiteHost
]() {
360 nsresult rv
= DeleteDataByOriginAttributesPattern(
361 self
->mDatabaseConnection
, aOriginAttributesPattern
, siteHost
);
362 self
->DecrementPendingWrites();
363 NS_ENSURE_SUCCESS_VOID(rv
);
365 NS_DISPATCH_EVENT_MAY_BLOCK
);
369 // nsIAsyncShutdownBlocker
371 NS_IMETHODIMP
BounceTrackingProtectionStorage::BlockShutdown(
372 nsIAsyncShutdownClient
* aClient
) {
373 MOZ_ASSERT(NS_IsMainThread());
374 DebugOnly
<nsresult
> rv
= WaitForInitialization();
375 // If init failed log a warning. This isn't an early return since we still
376 // need to try to tear down, including removing the shutdown blocker. A
377 // failure state shouldn't cause a shutdown hang.
378 NS_WARNING_ASSERTION(NS_FAILED(rv
), "BlockShutdown: Init failed");
380 MonitorAutoLock
lock(mMonitor
);
381 mShuttingDown
.Flip();
383 RefPtr
<BounceTrackingProtectionStorage
> self
= this;
384 mBackgroundThread
->Dispatch(
385 NS_NewRunnableFunction(
386 "BounceTrackingProtectionStorage::BlockShutdown",
388 MonitorAutoLock
lock(self
->mMonitor
);
390 MOZ_ASSERT(self
->mPendingWrites
== 0);
392 if (self
->mDatabaseConnection
) {
393 DebugOnly
<nsresult
> rv
= self
->mDatabaseConnection
->Close();
394 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
395 "Failed to close database connection");
396 self
->mDatabaseConnection
= nullptr;
399 nsresult rv
= NS_DispatchToMainThread(NS_NewRunnableFunction(
400 "BounceTrackingProtectionStorage::BlockShutdown "
401 "- mainthread callback",
402 [self
]() { self
->Finalize(); }));
403 NS_ENSURE_SUCCESS_VOID(rv
);
405 NS_DISPATCH_EVENT_MAY_BLOCK
);
410 nsresult
BounceTrackingProtectionStorage::WaitForInitialization() {
411 MOZ_ASSERT(NS_IsMainThread(),
412 "Must only wait for initialization in the main thread.");
413 MonitorAutoLock
lock(mMonitor
);
414 while (!mInitialized
&& !mErrored
&& !mShuttingDown
) {
418 return NS_ERROR_FAILURE
;
421 return NS_ERROR_NOT_AVAILABLE
;
426 void BounceTrackingProtectionStorage::Finalize() {
427 nsCOMPtr
<nsIAsyncShutdownClient
> asc
= GetAsyncShutdownBarrier();
429 DebugOnly
<nsresult
> rv
= asc
->RemoveBlocker(this);
430 MOZ_ASSERT(NS_SUCCEEDED(rv
));
436 BounceTrackingProtectionStorage::Observe(nsISupports
* aSubject
,
438 const char16_t
* aData
) {
439 AssertIsOnMainThread();
440 if (nsCRT::strcmp(aTopic
, "last-pb-context-exited") != 0) {
441 return nsresult::NS_ERROR_FAILURE
;
444 uint32_t removedCount
= 0;
445 // Clear in-memory private browsing entries.
446 for (auto iter
= mStateGlobal
.Iter(); !iter
.Done(); iter
.Next()) {
447 BounceTrackingStateGlobal
* stateGlobal
= iter
.Data();
448 MOZ_ASSERT(stateGlobal
);
449 if (stateGlobal
->IsPrivateBrowsing()) {
455 gBounceTrackingProtectionLog
, LogLevel::Debug
,
456 ("%s: last-pb-context-exited: Removed %d private browsing state globals",
457 __FUNCTION__
, removedCount
));
462 // nsIAsyncShutdownBlocker
464 already_AddRefed
<nsIAsyncShutdownClient
>
465 BounceTrackingProtectionStorage::GetAsyncShutdownBarrier() const {
466 nsCOMPtr
<nsIAsyncShutdownService
> svc
= components::AsyncShutdown::Service();
467 MOZ_RELEASE_ASSERT(svc
);
469 nsCOMPtr
<nsIAsyncShutdownClient
> client
;
470 nsresult rv
= svc
->GetProfileBeforeChange(getter_AddRefs(client
));
471 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv
));
472 MOZ_RELEASE_ASSERT(client
);
474 return client
.forget();
477 NS_IMETHODIMP
BounceTrackingProtectionStorage::GetState(nsIPropertyBag
**) {
481 NS_IMETHODIMP
BounceTrackingProtectionStorage::GetName(nsAString
& aName
) {
482 aName
.AssignLiteral("BounceTrackingProtectionStorage: Flushing to disk");
486 nsresult
BounceTrackingProtectionStorage::Init() {
487 nsresult rv
= InitInternal();
489 MonitorAutoLock
lock(mMonitor
);
491 mMonitor
.NotifyAll();
496 nsresult
BounceTrackingProtectionStorage::InitInternal() {
497 MOZ_LOG(gBounceTrackingProtectionLog
, LogLevel::Debug
, ("%s", __FUNCTION__
));
499 // Init shouldn't be called if the feature is disabled.
500 NS_ENSURE_TRUE(StaticPrefs::privacy_bounceTrackingProtection_mode() !=
501 nsIBounceTrackingProtection::MODE_DISABLED
,
504 // Register a shutdown blocker so we can flush pending changes to disk before
506 // Init may also be called during shutdown, e.g. because of clearing data
508 nsCOMPtr
<nsIAsyncShutdownClient
> shutdownBarrier
= GetAsyncShutdownBarrier();
509 NS_ENSURE_TRUE(shutdownBarrier
, NS_ERROR_FAILURE
);
512 nsresult rv
= shutdownBarrier
->GetIsClosed(&closed
);
513 NS_ENSURE_TRUE(!closed
, NS_ERROR_ILLEGAL_DURING_SHUTDOWN
);
514 NS_ENSURE_SUCCESS(rv
, rv
);
516 rv
= shutdownBarrier
->AddBlocker(
517 this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__
), __LINE__
, u
""_ns
);
518 NS_ENSURE_SUCCESS(rv
, rv
);
520 // Listen for last private browsing context exited message so we can clean up
521 // in memory state when the PBM session ends.
522 nsCOMPtr
<nsIObserverService
> observerService
=
523 mozilla::services::GetObserverService();
524 NS_ENSURE_TRUE(observerService
, NS_ERROR_FAILURE
);
525 rv
= observerService
->AddObserver(this, "last-pb-context-exited", false);
526 NS_ENSURE_SUCCESS(rv
, rv
);
528 // Create the database file.
529 rv
= NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
530 getter_AddRefs(mDatabaseFile
));
531 NS_ENSURE_SUCCESS(rv
, rv
);
533 rv
= mDatabaseFile
->AppendNative(BOUNCE_TRACKING_PROTECTION_DB_FILENAME
);
534 NS_ENSURE_SUCCESS(rv
, rv
);
536 // Init the database and import data.
538 NS_CreateBackgroundTaskQueue("BounceTrackingProtectionStorage",
539 getter_AddRefs(mBackgroundThread
)),
542 RefPtr
<BounceTrackingProtectionStorage
> self
= this;
544 return mBackgroundThread
->Dispatch(
545 NS_NewRunnableFunction("BounceTrackingProtectionStorage::Init",
547 MonitorAutoLock
lock(self
->mMonitor
);
548 nsresult rv
= self
->CreateDatabaseConnection();
549 if (NS_WARN_IF(NS_FAILED(rv
))) {
550 self
->mErrored
.Flip();
551 self
->mMonitor
.NotifyAll();
555 rv
= self
->LoadMemoryStateFromDisk();
556 if (NS_WARN_IF(NS_FAILED(rv
))) {
557 self
->mErrored
.Flip();
558 self
->mMonitor
.NotifyAll();
562 self
->mInitialized
.Flip();
563 self
->mMonitor
.NotifyAll();
565 NS_DISPATCH_EVENT_MAY_BLOCK
);
568 nsresult
BounceTrackingProtectionStorage::CreateDatabaseConnection() {
569 MOZ_ASSERT(!NS_IsMainThread());
570 NS_ENSURE_TRUE(mDatabaseFile
, NS_ERROR_NULL_POINTER
);
572 nsCOMPtr
<mozIStorageService
> storage
=
573 do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID
);
574 NS_ENSURE_TRUE(storage
, NS_ERROR_UNEXPECTED
);
576 nsresult rv
= storage
->OpenDatabase(mDatabaseFile
,
577 mozIStorageService::CONNECTION_DEFAULT
,
578 getter_AddRefs(mDatabaseConnection
));
579 if (rv
== NS_ERROR_FILE_CORRUPTED
) {
580 rv
= mDatabaseFile
->Remove(false);
581 NS_ENSURE_SUCCESS(rv
, rv
);
582 rv
= storage
->OpenDatabase(mDatabaseFile
,
583 mozIStorageService::CONNECTION_DEFAULT
,
584 getter_AddRefs(mDatabaseConnection
));
586 NS_ENSURE_SUCCESS(rv
, rv
);
588 NS_ENSURE_TRUE(mDatabaseConnection
, NS_ERROR_UNEXPECTED
);
590 mDatabaseConnection
->GetConnectionReady(&ready
);
591 NS_ENSURE_TRUE(ready
, NS_ERROR_UNEXPECTED
);
593 return EnsureTable();
596 nsresult
BounceTrackingProtectionStorage::EnsureTable() {
597 MOZ_ASSERT(!NS_IsMainThread());
598 NS_ENSURE_TRUE(mDatabaseConnection
, NS_ERROR_UNEXPECTED
);
600 nsresult rv
= mDatabaseConnection
->SetSchemaVersion(SCHEMA_VERSION
);
601 NS_ENSURE_SUCCESS(rv
, rv
);
603 const constexpr auto createTableQuery
=
604 "CREATE TABLE IF NOT EXISTS sites ("
605 "originAttributeSuffix TEXT NOT NULL,"
606 "siteHost TEXT NOT NULL, "
607 "entryType INTEGER NOT NULL, "
608 "timeStamp INTEGER NOT NULL, "
609 "PRIMARY KEY (originAttributeSuffix, siteHost)"
612 return mDatabaseConnection
->ExecuteSimpleSQL(createTableQuery
);
615 nsresult
BounceTrackingProtectionStorage::LoadMemoryStateFromDisk() {
616 MOZ_ASSERT(!NS_IsMainThread(),
617 "Must not load the table from disk in the main thread.");
619 const constexpr auto selectAllQuery(
620 "SELECT originAttributeSuffix, siteHost, entryType, timeStamp FROM sites;"_ns
);
622 nsCOMPtr
<mozIStorageStatement
> readStmt
;
623 nsresult rv
= mDatabaseConnection
->CreateStatement(selectAllQuery
,
624 getter_AddRefs(readStmt
));
625 NS_ENSURE_SUCCESS(rv
, rv
);
628 // Collect DB entries into an array to hand to the main thread later.
629 nsTArray
<ImportEntry
> importEntries
;
630 while (NS_SUCCEEDED(readStmt
->ExecuteStep(&hasResult
)) && hasResult
) {
631 nsAutoCString originAttributeSuffix
, siteHost
;
635 rv
= readStmt
->GetUTF8String(0, originAttributeSuffix
);
636 NS_ENSURE_SUCCESS(rv
, rv
);
637 rv
= readStmt
->GetUTF8String(1, siteHost
);
638 NS_ENSURE_SUCCESS(rv
, rv
);
639 rv
= readStmt
->GetInt32(2, &typeInt
);
640 NS_ENSURE_SUCCESS(rv
, rv
);
641 rv
= readStmt
->GetInt64(3, &timeStamp
);
642 NS_ENSURE_SUCCESS(rv
, rv
);
644 // Convert entryType field to enum.
645 BounceTrackingProtectionStorage::EntryType entryType
=
646 static_cast<BounceTrackingProtectionStorage::EntryType
>(typeInt
);
647 // Check that the enum value is valid.
650 BounceTrackingProtectionStorage::EntryType::BounceTracker
&&
652 BounceTrackingProtectionStorage::EntryType::UserActivation
)) {
657 bool success
= oa
.PopulateFromSuffix(originAttributeSuffix
);
658 if (NS_WARN_IF(!success
)) {
662 // Collect entries to dispatch to main thread later.
663 importEntries
.AppendElement(
664 ImportEntry
{oa
, siteHost
, entryType
, timeStamp
});
667 // We can only access the state map on the main thread.
668 RefPtr
<BounceTrackingProtectionStorage
> self
= this;
669 return NS_DispatchToMainThread(NS_NewRunnableFunction(
670 "BounceTrackingProtectionStorage::LoadMemoryStateFromDisk",
671 [self
, importEntries
= std::move(importEntries
)]() {
672 // For each entry get or create BounceTrackingStateGlobal and insert it
673 // into global state map.
674 for (const ImportEntry
& entry
: importEntries
) {
675 RefPtr
<BounceTrackingStateGlobal
> stateGlobal
=
676 self
->GetOrCreateStateGlobal(entry
.mOriginAttributes
);
677 MOZ_ASSERT(stateGlobal
);
680 if (entry
.mEntryType
==
681 BounceTrackingProtectionStorage::EntryType::BounceTracker
) {
682 rv
= stateGlobal
->RecordBounceTracker(entry
.mSiteHost
,
683 entry
.mTimeStamp
, true);
685 rv
= stateGlobal
->RecordUserActivation(entry
.mSiteHost
,
686 entry
.mTimeStamp
, true);
688 if (NS_WARN_IF(NS_FAILED(rv
)) &&
689 MOZ_LOG_TEST(gBounceTrackingProtectionLog
, LogLevel::Debug
)) {
690 nsAutoCString originAttributeSuffix
;
691 entry
.mOriginAttributes
.CreateSuffix(originAttributeSuffix
);
693 MOZ_LOG(gBounceTrackingProtectionLog
, LogLevel::Debug
,
694 ("%s: Failed to load entry from disk: "
695 "originAttributeSuffix=%s, siteHost=%s, entryType=%d, "
696 "timeStamp=%" PRId64
,
697 __FUNCTION__
, originAttributeSuffix
.get(),
698 PromiseFlatCString(entry
.mSiteHost
).get(),
699 static_cast<uint8_t>(entry
.mEntryType
), entry
.mTimeStamp
));
705 void BounceTrackingProtectionStorage::IncrementPendingWrites() {
706 MonitorAutoLock
lock(mMonitor
);
707 MOZ_ASSERT(mPendingWrites
< std::numeric_limits
<uint32_t>::max());
711 void BounceTrackingProtectionStorage::DecrementPendingWrites() {
712 MonitorAutoLock
lock(mMonitor
);
713 MOZ_ASSERT(mPendingWrites
> 0);
718 nsresult
BounceTrackingProtectionStorage::UpsertData(
719 mozIStorageConnection
* aDatabaseConnection
,
720 const OriginAttributes
& aOriginAttributes
, const nsACString
& aSiteHost
,
721 BounceTrackingProtectionStorage::EntryType aEntryType
, PRTime aTimeStamp
) {
722 MOZ_ASSERT(!NS_IsMainThread(),
723 "Must not write to the table from the main thread.");
724 MOZ_ASSERT(aDatabaseConnection
);
725 MOZ_ASSERT(!aSiteHost
.IsEmpty());
726 MOZ_ASSERT(aTimeStamp
> 0);
727 MOZ_ASSERT(aOriginAttributes
.mPrivateBrowsingId
==
728 nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID
,
729 "Must not write private browsing data to the table.");
731 auto constexpr upsertQuery
=
732 "INSERT INTO sites (originAttributeSuffix, siteHost, entryType, "
734 "VALUES (:originAttributeSuffix, :siteHost, :entryType, :timeStamp)"
735 "ON CONFLICT (originAttributeSuffix, siteHost)"
736 "DO UPDATE SET entryType = :entryType, timeStamp = :timeStamp;"_ns
;
738 nsCOMPtr
<mozIStorageStatement
> upsertStmt
;
739 nsresult rv
= aDatabaseConnection
->CreateStatement(
740 upsertQuery
, getter_AddRefs(upsertStmt
));
741 NS_ENSURE_SUCCESS(rv
, rv
);
743 // Serialize OriginAttributes.
744 nsAutoCString originAttributeSuffix
;
745 aOriginAttributes
.CreateSuffix(originAttributeSuffix
);
747 rv
= upsertStmt
->BindUTF8StringByName("originAttributeSuffix"_ns
,
748 originAttributeSuffix
);
749 NS_ENSURE_SUCCESS(rv
, rv
);
751 rv
= upsertStmt
->BindUTF8StringByName("siteHost"_ns
, aSiteHost
);
752 NS_ENSURE_SUCCESS(rv
, rv
);
754 rv
= upsertStmt
->BindInt32ByName("entryType"_ns
,
755 static_cast<int32_t>(aEntryType
));
756 NS_ENSURE_SUCCESS(rv
, rv
);
758 rv
= upsertStmt
->BindInt64ByName("timeStamp"_ns
, aTimeStamp
);
759 NS_ENSURE_SUCCESS(rv
, rv
);
761 return upsertStmt
->Execute();
765 nsresult
BounceTrackingProtectionStorage::DeleteData(
766 mozIStorageConnection
* aDatabaseConnection
,
767 Maybe
<OriginAttributes
> aOriginAttributes
, const nsACString
& aSiteHost
) {
768 MOZ_ASSERT(!NS_IsMainThread(),
769 "Must not write to the table from the main thread.");
770 MOZ_ASSERT(aDatabaseConnection
);
771 MOZ_ASSERT(!aSiteHost
.IsEmpty());
772 MOZ_ASSERT(aOriginAttributes
.isNothing() ||
773 aOriginAttributes
->mPrivateBrowsingId
==
774 nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID
);
776 nsAutoCString
deleteQuery("DELETE FROM sites WHERE siteHost = :siteHost");
778 if (aOriginAttributes
) {
779 deleteQuery
.AppendLiteral(
780 " AND originAttributeSuffix = :originAttributeSuffix");
783 nsCOMPtr
<mozIStorageStatement
> upsertStmt
;
784 nsresult rv
= aDatabaseConnection
->CreateStatement(
785 deleteQuery
, getter_AddRefs(upsertStmt
));
786 NS_ENSURE_SUCCESS(rv
, rv
);
788 rv
= upsertStmt
->BindUTF8StringByName("siteHost"_ns
, aSiteHost
);
789 NS_ENSURE_SUCCESS(rv
, rv
);
791 if (aOriginAttributes
) {
792 nsAutoCString originAttributeSuffix
;
793 aOriginAttributes
->CreateSuffix(originAttributeSuffix
);
794 rv
= upsertStmt
->BindUTF8StringByName("originAttributeSuffix"_ns
,
795 originAttributeSuffix
);
796 NS_ENSURE_SUCCESS(rv
, rv
);
799 return upsertStmt
->Execute();
803 nsresult
BounceTrackingProtectionStorage::DeleteDataInTimeRange(
804 mozIStorageConnection
* aDatabaseConnection
,
805 Maybe
<OriginAttributes
> aOriginAttributes
, PRTime aFrom
, Maybe
<PRTime
> aTo
,
806 Maybe
<BounceTrackingProtectionStorage::EntryType
> aEntryType
) {
807 MOZ_ASSERT(!NS_IsMainThread(),
808 "Must not write to the table from the main thread.");
809 MOZ_ASSERT(aDatabaseConnection
);
810 MOZ_ASSERT(aOriginAttributes
.isNothing() ||
811 aOriginAttributes
->mPrivateBrowsingId
==
812 nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID
);
813 MOZ_ASSERT(aFrom
>= 0);
814 MOZ_ASSERT(aTo
.isNothing() || aTo
.value() > aFrom
);
816 nsAutoCString
deleteQuery(
818 "WHERE timeStamp >= :aFrom"_ns
);
821 deleteQuery
.AppendLiteral(" AND timeStamp <= :aTo");
824 if (aOriginAttributes
) {
825 deleteQuery
.AppendLiteral(
826 " AND originAttributeSuffix = :originAttributeSuffix");
829 if (aEntryType
.isSome()) {
830 deleteQuery
.AppendLiteral(" AND entryType = :entryType");
832 deleteQuery
.AppendLiteral(";");
834 nsCOMPtr
<mozIStorageStatement
> deleteStmt
;
835 nsresult rv
= aDatabaseConnection
->CreateStatement(
836 deleteQuery
, getter_AddRefs(deleteStmt
));
837 NS_ENSURE_SUCCESS(rv
, rv
);
839 rv
= deleteStmt
->BindInt64ByName("aFrom"_ns
, aFrom
);
840 NS_ENSURE_SUCCESS(rv
, rv
);
843 rv
= deleteStmt
->BindInt64ByName("aTo"_ns
, aTo
.value());
844 NS_ENSURE_SUCCESS(rv
, rv
);
847 if (aOriginAttributes
) {
848 nsAutoCString originAttributeSuffix
;
849 aOriginAttributes
->CreateSuffix(originAttributeSuffix
);
850 rv
= deleteStmt
->BindUTF8StringByName("originAttributeSuffix"_ns
,
851 originAttributeSuffix
);
852 NS_ENSURE_SUCCESS(rv
, rv
);
855 if (aEntryType
.isSome()) {
856 rv
= deleteStmt
->BindInt32ByName("entryType"_ns
,
857 static_cast<int32_t>(*aEntryType
));
858 NS_ENSURE_SUCCESS(rv
, rv
);
861 return deleteStmt
->Execute();
865 nsresult
BounceTrackingProtectionStorage::DeleteDataByType(
866 mozIStorageConnection
* aDatabaseConnection
,
867 const Maybe
<OriginAttributes
>& aOriginAttributes
,
868 BounceTrackingProtectionStorage::EntryType aEntryType
) {
869 MOZ_ASSERT(!NS_IsMainThread(),
870 "Must not write to the table from the main thread.");
871 MOZ_ASSERT(aDatabaseConnection
);
872 MOZ_ASSERT(aOriginAttributes
.isNothing() ||
873 aOriginAttributes
->mPrivateBrowsingId
==
874 nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID
);
876 nsAutoCString
deleteQuery(
878 "WHERE entryType = :entryType"_ns
);
880 if (aOriginAttributes
) {
881 deleteQuery
.AppendLiteral(
882 " AND originAttributeSuffix = :originAttributeSuffix");
885 deleteQuery
.AppendLiteral(";");
887 nsCOMPtr
<mozIStorageStatement
> deleteStmt
;
888 nsresult rv
= aDatabaseConnection
->CreateStatement(
889 deleteQuery
, getter_AddRefs(deleteStmt
));
890 NS_ENSURE_SUCCESS(rv
, rv
);
892 rv
= deleteStmt
->BindInt32ByName("entryType"_ns
,
893 static_cast<int32_t>(aEntryType
));
894 NS_ENSURE_SUCCESS(rv
, rv
);
896 if (aOriginAttributes
) {
897 nsAutoCString originAttributeSuffix
;
898 aOriginAttributes
->CreateSuffix(originAttributeSuffix
);
899 rv
= deleteStmt
->BindUTF8StringByName("originAttributeSuffix"_ns
,
900 originAttributeSuffix
);
901 NS_ENSURE_SUCCESS(rv
, rv
);
904 return deleteStmt
->Execute();
907 nsresult
BounceTrackingProtectionStorage::DeleteDataByOriginAttributesPattern(
908 mozIStorageConnection
* aDatabaseConnection
,
909 const OriginAttributesPattern
& aOriginAttributesPattern
,
910 const Maybe
<nsCString
>& aSiteHost
) {
911 MOZ_ASSERT(!NS_IsMainThread(),
912 "Must not write to the table from the main thread.");
913 MOZ_ASSERT(aDatabaseConnection
);
914 if (aSiteHost
.isSome()) {
915 MOZ_ASSERT(!aSiteHost
->IsEmpty(), "aSiteHost must not be empty.");
918 nsAutoCString
deleteQuery(
919 "DELETE FROM sites WHERE "
920 "ORIGIN_ATTRS_PATTERN_MATCH_OA_SUFFIX(originAttributeSuffix)");
922 // Filtering by site host is optional.
923 if (aSiteHost
.isSome()) {
924 deleteQuery
.AppendLiteral(" AND siteHost = :siteHost");
927 deleteQuery
.AppendLiteral(";");
929 nsCOMPtr
<mozIStorageFunction
> patternMatchFunction(
930 new OriginAttrsPatternMatchOASuffixSQLFunction(aOriginAttributesPattern
));
932 nsresult rv
= aDatabaseConnection
->CreateFunction(
933 "ORIGIN_ATTRS_PATTERN_MATCH_OA_SUFFIX"_ns
, 1, patternMatchFunction
);
934 NS_ENSURE_SUCCESS(rv
, rv
);
936 nsCOMPtr
<mozIStorageStatement
> deleteStmt
;
937 rv
= aDatabaseConnection
->CreateStatement(deleteQuery
,
938 getter_AddRefs(deleteStmt
));
939 NS_ENSURE_SUCCESS(rv
, rv
);
941 if (aSiteHost
.isSome()) {
942 rv
= deleteStmt
->BindUTF8StringByName("siteHost"_ns
, aSiteHost
.ref());
943 NS_ENSURE_SUCCESS(rv
, rv
);
946 rv
= deleteStmt
->Execute();
947 NS_ENSURE_SUCCESS(rv
, rv
);
949 return aDatabaseConnection
->RemoveFunction(
950 "ORIGIN_ATTRS_PATTERN_MATCH_OA_SUFFIX"_ns
);
954 nsresult
BounceTrackingProtectionStorage::ClearData(
955 mozIStorageConnection
* aDatabaseConnection
) {
956 MOZ_ASSERT(!NS_IsMainThread(),
957 "Must not write to the table from the main thread.");
958 NS_ENSURE_ARG_POINTER(aDatabaseConnection
);
959 return aDatabaseConnection
->ExecuteSimpleSQL("DELETE FROM sites;"_ns
);
962 NS_IMPL_ISUPPORTS(OriginAttrsPatternMatchOASuffixSQLFunction
,
966 OriginAttrsPatternMatchOASuffixSQLFunction::OnFunctionCall(
967 mozIStorageValueArray
* aFunctionArguments
, nsIVariant
** aResult
) {
970 nsAutoCString originAttributeSuffix
;
971 rv
= aFunctionArguments
->GetUTF8String(0, originAttributeSuffix
);
972 NS_ENSURE_SUCCESS(rv
, rv
);
974 OriginAttributes originAttributes
;
975 bool parsedSuccessfully
=
976 originAttributes
.PopulateFromSuffix(originAttributeSuffix
);
977 NS_ENSURE_TRUE(parsedSuccessfully
, NS_ERROR_FAILURE
);
979 bool result
= mPattern
.Matches(originAttributes
);
981 RefPtr
<nsVariant
> outVar(new nsVariant());
982 rv
= outVar
->SetAsBool(result
);
983 NS_ENSURE_SUCCESS(rv
, rv
);
985 outVar
.forget(aResult
);
989 } // namespace mozilla