Bug 1485532 - Part 2: Make sure that tracking requests that webRequest.onBeforeRequest handlers do not handle will still get blocked by tracking protection; r=mayhemer
authorEhsan Akhgari <ehsan@mozilla.com>
Thu, 23 Aug 2018 14:56:41 -0400
changeset 481613 0f84be4d22bf2d1808548f01308820f343dd4a6e
parent 481612 c1ac05aafbd6f703897c26fc7c876ef102fc3dc0
child 481614 ccaeab2823a283ed8c1a887ab947fd15ac02f707
push id232
push userfmarier@mozilla.com
push dateWed, 05 Sep 2018 20:45:54 +0000
reviewersmayhemer
bugs1485532
milestone63.0a1
Bug 1485532 - Part 2: Make sure that tracking requests that webRequest.onBeforeRequest handlers do not handle will still get blocked by tracking protection; r=mayhemer
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpChannel.h
toolkit/components/antitracking/test/browser/browser_onBeforeRequestNotificationForTrackingResources.js
toolkit/components/antitracking/test/browser/browser_onModifyRequestNotificationForTrackingResources.js
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -331,16 +331,17 @@ nsHttpChannel::nsHttpChannel()
     , mConcurrentCacheAccess(0)
     , mIsPartialRequest(0)
     , mHasAutoRedirectVetoNotifier(0)
     , mPinCacheContent(0)
     , mIsCorsPreflightDone(0)
     , mStronglyFramed(false)
     , mUsedNetwork(0)
     , mAuthConnectionRestartable(0)
+    , mTrackingProtectionCancellationPending(0)
     , mPushedStream(nullptr)
     , mLocalBlocklist(false)
     , mOnTailUnblock(nullptr)
     , mWarningReporter(nullptr)
     , mIsReadingFromCache(false)
     , mFirstResponseSource(RESPONSE_PENDING)
     , mRaceCacheWithNetwork(false)
     , mRaceDelay(0)
@@ -6053,23 +6054,25 @@ nsHttpChannel::CancelForTrackingProtecti
     // Check if request was cancelled during on-modify-request or on-useragent.
     if (mCanceled) {
         return mStatus;
     }
 
     if (mSuspendCount) {
         LOG(("Waiting until resume in Cancel [this=%p]\n", this));
         MOZ_ASSERT(!mCallOnResume);
+        mTrackingProtectionCancellationPending = 1;
         mCallOnResume = &nsHttpChannel::HandleContinueCancelledByTrackingProtection;
         return NS_OK;
     }
 
     // Check to see if we should redirect this channel elsewhere by
     // nsIHttpChannel.redirectTo API request
     if (mAPIRedirectToURI) {
+        mTrackingProtectionCancellationPending = 1;
         return AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect);
     }
 
     return CancelInternal(NS_ERROR_TRACKING_URI);
 }
 
 void
 nsHttpChannel::ContinueCancelledByTrackingProtection()
@@ -6093,16 +6096,22 @@ nsHttpChannel::ContinueCancelledByTracki
     }
 
     Unused << CancelInternal(NS_ERROR_TRACKING_URI);
 }
 
 nsresult
 nsHttpChannel::CancelInternal(nsresult status)
 {
+    bool trackingProtectionCancellationPending =
+      !!mTrackingProtectionCancellationPending;
+    if (status == NS_ERROR_TRACKING_URI) {
+      mTrackingProtectionCancellationPending = 0;
+    }
+
     mCanceled = true;
     mStatus = status;
     if (mProxyRequest)
         mProxyRequest->Cancel(status);
     CancelNetworkRequest(status);
     mCacheInputStream.CloseAndRelease();
     if (mCachePump)
         mCachePump->Cancel(status);
@@ -6110,16 +6119,20 @@ nsHttpChannel::CancelInternal(nsresult s
         mAuthProvider->Cancel(status);
     if (mPreflightChannel)
         mPreflightChannel->Cancel(status);
     if (mRequestContext && mOnTailUnblock) {
         mOnTailUnblock = nullptr;
         mRequestContext->CancelTailedRequest(this);
         CloseCacheEntry(false);
         Unused << AsyncAbort(status);
+    } else if (trackingProtectionCancellationPending) {
+        // If we're coming from an asynchronous path when canceling a channel due
+        // to tracking protection, we need to AsyncAbort the channel now.
+        Unused << AsyncAbort(status);
     }
     return NS_OK;
 }
 
 void
 nsHttpChannel::CancelNetworkRequest(nsresult aStatus)
 {
     if (mTransaction) {
@@ -6571,16 +6584,24 @@ nsHttpChannel::BeginConnect()
 
 nsresult
 nsHttpChannel::BeginConnectActual()
 {
     if (mCanceled) {
         return mStatus;
     }
 
+    if (mTrackingProtectionCancellationPending) {
+        LOG(("Waiting for tracking protection cancellation in BeginConnectActual [this=%p]\n", this));
+        MOZ_ASSERT(!mCallOnResume ||
+                   mCallOnResume == &nsHttpChannel::HandleContinueCancelledByTrackingProtection,
+                   "We should be paused waiting for cancellation from tracking protection");
+        return NS_OK;
+    }
+
     if (!mConnectionInfo->UsingHttpProxy() &&
         !(mLoadFlags & (LOAD_NO_NETWORK_IO | LOAD_ONLY_FROM_CACHE))) {
         // Start a DNS lookup very early in case the real open is queued the DNS can
         // happen in parallel. Do not do so in the presence of an HTTP proxy as
         // all lookups other than for the proxy itself are done by the proxy.
         // Also we don't do a lookup if the LOAD_NO_NETWORK_IO or
         // LOAD_ONLY_FROM_CACHE flags are set.
         //
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -623,16 +623,21 @@ private:
     uint32_t                          mStronglyFramed : 1;
 
     // true if an HTTP transaction is created for the socket thread
     uint32_t                          mUsedNetwork : 1;
 
     // the next authentication request can be sent on a whole new connection
     uint32_t                          mAuthConnectionRestartable : 1;
 
+    // True if the channel classifier has marked the channel to be cancelled
+    // due to the tracking protection rules, but the asynchronous cancellation
+    // process hasn't finished yet.
+    uint32_t                          mTrackingProtectionCancellationPending : 1;
+
     nsTArray<nsContinueRedirectionFunc> mRedirectFuncStack;
 
     // Needed for accurate DNS timing
     RefPtr<nsDNSPrefetch>           mDNSPrefetch;
 
     Http2PushedStream                 *mPushedStream;
     // True if the channel's principal was found on a phishing, malware, or
     // tracking (if tracking protection is enabled) blocklist
--- a/toolkit/components/antitracking/test/browser/browser_onBeforeRequestNotificationForTrackingResources.js
+++ b/toolkit/components/antitracking/test/browser/browser_onBeforeRequestNotificationForTrackingResources.js
@@ -71,14 +71,22 @@ add_task(async function() {
   let tab = BrowserTestUtils.addTab(gBrowser, TEST_EMBEDDER_PAGE);
   gBrowser.selectedTab = tab;
 
   let browser = gBrowser.getBrowserForTab(tab);
   await BrowserTestUtils.browserLoaded(browser);
 
   await promise;
 
+  info("Verify the number of tracking nodes found");
+  await ContentTask.spawn(browser,
+                          { expected: 3,
+                          },
+                          async function(obj) {
+    is(content.document.blockedTrackingNodeCount, obj.expected, "Expected tracking nodes found");
+  });
+
   info("Removing the tab");
   BrowserTestUtils.removeTab(tab);
 
   UrlClassifierTestUtils.cleanupTestTrackers();
   await extension.unload();
 });
--- a/toolkit/components/antitracking/test/browser/browser_onModifyRequestNotificationForTrackingResources.js
+++ b/toolkit/components/antitracking/test/browser/browser_onModifyRequestNotificationForTrackingResources.js
@@ -64,13 +64,21 @@ add_task(async function() {
   let tab = BrowserTestUtils.addTab(gBrowser, TEST_EMBEDDER_PAGE);
   gBrowser.selectedTab = tab;
 
   let browser = gBrowser.getBrowserForTab(tab);
   await BrowserTestUtils.browserLoaded(browser);
 
   await promise;
 
+  info("Verify the number of tracking nodes found");
+  await ContentTask.spawn(browser,
+                          { expected: gExpectedResourcesSeen,
+                          },
+                          async function(obj) {
+    is(content.document.blockedTrackingNodeCount, obj.expected, "Expected tracking nodes found");
+  });
+
   info("Removing the tab");
   BrowserTestUtils.removeTab(tab);
 
   UrlClassifierTestUtils.cleanupTestTrackers();
 });