Bug 1495285 - Introduce TrackingDummyChannel to annotate channels before being intercepted by ServiceWorkers, r=francois, r=mayhemer, f=asuth
authorAndrea Marchesini <amarchesini@mozilla.com>
Fri, 12 Oct 2018 11:40:36 +0200
changeset 440865 75ed7377db616cdeb922a9ebf28e17aa7f97b492
parent 440864 f6d88f69e8499bc13b794ac2fd0eeabd7e44c182
child 440866 2c6af6cc1ae8bd7d8227f377fb0d8e255d41b918
push id34838
push userncsoregi@mozilla.com
push dateFri, 12 Oct 2018 16:57:48 +0000
treeherdermozilla-central@ede21c2f2f99 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfrancois, mayhemer
bugs1495285
milestone64.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 1495285 - Introduce TrackingDummyChannel to annotate channels before being intercepted by ServiceWorkers, r=francois, r=mayhemer, f=asuth
dom/serviceworkers/test/browser_antitracking.js
netwerk/base/nsChannelClassifier.cpp
netwerk/ipc/NeckoChild.cpp
netwerk/ipc/NeckoChild.h
netwerk/ipc/NeckoParent.cpp
netwerk/ipc/NeckoParent.h
netwerk/ipc/PNecko.ipdl
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/PTrackingDummyChannel.ipdl
netwerk/protocol/http/TrackingDummyChannel.cpp
netwerk/protocol/http/TrackingDummyChannel.h
netwerk/protocol/http/TrackingDummyChannelChild.cpp
netwerk/protocol/http/TrackingDummyChannelChild.h
netwerk/protocol/http/TrackingDummyChannelParent.cpp
netwerk/protocol/http/TrackingDummyChannelParent.h
netwerk/protocol/http/moz.build
netwerk/protocol/http/nsHttpChannel.cpp
--- a/dom/serviceworkers/test/browser_antitracking.js
+++ b/dom/serviceworkers/test/browser_antitracking.js
@@ -73,18 +73,17 @@ add_task(async function() {
     { url: SW_IFRAME_PAGE },
     async function ({ url }) {
       const payload =
         await content.wrappedJSObject.createIframeAndWaitForMessage(url);
       return payload;
     }
   );
 
-  // Currently TP will allow this, although it probably shouldn't?
-  ok(controlled, "Should be controlled (and not crash.)");
+  ok(!controlled, "Should not be controlled!");
 
   // ## Cleanup
   // Close the testing tab.
   BrowserTestUtils.removeTab(topTab);
   // Unregister the SW we registered for the tracking protection origin.
   await BrowserTestUtils.withNewTab(
     {
       gBrowser,
--- a/netwerk/base/nsChannelClassifier.cpp
+++ b/netwerk/base/nsChannelClassifier.cpp
@@ -37,16 +37,17 @@
 #include "nsIUrlClassifierDBService.h"
 #include "nsIURLFormatter.h"
 
 #include "mozilla/AntiTrackingCommon.h"
 #include "mozilla/ErrorNames.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/net/HttpBaseChannel.h"
+#include "mozilla/net/TrackingDummyChannel.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPrefs.h"
 #include "mozilla/Unused.h"
 
 namespace mozilla {
 namespace net {
 
@@ -324,16 +325,21 @@ SetIsTrackingResourceHelper(nsIChannel* 
     // request. We should notify the child process as well.
     parentChannel->NotifyTrackingResource(aIsThirdParty);
   }
 
   RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(aChannel);
   if (httpChannel) {
     httpChannel->SetIsTrackingResource(aIsThirdParty);
   }
+
+  RefPtr<TrackingDummyChannel> dummyChannel = do_QueryObject(aChannel);
+  if (dummyChannel) {
+    dummyChannel->SetIsTrackingResource();
+  }
 }
 
 static void
 LowerPriorityHelper(nsIChannel* aChannel)
 {
   MOZ_ASSERT(aChannel);
 
   bool isBlockingResource = false;
--- a/netwerk/ipc/NeckoChild.cpp
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -18,16 +18,17 @@
 #include "mozilla/net/WebSocketEventListenerChild.h"
 #include "mozilla/net/DNSRequestChild.h"
 #include "mozilla/net/ChannelDiverterChild.h"
 #include "mozilla/net/IPCTransportProvider.h"
 #include "mozilla/dom/network/TCPSocketChild.h"
 #include "mozilla/dom/network/TCPServerSocketChild.h"
 #include "mozilla/dom/network/UDPSocketChild.h"
 #include "mozilla/net/AltDataOutputStreamChild.h"
+#include "mozilla/net/TrackingDummyChannelChild.h"
 #ifdef MOZ_WEBRTC
 #include "mozilla/net/StunAddrsRequestChild.h"
 #endif
 
 #include "SerializedLoadContext.h"
 #include "nsGlobalWindow.h"
 #include "nsIOService.h"
 #include "nsINetworkPredictor.h"
@@ -529,10 +530,24 @@ NeckoChild::RecvNetworkChangeNotificatio
   nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
   if (obsService) {
     obsService->NotifyObservers(nullptr, NS_NETWORK_LINK_TOPIC,
                                 NS_ConvertUTF8toUTF16(type).get());
   }
   return IPC_OK();
 }
 
+PTrackingDummyChannelChild*
+NeckoChild::AllocPTrackingDummyChannelChild(nsIURI* aURI,
+                                            const OptionalLoadInfoArgs& aLoadInfo)
+{
+  return new TrackingDummyChannelChild();
+}
+
+bool
+NeckoChild::DeallocPTrackingDummyChannelChild(PTrackingDummyChannelChild* aActor)
+{
+  delete static_cast<TrackingDummyChannelChild*>(aActor);
+  return true;
+}
+
 } // namespace net
 } // namespace mozilla
--- a/netwerk/ipc/NeckoChild.h
+++ b/netwerk/ipc/NeckoChild.h
@@ -100,16 +100,23 @@ protected:
   virtual mozilla::ipc::IPCResult RecvCrossProcessRedirect(
                                     const uint32_t& aRegistrarId,
                                     nsIURI* aURI,
                                     const uint32_t& aNewLoadFlags,
                                     const OptionalLoadInfoArgs& aLoadInfoForwarder,
                                     const uint64_t& aChannelId,
                                     nsIURI* aOriginalURI,
                                     const uint64_t& aIdentifier) override;
+
+  virtual PTrackingDummyChannelChild*
+    AllocPTrackingDummyChannelChild(nsIURI* aURI,
+                                    const OptionalLoadInfoArgs& aLoadInfo) override;
+
+  virtual bool
+    DeallocPTrackingDummyChannelChild(PTrackingDummyChannelChild* aChannel) override;
 };
 
 /**
  * Reference to the PNecko Child protocol.
  * Null if this is not a content process.
  */
 extern PNeckoChild *gNeckoChild;
 
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -21,16 +21,17 @@
 #include "mozilla/net/DataChannelParent.h"
 #include "mozilla/net/SimpleChannelParent.h"
 #include "mozilla/net/AltDataOutputStreamParent.h"
 #include "mozilla/Unused.h"
 #include "mozilla/net/FileChannelParent.h"
 #include "mozilla/net/DNSRequestParent.h"
 #include "mozilla/net/ChannelDiverterParent.h"
 #include "mozilla/net/IPCTransportProvider.h"
+#include "mozilla/net/TrackingDummyChannelParent.h"
 #ifdef MOZ_WEBRTC
 #include "mozilla/net/StunAddrsRequestParent.h"
 #endif
 #include "mozilla/dom/ChromeUtils.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/TabContext.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/dom/network/TCPSocketParent.h"
@@ -1009,10 +1010,49 @@ NeckoParent::RecvGetExtensionFD(const UR
   if (result.isErr()) {
     FileDescriptor invalidFD;
     aResolve(invalidFD);
   }
 
   return IPC_OK();
 }
 
+PTrackingDummyChannelParent*
+NeckoParent::AllocPTrackingDummyChannelParent(nsIURI* aURI,
+                                              const OptionalLoadInfoArgs& aLoadInfo)
+{
+  RefPtr<TrackingDummyChannelParent> c = new TrackingDummyChannelParent();
+  return c.forget().take();
+}
+
+mozilla::ipc::IPCResult
+NeckoParent::RecvPTrackingDummyChannelConstructor(PTrackingDummyChannelParent* aActor,
+                                                  nsIURI* aURI,
+                                                  const OptionalLoadInfoArgs& aLoadInfo)
+{
+  TrackingDummyChannelParent* p =
+    static_cast<TrackingDummyChannelParent*>(aActor);
+
+  if (NS_WARN_IF(!aURI)) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+
+  nsCOMPtr<nsILoadInfo> loadInfo;
+  nsresult rv = LoadInfoArgsToLoadInfo(aLoadInfo, getter_AddRefs(loadInfo));
+  if (NS_WARN_IF(NS_FAILED(rv)) || !loadInfo) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+
+  p->Init(aURI, loadInfo);
+  return IPC_OK();
+}
+
+bool
+NeckoParent::DeallocPTrackingDummyChannelParent(PTrackingDummyChannelParent* aActor)
+{
+  RefPtr<TrackingDummyChannelParent> c =
+    dont_AddRef(static_cast<TrackingDummyChannelParent*>(aActor));
+  MOZ_ASSERT(c);
+  return true;
+}
+
 } // namespace net
 } // namespace mozilla
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -235,14 +235,26 @@ protected:
   /* WebExtensions */
   virtual mozilla::ipc::IPCResult
     RecvGetExtensionStream(const URIParams& aURI,
                            GetExtensionStreamResolver&& aResolve) override;
 
   virtual mozilla::ipc::IPCResult
     RecvGetExtensionFD(const URIParams& aURI,
                        GetExtensionFDResolver&& aResolve) override;
+
+  virtual PTrackingDummyChannelParent*
+    AllocPTrackingDummyChannelParent(nsIURI* aURI,
+                                     const OptionalLoadInfoArgs& aLoadInfo) override;
+
+  virtual bool
+    DeallocPTrackingDummyChannelParent(PTrackingDummyChannelParent* aChild) override;
+
+  virtual mozilla::ipc::IPCResult
+    RecvPTrackingDummyChannelConstructor(PTrackingDummyChannelParent* aActor,
+                                         nsIURI* aURI,
+                                         const OptionalLoadInfoArgs& aLoadInfo) override;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // mozilla_net_NeckoParent_h
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -21,16 +21,17 @@ include protocol PChannelDiverter;
 include protocol PFileDescriptorSet;
 include protocol PDataChannel;
 include protocol PSimpleChannel;
 include protocol PTransportProvider;
 include protocol PChildToParentStream; //FIXME: bug #792908
 include protocol PParentToChildStream; //FIXME: bug #792908
 include protocol PStunAddrsRequest;
 include protocol PFileChannel;
+include protocol PTrackingDummyChannel;
 
 include IPCStream;
 include URIParams;
 include NeckoChannelParams;
 include PBrowserOrId;
 include protocol PAltDataOutputStream;
 
 using class IPC::SerializedLoadContext from "SerializedLoadContext.h";
@@ -58,16 +59,17 @@ nested(upto inside_cpow) sync protocol P
   manages PDNSRequest;
   manages PDataChannel;
   manages PSimpleChannel;
   manages PFileChannel;
   manages PChannelDiverter;
   manages PTransportProvider;
   manages PAltDataOutputStream;
   manages PStunAddrsRequest;
+  manages PTrackingDummyChannel;
 
 parent:
   async __delete__();
 
   nested(inside_cpow) async PCookieService();
   async PHttpChannel(PBrowserOrId browser,
                      SerializedLoadContext loadContext,
                      HttpChannelCreationArgs args);
@@ -105,16 +107,18 @@ parent:
    * the parent and the child when we're redirecting to a data: URI.
    */
   async PDataChannel(uint32_t channelId);
   async PSimpleChannel(uint32_t channelId);
   async PFileChannel(uint32_t channelId);
 
   async PChannelDiverter(ChannelDiverterArgs channel);
 
+  async PTrackingDummyChannel(nsIURI uri, OptionalLoadInfoArgs loadInfo);
+
   /**
    * These are called from the child with the results of the auth prompt.
    * callbackId is the id that was passed in PBrowser::AsyncAuthPrompt,
    * corresponding to an nsIAuthPromptCallback
    */
   async OnAuthAvailable(uint64_t callbackId, nsString user,
                         nsString password, nsString domain);
   async OnAuthCancelled(uint64_t callbackId, bool userCancel);
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -54,25 +54,28 @@
 #include "nsIDOMWindowUtils.h"
 #include "nsIEventTarget.h"
 #include "nsRedirectHistoryEntry.h"
 #include "nsSocketTransportService2.h"
 #include "nsStreamUtils.h"
 #include "nsThreadUtils.h"
 #include "nsCORSListenerProxy.h"
 #include "nsApplicationCache.h"
+#include "TrackingDummyChannel.h"
 
 #ifdef MOZ_TASK_TRACER
 #include "GeckoTaskTracer.h"
 #endif
 
 #ifdef MOZ_GECKO_PROFILER
 #include "ProfilerMarkerPayload.h"
 #endif
 
+#include <functional>
+
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 
 namespace mozilla {
 namespace net {
 
 NS_IMPL_ISUPPORTS(InterceptStreamListener,
                   nsIStreamListener,
@@ -2693,28 +2696,56 @@ HttpChannelChild::AsyncOpen(nsIStreamLis
   // Set user agent override from docshell
   HttpBaseChannel::SetDocshellUserAgentOverride();
 
   MOZ_ASSERT_IF(mPostRedirectChannelShouldUpgrade,
                 mPostRedirectChannelShouldIntercept);
   bool shouldUpgrade = mPostRedirectChannelShouldUpgrade;
   if (mPostRedirectChannelShouldIntercept ||
       ShouldInterceptURI(mURI, shouldUpgrade)) {
-    mResponseCouldBeSynthesized = true;
-
-    nsCOMPtr<nsINetworkInterceptController> controller;
-    GetCallback(controller);
-
-    mInterceptListener = new InterceptStreamListener(this, mListenerContext);
-
-    RefPtr<InterceptedChannelContent> intercepted =
-        new InterceptedChannelContent(this, controller,
-                                      mInterceptListener, shouldUpgrade);
-    intercepted->NotifyController();
-    return NS_OK;
+    RefPtr<HttpChannelChild> self = this;
+
+    std::function<void(bool)> callback = [self, shouldUpgrade](bool aStorageAllowed) {
+      if (!aStorageAllowed) {
+        nsresult rv = self->ContinueAsyncOpen();
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          Unused << self->AsyncAbort(rv);
+        }
+        return;
+      }
+
+      self->mResponseCouldBeSynthesized = true;
+
+      nsCOMPtr<nsINetworkInterceptController> controller;
+      self->GetCallback(controller);
+
+      self->mInterceptListener =
+        new InterceptStreamListener(self, self->mListenerContext);
+
+      RefPtr<InterceptedChannelContent> intercepted =
+          new InterceptedChannelContent(self, controller,
+                                        self->mInterceptListener,
+                                        shouldUpgrade);
+      intercepted->NotifyController();
+    };
+
+    TrackingDummyChannel::StorageAllowedState state =
+      TrackingDummyChannel::StorageAllowed(this, callback);
+    if (state == TrackingDummyChannel::eStorageGranted) {
+      callback(true);
+      return NS_OK;
+    }
+
+    if (state == TrackingDummyChannel::eAsyncNeeded) {
+      // The async callback will be executed eventually.
+      return NS_OK;
+    }
+
+    MOZ_ASSERT(state == TrackingDummyChannel::eStorageDenied);
+    // Fall through
   }
 
   return ContinueAsyncOpen();
 }
 
 NS_IMETHODIMP
 HttpChannelChild::AsyncOpen2(nsIStreamListener *aListener)
 {
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/PTrackingDummyChannel.ipdl
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* 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 protocol PNecko;
+
+namespace mozilla {
+namespace net {
+
+// This protocol provides a mechanism for the "child intercept" mode of
+// ServiceWorker operation to work correctly with Tracking Protection
+// annotations.  ServiceWorkers should not be allowed for third-party iframes
+// which are annotated as tracking origins.
+//
+// In child intercept mode, the decision to intercept a channel is made in the
+// child process without consulting the parent process.  The decision is based
+// on whether there is a ServiceWorker with a scope covering the URL in question
+// and whether storage is allowed for the origin/URL.  When the
+// "network.cookie.cookieBehavior" preference is set to BEHAVIOR_REJECT_TRACKER,
+// annotated channels are denied storage which means that the ServiceWorker
+// should not intercept the channel.  However, the decision for tracking
+// protection to annotate a channel only occurs in the parent process.  The
+// dummy channel is a hack to allow the intercept decision process to ask the
+// parent process if the channel should be annotated.  Because this round-trip
+// to the parent has overhead, the dummy channel is only created 1) if the
+// ServiceWorker initially determines that the channel should be intercepted and
+// 2) it's a navigation request.
+//
+// This hack can be removed once Bug 1231208's new "parent intercept" mechanism
+// fully lands, the pref is enabled by default it stays enabled for long enough
+// to be confident we will never need/want to turn it off.  Then as part of bug
+// 1496997 we can remove this implementation.  Bug 1498259 covers removing this
+// hack in particular.
+protocol PTrackingDummyChannel
+{
+  manager PNecko;
+
+child:
+  async __delete__(bool aTrackingResource);
+};
+
+} // namespace net
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/TrackingDummyChannel.cpp
@@ -0,0 +1,356 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et 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 "TrackingDummyChannel.h"
+
+#include "mozilla/AntiTrackingCommon.h"
+#include "mozilla/net/TrackingDummyChannelChild.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/Preferences.h"
+#include "nsIChannel.h"
+#include "nsIURI.h"
+
+namespace mozilla {
+namespace net {
+
+namespace {
+
+// We need TrackingDummyChannel any time
+// privacy.trackingprotection.annotate_channels prefs is set to true.
+bool
+ChannelNeedsToBeAnnotated()
+{
+  static bool sChannelAnnotationNeededInited = false;
+  static bool sChannelAnnotationNeeded = false;
+
+  if (!sChannelAnnotationNeededInited) {
+    sChannelAnnotationNeededInited = true;
+    Preferences::AddBoolVarCache(&sChannelAnnotationNeeded,
+                                 "privacy.trackingprotection.annotate_channels");
+  }
+
+  return sChannelAnnotationNeeded;
+}
+
+} // anonymous
+
+/* static */ TrackingDummyChannel::StorageAllowedState
+TrackingDummyChannel::StorageAllowed(nsIChannel* aChannel,
+                                     const std::function<void(bool)>& aCallback)
+{
+  MOZ_ASSERT(!XRE_IsParentProcess());
+
+  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
+  if (!httpChannel) {
+    // Any non-http channel is allowed.
+    return eStorageGranted;
+  }
+
+  if (!nsContentUtils::IsNonSubresourceRequest(aChannel)) {
+    // If this is a sub-resource, we don't need to check if the channel is
+    // annotated as tracker because:
+    // - if the SW doesn't respond, or it sends us back to the network, the
+    //   channel will be annotated on the parent process.
+    // - if the SW answers, the content is taken from the cache, which is
+    //   considered trusted.
+    return eStorageGranted;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  aChannel->GetURI(getter_AddRefs(uri));
+
+  if (ChannelNeedsToBeAnnotated()) {
+    ContentChild* cc = static_cast<ContentChild*>(gNeckoChild->Manager());
+    if (cc->IsShuttingDown()) {
+      return eStorageDenied;
+    }
+
+    if (!TrackingDummyChannelChild::Create(httpChannel, uri, aCallback)) {
+      return eStorageDenied;
+    }
+
+    return eAsyncNeeded;
+  }
+
+  if (AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(httpChannel, uri,
+                                                              nullptr)) {
+    return eStorageGranted;
+  }
+
+  return eStorageDenied;
+}
+
+NS_IMPL_ADDREF(TrackingDummyChannel)
+NS_IMPL_RELEASE(TrackingDummyChannel)
+
+NS_INTERFACE_MAP_BEGIN(TrackingDummyChannel)
+  NS_INTERFACE_MAP_ENTRY(nsIRequest)
+  NS_INTERFACE_MAP_ENTRY(nsIChannel)
+  NS_INTERFACE_MAP_ENTRY_CONCRETE(TrackingDummyChannel)
+NS_INTERFACE_MAP_END
+
+TrackingDummyChannel::TrackingDummyChannel(nsIURI* aURI,
+                                           nsILoadInfo* aLoadInfo)
+  : mIsTrackingResource(false)
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+
+  SetOriginalURI(aURI);
+  SetLoadInfo(aLoadInfo);
+}
+
+TrackingDummyChannel::~TrackingDummyChannel() = default;
+
+bool
+TrackingDummyChannel::IsTrackingResource() const
+{
+  return mIsTrackingResource;
+}
+
+void
+TrackingDummyChannel::SetIsTrackingResource()
+{
+  mIsTrackingResource = true;
+}
+
+//-----------------------------------------------------------------------------
+// TrackingDummyChannel::nsIChannel
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetOriginalURI(nsIURI** aOriginalURI)
+{
+  NS_IF_ADDREF(*aOriginalURI = mURI);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::SetOriginalURI(nsIURI* aOriginalURI)
+{
+  mURI = aOriginalURI;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetURI(nsIURI** aURI)
+{
+  NS_IF_ADDREF(*aURI = mURI);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetOwner(nsISupports** aOwner)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::SetOwner(nsISupports* aOwner)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetNotificationCallbacks(nsIInterfaceRequestor** aNotificationCallbacks)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetSecurityInfo(nsISupports** aSecurityInfo)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetContentType(nsACString& aContentType)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::SetContentType(const nsACString& aContentType)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetContentCharset(nsACString& aContentCharset)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::SetContentCharset(const nsACString& aContentCharset)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetContentLength(int64_t* aContentLength)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::SetContentLength(int64_t aContentLength)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::Open(nsIInputStream** aRetval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::Open2(nsIInputStream** aStream)
+{
+  nsCOMPtr<nsIStreamListener> listener;
+  nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return Open(aStream);
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::AsyncOpen(nsIStreamListener* aListener, nsISupports* aContext)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::AsyncOpen2(nsIStreamListener* aListener)
+{
+  nsCOMPtr<nsIStreamListener> listener = aListener;
+  nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return AsyncOpen(listener, nullptr);
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetContentDisposition(uint32_t* aContentDisposition)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::SetContentDisposition(uint32_t aContentDisposition)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetContentDispositionFilename(nsAString& aContentDispositionFilename)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::SetContentDispositionFilename(const nsAString& aContentDispositionFilename)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetContentDispositionHeader(nsACString& aContentDispositionHeader)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetLoadInfo(nsILoadInfo** aLoadInfo)
+{
+  NS_IF_ADDREF(*aLoadInfo = mLoadInfo);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::SetLoadInfo(nsILoadInfo* aLoadInfo)
+{
+  mLoadInfo = aLoadInfo;
+  return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// TrackingDummyChannel::nsIRequest
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetName(nsACString& aName)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::IsPending(bool* aRetval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetStatus(nsresult* aStatus)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::Cancel(nsresult aStatus)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::Suspend()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::Resume()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetLoadGroup(nsILoadGroup** aLoadGroup)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetLoadFlags(nsLoadFlags* aLoadFlags)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetIsDocument(bool* aIsDocument)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+} // net namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/TrackingDummyChannel.h
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et 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/. */
+
+#ifndef mozilla_net_TrackingDummyChannel_h
+#define mozilla_net_TrackingDummyChannel_h
+
+#include "nsIChannel.h"
+#include <functional>
+
+#define TRACKING_DUMMY_CHANNEL_IID \
+  { 0x70ceb97d, 0xbfa6, 0x4255, \
+    { 0xb7, 0x08, 0xe1, 0xb4, 0x4a, 0x1e, 0x0e, 0x9a } }
+
+class nsIChannel;
+
+namespace mozilla {
+namespace net {
+
+/**
+ * In child intercept mode, the decision to intercept a channel is made in the
+ * child process without consulting the parent process.  The decision is based
+ * on whether there is a ServiceWorker with a scope covering the URL in question
+ * and whether storage is allowed for the origin/URL.  When the
+ * "network.cookie.cookieBehavior" preference is set to BEHAVIOR_REJECT_TRACKER,
+ * annotated channels are denied storage which means that the ServiceWorker
+ * should not intercept the channel.  However, the decision for tracking
+ * protection to annotate a channel only occurs in the parent process.  The
+ * dummy channel is a hack to allow the intercept decision process to ask the
+ * parent process if the channel should be annotated.  Because this round-trip
+ * to the parent has overhead, the dummy channel is only created 1) if the
+ * ServiceWorker initially determines that the channel should be intercepted and
+ * 2) it's a navigation request.
+ *
+ * This hack can be removed once Bug 1231208's new "parent intercept" mechanism
+ * fully lands, the pref is enabled by default it stays enabled for long enough
+ * to be confident we will never need/want to turn it off.  Then as part of bug
+ * 1496997 we can remove this implementation.  Bug 1498259 covers removing this
+ * hack in particular.
+ */
+class TrackingDummyChannel final : public nsIChannel
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(TRACKING_DUMMY_CHANNEL_IID)
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIREQUEST
+  NS_DECL_NSICHANNEL
+
+  enum StorageAllowedState
+  {
+    eStorageGranted,
+    eStorageDenied,
+    eAsyncNeeded,
+  };
+
+  static StorageAllowedState
+  StorageAllowed(nsIChannel* aChannel,
+                 const std::function<void(bool)>& aCallback);
+
+  TrackingDummyChannel(nsIURI* aURI,
+                       nsILoadInfo* aLoadInfo);
+
+  bool
+  IsTrackingResource() const;
+
+  void
+  SetIsTrackingResource();
+
+private:
+  ~TrackingDummyChannel();
+
+  nsCOMPtr<nsILoadInfo> mLoadInfo;
+  nsCOMPtr<nsIURI> mURI;
+
+  bool mIsTrackingResource;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(TrackingDummyChannel, TRACKING_DUMMY_CHANNEL_IID)
+
+} // net namespace
+} // mozilla namespace
+
+#endif // mozilla_net_TrackingDummyChannel_h
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/TrackingDummyChannelChild.cpp
@@ -0,0 +1,90 @@
+/* -*- 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 "TrackingDummyChannelChild.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "nsIChannel.h"
+#include "nsIURI.h"
+
+namespace mozilla {
+namespace net {
+
+/* static */ bool
+TrackingDummyChannelChild::Create(nsIHttpChannel* aChannel, nsIURI* aURI,
+                                  const std::function<void(bool)>& aCallback)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MOZ_ASSERT(aChannel);
+  MOZ_ASSERT(aURI);
+
+  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+  if (!loadInfo) {
+    return false;
+  }
+
+  OptionalLoadInfoArgs loadInfoArgs;
+  mozilla::ipc::LoadInfoToLoadInfoArgs(loadInfo, &loadInfoArgs);
+
+  PTrackingDummyChannelChild* actor =
+    gNeckoChild->SendPTrackingDummyChannelConstructor(aURI, loadInfoArgs);
+  if (!actor) {
+    return false;
+  }
+
+  bool isThirdParty =
+    nsContentUtils::IsThirdPartyWindowOrChannel(nullptr, aChannel, aURI);
+
+  static_cast<TrackingDummyChannelChild*>(actor)->Initialize(aChannel, aURI,
+                                                             isThirdParty,
+                                                             aCallback);
+  return true;
+}
+
+TrackingDummyChannelChild::TrackingDummyChannelChild()
+  : mIsThirdParty(false)
+{}
+
+TrackingDummyChannelChild::~TrackingDummyChannelChild() = default;
+
+void
+TrackingDummyChannelChild::Initialize(nsIHttpChannel* aChannel,
+                                      nsIURI* aURI,
+                                      bool aIsThirdParty,
+                                      const std::function<void(bool)>& aCallback)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mChannel = aChannel;
+  mURI = aURI;
+  mIsThirdParty = aIsThirdParty;
+  mCallback = aCallback;
+}
+
+mozilla::ipc::IPCResult
+TrackingDummyChannelChild::Recv__delete__(const bool& aTrackingResource)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!mChannel) {
+    return IPC_OK();
+  }
+
+  nsCOMPtr<nsIHttpChannel> channel = std::move(mChannel);
+
+  RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(channel);
+  httpChannel->SetIsTrackingResource(mIsThirdParty);
+
+  bool storageGranted =
+    AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(httpChannel, mURI,
+                                                            nullptr);
+  mCallback(storageGranted);
+  return IPC_OK();
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/TrackingDummyChannelChild.h
@@ -0,0 +1,51 @@
+/* -*- 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/. */
+
+#ifndef mozilla_net_TrackingDummyChannelChild_h
+#define mozilla_net_TrackingDummyChannelChild_h
+
+#include "mozilla/net/PTrackingDummyChannelChild.h"
+#include "nsCOMPtr.h"
+
+#include <functional>
+
+class nsIHttpChannel;
+class nsIURI;
+
+namespace mozilla {
+namespace net {
+
+class TrackingDummyChannelChild final : public PTrackingDummyChannelChild
+{
+public:
+  static bool
+  Create(nsIHttpChannel* aChannel, nsIURI* aURI,
+         const std::function<void(bool)>& aCallback);
+
+  // Used by PNeckoChild only!
+  TrackingDummyChannelChild();
+  ~TrackingDummyChannelChild();
+
+private:
+  void
+  Initialize(nsIHttpChannel* aChannel,
+             nsIURI* aURI,
+             bool aIsThirdParty,
+             const std::function<void(bool)>& aCallback);
+
+  mozilla::ipc::IPCResult
+  Recv__delete__(const bool& aTrackingResource) override;
+
+  nsCOMPtr<nsIHttpChannel> mChannel;
+  nsCOMPtr<nsIURI> mURI;
+  std::function<void(bool)> mCallback;
+  bool mIsThirdParty;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_TrackingDummyChannelChild_h
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/TrackingDummyChannelParent.cpp
@@ -0,0 +1,64 @@
+/* -*- 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 "TrackingDummyChannelParent.h"
+#include "mozilla/Unused.h"
+#include "nsChannelClassifier.h"
+#include "nsIChannel.h"
+#include "nsIPrincipal.h"
+#include "nsNetUtil.h"
+
+namespace mozilla {
+namespace net {
+
+TrackingDummyChannelParent::TrackingDummyChannelParent()
+  : mIPCActive(true)
+{}
+
+TrackingDummyChannelParent::~TrackingDummyChannelParent() = default;
+
+void
+TrackingDummyChannelParent::Init(nsIURI* aURI,
+                                 nsILoadInfo* aLoadInfo)
+{
+  MOZ_ASSERT(mIPCActive);
+
+  RefPtr<TrackingDummyChannelParent> self = this;
+  auto onExit = MakeScopeExit([self] {
+    Unused << Send__delete__(self, false);
+  });
+
+  if (!aURI) {
+    return;
+  }
+
+  RefPtr<TrackingDummyChannel> channel =
+    new TrackingDummyChannel(aURI, aLoadInfo);
+
+  RefPtr<nsChannelClassifier> channelClassifier =
+    new nsChannelClassifier(channel);
+
+  bool willCallback =
+    NS_SUCCEEDED(channelClassifier->CheckIsTrackerWithLocalTable(
+      [self = std::move(self), channel]() {
+        if (self->mIPCActive) {
+          Unused << Send__delete__(self, channel->IsTrackingResource());
+        }
+      }));
+
+  if (willCallback) {
+    onExit.release();
+  }
+}
+
+void
+TrackingDummyChannelParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  mIPCActive = false;
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/TrackingDummyChannelParent.h
@@ -0,0 +1,42 @@
+/* -*- 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/. */
+
+#ifndef mozilla_net_TrackingDummyChannelParent_h
+#define mozilla_net_TrackingDummyChannelParent_h
+
+#include "mozilla/net/PTrackingDummyChannelParent.h"
+#include "nsISupportsImpl.h"
+
+class nsILoadInfo;
+class nsIURI;
+
+namespace mozilla {
+namespace net {
+
+class TrackingDummyChannelParent final : public PTrackingDummyChannelParent
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(TrackingDummyChannelParent)
+
+  TrackingDummyChannelParent();
+
+  void
+  Init(nsIURI* aURI,
+       nsILoadInfo* aLoadInfo);
+
+private:
+  ~TrackingDummyChannelParent();
+
+  void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  bool mIPCActive;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_TrackingDummyChannelParent_h
--- a/netwerk/protocol/http/moz.build
+++ b/netwerk/protocol/http/moz.build
@@ -47,16 +47,19 @@ EXPORTS.mozilla.net += [
     'HttpChannelChild.h',
     'HttpChannelParent.h',
     'HttpInfo.h',
     'nsServerTiming.h',
     'NullHttpChannel.h',
     'PHttpChannelParams.h',
     'PSpdyPush.h',
     'TimingStruct.h',
+    'TrackingDummyChannel.h',
+    'TrackingDummyChannelChild.h',
+    'TrackingDummyChannelParent.h',
 ]
 
 SOURCES += [
     'nsHttpChannelAuthProvider.cpp', # redefines GetAuthType
 ]
 
 UNIFIED_SOURCES += [
     'AltDataOutputStreamChild.cpp',
@@ -95,28 +98,32 @@ UNIFIED_SOURCES += [
     'nsHttpHeaderArray.cpp',
     'nsHttpNTLMAuth.cpp',
     'nsHttpRequestHead.cpp',
     'nsHttpResponseHead.cpp',
     'nsHttpTransaction.cpp',
     'nsServerTiming.cpp',
     'NullHttpChannel.cpp',
     'NullHttpTransaction.cpp',
+    'TrackingDummyChannel.cpp',
+    'TrackingDummyChannelChild.cpp',
+    'TrackingDummyChannelParent.cpp',
     'TunnelUtils.cpp',
 ]
 
 # These files cannot be built in unified mode because of OS X headers.
 SOURCES += [
     'nsHttpHandler.cpp',
 ]
 
 IPDL_SOURCES += [
     'PAltDataOutputStream.ipdl',
     'PHttpBackgroundChannel.ipdl',
     'PHttpChannel.ipdl',
+    'PTrackingDummyChannel.ipdl',
 ]
 
 EXTRA_JS_MODULES += [
     'UserAgentOverrides.jsm',
     'UserAgentUpdates.jsm',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -104,35 +104,42 @@
 #include "mozilla/extensions/StreamFilterParent.h"
 #include "mozilla/net/Predictor.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/NullPrincipal.h"
 #include "CacheControlParser.h"
 #include "nsMixedContentBlocker.h"
 #include "CacheStorageService.h"
 #include "HttpChannelParent.h"
+#include "HttpChannelParentListener.h"
 #include "InterceptedHttpChannel.h"
 #include "nsIBufferedStreams.h"
 #include "nsIFileStreams.h"
 #include "nsIMIMEInputStream.h"
 #include "nsIMultiplexInputStream.h"
 #include "../../cache2/CacheFileUtils.h"
 #include "nsINetworkLinkService.h"
+#include "nsIRedirectProcessChooser.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/Promise.h"
+#include "mozilla/dom/ServiceWorkerUtils.h"
 
 #ifdef MOZ_TASK_TRACER
 #include "GeckoTaskTracer.h"
 #endif
 
 #ifdef MOZ_GECKO_PROFILER
 #include "ProfilerMarkerPayload.h"
 #endif
 
-namespace mozilla { namespace net {
+namespace mozilla {
+
+using namespace dom;
+
+namespace net {
 
 namespace {
 
 static bool sRCWNEnabled = false;
 static uint32_t sRCWNQueueSizeNormal = 50;
 static uint32_t sRCWNQueueSizePriority = 10;
 static uint32_t sRCWNSmallResourceSizeKB = 256;
 static uint32_t sRCWNMinWaitMs = 0;
@@ -6770,17 +6777,17 @@ nsHttpChannel::ProcessId()
   RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel);
   if (httpParent) {
     return httpParent->OtherPid();
   }
   return base::GetCurrentProcId();
 }
 
 bool
-nsHttpChannel::AttachStreamFilter(ipc::Endpoint<extensions::PStreamFilterParent>&& aEndpoint)
+nsHttpChannel::AttachStreamFilter(mozilla::ipc::Endpoint<extensions::PStreamFilterParent>&& aEndpoint)
 
 {
   nsCOMPtr<nsIParentChannel> parentChannel;
   NS_QueryNotificationCallbacks(this, parentChannel);
   RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel);
   if (httpParent) {
     return httpParent->SendAttachStreamFilter(std::move(aEndpoint));
   }