Bug 1550519 - Show a translucent parent highlight when a subgrid is highlighted....
[gecko.git] / dom / base / ContentBlockingLog.h
bloba577f44f0f4888fb4aa1f91158478c52b08cfb49
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_dom_ContentBlockingLog_h
8 #define mozilla_dom_ContentBlockingLog_h
10 #include "mozilla/AntiTrackingCommon.h"
11 #include "mozilla/JSONWriter.h"
12 #include "mozilla/Maybe.h"
13 #include "mozilla/StaticPrefs.h"
14 #include "mozilla/Tuple.h"
15 #include "mozilla/UniquePtr.h"
16 #include "nsReadableUtils.h"
17 #include "nsTArray.h"
18 #include "nsWindowSizes.h"
20 namespace mozilla {
21 namespace dom {
23 class ContentBlockingLog final {
24 typedef AntiTrackingCommon::StorageAccessGrantedReason
25 StorageAccessGrantedReason;
27 struct LogEntry {
28 uint32_t mType;
29 uint32_t mRepeatCount;
30 bool mBlocked;
31 Maybe<AntiTrackingCommon::StorageAccessGrantedReason> mReason;
32 nsTArray<nsCString> mTrackingFullHashes;
35 struct OriginDataEntry {
36 OriginDataEntry() : mHasTrackingContentLoaded(false) {}
38 bool mHasTrackingContentLoaded;
39 Maybe<bool> mHasCookiesLoaded;
40 nsTArray<LogEntry> mLogs;
43 struct OriginEntry {
44 OriginEntry() { mData = MakeUnique<OriginDataEntry>(); }
46 nsCString mOrigin;
47 UniquePtr<OriginDataEntry> mData;
50 typedef nsTArray<OriginEntry> OriginDataTable;
52 struct StringWriteFunc : public JSONWriteFunc {
53 nsACString&
54 mBuffer; // The lifetime of the struct must be bound to the buffer
55 explicit StringWriteFunc(nsACString& aBuffer) : mBuffer(aBuffer) {}
57 void Write(const char* aStr) override { mBuffer.Append(aStr); }
60 struct Comparator {
61 public:
62 bool Equals(const OriginDataTable::elem_type& aLeft,
63 const OriginDataTable::elem_type& aRight) const {
64 return aLeft.mOrigin.Equals(aRight.mOrigin);
67 bool Equals(const OriginDataTable::elem_type& aLeft,
68 const nsACString& aRight) const {
69 return aLeft.mOrigin.Equals(aRight);
73 public:
74 static const nsLiteralCString kDummyOriginHash;
76 ContentBlockingLog() = default;
77 ~ContentBlockingLog() = default;
79 void RecordLog(
80 const nsACString& aOrigin, uint32_t aType, bool aBlocked,
81 const Maybe<AntiTrackingCommon::StorageAccessGrantedReason>& aReason,
82 const nsTArray<nsCString>& aTrackingFullHashes) {
83 DebugOnly<bool> isCookiesBlockedTracker =
84 aType == nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER;
85 MOZ_ASSERT_IF(aBlocked, aReason.isNothing());
86 MOZ_ASSERT_IF(!isCookiesBlockedTracker, aReason.isNothing());
87 MOZ_ASSERT_IF(isCookiesBlockedTracker && !aBlocked, aReason.isSome());
89 if (aOrigin.IsVoid()) {
90 return;
92 auto index = mLog.IndexOf(aOrigin, 0, Comparator());
93 if (index != OriginDataTable::NoIndex) {
94 OriginEntry& entry = mLog[index];
95 if (!entry.mData) {
96 return;
99 if (aType == nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT) {
100 entry.mData->mHasTrackingContentLoaded = aBlocked;
101 return;
103 if (aType == nsIWebProgressListener::STATE_COOKIES_LOADED) {
104 if (entry.mData->mHasCookiesLoaded.isSome()) {
105 entry.mData->mHasCookiesLoaded.ref() = aBlocked;
106 } else {
107 entry.mData->mHasCookiesLoaded.emplace(aBlocked);
109 return;
111 if (!entry.mData->mLogs.IsEmpty()) {
112 auto& last = entry.mData->mLogs.LastElement();
113 if (last.mType == aType && last.mBlocked == aBlocked) {
114 ++last.mRepeatCount;
115 // Don't record recorded events. This helps compress our log.
116 // We don't care about if the the reason is the same, just keep the
117 // first one.
118 // Note: {aReason, aTrackingFullHashes} are not compared here and we
119 // simply keep the first ones.
120 #ifdef DEBUG
121 for (const auto& hash : aTrackingFullHashes) {
122 MOZ_ASSERT(last.mTrackingFullHashes.Contains(hash));
124 #endif
125 return;
128 if (entry.mData->mLogs.Length() ==
129 std::max(1u,
130 StaticPrefs::browser_contentblocking_originlog_length())) {
131 // Cap the size at the maximum length adjustable by the pref
132 entry.mData->mLogs.RemoveElementAt(0);
134 entry.mData->mLogs.AppendElement(
135 LogEntry{aType, 1u, aBlocked, aReason,
136 nsTArray<nsCString>(aTrackingFullHashes)});
137 return;
140 // The entry has not been found.
142 OriginEntry* entry = mLog.AppendElement();
143 if (NS_WARN_IF(!entry || !entry->mData)) {
144 return;
147 entry->mOrigin = aOrigin;
149 if (aType == nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT) {
150 entry->mData->mHasTrackingContentLoaded = aBlocked;
151 } else if (aType == nsIWebProgressListener::STATE_COOKIES_LOADED) {
152 MOZ_ASSERT(entry->mData->mHasCookiesLoaded.isNothing());
153 entry->mData->mHasCookiesLoaded.emplace(aBlocked);
154 } else {
155 entry->mData->mLogs.AppendElement(
156 LogEntry{aType, 1u, aBlocked, aReason,
157 nsTArray<nsCString>(aTrackingFullHashes)});
161 void ReportOrigins();
162 void ReportLog(nsIPrincipal* aFirstPartyPrincipal);
164 nsAutoCString Stringify() {
165 nsAutoCString buffer;
167 JSONWriter w(MakeUnique<StringWriteFunc>(buffer));
168 w.Start();
170 for (const OriginEntry& entry : mLog) {
171 if (!entry.mData) {
172 continue;
175 w.StartArrayProperty(entry.mOrigin.get(), w.SingleLineStyle);
177 if (entry.mData->mHasTrackingContentLoaded) {
178 w.StartArrayElement(w.SingleLineStyle);
180 w.IntElement(nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT);
181 w.BoolElement(true); // blocked
182 w.IntElement(1); // repeat count
184 w.EndArray();
186 if (entry.mData->mHasCookiesLoaded.isSome()) {
187 w.StartArrayElement(w.SingleLineStyle);
189 w.IntElement(nsIWebProgressListener::STATE_COOKIES_LOADED);
190 w.BoolElement(entry.mData->mHasCookiesLoaded.value()); // blocked
191 w.IntElement(1); // repeat count
193 w.EndArray();
195 for (const LogEntry& item : entry.mData->mLogs) {
196 w.StartArrayElement(w.SingleLineStyle);
198 w.IntElement(item.mType);
199 w.BoolElement(item.mBlocked);
200 w.IntElement(item.mRepeatCount);
201 if (item.mReason.isSome()) {
202 w.IntElement(item.mReason.value());
205 w.EndArray();
207 w.EndArray();
210 w.End();
212 return buffer;
215 bool HasBlockedAnyOfType(uint32_t aType) const {
216 // Note: nothing inside this loop should return false, the goal for the
217 // loop is to scan the log to see if we find a matching entry, and if so
218 // we would return true, otherwise in the end of the function outside of
219 // the loop we take the common `return false;` statement.
220 for (const OriginEntry& entry : mLog) {
221 if (!entry.mData) {
222 continue;
225 if (aType == nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT) {
226 if (entry.mData->mHasTrackingContentLoaded) {
227 return true;
229 } else if (aType == nsIWebProgressListener::STATE_COOKIES_LOADED) {
230 if (entry.mData->mHasCookiesLoaded.isSome() &&
231 entry.mData->mHasCookiesLoaded.value()) {
232 return true;
234 } else {
235 for (const auto& item : entry.mData->mLogs) {
236 if (((item.mType & aType) != 0) && item.mBlocked) {
237 return true;
242 return false;
245 void AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
246 aSizes.mDOMOtherSize +=
247 mLog.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
249 // Now add the sizes of each origin log queue.
250 for (const OriginEntry& entry : mLog) {
251 if (entry.mData) {
252 aSizes.mDOMOtherSize += aSizes.mState.mMallocSizeOf(entry.mData.get()) +
253 entry.mData->mLogs.ShallowSizeOfExcludingThis(
254 aSizes.mState.mMallocSizeOf);
259 private:
260 OriginDataTable mLog;
263 } // namespace dom
264 } // namespace mozilla
266 #endif