summaryrefslogtreecommitdiffstats
path: root/toolkit/components/antitracking/ContentBlockingLog.h
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/antitracking/ContentBlockingLog.h')
-rw-r--r--toolkit/components/antitracking/ContentBlockingLog.h381
1 files changed, 381 insertions, 0 deletions
diff --git a/toolkit/components/antitracking/ContentBlockingLog.h b/toolkit/components/antitracking/ContentBlockingLog.h
new file mode 100644
index 0000000000..0e4b7c4b09
--- /dev/null
+++ b/toolkit/components/antitracking/ContentBlockingLog.h
@@ -0,0 +1,381 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ContentBlockingLog_h
+#define mozilla_ContentBlockingLog_h
+
+#include "mozilla/ContentBlockingNotifier.h"
+#include "mozilla/JSONStringWriteFuncs.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/StaticPrefs_browser.h"
+
+#include "mozilla/UniquePtr.h"
+#include "nsIWebProgressListener.h"
+#include "nsReadableUtils.h"
+#include "nsTArray.h"
+#include "nsWindowSizes.h"
+
+class nsIPrincipal;
+
+namespace mozilla {
+
+class nsRFPService;
+
+class ContentBlockingLog final {
+ typedef ContentBlockingNotifier::StorageAccessPermissionGrantedReason
+ StorageAccessPermissionGrantedReason;
+
+ protected:
+ struct LogEntry {
+ uint32_t mType;
+ uint32_t mRepeatCount;
+ bool mBlocked;
+ Maybe<ContentBlockingNotifier::StorageAccessPermissionGrantedReason>
+ mReason;
+ nsTArray<nsCString> mTrackingFullHashes;
+ Maybe<ContentBlockingNotifier::CanvasFingerprinter> mCanvasFingerprinter;
+ Maybe<bool> mCanvasFingerprinterKnownText;
+ };
+
+ struct OriginDataEntry {
+ OriginDataEntry()
+ : mHasLevel1TrackingContentLoaded(false),
+ mHasLevel2TrackingContentLoaded(false),
+ mHasSuspiciousFingerprintingActivity(false) {}
+
+ bool mHasLevel1TrackingContentLoaded;
+ bool mHasLevel2TrackingContentLoaded;
+ bool mHasSuspiciousFingerprintingActivity;
+ Maybe<bool> mHasCookiesLoaded;
+ Maybe<bool> mHasTrackerCookiesLoaded;
+ Maybe<bool> mHasSocialTrackerCookiesLoaded;
+ nsTArray<LogEntry> mLogs;
+ };
+
+ struct OriginEntry {
+ OriginEntry() { mData = MakeUnique<OriginDataEntry>(); }
+
+ nsCString mOrigin;
+ UniquePtr<OriginDataEntry> mData;
+ };
+
+ friend class nsRFPService;
+
+ typedef nsTArray<OriginEntry> OriginDataTable;
+
+ struct Comparator {
+ public:
+ bool Equals(const OriginDataTable::value_type& aLeft,
+ const OriginDataTable::value_type& aRight) const {
+ return aLeft.mOrigin.Equals(aRight.mOrigin);
+ }
+
+ bool Equals(const OriginDataTable::value_type& aLeft,
+ const nsACString& aRight) const {
+ return aLeft.mOrigin.Equals(aRight);
+ }
+ };
+
+ public:
+ static const nsLiteralCString kDummyOriginHash;
+
+ ContentBlockingLog() = default;
+ ~ContentBlockingLog() = default;
+
+ // Record the log in the parent process. This should be called only in the
+ // parent process and will replace the RecordLog below after we remove the
+ // ContentBlockingLog from content processes.
+ Maybe<uint32_t> RecordLogParent(
+ const nsACString& aOrigin, uint32_t aType, bool aBlocked,
+ const Maybe<
+ ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
+ aReason,
+ const nsTArray<nsCString>& aTrackingFullHashes,
+ const Maybe<ContentBlockingNotifier::CanvasFingerprinter>&
+ aCanvasFingerprinter,
+ const Maybe<bool> aCanvasFingerprinterKnownText);
+
+ void RecordLog(
+ const nsACString& aOrigin, uint32_t aType, bool aBlocked,
+ const Maybe<
+ ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
+ aReason,
+ const nsTArray<nsCString>& aTrackingFullHashes) {
+ RecordLogInternal(aOrigin, aType, aBlocked, aReason, aTrackingFullHashes);
+ }
+
+ void ReportLog(nsIPrincipal* aFirstPartyPrincipal);
+ void ReportCanvasFingerprintingLog(nsIPrincipal* aFirstPartyPrincipal);
+ void ReportFontFingerprintingLog(nsIPrincipal* aFirstPartyPrincipal);
+ void ReportEmailTrackingLog(nsIPrincipal* aFirstPartyPrincipal);
+
+ nsAutoCString Stringify() {
+ nsAutoCString buffer;
+
+ JSONStringRefWriteFunc js(buffer);
+ JSONWriter w(js);
+ w.Start();
+
+ for (const OriginEntry& entry : mLog) {
+ if (!entry.mData) {
+ continue;
+ }
+
+ w.StartArrayProperty(entry.mOrigin, w.SingleLineStyle);
+
+ StringifyCustomFields(entry, w);
+ for (const LogEntry& item : entry.mData->mLogs) {
+ w.StartArrayElement(w.SingleLineStyle);
+ {
+ w.IntElement(item.mType);
+ w.BoolElement(item.mBlocked);
+ w.IntElement(item.mRepeatCount);
+ if (item.mReason.isSome()) {
+ w.IntElement(item.mReason.value());
+ }
+ }
+ w.EndArray();
+ }
+ w.EndArray();
+ }
+
+ w.End();
+
+ return buffer;
+ }
+
+ bool HasBlockedAnyOfType(uint32_t aType) const {
+ // Note: nothing inside this loop should return false, the goal for the
+ // loop is to scan the log to see if we find a matching entry, and if so
+ // we would return true, otherwise in the end of the function outside of
+ // the loop we take the common `return false;` statement.
+ for (const OriginEntry& entry : mLog) {
+ if (!entry.mData) {
+ continue;
+ }
+
+ if (aType ==
+ nsIWebProgressListener::STATE_LOADED_LEVEL_1_TRACKING_CONTENT) {
+ if (entry.mData->mHasLevel1TrackingContentLoaded) {
+ return true;
+ }
+ } else if (aType == nsIWebProgressListener::
+ STATE_LOADED_LEVEL_2_TRACKING_CONTENT) {
+ if (entry.mData->mHasLevel2TrackingContentLoaded) {
+ return true;
+ }
+ } else if (aType == nsIWebProgressListener::STATE_COOKIES_LOADED) {
+ if (entry.mData->mHasCookiesLoaded.isSome() &&
+ entry.mData->mHasCookiesLoaded.value()) {
+ return true;
+ }
+ } else if (aType ==
+ nsIWebProgressListener::STATE_COOKIES_LOADED_TRACKER) {
+ if (entry.mData->mHasTrackerCookiesLoaded.isSome() &&
+ entry.mData->mHasTrackerCookiesLoaded.value()) {
+ return true;
+ }
+ } else if (aType ==
+ nsIWebProgressListener::STATE_COOKIES_LOADED_SOCIALTRACKER) {
+ if (entry.mData->mHasSocialTrackerCookiesLoaded.isSome() &&
+ entry.mData->mHasSocialTrackerCookiesLoaded.value()) {
+ return true;
+ }
+ } else {
+ for (const auto& item : entry.mData->mLogs) {
+ if (((item.mType & aType) != 0) && item.mBlocked) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ void AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
+ aSizes.mDOMSizes.mDOMOtherSize +=
+ mLog.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
+
+ // Now add the sizes of each origin log queue.
+ for (const OriginEntry& entry : mLog) {
+ if (entry.mData) {
+ aSizes.mDOMSizes.mDOMOtherSize +=
+ aSizes.mState.mMallocSizeOf(entry.mData.get()) +
+ entry.mData->mLogs.ShallowSizeOfExcludingThis(
+ aSizes.mState.mMallocSizeOf);
+ }
+ }
+ }
+
+ uint32_t GetContentBlockingEventsInLog() {
+ uint32_t events = 0;
+
+ // We iterate the whole log to produce the overview of blocked events.
+ for (const OriginEntry& entry : mLog) {
+ if (!entry.mData) {
+ continue;
+ }
+
+ if (entry.mData->mHasLevel1TrackingContentLoaded) {
+ events |= nsIWebProgressListener::STATE_LOADED_LEVEL_1_TRACKING_CONTENT;
+ }
+
+ if (entry.mData->mHasLevel2TrackingContentLoaded) {
+ events |= nsIWebProgressListener::STATE_LOADED_LEVEL_2_TRACKING_CONTENT;
+ }
+
+ if (entry.mData->mHasSuspiciousFingerprintingActivity) {
+ events |=
+ nsIWebProgressListener::STATE_BLOCKED_SUSPICIOUS_FINGERPRINTING;
+ }
+
+ if (entry.mData->mHasCookiesLoaded.isSome() &&
+ entry.mData->mHasCookiesLoaded.value()) {
+ events |= nsIWebProgressListener::STATE_COOKIES_LOADED;
+ }
+
+ if (entry.mData->mHasTrackerCookiesLoaded.isSome() &&
+ entry.mData->mHasTrackerCookiesLoaded.value()) {
+ events |= nsIWebProgressListener::STATE_COOKIES_LOADED_TRACKER;
+ }
+
+ if (entry.mData->mHasSocialTrackerCookiesLoaded.isSome() &&
+ entry.mData->mHasSocialTrackerCookiesLoaded.value()) {
+ events |= nsIWebProgressListener::STATE_COOKIES_LOADED_SOCIALTRACKER;
+ }
+
+ for (const auto& item : entry.mData->mLogs) {
+ if (item.mBlocked) {
+ events |= item.mType;
+ }
+ }
+ }
+
+ return events;
+ }
+
+ private:
+ OriginEntry* RecordLogInternal(
+ const nsACString& aOrigin, uint32_t aType, bool aBlocked,
+ const Maybe<
+ ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
+ aReason = Nothing(),
+ const nsTArray<nsCString>& aTrackingFullHashes = nsTArray<nsCString>(),
+ const Maybe<ContentBlockingNotifier::CanvasFingerprinter>&
+ aCanvasFingerprinter = Nothing(),
+ const Maybe<bool> aCanvasFingerprinterKnownText = Nothing());
+
+ bool RecordLogEntryInCustomField(uint32_t aType, OriginEntry& aEntry,
+ bool aBlocked) {
+ if (aType ==
+ nsIWebProgressListener::STATE_LOADED_LEVEL_1_TRACKING_CONTENT) {
+ aEntry.mData->mHasLevel1TrackingContentLoaded = aBlocked;
+ return true;
+ }
+ if (aType ==
+ nsIWebProgressListener::STATE_LOADED_LEVEL_2_TRACKING_CONTENT) {
+ aEntry.mData->mHasLevel2TrackingContentLoaded = aBlocked;
+ return true;
+ }
+ if (aType == nsIWebProgressListener::STATE_COOKIES_LOADED) {
+ if (aEntry.mData->mHasCookiesLoaded.isSome()) {
+ aEntry.mData->mHasCookiesLoaded.ref() = aBlocked;
+ } else {
+ aEntry.mData->mHasCookiesLoaded.emplace(aBlocked);
+ }
+ return true;
+ }
+ if (aType == nsIWebProgressListener::STATE_COOKIES_LOADED_TRACKER) {
+ if (aEntry.mData->mHasTrackerCookiesLoaded.isSome()) {
+ aEntry.mData->mHasTrackerCookiesLoaded.ref() = aBlocked;
+ } else {
+ aEntry.mData->mHasTrackerCookiesLoaded.emplace(aBlocked);
+ }
+ return true;
+ }
+ if (aType == nsIWebProgressListener::STATE_COOKIES_LOADED_SOCIALTRACKER) {
+ if (aEntry.mData->mHasSocialTrackerCookiesLoaded.isSome()) {
+ aEntry.mData->mHasSocialTrackerCookiesLoaded.ref() = aBlocked;
+ } else {
+ aEntry.mData->mHasSocialTrackerCookiesLoaded.emplace(aBlocked);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ void StringifyCustomFields(const OriginEntry& aEntry, JSONWriter& aWriter) {
+ if (aEntry.mData->mHasLevel1TrackingContentLoaded) {
+ aWriter.StartArrayElement(aWriter.SingleLineStyle);
+ {
+ aWriter.IntElement(
+ nsIWebProgressListener::STATE_LOADED_LEVEL_1_TRACKING_CONTENT);
+ aWriter.BoolElement(true); // blocked
+ aWriter.IntElement(1); // repeat count
+ }
+ aWriter.EndArray();
+ }
+ if (aEntry.mData->mHasLevel2TrackingContentLoaded) {
+ aWriter.StartArrayElement(aWriter.SingleLineStyle);
+ {
+ aWriter.IntElement(
+ nsIWebProgressListener::STATE_LOADED_LEVEL_2_TRACKING_CONTENT);
+ aWriter.BoolElement(true); // blocked
+ aWriter.IntElement(1); // repeat count
+ }
+ aWriter.EndArray();
+ }
+ if (aEntry.mData->mHasCookiesLoaded.isSome()) {
+ aWriter.StartArrayElement(aWriter.SingleLineStyle);
+ {
+ aWriter.IntElement(nsIWebProgressListener::STATE_COOKIES_LOADED);
+ aWriter.BoolElement(
+ aEntry.mData->mHasCookiesLoaded.value()); // blocked
+ aWriter.IntElement(1); // repeat count
+ }
+ aWriter.EndArray();
+ }
+ if (aEntry.mData->mHasTrackerCookiesLoaded.isSome()) {
+ aWriter.StartArrayElement(aWriter.SingleLineStyle);
+ {
+ aWriter.IntElement(
+ nsIWebProgressListener::STATE_COOKIES_LOADED_TRACKER);
+ aWriter.BoolElement(
+ aEntry.mData->mHasTrackerCookiesLoaded.value()); // blocked
+ aWriter.IntElement(1); // repeat count
+ }
+ aWriter.EndArray();
+ }
+ if (aEntry.mData->mHasSocialTrackerCookiesLoaded.isSome()) {
+ aWriter.StartArrayElement(aWriter.SingleLineStyle);
+ {
+ aWriter.IntElement(
+ nsIWebProgressListener::STATE_COOKIES_LOADED_SOCIALTRACKER);
+ aWriter.BoolElement(
+ aEntry.mData->mHasSocialTrackerCookiesLoaded.value()); // blocked
+ aWriter.IntElement(1); // repeat count
+ }
+ aWriter.EndArray();
+ }
+ if (aEntry.mData->mHasSuspiciousFingerprintingActivity) {
+ aWriter.StartArrayElement(aWriter.SingleLineStyle);
+ {
+ aWriter.IntElement(
+ nsIWebProgressListener::STATE_BLOCKED_SUSPICIOUS_FINGERPRINTING);
+ aWriter.BoolElement(true); // blocked
+ aWriter.IntElement(1); // repeat count
+ }
+ aWriter.EndArray();
+ }
+ }
+
+ private:
+ OriginDataTable mLog;
+};
+
+} // namespace mozilla
+
+#endif