diff options
Diffstat (limited to 'toolkit/components/antitracking/bouncetrackingprotection')
12 files changed, 339 insertions, 63 deletions
diff --git a/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingProtection.cpp b/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingProtection.cpp index e5d9ccfea9..2b0577d5c6 100644 --- a/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingProtection.cpp +++ b/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingProtection.cpp @@ -12,6 +12,7 @@ #include "ErrorList.h" #include "mozilla/AlreadyAddRefed.h" #include "mozilla/ClearOnShutdown.h" +#include "mozilla/ContentBlockingAllowList.h" #include "mozilla/Logging.h" #include "mozilla/Services.h" #include "mozilla/StaticPrefs_privacy.h" @@ -122,7 +123,7 @@ nsresult BounceTrackingProtection::RecordStatefulBounces( aBounceTrackingState->Describe().get())); // Assert: navigable’s bounce tracking record is not null. - BounceTrackingRecord* record = + const Maybe<BounceTrackingRecord>& record = aBounceTrackingState->GetBounceTrackingRecord(); NS_ENSURE_TRUE(record, NS_ERROR_FAILURE); @@ -369,6 +370,11 @@ BounceTrackingProtection::TestRunPurgeBounceTrackers( } NS_IMETHODIMP +BounceTrackingProtection::TestClearExpiredUserActivations() { + return ClearExpiredUserInteractions(); +} + +NS_IMETHODIMP BounceTrackingProtection::TestAddBounceTrackerCandidate( JS::Handle<JS::Value> aOriginAttributes, const nsACString& aHost, const PRTime aBounceTime, JSContext* aCx) { @@ -415,6 +421,22 @@ BounceTrackingProtection::TestAddUserActivation( RefPtr<BounceTrackingProtection::PurgeBounceTrackersMozPromise> BounceTrackingProtection::PurgeBounceTrackers() { + // Prevent multiple purge operations from running at the same time. + if (mPurgeInProgress) { + MOZ_LOG(gBounceTrackingProtectionLog, LogLevel::Debug, + ("%s: Skip: Purge already in progress.", __FUNCTION__)); + return PurgeBounceTrackersMozPromise::CreateAndReject( + nsresult::NS_ERROR_NOT_AVAILABLE, __func__); + } + mPurgeInProgress = true; + + // Obtain a cache of ContentBlockingAllowList permissions so we only need to + // fetch permissions once even when we do multiple base domain lookups. + ContentBlockingAllowListCache contentBlockingAllowListCache; + + // Collect promises for all clearing operations to later await on. + nsTArray<RefPtr<ClearDataMozPromise>> clearPromises; + // Run the purging algorithm for all global state objects. for (const auto& entry : mStorage->StateGlobalMapRef()) { const OriginAttributes& originAttributes = entry.GetKey(); @@ -429,13 +451,17 @@ BounceTrackingProtection::PurgeBounceTrackers() { oaSuffix.get())); } - PurgeBounceTrackersForStateGlobal(stateGlobal, originAttributes); + nsresult rv = PurgeBounceTrackersForStateGlobal( + stateGlobal, contentBlockingAllowListCache, clearPromises); + if (NS_WARN_IF(NS_FAILED(rv))) { + return PurgeBounceTrackersMozPromise::CreateAndReject(rv, __func__); + } } // Wait for all data clearing operations to complete. mClearPromises contains // one promise per host / clear task. return ClearDataMozPromise::AllSettled(GetCurrentSerialEventTarget(), - mClearPromises) + clearPromises) ->Then( GetCurrentSerialEventTarget(), __func__, [&](ClearDataMozPromise::AllSettledPromiseType::ResolveOrRejectValue&& @@ -450,7 +476,7 @@ BounceTrackingProtection::PurgeBounceTrackers() { // If any clear call failed reject. for (auto& result : aResults.ResolveValue()) { if (result.IsReject()) { - mClearPromises.Clear(); + mPurgeInProgress = false; return PurgeBounceTrackersMozPromise::CreateAndReject( NS_ERROR_FAILURE, __func__); } @@ -458,7 +484,8 @@ BounceTrackingProtection::PurgeBounceTrackers() { } // No clearing errors, resolve. - mClearPromises.Clear(); + + mPurgeInProgress = false; return PurgeBounceTrackersMozPromise::CreateAndResolve( std::move(purgedSiteHosts), __func__); }); @@ -466,34 +493,17 @@ BounceTrackingProtection::PurgeBounceTrackers() { nsresult BounceTrackingProtection::PurgeBounceTrackersForStateGlobal( BounceTrackingStateGlobal* aStateGlobal, - const OriginAttributes& aOriginAttributes) { + ContentBlockingAllowListCache& aContentBlockingAllowList, + nsTArray<RefPtr<ClearDataMozPromise>>& aClearPromises) { MOZ_ASSERT(aStateGlobal); MOZ_LOG(gBounceTrackingProtectionLog, LogLevel::Debug, - ("%s: #mUserActivation: %d, #mBounceTrackers: %d", __FUNCTION__, - aStateGlobal->UserActivationMapRef().Count(), - aStateGlobal->BounceTrackersMapRef().Count())); - - // Purge already in progress. - if (!mClearPromises.IsEmpty()) { - MOZ_LOG(gBounceTrackingProtectionLog, LogLevel::Debug, - ("%s: Skip: Purge already in progress.", __FUNCTION__)); - return NS_ERROR_NOT_AVAILABLE; - } + ("%s: %s", __FUNCTION__, aStateGlobal->Describe().get())); const PRTime now = PR_Now(); - // Convert the user activation lifetime into microseconds for calculation with - // PRTime values. The pref is a 32-bit value. Cast into 64-bit before - // multiplying so we get the correct result. - int64_t activationLifetimeUsec = - static_cast<int64_t>( - StaticPrefs:: - privacy_bounceTrackingProtection_bounceTrackingActivationLifetimeSec()) * - PR_USEC_PER_SEC; // 1. Remove hosts from the user activation map whose user activation flag has // expired. - nsresult rv = - aStateGlobal->ClearUserActivationBefore(now - activationLifetimeUsec); + nsresult rv = ClearExpiredUserInteractions(aStateGlobal); NS_ENSURE_SUCCESS(rv, rv); // 2. Go over bounce tracker candidate map and purge state. @@ -502,7 +512,6 @@ nsresult BounceTrackingProtection::PurgeBounceTrackersForStateGlobal( do_GetService("@mozilla.org/clear-data-service;1", &rv); NS_ENSURE_SUCCESS(rv, rv); - mClearPromises.Clear(); nsTArray<nsCString> purgedSiteHosts; // Collect hosts to remove from the bounce trackers map. We can not remove @@ -545,6 +554,27 @@ nsresult BounceTrackingProtection::PurgeBounceTrackersForStateGlobal( continue; } + // Gecko specific: If the host is on the content blocking allow-list, + // continue. + bool isAllowListed = false; + rv = aContentBlockingAllowList.CheckForBaseDomain( + host, aStateGlobal->OriginAttributesRef(), isAllowListed); + if (NS_WARN_IF(NS_FAILED(rv))) { + continue; + } + if (isAllowListed) { + if (MOZ_LOG_TEST(gBounceTrackingProtectionLog, LogLevel::Debug)) { + nsAutoCString originAttributeSuffix; + aStateGlobal->OriginAttributesRef().CreateSuffix(originAttributeSuffix); + MOZ_LOG(gBounceTrackingProtectionLog, LogLevel::Debug, + ("%s: Skip host on the content blocking allow-list: host: %s, " + "originAttributes: %s", + __FUNCTION__, PromiseFlatCString(host).get(), + originAttributeSuffix.get())); + } + continue; + } + // No exception above applies, clear state for the given host. RefPtr<ClearDataMozPromise::Private> clearPromise = @@ -562,7 +592,7 @@ nsresult BounceTrackingProtection::PurgeBounceTrackersForStateGlobal( clearPromise->Reject(0, __func__); } - mClearPromises.AppendElement(clearPromise); + aClearPromises.AppendElement(clearPromise); // Remove it from the bounce trackers map, it's about to be purged. If the // clear call fails still remove it. We want to avoid an ever growing list @@ -575,6 +605,43 @@ nsresult BounceTrackingProtection::PurgeBounceTrackersForStateGlobal( return aStateGlobal->RemoveBounceTrackers(bounceTrackerCandidatesToRemove); } +nsresult BounceTrackingProtection::ClearExpiredUserInteractions( + BounceTrackingStateGlobal* aStateGlobal) { + if (!aStateGlobal && mStorage->StateGlobalMapRef().IsEmpty()) { + // Nothing to clear. + return NS_OK; + } + + const PRTime now = PR_Now(); + + // Convert the user activation lifetime into microseconds for calculation with + // PRTime values. The pref is a 32-bit value. Cast into 64-bit before + // multiplying so we get the correct result. + int64_t activationLifetimeUsec = + static_cast<int64_t>( + StaticPrefs:: + privacy_bounceTrackingProtection_bounceTrackingActivationLifetimeSec()) * + PR_USEC_PER_SEC; + + // Clear user activation for the given state global. + if (aStateGlobal) { + return aStateGlobal->ClearUserActivationBefore(now - + activationLifetimeUsec); + } + + // aStateGlobal not passed, clear user activation for all state globals. + for (const auto& entry : mStorage->StateGlobalMapRef()) { + const RefPtr<BounceTrackingStateGlobal>& stateGlobal = entry.GetData(); + MOZ_ASSERT(stateGlobal); + + nsresult rv = + stateGlobal->ClearUserActivationBefore(now - activationLifetimeUsec); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + // ClearDataCallback NS_IMPL_ISUPPORTS(BounceTrackingProtection::ClearDataCallback, diff --git a/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingProtection.h b/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingProtection.h index 98c61504c0..e99cf895be 100644 --- a/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingProtection.h +++ b/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingProtection.h @@ -17,6 +17,7 @@ namespace mozilla { class BounceTrackingState; class BounceTrackingStateGlobal; class BounceTrackingProtectionStorage; +class ContentBlockingAllowListCache; class OriginAttributes; extern LazyLogModule gBounceTrackingProtectionLog; @@ -33,10 +34,17 @@ class BounceTrackingProtection final : public nsIBounceTrackingProtection { // navigation start for bounce tracking, or if the client bounce detection // timer expires after process response received for bounce tracking without // observing a client redirect. - nsresult RecordStatefulBounces(BounceTrackingState* aBounceTrackingState); + [[nodiscard]] nsresult RecordStatefulBounces( + BounceTrackingState* aBounceTrackingState); // Stores a user activation flag with a timestamp for the given principal. - nsresult RecordUserActivation(nsIPrincipal* aPrincipal); + [[nodiscard]] nsresult RecordUserActivation(nsIPrincipal* aPrincipal); + + // Clears expired user interaction flags for the given state global. If + // aStateGlobal == nullptr, clears expired user interaction flags for all + // state globals. + [[nodiscard]] nsresult ClearExpiredUserInteractions( + BounceTrackingStateGlobal* aStateGlobal = nullptr); private: BounceTrackingProtection(); @@ -53,13 +61,19 @@ class BounceTrackingProtection final : public nsIBounceTrackingProtection { MozPromise<nsTArray<nsCString>, nsresult, true>; RefPtr<PurgeBounceTrackersMozPromise> PurgeBounceTrackers(); - nsresult PurgeBounceTrackersForStateGlobal( - BounceTrackingStateGlobal* aStateGlobal, - const OriginAttributes& aOriginAttributes); - // Pending clear operations are stored as ClearDataMozPromise, one per host. using ClearDataMozPromise = MozPromise<nsCString, uint32_t, true>; - nsTArray<RefPtr<ClearDataMozPromise>> mClearPromises; + + // Clear state for classified bounce trackers for a specific state global. + // aClearPromises is populated with promises for each host that is cleared. + [[nodiscard]] nsresult PurgeBounceTrackersForStateGlobal( + BounceTrackingStateGlobal* aStateGlobal, + ContentBlockingAllowListCache& aContentBlockingAllowList, + nsTArray<RefPtr<ClearDataMozPromise>>& aClearPromises); + + // Whether a purge operation is currently in progress. This avoids running + // multiple purge operations at the same time. + bool mPurgeInProgress = false; // Wraps nsIClearDataCallback in MozPromise. class ClearDataCallback final : public nsIClearDataCallback { diff --git a/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingRecord.cpp b/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingRecord.cpp index 14ee178ae2..d4b33f9edb 100644 --- a/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingRecord.cpp +++ b/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingRecord.cpp @@ -12,13 +12,11 @@ namespace mozilla { extern LazyLogModule gBounceTrackingProtectionLog; -NS_IMPL_CYCLE_COLLECTION(BounceTrackingRecord); - void BounceTrackingRecord::SetInitialHost(const nsACString& aHost) { mInitialHost = aHost; } -const nsACString& BounceTrackingRecord::GetInitialHost() { +const nsACString& BounceTrackingRecord::GetInitialHost() const { return mInitialHost; } @@ -26,7 +24,9 @@ void BounceTrackingRecord::SetFinalHost(const nsACString& aHost) { mFinalHost = aHost; } -const nsACString& BounceTrackingRecord::GetFinalHost() { return mFinalHost; } +const nsACString& BounceTrackingRecord::GetFinalHost() const { + return mFinalHost; +} void BounceTrackingRecord::AddBounceHost(const nsACString& aHost) { mBounceHosts.Insert(aHost); @@ -57,11 +57,12 @@ void BounceTrackingRecord::AddStorageAccessHost(const nsACString& aHost) { mStorageAccessHosts.Insert(aHost); } -const nsTHashSet<nsCString>& BounceTrackingRecord::GetBounceHosts() { +const nsTHashSet<nsCString>& BounceTrackingRecord::GetBounceHosts() const { return mBounceHosts; } -const nsTHashSet<nsCString>& BounceTrackingRecord::GetStorageAccessHosts() { +const nsTHashSet<nsCString>& BounceTrackingRecord::GetStorageAccessHosts() + const { return mStorageAccessHosts; } diff --git a/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingRecord.h b/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingRecord.h index d3e980d00b..73985a00a4 100644 --- a/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingRecord.h +++ b/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingRecord.h @@ -7,9 +7,7 @@ #ifndef mozilla_BounceTrackingRecord_h #define mozilla_BounceTrackingRecord_h -#include "nsISupports.h" #include "nsStringFwd.h" -#include "nsCycleCollectionParticipant.h" #include "nsTHashSet.h" namespace mozilla { @@ -22,31 +20,26 @@ class CanonicalBrowsingContext; // navigation. class BounceTrackingRecord final { public: - NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(BounceTrackingRecord); - NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(BounceTrackingRecord); - void SetInitialHost(const nsACString& aHost); - const nsACString& GetInitialHost(); + const nsACString& GetInitialHost() const; void SetFinalHost(const nsACString& aHost); - const nsACString& GetFinalHost(); + const nsACString& GetFinalHost() const; void AddBounceHost(const nsACString& aHost); void AddStorageAccessHost(const nsACString& aHost); - const nsTHashSet<nsCString>& GetBounceHosts(); + const nsTHashSet<nsCString>& GetBounceHosts() const; - const nsTHashSet<nsCString>& GetStorageAccessHosts(); + const nsTHashSet<nsCString>& GetStorageAccessHosts() const; // Create a string that describes this record. Used for logging. nsCString Describe(); private: - ~BounceTrackingRecord() = default; - // A site's host. The initiator site of the current extended navigation. nsAutoCString mInitialHost; diff --git a/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingState.cpp b/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingState.cpp index c5abb8b8d7..b4af3daa07 100644 --- a/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingState.cpp +++ b/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingState.cpp @@ -143,10 +143,11 @@ nsresult BounceTrackingState::Init( } void BounceTrackingState::ResetBounceTrackingRecord() { - mBounceTrackingRecord = nullptr; + mBounceTrackingRecord = Nothing(); } -BounceTrackingRecord* BounceTrackingState::GetBounceTrackingRecord() { +const Maybe<BounceTrackingRecord>& +BounceTrackingState::GetBounceTrackingRecord() { return mBounceTrackingRecord; } @@ -452,7 +453,7 @@ nsresult BounceTrackingState::OnStartNavigation( // tracking record to a new bounce tracking record with initial host set to // initialHost. if (!mBounceTrackingRecord) { - mBounceTrackingRecord = new BounceTrackingRecord(); + mBounceTrackingRecord = Some(BounceTrackingRecord()); mBounceTrackingRecord->SetInitialHost(siteHost); MOZ_LOG(gBounceTrackingProtectionLog, LogLevel::Debug, @@ -477,7 +478,7 @@ nsresult BounceTrackingState::OnStartNavigation( NS_ENSURE_SUCCESS(rv, rv); MOZ_ASSERT(!mBounceTrackingRecord); - mBounceTrackingRecord = new BounceTrackingRecord(); + mBounceTrackingRecord = Some(BounceTrackingRecord()); mBounceTrackingRecord->SetInitialHost(siteHost); return NS_OK; @@ -539,8 +540,12 @@ nsresult BounceTrackingState::OnResponseReceived( ("%s: Calling RecordStatefulBounces after timeout.", __FUNCTION__)); BounceTrackingState* bounceTrackingState = thisWeak; - bounceTrackingState->mBounceTrackingProtection->RecordStatefulBounces( - bounceTrackingState); + DebugOnly<nsresult> rv = + bounceTrackingState->mBounceTrackingProtection + ->RecordStatefulBounces(bounceTrackingState); + NS_WARNING_ASSERTION( + NS_SUCCEEDED(rv), + "Running RecordStatefulBounces after a timeout failed."); bounceTrackingState->mClientBounceDetectionTimeout = nullptr; }, diff --git a/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingState.h b/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingState.h index 70deee5abe..17d324bda9 100644 --- a/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingState.h +++ b/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingState.h @@ -7,6 +7,7 @@ #ifndef mozilla_BounceTrackingState_h #define mozilla_BounceTrackingState_h +#include "BounceTrackingRecord.h" #include "mozilla/WeakPtr.h" #include "mozilla/OriginAttributes.h" #include "nsIPrincipal.h" @@ -22,7 +23,6 @@ class nsIPrincipal; namespace mozilla { class BounceTrackingProtection; -class BounceTrackingRecord; namespace dom { class CanonicalBrowsingContext; @@ -55,7 +55,7 @@ class BounceTrackingState : public nsIWebProgressListener, static void ResetAllForOriginAttributesPattern( const OriginAttributesPattern& aPattern); - BounceTrackingRecord* GetBounceTrackingRecord(); + const Maybe<BounceTrackingRecord>& GetBounceTrackingRecord(); void ResetBounceTrackingRecord(); @@ -113,7 +113,7 @@ class BounceTrackingState : public nsIWebProgressListener, // Record to keep track of extended navigation data. Reset on extended // navigation end. - RefPtr<BounceTrackingRecord> mBounceTrackingRecord; + Maybe<BounceTrackingRecord> mBounceTrackingRecord; // Timer to wait to wait for a client redirect after a navigation ends. RefPtr<nsITimer> mClientBounceDetectionTimeout; diff --git a/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingStateGlobal.cpp b/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingStateGlobal.cpp index 3481753431..b94c887c90 100644 --- a/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingStateGlobal.cpp +++ b/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingStateGlobal.cpp @@ -106,6 +106,11 @@ nsresult BounceTrackingStateGlobal::ClearByTimeRange( NS_ENSURE_ARG_MIN(aFrom, 0); NS_ENSURE_TRUE(!aTo || aTo.value() > aFrom, NS_ERROR_INVALID_ARG); + MOZ_LOG(gBounceTrackingProtectionLog, LogLevel::Debug, + ("%s: Clearing user activations by time range from %" PRIu64 + " to %" PRIu64 " %s", + __FUNCTION__, aFrom, aTo.valueOr(0), Describe().get())); + // Clear in memory user activation data. if (aEntryType.isNothing() || aEntryType.value() == @@ -113,10 +118,10 @@ nsresult BounceTrackingStateGlobal::ClearByTimeRange( for (auto iter = mUserActivation.Iter(); !iter.Done(); iter.Next()) { if (iter.Data() >= aFrom && (aTo.isNothing() || iter.Data() <= aTo.value())) { - iter.Remove(); MOZ_LOG(gBounceTrackingProtectionLog, LogLevel::Debug, ("%s: Remove user activation for %s", __FUNCTION__, PromiseFlatCString(iter.Key()).get())); + iter.Remove(); } } } @@ -128,10 +133,10 @@ nsresult BounceTrackingStateGlobal::ClearByTimeRange( for (auto iter = mBounceTrackers.Iter(); !iter.Done(); iter.Next()) { if (iter.Data() >= aFrom && (aTo.isNothing() || iter.Data() <= aTo.value())) { - iter.Remove(); MOZ_LOG(gBounceTrackingProtectionLog, LogLevel::Debug, ("%s: Remove bouncer tracker for %s", __FUNCTION__, PromiseFlatCString(iter.Key()).get())); + iter.Remove(); } } } @@ -187,4 +192,27 @@ nsresult BounceTrackingStateGlobal::RemoveBounceTrackers( return NS_OK; } +// static +nsCString BounceTrackingStateGlobal::DescribeMap( + const nsTHashMap<nsCStringHashKey, PRTime>& aMap) { + nsAutoCString mapStr; + + for (auto iter = aMap.ConstIter(); !iter.Done(); iter.Next()) { + mapStr.Append(nsPrintfCString("{ %s: %" PRIu64 " }, ", + PromiseFlatCString(iter.Key()).get(), + iter.Data())); + } + + return std::move(mapStr); +} + +nsCString BounceTrackingStateGlobal::Describe() { + nsAutoCString originAttributeSuffix; + mOriginAttributes.CreateSuffix(originAttributeSuffix); + return nsPrintfCString( + "{ mOriginAttributes: %s, mUserActivation: %s, mBounceTrackers: %s }", + originAttributeSuffix.get(), DescribeMap(mUserActivation).get(), + DescribeMap(mBounceTrackers).get()); +} + } // namespace mozilla diff --git a/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingStateGlobal.h b/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingStateGlobal.h index 6680ceae6f..c8ed72c11f 100644 --- a/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingStateGlobal.h +++ b/toolkit/components/antitracking/bouncetrackingprotection/BounceTrackingStateGlobal.h @@ -37,6 +37,10 @@ class BounceTrackingStateGlobal final { bool ShouldPersistToDisk() const { return !IsPrivateBrowsing(); } + const OriginAttributes& OriginAttributesRef() const { + return mOriginAttributes; + }; + bool HasUserActivation(const nsACString& aSiteHost) const; // Store a user interaction flag for the given host. This will remove the @@ -79,6 +83,9 @@ class BounceTrackingStateGlobal final { return mBounceTrackers; } + // Create a string that describes this object. Used for logging. + nsCString Describe(); + private: ~BounceTrackingStateGlobal() = default; @@ -103,6 +110,10 @@ class BounceTrackingStateGlobal final { // on the given site host performed an action that could indicate stateful // bounce tracking took place. nsTHashMap<nsCStringHashKey, PRTime> mBounceTrackers{}; + + // Helper to create a string representation of a siteHost -> timestamp map. + static nsCString DescribeMap( + const nsTHashMap<nsCStringHashKey, PRTime>& aMap); }; } // namespace mozilla diff --git a/toolkit/components/antitracking/bouncetrackingprotection/nsIBounceTrackingProtection.idl b/toolkit/components/antitracking/bouncetrackingprotection/nsIBounceTrackingProtection.idl index 9ade9cb0ea..1163492333 100644 --- a/toolkit/components/antitracking/bouncetrackingprotection/nsIBounceTrackingProtection.idl +++ b/toolkit/components/antitracking/bouncetrackingprotection/nsIBounceTrackingProtection.idl @@ -28,6 +28,10 @@ interface nsIBounceTrackingProtection : nsISupports { [implicit_jscontext] Promise testRunPurgeBounceTrackers(); + // Clear expired user activation flags. Expiry is set via pref + // "privacy.bounceTrackingProtection.bounceTrackingActivationLifetimeSec". + void testClearExpiredUserActivations(); + // Getters and setters for user activation and bounce tracker state. // These are used for testing purposes only. // State is keyed by OriginAttributes. diff --git a/toolkit/components/antitracking/bouncetrackingprotection/test/browser/browser_bouncetracking_purge.js b/toolkit/components/antitracking/bouncetrackingprotection/test/browser/browser_bouncetracking_purge.js index a8e98b80f0..eedd374197 100644 --- a/toolkit/components/antitracking/bouncetrackingprotection/test/browser/browser_bouncetracking_purge.js +++ b/toolkit/components/antitracking/bouncetrackingprotection/test/browser/browser_bouncetracking_purge.js @@ -119,3 +119,69 @@ add_task(async function test_purging_skip_open_tab_extra_window() { bounceTrackingProtection.clearAll(); }); + +add_task(async function test_purging_skip_content_blocking_allow_list() { + initBounceTrackerState(); + + await BrowserTestUtils.withNewTab("https://example.com", async browser => { + window.ContentBlockingAllowList.add(browser); + }); + + Assert.deepEqual( + await bounceTrackingProtection.testRunPurgeBounceTrackers(), + ["example.net"], + "Should only purge example.net. example.org is within the grace period, example.com is allow-listed." + ); + + info( + "Remove the allow-list entry for example.com and test that it gets purged now." + ); + + await BrowserTestUtils.withNewTab("https://example.com", async browser => { + window.ContentBlockingAllowList.remove(browser); + }); + Assert.deepEqual( + await bounceTrackingProtection.testRunPurgeBounceTrackers(), + ["example.com"], + "example.com should have been purged now that it is no longer allow-listed." + ); + + bounceTrackingProtection.clearAll(); +}); + +add_task( + async function test_purging_skip_content_blocking_allow_list_subdomain() { + initBounceTrackerState(); + + await BrowserTestUtils.withNewTab( + "https://test1.example.com", + async browser => { + window.ContentBlockingAllowList.add(browser); + } + ); + + Assert.deepEqual( + await bounceTrackingProtection.testRunPurgeBounceTrackers(), + ["example.net"], + "Should only purge example.net. example.org is within the grace period, example.com is allow-listed via test1.example.com." + ); + + info( + "Remove the allow-list entry for test1.example.com and test that it gets purged now." + ); + + await BrowserTestUtils.withNewTab( + "https://test1.example.com", + async browser => { + window.ContentBlockingAllowList.remove(browser); + } + ); + Assert.deepEqual( + await bounceTrackingProtection.testRunPurgeBounceTrackers(), + ["example.com"], + "example.com should have been purged now that test1.example.com it is no longer allow-listed." + ); + + bounceTrackingProtection.clearAll(); + } +); diff --git a/toolkit/components/antitracking/bouncetrackingprotection/test/xpcshell/test_bouncetracking_clearExpiredUserActivation.js b/toolkit/components/antitracking/bouncetrackingprotection/test/xpcshell/test_bouncetracking_clearExpiredUserActivation.js new file mode 100644 index 0000000000..28a1350b3e --- /dev/null +++ b/toolkit/components/antitracking/bouncetrackingprotection/test/xpcshell/test_bouncetracking_clearExpiredUserActivation.js @@ -0,0 +1,85 @@ +/* Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Test that expired user activations are cleared by the the helper method + * testClearExpiredUserActivations. + */ +add_task(async function test() { + // Need a profile to data clearing calls. + do_get_profile(); + + let btp = Cc["@mozilla.org/bounce-tracking-protection;1"].getService( + Ci.nsIBounceTrackingProtection + ); + + // Reset global bounce tracking state. + btp.clearAll(); + + // Assert initial test state. + Assert.deepEqual( + btp.testGetBounceTrackerCandidateHosts({}), + [], + "No tracker candidates initially." + ); + Assert.deepEqual( + btp.testGetUserActivationHosts({}), + [], + "No user activation hosts initially." + ); + + // Get the bounce tracking activation lifetime. The pref is in seconds, we + // need to convert it to microseconds, as the user activation timestamps are + // in microseconds (PRTime). + let bounceTrackingActivationLifetimeUSec = + 1000 * + 1000 * + Services.prefs.getIntPref( + "privacy.bounceTrackingProtection.bounceTrackingActivationLifetimeSec" + ); + + // Add some test data for user activation. + btp.testAddUserActivation({}, "not-expired1.com", Date.now() * 1000); + btp.testAddUserActivation( + {}, + "not-expired2.com", + Date.now() * 1000 - bounceTrackingActivationLifetimeUSec / 2 + ); + btp.testAddUserActivation( + { privateBrowsingId: 1 }, + "pbm-not-expired.com", + Date.now() * 1000 + ); + btp.testAddUserActivation( + {}, + "expired1.com", + Date.now() * 1000 - bounceTrackingActivationLifetimeUSec * 2 + ); + btp.testAddUserActivation( + {}, + "expired2.com", + Date.now() * 1000 - (bounceTrackingActivationLifetimeUSec + 1000 * 1000) + ); + btp.testAddUserActivation({ privateBrowsingId: 1 }, "pbm-expired.com", 1); + + // Clear expired user activations. + btp.testClearExpiredUserActivations(); + + // Assert that expired user activations have been cleared. + Assert.deepEqual( + btp.testGetUserActivationHosts({}).sort(), + ["not-expired1.com", "not-expired2.com"], + "Expired user activation flags have been cleared for normal browsing." + ); + + Assert.deepEqual( + btp.testGetUserActivationHosts({ privateBrowsingId: 1 }).sort(), + ["pbm-not-expired.com"], + "Expired user activation flags have been cleared for private browsing." + ); + + // Reset global bounce tracking state. + btp.clearAll(); +}); diff --git a/toolkit/components/antitracking/bouncetrackingprotection/test/xpcshell/xpcshell.toml b/toolkit/components/antitracking/bouncetrackingprotection/test/xpcshell/xpcshell.toml index 16e270b85c..c3aeee502f 100644 --- a/toolkit/components/antitracking/bouncetrackingprotection/test/xpcshell/xpcshell.toml +++ b/toolkit/components/antitracking/bouncetrackingprotection/test/xpcshell/xpcshell.toml @@ -5,4 +5,6 @@ prefs = [ "privacy.bounceTrackingProtection.bounceTrackingPurgeTimerPeriodSec=0", ] +["test_bouncetracking_clearExpiredUserActivation.js"] + ["test_bouncetracking_purge.js"] |