Bug 1485182 - Part 1: Run http-on-modify-request observers when tracking protection cancels an HTTP channel; r=mayhemer
authorEhsan Akhgari <ehsan@mozilla.com>
Tue, 21 Aug 2018 17:59:49 -0400
changeset 481187 48213c5c25e9aa9389375e4e71c1a58567d37ca2
parent 481186 3b7a5715e699075b00ec90ad524b40a52202e059
child 481188 a5f51c76930c49160bf5e909301d8e7f1a83e379
push id232
push userfmarier@mozilla.com
push dateWed, 05 Sep 2018 20:45:54 +0000
reviewersmayhemer
bugs1485182
milestone63.0a1
Bug 1485182 - Part 1: Run http-on-modify-request observers when tracking protection cancels an HTTP channel; r=mayhemer
netwerk/base/nsChannelClassifier.cpp
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpChannel.h
netwerk/protocol/http/nsIHttpChannelInternal.idl
--- a/netwerk/base/nsChannelClassifier.cpp
+++ b/netwerk/base/nsChannelClassifier.cpp
@@ -1182,17 +1182,22 @@ TrackingURICallback::OnTrackerFound(nsre
   nsCOMPtr<nsIChannel> channel = mChannelClassifier->GetChannel();
   MOZ_ASSERT(channel);
   if (aErrorCode == NS_ERROR_TRACKING_URI &&
       mChannelClassifier->ShouldEnableTrackingProtection()) {
     mChannelClassifier->SetBlockedContent(channel, aErrorCode,
                                           mList, mProvider, mFullHash);
     LOG(("TrackingURICallback[%p]::OnTrackerFound, cancelling channel[%p]",
          mChannelClassifier.get(), channel.get()));
-    channel->Cancel(aErrorCode);
+    nsCOMPtr<nsIHttpChannelInternal> httpChannel = do_QueryInterface(channel);
+    if (httpChannel) {
+      Unused << httpChannel->CancelForTrackingProtection();
+    } else {
+      Unused << channel->Cancel(aErrorCode);
+    }
   } else {
     MOZ_ASSERT(aErrorCode == NS_ERROR_TRACKING_ANNOTATION_URI);
     MOZ_ASSERT(mChannelClassifier->ShouldEnableTrackingAnnotation());
 
     bool isThirdPartyWithTopLevelWinURI = false;
     nsresult rv = IsThirdParty(channel, &isThirdPartyWithTopLevelWinURI);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       LOG(("TrackingURICallback[%p]::OnTrackerFound IsThirdParty() failed",
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -4719,10 +4719,16 @@ HttpBaseChannel::GetNativeServerTiming(n
   if (NS_SUCCEEDED(mURI->SchemeIs("https", &isHTTPS)) && isHTTPS) {
     ParseServerTimingHeader(mResponseHead, aServerTiming);
     ParseServerTimingHeader(mResponseTrailers, aServerTiming);
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+HttpBaseChannel::CancelForTrackingProtection()
+{
+  return Cancel(NS_ERROR_TRACKING_URI);
+}
+
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -266,16 +266,17 @@ public:
   virtual void SetAltDataForChild(bool aIsForChild) override;
   NS_IMETHOD GetConnectionInfoHashKey(nsACString& aConnectionInfoHashKey) override;
   NS_IMETHOD GetIntegrityMetadata(nsAString& aIntegrityMetadata) override;
   NS_IMETHOD SetIntegrityMetadata(const nsAString& aIntegrityMetadata) override;
   NS_IMETHOD GetLastRedirectFlags(uint32_t *aValue) override;
   NS_IMETHOD SetLastRedirectFlags(uint32_t aValue) override;
   NS_IMETHOD GetNavigationStartTimeStamp(TimeStamp* aTimeStamp) override;
   NS_IMETHOD SetNavigationStartTimeStamp(TimeStamp aTimeStamp) override;
+  NS_IMETHOD CancelForTrackingProtection() override;
 
   inline void CleanRedirectCacheChainIfNecessary()
   {
       mRedirectedCachekeys = nullptr;
   }
   NS_IMETHOD HTTPUpgrade(const nsACString & aProtocolName,
                          nsIHttpUpgradeListener *aListener) override;
 
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -462,16 +462,31 @@ nsHttpChannel::PrepareToConnect()
         mCallOnResume = &nsHttpChannel::HandleOnBeforeConnect;
         return NS_OK;
     }
 
     return OnBeforeConnect();
 }
 
 void
+nsHttpChannel::HandleContinueCancelledByTrackingProtection()
+{
+    MOZ_ASSERT(!mCallOnResume, "How did that happen?");
+
+    if (mSuspendCount) {
+        LOG(("Waiting until resume HandleContinueCancelledByTrackingProtection [this=%p]\n", this));
+        mCallOnResume = &nsHttpChannel::HandleContinueCancelledByTrackingProtection;
+        return;
+    }
+
+    LOG(("nsHttpChannel::HandleContinueCancelledByTrackingProtection [this=%p]\n", this));
+    ContinueCancelledByTrackingProtection();
+}
+
+void
 nsHttpChannel::HandleOnBeforeConnect()
 {
     MOZ_ASSERT(!mCallOnResume, "How did that happen?");
     nsresult rv;
 
     if (mSuspendCount) {
         LOG(("Waiting until resume OnBeforeConnect [this=%p]\n", this));
         mCallOnResume = &nsHttpChannel::HandleOnBeforeConnect;
@@ -5982,27 +5997,98 @@ NS_INTERFACE_MAP_END_INHERITING(HttpBase
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsHttpChannel::Cancel(nsresult status)
 {
     MOZ_ASSERT(NS_IsMainThread());
     // We should never have a pump open while a CORS preflight is in progress.
     MOZ_ASSERT_IF(mPreflightChannel, !mCachePump);
+    MOZ_ASSERT(status != NS_ERROR_TRACKING_URI,
+               "NS_ERROR_TRACKING_URI needs to be handled by CancelForTrackingProtection()");
 
     LOG(("nsHttpChannel::Cancel [this=%p status=%" PRIx32 "]\n",
          this, static_cast<uint32_t>(status)));
     if (mCanceled) {
         LOG(("  ignoring; already canceled\n"));
         return NS_OK;
     }
 
     if (mWaitingForRedirectCallback) {
         LOG(("channel canceled during wait for redirect callback"));
     }
+
+    return CancelInternal(status);
+}
+
+NS_IMETHODIMP
+nsHttpChannel::CancelForTrackingProtection()
+{
+    MOZ_ASSERT(NS_IsMainThread());
+    // We should never have a pump open while a CORS preflight is in progress.
+    MOZ_ASSERT_IF(mPreflightChannel, !mCachePump);
+
+    LOG(("nsHttpChannel::CancelForTrackingProtection [this=%p]\n", this));
+
+    if (mCanceled) {
+        LOG(("  ignoring; already canceled\n"));
+        return NS_OK;
+    }
+
+    // We are being canceled by the channel classifier because of tracking
+    // protection, but we haven't yet had a chance to dispatch the
+    // "http-on-modify-request" notifications yet (this would normally be
+    // done in PrepareToConnect()).  So do that now, before proceeding to
+    // cancel.
+    //
+    // Note that running these observers can itself result in the channel
+    // being canceled.  In that case, we accept that cancelation code as
+    // the cause of the cancelation, as if the classification of the channel
+    // would have occurred past this point!
+
+    // notify "http-on-modify-request" observers
+    CallOnModifyRequestObservers();
+
+    SetLoadGroupUserAgentOverride();
+
+    // 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);
+        mCallOnResume = &nsHttpChannel::HandleContinueCancelledByTrackingProtection;
+        return NS_OK;
+    }
+
+    return CancelInternal(NS_ERROR_TRACKING_URI);
+}
+
+void
+nsHttpChannel::ContinueCancelledByTrackingProtection()
+{
+    MOZ_ASSERT(NS_IsMainThread());
+    // We should never have a pump open while a CORS preflight is in progress.
+    MOZ_ASSERT_IF(mPreflightChannel, !mCachePump);
+
+    LOG(("nsHttpChannel::ContinueCancelledByTrackingProtection [this=%p]\n",
+         this));
+    if (mCanceled) {
+        LOG(("  ignoring; already canceled\n"));
+        return;
+    }
+
+    Unused << CancelInternal(NS_ERROR_TRACKING_URI);
+}
+
+nsresult
+nsHttpChannel::CancelInternal(nsresult status)
+{
     mCanceled = true;
     mStatus = status;
     if (mProxyRequest)
         mProxyRequest->Cancel(status);
     CancelNetworkRequest(status);
     mCacheInputStream.CloseAndRelease();
     if (mCachePump)
         mCachePump->Cancel(status);
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -158,16 +158,17 @@ public:
     NS_IMETHOD AsyncOpen2(nsIStreamListener *aListener) override;
     // nsIHttpChannel
     NS_IMETHOD GetEncodedBodySize(uint64_t *aEncodedBodySize) override;
     // nsIHttpChannelInternal
     NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey) override;
     NS_IMETHOD SetChannelIsForDownload(bool aChannelIsForDownload) override;
     NS_IMETHOD GetNavigationStartTimeStamp(TimeStamp* aTimeStamp) override;
     NS_IMETHOD SetNavigationStartTimeStamp(TimeStamp aTimeStamp) override;
+    NS_IMETHOD CancelForTrackingProtection() override;
     // nsISupportsPriority
     NS_IMETHOD SetPriority(int32_t value) override;
     // nsIClassOfService
     NS_IMETHOD SetClassFlags(uint32_t inFlags) override;
     NS_IMETHOD AddClassFlags(uint32_t inFlags) override;
     NS_IMETHOD ClearClassFlags(uint32_t inFlags) override;
 
     // nsIResumableChannel
@@ -279,16 +280,19 @@ public:
 
 protected:
     virtual ~nsHttpChannel();
 
 private:
     typedef nsresult (nsHttpChannel::*nsContinueRedirectionFunc)(nsresult result);
 
     bool     RequestIsConditional();
+    void HandleContinueCancelledByTrackingProtection();
+    nsresult CancelInternal(nsresult status);
+    void ContinueCancelledByTrackingProtection();
 
     // Connections will only be established in this function.
     // (including DNS prefetch and speculative connection.)
     nsresult BeginConnectActual();
 
     // We might synchronously or asynchronously call BeginConnectActual,
     // which includes DNS prefetch and speculative connection, according to
     // whether an async tracker lookup is required. If the tracker lookup
--- a/netwerk/protocol/http/nsIHttpChannelInternal.idl
+++ b/netwerk/protocol/http/nsIHttpChannelInternal.idl
@@ -328,9 +328,17 @@ interface nsIHttpChannelInternal : nsISu
      * SetupReplacementChannel() method.
      */
     [noscript, infallible]
     attribute unsigned long lastRedirectFlags;
 
       // This is use to determine the duration since navigation started.
     [noscript] attribute TimeStamp navigationStartTimeStamp;
 
+    /**
+     * Cancel a channel because we have determined that it needs to be blocked
+     * for tracking protection.  This is an internal API that is meant to be
+     * called by the channel classifier.  Please DO NOT use this API if you don't
+     * know whether you should be using it.
+     */
+    [noscript] void cancelForTrackingProtection();
+
 };