Bug 1481252 - Part 1: Report FastBlock status to docshell; r=valentin
authorLiang-Heng Chen <xeonchen@gmail.com>
Thu, 16 Aug 2018 15:29:22 +0000
changeset 487229 74da82ac273d53e5aac36be2fd4f0f10676274f1
parent 487228 61909c98d50d54bcedec18a3c963ac8c37aa0664
child 487230 aa640b891abd0109df4552663e20318c48877d88
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvalentin
bugs1481252
milestone63.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 1481252 - Part 1: Report FastBlock status to docshell; r=valentin Differential Revision: https://phabricator.services.mozilla.com/D3024
ipc/glue/BackgroundUtils.cpp
netwerk/base/LoadInfo.cpp
netwerk/base/LoadInfo.h
netwerk/base/nsILoadInfo.idl
netwerk/ipc/NeckoChannelParams.ipdlh
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/nsHttpChannel.cpp
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -625,34 +625,43 @@ LoadInfoArgsToLoadInfo(const OptionalLoa
 
 void
 LoadInfoToParentLoadInfoForwarder(nsILoadInfo* aLoadInfo,
                                   ParentLoadInfoForwarderArgs* aForwarderArgsOut)
 {
   if (!aLoadInfo) {
     *aForwarderArgsOut = ParentLoadInfoForwarderArgs(false, void_t(),
                                                      nsILoadInfo::TAINTING_BASIC,
+                                                     false,
+                                                     false,
                                                      false);
     return;
   }
 
   OptionalIPCServiceWorkerDescriptor ipcController = void_t();
   Maybe<ServiceWorkerDescriptor> controller(aLoadInfo->GetController());
   if (controller.isSome()) {
     ipcController = controller.ref().ToIPC();
   }
 
   uint32_t tainting = nsILoadInfo::TAINTING_BASIC;
   Unused << aLoadInfo->GetTainting(&tainting);
 
+  bool isTracker;
+  MOZ_ALWAYS_SUCCEEDS(aLoadInfo->GetIsTracker(&isTracker));
+  bool isTrackerBlocked;
+  MOZ_ALWAYS_SUCCEEDS(aLoadInfo->GetIsTrackerBlocked(&isTrackerBlocked));
+
   *aForwarderArgsOut = ParentLoadInfoForwarderArgs(
     aLoadInfo->GetAllowInsecureRedirectToDataURI(),
     ipcController,
     tainting,
-    aLoadInfo->GetServiceWorkerTaintingSynthesized()
+    aLoadInfo->GetServiceWorkerTaintingSynthesized(),
+    isTracker,
+    isTrackerBlocked
   );
 }
 
 nsresult
 MergeParentLoadInfoForwarder(ParentLoadInfoForwarderArgs const& aForwarderArgs,
                              nsILoadInfo* aLoadInfo)
 {
   if (!aLoadInfo) {
@@ -674,16 +683,19 @@ MergeParentLoadInfoForwarder(ParentLoadI
 
   if (aForwarderArgs.serviceWorkerTaintingSynthesized()) {
     aLoadInfo->SynthesizeServiceWorkerTainting(
       static_cast<LoadTainting>(aForwarderArgs.tainting()));
   } else {
     aLoadInfo->MaybeIncreaseTainting(aForwarderArgs.tainting());
   }
 
+  MOZ_ALWAYS_SUCCEEDS(aLoadInfo->SetIsTracker(aForwarderArgs.isTracker()));
+  MOZ_ALWAYS_SUCCEEDS(aLoadInfo->SetIsTrackerBlocked(aForwarderArgs.isTrackerBlocked()));
+
   return NS_OK;
 }
 
 void
 LoadInfoToChildLoadInfoForwarder(nsILoadInfo* aLoadInfo,
                                  ChildLoadInfoForwarderArgs* aForwarderArgsOut)
 {
   if (!aLoadInfo) {
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -82,16 +82,18 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
   , mInitialSecurityCheckDone(false)
   , mIsThirdPartyContext(false)
   , mIsDocshellReload(false)
   , mSendCSPViolationEvents(true)
   , mForcePreflight(false)
   , mIsPreflight(false)
   , mLoadTriggeredFromExternal(false)
   , mServiceWorkerTaintingSynthesized(false)
+  , mIsTracker(false)
+  , mIsTrackerBlocked(false)
 {
   MOZ_ASSERT(mLoadingPrincipal);
   MOZ_ASSERT(mTriggeringPrincipal);
 
 #ifdef DEBUG
   // TYPE_DOCUMENT loads initiated by javascript tests will go through
   // nsIOService and use the wrong constructor.  Don't enforce the
   // !TYPE_DOCUMENT check in those cases
@@ -317,16 +319,18 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* a
   , mInitialSecurityCheckDone(false)
   , mIsThirdPartyContext(false) // NB: TYPE_DOCUMENT implies not third-party.
   , mIsDocshellReload(false)
   , mSendCSPViolationEvents(true)
   , mForcePreflight(false)
   , mIsPreflight(false)
   , mLoadTriggeredFromExternal(false)
   , mServiceWorkerTaintingSynthesized(false)
+  , mIsTracker(false)
+  , mIsTrackerBlocked(false)
 {
   // Top-level loads are never third-party
   // Grab the information we can out of the window.
   MOZ_ASSERT(aOuterWindow);
   MOZ_ASSERT(mTriggeringPrincipal);
 
   // if the load is sandboxed, we can not also inherit the principal
   if (mSecurityFlags & nsILoadInfo::SEC_SANDBOXED) {
@@ -414,16 +418,18 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
   , mAncestorPrincipals(rhs.mAncestorPrincipals)
   , mAncestorOuterWindowIDs(rhs.mAncestorOuterWindowIDs)
   , mCorsUnsafeHeaders(rhs.mCorsUnsafeHeaders)
   , mForcePreflight(rhs.mForcePreflight)
   , mIsPreflight(rhs.mIsPreflight)
   , mLoadTriggeredFromExternal(rhs.mLoadTriggeredFromExternal)
   // mServiceWorkerTaintingSynthesized must be handled specially during redirect
   , mServiceWorkerTaintingSynthesized(false)
+  , mIsTracker(rhs.mIsTracker)
+  , mIsTrackerBlocked(rhs.mIsTrackerBlocked)
 {
 }
 
 LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
                    nsIPrincipal* aTriggeringPrincipal,
                    nsIPrincipal* aPrincipalToInherit,
                    nsIPrincipal* aSandboxedLoadingPrincipal,
                    nsIPrincipal* aTopLevelPrincipal,
@@ -501,16 +507,18 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
   , mOriginAttributes(aOriginAttributes)
   , mAncestorPrincipals(std::move(aAncestorPrincipals))
   , mAncestorOuterWindowIDs(aAncestorOuterWindowIDs)
   , mCorsUnsafeHeaders(aCorsUnsafeHeaders)
   , mForcePreflight(aForcePreflight)
   , mIsPreflight(aIsPreflight)
   , mLoadTriggeredFromExternal(aLoadTriggeredFromExternal)
   , mServiceWorkerTaintingSynthesized(aServiceWorkerTaintingSynthesized)
+  , mIsTracker(false)
+  , mIsTrackerBlocked(false)
 {
   // Only top level TYPE_DOCUMENT loads can have a null loadingPrincipal
   MOZ_ASSERT(mLoadingPrincipal || aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT);
   MOZ_ASSERT(mTriggeringPrincipal);
 
   mRedirectChainIncludingInternalRedirects.SwapElements(
     aRedirectChainIncludingInternalRedirects);
 
@@ -1324,16 +1332,46 @@ LoadInfo::SynthesizeServiceWorkerTaintin
   MOZ_DIAGNOSTIC_ASSERT(aTainting <= LoadTainting::Opaque);
   mTainting = aTainting;
 
   // Flag to prevent the tainting from being increased.
   mServiceWorkerTaintingSynthesized = true;
 }
 
 NS_IMETHODIMP
+LoadInfo::GetIsTracker(bool *aIsTracker)
+{
+  MOZ_ASSERT(aIsTracker);
+  *aIsTracker = mIsTracker;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadInfo::SetIsTracker(bool aIsTracker)
+{
+  mIsTracker = aIsTracker;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadInfo::GetIsTrackerBlocked(bool *aIsTrackerBlocked)
+{
+  MOZ_ASSERT(aIsTrackerBlocked);
+  *aIsTrackerBlocked = mIsTrackerBlocked;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadInfo::SetIsTrackerBlocked(bool aIsTrackerBlocked)
+{
+  mIsTrackerBlocked = aIsTrackerBlocked;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 LoadInfo::GetIsTopLevelLoad(bool *aResult)
 {
   *aResult = mFrameOuterWindowID ? mFrameOuterWindowID == mOuterWindowID
                                  : mParentOuterWindowID == mOuterWindowID;
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/netwerk/base/LoadInfo.h
+++ b/netwerk/base/LoadInfo.h
@@ -203,15 +203,18 @@ private:
   RedirectHistoryArray             mRedirectChain;
   nsTArray<nsCOMPtr<nsIPrincipal>> mAncestorPrincipals;
   nsTArray<uint64_t>               mAncestorOuterWindowIDs;
   nsTArray<nsCString>              mCorsUnsafeHeaders;
   bool                             mForcePreflight;
   bool                             mIsPreflight;
   bool                             mLoadTriggeredFromExternal;
   bool                             mServiceWorkerTaintingSynthesized;
+
+  bool                             mIsTracker;
+  bool                             mIsTrackerBlocked;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // mozilla_LoadInfo_h
 
--- a/netwerk/base/nsILoadInfo.idl
+++ b/netwerk/base/nsILoadInfo.idl
@@ -1046,9 +1046,16 @@ interface nsILoadInfo : nsISupports
     * exact tainting level of the Response passed to FetchEvent.respondWith().
     * This method allows us to override the tainting level in that case.
     *
     * NOTE: This should not be used outside of service worker code! Use
     *       nsILoadInfo::MaybeIncreaseTainting() instead.
    */
   [noscript, nostdcall, notxpcom]
   void SynthesizeServiceWorkerTainting(in LoadTainting aTainting);
+
+  /**
+    * These flags are used for FastBlock statistics to see if a resource is a
+    * tracker and whether it was blocked by the FastBlock mechanism or not.
+    */
+  [infallible] attribute boolean isTracker;
+  [infallible] attribute boolean isTrackerBlocked;
 };
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -138,16 +138,20 @@ struct ParentLoadInfoForwarderArgs
   // The service worker may synthesize a Response with a particular
   // tainting value.
   uint32_t tainting;
 
   // We must also note that the tainting value was explicitly set
   // by the service worker.
   bool serviceWorkerTaintingSynthesized;
 
+  // Tracker information, currently used by FastBlock
+  bool isTracker;
+  bool isTrackerBlocked;
+
   // IMPORTANT: when you add new properites here you must also update
   // LoadInfoToParentLoadInfoForwarder and MergeParentLoadInfoForwarder
   // in BackgroundUtils.cpp/.h!
 };
 
 /**
  * This structure is used to carry selected properties of a LoadInfo
  * object to the parent process that might have changed in the child
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -691,16 +691,48 @@ public:
     return NS_OK;
   }
 
   NS_DECL_ISUPPORTS
 };
 
 NS_IMPL_ISUPPORTS(SyntheticDiversionListener, nsIStreamListener);
 
+static nsresult
+GetTopDocument(nsIChannel* aChannel, nsIDocument** aResult)
+{
+  nsresult rv;
+
+  nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = services::GetThirdPartyUtil();
+  if (NS_WARN_IF(!thirdPartyUtil)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<mozIDOMWindowProxy> win;
+  rv = thirdPartyUtil->GetTopWindowForChannel(aChannel,
+                                              getter_AddRefs(win));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  auto* pwin = nsPIDOMWindowOuter::From(win);
+  nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell();
+  if (!docShell) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIDocument> doc = docShell->GetDocument();
+  if (!doc) {
+    return NS_ERROR_FAILURE;
+  }
+
+  doc.forget(aResult);
+  return NS_OK;
+}
+
 void
 HttpChannelChild::DoOnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
 {
   LOG(("HttpChannelChild::DoOnStartRequest [this=%p]\n", this));
 
   // In theory mListener should not be null, but in practice sometimes it is.
   MOZ_ASSERT(mListener);
   if (!mListener) {
@@ -708,16 +740,33 @@ HttpChannelChild::DoOnStartRequest(nsIRe
     return;
   }
 
   if (mSynthesizedResponsePump && mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) {
     mSynthesizedResponsePump->PeekStream(CallTypeSniffers,
                                          static_cast<nsIChannel*>(this));
   }
 
+  bool isTracker;
+  if (NS_SUCCEEDED(mLoadInfo->GetIsTracker(&isTracker)) && isTracker) {
+    bool isTrackerBlocked;
+    Unused << mLoadInfo->GetIsTrackerBlocked(&isTrackerBlocked);
+
+    LOG(("HttpChannelChild::DoOnStartRequest FastBlock %d [this=%p]\n",
+         isTrackerBlocked,
+         this));
+
+    nsCOMPtr<nsIDocument> doc;
+    if (!NS_WARN_IF(NS_FAILED(GetTopDocument(this,
+                                             getter_AddRefs(doc))))) {
+
+      doc->IncrementTrackerCount(isTrackerBlocked);
+    }
+  }
+
   nsresult rv = mListener->OnStartRequest(aRequest, aContext);
   if (NS_FAILED(rv)) {
     Cancel(rv);
     return;
   }
 
   if (mDivertingToParent) {
     mListener = nullptr;
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -696,22 +696,28 @@ nsHttpChannel::CheckFastBlocked()
 
     if (!sIsContentBlockingEnabled || !sIsFastBlockEnabled ||
         IsContentPolicyTypeWhitelistedForFastBlock(mLoadInfo) ||
         !timestamp) {
         return false;
     }
 
     TimeDuration duration = TimeStamp::NowLoRes() - timestamp;
-    if (duration.ToMilliseconds() < sFastBlockTimeout) {
-        return false;
-    }
-
-    LOG(("FastBlock timeout (%lf) [this=%p]\n", duration.ToMilliseconds(), this));
-    return true;
+    bool isFastBlocking = duration.ToMilliseconds() >= sFastBlockTimeout;
+
+    if (mLoadInfo) {
+        MOZ_ALWAYS_SUCCEEDS(mLoadInfo->SetIsTracker(true));
+        MOZ_ALWAYS_SUCCEEDS(mLoadInfo->SetIsTrackerBlocked(isFastBlocking));
+    }
+
+    LOG(("FastBlock %s (%lf) [this=%p]\n",
+         isFastBlocking ? "timeout" : "passed",
+         duration.ToMilliseconds(),
+         this));
+    return isFastBlocking;
 }
 
 bool
 nsHttpChannel::IsThirdPartyChannel()
 {
     if (mIsThirdPartyChannel) {
         return *mIsThirdPartyChannel;
     }