diff --git a/netwerk/url-classifier/UrlClassifierExceptionList.cpp b/netwerk/url-classifier/UrlClassifierExceptionList.cpp new file mode 100644 index 000000000000..8d0ecec839b8 --- /dev/null +++ b/netwerk/url-classifier/UrlClassifierExceptionList.cpp @@ -0,0 +1,73 @@ +/* -*- 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 "UrlClassifierExceptionList.h" +#include "nsIUrlClassifierExceptionListEntry.h" +#include "nsIURI.h" +#include "mozilla/net/UrlClassifierCommon.h" + +namespace mozilla::net { + +NS_IMPL_ISUPPORTS(UrlClassifierExceptionList, nsIUrlClassifierExceptionList) + +NS_IMETHODIMP +UrlClassifierExceptionList::Init(const nsACString& aFeature) { + mFeature = aFeature; + return NS_OK; +} + +NS_IMETHODIMP +UrlClassifierExceptionList::AddEntry( + nsIUrlClassifierExceptionListEntry* aEntry) { + if (!aEntry) { + return NS_ERROR_INVALID_ARG; + } + + mEntries.AppendElement(aEntry); + return NS_OK; +} + +NS_IMETHODIMP +UrlClassifierExceptionList::Matches(nsIURI* aURI, nsIURI* aTopLevelURI, + bool aIsPrivateBrowsing, bool* aResult) { + NS_ENSURE_ARG_POINTER(aURI); + NS_ENSURE_ARG_POINTER(aResult); + + *aResult = false; + + UC_LOG_DEBUG( + ("UrlClassifierExceptionList::%s - aURI: %s, aTopLevelURI: %s, " + "aIsPrivateBrowsing: %d", + __FUNCTION__, aURI->GetSpecOrDefault().get(), + aTopLevelURI ? aTopLevelURI->GetSpecOrDefault().get() : "null", + aIsPrivateBrowsing)); + + for (auto& entry : mEntries) { + nsresult rv = + entry->Matches(aURI, aTopLevelURI, aIsPrivateBrowsing, aResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + continue; + } + if (*aResult) { + // Match found, return immediately. + if (MOZ_LOG_TEST(UrlClassifierCommon::sLog, LogLevel::Debug)) { + nsAutoCString entryString; + Unused << entry->Describe(entryString); + UC_LOG_DEBUG( + ("UrlClassifierExceptionList::%s - Exception list match found. " + "entry: %s", + __FUNCTION__, entryString.get())); + } + return NS_OK; + } + } + + // No match found, return false. + UC_LOG_DEBUG(("%s - No match found", __FUNCTION__)); + return NS_OK; +} + +} // namespace mozilla::net diff --git a/netwerk/url-classifier/UrlClassifierExceptionList.h b/netwerk/url-classifier/UrlClassifierExceptionList.h new file mode 100644 index 000000000000..2c46f160fcfd --- /dev/null +++ b/netwerk/url-classifier/UrlClassifierExceptionList.h @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_UrlClassifierExceptionList_h +#define mozilla_UrlClassifierExceptionList_h + +#include "nsIUrlClassifierExceptionList.h" +#include "nsISupports.h" +#include "nsTArray.h" +#include "nsString.h" + +namespace mozilla::net { + +/** + * @see nsIUrlClassifierExceptionList + */ +class UrlClassifierExceptionList final : public nsIUrlClassifierExceptionList { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIURLCLASSIFIEREXCEPTIONLIST + + UrlClassifierExceptionList() = default; + + private: + ~UrlClassifierExceptionList() = default; + + nsCString mFeature; + nsTArray> mEntries; +}; + +} // namespace mozilla::net + +#endif diff --git a/netwerk/url-classifier/UrlClassifierExceptionListEntry.cpp b/netwerk/url-classifier/UrlClassifierExceptionListEntry.cpp new file mode 100644 index 000000000000..9d1df9da7c2c --- /dev/null +++ b/netwerk/url-classifier/UrlClassifierExceptionListEntry.cpp @@ -0,0 +1,152 @@ +/* -*- 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 "UrlClassifierExceptionListEntry.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/Preferences.h" + +namespace mozilla::net { + +NS_IMPL_ISUPPORTS(UrlClassifierExceptionListEntry, + nsIUrlClassifierExceptionListEntry) + +NS_IMETHODIMP +UrlClassifierExceptionListEntry::Init( + const nsACString& aUrlPattern, const nsACString& aTopLevelUrlPattern, + bool aIsPrivateBrowsingOnly, + const nsTArray& aFilterContentBlockingCategories, + const nsTArray& aClassifierFeatures) { + mUrlPattern = aUrlPattern; + mTopLevelUrlPattern = aTopLevelUrlPattern; + mIsPrivateBrowsingOnly = aIsPrivateBrowsingOnly; + mFilterContentBlockingCategories = aFilterContentBlockingCategories.Clone(); + mClassifierFeatures = aClassifierFeatures.Clone(); + + // Create pattern from urlPattern and topLevelUrlPattern strings. + ErrorResult error; + mMatcher = new extensions::MatchPatternCore( + NS_ConvertUTF8toUTF16(mUrlPattern), false, false, error); + RETURN_NSRESULT_ON_FAILURE(error); + + if (!mTopLevelUrlPattern.IsEmpty()) { + mTopLevelMatcher = new extensions::MatchPatternCore( + NS_ConvertUTF8toUTF16(mTopLevelUrlPattern), false, false, error); + RETURN_NSRESULT_ON_FAILURE(error); + } + return NS_OK; +} + +NS_IMETHODIMP +UrlClassifierExceptionListEntry::Matches(nsIURI* aURI, nsIURI* aTopLevelURI, + bool aIsPrivateBrowsing, + bool* aResult) { + NS_ENSURE_ARG_POINTER(aURI); + NS_ENSURE_ARG_POINTER(aResult); + *aResult = false; + + // Do the private browsing check first, because it's cheap. + if (!aIsPrivateBrowsing && mIsPrivateBrowsingOnly) { + return NS_OK; + } + + // Next, check if the current content blocking category pref matches the + // allowed content blocking categories for this exception entry. + if (!mFilterContentBlockingCategories.IsEmpty()) { + nsCString prefValue; + // TODO: Bug 1956620: This is a desktop-only pref. We also need to check + // Fenix. + nsresult rv = + Preferences::GetCString("browser.contentblocking.category", prefValue); + + // If the pref is not set this check is skipped. + if (NS_SUCCEEDED(rv) && !prefValue.IsEmpty()) { + if (!mFilterContentBlockingCategories.Contains(prefValue)) { + return NS_OK; + } + } + } + + // Check if the load URI matches the urlPattern. + if (!mMatcher->Matches(aURI)) { + return NS_OK; + } + + // If this entry filters for top level site, check if the top level URI + // matches the topLevelUrlPattern. If the entry filters for top level site, + // but the caller does not provide one, we will not match. + if (mTopLevelMatcher && + (!aTopLevelURI || !mTopLevelMatcher->Matches(aTopLevelURI))) { + return NS_OK; + } + + *aResult = true; + return NS_OK; +} + +NS_IMETHODIMP +UrlClassifierExceptionListEntry::GetUrlPattern(nsACString& aUrlPattern) { + aUrlPattern = mUrlPattern; + return NS_OK; +} + +NS_IMETHODIMP +UrlClassifierExceptionListEntry::GetTopLevelUrlPattern( + nsACString& aTopLevelUrlPattern) { + aTopLevelUrlPattern = mTopLevelUrlPattern; + return NS_OK; +} + +NS_IMETHODIMP +UrlClassifierExceptionListEntry::GetIsPrivateBrowsingOnly( + bool* aIsPrivateBrowsingOnly) { + *aIsPrivateBrowsingOnly = mIsPrivateBrowsingOnly; + return NS_OK; +} + +NS_IMETHODIMP +UrlClassifierExceptionListEntry::GetFilterContentBlockingCategories( + nsTArray& aFilterContentBlockingCategories) { + aFilterContentBlockingCategories = mFilterContentBlockingCategories.Clone(); + return NS_OK; +} + +NS_IMETHODIMP +UrlClassifierExceptionListEntry::GetClassifierFeatures( + nsTArray& aClassifierFeatures) { + aClassifierFeatures = mClassifierFeatures.Clone(); + return NS_OK; +} + +NS_IMETHODIMP +UrlClassifierExceptionListEntry::Describe(nsACString& aDescription) { + nsAutoCString categories; + for (const auto& category : mFilterContentBlockingCategories) { + if (!categories.IsEmpty()) { + categories.AppendLiteral(", "); + } + categories.Append(category); + } + + nsAutoCString classifierFeatures; + for (const auto& feature : mClassifierFeatures) { + if (!classifierFeatures.IsEmpty()) { + classifierFeatures.AppendLiteral(", "); + } + classifierFeatures.Append(feature); + } + + aDescription.AppendPrintf( + "UrlClassifierExceptionListEntry(urlPattern='%s', " + "topLevelUrlPattern='%s', isPrivateBrowsingOnly=%s, " + "filterContentBlockingCategories=[%s], classifierFeatures=[%s])", + mUrlPattern.get(), mTopLevelUrlPattern.get(), + mIsPrivateBrowsingOnly ? "true" : "false", categories.get(), + classifierFeatures.get()); + + return NS_OK; +} + +} // namespace mozilla::net diff --git a/netwerk/url-classifier/UrlClassifierExceptionListEntry.h b/netwerk/url-classifier/UrlClassifierExceptionListEntry.h new file mode 100644 index 000000000000..ec263cec3c67 --- /dev/null +++ b/netwerk/url-classifier/UrlClassifierExceptionListEntry.h @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_UrlClassifierExceptionListEntry_h +#define mozilla_UrlClassifierExceptionListEntry_h + +#include "mozilla/extensions/MatchPattern.h" +#include "nsIUrlClassifierExceptionListEntry.h" +#include "nsString.h" +#include "nsISupports.h" + +namespace mozilla::net { + +/** + * @see nsIUrlClassifierExceptionListEntry + */ +class UrlClassifierExceptionListEntry final + : public nsIUrlClassifierExceptionListEntry { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIURLCLASSIFIEREXCEPTIONLISTENTRY + + UrlClassifierExceptionListEntry() = default; + + UrlClassifierExceptionListEntry( + const nsACString& aUrlPattern, const nsACString& aTopLevelUrlPattern, + bool aIsPrivateBrowsingOnly, + const nsTArray& aClassifierFeatures) + : mUrlPattern(aUrlPattern), + mTopLevelUrlPattern(aTopLevelUrlPattern), + mIsPrivateBrowsingOnly(aIsPrivateBrowsingOnly) { + mClassifierFeatures = aClassifierFeatures.Clone(); + } + + private: + ~UrlClassifierExceptionListEntry() = default; + + nsCString mUrlPattern; + nsCString mTopLevelUrlPattern; + bool mIsPrivateBrowsingOnly{}; + nsTArray mFilterContentBlockingCategories; + nsTArray mClassifierFeatures; + + RefPtr mMatcher; + RefPtr mTopLevelMatcher; +}; + +} // namespace mozilla::net + +#endif diff --git a/netwerk/url-classifier/components.conf b/netwerk/url-classifier/components.conf index 3cf63a06f112..fbc8fd07f13a 100644 --- a/netwerk/url-classifier/components.conf +++ b/netwerk/url-classifier/components.conf @@ -19,4 +19,18 @@ Classes = [ 'esModule': 'resource://gre/modules/UrlClassifierExceptionListService.sys.mjs', 'constructor': 'UrlClassifierExceptionListService', }, + { + 'cid': '{8753A413-3ED6-4A61-A1DC-B31A7E69B796}', + 'interfaces': ['nsIUrlClassifierExceptionListEntry'], + 'headers': ['mozilla/net/UrlClassifierExceptionListEntry.h'], + 'type': 'mozilla::net::UrlClassifierExceptionListEntry', + 'contract_ids': ['@mozilla.org/url-classifier/exception-list-entry;1'], + }, + { + 'cid': '{807535BF-018E-4300-B8D3-4A6405FB9F65}', + 'interfaces': ['nsIUrlClassifierExceptionList'], + 'headers': ['mozilla/net/UrlClassifierExceptionList.h'], + 'type': 'mozilla::net::UrlClassifierExceptionList', + 'contract_ids': ['@mozilla.org/url-classifier/exception-list;1'], + }, ] diff --git a/netwerk/url-classifier/moz.build b/netwerk/url-classifier/moz.build index 089693818226..564083d8a528 100644 --- a/netwerk/url-classifier/moz.build +++ b/netwerk/url-classifier/moz.build @@ -10,6 +10,8 @@ with Files("**"): XPIDL_SOURCES += [ "nsIChannelClassifierService.idl", "nsIURIClassifier.idl", + "nsIUrlClassifierExceptionList.idl", + "nsIUrlClassifierExceptionListEntry.idl", "nsIUrlClassifierExceptionListService.idl", "nsIUrlClassifierFeature.idl", ] @@ -32,6 +34,8 @@ UNIFIED_SOURCES += [ "ChannelClassifierService.cpp", "nsChannelClassifier.cpp", "UrlClassifierCommon.cpp", + "UrlClassifierExceptionList.cpp", + "UrlClassifierExceptionListEntry.cpp", "UrlClassifierFeatureBase.cpp", "UrlClassifierFeatureConsentManagerAnnotation.cpp", "UrlClassifierFeatureCryptominingAnnotation.cpp", @@ -54,6 +58,8 @@ EXPORTS.mozilla.net += [ "AsyncUrlChannelClassifier.h", "ChannelClassifierService.h", "UrlClassifierCommon.h", + "UrlClassifierExceptionList.h", + "UrlClassifierExceptionListEntry.h", "UrlClassifierFeatureFactory.h", "UrlClassifierFeatureResult.h", ] diff --git a/netwerk/url-classifier/nsIUrlClassifierExceptionList.idl b/netwerk/url-classifier/nsIUrlClassifierExceptionList.idl new file mode 100644 index 000000000000..e6e1bd903878 --- /dev/null +++ b/netwerk/url-classifier/nsIUrlClassifierExceptionList.idl @@ -0,0 +1,37 @@ +/* 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 "nsISupports.idl" +#include "nsIURI.idl" +#include "nsIUrlClassifierExceptionListEntry.idl" + +/** + * Interface for managing URL classifier exception lists. + * + * @see nsIUrlClassifierExceptionListEntry + */ +[scriptable, uuid(807535BF-018E-4300-B8D3-4A6405FB9F65)] +interface nsIUrlClassifierExceptionList : nsISupports +{ + /** + * Initialize the exception list for a specific feature. + * @param aFeature The feature to initialize the exception list for + */ + void init(in ACString aFeature); + + /** + * Add a new exception list entry to the list. + * @param aEntry The exception list entry to add + */ + void addEntry(in nsIUrlClassifierExceptionListEntry aEntry); + + /** + * Check if the exception list matches the given URI. + * @param aURI The URI to check + * @param aTopLevelURI The top-level URI to check + * @param aIsPrivateBrowsing Whether the load is in private browsing mode + * @return True if the exception list matches, false otherwise + */ + boolean matches(in nsIURI aURI, in nsIURI aTopLevelURI, in boolean aIsPrivateBrowsing); +}; diff --git a/netwerk/url-classifier/nsIUrlClassifierExceptionListEntry.idl b/netwerk/url-classifier/nsIUrlClassifierExceptionListEntry.idl new file mode 100644 index 000000000000..271550fd8c65 --- /dev/null +++ b/netwerk/url-classifier/nsIUrlClassifierExceptionListEntry.idl @@ -0,0 +1,75 @@ +/* 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 "nsISupports.idl" +#include "nsIURI.idl" + +/** + * Represents a single exception list entry for the url classifier exception list. + * Needs to be initialized with init() before use. + * + * @see nsIUrlClassifierExceptionList + */ +[scriptable, uuid(8753A413-3ED6-4A61-A1DC-B31A7E69B796)] +interface nsIUrlClassifierExceptionListEntry : nsISupports +{ + /** + * Initialize all fields of the exception list entry. + * @param aUrlPattern - The urlPattern for the url to be loaded. See https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Match_patterns for more info. + * @param aTopLevelUrlPattern - Optional top-level url pattern to filter for this exception. If not set the exception applies to all top level sites. + * @param aIsPrivateBrowsingOnly - Whether this applies only to private browsing + * @param aFilterContentBlockingCategories - The content blocking categories to filter for this exception. + * @param aClassifierFeatures - The list of url classifier features to apply this exception to. + */ + void init(in ACString aUrlPattern, + in ACString aTopLevelUrlPattern, + in boolean aIsPrivateBrowsingOnly, + in Array aFilterContentBlockingCategories, + in Array aClassifierFeatures); + + /** + * Check if the exception list entry matches the given load. + * @param aURI The URI to check + * @param aTopLevelURI The top-level URI to check + * @param aIsPrivateBrowsing Whether the load is in private browsing mode + * @return True if the exception list entry matches the given load and it + * should be skipped from classification, false otherwise + */ + boolean matches(in nsIURI aURI, in nsIURI aTopLevelURI, in boolean aIsPrivateBrowsing); + + /** + * The urlPattern name for this exception entry. + */ + readonly attribute ACString urlPattern; + + /** + * Optional top-level url pattern to filter for this exception. If not set + * the exception applies to all top level sites. + */ + readonly attribute ACString topLevelUrlPattern; + + /** + * Whether this exception only applies in private browsing mode. + */ + readonly attribute boolean isPrivateBrowsingOnly; + + /** + * Optional array of content blocking categories to filter for this + * exception. If not set the exception applies to all content blocking + * categories. + */ + readonly attribute Array filterContentBlockingCategories; + + /** + * The list of url classifier features to apply this exception to. + */ + readonly attribute Array classifierFeatures; + + /** + * Returns a string containing all attributes of this exception list entry. + * This is intended for logging purposes only. + * @return A string containing all attributes + */ + [noscript] ACString describe(); +};