UrlClassifierFeatureEmailTrackingDataCollection.cpp (10172B)
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/. */ 6 7 #include "UrlClassifierFeatureEmailTrackingDataCollection.h" 8 9 #include "mozilla/AntiTrackingUtils.h" 10 #include "mozilla/ClearOnShutdown.h" 11 #include "mozilla/ContentBlockingNotifier.h" 12 #include "mozilla/dom/BrowsingContext.h" 13 #include "mozilla/dom/WindowGlobalParent.h" 14 #include "mozilla/net/UrlClassifierCommon.h" 15 #include "mozilla/StaticPrefs_privacy.h" 16 #include "mozilla/StaticPtr.h" 17 #include "mozilla/glean/AntitrackingMetrics.h" 18 19 #include "nsIChannel.h" 20 #include "nsIClassifiedChannel.h" 21 #include "nsILoadInfo.h" 22 23 namespace mozilla::net { 24 25 namespace { 26 27 #define EMAILTRACKING_DATACOLLECTION_FEATURE_NAME \ 28 "emailtracking-data-collection" 29 30 #define URLCLASSIFIER_EMAILTRACKING_DATACOLLECTION_BLOCKLIST \ 31 "urlclassifier.features.emailtracking.datacollection.blocklistTables" 32 #define URLCLASSIFIER_EMAILTRACKING_DATACOLLECTION_BLOCKLIST_TEST_ENTRIES \ 33 "urlclassifier.features.emailtracking.datacollection.blocklistHosts" 34 #define URLCLASSIFIER_EMAILTRACKING_DATACOLLECTION_ENTITYLIST \ 35 "urlclassifier.features.emailtracking.datacollection.allowlistTables" 36 #define URLCLASSIFIER_EMAILTRACKING_DATACOLLECTION_ENTITYLIST_TEST_ENTRIES \ 37 "urlclassifier.features.emailtracking.datacollection.allowlistHosts" 38 #define URLCLASSIFIER_EMAILTRACKING_DATACOLLECTION_EXCEPTION_URLS \ 39 "urlclassifier.features.emailtracking.datacollection.skipURLs" 40 #define TABLE_EMAILTRACKING_DATACOLLECTION_BLOCKLIST_PREF \ 41 "emailtracking-data-collection-blocklist-pref" 42 #define TABLE_EMAILTRACKING_DATACOLLECTION_ENTITYLIST_PREF \ 43 "emailtracking-data-collection-allowlist-pref" 44 45 StaticRefPtr<UrlClassifierFeatureEmailTrackingDataCollection> 46 gFeatureEmailTrackingDataCollection; 47 StaticAutoPtr<nsCString> gEmailWebAppDomainsPref; 48 static constexpr char kEmailWebAppDomainPrefName[] = 49 "privacy.trackingprotection.emailtracking.webapp.domains"; 50 51 void EmailWebAppDomainPrefChangeCallback(const char* aPrefName, void*) { 52 MOZ_ASSERT(NS_IsMainThread()); 53 MOZ_ASSERT(!strcmp(aPrefName, kEmailWebAppDomainPrefName)); 54 MOZ_ASSERT(gEmailWebAppDomainsPref); 55 56 Preferences::GetCString(kEmailWebAppDomainPrefName, *gEmailWebAppDomainsPref); 57 } 58 59 } // namespace 60 61 UrlClassifierFeatureEmailTrackingDataCollection:: 62 UrlClassifierFeatureEmailTrackingDataCollection() 63 : UrlClassifierFeatureAntiTrackingBase( 64 nsLiteralCString(EMAILTRACKING_DATACOLLECTION_FEATURE_NAME), 65 nsLiteralCString( 66 URLCLASSIFIER_EMAILTRACKING_DATACOLLECTION_BLOCKLIST), 67 nsLiteralCString( 68 URLCLASSIFIER_EMAILTRACKING_DATACOLLECTION_ENTITYLIST), 69 nsLiteralCString( 70 URLCLASSIFIER_EMAILTRACKING_DATACOLLECTION_BLOCKLIST_TEST_ENTRIES), 71 nsLiteralCString( 72 URLCLASSIFIER_EMAILTRACKING_DATACOLLECTION_ENTITYLIST_TEST_ENTRIES), 73 nsLiteralCString(TABLE_EMAILTRACKING_DATACOLLECTION_BLOCKLIST_PREF), 74 nsLiteralCString(TABLE_EMAILTRACKING_DATACOLLECTION_ENTITYLIST_PREF), 75 nsLiteralCString( 76 URLCLASSIFIER_EMAILTRACKING_DATACOLLECTION_EXCEPTION_URLS)) {} 77 78 /* static */ 79 const char* UrlClassifierFeatureEmailTrackingDataCollection::Name() { 80 return EMAILTRACKING_DATACOLLECTION_FEATURE_NAME; 81 } 82 83 /* static */ 84 void UrlClassifierFeatureEmailTrackingDataCollection::MaybeInitialize() { 85 UC_LOG_LEAK( 86 ("UrlClassifierFeatureEmailTrackingDataCollection::MaybeInitialize")); 87 88 if (!gFeatureEmailTrackingDataCollection) { 89 gFeatureEmailTrackingDataCollection = 90 new UrlClassifierFeatureEmailTrackingDataCollection(); 91 gFeatureEmailTrackingDataCollection->InitializePreferences(); 92 } 93 } 94 95 /* static */ 96 void UrlClassifierFeatureEmailTrackingDataCollection::MaybeShutdown() { 97 UC_LOG_LEAK( 98 ("UrlClassifierFeatureEmailTrackingDataCollection::MaybeShutdown")); 99 100 if (gFeatureEmailTrackingDataCollection) { 101 gFeatureEmailTrackingDataCollection->ShutdownPreferences(); 102 gFeatureEmailTrackingDataCollection = nullptr; 103 } 104 } 105 106 /* static */ 107 already_AddRefed<UrlClassifierFeatureEmailTrackingDataCollection> 108 UrlClassifierFeatureEmailTrackingDataCollection::MaybeCreate( 109 nsIChannel* aChannel) { 110 MOZ_ASSERT(aChannel); 111 112 UC_LOG_LEAK( 113 ("UrlClassifierFeatureEmailTrackingDataCollection::MaybeCreate - channel " 114 "%p", 115 aChannel)); 116 117 if (!StaticPrefs:: 118 privacy_trackingprotection_emailtracking_data_collection_enabled()) { 119 return nullptr; 120 } 121 122 RefPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 123 bool isThirdParty = loadInfo->GetIsThirdPartyContextToTopWindow(); 124 125 // We don't need to collect data if the email tracker is loaded in first-party 126 // context. 127 if (!isThirdParty) { 128 return nullptr; 129 } 130 131 MaybeInitialize(); 132 MOZ_ASSERT(gFeatureEmailTrackingDataCollection); 133 134 RefPtr<UrlClassifierFeatureEmailTrackingDataCollection> self = 135 gFeatureEmailTrackingDataCollection; 136 return self.forget(); 137 } 138 139 /* static */ 140 already_AddRefed<nsIUrlClassifierFeature> 141 UrlClassifierFeatureEmailTrackingDataCollection::GetIfNameMatches( 142 const nsACString& aName) { 143 if (!aName.EqualsLiteral(EMAILTRACKING_DATACOLLECTION_FEATURE_NAME)) { 144 return nullptr; 145 } 146 147 MaybeInitialize(); 148 MOZ_ASSERT(gFeatureEmailTrackingDataCollection); 149 150 RefPtr<UrlClassifierFeatureEmailTrackingDataCollection> self = 151 gFeatureEmailTrackingDataCollection; 152 return self.forget(); 153 } 154 155 NS_IMETHODIMP 156 UrlClassifierFeatureEmailTrackingDataCollection::ProcessChannel( 157 nsIChannel* aChannel, const nsTArray<nsCString>& aList, 158 const nsTArray<nsCString>& aHashes, bool* aShouldContinue) { 159 NS_ENSURE_ARG_POINTER(aChannel); 160 NS_ENSURE_ARG_POINTER(aShouldContinue); 161 162 // This is not a blocking feature. 163 *aShouldContinue = true; 164 165 UC_LOG( 166 ("UrlClassifierFeatureEmailTrackingDataCollection::ProcessChannel - " 167 "collecting data from channel %p", 168 aChannel)); 169 170 static std::vector<UrlClassifierCommon::ClassificationData> 171 sClassificationData = { 172 {"base-email-track-"_ns, 173 nsIClassifiedChannel::ClassificationFlags::CLASSIFIED_EMAILTRACKING}, 174 {"content-email-track-"_ns, 175 nsIClassifiedChannel::ClassificationFlags:: 176 CLASSIFIED_EMAILTRACKING_CONTENT}, 177 }; 178 179 // Get if the top window is a email webapp. 180 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 181 182 RefPtr<dom::BrowsingContext> bc; 183 loadInfo->GetBrowsingContext(getter_AddRefs(bc)); 184 if (!bc || bc->IsDiscarded()) { 185 return NS_OK; 186 } 187 188 RefPtr<dom::WindowGlobalParent> topWindowParent = 189 bc->Canonical()->GetTopWindowContext(); 190 if (!topWindowParent || topWindowParent->IsDiscarded()) { 191 return NS_OK; 192 } 193 194 // Cache the email webapp domains pref value and register the callback 195 // function to update the cached value when the pref changes. 196 if (!gEmailWebAppDomainsPref) { 197 gEmailWebAppDomainsPref = new nsCString(); 198 199 Preferences::RegisterCallbackAndCall(EmailWebAppDomainPrefChangeCallback, 200 kEmailWebAppDomainPrefName); 201 RunOnShutdown([]() { 202 Preferences::UnregisterCallback(EmailWebAppDomainPrefChangeCallback, 203 kEmailWebAppDomainPrefName); 204 gEmailWebAppDomainsPref = nullptr; 205 }); 206 } 207 208 bool isTopEmailWebApp = topWindowParent->DocumentPrincipal()->IsURIInList( 209 *gEmailWebAppDomainsPref); 210 211 uint32_t flags = UrlClassifierCommon::TablesToClassificationFlags( 212 aList, sClassificationData, 213 nsIClassifiedChannel::ClassificationFlags::CLASSIFIED_EMAILTRACKING); 214 215 if (flags & nsIClassifiedChannel::ClassificationFlags:: 216 CLASSIFIED_EMAILTRACKING_CONTENT) { 217 glean::contentblocking::email_tracker_count 218 .EnumGet(isTopEmailWebApp 219 ? glean::contentblocking::EmailTrackerCountLabel:: 220 eContentEmailWebapp 221 : glean::contentblocking::EmailTrackerCountLabel:: 222 eContentNormal) 223 .Add(); 224 225 // Notify the load event to record the content blocking log. 226 // 227 // Note that we need to change the code here if we decided to block content 228 // email trackers in the future. 229 ContentBlockingNotifier::OnEvent( 230 aChannel, 231 nsIWebProgressListener::STATE_LOADED_EMAILTRACKING_LEVEL_2_CONTENT); 232 } else { 233 glean::contentblocking::email_tracker_count 234 .EnumGet( 235 isTopEmailWebApp 236 ? glean::contentblocking::EmailTrackerCountLabel:: 237 eBaseEmailWebapp 238 : glean::contentblocking::EmailTrackerCountLabel::eBaseNormal) 239 .Add(); 240 // Notify to record content blocking log. Note that we don't need to notify 241 // if email tracking is enabled because the email tracking protection 242 // feature will be responsible for notifying the blocking event. 243 // 244 // Note that we need to change the code here if we decided to block content 245 // email trackers in the future. 246 if (!StaticPrefs::privacy_trackingprotection_emailtracking_enabled()) { 247 ContentBlockingNotifier::OnEvent( 248 aChannel, 249 nsIWebProgressListener::STATE_LOADED_EMAILTRACKING_LEVEL_1_CONTENT); 250 } 251 } 252 253 return NS_OK; 254 } 255 256 NS_IMETHODIMP 257 UrlClassifierFeatureEmailTrackingDataCollection::GetURIByListType( 258 nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType, 259 nsIUrlClassifierFeature::URIType* aURIType, nsIURI** aURI) { 260 NS_ENSURE_ARG_POINTER(aChannel); 261 NS_ENSURE_ARG_POINTER(aURIType); 262 NS_ENSURE_ARG_POINTER(aURI); 263 264 if (aListType == nsIUrlClassifierFeature::blocklist) { 265 *aURIType = nsIUrlClassifierFeature::blocklistURI; 266 return aChannel->GetURI(aURI); 267 } 268 269 MOZ_ASSERT(aListType == nsIUrlClassifierFeature::entitylist); 270 271 *aURIType = nsIUrlClassifierFeature::pairwiseEntitylistURI; 272 return UrlClassifierCommon::CreatePairwiseEntityListURI(aChannel, aURI); 273 } 274 275 } // namespace mozilla::net