Bug 1943650 - Command-line --help output misformatted after --dbus-service. r=emilio
[gecko.git] / toolkit / components / antitracking / bouncetrackingprotection / BounceTrackingProtectionStorage.cpp
blob3aac946065fd2f1269c5cccbc645c905b107244f
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"
6 #include <cstdint>
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"
20 #include "nsCOMPtr.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"
28 #include "nscore.h"
29 #include "nsAppDirectoryServiceDefs.h"
30 #include "nsCRT.h"
32 #define BOUNCE_TRACKING_PROTECTION_DB_FILENAME \
33 "bounce-tracking-protection.sqlite"_ns
34 #define SCHEMA_VERSION 1
36 namespace mozilla {
38 NS_IMPL_ISUPPORTS(BounceTrackingProtectionStorage, nsIAsyncShutdownBlocker,
39 nsIObserver);
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,
71 aOriginAttributes);
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);
96 if (stateGlobal) {
97 nsresult rv = stateGlobal->ClearSiteHost(aSiteHost, true);
98 NS_ENSURE_SUCCESS(rv, rv);
100 } else {
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()) {
114 return NS_OK;
116 return DeleteDBEntries(aOriginAttributes, aSiteHost);
119 nsresult BounceTrackingProtectionStorage::ClearByTimeRange(PRTime aFrom,
120 PRTime aTo) {
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.
125 nsresult rv =
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())) {
143 continue;
146 // If there is no site host filtering we can remove the entire state global.
147 if (aSiteHost.isNothing()) {
148 iter.Remove();
149 continue;
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) {
163 return NS_OK;
165 return DeleteDBEntriesByOriginAttributesPattern(aOriginAttributesPattern,
166 aSiteHost);
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]() {
199 nsresult rv =
200 UpsertData(self->mDatabaseConnection, aOriginAttributes,
201 siteHost, aEntryType, aTimeStamp);
202 self->DecrementPendingWrites();
203 NS_ENSURE_SUCCESS_VOID(rv);
205 NS_DISPATCH_EVENT_MAY_BLOCK);
207 return NS_OK;
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]() {
243 nsresult rv =
244 DeleteData(self->mDatabaseConnection,
245 originAttributes, siteHost);
246 self->DecrementPendingWrites();
247 NS_ENSURE_SUCCESS_VOID(rv);
249 NS_DISPATCH_EVENT_MAY_BLOCK);
251 return NS_OK;
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",
267 [self]() {
268 nsresult rv =
269 ClearData(self->mDatabaseConnection);
270 self->DecrementPendingWrites();
271 NS_ENSURE_SUCCESS_VOID(rv);
273 NS_DISPATCH_EVENT_MAY_BLOCK);
274 return NS_OK;
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]() {
298 nsresult rv =
299 DeleteDataInTimeRange(self->mDatabaseConnection,
300 originAttributes, aFrom, aTo, aEntryType);
301 self->DecrementPendingWrites();
302 NS_ENSURE_SUCCESS_VOID(rv);
304 NS_DISPATCH_EVENT_MAY_BLOCK);
305 return NS_OK;
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);
333 return NS_OK;
336 nsresult
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);
366 return NS_OK;
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",
387 [self]() {
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);
407 return NS_OK;
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) {
415 mMonitor.Wait();
417 if (mErrored) {
418 return NS_ERROR_FAILURE;
420 if (mShuttingDown) {
421 return NS_ERROR_NOT_AVAILABLE;
423 return NS_OK;
426 void BounceTrackingProtectionStorage::Finalize() {
427 nsCOMPtr<nsIAsyncShutdownClient> asc = GetAsyncShutdownBarrier();
428 MOZ_ASSERT(asc);
429 DebugOnly<nsresult> rv = asc->RemoveBlocker(this);
430 MOZ_ASSERT(NS_SUCCEEDED(rv));
433 // nsIObserver
435 NS_IMETHODIMP
436 BounceTrackingProtectionStorage::Observe(nsISupports* aSubject,
437 const char* aTopic,
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()) {
450 iter.Remove();
451 removedCount++;
454 MOZ_LOG(
455 gBounceTrackingProtectionLog, LogLevel::Debug,
456 ("%s: last-pb-context-exited: Removed %d private browsing state globals",
457 __FUNCTION__, removedCount));
459 return NS_OK;
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**) {
478 return NS_OK;
481 NS_IMETHODIMP BounceTrackingProtectionStorage::GetName(nsAString& aName) {
482 aName.AssignLiteral("BounceTrackingProtectionStorage: Flushing to disk");
483 return NS_OK;
486 nsresult BounceTrackingProtectionStorage::Init() {
487 nsresult rv = InitInternal();
488 if (NS_FAILED(rv)) {
489 MonitorAutoLock lock(mMonitor);
490 mErrored.Flip();
491 mMonitor.NotifyAll();
493 return rv;
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,
502 NS_ERROR_FAILURE);
504 // Register a shutdown blocker so we can flush pending changes to disk before
505 // shutdown.
506 // Init may also be called during shutdown, e.g. because of clearing data
507 // during shutdown.
508 nsCOMPtr<nsIAsyncShutdownClient> shutdownBarrier = GetAsyncShutdownBarrier();
509 NS_ENSURE_TRUE(shutdownBarrier, NS_ERROR_FAILURE);
511 bool closed;
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.
537 NS_ENSURE_SUCCESS(
538 NS_CreateBackgroundTaskQueue("BounceTrackingProtectionStorage",
539 getter_AddRefs(mBackgroundThread)),
540 NS_ERROR_FAILURE);
542 RefPtr<BounceTrackingProtectionStorage> self = this;
544 return mBackgroundThread->Dispatch(
545 NS_NewRunnableFunction("BounceTrackingProtectionStorage::Init",
546 [self]() {
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();
552 return;
555 rv = self->LoadMemoryStateFromDisk();
556 if (NS_WARN_IF(NS_FAILED(rv))) {
557 self->mErrored.Flip();
558 self->mMonitor.NotifyAll();
559 return;
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);
589 bool ready = false;
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)"
610 ");"_ns;
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);
627 bool hasResult;
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;
632 int64_t timeStamp;
633 int32_t typeInt;
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.
648 if (NS_WARN_IF(
649 entryType !=
650 BounceTrackingProtectionStorage::EntryType::BounceTracker &&
651 entryType !=
652 BounceTrackingProtectionStorage::EntryType::UserActivation)) {
653 continue;
656 OriginAttributes oa;
657 bool success = oa.PopulateFromSuffix(originAttributeSuffix);
658 if (NS_WARN_IF(!success)) {
659 continue;
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);
679 nsresult rv;
680 if (entry.mEntryType ==
681 BounceTrackingProtectionStorage::EntryType::BounceTracker) {
682 rv = stateGlobal->RecordBounceTracker(entry.mSiteHost,
683 entry.mTimeStamp, true);
684 } else {
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));
702 }));
705 void BounceTrackingProtectionStorage::IncrementPendingWrites() {
706 MonitorAutoLock lock(mMonitor);
707 MOZ_ASSERT(mPendingWrites < std::numeric_limits<uint32_t>::max());
708 mPendingWrites++;
711 void BounceTrackingProtectionStorage::DecrementPendingWrites() {
712 MonitorAutoLock lock(mMonitor);
713 MOZ_ASSERT(mPendingWrites > 0);
714 mPendingWrites--;
717 // static
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, "
733 "timeStamp)"
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();
764 // static
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();
802 // static
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(
817 "DELETE FROM sites "
818 "WHERE timeStamp >= :aFrom"_ns);
820 if (aTo.isSome()) {
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);
842 if (aTo.isSome()) {
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();
864 // static
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(
877 "DELETE FROM sites "
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);
953 // static
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,
963 mozIStorageFunction)
965 NS_IMETHODIMP
966 OriginAttrsPatternMatchOASuffixSQLFunction::OnFunctionCall(
967 mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult) {
968 nsresult rv;
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);
986 return NS_OK;
989 } // namespace mozilla