Bug 1451307 - Part 1 - Consider iframes when comparing loading URIs in nsChannelClassifier::SetBlockedContent. r=mayhemer,francois draft
authorJohann Hofmann <jhofmann@mozilla.com>
Thu, 19 Apr 2018 17:21:58 +0200
changeset 813027 51b05505a9b85d7841851dd44c925da95029c958
parent 812962 3cfc350101967376909ad3c729f9779ae0ab7a94
child 813028 ea0e77f0873e5f8f655d50c5cbec00d114219f7a
push id114745
push userjhofmann@mozilla.com
push dateMon, 02 Jul 2018 09:21:29 +0000
reviewersmayhemer, francois
bugs1451307
milestone63.0a1
Bug 1451307 - Part 1 - Consider iframes when comparing loading URIs in nsChannelClassifier::SetBlockedContent. r=mayhemer,francois MozReview-Commit-ID: 17edGCCTEmo
netwerk/base/nsChannelClassifier.cpp
netwerk/base/nsChannelClassifier.h
--- a/netwerk/base/nsChannelClassifier.cpp
+++ b/netwerk/base/nsChannelClassifier.cpp
@@ -797,16 +797,35 @@ nsChannelClassifier::SameLoadingURI(nsID
     return false;
   }
   bool equals = false;
   nsresult rv = docURI->EqualsExceptRef(channelLoadingURI, &equals);
   return NS_SUCCEEDED(rv) && equals;
 }
 
 // static
+nsPIDOMWindowOuter*
+nsChannelClassifier::GetWindowForChannel(nsIChannel *aChannel)
+{
+  nsCOMPtr<nsILoadContext> ctx;
+  NS_QueryNotificationCallbacks(aChannel, ctx);
+  if (!ctx) {
+    return nullptr;
+  }
+
+  nsCOMPtr<mozIDOMWindowProxy> window;
+  ctx->GetAssociatedWindow(getter_AddRefs(window));
+  if (!window) {
+    return nullptr;
+  }
+
+  return nsPIDOMWindowOuter::From(window);
+}
+
+// static
 nsresult
 nsChannelClassifier::SetBlockedContent(nsIChannel *channel,
                                        nsresult aErrorCode,
                                        const nsACString& aList,
                                        const nsACString& aProvider,
                                        const nsACString& aFullHash)
 {
   NS_ENSURE_ARG(!aList.IsEmpty());
@@ -824,51 +843,69 @@ nsChannelClassifier::SetBlockedContent(n
   nsresult rv;
   nsCOMPtr<nsIClassifiedChannel> classifiedChannel = do_QueryInterface(channel, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (classifiedChannel) {
     classifiedChannel->SetMatchedInfo(aList, aProvider, aFullHash);
   }
 
-  nsCOMPtr<mozIDOMWindowProxy> win;
-  nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
-    do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, NS_OK);
-  rv = thirdPartyUtil->GetTopWindowForChannel(channel, getter_AddRefs(win));
-  NS_ENSURE_SUCCESS(rv, NS_OK);
-  auto* pwin = nsPIDOMWindowOuter::From(win);
-  nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell();
-  if (!docShell) {
-    return NS_OK;
-  }
-  nsCOMPtr<nsIDocument> doc = docShell->GetDocument();
-  NS_ENSURE_TRUE(doc, NS_OK);
-
   // This event might come after the user has navigated to another page.
   // To prevent showing the TrackingProtection UI on the wrong page, we need to
   // check that the loading URI for the channel is the same as the URI currently
-  // loaded in the document.
-  if (!SameLoadingURI(doc, channel)) {
+  // loaded in the document that is associated with the channel (which may be
+  // different than the top-level document, so we avoid ignoring all iframes).
+  nsCOMPtr<nsPIDOMWindowOuter> win = GetWindowForChannel(channel);
+  if (!win) {
+    return NS_OK;
+  }
+  nsCOMPtr<nsIDocument> frameDoc = win->GetExtantDoc();
+
+  // If the blocked request is the document load request to an iframe that
+  // contains a tracking page, the window associated with the channel belongs
+  // to that iframe, not its loading parent. In this case we actually want to
+  // compare load URIs with the parent frame, so we step up one level.
+  bool isDocumentLoad = false;
+  Unused << NS_WARN_IF(NS_FAILED(channel->GetIsDocument(&isDocumentLoad)));
+  if (isDocumentLoad || !frameDoc) {
+    win = win->GetParent();
+    if (NS_WARN_IF(!win)) {
+      return NS_OK;
+    }
+    frameDoc = win->GetExtantDoc();
+  }
+
+  if (!frameDoc || !SameLoadingURI(frameDoc, channel)) {
+    return NS_OK;
+  }
+
+  // Get the root docshell for updating the security UI.
+  nsCOMPtr<nsPIDOMWindowOuter> topWin = win->GetScriptableTop();
+  nsCOMPtr<nsIDocShell> topDocShell = topWin->GetDocShell();
+  if (!topDocShell) {
     return NS_OK;
   }
 
   // Notify nsIWebProgressListeners of this security event.
   // Can be used to change the UI state.
-  nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell, &rv);
+  nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(topDocShell, &rv);
   NS_ENSURE_SUCCESS(rv, NS_OK);
   uint32_t state = 0;
   nsCOMPtr<nsISecureBrowserUI> securityUI;
-  docShell->GetSecurityUI(getter_AddRefs(securityUI));
+  topDocShell->GetSecurityUI(getter_AddRefs(securityUI));
   if (!securityUI) {
     return NS_OK;
   }
   securityUI->GetState(&state);
   if (aErrorCode == NS_ERROR_TRACKING_URI) {
-    doc->SetHasTrackingContentBlocked(true);
+    nsCOMPtr<nsIDocument> topLevelDoc = topDocShell->GetDocument();
+    if (!topLevelDoc) {
+      return NS_OK;
+    }
+    topLevelDoc->SetHasTrackingContentBlocked(true);
     state |= nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
   } else {
     state |= nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT;
   }
 
   eventSink->OnSecurityChange(channel, state);
 
   // Log a warning to the web console.
@@ -879,17 +916,17 @@ nsChannelClassifier::SetBlockedContent(n
   const char* message = (aErrorCode == NS_ERROR_TRACKING_URI) ?
     "TrackingUriBlocked" : "UnsafeUriBlocked";
   nsCString category = (aErrorCode == NS_ERROR_TRACKING_URI) ?
     NS_LITERAL_CSTRING("Tracking Protection") :
     NS_LITERAL_CSTRING("Safe Browsing");
 
   nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
                                   category,
-                                  doc,
+                                  frameDoc,
                                   nsContentUtils::eNECKO_PROPERTIES,
                                   message,
                                   params, ArrayLength(params));
 
   return NS_OK;
 }
 
 namespace {
--- a/netwerk/base/nsChannelClassifier.h
+++ b/netwerk/base/nsChannelClassifier.h
@@ -75,16 +75,18 @@ private:
     // Helper function so that we ensure we call ContinueBeginConnect once
     // Start is called. Returns NS_OK if and only if we will get a callback
     // from the classifier service.
     nsresult StartInternal();
     // Helper function to check a URI against the hostname whitelist
     bool IsHostnameWhitelisted(nsIURI *aUri, const nsACString &aWhitelisted);
     // Checks that the channel was loaded by the URI currently loaded in aDoc
     static bool SameLoadingURI(nsIDocument *aDoc, nsIChannel *aChannel);
+    // Helper function to get the (not necessarily top-level) window from a channel.
+    static nsPIDOMWindowOuter* GetWindowForChannel(nsIChannel *aChannel);
     // Note this function will be also used to decide whether or not to enable
     // channel annotation. When |aAnnotationsOnly| is true, this function
     // is called by ShouldEnableTrackingAnnotation(). Otherwise, this is called
     // by ShouldEnableTrackingProtection().
     nsresult ShouldEnableTrackingProtectionInternal(nsIChannel *aChannel,
                                                     bool aAnnotationsOnly,
                                                     bool *result);