LCOV - code coverage report
Current view: top level - netwerk/base - nsChannelClassifier.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 188 570 33.0 %
Date: 2017-07-14 16:53:18 Functions: 32 64 50.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set sw=2 sts=2 ts=8 et 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 "nsChannelClassifier.h"
       8             : 
       9             : #include "mozIThirdPartyUtil.h"
      10             : #include "nsCharSeparatedTokenizer.h"
      11             : #include "nsContentUtils.h"
      12             : #include "nsIAddonPolicyService.h"
      13             : #include "nsICacheEntry.h"
      14             : #include "nsICachingChannel.h"
      15             : #include "nsIChannel.h"
      16             : #include "nsIClassOfService.h"
      17             : #include "nsIDocShell.h"
      18             : #include "nsIDocument.h"
      19             : #include "nsIDOMDocument.h"
      20             : #include "nsIHttpChannel.h"
      21             : #include "nsIHttpChannelInternal.h"
      22             : #include "nsIIOService.h"
      23             : #include "nsIParentChannel.h"
      24             : #include "nsIPermissionManager.h"
      25             : #include "nsIPrivateBrowsingTrackingProtectionWhitelist.h"
      26             : #include "nsIProtocolHandler.h"
      27             : #include "nsIScriptError.h"
      28             : #include "nsIScriptSecurityManager.h"
      29             : #include "nsISecureBrowserUI.h"
      30             : #include "nsISecurityEventSink.h"
      31             : #include "nsISupportsPriority.h"
      32             : #include "nsIURL.h"
      33             : #include "nsIWebProgressListener.h"
      34             : #include "nsNetUtil.h"
      35             : #include "nsPIDOMWindow.h"
      36             : #include "nsXULAppAPI.h"
      37             : #include "nsQueryObject.h"
      38             : 
      39             : #include "mozilla/ErrorNames.h"
      40             : #include "mozilla/Logging.h"
      41             : #include "mozilla/Preferences.h"
      42             : #include "mozilla/net/HttpBaseChannel.h"
      43             : #include "mozilla/ClearOnShutdown.h"
      44             : #include "mozilla/Unused.h"
      45             : 
      46             : namespace mozilla {
      47             : namespace net {
      48             : 
      49             : //
      50             : // MOZ_LOG=nsChannelClassifier:5
      51             : //
      52             : static LazyLogModule gChannelClassifierLog("nsChannelClassifier");
      53             : 
      54             : 
      55             : #undef LOG
      56             : #define LOG(args)     MOZ_LOG(gChannelClassifierLog, LogLevel::Debug, args)
      57             : #define LOG_ENABLED() MOZ_LOG_TEST(gChannelClassifierLog, LogLevel::Debug)
      58             : 
      59             : #define URLCLASSIFIER_SKIP_HOSTNAMES       "urlclassifier.skipHostnames"
      60             : #define URLCLASSIFIER_TRACKING_WHITELIST   "urlclassifier.trackingWhitelistTable"
      61             : #define URLCLASSIFIER_TRACKING_TABLE       "urlclassifier.trackingTable"
      62             : 
      63             : // Put CachedPrefs in anonymous namespace to avoid any collision from outside of
      64             : // this file.
      65             : namespace {
      66             : 
      67             : /**
      68             :  * It is not recommended to read from Preference everytime a channel is
      69             :  * connected.
      70             :  * That is not fast and we should cache preference values and reuse them
      71             :  */
      72             : class CachedPrefs final
      73             : {
      74             : public:
      75             :   static CachedPrefs* GetInstance();
      76             : 
      77             :   void Init();
      78           0 :   bool IsAllowListExample() { return sAllowListExample;}
      79           0 :   bool IsLowerNetworkPriority() { return sLowerNetworkPriority;}
      80           4 :   bool IsAnnotateChannelEnabled() { return sAnnotateChannelEnabled;}
      81           0 :   nsCString GetTrackingWhiteList() { return mTrackingWhitelist; }
      82           1 :   void SetTrackingWhiteList(const nsACString& aList) { mTrackingWhitelist = aList; }
      83           4 :   nsCString GetSkipHostnames() { return mSkipHostnames; }
      84           1 :   void SetSkipHostnames(const nsACString& aHostnames) { mSkipHostnames = aHostnames; }
      85           1 :   void SetTrackingBlackList(const nsACString& aList) { mTrackingBlacklist = aList; }
      86           1 :   nsCString GetTrackingBlackList() { return mTrackingBlacklist; }
      87             : 
      88             : private:
      89             :   friend class StaticAutoPtr<CachedPrefs>;
      90             :   CachedPrefs();
      91             :   ~CachedPrefs();
      92             : 
      93             :   static void OnPrefsChange(const char* aPrefName, void* );
      94             : 
      95             :   // Whether channels should be annotated as being on the tracking protection
      96             :   // list.
      97             :   static bool sAnnotateChannelEnabled;
      98             :   // Whether the priority of the channels annotated as being on the tracking
      99             :   // protection list should be lowered.
     100             :   static bool sLowerNetworkPriority;
     101             :   static bool sAllowListExample;
     102             : 
     103             :   nsCString mTrackingWhitelist;
     104             :   nsCString mSkipHostnames;
     105             :   nsCString mTrackingBlacklist;
     106             : 
     107             :   static StaticAutoPtr<CachedPrefs> sInstance;
     108             : };
     109             : 
     110             : bool CachedPrefs::sAllowListExample = false;
     111             : bool CachedPrefs::sLowerNetworkPriority = false;
     112             : bool CachedPrefs::sAnnotateChannelEnabled = false;
     113             : 
     114           3 : StaticAutoPtr<CachedPrefs> CachedPrefs::sInstance;
     115             : 
     116             : // static
     117             : void
     118           3 : CachedPrefs::OnPrefsChange(const char* aPref, void* aClosure)
     119             : {
     120           3 :   CachedPrefs* prefs = static_cast<CachedPrefs*> (aClosure);
     121             : 
     122           3 :   if (!strcmp(aPref, URLCLASSIFIER_SKIP_HOSTNAMES)) {
     123           2 :     nsCString skipHostnames = Preferences::GetCString(URLCLASSIFIER_SKIP_HOSTNAMES);
     124           1 :     ToLowerCase(skipHostnames);
     125           1 :     prefs->SetSkipHostnames(skipHostnames);
     126           2 :   } else if (!strcmp(aPref, URLCLASSIFIER_TRACKING_WHITELIST)) {
     127           2 :     nsCString trackingWhitelist = Preferences::GetCString(URLCLASSIFIER_TRACKING_WHITELIST);
     128           1 :     prefs->SetTrackingWhiteList(trackingWhitelist);
     129           1 :   } else if (!strcmp(aPref, URLCLASSIFIER_TRACKING_TABLE)) {
     130           2 :     nsCString trackingBlacklist = Preferences::GetCString(URLCLASSIFIER_TRACKING_TABLE);
     131           1 :     prefs->SetTrackingBlackList(trackingBlacklist);
     132             :   }
     133           3 : }
     134             : 
     135             : void
     136           1 : CachedPrefs::Init()
     137             : {
     138             :   Preferences::AddBoolVarCache(&sAnnotateChannelEnabled,
     139           1 :                                "privacy.trackingprotection.annotate_channels");
     140             :   Preferences::AddBoolVarCache(&sLowerNetworkPriority,
     141           1 :                                "privacy.trackingprotection.lower_network_priority");
     142             :   Preferences::AddBoolVarCache(&sAllowListExample,
     143           1 :                                "channelclassifier.allowlist_example");
     144             :   Preferences::RegisterCallbackAndCall(CachedPrefs::OnPrefsChange,
     145           1 :                                        URLCLASSIFIER_SKIP_HOSTNAMES, this);
     146             :   Preferences::RegisterCallbackAndCall(CachedPrefs::OnPrefsChange,
     147           1 :                                        URLCLASSIFIER_TRACKING_WHITELIST, this);
     148             :   Preferences::RegisterCallbackAndCall(CachedPrefs::OnPrefsChange,
     149           1 :                                        URLCLASSIFIER_TRACKING_TABLE, this);
     150             : 
     151           1 : }
     152             : 
     153             : // static
     154             : CachedPrefs*
     155           9 : CachedPrefs::GetInstance()
     156             : {
     157           9 :   if (!sInstance) {
     158           1 :     sInstance = new CachedPrefs();
     159           1 :     sInstance->Init();
     160           1 :     ClearOnShutdown(&sInstance);
     161             :   }
     162           9 :   MOZ_ASSERT(sInstance);
     163           9 :   return sInstance;
     164             : }
     165             : 
     166           1 : CachedPrefs::CachedPrefs()
     167             : {
     168           1 :   MOZ_COUNT_CTOR(CachedPrefs);
     169           1 : }
     170             : 
     171           0 : CachedPrefs::~CachedPrefs()
     172             : {
     173           0 :   MOZ_COUNT_DTOR(CachedPrefs);
     174             : 
     175           0 :   Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_SKIP_HOSTNAMES, this);
     176           0 :   Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_TRACKING_WHITELIST, this);
     177           0 :   Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_TRACKING_TABLE, this);
     178           0 : }
     179             : } // anonymous namespace
     180             : 
     181             : static void
     182           0 : SetIsTrackingResourceHelper(nsIChannel* aChannel)
     183             : {
     184           0 :   MOZ_ASSERT(aChannel);
     185             : 
     186           0 :   nsCOMPtr<nsIParentChannel> parentChannel;
     187           0 :   NS_QueryNotificationCallbacks(aChannel, parentChannel);
     188           0 :   if (parentChannel) {
     189             :     // This channel is a parent-process proxy for a child process
     190             :     // request. We should notify the child process as well.
     191           0 :     parentChannel->NotifyTrackingResource();
     192             :   }
     193             : 
     194           0 :   RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(aChannel);
     195           0 :   if (httpChannel) {
     196           0 :     httpChannel->SetIsTrackingResource();
     197             :   }
     198           0 : }
     199             : 
     200             : static void
     201           0 : LowerPriorityHelper(nsIChannel* aChannel)
     202             : {
     203           0 :   MOZ_ASSERT(aChannel);
     204             : 
     205           0 :   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(aChannel);
     206           0 :   if (p) {
     207           0 :     p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
     208             :   }
     209           0 : }
     210             : 
     211             : static void
     212           0 : SetThrottleableHelper(nsIChannel* aChannel)
     213             : {
     214           0 :   MOZ_ASSERT(aChannel);
     215             : 
     216           0 :   nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(aChannel));
     217           0 :   if (cos) {
     218           0 :     cos->AddClassFlags(nsIClassOfService::Throttleable);
     219             :   }
     220           0 : }
     221             : 
     222         621 : NS_IMPL_ISUPPORTS(nsChannelClassifier,
     223             :                   nsIURIClassifierCallback,
     224             :                   nsIObserver)
     225             : 
     226          67 : nsChannelClassifier::nsChannelClassifier(nsIChannel *aChannel)
     227             :   : mIsAllowListed(false),
     228             :     mSuspendedChannel(false),
     229             :     mChannel(aChannel),
     230          67 :     mTrackingProtectionEnabled(Nothing()),
     231         134 :     mTrackingAnnotationEnabled(Nothing())
     232             : {
     233          67 :   MOZ_ASSERT(mChannel);
     234          67 : }
     235             : 
     236             : bool
     237          13 : nsChannelClassifier::ShouldEnableTrackingProtection()
     238             : {
     239          13 :   if (mTrackingProtectionEnabled) {
     240           9 :     return mTrackingProtectionEnabled.value();
     241             :   }
     242             : 
     243           4 :   mTrackingProtectionEnabled = Some(false);
     244             : 
     245           8 :   nsCOMPtr<nsILoadContext> loadContext;
     246           4 :   NS_QueryNotificationCallbacks(mChannel, loadContext);
     247           4 :   if (loadContext && loadContext->UseTrackingProtection()) {
     248           0 :     Unused << ShouldEnableTrackingProtectionInternal(
     249             :       mChannel, false, mTrackingProtectionEnabled.ptr());
     250             :   }
     251             : 
     252           4 :   return mTrackingProtectionEnabled.value();
     253             : }
     254             : 
     255             : bool
     256           4 : nsChannelClassifier::ShouldEnableTrackingAnnotation()
     257             : {
     258           4 :   if (mTrackingAnnotationEnabled) {
     259           0 :     return mTrackingAnnotationEnabled.value();
     260             :   }
     261             : 
     262           4 :   mTrackingAnnotationEnabled = Some(false);
     263             : 
     264           4 :   if (!CachedPrefs::GetInstance()->IsAnnotateChannelEnabled()) {
     265           0 :     return mTrackingAnnotationEnabled.value();
     266             :   }
     267             : 
     268             :   // If tracking protection is enabled, no need to do channel annotation.
     269           4 :   if (ShouldEnableTrackingProtection()) {
     270           0 :     return mTrackingAnnotationEnabled.value();
     271             :   }
     272             : 
     273             :   // To prevent calling ShouldEnableTrackingProtectionInternal() again,
     274             :   // check loadContext->UseTrackingProtection() here.
     275             :   // If loadContext->UseTrackingProtection() is true, here it means
     276             :   // ShouldEnableTrackingProtectionInternal() has been called before in
     277             :   // ShouldEnableTrackingProtection() above and the result is false.
     278             :   // So, we can just return false.
     279           8 :   nsCOMPtr<nsILoadContext> loadContext;
     280           4 :   NS_QueryNotificationCallbacks(mChannel, loadContext);
     281           4 :   if (loadContext && loadContext->UseTrackingProtection()) {
     282           0 :     return mTrackingAnnotationEnabled.value();
     283             :   }
     284             : 
     285           4 :   Unused << ShouldEnableTrackingProtectionInternal(
     286             :       mChannel, true, mTrackingAnnotationEnabled.ptr());
     287             : 
     288           4 :   return mTrackingAnnotationEnabled.value();
     289             : }
     290             : 
     291             : nsresult
     292           4 : nsChannelClassifier::ShouldEnableTrackingProtectionInternal(
     293             :                                                          nsIChannel *aChannel,
     294             :                                                          bool aAnnotationsOnly,
     295             :                                                          bool *result)
     296             : {
     297             :     // Should only be called in the parent process.
     298           4 :     MOZ_ASSERT(XRE_IsParentProcess());
     299             : 
     300           4 :     NS_ENSURE_ARG(result);
     301           4 :     *result = false;
     302             : 
     303             :     nsresult rv;
     304             :     nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
     305           8 :         do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
     306           4 :     NS_ENSURE_SUCCESS(rv, rv);
     307             : 
     308           8 :     nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(aChannel, &rv);
     309           4 :     if (NS_FAILED(rv) || !chan) {
     310           0 :       LOG(("nsChannelClassifier[%p]: Not an HTTP channel", this));
     311           0 :       return NS_OK;
     312             :     }
     313             : 
     314           8 :     nsCOMPtr<nsIURI> topWinURI;
     315           4 :     rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI));
     316           4 :     NS_ENSURE_SUCCESS(rv, rv);
     317             : 
     318           3 :     if (!topWinURI) {
     319           1 :       LOG(("nsChannelClassifier[%p]: No window URI\n", this));
     320             :     }
     321             : 
     322           6 :     nsCOMPtr<nsIURI> chanURI;
     323           3 :     rv = aChannel->GetURI(getter_AddRefs(chanURI));
     324           3 :     NS_ENSURE_SUCCESS(rv, rv);
     325             : 
     326             :     // Third party checks don't work for chrome:// URIs in mochitests, so just
     327             :     // default to isThirdParty = true. We check isThirdPartyWindow to expand
     328             :     // the list of domains that are considered first party (e.g., if
     329             :     // facebook.com includes an iframe from fatratgames.com, all subsources
     330             :     // included in that iframe are considered third-party with
     331             :     // isThirdPartyChannel, even if they are not third-party w.r.t.
     332             :     // facebook.com), and isThirdPartyChannel to prevent top-level navigations
     333             :     // from being detected as third-party.
     334           3 :     bool isThirdPartyChannel = true;
     335           3 :     bool isThirdPartyWindow = true;
     336           3 :     thirdPartyUtil->IsThirdPartyURI(chanURI, topWinURI, &isThirdPartyWindow);
     337           3 :     thirdPartyUtil->IsThirdPartyChannel(aChannel, nullptr, &isThirdPartyChannel);
     338           3 :     if (!isThirdPartyWindow || !isThirdPartyChannel) {
     339           2 :       *result = false;
     340           2 :       if (LOG_ENABLED()) {
     341           0 :         LOG(("nsChannelClassifier[%p]: Skipping tracking protection checks "
     342             :              "for first party or top-level load channel[%p] with uri %s",
     343             :              this, aChannel, chanURI->GetSpecOrDefault().get()));
     344             :       }
     345           2 :       return NS_OK;
     346             :     }
     347             : 
     348             :     // Unlike full Tracking Protection, annotations don't block anything
     349             :     // so we don't need to take into account add-ons or user exceptions.
     350           1 :     if (aAnnotationsOnly) {
     351           1 :       *result = true;
     352           1 :       return NS_OK;
     353             :     }
     354             : 
     355           0 :     if (AddonMayLoad(aChannel, chanURI)) {
     356           0 :         return NS_OK;
     357             :     }
     358             : 
     359           0 :     nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
     360           0 :     NS_ENSURE_SUCCESS(rv, rv);
     361             : 
     362           0 :     if (!topWinURI && CachedPrefs::GetInstance()->IsAllowListExample()) {
     363           0 :       LOG(("nsChannelClassifier[%p]: Allowlisting test domain\n", this));
     364           0 :       rv = ios->NewURI(NS_LITERAL_CSTRING("http://allowlisted.example.com"),
     365           0 :                        nullptr, nullptr, getter_AddRefs(topWinURI));
     366           0 :       NS_ENSURE_SUCCESS(rv, rv);
     367             :     }
     368             : 
     369             :     // Take the host/port portion so we can allowlist by site. Also ignore the
     370             :     // scheme, since users who put sites on the allowlist probably don't expect
     371             :     // allowlisting to depend on scheme.
     372           0 :     nsCOMPtr<nsIURL> url = do_QueryInterface(topWinURI, &rv);
     373           0 :     if (NS_FAILED(rv)) {
     374           0 :       return rv; // normal for some loads, no need to print a warning
     375             :     }
     376             : 
     377           0 :     nsCString escaped(NS_LITERAL_CSTRING("https://"));
     378           0 :     nsAutoCString temp;
     379           0 :     rv = url->GetHostPort(temp);
     380           0 :     NS_ENSURE_SUCCESS(rv, rv);
     381           0 :     escaped.Append(temp);
     382             : 
     383             :     // Stuff the whole thing back into a URI for the permission manager.
     384           0 :     rv = ios->NewURI(escaped, nullptr, nullptr, getter_AddRefs(topWinURI));
     385           0 :     NS_ENSURE_SUCCESS(rv, rv);
     386             : 
     387             :     nsCOMPtr<nsIPermissionManager> permMgr =
     388           0 :         do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
     389           0 :     NS_ENSURE_SUCCESS(rv, rv);
     390             : 
     391           0 :     uint32_t permissions = nsIPermissionManager::UNKNOWN_ACTION;
     392           0 :     rv = permMgr->TestPermission(topWinURI, "trackingprotection", &permissions);
     393           0 :     NS_ENSURE_SUCCESS(rv, rv);
     394             : 
     395           0 :     if (permissions == nsIPermissionManager::ALLOW_ACTION) {
     396           0 :       LOG(("nsChannelClassifier[%p]: Allowlisting channel[%p] for %s", this,
     397             :            aChannel, escaped.get()));
     398           0 :       mIsAllowListed = true;
     399           0 :       *result = false;
     400             :     } else {
     401           0 :       *result = true;
     402             :     }
     403             : 
     404             :     // In Private Browsing Mode we also check against an in-memory list.
     405           0 :     if (NS_UsePrivateBrowsing(aChannel)) {
     406             :       nsCOMPtr<nsIPrivateBrowsingTrackingProtectionWhitelist> pbmtpWhitelist =
     407           0 :           do_GetService(NS_PBTRACKINGPROTECTIONWHITELIST_CONTRACTID, &rv);
     408           0 :       NS_ENSURE_SUCCESS(rv, rv);
     409             : 
     410           0 :       bool exists = false;
     411           0 :       rv = pbmtpWhitelist->ExistsInAllowList(topWinURI, &exists);
     412           0 :       NS_ENSURE_SUCCESS(rv, rv);
     413             : 
     414           0 :       if (exists) {
     415           0 :         mIsAllowListed = true;
     416           0 :         LOG(("nsChannelClassifier[%p]: Allowlisting channel[%p] in PBM for %s",
     417             :              this, aChannel, escaped.get()));
     418             :       }
     419             : 
     420           0 :       *result = !exists;
     421             :     }
     422             : 
     423             :     // Tracking protection will be enabled so return without updating
     424             :     // the security state. If any channels are subsequently cancelled
     425             :     // (page elements blocked) the state will be then updated.
     426           0 :     if (*result) {
     427           0 :       if (LOG_ENABLED()) {
     428           0 :         LOG(("nsChannelClassifier[%p]: Enabling tracking protection checks on "
     429             :              "channel[%p] with uri %s for toplevel window %s", this, aChannel,
     430             :              chanURI->GetSpecOrDefault().get(),
     431             :              topWinURI->GetSpecOrDefault().get()));
     432             :       }
     433           0 :       return NS_OK;
     434             :     }
     435             : 
     436             :     // Tracking protection will be disabled so update the security state
     437             :     // of the document and fire a secure change event. If we can't get the
     438             :     // window for the channel, then the shield won't show up so we can't send
     439             :     // an event to the securityUI anyway.
     440           0 :     return NotifyTrackingProtectionDisabled(aChannel);
     441             : }
     442             : 
     443             : bool
     444           0 : nsChannelClassifier::AddonMayLoad(nsIChannel *aChannel, nsIURI *aUri)
     445             : {
     446           0 :     nsCOMPtr<nsILoadInfo> channelLoadInfo = aChannel->GetLoadInfo();
     447           0 :     if (!channelLoadInfo)
     448           0 :         return false;
     449             : 
     450             :     // loadingPrincipal is used here to ensure we are loading into an
     451             :     // addon principal.  This allows an addon, with explicit permission, to
     452             :     // call out to API endpoints that may otherwise get blocked.
     453           0 :     nsIPrincipal* loadingPrincipal = channelLoadInfo->LoadingPrincipal();
     454           0 :     if (!loadingPrincipal)
     455           0 :         return false;
     456             : 
     457           0 :     return BasePrincipal::Cast(loadingPrincipal)->AddonAllowsLoad(aUri, true);
     458             : }
     459             : 
     460             : // static
     461             : nsresult
     462           0 : nsChannelClassifier::NotifyTrackingProtectionDisabled(nsIChannel *aChannel)
     463             : {
     464             :     // Can be called in EITHER the parent or child process.
     465           0 :     nsCOMPtr<nsIParentChannel> parentChannel;
     466           0 :     NS_QueryNotificationCallbacks(aChannel, parentChannel);
     467           0 :     if (parentChannel) {
     468             :       // This channel is a parent-process proxy for a child process request.
     469             :       // Tell the child process channel to do this instead.
     470           0 :       parentChannel->NotifyTrackingProtectionDisabled();
     471           0 :       return NS_OK;
     472             :     }
     473             : 
     474             :     nsresult rv;
     475             :     nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
     476           0 :         do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
     477           0 :     NS_ENSURE_SUCCESS(rv, rv);
     478             : 
     479           0 :     nsCOMPtr<mozIDOMWindowProxy> win;
     480           0 :     rv = thirdPartyUtil->GetTopWindowForChannel(aChannel, getter_AddRefs(win));
     481           0 :     NS_ENSURE_SUCCESS(rv, rv);
     482             : 
     483           0 :     auto* pwin = nsPIDOMWindowOuter::From(win);
     484           0 :     nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell();
     485           0 :     if (!docShell) {
     486           0 :       return NS_OK;
     487             :     }
     488           0 :     nsCOMPtr<nsIDocument> doc = docShell->GetDocument();
     489           0 :     NS_ENSURE_TRUE(doc, NS_OK);
     490             : 
     491             :     // Notify nsIWebProgressListeners of this security event.
     492             :     // Can be used to change the UI state.
     493           0 :     nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell, &rv);
     494           0 :     NS_ENSURE_SUCCESS(rv, NS_OK);
     495           0 :     uint32_t state = 0;
     496           0 :     nsCOMPtr<nsISecureBrowserUI> securityUI;
     497           0 :     docShell->GetSecurityUI(getter_AddRefs(securityUI));
     498           0 :     if (!securityUI) {
     499           0 :       return NS_OK;
     500             :     }
     501           0 :     doc->SetHasTrackingContentLoaded(true);
     502           0 :     securityUI->GetState(&state);
     503           0 :     state |= nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT;
     504           0 :     eventSink->OnSecurityChange(nullptr, state);
     505             : 
     506           0 :     return NS_OK;
     507             : }
     508             : 
     509             : void
     510          67 : nsChannelClassifier::Start()
     511             : {
     512          67 :   nsresult rv = StartInternal();
     513          67 :   if (NS_FAILED(rv)) {
     514             :     // If we aren't getting a callback for any reason, assume a good verdict and
     515             :     // make sure we resume the channel if necessary.
     516         268 :     OnClassifyComplete(NS_OK, NS_LITERAL_CSTRING(""),NS_LITERAL_CSTRING(""),
     517         335 :                        NS_LITERAL_CSTRING(""));
     518             :   }
     519          67 : }
     520             : 
     521             : nsresult
     522          67 : nsChannelClassifier::StartInternal()
     523             : {
     524             :     // Should only be called in the parent process.
     525          67 :     MOZ_ASSERT(XRE_IsParentProcess());
     526             : 
     527             :     // Don't bother to run the classifier on a load that has already failed.
     528             :     // (this might happen after a redirect)
     529             :     nsresult status;
     530          67 :     mChannel->GetStatus(&status);
     531          67 :     if (NS_FAILED(status))
     532           0 :         return status;
     533             : 
     534             :     // Don't bother to run the classifier on a cached load that was
     535             :     // previously classified as good.
     536          67 :     if (HasBeenClassified(mChannel)) {
     537           0 :         return NS_ERROR_UNEXPECTED;
     538             :     }
     539             : 
     540         134 :     nsCOMPtr<nsIURI> uri;
     541          67 :     nsresult rv = mChannel->GetURI(getter_AddRefs(uri));
     542          67 :     NS_ENSURE_SUCCESS(rv, rv);
     543             : 
     544             :     // Don't bother checking certain types of URIs.
     545          67 :     bool isAbout = false;
     546          67 :     rv = uri->SchemeIs("about", &isAbout);
     547          67 :     NS_ENSURE_SUCCESS(rv, rv);
     548          67 :     if (isAbout) return NS_ERROR_UNEXPECTED;
     549             : 
     550             :     bool hasFlags;
     551          65 :     rv = NS_URIChainHasFlags(uri,
     552             :                              nsIProtocolHandler::URI_DANGEROUS_TO_LOAD,
     553             :                              &hasFlags);
     554          65 :     NS_ENSURE_SUCCESS(rv, rv);
     555          65 :     if (hasFlags) return NS_ERROR_UNEXPECTED;
     556             : 
     557          65 :     rv = NS_URIChainHasFlags(uri,
     558             :                              nsIProtocolHandler::URI_IS_LOCAL_FILE,
     559             :                              &hasFlags);
     560          65 :     NS_ENSURE_SUCCESS(rv, rv);
     561          65 :     if (hasFlags) return NS_ERROR_UNEXPECTED;
     562             : 
     563           4 :     rv = NS_URIChainHasFlags(uri,
     564             :                              nsIProtocolHandler::URI_IS_UI_RESOURCE,
     565             :                              &hasFlags);
     566           4 :     NS_ENSURE_SUCCESS(rv, rv);
     567           4 :     if (hasFlags) return NS_ERROR_UNEXPECTED;
     568             : 
     569           4 :     rv = NS_URIChainHasFlags(uri,
     570             :                              nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
     571             :                              &hasFlags);
     572           4 :     NS_ENSURE_SUCCESS(rv, rv);
     573           4 :     if (hasFlags) return NS_ERROR_UNEXPECTED;
     574             : 
     575           8 :     nsCString skipHostnames = CachedPrefs::GetInstance()->GetSkipHostnames();
     576           4 :     if (!skipHostnames.IsEmpty()) {
     577           0 :       LOG(("nsChannelClassifier[%p]:StartInternal whitelisted hostnames = %s",
     578             :            this, skipHostnames.get()));
     579           0 :       if (IsHostnameWhitelisted(uri, skipHostnames)) {
     580           0 :         return NS_ERROR_UNEXPECTED;
     581             :       }
     582             :     }
     583             : 
     584             :     nsCOMPtr<nsIURIClassifier> uriClassifier =
     585           8 :         do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
     586           8 :     if (rv == NS_ERROR_FACTORY_NOT_REGISTERED ||
     587           4 :         rv == NS_ERROR_NOT_AVAILABLE) {
     588             :         // no URI classifier, ignore this failure.
     589           0 :         return NS_ERROR_NOT_AVAILABLE;
     590             :     }
     591           4 :     NS_ENSURE_SUCCESS(rv, rv);
     592             : 
     593             :     nsCOMPtr<nsIScriptSecurityManager> securityManager =
     594           8 :         do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
     595           4 :     NS_ENSURE_SUCCESS(rv, rv);
     596             : 
     597           8 :     nsCOMPtr<nsIPrincipal> principal;
     598           4 :     rv = securityManager->GetChannelURIPrincipal(mChannel, getter_AddRefs(principal));
     599           4 :     NS_ENSURE_SUCCESS(rv, rv);
     600             : 
     601             :     bool expectCallback;
     602           4 :     if (LOG_ENABLED()) {
     603           0 :       nsCOMPtr<nsIURI> principalURI;
     604           0 :       principal->GetURI(getter_AddRefs(principalURI));
     605           0 :       LOG(("nsChannelClassifier[%p]: Classifying principal %s on channel with "
     606             :            "uri %s", this, principalURI->GetSpecOrDefault().get(),
     607             :            uri->GetSpecOrDefault().get()));
     608             :     }
     609             :     // The classify is running in parent process, no need to give a valid event
     610             :     // target
     611          12 :     rv = uriClassifier->Classify(principal, nullptr,
     612           4 :                                  ShouldEnableTrackingProtection(),
     613           4 :                                  this, &expectCallback);
     614           4 :     if (NS_FAILED(rv)) {
     615           0 :         return rv;
     616             :     }
     617             : 
     618           4 :     if (expectCallback) {
     619             :         // Suspend the channel, it will be resumed when we get the classifier
     620             :         // callback.
     621           0 :         rv = mChannel->Suspend();
     622           0 :         if (NS_FAILED(rv)) {
     623             :             // Some channels (including nsJSChannel) fail on Suspend.  This
     624             :             // shouldn't be fatal, but will prevent malware from being
     625             :             // blocked on these channels.
     626           0 :             LOG(("nsChannelClassifier[%p]: Couldn't suspend channel", this));
     627           0 :             return rv;
     628             :         }
     629             : 
     630           0 :         mSuspendedChannel = true;
     631           0 :         LOG(("nsChannelClassifier[%p]: suspended channel %p",
     632             :              this, mChannel.get()));
     633             :     } else {
     634           4 :         LOG(("nsChannelClassifier[%p]: not expecting callback", this));
     635           4 :         return NS_ERROR_FAILURE;
     636             :     }
     637             : 
     638             :     // Add an observer for shutdown
     639           0 :     AddShutdownObserver();
     640           0 :     return NS_OK;
     641             : }
     642             : 
     643             : bool
     644           0 : nsChannelClassifier::IsHostnameWhitelisted(nsIURI *aUri,
     645             :                                            const nsACString &aWhitelisted)
     646             : {
     647           0 :   nsAutoCString host;
     648           0 :   nsresult rv = aUri->GetHost(host);
     649           0 :   if (NS_FAILED(rv) || host.IsEmpty()) {
     650           0 :     return false;
     651             :   }
     652           0 :   ToLowerCase(host);
     653             : 
     654           0 :   nsCCharSeparatedTokenizer tokenizer(aWhitelisted, ',');
     655           0 :   while (tokenizer.hasMoreTokens()) {
     656           0 :     const nsACString& token = tokenizer.nextToken();
     657           0 :     if (token.Equals(host)) {
     658           0 :       LOG(("nsChannelClassifier[%p]:StartInternal skipping %s (whitelisted)",
     659             :            this, host.get()));
     660           0 :       return true;
     661             :     }
     662             :   }
     663             : 
     664           0 :   return false;
     665             : }
     666             : 
     667             : // Note in the cache entry that this URL was classified, so that future
     668             : // cached loads don't need to be checked.
     669             : void
     670           0 : nsChannelClassifier::MarkEntryClassified(nsresult status)
     671             : {
     672             :     // Should only be called in the parent process.
     673           0 :     MOZ_ASSERT(XRE_IsParentProcess());
     674             : 
     675             :     // Don't cache tracking classifications because we support allowlisting.
     676           0 :     if (status == NS_ERROR_TRACKING_URI || mIsAllowListed) {
     677           0 :         return;
     678             :     }
     679             : 
     680           0 :     if (LOG_ENABLED()) {
     681           0 :       nsAutoCString errorName;
     682           0 :       GetErrorName(status, errorName);
     683           0 :       nsCOMPtr<nsIURI> uri;
     684           0 :       mChannel->GetURI(getter_AddRefs(uri));
     685           0 :       nsAutoCString spec;
     686           0 :       uri->GetAsciiSpec(spec);
     687           0 :       LOG(("nsChannelClassifier::MarkEntryClassified[%s] %s",
     688             :            errorName.get(), spec.get()));
     689             :     }
     690             : 
     691           0 :     nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(mChannel);
     692           0 :     if (!cachingChannel) {
     693           0 :         return;
     694             :     }
     695             : 
     696           0 :     nsCOMPtr<nsISupports> cacheToken;
     697           0 :     cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
     698           0 :     if (!cacheToken) {
     699           0 :         return;
     700             :     }
     701             : 
     702             :     nsCOMPtr<nsICacheEntry> cacheEntry =
     703           0 :         do_QueryInterface(cacheToken);
     704           0 :     if (!cacheEntry) {
     705           0 :         return;
     706             :     }
     707             : 
     708           0 :     cacheEntry->SetMetaDataElement("necko:classified",
     709           0 :                                    NS_SUCCEEDED(status) ? "1" : nullptr);
     710             : }
     711             : 
     712             : bool
     713          67 : nsChannelClassifier::HasBeenClassified(nsIChannel *aChannel)
     714             : {
     715             :     // Should only be called in the parent process.
     716          67 :     MOZ_ASSERT(XRE_IsParentProcess());
     717             : 
     718             :     nsCOMPtr<nsICachingChannel> cachingChannel =
     719         134 :         do_QueryInterface(aChannel);
     720          67 :     if (!cachingChannel) {
     721          63 :         return false;
     722             :     }
     723             : 
     724             :     // Only check the tag if we are loading from the cache without
     725             :     // validation.
     726             :     bool fromCache;
     727           4 :     if (NS_FAILED(cachingChannel->IsFromCache(&fromCache)) || !fromCache) {
     728           4 :         return false;
     729             :     }
     730             : 
     731           0 :     nsCOMPtr<nsISupports> cacheToken;
     732           0 :     cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
     733           0 :     if (!cacheToken) {
     734           0 :         return false;
     735             :     }
     736             : 
     737             :     nsCOMPtr<nsICacheEntry> cacheEntry =
     738           0 :         do_QueryInterface(cacheToken);
     739           0 :     if (!cacheEntry) {
     740           0 :         return false;
     741             :     }
     742             : 
     743           0 :     nsXPIDLCString tag;
     744           0 :     cacheEntry->GetMetaDataElement("necko:classified", getter_Copies(tag));
     745           0 :     return tag.EqualsLiteral("1");
     746             : }
     747             : 
     748             : //static
     749             : bool
     750           0 : nsChannelClassifier::SameLoadingURI(nsIDocument *aDoc, nsIChannel *aChannel)
     751             : {
     752           0 :   nsCOMPtr<nsIURI> docURI = aDoc->GetDocumentURI();
     753           0 :   nsCOMPtr<nsILoadInfo> channelLoadInfo = aChannel->GetLoadInfo();
     754           0 :   if (!channelLoadInfo || !docURI) {
     755           0 :     return false;
     756             :   }
     757             : 
     758           0 :   nsCOMPtr<nsIPrincipal> channelLoadingPrincipal = channelLoadInfo->LoadingPrincipal();
     759           0 :   if (!channelLoadingPrincipal) {
     760             :     // TYPE_DOCUMENT loads will not have a channelLoadingPrincipal. But top level
     761             :     // loads should not be blocked by Tracking Protection, so we will return
     762             :     // false
     763           0 :     return false;
     764             :   }
     765           0 :   nsCOMPtr<nsIURI> channelLoadingURI;
     766           0 :   channelLoadingPrincipal->GetURI(getter_AddRefs(channelLoadingURI));
     767           0 :   if (!channelLoadingURI) {
     768           0 :     return false;
     769             :   }
     770           0 :   bool equals = false;
     771           0 :   nsresult rv = docURI->EqualsExceptRef(channelLoadingURI, &equals);
     772           0 :   return NS_SUCCEEDED(rv) && equals;
     773             : }
     774             : 
     775             : // static
     776             : nsresult
     777           0 : nsChannelClassifier::SetBlockedContent(nsIChannel *channel,
     778             :                                        nsresult aErrorCode,
     779             :                                        const nsACString& aList,
     780             :                                        const nsACString& aProvider,
     781             :                                        const nsACString& aPrefix)
     782             : {
     783           0 :   NS_ENSURE_ARG(!aList.IsEmpty());
     784           0 :   NS_ENSURE_ARG(!aPrefix.IsEmpty());
     785             : 
     786             :   // Can be called in EITHER the parent or child process.
     787           0 :   nsCOMPtr<nsIParentChannel> parentChannel;
     788           0 :   NS_QueryNotificationCallbacks(channel, parentChannel);
     789           0 :   if (parentChannel) {
     790             :     // This channel is a parent-process proxy for a child process request.
     791             :     // Tell the child process channel to do this instead.
     792           0 :     parentChannel->SetClassifierMatchedInfo(aList, aProvider, aPrefix);
     793           0 :     return NS_OK;
     794             :   }
     795             : 
     796             :   nsresult rv;
     797           0 :   nsCOMPtr<nsIClassifiedChannel> classifiedChannel = do_QueryInterface(channel, &rv);
     798           0 :   NS_ENSURE_SUCCESS(rv, rv);
     799             : 
     800           0 :   if (classifiedChannel) {
     801           0 :     classifiedChannel->SetMatchedInfo(aList, aProvider, aPrefix);
     802             :   }
     803             : 
     804           0 :   nsCOMPtr<mozIDOMWindowProxy> win;
     805             :   nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
     806           0 :     do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
     807           0 :   NS_ENSURE_SUCCESS(rv, NS_OK);
     808           0 :   rv = thirdPartyUtil->GetTopWindowForChannel(channel, getter_AddRefs(win));
     809           0 :   NS_ENSURE_SUCCESS(rv, NS_OK);
     810           0 :   auto* pwin = nsPIDOMWindowOuter::From(win);
     811           0 :   nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell();
     812           0 :   if (!docShell) {
     813           0 :     return NS_OK;
     814             :   }
     815           0 :   nsCOMPtr<nsIDocument> doc = docShell->GetDocument();
     816           0 :   NS_ENSURE_TRUE(doc, NS_OK);
     817             : 
     818             :   // This event might come after the user has navigated to another page.
     819             :   // To prevent showing the TrackingProtection UI on the wrong page, we need to
     820             :   // check that the loading URI for the channel is the same as the URI currently
     821             :   // loaded in the document.
     822           0 :   if (!SameLoadingURI(doc, channel)) {
     823           0 :     return NS_OK;
     824             :   }
     825             : 
     826             :   // Notify nsIWebProgressListeners of this security event.
     827             :   // Can be used to change the UI state.
     828           0 :   nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell, &rv);
     829           0 :   NS_ENSURE_SUCCESS(rv, NS_OK);
     830           0 :   uint32_t state = 0;
     831           0 :   nsCOMPtr<nsISecureBrowserUI> securityUI;
     832           0 :   docShell->GetSecurityUI(getter_AddRefs(securityUI));
     833           0 :   if (!securityUI) {
     834           0 :     return NS_OK;
     835             :   }
     836           0 :   securityUI->GetState(&state);
     837           0 :   if (aErrorCode == NS_ERROR_TRACKING_URI) {
     838           0 :     doc->SetHasTrackingContentBlocked(true);
     839           0 :     state |= nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
     840             :   } else {
     841           0 :     state |= nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT;
     842             :   }
     843             : 
     844           0 :   eventSink->OnSecurityChange(nullptr, state);
     845             : 
     846             :   // Log a warning to the web console.
     847           0 :   nsCOMPtr<nsIURI> uri;
     848           0 :   channel->GetURI(getter_AddRefs(uri));
     849           0 :   NS_ConvertUTF8toUTF16 spec(uri->GetSpecOrDefault());
     850           0 :   const char16_t* params[] = { spec.get() };
     851           0 :   const char* message = (aErrorCode == NS_ERROR_TRACKING_URI) ?
     852           0 :     "TrackingUriBlocked" : "UnsafeUriBlocked";
     853             :   nsCString category = (aErrorCode == NS_ERROR_TRACKING_URI) ?
     854           0 :     NS_LITERAL_CSTRING("Tracking Protection") :
     855           0 :     NS_LITERAL_CSTRING("Safe Browsing");
     856             : 
     857           0 :   nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
     858             :                                   category,
     859             :                                   doc,
     860             :                                   nsContentUtils::eNECKO_PROPERTIES,
     861             :                                   message,
     862           0 :                                   params, ArrayLength(params));
     863             : 
     864           0 :   return NS_OK;
     865             : }
     866             : 
     867             : namespace {
     868             : 
     869             : // The purpose of this class is only for implementing all nsISupports methods.
     870             : // This is a workaround for template derived class.
     871             : class URIClassifierCallbackBase : public nsIURIClassifierCallback {
     872             : public:
     873           0 :   URIClassifierCallbackBase() = default;
     874             : 
     875             :   NS_DECL_THREADSAFE_ISUPPORTS
     876             : 
     877             : protected:
     878           0 :   virtual ~URIClassifierCallbackBase() = default;
     879             : };
     880             : 
     881           0 : NS_IMPL_ISUPPORTS(URIClassifierCallbackBase, nsIURIClassifierCallback)
     882             : 
     883             : // A template class for reusing the code.
     884             : // OnClassifyCompleteInternal will be called to pass the result.
     885             : template<class T>
     886             : class IsTrackerWhitelistedCallback final : public URIClassifierCallbackBase {
     887             : public:
     888           0 :   explicit IsTrackerWhitelistedCallback(T* aClosure,
     889             :                                         const nsACString& aList,
     890             :                                         const nsACString& aProvider,
     891             :                                         const nsACString& aPrefix,
     892             :                                         nsIURI* aWhitelistURI)
     893             :     : mClosure(aClosure)
     894             :     , mWhitelistURI(aWhitelistURI)
     895             :     , mList(aList)
     896             :     , mProvider(aProvider)
     897           0 :     , mPrefix(aPrefix)
     898             :   {
     899           0 :   }
     900             : 
     901           0 :   NS_IMETHOD OnClassifyComplete(nsresult /*aErrorCode*/,
     902             :                                 const nsACString& aLists, // Only this matters.
     903             :                                 const nsACString& /*aProvider*/,
     904             :                                 const nsACString& /*aPrefix*/) override
     905             :   {
     906             :     nsresult rv;
     907           0 :     if (aLists.IsEmpty()) {
     908           0 :       if (LOG_ENABLED()) {
     909           0 :         MOZ_ASSERT(mWhitelistURI);
     910             : 
     911           0 :         LOG(("nsChannelClassifier[%p]: %s is not in the whitelist",
     912             :              mClosure.get(), mWhitelistURI->GetSpecOrDefault().get()));
     913             :       }
     914           0 :       rv = NS_ERROR_TRACKING_URI;
     915             :     } else {
     916           0 :       LOG(("nsChannelClassifier[%p]:OnClassifyComplete tracker found "
     917             :            "in whitelist so we won't block it", mClosure.get()));
     918           0 :       rv = NS_OK;
     919             :     }
     920             : 
     921           0 :     rv = mClosure->OnClassifyCompleteInternal(rv, mList, mProvider, mPrefix);
     922           0 :     mClosure = nullptr;
     923           0 :     return rv;
     924             :   }
     925             : 
     926             : private:
     927           0 :   ~IsTrackerWhitelistedCallback() = default;
     928             : 
     929             :   RefPtr<T> mClosure;
     930             :   nsCOMPtr<nsIURI> mWhitelistURI;
     931             : 
     932             :   // The following 3 values are for forwarding the callback.
     933             :   nsCString mList;
     934             :   nsCString mProvider;
     935             :   nsCString mPrefix;
     936             : };
     937             : 
     938             : // This class is designed to get the results of checking blacklist and whitelist.
     939             : // 1. The result of local blacklist will be sent back via
     940             : //    OnClassifyComplete, which is called by nsIURIClassifier service.
     941             : // 2. The result of local whitelist is got via OnClassifyCompleteInternal,
     942             : //    which is called by IsTrackerWhitelistedCallback::OnClassifyComplete.
     943             : class IsTrackerBlacklistedCallback final : public nsIURIClassifierCallback {
     944             : public:
     945           1 :   explicit IsTrackerBlacklistedCallback(nsChannelClassifier* aChannelClassifier,
     946             :                                         nsIURIClassifierCallback* aCallback)
     947           1 :     : mChannelClassifier(aChannelClassifier)
     948           1 :     , mChannelCallback(aCallback)
     949             :   {
     950           1 :   }
     951             : 
     952             :   NS_DECL_THREADSAFE_ISUPPORTS
     953             :   NS_DECL_NSIURICLASSIFIERCALLBACK
     954             : 
     955             :   nsresult OnClassifyCompleteInternal(nsresult aErrorCode,
     956             :                                       const nsACString& aList,
     957             :                                       const nsACString& aProvider,
     958             :                                       const nsACString& aPrefix);
     959             : 
     960             : private:
     961           1 :   ~IsTrackerBlacklistedCallback() = default;
     962             : 
     963             :   RefPtr<nsChannelClassifier> mChannelClassifier;
     964             :   nsCOMPtr<nsIURIClassifierCallback> mChannelCallback;
     965             : };
     966             : 
     967           8 : NS_IMPL_ISUPPORTS(IsTrackerBlacklistedCallback, nsIURIClassifierCallback)
     968             : 
     969             : /*virtual*/ nsresult
     970           1 : IsTrackerBlacklistedCallback::OnClassifyComplete(nsresult aErrorCode,
     971             :                                                  const nsACString& aLists,
     972             :                                                  const nsACString& aProvider,
     973             :                                                  const nsACString& aPrefix)
     974             : {
     975           1 :   nsresult status = aLists.IsEmpty() ? NS_OK : NS_ERROR_TRACKING_URI;
     976           1 :   bool tpEnabled = mChannelClassifier->ShouldEnableTrackingProtection();
     977             : 
     978           1 :   LOG(("IsTrackerBlacklistedCallback[%p]:OnClassifyComplete "
     979             :        " status=0x%" PRIx32 ", tpEnabled=%d",
     980             :        mChannelClassifier.get(), static_cast<uint32_t>(status), tpEnabled));
     981             : 
     982             :   // If this is not in local blacklist or tracking protection is enabled,
     983             :   // directly send the status back.
     984             :   // The whitelist will be checked at nsChannelClassifier::OnClassifyComplete
     985             :   // when tracking protection is enabled, so we can just return here.
     986           1 :   if (NS_SUCCEEDED(status) || tpEnabled) {
     987           1 :     return mChannelCallback->OnClassifyComplete(
     988           1 :       status, aLists, aProvider, aPrefix);
     989             :   }
     990             : 
     991           0 :   nsCOMPtr<nsIChannel> channel = mChannelClassifier->GetChannel();
     992           0 :   if (LOG_ENABLED()) {
     993           0 :     nsCOMPtr<nsIURI> uri;
     994           0 :     channel->GetURI(getter_AddRefs(uri));
     995           0 :     LOG(("IsTrackerBlacklistedCallback[%p]:OnClassifyComplete channel [%p] "
     996             :          "uri=%s, is in blacklist. Start checking whitelist.",
     997             :          mChannelClassifier.get(), channel.get(),
     998             :          uri->GetSpecOrDefault().get()));
     999             :   }
    1000             : 
    1001           0 :   nsCOMPtr<nsIURI> whitelistURI = mChannelClassifier->CreateWhiteListURI();
    1002             :   nsCOMPtr<nsIURIClassifierCallback> callback =
    1003             :     new IsTrackerWhitelistedCallback<IsTrackerBlacklistedCallback>(
    1004           0 :       this, aLists, aProvider, aPrefix, whitelistURI);
    1005             : 
    1006             :   // If IsTrackerWhitelisted has failed, it means the uri is not in whitelist.
    1007           0 :   if (NS_FAILED(mChannelClassifier->IsTrackerWhitelisted(whitelistURI, callback))) {
    1008           0 :     LOG(("IsTrackerBlacklistedCallback[%p]:OnClassifyComplete channel [%p] "
    1009             :          "IsTrackerWhitelisted has failed.",
    1010             :          mChannelClassifier.get(), channel.get()));
    1011             : 
    1012           0 :     MOZ_ASSERT(mChannelClassifier->ShouldEnableTrackingAnnotation());
    1013             : 
    1014           0 :     SetIsTrackingResourceHelper(channel);
    1015           0 :     if (CachedPrefs::GetInstance()->IsLowerNetworkPriority()) {
    1016           0 :       LowerPriorityHelper(channel);
    1017             :     }
    1018           0 :     SetThrottleableHelper(channel);
    1019             : 
    1020             :     // We don't want to disable speculative connection when tracking protection
    1021             :     // is disabled. So, change the status to NS_OK.
    1022           0 :     status = NS_OK;
    1023             : 
    1024           0 :     return mChannelCallback->OnClassifyComplete(
    1025           0 :       status, aLists, aProvider, aPrefix);
    1026             :   }
    1027             : 
    1028             :   // OnClassifyCompleteInternal() will be called once we know
    1029             :   // if the tracker is whitelisted.
    1030           0 :   return NS_OK;
    1031             : }
    1032             : 
    1033             : nsresult
    1034           0 : IsTrackerBlacklistedCallback::OnClassifyCompleteInternal(nsresult aErrorCode,
    1035             :                                                          const nsACString& aLists,
    1036             :                                                          const nsACString& aProvider,
    1037             :                                                          const nsACString& aPrefix)
    1038             : {
    1039           0 :   LOG(("IsTrackerBlacklistedCallback[%p]:OnClassifyCompleteInternal"
    1040             :        " status=0x%" PRIx32,
    1041             :        mChannelClassifier.get(), static_cast<uint32_t>(aErrorCode)));
    1042             : 
    1043           0 :   if (NS_SUCCEEDED(aErrorCode)) {
    1044           0 :     return mChannelCallback->OnClassifyComplete(
    1045           0 :       aErrorCode, aLists, aProvider, aPrefix);
    1046             :   }
    1047             : 
    1048           0 :   MOZ_ASSERT(mChannelClassifier->ShouldEnableTrackingAnnotation());
    1049           0 :   MOZ_ASSERT(aErrorCode == NS_ERROR_TRACKING_URI);
    1050             : 
    1051           0 :   nsCOMPtr<nsIChannel> channel = mChannelClassifier->GetChannel();
    1052           0 :   if (LOG_ENABLED()) {
    1053           0 :     nsCOMPtr<nsIURI> uri;
    1054           0 :     channel->GetURI(getter_AddRefs(uri));
    1055           0 :     LOG(("IsTrackerBlacklistedCallback[%p]:OnClassifyCompleteInternal "
    1056             :          "channel [%p] uri=%s, is not in whitelist",
    1057             :          mChannelClassifier.get(), channel.get(),
    1058             :          uri->GetSpecOrDefault().get()));
    1059             :   }
    1060             : 
    1061           0 :   SetIsTrackingResourceHelper(channel);
    1062           0 :   if (CachedPrefs::GetInstance()->IsLowerNetworkPriority()) {
    1063           0 :     LowerPriorityHelper(channel);
    1064             :   }
    1065           0 :   SetThrottleableHelper(channel);
    1066             : 
    1067           0 :   return mChannelCallback->OnClassifyComplete(
    1068           0 :       NS_OK, aLists, aProvider, aPrefix);
    1069             : }
    1070             : 
    1071             : } // end of unnamed namespace/
    1072             : 
    1073             : already_AddRefed<nsIURI>
    1074           0 : nsChannelClassifier::CreateWhiteListURI() const
    1075             : {
    1076             :   nsresult rv;
    1077           0 :   nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(mChannel, &rv);
    1078           0 :   if (!chan) {
    1079           0 :     return nullptr;
    1080             :   }
    1081             : 
    1082           0 :   nsCOMPtr<nsIURI> topWinURI;
    1083           0 :   rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI));
    1084           0 :   NS_ENSURE_SUCCESS(rv, nullptr);
    1085           0 :   if (!topWinURI) {
    1086           0 :     LOG(("nsChannelClassifier[%p]: No window URI", this));
    1087           0 :     return nullptr;
    1088             :   }
    1089             : 
    1090             :   nsCOMPtr<nsIScriptSecurityManager> securityManager =
    1091           0 :     do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
    1092           0 :   NS_ENSURE_SUCCESS(rv, nullptr);
    1093           0 :   nsCOMPtr<nsIPrincipal> chanPrincipal;
    1094           0 :   rv = securityManager->GetChannelURIPrincipal(mChannel,
    1095           0 :                                                getter_AddRefs(chanPrincipal));
    1096           0 :   if (NS_FAILED(rv)) {
    1097           0 :     return nullptr;
    1098             :   }
    1099             : 
    1100             :   // Craft a whitelist URL like "toplevel.page/?resource=third.party.domain"
    1101           0 :   nsAutoCString pageHostname, resourceDomain;
    1102           0 :   rv = topWinURI->GetHost(pageHostname);
    1103           0 :   NS_ENSURE_SUCCESS(rv, nullptr);
    1104           0 :   rv = chanPrincipal->GetBaseDomain(resourceDomain);
    1105           0 :   NS_ENSURE_SUCCESS(rv, nullptr);
    1106           0 :   nsAutoCString whitelistEntry = NS_LITERAL_CSTRING("http://") +
    1107           0 :     pageHostname + NS_LITERAL_CSTRING("/?resource=") + resourceDomain;
    1108           0 :   LOG(("nsChannelClassifier[%p]: Looking for %s in the whitelist",
    1109             :        this, whitelistEntry.get()));
    1110             : 
    1111           0 :   nsCOMPtr<nsIURI> whitelistURI;
    1112           0 :   rv = NS_NewURI(getter_AddRefs(whitelistURI), whitelistEntry);
    1113             : 
    1114           0 :   return NS_SUCCEEDED(rv) ? whitelistURI.forget() : nullptr;
    1115             : }
    1116             : 
    1117             : nsresult
    1118           0 : nsChannelClassifier::IsTrackerWhitelisted(nsIURI* aWhiteListURI,
    1119             :                                           nsIURIClassifierCallback *aCallback)
    1120             : {
    1121           0 :   if (!aCallback || !aWhiteListURI) {
    1122           0 :     return NS_ERROR_INVALID_ARG;
    1123             :   }
    1124             : 
    1125             :   nsresult rv;
    1126             :   nsCOMPtr<nsIURIClassifier> uriClassifier =
    1127           0 :     do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
    1128           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1129             : 
    1130           0 :   nsCString trackingWhitelist = CachedPrefs::GetInstance()->GetTrackingWhiteList();
    1131           0 :   if (trackingWhitelist.IsEmpty()) {
    1132           0 :     LOG(("nsChannelClassifier[%p]:IsTrackerWhitelisted whitelist disabled",
    1133             :          this));
    1134           0 :     return NS_ERROR_TRACKING_URI;
    1135             :   }
    1136             : 
    1137           0 :   return uriClassifier->AsyncClassifyLocalWithTables(aWhiteListURI, trackingWhitelist, aCallback);
    1138             : }
    1139             : 
    1140             : NS_IMETHODIMP
    1141          67 : nsChannelClassifier::OnClassifyComplete(nsresult aErrorCode,
    1142             :                                         const nsACString& aList,
    1143             :                                         const nsACString& aProvider,
    1144             :                                         const nsACString& aPrefix)
    1145             : {
    1146             :   // Should only be called in the parent process.
    1147          67 :   MOZ_ASSERT(XRE_IsParentProcess());
    1148             : 
    1149          67 :   if (aErrorCode == NS_ERROR_TRACKING_URI) {
    1150           0 :     nsCOMPtr<nsIURI> whitelistURI = CreateWhiteListURI();
    1151             :     nsCOMPtr<nsIURIClassifierCallback> callback =
    1152             :       new IsTrackerWhitelistedCallback<nsChannelClassifier>(
    1153           0 :         this, aList, aProvider, aPrefix, whitelistURI);
    1154           0 :     if (whitelistURI &&
    1155           0 :         NS_SUCCEEDED(IsTrackerWhitelisted(whitelistURI, callback))) {
    1156             :       // OnClassifyCompleteInternal() will be called once we know
    1157             :       // if the tracker is whitelisted.
    1158           0 :       return NS_OK;
    1159             :     }
    1160             :   }
    1161             : 
    1162          67 :   return OnClassifyCompleteInternal(aErrorCode, aList, aProvider, aPrefix);
    1163             : }
    1164             : 
    1165             : nsresult
    1166          67 : nsChannelClassifier::OnClassifyCompleteInternal(nsresult aErrorCode,
    1167             :                                                 const nsACString& aList,
    1168             :                                                 const nsACString& aProvider,
    1169             :                                                 const nsACString& aPrefix)
    1170             : {
    1171          67 :     if (mSuspendedChannel) {
    1172           0 :       nsAutoCString errorName;
    1173           0 :       if (LOG_ENABLED()) {
    1174           0 :         GetErrorName(aErrorCode, errorName);
    1175           0 :         LOG(("nsChannelClassifier[%p]:OnClassifyComplete %s (suspended channel)",
    1176             :              this, errorName.get()));
    1177             :       }
    1178           0 :       MarkEntryClassified(aErrorCode);
    1179             : 
    1180           0 :       if (NS_FAILED(aErrorCode)) {
    1181           0 :         if (LOG_ENABLED()) {
    1182           0 :           nsCOMPtr<nsIURI> uri;
    1183           0 :           mChannel->GetURI(getter_AddRefs(uri));
    1184           0 :           LOG(("nsChannelClassifier[%p]: cancelling channel %p for %s "
    1185             :                "with error code %s", this, mChannel.get(),
    1186             :                uri->GetSpecOrDefault().get(), errorName.get()));
    1187             :         }
    1188             : 
    1189             :         // Channel will be cancelled (page element blocked) due to tracking
    1190             :         // protection or Safe Browsing.
    1191             :         // Do update the security state of the document and fire a security
    1192             :         // change event.
    1193           0 :         SetBlockedContent(mChannel, aErrorCode, aList, aProvider, aPrefix);
    1194             : 
    1195           0 :         mChannel->Cancel(aErrorCode);
    1196             :       }
    1197           0 :       LOG(("nsChannelClassifier[%p]: resuming channel %p from "
    1198             :            "OnClassifyComplete", this, mChannel.get()));
    1199           0 :       mChannel->Resume();
    1200             :     }
    1201             : 
    1202          67 :     mChannel = nullptr;
    1203          67 :     RemoveShutdownObserver();
    1204             : 
    1205          67 :     return NS_OK;
    1206             : }
    1207             : 
    1208             : nsresult
    1209           4 : nsChannelClassifier::CheckIsTrackerWithLocalTable(nsIURIClassifierCallback* aCallback)
    1210             : {
    1211             :   nsresult rv;
    1212             : 
    1213           4 :   if (!aCallback) {
    1214           0 :     return NS_ERROR_INVALID_ARG;
    1215             :   }
    1216             : 
    1217             :   nsCOMPtr<nsIURIClassifier> uriClassifier =
    1218           8 :     do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
    1219           4 :   if (NS_FAILED(rv)) {
    1220           0 :     return rv;
    1221             :   }
    1222             : 
    1223           4 :   if (!ShouldEnableTrackingProtection() && !ShouldEnableTrackingAnnotation()) {
    1224           3 :     return NS_ERROR_FAILURE;
    1225             :   }
    1226             : 
    1227           2 :   nsCOMPtr<nsIURI> uri;
    1228           1 :   rv = mChannel->GetURI(getter_AddRefs(uri));
    1229           1 :   if (NS_FAILED(rv) || !uri) {
    1230           0 :     return rv;
    1231             :   }
    1232             : 
    1233             :   nsCString trackingBlacklist =
    1234           2 :     CachedPrefs::GetInstance()->GetTrackingBlackList();
    1235           1 :   if (trackingBlacklist.IsEmpty()) {
    1236           0 :     LOG(("nsChannelClassifier[%p]:CheckIsTrackerWithLocalTable blacklist is empty",
    1237             :          this));
    1238           0 :     return NS_ERROR_FAILURE;
    1239             :   }
    1240             : 
    1241             :   nsCOMPtr<nsIURIClassifierCallback> callback =
    1242           2 :     new IsTrackerBlacklistedCallback(this, aCallback);
    1243             : 
    1244           1 :   LOG(("nsChannelClassifier[%p]:CheckIsTrackerWithLocalTable for uri=%s\n",
    1245             :        this, uri->GetSpecOrDefault().get()));
    1246           2 :   return uriClassifier->AsyncClassifyLocalWithTables(uri,
    1247             :                                                      trackingBlacklist,
    1248           2 :                                                      callback);
    1249             : }
    1250             : 
    1251             : already_AddRefed<nsIChannel>
    1252           0 : nsChannelClassifier::GetChannel()
    1253             : {
    1254           0 :   nsCOMPtr<nsIChannel> channel = mChannel;
    1255           0 :   return channel.forget();
    1256             : }
    1257             : 
    1258             : void
    1259           0 : nsChannelClassifier::AddShutdownObserver()
    1260             : {
    1261           0 :   nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
    1262           0 :   if (observerService) {
    1263           0 :     observerService->AddObserver(this, "profile-change-net-teardown", false);
    1264             :   }
    1265           0 : }
    1266             : 
    1267             : void
    1268          67 : nsChannelClassifier::RemoveShutdownObserver()
    1269             : {
    1270         134 :   nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
    1271          67 :   if (observerService) {
    1272          67 :     observerService->RemoveObserver(this, "profile-change-net-teardown");
    1273             :   }
    1274          67 : }
    1275             : 
    1276             : ///////////////////////////////////////////////////////////////////////////////
    1277             : // nsIObserver implementation
    1278             : NS_IMETHODIMP
    1279           0 : nsChannelClassifier::Observe(nsISupports *aSubject, const char *aTopic,
    1280             :                              const char16_t *aData)
    1281             : {
    1282           0 :   if (!strcmp(aTopic, "profile-change-net-teardown")) {
    1283             :     // If we aren't getting a callback for any reason, make sure
    1284             :     // we resume the channel.
    1285             : 
    1286           0 :     if (mChannel && mSuspendedChannel) {
    1287           0 :       mSuspendedChannel = false;
    1288           0 :       mChannel->Cancel(NS_ERROR_ABORT);
    1289           0 :       mChannel->Resume();
    1290           0 :       mChannel = nullptr;
    1291             :     }
    1292             : 
    1293           0 :     RemoveShutdownObserver();
    1294             :   }
    1295             : 
    1296           0 :   return NS_OK;
    1297             : }
    1298             : 
    1299             : #undef LOG_ENABLED
    1300             : 
    1301             : } // namespace net
    1302           9 : } // namespace mozilla

Generated by: LCOV version 1.13