/* -*- 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/. */ #include "ChannelClassifierService.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/dom/BrowserParent.h" #include "mozilla/dom/BrowsingContext.h" #include "mozilla/dom/CanonicalBrowsingContext.h" #include "mozilla/dom/WindowGlobalParent.h" #include "mozilla/net/UrlClassifierCommon.h" #include "UrlClassifierFeatureCryptominingProtection.h" #include "UrlClassifierFeatureFingerprintingProtection.h" #include "UrlClassifierFeatureSocialTrackingProtection.h" #include "UrlClassifierFeatureTrackingProtection.h" #include "mozilla/StaticPtr.h" #include "nsIChannel.h" namespace mozilla { namespace net { static StaticRefPtr<ChannelClassifierService> gChannelClassifierService; NS_IMPL_ISUPPORTS(UrlClassifierBlockedChannel, nsIUrlClassifierBlockedChannel) UrlClassifierBlockedChannel::UrlClassifierBlockedChannel(nsIChannel* aChannel) : mChannel(aChannel), mDecision(ChannelBlockDecision::Blocked), mReason(TRACKING_PROTECTION) { MOZ_ASSERT(aChannel); } NS_IMETHODIMP UrlClassifierBlockedChannel::GetReason(uint8_t* aReason) { NS_ENSURE_ARG_POINTER(aReason); *aReason = mReason; return NS_OK; } NS_IMETHODIMP UrlClassifierBlockedChannel::GetUrl(nsAString& aUrl) { nsCOMPtr<nsIURI> uri; mChannel->GetURI(getter_AddRefs(uri)); if (uri) { CopyUTF8toUTF16(uri->GetSpecOrDefault(), aUrl); } return NS_OK; } NS_IMETHODIMP UrlClassifierBlockedChannel::GetTabId(uint64_t* aTabId) { NS_ENSURE_ARG_POINTER(aTabId); *aTabId = 0; nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo(); MOZ_ASSERT(loadInfo); RefPtr<dom::BrowsingContext> browsingContext; nsresult rv = loadInfo->GetTargetBrowsingContext(getter_AddRefs(browsingContext)); if (NS_WARN_IF(NS_FAILED(rv)) || !browsingContext) { return NS_ERROR_FAILURE; } // Get top-level browsing context to ensure window global parent is ready // to use, tabId is the same anyway. dom::CanonicalBrowsingContext* top = browsingContext->Canonical()->Top(); dom::WindowGlobalParent* wgp = top->GetCurrentWindowGlobal(); if (!wgp) { return NS_ERROR_FAILURE; } RefPtr<dom::BrowserParent> browserParent = wgp->GetBrowserParent(); if (!browserParent) { return NS_ERROR_FAILURE; } *aTabId = browserParent->GetTabId(); return NS_OK; } NS_IMETHODIMP UrlClassifierBlockedChannel::GetChannelId(uint64_t* aChannelId) { NS_ENSURE_ARG_POINTER(aChannelId); nsCOMPtr<nsIIdentChannel> channel(do_QueryInterface(mChannel)); *aChannelId = channel ? channel->ChannelId() : 0; return NS_OK; } NS_IMETHODIMP UrlClassifierBlockedChannel::GetTopLevelUrl(nsAString& aTopLevelUrl) { nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo(); MOZ_ASSERT(loadInfo); RefPtr<dom::BrowsingContext> browsingContext; nsresult rv = loadInfo->GetTargetBrowsingContext(getter_AddRefs(browsingContext)); if (NS_WARN_IF(NS_FAILED(rv)) || !browsingContext) { return NS_ERROR_FAILURE; } // Get top-level browsing context to ensure window global parent is ready // to use, tabId is the same anyway. dom::CanonicalBrowsingContext* top = browsingContext->Canonical()->Top(); dom::WindowGlobalParent* wgp = top->GetCurrentWindowGlobal(); if (!wgp) { return NS_ERROR_FAILURE; } RefPtr<nsIURI> uri = wgp->GetDocumentURI(); if (!uri) { return NS_ERROR_FAILURE; } CopyUTF8toUTF16(uri->GetSpecOrDefault(), aTopLevelUrl); return NS_OK; } NS_IMETHODIMP UrlClassifierBlockedChannel::GetTables(nsACString& aTables) { aTables.Assign(mTables); return NS_OK; } NS_IMETHODIMP UrlClassifierBlockedChannel::GetIsPrivateBrowsing(bool* aIsPrivateBrowsing) { NS_ENSURE_ARG_POINTER(aIsPrivateBrowsing); *aIsPrivateBrowsing = NS_UsePrivateBrowsing(mChannel); return NS_OK; } NS_IMETHODIMP UrlClassifierBlockedChannel::Allow() { UC_LOG(("ChannelClassifierService: allow loading the channel %p", mChannel.get())); mDecision = ChannelBlockDecision::Allowed; return NS_OK; } NS_IMETHODIMP UrlClassifierBlockedChannel::Replace() { UC_LOG(("ChannelClassifierService: replace channel %p", mChannel.get())); mDecision = ChannelBlockDecision::Replaced; return NS_OK; } void UrlClassifierBlockedChannel::SetReason(const nsACString& aFeatureName, const nsACString& aTableName) { mTables = aTableName; nsCOMPtr<nsIUrlClassifierFeature> feature; feature = UrlClassifierFeatureTrackingProtection::GetIfNameMatches(aFeatureName); if (feature) { mReason = TRACKING_PROTECTION; return; } feature = UrlClassifierFeatureSocialTrackingProtection::GetIfNameMatches( aFeatureName); if (feature) { mReason = SOCIAL_TRACKING_PROTECTION; return; } feature = UrlClassifierFeatureFingerprintingProtection::GetIfNameMatches( aFeatureName); if (feature) { mReason = FINGERPRINTING_PROTECTION; return; } feature = UrlClassifierFeatureCryptominingProtection::GetIfNameMatches( aFeatureName); if (feature) { mReason = CRYPTOMINING_PROTECTION; return; } } NS_IMPL_ISUPPORTS(ChannelClassifierService, nsIChannelClassifierService) // static already_AddRefed<nsIChannelClassifierService> ChannelClassifierService::GetSingleton() { if (gChannelClassifierService) { return do_AddRef(gChannelClassifierService); } gChannelClassifierService = new ChannelClassifierService(); ClearOnShutdown(&gChannelClassifierService); return do_AddRef(gChannelClassifierService); } ChannelClassifierService::ChannelClassifierService() { mListeners.Clear(); } NS_IMETHODIMP ChannelClassifierService::AddListener(nsIObserver* aObserver) { MOZ_ASSERT(aObserver); MOZ_ASSERT(!mListeners.Contains(aObserver)); mListeners.AppendElement(aObserver); return NS_OK; } NS_IMETHODIMP ChannelClassifierService::RemoveListener(nsIObserver* aObserver) { MOZ_ASSERT(aObserver); MOZ_ASSERT(mListeners.Contains(aObserver)); mListeners.RemoveElement(aObserver); return NS_OK; } /* static */ ChannelBlockDecision ChannelClassifierService::OnBeforeBlockChannel( nsIChannel* aChannel, const nsACString& aFeatureName, const nsACString& aTableName) { MOZ_ASSERT(aChannel); // Don't bother continuing if no one has ever registered listener if (!gChannelClassifierService || !gChannelClassifierService->HasListener()) { return ChannelBlockDecision::Blocked; } ChannelBlockDecision decision; nsresult rv = gChannelClassifierService->OnBeforeBlockChannel( aChannel, aFeatureName, aTableName, decision); if (NS_WARN_IF(NS_FAILED(rv))) { return ChannelBlockDecision::Blocked; } return decision; } nsresult ChannelClassifierService::OnBeforeBlockChannel( nsIChannel* aChannel, const nsACString& aFeatureName, const nsACString& aTableName, ChannelBlockDecision& aDecision) { MOZ_ASSERT(aChannel); aDecision = ChannelBlockDecision::Blocked; RefPtr<UrlClassifierBlockedChannel> channel = new UrlClassifierBlockedChannel(aChannel); channel->SetReason(aFeatureName, aTableName); for (const auto& listener : mListeners) { listener->Observe( NS_ISUPPORTS_CAST(nsIUrlClassifierBlockedChannel*, channel), "urlclassifier-before-block-channel", nullptr); aDecision = channel->GetDecision(); } return NS_OK; } } // namespace net } // namespace mozilla