Bug 1488974 - Disable FastBlock after the load event has fired. r=mayhemer,Ehsan
authorFrancois Marier <francois@mozilla.com>
Fri, 28 Sep 2018 19:12:10 +0000
changeset 487153 0c1057bba22441122eb5f160752af63f1ba517a5
parent 487143 684521e3af2ff1029d60bb207ae0d75058c3ce1f
child 487154 2f0c8d5a75d642197bb74fcb3479b8a02e99e8d0
push id246
push userfmarier@mozilla.com
push dateSat, 13 Oct 2018 00:15:40 +0000
reviewersmayhemer, Ehsan
bugs1488974
milestone64.0a1
Bug 1488974 - Disable FastBlock after the load event has fired. r=mayhemer,Ehsan The test used to assume that the load event didn't matter and so the expected values had to be updated to match the new behavior. A new "slowIFrame" test was added to capture what was previously tested by the "badIFrame". Differential Revision: https://phabricator.services.mozilla.com/D7031
ipc/glue/BackgroundUtils.cpp
netwerk/base/LoadInfo.cpp
netwerk/base/LoadInfo.h
netwerk/base/nsILoadInfo.idl
netwerk/ipc/NeckoChannelParams.ipdlh
netwerk/protocol/http/nsHttpChannel.cpp
toolkit/components/url-classifier/tests/mochitest/fastblock.html
toolkit/components/url-classifier/tests/mochitest/fastblock_iframe.html
toolkit/components/url-classifier/tests/mochitest/test_fastblock_bug1477046.html
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -451,17 +451,18 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoa
       ipcReservedClientInfo,
       ipcInitialClientInfo,
       ipcController,
       aLoadInfo->CorsUnsafeHeaders(),
       aLoadInfo->GetForcePreflight(),
       aLoadInfo->GetIsPreflight(),
       aLoadInfo->GetLoadTriggeredFromExternal(),
       aLoadInfo->GetServiceWorkerTaintingSynthesized(),
-      aLoadInfo->GetDocumentHasUserInteracted()
+      aLoadInfo->GetDocumentHasUserInteracted(),
+      aLoadInfo->GetDocumentHasLoaded()
       );
 
   return NS_OK;
 }
 
 nsresult
 LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
                        nsILoadInfo** outLoadInfo)
@@ -613,17 +614,18 @@ LoadInfoArgsToLoadInfo(const OptionalLoa
                           redirectChain,
                           std::move(ancestorPrincipals),
                           loadInfoArgs.ancestorOuterWindowIDs(),
                           loadInfoArgs.corsUnsafeHeaders(),
                           loadInfoArgs.forcePreflight(),
                           loadInfoArgs.isPreflight(),
                           loadInfoArgs.loadTriggeredFromExternal(),
                           loadInfoArgs.serviceWorkerTaintingSynthesized(),
-                          loadInfoArgs.documentHasUserInteracted()
+                          loadInfoArgs.documentHasUserInteracted(),
+                          loadInfoArgs.documentHasLoaded()
                           );
 
    loadInfo.forget(outLoadInfo);
    return NS_OK;
 }
 
 void
 LoadInfoToParentLoadInfoForwarder(nsILoadInfo* aLoadInfo,
@@ -631,17 +633,18 @@ LoadInfoToParentLoadInfoForwarder(nsILoa
 {
   if (!aLoadInfo) {
     *aForwarderArgsOut = ParentLoadInfoForwarderArgs(false, void_t(),
                                                      nsILoadInfo::TAINTING_BASIC,
                                                      false, // serviceWorkerTaintingSynthesized
                                                      false, // isTracker
                                                      false, // isTrackerBlocked
                                                      mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::all, // trackerBlockedReason
-                                                     false  // documentHasUserInteracted
+                                                     false, // documentHasUserInteracted
+                                                     false  // documentHasLoaded
                                                     );
     return;
   }
 
   OptionalIPCServiceWorkerDescriptor ipcController = void_t();
   Maybe<ServiceWorkerDescriptor> controller(aLoadInfo->GetController());
   if (controller.isSome()) {
     ipcController = controller.ref().ToIPC();
@@ -657,17 +660,18 @@ LoadInfoToParentLoadInfoForwarder(nsILoa
   *aForwarderArgsOut = ParentLoadInfoForwarderArgs(
     aLoadInfo->GetAllowInsecureRedirectToDataURI(),
     ipcController,
     tainting,
     aLoadInfo->GetServiceWorkerTaintingSynthesized(),
     aLoadInfo->GetIsTracker(),
     aLoadInfo->GetIsTrackerBlocked(),
     label,
-    aLoadInfo->GetDocumentHasUserInteracted()
+    aLoadInfo->GetDocumentHasUserInteracted(),
+    aLoadInfo->GetDocumentHasLoaded()
   );
 }
 
 nsresult
 MergeParentLoadInfoForwarder(ParentLoadInfoForwarderArgs const& aForwarderArgs,
                              nsILoadInfo* aLoadInfo)
 {
   if (!aLoadInfo) {
@@ -693,16 +697,17 @@ MergeParentLoadInfoForwarder(ParentLoadI
   } else {
     aLoadInfo->MaybeIncreaseTainting(aForwarderArgs.tainting());
   }
 
   MOZ_ALWAYS_SUCCEEDS(aLoadInfo->SetIsTracker(aForwarderArgs.isTracker()));
   MOZ_ALWAYS_SUCCEEDS(aLoadInfo->SetIsTrackerBlocked(aForwarderArgs.isTrackerBlocked()));
   MOZ_ALWAYS_SUCCEEDS(aLoadInfo->SetTrackerBlockedReason(aForwarderArgs.trackerBlockedReason()));
   MOZ_ALWAYS_SUCCEEDS(aLoadInfo->SetDocumentHasUserInteracted(aForwarderArgs.documentHasUserInteracted()));
+  MOZ_ALWAYS_SUCCEEDS(aLoadInfo->SetDocumentHasLoaded(aForwarderArgs.documentHasLoaded()));
 
   return NS_OK;
 }
 
 void
 LoadInfoToChildLoadInfoForwarder(nsILoadInfo* aLoadInfo,
                                  ChildLoadInfoForwarderArgs* aForwarderArgsOut)
 {
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -87,16 +87,17 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
   , mTrackerBlockedReason(mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::all)
   , mForcePreflight(false)
   , mIsPreflight(false)
   , mLoadTriggeredFromExternal(false)
   , mServiceWorkerTaintingSynthesized(false)
   , mIsTracker(false)
   , mIsTrackerBlocked(false)
   , mDocumentHasUserInteracted(false)
+  , mDocumentHasLoaded(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
@@ -158,16 +159,31 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
       mTopOuterWindowID = FindTopOuterWindowID(contextOuter);
 
       nsGlobalWindowInner* innerWindow =
         nsGlobalWindowInner::Cast(contextOuter->GetCurrentInnerWindow());
       if (innerWindow) {
         mTopLevelPrincipal = innerWindow->GetTopLevelPrincipal();
         mTopLevelStorageAreaPrincipal =
           innerWindow->GetTopLevelStorageAreaPrincipal();
+        mDocumentHasLoaded = innerWindow->IsDocumentLoaded();
+
+        if (innerWindow->IsFrame()) {
+          // For resources within iframes, we actually want the
+          // top-level document's flag, not the iframe document's.
+          mDocumentHasLoaded = false;
+          nsGlobalWindowOuter* topOuter = innerWindow->GetScriptableTopInternal();
+          if (topOuter) {
+            nsGlobalWindowInner* topInner =
+              nsGlobalWindowInner::Cast(topOuter->GetCurrentInnerWindow());
+            if (topInner) {
+              mDocumentHasLoaded = topInner->IsDocumentLoaded();
+            }
+          }
+        }
       }
     }
 
     mInnerWindowID = aLoadingContext->OwnerDoc()->InnerWindowID();
     mAncestorPrincipals = aLoadingContext->OwnerDoc()->AncestorPrincipals();
     mAncestorOuterWindowIDs = aLoadingContext->OwnerDoc()->AncestorOuterWindowIDs();
     MOZ_DIAGNOSTIC_ASSERT(mAncestorPrincipals.Length() == mAncestorOuterWindowIDs.Length());
     mDocumentHasUserInteracted = aLoadingContext->OwnerDoc()->UserHasInteracted();
@@ -327,16 +343,17 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* a
   , mTrackerBlockedReason(mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::all)
   , mForcePreflight(false)
   , mIsPreflight(false)
   , mLoadTriggeredFromExternal(false)
   , mServiceWorkerTaintingSynthesized(false)
   , mIsTracker(false)
   , mIsTrackerBlocked(false)
   , mDocumentHasUserInteracted(false)
+  , mDocumentHasLoaded(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) {
@@ -428,16 +445,17 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
   , mForcePreflight(rhs.mForcePreflight)
   , mIsPreflight(rhs.mIsPreflight)
   , mLoadTriggeredFromExternal(rhs.mLoadTriggeredFromExternal)
   // mServiceWorkerTaintingSynthesized must be handled specially during redirect
   , mServiceWorkerTaintingSynthesized(false)
   , mIsTracker(rhs.mIsTracker)
   , mIsTrackerBlocked(rhs.mIsTrackerBlocked)
   , mDocumentHasUserInteracted(rhs.mDocumentHasUserInteracted)
+  , mDocumentHasLoaded(rhs.mDocumentHasLoaded)
 {
 }
 
 LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
                    nsIPrincipal* aTriggeringPrincipal,
                    nsIPrincipal* aPrincipalToInherit,
                    nsIPrincipal* aSandboxedLoadingPrincipal,
                    nsIPrincipal* aTopLevelPrincipal,
@@ -474,17 +492,18 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
                    RedirectHistoryArray& aRedirectChain,
                    nsTArray<nsCOMPtr<nsIPrincipal>>&& aAncestorPrincipals,
                    const nsTArray<uint64_t>& aAncestorOuterWindowIDs,
                    const nsTArray<nsCString>& aCorsUnsafeHeaders,
                    bool aForcePreflight,
                    bool aIsPreflight,
                    bool aLoadTriggeredFromExternal,
                    bool aServiceWorkerTaintingSynthesized,
-                   bool aDocumentHasUserInteracted)
+                   bool aDocumentHasUserInteracted,
+                   bool aDocumentHasLoaded)
   : mLoadingPrincipal(aLoadingPrincipal)
   , mTriggeringPrincipal(aTriggeringPrincipal)
   , mPrincipalToInherit(aPrincipalToInherit)
   , mTopLevelPrincipal(aTopLevelPrincipal)
   , mTopLevelStorageAreaPrincipal(aTopLevelStorageAreaPrincipal)
   , mResultPrincipalURI(aResultPrincipalURI)
   , mClientInfo(aClientInfo)
   , mReservedClientInfo(aReservedClientInfo)
@@ -520,16 +539,17 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
   , mTrackerBlockedReason(mozilla::Telemetry::LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED::all)
   , mForcePreflight(aForcePreflight)
   , mIsPreflight(aIsPreflight)
   , mLoadTriggeredFromExternal(aLoadTriggeredFromExternal)
   , mServiceWorkerTaintingSynthesized(aServiceWorkerTaintingSynthesized)
   , mIsTracker(false)
   , mIsTrackerBlocked(false)
   , mDocumentHasUserInteracted(aDocumentHasUserInteracted)
+  , mDocumentHasLoaded(aDocumentHasLoaded)
 {
   // Only top level TYPE_DOCUMENT loads can have a null loadingPrincipal
   MOZ_ASSERT(mLoadingPrincipal || aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT);
   MOZ_ASSERT(mTriggeringPrincipal);
 
   mRedirectChainIncludingInternalRedirects.SwapElements(
     aRedirectChainIncludingInternalRedirects);
 
@@ -1403,16 +1423,31 @@ LoadInfo::GetDocumentHasUserInteracted(b
 NS_IMETHODIMP
 LoadInfo::SetDocumentHasUserInteracted(bool aDocumentHasUserInteracted)
 {
   mDocumentHasUserInteracted = aDocumentHasUserInteracted;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+LoadInfo::GetDocumentHasLoaded(bool *aDocumentHasLoaded)
+{
+  MOZ_ASSERT(aDocumentHasLoaded);
+  *aDocumentHasLoaded = mDocumentHasLoaded;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadInfo::SetDocumentHasLoaded(bool aDocumentHasLoaded)
+{
+  mDocumentHasLoaded = aDocumentHasLoaded;
+  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
@@ -131,17 +131,18 @@ private:
            RedirectHistoryArray& aRedirectChain,
            nsTArray<nsCOMPtr<nsIPrincipal>>&& aAncestorPrincipals,
            const nsTArray<uint64_t>& aAncestorOuterWindowIDs,
            const nsTArray<nsCString>& aUnsafeHeaders,
            bool aForcePreflight,
            bool aIsPreflight,
            bool aLoadTriggeredFromExternal,
            bool aServiceWorkerTaintingSynthesized,
-           bool aDocumentHasUserInteracted);
+           bool aDocumentHasUserInteracted,
+           bool aDocumentHasLoaded);
   LoadInfo(const LoadInfo& rhs);
 
   NS_IMETHOD GetRedirects(JSContext* aCx, JS::MutableHandle<JS::Value> aRedirects,
                           const RedirectHistoryArray& aArra);
 
   friend nsresult
   mozilla::ipc::LoadInfoArgsToLoadInfo(
     const mozilla::net::OptionalLoadInfoArgs& aLoadInfoArgs,
@@ -211,15 +212,16 @@ private:
   bool                             mForcePreflight;
   bool                             mIsPreflight;
   bool                             mLoadTriggeredFromExternal;
   bool                             mServiceWorkerTaintingSynthesized;
 
   bool                             mIsTracker;
   bool                             mIsTrackerBlocked;
   bool                             mDocumentHasUserInteracted;
+  bool                             mDocumentHasLoaded;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // mozilla_LoadInfo_h
 
--- a/netwerk/base/nsILoadInfo.idl
+++ b/netwerk/base/nsILoadInfo.idl
@@ -1061,9 +1061,15 @@ interface nsILoadInfo : nsISupports
   [infallible] attribute boolean isTracker;
   [infallible] attribute boolean isTrackerBlocked;
   attribute AnalyticsProvider trackerBlockedReason;
 
   /**
     * The top-level document has been user-interacted.
     */
   [infallible] attribute boolean documentHasUserInteracted;
+
+  /**
+    * This attribute represents whether the document to which this
+    * load belongs had finished loading when the load was initiated.
+    */
+  [infallible] attribute boolean documentHasLoaded;
 };
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -103,16 +103,17 @@ struct LoadInfoArgs
   OptionalIPCServiceWorkerDescriptor controller;
 
   nsCString[]                 corsUnsafeHeaders;
   bool                        forcePreflight;
   bool                        isPreflight;
   bool                        loadTriggeredFromExternal;
   bool                        serviceWorkerTaintingSynthesized;
   bool                        documentHasUserInteracted;
+  bool                        documentHasLoaded;
 };
 
 /**
  * Not every channel necessarily has a loadInfo attached.
  */
 union OptionalLoadInfoArgs
 {
   void_t;
@@ -145,16 +146,17 @@ struct ParentLoadInfoForwarderArgs
   // by the service worker.
   bool serviceWorkerTaintingSynthesized;
 
   // Tracker information, currently used by FastBlock
   bool isTracker;
   bool isTrackerBlocked;
   LABELS_DOCUMENT_ANALYTICS_TRACKER_FASTBLOCKED trackerBlockedReason;
   bool documentHasUserInteracted;
+  bool documentHasLoaded;
 
   // 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
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -697,17 +697,18 @@ IsContentPolicyTypeWhitelistedForFastBlo
   default:
     return false;
   }
 }
 
 bool
 nsHttpChannel::CheckFastBlocked()
 {
-    LOG(("nsHttpChannel::CheckFastBlocked [this=%p]\n", this));
+    LOG(("nsHttpChannel::CheckFastBlocked [this=%p, url=%s]",
+         this, mSpec.get()));
     MOZ_ASSERT(mIsThirdPartyTrackingResource);
 
     static bool sFastBlockInited = false;
     static uint32_t sFastBlockTimeout = 0;
     static uint32_t sFastBlockLimit = 0;
 
     if (!sFastBlockInited) {
         sFastBlockInited = true;
@@ -726,24 +727,29 @@ nsHttpChannel::CheckFastBlocked()
     if (NS_FAILED(GetNavigationStartTimeStamp(&timestamp)) || !timestamp) {
         LOG(("FastBlock passed (no timestamp) [this=%p]\n", this));
 
         return false;
     }
 
     bool engageFastBlock = false;
 
-    if (IsContentPolicyTypeWhitelistedForFastBlock(mLoadInfo) ||
-        // If the user has interacted with the document, we disable fastblock.
-        (mLoadInfo && mLoadInfo->GetDocumentHasUserInteracted())) {
-
-        LOG(("FastBlock passed (invalid) [this=%p]\n", this));
+    TimeDuration duration = TimeStamp::NowLoRes() - timestamp;
+    if (IsContentPolicyTypeWhitelistedForFastBlock(mLoadInfo)) {
+        LOG(("FastBlock passed (whitelisted content type %u) (%lf) [this=%p]\n",
+             mLoadInfo ? mLoadInfo->GetExternalContentPolicyType() : nsIContentPolicy::TYPE_OTHER,
+             duration.ToMilliseconds(), this));
+    } else if (mLoadInfo && mLoadInfo->GetDocumentHasUserInteracted()) {
+        LOG(("FastBlock passed (user interaction) (%lf) [this=%p]\n",
+             duration.ToMilliseconds(), this));
+    } else if (mLoadInfo && mLoadInfo->GetDocumentHasLoaded()) {
+        LOG(("FastBlock passed (document loaded) (%lf) [this=%p]\n",
+             duration.ToMilliseconds(), this));
     } else {
-        TimeDuration duration = TimeStamp::NowLoRes() - timestamp;
-        bool hasFastBlockStarted = duration.ToMilliseconds() >= sFastBlockTimeout;
+            bool hasFastBlockStarted = duration.ToMilliseconds() >= sFastBlockTimeout;
         bool hasFastBlockStopped = false;
         if ((sFastBlockLimit != 0) && (sFastBlockLimit > sFastBlockTimeout)) {
             hasFastBlockStopped = duration.ToMilliseconds() > sFastBlockLimit;
         }
         LOG(("FastBlock started=%d stopped=%d (%lf) [this=%p]\n",
              static_cast<int>(hasFastBlockStarted),
              static_cast<int>(hasFastBlockStopped),
              duration.ToMilliseconds(),
--- a/toolkit/components/url-classifier/tests/mochitest/fastblock.html
+++ b/toolkit/components/url-classifier/tests/mochitest/fastblock.html
@@ -2,16 +2,19 @@
 <html>
 <head>
   <title></title>
 </head>
 <body>
   <!-- Tracking iframe contains some trackers -->
   <iframe id="fastIFrame" data-touched="not sure" src="http://tracking.example.org/chrome/toolkit/components/url-classifier/tests/mochitest/fastblock_iframe.html" onload="this.dataset.touched='yes';" onerror="this.dataset.touched='no';"></iframe>
 
+  <!-- A slow tracking iframe containing some trackers -->
+  <iframe id="slowIFrame" data-touched="not sure" src="http://tracking.example.org/chrome/toolkit/components/url-classifier/tests/mochitest/fastblock_iframe.html?slow" onload="this.dataset.touched='yes';"></iframe>
+
   <!-- A fast tracker that redirects to become a slow tracker -->
   <script id="redirectScript"src="http://example.com/chrome/toolkit/components/url-classifier/tests/mochitest/redirect_tracker.sjs" onload="this.dataset.touched='yes';" onerror="this.dataset.touched='no';"></script>
 
   <!-- Tracking URL -->
   <script id="goodScript" data-touched="not sure" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/good.js" onload="this.dataset.touched='yes';" onerror="this.dataset.touched='no';"></script>
 
   <!-- Tracking Annotation -->
   <script id="fastScript" data-touched="not sure" src="http://tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" onload="this.dataset.touched='yes';" onerror="this.dataset.touched='no';"></script>
--- a/toolkit/components/url-classifier/tests/mochitest/fastblock_iframe.html
+++ b/toolkit/components/url-classifier/tests/mochitest/fastblock_iframe.html
@@ -1,18 +1,18 @@
 <!DOCTYPE html>
 <html>
 <head>
   <title></title>
 </head>
 <body>
 
   <!-- Tracking URL -->
-  <script id="goodIFrameScript" data-touched="not sure" src="http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/good.js" onload="this.dataset.touched='yes';" onerror="this.dataset.touched='no';"></script>
+  <script id="goodIFrameScript" data-touched="not sure" src="http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/good.js?iframe" onload="this.dataset.touched='yes';" onerror="this.dataset.touched='no';"></script>
 
   <!-- Tracking Annotation -->
-  <script id="fastIFrameScript" data-touched="not sure" src="http://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" onload="this.dataset.touched='yes';" onerror="this.dataset.touched='no';"></script>
+  <script id="fastIFrameScript" data-touched="not sure" src="http://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/evil.js?iframe" onload="this.dataset.touched='yes';" onerror="this.dataset.touched='no';"></script>
 
   <!-- Tracking Annotation -->
-  <script id="slowIFrameScript" data-touched="not sure" src="http://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js" onload="this.dataset.touched='yes';" onerror="this.dataset.touched='no';"></script>
+  <script id="slowIFrameScript" data-touched="not sure" src="http://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js?iframe" onload="this.dataset.touched='yes';" onerror="this.dataset.touched='no';"></script>
 
 </body>
 </html>
--- a/toolkit/components/url-classifier/tests/mochitest/test_fastblock_bug1477046.html
+++ b/toolkit/components/url-classifier/tests/mochitest/test_fastblock_bug1477046.html
@@ -14,27 +14,35 @@ https://bugzilla.mozilla.org/show_bug.cg
 | redirectScript   | Yes     | Yes       |
 -------------------+---------+------------
 | fastIFrame       | Yes     | No        |
 -------------------+---------+------------
 | goodIFrameScript | No      | N/A       |
 | fastIFrameScript | Yes     | No        |
 | slowIFrameScript | Yes     | Yes       |
 -------------------+---------+------------
-| badIFrame        | Yes     | Yes       |
+| slowIFrame       | Yes     | Yes       |
+-------------------+---------+------------
+| goodIFrameScript | No      | N/A       |
+| fastIFrameScript | Yes     | N/A       |
+| slowIFrameScript | Yes     | N/A       |
 -------------------+---------+------------
-| goodIFrameScript | N/A     | N/A       |
-| fastIFrameScript | N/A     | N/A       |
-| slowIFrameScript | N/A     | N/A       |
+|          (load event fires)            |
+-------------------+---------+------------
+| badIFrame        | Yes     | No        |
+-------------------+---------+------------
+| goodIFrameScript | No      | N/A       |
+| fastIFrameScript | Yes     | No        |
+| slowIFrameScript | Yes     | No        |
 -------------------+---------+------------
 | goodIFrame       | No      | N/A       |
 -------------------+---------+------------
 | goodIFrameScript | No      | N/A       |
-| fastIFrameScript | Yes     | Yes       |
-| slowIFrameScript | Yes     | Yes       |
+| fastIFrameScript | Yes     | No        |
+| slowIFrameScript | Yes     | No        |
 ------------------------------------------
 
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 1477046</title>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
@@ -68,19 +76,23 @@ const gValidHosts = [
   "itisatracker.org",
   "trackertest.org",
   "tracking.example.com",
   "tracking.example.org",
 ];
 const gSlowTrackers = [
   "http://tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js",
   "http://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js",
+  "http://tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js?iframe",
+  "http://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js?iframe",
+  "http://tracking.example.org/chrome/toolkit/components/url-classifier/tests/mochitest/fastblock_iframe.html?slow",
 ];
 const gRedirectTracker =
   "http://example.com/chrome/toolkit/components/url-classifier/tests/mochitest/redirect_tracker.sjs";
+
 const gInfiniteTimeout = 300000;
 const gDebug = false;
 
 function log(aMsg) {
   if (gDebug) {
     info("[FastBlock] " + aMsg + "\n");
   }
 }
@@ -359,69 +371,72 @@ async function testFastBlock(aWindow) {
   let browser = aWindow.gBrowser.selectedBrowser;
   let results = await ContentTask.spawn(browser, {}, () => {
     let iframe = content.document.getElementById("fastIFrame").contentDocument;
     return {
       goodScript: content.document.getElementById("goodScript").dataset.touched,
       fastScript: content.document.getElementById("fastScript").dataset.touched,
       slowScript: content.document.getElementById("slowScript").dataset.touched,
       fastIFrame: content.document.getElementById("fastIFrame").dataset.touched,
+      slowIFrame: content.document.getElementById("slowIFrame").dataset.touched,
       goodIFrameScript: iframe.getElementById("goodIFrameScript").dataset.touched,
       fastIFrameScript: iframe.getElementById("fastIFrameScript").dataset.touched,
       slowIFrameScript: iframe.getElementById("slowIFrameScript").dataset.touched,
       redirectScript: content.document.getElementById("redirectScript").dataset.touched,
       numTrackersFound: content.document.numTrackersFound,
       numTrackersBlocked: content.document.numTrackersBlocked,
     };
   });
 
   let { goodScript,
         fastScript,
         slowScript,
         fastIFrame,
+        slowIFrame,
         goodIFrameScript,
         fastIFrameScript,
         slowIFrameScript,
         redirectScript,
         numTrackersFound,
         numTrackersBlocked,
   } = results;
 
   is(goodScript, "yes", "is not a tracker");
   is(fastScript, "yes", "is a fast tracker");
   is(slowScript, "no", "is a blocked tracker");
   is(fastIFrame, "yes", "fast iframe loaded");
+  is(slowIFrame, "not sure", "slow tracking iframe blocked");
   is(goodIFrameScript, "yes", "is not a tracker");
   is(fastIFrameScript, "yes", "is a fast tracker");
   is(slowIFrameScript, "no", "is a blocked tracker");
   is(redirectScript, "no", "redirect to blocked tracker");
-  is(numTrackersFound, 6, "6 trackers found");
-  is(numTrackersBlocked, 3, "3 tracker blocked");
+  is(numTrackersFound, 7, "7 trackers found");
+  is(numTrackersBlocked, 4, "4 tracker blocked");
 
   let badIFrameLoaded = await addIFrame(aWindow, gBadIFramePage, "badIFrame");
-  ok(!badIFrameLoaded, "tracking iframe is blocked");
+  ok(badIFrameLoaded, "tracking iframe after load event is loaded");
 
   results = await ContentTask.spawn(browser, {}, () => {
     let iframe = content.document.getElementById("badIFrame").contentDocument;
     return {
       badIFrame: content.document.getElementById("badIFrame").dataset.touched,
-      goodIFrameScript: iframe.getElementById("goodIFrameScript"),
-      fastIFrameScript: iframe.getElementById("fastIFrameScript"),
-      slowIFrameScript: iframe.getElementById("slowIFrameScript"),
+      goodIFrameScript: iframe.getElementById("goodIFrameScript").dataset.touched,
+      fastIFrameScript: iframe.getElementById("fastIFrameScript").dataset.touched,
+      slowIFrameScript: iframe.getElementById("slowIFrameScript").dataset.touched,
       numTrackersFound: content.document.numTrackersFound,
       numTrackersBlocked: content.document.numTrackersBlocked,
     };
   });
 
-  is(results.badIFrame, "no", "bad iframe blocked");
-  ok(!results.goodIFrameScript, "iframe is not loaded");
-  ok(!results.fastIFrameScript, "iframe is not loaded");
-  ok(!results.slowIFrameScript, "iframe is not loaded");
+  is(results.badIFrame, "yes", "post-load tracking iframe loaded");
+  ok(results.goodIFrameScript, "yes", "is not a tracker");
+  ok(results.fastIFrameScript, "yes", "is a loaded tracker");
+  ok(results.slowIFrameScript, "yes", "is a loaded tracker");
 
-  is(results.numTrackersFound, 7, "7 trackers found");
+  is(results.numTrackersFound, 10, "10 trackers found");
   is(results.numTrackersBlocked, 4, "4 tracker blocked");
 
   let goodIFrameLoaded = await addIFrame(aWindow, gGoodIFramePage, "goodIFrame");
   ok(goodIFrameLoaded, "non tracking iframe is loaded");
 
   results = await ContentTask.spawn(browser, {}, () => {
     let iframe = content.document.getElementById("goodIFrame").contentDocument;
     return {
@@ -431,60 +446,63 @@ async function testFastBlock(aWindow) {
       slowIFrameScript: iframe.getElementById("slowIFrameScript").dataset.touched,
       numTrackersFound: content.document.numTrackersFound,
       numTrackersBlocked: content.document.numTrackersBlocked,
     };
   });
 
   is(results.goodIFrame, "yes", "non tracking iframe is loaded");
   is(results.goodIFrameScript, "yes", "is not a tracker");
-  is(results.fastIFrameScript, "no", "is a blocked tracker");
-  is(results.slowIFrameScript, "no", "is a blocked tracker");
+  is(results.fastIFrameScript, "yes", "is a loaded tracker");
+  is(results.slowIFrameScript, "yes", "is a loaded tracker");
 
-  is(results.numTrackersFound, 9, "9 trackers found");
-  is(results.numTrackersBlocked, 6, "6 tracker blocked");
+  is(results.numTrackersFound, 12, "12 trackers found");
+  is(results.numTrackersBlocked, 4, "4 tracker blocked");
 }
 
 async function testNoFastBlock(aWindow) {
   log("testNoFastBlock");
   let browser = aWindow.gBrowser.selectedBrowser;
   let results = await ContentTask.spawn(browser, {}, () => {
     let iframe = content.document.getElementById("fastIFrame").contentDocument;
     return {
       goodScript: content.document.getElementById("goodScript").dataset.touched,
       fastScript: content.document.getElementById("fastScript").dataset.touched,
       slowScript: content.document.getElementById("slowScript").dataset.touched,
       fastIFrame: content.document.getElementById("fastIFrame").dataset.touched,
+      slowIFrame: content.document.getElementById("slowIFrame").dataset.touched,
       goodIFrameScript: iframe.getElementById("goodIFrameScript").dataset.touched,
       fastIFrameScript: iframe.getElementById("fastIFrameScript").dataset.touched,
       slowIFrameScript: iframe.getElementById("slowIFrameScript").dataset.touched,
       numTrackersFound: content.document.numTrackersFound,
       numTrackersBlocked: content.document.numTrackersBlocked,
     };
   });
 
   let { goodScript,
         fastScript,
         slowScript,
         fastIFrame,
+        slowIFrame,
         goodIFrameScript,
         fastIFrameScript,
         slowIFrameScript,
         numTrackersFound,
         numTrackersBlocked,
   } = results;
 
   is(goodScript, "yes", "is not a tracker");
   is(fastScript, "yes", "FastBlock is disabled");
   is(slowScript, "yes", "FastBlock is disabled");
   is(fastIFrame, "yes", "fast iframe loaded");
+  is(slowIFrame, "yes", "FastBlock is disabled");
   is(goodIFrameScript, "yes", "is not a tracker");
   is(fastIFrameScript, "yes", "FastBlock is disabled");
   is(slowIFrameScript, "yes", "FastBlock is disabled");
-  is(numTrackersFound, 6, "6 trackers found");
+  is(numTrackersFound, 7, "7 trackers found");
   is(numTrackersBlocked, 0, "no tracker blocked");
 
   let iframeLoaded = await addIFrame(aWindow, gBadIFramePage, "badIFrame");
   ok(iframeLoaded, "tracking iframe is loaded");
 
   results = await ContentTask.spawn(browser, log, (LOG) => {
     let iframe = content.document.getElementById("badIFrame").contentDocument;
     return {
@@ -497,17 +515,17 @@ async function testNoFastBlock(aWindow) 
     };
   });
 
   is(results.badIFrame, "yes", "FastBlock is disabled");
   is(results.goodIFrameScript, "yes", "is not a tracker");
   is(results.fastIFrameScript, "yes", "FastBlock is disabled");
   is(results.slowIFrameScript, "yes", "FastBlock is disabled");
 
-  is(results.numTrackersFound, 9, "9 trackers found");
+  is(results.numTrackersFound, 10, "10 trackers found");
   is(results.numTrackersBlocked, 0, "0 tracker blocked");
 
   let goodIFrameLoaded = await addIFrame(aWindow, gGoodIFramePage, "goodIFrame");
   ok(goodIFrameLoaded, "non tracking iframe is loaded");
 
   results = await ContentTask.spawn(browser, {}, () => {
     let iframe = content.document.getElementById("goodIFrame").contentDocument;
     return {
@@ -520,17 +538,17 @@ async function testNoFastBlock(aWindow) 
     };
   });
 
   is(results.goodIFrame, "yes", "non tracking iframe is loaded");
   is(results.goodIFrameScript, "yes", "is not a tracker");
   is(results.fastIFrameScript, "yes", "FastBlock is disabled");
   is(results.slowIFrameScript, "yes", "FastBlock is disabled");
 
-  is(results.numTrackersFound, 11, "11 trackers found");
+  is(results.numTrackersFound, 12, "12 trackers found");
   is(results.numTrackersBlocked, 0, "0 tracker blocked");
 }
 
 async function testPrefsSwitch() {
   // FastBlock ON
   await runTest({
     "set": [
       ["browser.contentblocking.enabled", true],