Bug 1493563 - Part 2: Record a log of content blocking actions on each top-level document; r=baku
☠☠ backed out by 1681bd622c63 ☠ ☠
authorEhsan Akhgari <ehsan@mozilla.com>
Sat, 22 Sep 2018 17:04:38 -0400
changeset 487069 a1ff0cd6256391045da570fd8f48eeaf27b6c443
parent 487068 932b41e211e04744ced36241c7469356f552a0bd
child 487070 c8ab17addb7a545f3e16588ef373a3dc496b78b5
push id246
push userfmarier@mozilla.com
push dateSat, 13 Oct 2018 00:15:40 +0000
reviewersbaku
bugs1493563
milestone64.0a1
Bug 1493563 - Part 2: Record a log of content blocking actions on each top-level document; r=baku Differential Revision: https://phabricator.services.mozilla.com/D6592
dom/base/ContentBlockingLog.h
dom/base/moz.build
dom/base/nsGlobalWindowOuter.cpp
dom/base/nsGlobalWindowOuter.h
dom/base/nsIDocument.h
dom/base/nsPIDOMWindow.h
toolkit/components/antitracking/AntiTrackingCommon.cpp
new file mode 100644
--- /dev/null
+++ b/dom/base/ContentBlockingLog.h
@@ -0,0 +1,53 @@
+/* -*- 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_dom_ContentBlockingLog_h
+#define mozilla_dom_ContentBlockingLog_h
+
+#include "mozilla/Pair.h"
+#include "mozilla/UniquePtr.h"
+#include "nsClassHashtable.h"
+#include "nsHashKeys.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace dom {
+
+class ContentBlockingLog final
+{
+  // Each element is a pair of (type, blocked). The type values come from the
+  // blocking types defined in nsIWebProgressListener.
+  typedef nsTArray<mozilla::Pair<uint32_t, bool>> OriginLog;
+
+public:
+  ContentBlockingLog() = default;
+  ~ContentBlockingLog() = default;
+
+  void RecordLog(const nsAString& aOrigin, uint32_t aType, bool aBlocked)
+  {
+    if (aOrigin.IsVoid()) {
+      return;
+    }
+    auto entry = mLog.LookupForAdd(aOrigin);
+    if (entry) {
+      entry.Data()->AppendElement(mozilla::MakePair(aType, aBlocked));
+    } else {
+      entry.OrInsert([=] {
+        auto log(MakeUnique<OriginLog>());
+        log->AppendElement(mozilla::MakePair(aType, aBlocked));
+        return log.release();
+      });
+    }
+  }
+
+private:
+  nsClassHashtable<nsStringHashKey, OriginLog> mLog;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -148,16 +148,17 @@ EXPORTS.mozilla.dom += [
     'CharacterData.h',
     'ChildIterator.h',
     'ChildProcessMessageManager.h',
     'ChromeMessageBroadcaster.h',
     'ChromeMessageSender.h',
     'ChromeNodeList.h',
     'ChromeUtils.h',
     'Comment.h',
+    'ContentBlockingLog.h',
     'ContentFrameMessageManager.h',
     'ContentProcessMessageManager.h',
     'CustomElementRegistry.h',
     'DirectionalityUtils.h',
     'DispatcherTrait.h',
     'DocGroup.h',
     'DocumentFragment.h',
     'DocumentOrShadowRoot.h',
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -5276,17 +5276,18 @@ nsGlobalWindowOuter::FirePopupBlockedEve
 
   event->SetTrusted(true);
 
   aDoc->DispatchEvent(*event);
 }
 
 void
 nsGlobalWindowOuter::NotifyContentBlockingState(unsigned aState,
-                                                nsIChannel* aChannel)
+                                                nsIChannel* aChannel,
+                                                nsIURI* aURIHint)
 {
   nsCOMPtr<nsIDocShell> docShell = GetDocShell();
   if (!docShell) {
     return;
   }
   nsCOMPtr<nsIDocument> doc = docShell->GetDocument();
   NS_ENSURE_TRUE_VOID(doc);
 
@@ -5305,28 +5306,33 @@ nsGlobalWindowOuter::NotifyContentBlocki
   NS_ENSURE_SUCCESS_VOID(rv);
   uint32_t state = 0;
   nsCOMPtr<nsISecureBrowserUI> securityUI;
   docShell->GetSecurityUI(getter_AddRefs(securityUI));
   if (!securityUI) {
     return;
   }
   securityUI->GetState(&state);
+  nsAutoString origin;
+  origin.SetIsVoid(true);
+  if (aURIHint) {
+    nsContentUtils::GetUTFOrigin(aURIHint, origin);
+  }
   if (aState == nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT) {
-    doc->SetHasTrackingContentBlocked(true);
+    doc->SetHasTrackingContentBlocked(true, origin);
   } else if (aState == nsIWebProgressListener::STATE_BLOCKED_SLOW_TRACKING_CONTENT) {
-    doc->SetHasSlowTrackingContentBlocked(true);
+    doc->SetHasSlowTrackingContentBlocked(true, origin);
   } else if (aState == nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION) {
-    doc->SetHasCookiesBlockedByPermission(true);
+    doc->SetHasCookiesBlockedByPermission(true, origin);
   } else if (aState == nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER) {
-    doc->SetHasTrackingCookiesBlocked(true);
+    doc->SetHasTrackingCookiesBlocked(true, origin);
   } else if (aState == nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL) {
-    doc->SetHasAllCookiesBlocked(true);
+    doc->SetHasAllCookiesBlocked(true, origin);
   } else if (aState == nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN) {
-    doc->SetHasForeignCookiesBlocked(true);
+    doc->SetHasForeignCookiesBlocked(true, origin);
   } else {
     // Ignore nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT;
   }
   state |= aState;
 
   eventSink->OnSecurityChange(aChannel, state);
 }
 
--- a/dom/base/nsGlobalWindowOuter.h
+++ b/dom/base/nsGlobalWindowOuter.h
@@ -479,17 +479,18 @@ public:
   virtual void
   FirePopupBlockedEvent(nsIDocument* aDoc,
                         nsIURI* aPopupURI,
                         const nsAString& aPopupWindowName,
                         const nsAString& aPopupWindowFeatures) override;
 
   virtual void
   NotifyContentBlockingState(unsigned aState,
-                             nsIChannel* aChannel) override;
+                             nsIChannel* aChannel,
+                             nsIURI* aURIHint) override;
 
   virtual uint32_t GetSerial() override {
     return mSerial;
   }
 
   void AddSizeOfIncludingThis(nsWindowSizes& aWindowSizes) const;
 
   void AllowScriptsToClose()
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2,16 +2,17 @@
 /* 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 nsIDocument_h___
 #define nsIDocument_h___
 
 #include "mozilla/FlushType.h"           // for enum
+#include "mozilla/Pair.h"                // for Pair
 #include "nsAutoPtr.h"                   // for member
 #include "nsCOMArray.h"                  // for member
 #include "nsCompatibility.h"             // for member
 #include "nsCOMPtr.h"                    // for member
 #include "nsGkAtoms.h"                   // for static class members
 #include "nsIApplicationCache.h"
 #include "nsIApplicationCacheContainer.h"
 #include "nsIContentViewer.h"
@@ -24,30 +25,32 @@
 #include "nsIPresShell.h"
 #include "nsIChannelEventSink.h"
 #include "nsIProgressEventSink.h"
 #include "nsISecurityEventSink.h"
 #include "nsIScriptGlobalObject.h"       // for member (in nsCOMPtr)
 #include "nsIServiceManager.h"
 #include "nsIURI.h"                      // for use in inline functions
 #include "nsIUUIDGenerator.h"
+#include "nsIWebProgressListener.h"      // for nsIWebProgressListener
 #include "nsPIDOMWindow.h"               // for use in inline functions
 #include "nsPropertyTable.h"             // for member
 #include "nsStringFwd.h"
 #include "nsTHashtable.h"                // for member
 #include "nsURIHashKey.h"
 #include "mozilla/net/ReferrerPolicy.h"  // for member
 #include "nsWeakReference.h"
 #include "mozilla/UseCounter.h"
 #include "mozilla/WeakPtr.h"
 #include "Units.h"
 #include "nsContentListDeclarations.h"
 #include "nsExpirationTracker.h"
 #include "nsClassHashtable.h"
 #include "mozilla/CORSMode.h"
+#include "mozilla/dom/ContentBlockingLog.h"
 #include "mozilla/dom/DispatcherTrait.h"
 #include "mozilla/dom/DocumentOrShadowRoot.h"
 #include "mozilla/EnumSet.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/NotNull.h"
 #include "mozilla/SegmentedVector.h"
 #include "mozilla/ServoBindingTypes.h"
 #include "mozilla/StyleSheet.h"
@@ -1022,59 +1025,83 @@ public:
   bool GetHasCookiesBlockedByPermission()
   {
     return mHasCookiesBlockedByPermission;
   }
 
   /**
    * Set the tracking content blocked flag for this document.
    */
-  void SetHasTrackingContentBlocked(bool aHasTrackingContentBlocked)
+  void SetHasTrackingContentBlocked(bool aHasTrackingContentBlocked,
+                                    const nsAString& aOriginBlocked)
   {
     mHasTrackingContentBlocked = aHasTrackingContentBlocked;
+    RecordContentBlockingLog(aOriginBlocked,
+                             nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT,
+                             aHasTrackingContentBlocked);
   }
 
   /**
    * Set the slow tracking content blocked flag for this document.
    */
-  void SetHasSlowTrackingContentBlocked(bool aHasSlowTrackingContentBlocked)
+  void SetHasSlowTrackingContentBlocked(bool aHasSlowTrackingContentBlocked,
+                                        const nsAString& aOriginBlocked)
   {
     mHasSlowTrackingContentBlocked = aHasSlowTrackingContentBlocked;
+    RecordContentBlockingLog(aOriginBlocked,
+                             nsIWebProgressListener::STATE_BLOCKED_SLOW_TRACKING_CONTENT,
+                             aHasSlowTrackingContentBlocked);
   }
 
   /**
    * Set the all cookies blocked flag for this document.
    */
-  void SetHasAllCookiesBlocked(bool aHasAllCookiesBlocked)
+  void SetHasAllCookiesBlocked(bool aHasAllCookiesBlocked,
+                               const nsAString& aOriginBlocked)
   {
     mHasAllCookiesBlocked = aHasAllCookiesBlocked;
+    RecordContentBlockingLog(aOriginBlocked,
+                             nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL,
+                             aHasAllCookiesBlocked);
   }
 
   /**
    * Set the tracking cookies blocked flag for this document.
    */
-  void SetHasTrackingCookiesBlocked(bool aHasTrackingCookiesBlocked)
+  void SetHasTrackingCookiesBlocked(bool aHasTrackingCookiesBlocked,
+                                    const nsAString& aOriginBlocked)
   {
     mHasTrackingCookiesBlocked = aHasTrackingCookiesBlocked;
+    RecordContentBlockingLog(aOriginBlocked,
+                             nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER,
+                             aHasTrackingCookiesBlocked);
   }
 
   /**
    * Set the third-party cookies blocked flag for this document.
    */
-  void SetHasForeignCookiesBlocked(bool aHasForeignCookiesBlocked)
+  void SetHasForeignCookiesBlocked(bool aHasForeignCookiesBlocked,
+                                   const nsAString& aOriginBlocked)
   {
     mHasForeignCookiesBlocked = aHasForeignCookiesBlocked;
+    RecordContentBlockingLog(aOriginBlocked,
+                             nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN,
+                             aHasForeignCookiesBlocked);
   }
 
   /**
    * Set the cookies blocked by site permission flag for this document.
    */
-  void SetHasCookiesBlockedByPermission(bool aHasCookiesBlockedByPermission)
+  void SetHasCookiesBlockedByPermission(bool aHasCookiesBlockedByPermission,
+                                        const nsAString& aOriginBlocked)
   {
     mHasCookiesBlockedByPermission = aHasCookiesBlockedByPermission;
+    RecordContentBlockingLog(aOriginBlocked,
+                             nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION,
+                             aHasCookiesBlockedByPermission);
   }
 
   /**
    * Get tracking content loaded flag for this document.
    */
   bool GetHasTrackingContentLoaded()
   {
     return mHasTrackingContentLoaded;
@@ -3845,16 +3872,22 @@ protected:
   void NotifyStyleSheetApplicableStateChanged();
   // Just like EnableStyleSheetsForSet, but doesn't check whether
   // aSheetSet is null and allows the caller to control whether to set
   // aSheetSet as the preferred set in the CSSLoader.
   void EnableStyleSheetsForSetInternal(const nsAString& aSheetSet,
                                        bool aUpdateCSSLoader);
 
 private:
+  void RecordContentBlockingLog(const nsAString& aOrigin,
+                                uint32_t aType, bool aBlocked)
+  {
+    mContentBlockingLog.RecordLog(aOrigin, aType, aBlocked);
+  }
+
   mutable std::bitset<eDeprecatedOperationCount> mDeprecationWarnedAbout;
   mutable std::bitset<eDocumentWarningCount> mDocWarningWarnedAbout;
 
   // Lazy-initialization to have mDocGroup initialized in prior to the
   // SelectorCaches.
   mozilla::UniquePtr<SelectorCache> mSelectorCache;
 
 protected:
@@ -4514,16 +4547,21 @@ protected:
 
   RefPtr<mozilla::dom::DocGroup> mDocGroup;
 
   // The set of all the tracking script URLs.  URLs are added to this set by
   // calling NoteScriptTrackingStatus().  Currently we assume that a URL not
   // existing in the set means the corresponding script isn't a tracking script.
   nsTHashtable<nsCStringHashKey> mTrackingScripts;
 
+  // The log of all content blocking actions taken on this document.  This is only
+  // stored on top-level documents and includes the activity log for all of the
+  // nested subdocuments as well.
+  mozilla::dom::ContentBlockingLog mContentBlockingLog;
+
   // List of ancestor principals.  This is set at the point a document
   // is connected to a docshell and not mutated thereafter.
   nsTArray<nsCOMPtr<nsIPrincipal>> mAncestorPrincipals;
   // List of ancestor outerWindowIDs that correspond to the ancestor principals.
   nsTArray<uint64_t> mAncestorOuterWindowIDs;
 
   // Pointer to our parser if we're currently in the process of being
   // parsed into.
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -1089,17 +1089,18 @@ public:
   virtual void
   FirePopupBlockedEvent(nsIDocument* aDoc,
                         nsIURI* aPopupURI,
                         const nsAString& aPopupWindowName,
                         const nsAString& aPopupWindowFeatures) = 0;
 
   virtual void
   NotifyContentBlockingState(unsigned aState,
-                             nsIChannel* aChannel) = 0;
+                             nsIChannel* aChannel,
+                             nsIURI* aURIHint = nullptr) = 0;
 
   // WebIDL-ish APIs
   void MarkUncollectableForCCGeneration(uint32_t aGeneration)
   {
     mMarkedCCGeneration = aGeneration;
   }
 
   uint32_t GetMarkedCCGeneration()
--- a/toolkit/components/antitracking/AntiTrackingCommon.cpp
+++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp
@@ -1083,17 +1083,17 @@ AntiTrackingCommon::NotifyRejection(nsIC
   nsCOMPtr<nsPIDOMWindowOuter> pwin = nsPIDOMWindowOuter::From(win);
   if (!pwin) {
     return;
   }
 
   nsCOMPtr<nsIURI> uri;
   aChannel->GetURI(getter_AddRefs(uri));
 
-  pwin->NotifyContentBlockingState(aRejectedReason, aChannel);
+  pwin->NotifyContentBlockingState(aRejectedReason, aChannel, uri);
 
   ReportBlockingToConsole(pwin, uri, aRejectedReason);
 }
 
 /* static */ void
 AntiTrackingCommon::NotifyRejection(nsPIDOMWindowInner* aWindow,
                                     uint32_t aRejectedReason)
 {
@@ -1122,17 +1122,17 @@ AntiTrackingCommon::NotifyRejection(nsPI
 
   if (!pwin) {
     return;
   }
 
   nsCOMPtr<nsIURI> uri;
   channel->GetURI(getter_AddRefs(uri));
 
-  pwin->NotifyContentBlockingState(aRejectedReason, channel);
+  pwin->NotifyContentBlockingState(aRejectedReason, channel, uri);
 
   ReportBlockingToConsole(pwin, uri, aRejectedReason);
 }
 
 /* static */ void
 AntiTrackingCommon::StoreUserInteractionFor(nsIPrincipal* aPrincipal)
 {
   if (XRE_IsParentProcess()) {