Bug 1509112 - Consider the content frame sitting directly beneath a moz-extension frame that has a host permission granting access to that frame as a top-level frame; r=kmag,baku
authorEhsan Akhgari <ehsan@mozilla.com>
Wed, 23 Jan 2019 14:55:49 +0000
changeset 515107 afa7959985cdee0203b6e5d5de61392578ddcbff
parent 515106 375bd8800b2d5b759453308e6115406decd20df8
child 515108 fb4f75995571f086bfcbed44221485736c029f44
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskmag, baku
bugs1509112
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1509112 - Consider the content frame sitting directly beneath a moz-extension frame that has a host permission granting access to that frame as a top-level frame; r=kmag,baku Differential Revision: https://phabricator.services.mozilla.com/D14832
dom/base/ThirdPartyUtil.cpp
dom/base/nsGlobalWindowOuter.cpp
dom/base/nsGlobalWindowOuter.h
netwerk/base/mozIThirdPartyUtil.idl
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/url-classifier/UrlClassifierCommon.cpp
netwerk/url-classifier/UrlClassifierCommon.h
toolkit/components/antitracking/AntiTrackingCommon.cpp
toolkit/components/antitracking/AntiTrackingCommon.h
toolkit/components/antitracking/test/browser/browser.ini
toolkit/components/antitracking/test/browser/browser_addonHostPermissionIgnoredInTP.js
toolkit/components/antitracking/test/browser/container.html
toolkit/components/url-classifier/nsUrlClassifierUtils.cpp
--- a/dom/base/ThirdPartyUtil.cpp
+++ b/dom/base/ThirdPartyUtil.cpp
@@ -243,33 +243,36 @@ ThirdPartyUtil::IsThirdPartyChannel(nsIC
   }
 
   // Determine whether aURI is foreign with respect to channelURI.
   return IsThirdPartyInternal(channelDomain, aURI, aResult);
 }
 
 NS_IMETHODIMP
 ThirdPartyUtil::GetTopWindowForChannel(nsIChannel* aChannel,
+                                       nsIURI* aURIBeingLoaded,
                                        mozIDOMWindowProxy** aWin) {
   NS_ENSURE_ARG(aWin);
 
   // Find the associated window and its parent window.
   nsCOMPtr<nsILoadContext> ctx;
   NS_QueryNotificationCallbacks(aChannel, ctx);
   if (!ctx) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsCOMPtr<mozIDOMWindowProxy> window;
   ctx->GetAssociatedWindow(getter_AddRefs(window));
   if (!window) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  nsCOMPtr<nsPIDOMWindowOuter> top = nsPIDOMWindowOuter::From(window)->GetTop();
+  nsCOMPtr<nsPIDOMWindowOuter> top =
+      nsGlobalWindowOuter::Cast(window)
+          ->GetTopExcludingExtensionAccessibleContentFrames(aURIBeingLoaded);
   top.forget(aWin);
   return NS_OK;
 }
 
 // Get the base domain for aHostURI; e.g. for "www.bbc.co.uk", this would be
 // "bbc.co.uk". Only properly-formed URI's are tolerated, though a trailing
 // dot may be present. If aHostURI is an IP address, an alias such as
 // 'localhost', an eTLD such as 'co.uk', or the empty string, aBaseDomain will
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -2998,20 +2998,23 @@ already_AddRefed<nsPIDOMWindowOuter> nsG
     nsCOMPtr<nsPIDOMWindowOuter> win = parent->GetWindow();
     return win.forget();
   }
 
   nsCOMPtr<nsPIDOMWindowOuter> win(this);
   return win.forget();
 }
 
-static nsresult GetTopImpl(nsGlobalWindowOuter* aWin, nsPIDOMWindowOuter** aTop,
-                           bool aScriptable) {
+static nsresult GetTopImpl(nsGlobalWindowOuter* aWin, nsIURI* aURIBeingLoaded,
+                           nsPIDOMWindowOuter** aTop, bool aScriptable,
+                           bool aExcludingExtensionAccessibleContentFrames) {
   *aTop = nullptr;
 
+  MOZ_ASSERT_IF(aExcludingExtensionAccessibleContentFrames, !aScriptable);
+
   // Walk up the parent chain.
 
   nsCOMPtr<nsPIDOMWindowOuter> prevParent = aWin;
   nsCOMPtr<nsPIDOMWindowOuter> parent = aWin;
   do {
     if (!parent) {
       break;
     }
@@ -3019,16 +3022,53 @@ static nsresult GetTopImpl(nsGlobalWindo
     prevParent = parent;
 
     if (aScriptable) {
       parent = parent->GetScriptableParent();
     } else {
       parent = parent->GetParent();
     }
 
+    if (aExcludingExtensionAccessibleContentFrames) {
+      if (auto* p = nsGlobalWindowOuter::Cast(parent)) {
+        nsGlobalWindowInner* currentInner = p->GetCurrentInnerWindowInternal();
+        nsIURI* uri = prevParent->GetDocumentURI();
+        if (!uri) {
+          // If our parent doesn't have a URI yet, we have a document that is in
+          // the process of being loaded.  In that case, our caller is
+          // responsible for passing in the URI for the document that is being
+          // loaded, so we fall back to using that URI here.
+          uri = aURIBeingLoaded;
+        }
+
+        if (currentInner && uri) {
+          // If we find an inner window, we better find the uri for the current
+          // window we're looking at.  If we can't find it directly, it is the
+          // responsibility of our caller to provide it to us.
+          MOZ_DIAGNOSTIC_ASSERT(uri);
+
+          // If the new parent has permission to load the current page, we're
+          // at a moz-extension:// frame which has a host permission that allows
+          // it to load the document that we've loaded.  In that case, stop at
+          // this frame and consider it the top-level frame.
+          //
+          // Note that it's possible for the set of URIs accepted by
+          // AddonAllowsLoad() to change at runtime, but we don't need to cache
+          // the result of this check, since the important consumer of this code
+          // (which is nsIHttpChannelInternal.topWindowURI) already caches the
+          // result after computing it the first time.
+          if (BasePrincipal::Cast(p->GetPrincipal())
+                  ->AddonAllowsLoad(uri, true)) {
+            parent = prevParent;
+            break;
+          }
+        }
+      }
+    }
+
   } while (parent != prevParent);
 
   if (parent) {
     parent.swap(*aTop);
   }
 
   return NS_OK;
 }
@@ -3037,23 +3077,37 @@ static nsresult GetTopImpl(nsGlobalWindo
  * GetScriptableTop is called when script reads window.top.
  *
  * In contrast to GetRealTop, GetScriptableTop respects <iframe mozbrowser>
  * boundaries.  If we encounter a window owned by an <iframe mozbrowser> while
  * walking up the window hierarchy, we'll stop and return that window.
  */
 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetScriptableTop() {
   nsCOMPtr<nsPIDOMWindowOuter> window;
-  GetTopImpl(this, getter_AddRefs(window), /* aScriptable = */ true);
+  GetTopImpl(this, /* aURIBeingLoaded = */ nullptr, getter_AddRefs(window),
+             /* aScriptable = */ true,
+             /* aExcludingExtensionAccessibleContentFrames = */ false);
   return window.get();
 }
 
 already_AddRefed<nsPIDOMWindowOuter> nsGlobalWindowOuter::GetTop() {
   nsCOMPtr<nsPIDOMWindowOuter> window;
-  GetTopImpl(this, getter_AddRefs(window), /* aScriptable = */ false);
+  GetTopImpl(this, /* aURIBeingLoaded = */ nullptr, getter_AddRefs(window),
+             /* aScriptable = */ false,
+             /* aExcludingExtensionAccessibleContentFrames = */ false);
+  return window.forget();
+}
+
+already_AddRefed<nsPIDOMWindowOuter>
+nsGlobalWindowOuter::GetTopExcludingExtensionAccessibleContentFrames(
+    nsIURI* aURIBeingLoaded) {
+  nsCOMPtr<nsPIDOMWindowOuter> window;
+  GetTopImpl(this, aURIBeingLoaded, getter_AddRefs(window),
+             /* aScriptable = */ false,
+             /* aExcludingExtensionAccessibleContentFrames = */ true);
   return window.forget();
 }
 
 void nsGlobalWindowOuter::GetContentOuter(JSContext* aCx,
                                           JS::MutableHandle<JSObject*> aRetval,
                                           CallerType aCallerType,
                                           ErrorResult& aError) {
   nsCOMPtr<nsPIDOMWindowOuter> content =
--- a/dom/base/nsGlobalWindowOuter.h
+++ b/dom/base/nsGlobalWindowOuter.h
@@ -355,16 +355,22 @@ class nsGlobalWindowOuter final : public
   NS_DECL_NSIINTERFACEREQUESTOR
 
   // nsIObserver
   NS_DECL_NSIOBSERVER
 
   already_AddRefed<nsPIDOMWindowOuter> IndexedGetterOuter(uint32_t aIndex);
 
   already_AddRefed<nsPIDOMWindowOuter> GetTop() override;
+  // Similar to GetTop() except that it stops at content frames that an
+  // extension has permission to access.  This is used by the third-party util
+  // service in order to determine the top window for a channel which is used
+  // in third-partiness checks.
+  already_AddRefed<nsPIDOMWindowOuter>
+  GetTopExcludingExtensionAccessibleContentFrames(nsIURI* aURIBeingLoaded);
   nsPIDOMWindowOuter* GetScriptableTop() override;
   inline nsGlobalWindowOuter* GetTopInternal();
 
   inline nsGlobalWindowOuter* GetScriptableTopInternal();
 
   already_AddRefed<mozilla::dom::BrowsingContext> GetChildWindow(
       const nsAString& aName);
 
--- a/netwerk/base/mozIThirdPartyUtil.idl
+++ b/netwerk/base/mozIThirdPartyUtil.idl
@@ -149,17 +149,18 @@ interface mozIThirdPartyUtil : nsISuppor
    */
   nsIURI getURIFromWindow(in mozIDOMWindowProxy aWindow);
 
   /**
    * getTopWindowForChannel
    *
    * Returns the top-level window associated with the given channel.
    */
-  mozIDOMWindowProxy getTopWindowForChannel(in nsIChannel aChannel);
+  [noscript]
+  mozIDOMWindowProxy getTopWindowForChannel(in nsIChannel aChannel, [optional] in nsIURI aURIBeingLoaded);
 };
 
 %{ C++
 /**
  * The mozIThirdPartyUtil implementation is an XPCOM service registered
  * under the ContractID:
  */
 #define THIRDPARTYUTIL_CONTRACTID "@mozilla.org/thirdpartyutil;1"
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -37,16 +37,17 @@
 #include "nsIMutableArray.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIObserverService.h"
 #include "nsIProtocolProxyService.h"
 #include "nsProxyRelease.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDocShell.h"
 #include "nsINetworkInterceptController.h"
+#include "mozilla/AntiTrackingCommon.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/PerformanceStorage.h"
 #include "mozilla/NullPrincipal.h"
 #include "mozilla/Services.h"
 #include "mozIThirdPartyUtil.h"
 #include "nsStreamUtils.h"
 #include "nsThreadUtils.h"
 #include "nsContentSecurityManager.h"
@@ -2315,27 +2316,35 @@ HttpBaseChannel::SetTopWindowURIIfUnknow
   }
 
   mTopWindowURI = aTopWindowURI;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::GetTopWindowURI(nsIURI** aTopWindowURI) {
+  nsCOMPtr<nsIURI> uriBeingLoaded =
+      AntiTrackingCommon::MaybeGetDocumentURIBeingLoaded(this);
+  return GetTopWindowURI(uriBeingLoaded, aTopWindowURI);
+}
+
+nsresult HttpBaseChannel::GetTopWindowURI(nsIURI* aURIBeingLoaded,
+                                          nsIURI** aTopWindowURI) {
   nsresult rv = NS_OK;
   nsCOMPtr<mozIThirdPartyUtil> util;
   // Only compute the top window URI once. In e10s, this must be computed in the
   // child. The parent gets the top window URI through HttpChannelOpenArgs.
   if (!mTopWindowURI) {
     util = services::GetThirdPartyUtil();
     if (!util) {
       return NS_ERROR_NOT_AVAILABLE;
     }
     nsCOMPtr<mozIDOMWindowProxy> win;
-    rv = util->GetTopWindowForChannel(this, getter_AddRefs(win));
+    rv = util->GetTopWindowForChannel(this, aURIBeingLoaded,
+                                      getter_AddRefs(win));
     if (NS_SUCCEEDED(rv)) {
       rv = util->GetURIFromWindow(win, getter_AddRefs(mTopWindowURI));
 #if DEBUG
       if (mTopWindowURI) {
         nsCString spec;
         if (NS_SUCCEEDED(mTopWindowURI->GetSpec(spec))) {
           LOG(("HttpChannelBase::Setting topwindow URI spec %s [this=%p]\n",
                spec.get(), this));
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -446,16 +446,18 @@ class HttpBaseChannel : public nsHashPro
   }
 
   MOZ_MUST_USE nsresult SetTopWindowURI(nsIURI *aTopWindowURI) {
     mTopWindowURI = aTopWindowURI;
     return NS_OK;
   }
 
  protected:
+  nsresult GetTopWindowURI(nsIURI *aURIBeingLoaded, nsIURI **aTopWindowURI);
+
   // Handle notifying listener, removing from loadgroup if request failed.
   void DoNotifyListener();
   virtual void DoNotifyListenerCleanup() = 0;
 
   // drop reference to listener, its callbacks, and the progress sink
   virtual void ReleaseListeners();
 
   // Call AsyncAbort().
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -2687,17 +2687,17 @@ nsresult HttpChannelChild::ContinueAsync
                          openArgs.synthesizedSecurityInfoSerialization());
   }
 
   OptionalCorsPreflightArgs optionalCorsPreflightArgs;
   GetClientSetCorsPreflightParameters(optionalCorsPreflightArgs);
 
   // NB: This call forces us to cache mTopWindowURI if we haven't already.
   nsCOMPtr<nsIURI> uri;
-  GetTopWindowURI(getter_AddRefs(uri));
+  GetTopWindowURI(mURI, getter_AddRefs(uri));
 
   SerializeURI(mTopWindowURI, openArgs.topWindowURI());
 
   openArgs.preflightArgs() = optionalCorsPreflightArgs;
 
   openArgs.uploadStreamHasHeaders() = mUploadStreamHasHeaders;
   openArgs.priority() = mPriority;
   openArgs.classOfService() = mClassOfService;
--- a/netwerk/url-classifier/UrlClassifierCommon.cpp
+++ b/netwerk/url-classifier/UrlClassifierCommon.cpp
@@ -1,16 +1,17 @@
 /* -*- 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 "mozilla/net/UrlClassifierCommon.h"
 
+#include "mozilla/AntiTrackingCommon.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/StaticPrefs.h"
 #include "mozIThirdPartyUtil.h"
 #include "nsContentUtils.h"
 #include "nsIChannel.h"
 #include "nsIClassifiedChannel.h"
 #include "mozilla/dom/Document.h"
 #include "nsIDocShell.h"
@@ -54,30 +55,32 @@ LazyLogModule UrlClassifierCommon::sLog(
   NS_QueryNotificationCallbacks(aChannel, parentChannel);
   if (parentChannel) {
     // This channel is a parent-process proxy for a child process request.
     // Tell the child process channel to do this instead.
     parentChannel->NotifyTrackingProtectionDisabled();
     return;
   }
 
-  NotifyChannelBlocked(aChannel,
+  nsCOMPtr<nsIURI> uriBeingLoaded =
+      AntiTrackingCommon::MaybeGetDocumentURIBeingLoaded(aChannel);
+  NotifyChannelBlocked(aChannel, uriBeingLoaded,
                        nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT);
 }
 
 /* static */ void UrlClassifierCommon::NotifyChannelBlocked(
-    nsIChannel* aChannel, unsigned aBlockedReason) {
+    nsIChannel* aChannel, nsIURI* aURIBeingLoaded, unsigned aBlockedReason) {
   nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = services::GetThirdPartyUtil();
   if (NS_WARN_IF(!thirdPartyUtil)) {
     return;
   }
 
   nsCOMPtr<mozIDOMWindowProxy> win;
-  nsresult rv =
-      thirdPartyUtil->GetTopWindowForChannel(aChannel, getter_AddRefs(win));
+  nsresult rv = thirdPartyUtil->GetTopWindowForChannel(
+      aChannel, aURIBeingLoaded, getter_AddRefs(win));
   NS_ENSURE_SUCCESS_VOID(rv);
   auto* pwin = nsPIDOMWindowOuter::From(win);
   nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell();
   if (!docShell) {
     return;
   }
   RefPtr<dom::Document> doc = docShell->GetDocument();
   NS_ENSURE_TRUE_VOID(doc);
@@ -201,35 +204,38 @@ LazyLogModule UrlClassifierCommon::sLog(
     classifiedChannel->SetMatchedInfo(aList, aProvider, aFullHash);
   }
 
   nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = services::GetThirdPartyUtil();
   if (NS_WARN_IF(!thirdPartyUtil)) {
     return NS_OK;
   }
 
+  nsCOMPtr<nsIURI> uriBeingLoaded =
+      AntiTrackingCommon::MaybeGetDocumentURIBeingLoaded(channel);
   nsCOMPtr<mozIDOMWindowProxy> win;
-  rv = thirdPartyUtil->GetTopWindowForChannel(channel, getter_AddRefs(win));
+  rv = thirdPartyUtil->GetTopWindowForChannel(channel, uriBeingLoaded,
+                                              getter_AddRefs(win));
   NS_ENSURE_SUCCESS(rv, NS_OK);
   auto* pwin = nsPIDOMWindowOuter::From(win);
   nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell();
   if (!docShell) {
     return NS_OK;
   }
   RefPtr<dom::Document> doc = docShell->GetDocument();
   NS_ENSURE_TRUE(doc, NS_OK);
 
   unsigned state;
   if (aErrorCode == NS_ERROR_TRACKING_URI) {
     state = nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
   } else {
     state = nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT;
   }
 
-  UrlClassifierCommon::NotifyChannelBlocked(channel, state);
+  UrlClassifierCommon::NotifyChannelBlocked(channel, uriBeingLoaded, state);
 
   // Log a warning to the web console.
   nsCOMPtr<nsIURI> uri;
   channel->GetURI(getter_AddRefs(uri));
   NS_ConvertUTF8toUTF16 spec(uri->GetSpecOrDefault());
   const char16_t* params[] = {spec.get()};
   const char* message = (aErrorCode == NS_ERROR_TRACKING_URI)
                             ? "TrackerUriBlocked"
--- a/netwerk/url-classifier/UrlClassifierCommon.h
+++ b/netwerk/url-classifier/UrlClassifierCommon.h
@@ -24,31 +24,33 @@ class UrlClassifierCommon final {
   static const nsCString::size_type sMaxSpecLength;
 
   static LazyLogModule sLog;
 
   static bool AddonMayLoad(nsIChannel* aChannel, nsIURI* aURI);
 
   static void NotifyTrackingProtectionDisabled(nsIChannel* aChannel);
 
-  // aBlockedReason must be one of the nsIWebProgressListener state.
-  static void NotifyChannelBlocked(nsIChannel* aChannel,
-                                   unsigned aBlockedReason);
-
   static bool ShouldEnableClassifier(
       nsIChannel* aChannel,
       AntiTrackingCommon::ContentBlockingAllowListPurpose aBlockingPurpose);
 
   static nsresult SetBlockedContent(nsIChannel* channel, nsresult aErrorCode,
                                     const nsACString& aList,
                                     const nsACString& aProvider,
                                     const nsACString& aFullHash);
 
   // Use this function only when you are looking for a pairwise whitelist uri
   // with the format: http://toplevel.page/?resource=channel.uri.domain
   static nsresult CreatePairwiseWhiteListURI(nsIChannel* aChannel,
                                              nsIURI** aURI);
+
+ private:
+  // aBlockedReason must be one of the nsIWebProgressListener state.
+  static void NotifyChannelBlocked(nsIChannel* aChannel,
+                                   nsIURI* aURIBeingLoaded,
+                                   unsigned aBlockedReason);
 };
 
 }  // namespace net
 }  // namespace mozilla
 
 #endif  // mozilla_net_UrlClassifierCommon_h
--- a/toolkit/components/antitracking/AntiTrackingCommon.cpp
+++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp
@@ -1464,19 +1464,20 @@ nsresult AntiTrackingCommon::IsOnContent
 
   MOZ_ASSERT(XRE_IsContentProcess());
 
   nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = services::GetThirdPartyUtil();
   if (!thirdPartyUtil) {
     return;
   }
 
+  nsCOMPtr<nsIURI> uriBeingLoaded = MaybeGetDocumentURIBeingLoaded(aChannel);
   nsCOMPtr<mozIDOMWindowProxy> win;
-  nsresult rv =
-      thirdPartyUtil->GetTopWindowForChannel(aChannel, getter_AddRefs(win));
+  nsresult rv = thirdPartyUtil->GetTopWindowForChannel(aChannel, uriBeingLoaded,
+                                                       getter_AddRefs(win));
   NS_ENSURE_SUCCESS_VOID(rv);
 
   nsCOMPtr<nsPIDOMWindowOuter> pwin = nsPIDOMWindowOuter::From(win);
   if (!pwin) {
     return;
   }
 
   nsCOMPtr<nsIURI> uri;
@@ -1598,8 +1599,29 @@ nsresult AntiTrackingCommon::IsOnContent
   nsresult rv = pm->TestPermissionFromPrincipal(aPrincipal,
                                                 USER_INTERACTION_PERM, &result);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false;
   }
 
   return result == nsIPermissionManager::ALLOW_ACTION;
 }
+
+/* static */ already_AddRefed<nsIURI>
+AntiTrackingCommon::MaybeGetDocumentURIBeingLoaded(nsIChannel* aChannel) {
+  nsCOMPtr<nsIURI> uriBeingLoaded;
+  nsLoadFlags loadFlags = 0;
+  nsresult rv = aChannel->GetLoadFlags(&loadFlags);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return nullptr;
+  }
+  if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
+    // If the channel being loaded is a document channel, this call may be
+    // coming from an OnStopRequest notification, which might mean that our
+    // document may still be in the loading process, so we may need to pass in
+    // the uriBeingLoaded argument explicitly.
+    rv = aChannel->GetURI(getter_AddRefs(uriBeingLoaded));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return nullptr;
+    }
+  }
+  return uriBeingLoaded.forget();
+}
--- a/toolkit/components/antitracking/AntiTrackingCommon.h
+++ b/toolkit/components/antitracking/AntiTrackingCommon.h
@@ -148,13 +148,17 @@ class AntiTrackingCommon final {
   //  * nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN
   static void NotifyBlockingDecision(nsIChannel* aChannel,
                                      BlockingDecision aDecision,
                                      uint32_t aRejectedReason);
 
   static void NotifyBlockingDecision(nsPIDOMWindowInner* aWindow,
                                      BlockingDecision aDecision,
                                      uint32_t aRejectedReason);
+
+  // Get the current document URI from a document channel as it is being loaded.
+  static already_AddRefed<nsIURI> MaybeGetDocumentURIBeingLoaded(
+      nsIChannel* aChannel);
 };
 
 }  // namespace mozilla
 
 #endif  // mozilla_antitrackingservice_h
--- a/toolkit/components/antitracking/test/browser/browser.ini
+++ b/toolkit/components/antitracking/test/browser/browser.ini
@@ -1,15 +1,16 @@
 [DEFAULT]
 prefs =
   # Disable the Storage Access API prompts for all of the tests in this directory
   dom.storage_access.prompt.testing=true
   dom.storage_access.prompt.testing.allow=true
 
 support-files =
+  container.html
   embedder.html
   head.js
   image.sjs
   imageCacheWorker.js
   page.html
   3rdParty.html
   3rdPartySVG.html
   3rdPartyUI.html
@@ -17,16 +18,17 @@ support-files =
   3rdPartyOpen.html
   3rdPartyOpenUI.html
   empty.js
   popup.html
   server.sjs
   storageAccessAPIHelpers.js
   !/browser/modules/test/browser/head.js
 
+[browser_addonHostPermissionIgnoredInTP.js]
 [browser_allowListSeparationInPrivateAndNormalWindows.js]
 skip-if = os == "mac" && !debug # Bug 1503778
 [browser_backgroundImageAssertion.js]
 [browser_blockingCookies.js]
 [browser_blockingDOMCache.js]
 skip-if = (os == "win" && os_version == "6.1" && bits == 32 && !debug) # Bug 1491937
 [browser_blockingIndexedDb.js]
 [browser_blockingIndexedDbInWorkers.js]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/test/browser/browser_addonHostPermissionIgnoredInTP.js
@@ -0,0 +1,46 @@
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+add_task(async function() {
+  info("Starting test");
+
+  await SpecialPowers.flushPrefEnv();
+  await SpecialPowers.pushPrefEnv({"set": [
+    ["privacy.trackingprotection.enabled", true],
+    // prevent the content blocking on-boarding UI to start mid-way through the test!
+    [ContentBlocking.prefIntroCount, ContentBlocking.MAX_INTROS],
+  ]});
+
+  await UrlClassifierTestUtils.addTestTrackers();
+
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {permissions: ["https://tracking.example.com/"]},
+    files: {
+      "page.html": '<html><head></head><body><iframe src="https://tracking.example.com/browser/toolkit/components/antitracking/test/browser/container.html"></iframe></body></html>',
+    },
+    async background() {
+      browser.test.sendMessage("ready", browser.runtime.getURL("page.html"));
+    },
+  });
+  await extension.startup();
+  let url = await extension.awaitMessage("ready");
+
+  info("Creating a new tab");
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+  let browser = tab.linkedBrowser;
+
+  info("Verify the number of script nodes found");
+  await ContentTask.spawn(browser,
+                          {},
+                          async function(obj) {
+    let doc = content.document.querySelector("iframe").contentDocument;
+    doc = doc.querySelector("iframe").contentDocument;
+    let scripts = doc.querySelectorAll("script");
+    is(scripts.length, 3, "Expected script nodes found");
+  });
+
+  info("Removing the tab");
+  BrowserTestUtils.removeTab(tab);
+
+  UrlClassifierTestUtils.cleanupTestTrackers();
+  await extension.unload();
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/test/browser/container.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+<iframe src="embedder.html"></iframe>
+</body>
+</html>
--- a/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp
@@ -591,17 +591,18 @@ static nsresult AddTabThreatSources(Thre
   }
 
   nsresult rv;
   nsCOMPtr<mozIDOMWindowProxy> win;
   nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
       do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = thirdPartyUtil->GetTopWindowForChannel(aChannel, getter_AddRefs(win));
+  rv = thirdPartyUtil->GetTopWindowForChannel(aChannel, nullptr,
+                                              getter_AddRefs(win));
   NS_ENSURE_SUCCESS(rv, rv);
 
   auto* pwin = nsPIDOMWindowOuter::From(win);
   nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell();
   if (!docShell) {
     return NS_OK;
   }