summaryrefslogtreecommitdiffstats
path: root/toolkit/components/antitracking/ContentBlockingAllowList.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/antitracking/ContentBlockingAllowList.cpp')
-rw-r--r--toolkit/components/antitracking/ContentBlockingAllowList.cpp257
1 files changed, 257 insertions, 0 deletions
diff --git a/toolkit/components/antitracking/ContentBlockingAllowList.cpp b/toolkit/components/antitracking/ContentBlockingAllowList.cpp
new file mode 100644
index 0000000000..25806cd1e7
--- /dev/null
+++ b/toolkit/components/antitracking/ContentBlockingAllowList.cpp
@@ -0,0 +1,257 @@
+/* -*- 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 "AntiTrackingLog.h"
+#include "ContentBlockingAllowList.h"
+
+#include "mozilla/dom/BrowsingContext.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/PermissionManager.h"
+#include "mozilla/ScopeExit.h"
+#include "nsContentUtils.h"
+#include "nsGlobalWindowInner.h"
+#include "nsICookieJarSettings.h"
+#include "nsIHttpChannel.h"
+#include "nsIHttpChannelInternal.h"
+#include "nsNetUtil.h"
+
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS(ContentBlockingAllowList, nsIContentBlockingAllowList)
+
+NS_IMETHODIMP
+// Wrapper for the static ContentBlockingAllowList::ComputePrincipal method
+ContentBlockingAllowList::ComputeContentBlockingAllowListPrincipal(
+ nsIPrincipal* aDocumentPrincipal, nsIPrincipal** aPrincipal) {
+ NS_ENSURE_ARG_POINTER(aDocumentPrincipal);
+ NS_ENSURE_ARG_POINTER(aPrincipal);
+
+ nsCOMPtr<nsIPrincipal> principal;
+ ContentBlockingAllowList::ComputePrincipal(aDocumentPrincipal,
+ getter_AddRefs(principal));
+
+ NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
+
+ principal.forget(aPrincipal);
+
+ return NS_OK;
+}
+
+/* static */ bool ContentBlockingAllowList::Check(
+ nsIPrincipal* aTopWinPrincipal, bool aIsPrivateBrowsing) {
+ bool isAllowed = false;
+ nsresult rv = Check(aTopWinPrincipal, aIsPrivateBrowsing, isAllowed);
+ if (NS_SUCCEEDED(rv) && isAllowed) {
+ LOG(
+ ("The top-level window is on the content blocking allow list, "
+ "bail out early"));
+ return true;
+ }
+ if (NS_FAILED(rv)) {
+ LOG(("Checking the content blocking allow list for failed with %" PRIx32,
+ static_cast<uint32_t>(rv)));
+ }
+ return false;
+}
+
+/* static */ bool ContentBlockingAllowList::Check(
+ nsICookieJarSettings* aCookieJarSettings) {
+ if (!aCookieJarSettings) {
+ LOG(
+ ("Could not check the content blocking allow list because the cookie "
+ "jar settings wasn't available"));
+ return false;
+ }
+
+ return aCookieJarSettings->GetIsOnContentBlockingAllowList();
+}
+
+/* static */ bool ContentBlockingAllowList::Check(nsPIDOMWindowInner* aWindow) {
+ // TODO: this is a quick fix to ensure that we allow storage permission for
+ // a chrome window. We should check if there is a better way to do this in
+ // Bug 1626223.
+ if (nsGlobalWindowInner::Cast(aWindow)->GetPrincipal() ==
+ nsContentUtils::GetSystemPrincipal()) {
+ return true;
+ }
+
+ // We can check the IsOnContentBlockingAllowList flag in the document's
+ // CookieJarSettings. Because this flag represents the fact that whether the
+ // top-level document is on the content blocking allow list. And this flag was
+ // propagated from the top-level as the CookieJarSettings inherits from the
+ // parent.
+ RefPtr<dom::Document> doc = nsGlobalWindowInner::Cast(aWindow)->GetDocument();
+
+ if (!doc) {
+ LOG(
+ ("Could not check the content blocking allow list because the document "
+ "wasn't available"));
+ return false;
+ }
+
+ nsCOMPtr<nsICookieJarSettings> cookieJarSettings = doc->CookieJarSettings();
+
+ return ContentBlockingAllowList::Check(cookieJarSettings);
+}
+
+/* static */ bool ContentBlockingAllowList::Check(nsIHttpChannel* aChannel) {
+ nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
+ nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
+
+ Unused << loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
+
+ return ContentBlockingAllowList::Check(cookieJarSettings);
+}
+
+nsresult ContentBlockingAllowList::Check(
+ nsIPrincipal* aContentBlockingAllowListPrincipal, bool aIsPrivateBrowsing,
+ bool& aIsAllowListed) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ aIsAllowListed = false;
+
+ if (!aContentBlockingAllowListPrincipal) {
+ // Nothing to do!
+ return NS_OK;
+ }
+
+ LOG_PRIN(("Deciding whether the user has overridden content blocking for %s",
+ _spec),
+ aContentBlockingAllowListPrincipal);
+
+ PermissionManager* permManager = PermissionManager::GetInstance();
+ NS_ENSURE_TRUE(permManager, NS_ERROR_FAILURE);
+
+ // Check both the normal mode and private browsing mode user override
+ // permissions.
+ std::pair<const nsLiteralCString, bool> types[] = {
+ {"trackingprotection"_ns, false}, {"trackingprotection-pb"_ns, true}};
+
+ for (const auto& type : types) {
+ if (aIsPrivateBrowsing != type.second) {
+ continue;
+ }
+
+ uint32_t permissions = nsIPermissionManager::UNKNOWN_ACTION;
+ nsresult rv = permManager->TestPermissionFromPrincipal(
+ aContentBlockingAllowListPrincipal, type.first, &permissions);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (permissions == nsIPermissionManager::ALLOW_ACTION) {
+ aIsAllowListed = true;
+ LOG(("Found user override type %s", type.first.get()));
+ // Stop checking the next permisson type if we decided to override.
+ break;
+ }
+ }
+
+ if (!aIsAllowListed) {
+ LOG(("No user override found"));
+ }
+
+ return NS_OK;
+}
+
+/* static */ void ContentBlockingAllowList::ComputePrincipal(
+ nsIPrincipal* aDocumentPrincipal, nsIPrincipal** aPrincipal) {
+ MOZ_ASSERT(aPrincipal);
+
+ auto returnInputArgument =
+ MakeScopeExit([&] { NS_IF_ADDREF(*aPrincipal = aDocumentPrincipal); });
+
+ BasePrincipal* bp = BasePrincipal::Cast(aDocumentPrincipal);
+ if (!bp || !bp->IsContentPrincipal()) {
+ // If we have something other than a content principal, just return what we
+ // have. This includes the case where we were passed a nullptr.
+ return;
+ }
+
+ if (aDocumentPrincipal->SchemeIs("chrome") ||
+ aDocumentPrincipal->SchemeIs("about")) {
+ returnInputArgument.release();
+ *aPrincipal = nullptr;
+ return;
+ }
+
+ // Take the host/port portion so we can allowlist by site. Also ignore the
+ // scheme, since users who put sites on the allowlist probably don't expect
+ // allowlisting to depend on scheme.
+ nsAutoCString escaped("https://"_ns);
+ nsAutoCString temp;
+ nsresult rv = aDocumentPrincipal->GetHostPort(temp);
+ // view-source URIs will be handled by the next block.
+ if (NS_FAILED(rv) && !aDocumentPrincipal->SchemeIs("view-source")) {
+ // Normal for some loads, no need to print a warning
+ return;
+ }
+
+ // GetHostPort returns an empty string (with a success error code) for file://
+ // URIs.
+ if (temp.IsEmpty()) {
+ // In this case we want to make sure that our allow list principal would be
+ // computed as null.
+ returnInputArgument.release();
+ *aPrincipal = nullptr;
+ return;
+ }
+ escaped.Append(temp);
+ nsCOMPtr<nsIURI> uri;
+ rv = NS_NewURI(getter_AddRefs(uri), escaped);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateContentPrincipal(
+ uri, aDocumentPrincipal->OriginAttributesRef());
+ if (NS_WARN_IF(!principal)) {
+ return;
+ }
+
+ returnInputArgument.release();
+ principal.forget(aPrincipal);
+}
+
+/* static */ void ContentBlockingAllowList::RecomputePrincipal(
+ nsIURI* aURIBeingLoaded, const OriginAttributes& aAttrs,
+ nsIPrincipal** aPrincipal) {
+ MOZ_ASSERT(aPrincipal);
+
+ auto returnInputArgument = MakeScopeExit([&] { *aPrincipal = nullptr; });
+
+ // Take the host/port portion so we can allowlist by site. Also ignore the
+ // scheme, since users who put sites on the allowlist probably don't expect
+ // allowlisting to depend on scheme.
+ nsAutoCString escaped("https://"_ns);
+ nsAutoCString temp;
+ nsresult rv = aURIBeingLoaded->GetHostPort(temp);
+ // view-source URIs will be handled by the next block.
+ if (NS_FAILED(rv) && !aURIBeingLoaded->SchemeIs("view-source")) {
+ // Normal for some loads, no need to print a warning
+ return;
+ }
+
+ // GetHostPort returns an empty string (with a success error code) for file://
+ // URIs.
+ if (temp.IsEmpty()) {
+ return;
+ }
+ escaped.Append(temp);
+
+ nsCOMPtr<nsIURI> uri;
+ rv = NS_NewURI(getter_AddRefs(uri), escaped);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal =
+ BasePrincipal::CreateContentPrincipal(uri, aAttrs);
+ if (NS_WARN_IF(!principal)) {
+ return;
+ }
+
+ returnInputArgument.release();
+ principal.forget(aPrincipal);
+}