Bug 1493563 - Part 5: Present the old state and the content blocking log to the web progress listeners; r=baku
☠☠ backed out by fe88b8c39160 ☠ ☠
authorEhsan Akhgari <ehsan@mozilla.com>
Sat, 22 Sep 2018 20:29:07 -0400
changeset 494000 dbba8451aab146378145ee6712d65d6542874c64
parent 493999 a6141dbd032846c012b6ad956424427dfe1f1166
child 494001 7a8a0d1cfd696888be5941d3902e757f2c8bd1a5
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1493563
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 1493563 - Part 5: Present the old state and the content blocking log to the web progress listeners; r=baku Differential Revision: https://phabricator.services.mozilla.com/D6595
accessible/base/DocManager.cpp
browser/base/content/browser-contentblocking.js
browser/base/content/browser.js
browser/base/content/tabbrowser.js
browser/base/content/test/general/browser_alltabslistener.js
browser/components/extensions/parent/ext-tabs.js
browser/components/shell/nsMacShellService.cpp
docshell/base/nsDocShell.cpp
docshell/base/nsDocShellTreeOwner.cpp
dom/base/ContentBlockingLog.h
dom/browser-element/BrowserElementChildPreload.js
dom/clients/manager/ClientNavigateOpChild.cpp
dom/clients/manager/ClientOpenWindowUtils.cpp
dom/html/HTMLFormElement.cpp
dom/html/nsHTMLDNSPrefetch.cpp
dom/ipc/TabParent.cpp
dom/presentation/PresentationCallbacks.cpp
editor/composer/nsEditingSession.cpp
editor/composer/test/test_bug434998.xul
editor/libeditor/tests/test_bug607584.xul
editor/libeditor/tests/test_bug616590.xul
editor/libeditor/tests/test_bug780908.xul
layout/base/tests/chrome/printpreview_bug396024_helper.xul
layout/base/tests/chrome/printpreview_bug482976_helper.xul
layout/base/tests/chrome/printpreview_helper.xul
layout/printing/ipc/RemotePrintJobChild.cpp
layout/printing/nsPrintJob.cpp
layout/tools/layout-debug/ui/content/layoutdebug.js
mobile/android/chrome/content/browser.js
mobile/android/modules/geckoview/GeckoViewProgress.jsm
mobile/android/modules/geckoview/GeckoViewTrackingProtection.jsm
netwerk/base/nsISecureBrowserUI.idl
security/manager/ssl/nsSecureBrowserUIImpl.cpp
toolkit/actors/PrintingChild.jsm
toolkit/components/antitracking/test/browser/head.js
toolkit/components/browser/nsWebBrowser.cpp
toolkit/components/printing/content/printPreviewProgress.js
toolkit/components/printing/content/printProgress.js
toolkit/components/printingui/ipc/PrintProgressDialogChild.cpp
toolkit/components/printingui/nsPrintProgress.cpp
toolkit/components/printingui/nsPrintingPromptService.cpp
toolkit/components/statusfilter/nsBrowserStatusFilter.cpp
toolkit/components/url-classifier/tests/mochitest/test_threathit_report.html
toolkit/modules/RemoteSecurityUI.jsm
toolkit/modules/RemoteWebProgress.jsm
toolkit/modules/WebProgressChild.jsm
toolkit/mozapps/downloads/nsHelperAppDlg.js
toolkit/mozapps/extensions/content/extensions.js
uriloader/base/nsDocLoader.cpp
uriloader/base/nsIWebProgressListener.idl
uriloader/prefetch/nsOfflineCacheUpdateService.cpp
uriloader/prefetch/nsPrefetchService.cpp
xpfe/appshell/nsChromeTreeOwner.cpp
xpfe/appshell/nsWebShellWindow.cpp
--- a/accessible/base/DocManager.cpp
+++ b/accessible/base/DocManager.cpp
@@ -339,17 +339,19 @@ DocManager::OnStatusChange(nsIWebProgres
 {
   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DocManager::OnSecurityChange(nsIWebProgress* aWebProgress,
                              nsIRequest* aRequest,
-                             uint32_t aState)
+                             uint32_t aOldState,
+                             uint32_t aState,
+                             const nsAString& aContentBlockingLogJSON)
 {
   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsIDOMEventListener
 
--- a/browser/base/content/browser-contentblocking.js
+++ b/browser/base/content/browser-contentblocking.js
@@ -459,17 +459,18 @@ var ContentBlocking = {
 
   shieldHistogramAdd(value) {
     if (PrivateBrowsingUtils.isWindowPrivate(window)) {
       return;
     }
     Services.telemetry.getHistogramById("TRACKING_PROTECTION_SHIELD").add(value);
   },
 
-  onSecurityChange(state, webProgress, isSimulated) {
+  onSecurityChange(oldState, state, webProgress, isSimulated,
+                   contentBlockingLogJSON) {
     let baseURI = this._baseURIForChannelClassifier;
 
     // Don't deal with about:, file: etc.
     if (!baseURI) {
       this.iconBox.removeAttribute("animate");
       this.iconBox.removeAttribute("active");
       this.iconBox.removeAttribute("hasException");
       return;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4982,17 +4982,18 @@ var XULBrowserWindow = {
   _lastLocation: null,
 
   // This is called in multiple ways:
   //  1. Due to the nsIWebProgressListener.onSecurityChange notification.
   //  2. Called by tabbrowser.xml when updating the current browser.
   //  3. Called directly during this object's initializations.
   // aRequest will be null always in case 2 and 3, and sometimes in case 1 (for
   // instance, there won't be a request when STATE_BLOCKED_TRACKING_CONTENT is observed).
-  onSecurityChange(aWebProgress, aRequest, aState, aIsSimulated) {
+  onSecurityChange(aWebProgress, aRequest, aOldState, aState,
+                   aContentBlockingLogJSON, aIsSimulated) {
     // Don't need to do anything if the data we use to update the UI hasn't
     // changed
     let uri = gBrowser.currentURI;
     let spec = uri.spec;
     if (this._state == aState &&
         this._lastLocation == spec) {
       // Switching to a tab of the same URL doesn't change most security
       // information, but tab specific permissions may be different.
@@ -5009,17 +5010,18 @@ var XULBrowserWindow = {
     // Make sure the "https" part of the URL is striked out or not,
     // depending on the current mixed active content blocking state.
     gURLBar.formatValue();
 
     try {
       uri = Services.uriFixup.createExposableURI(uri);
     } catch (e) {}
     gIdentityHandler.updateIdentity(this._state, uri);
-    ContentBlocking.onSecurityChange(this._state, aWebProgress, aIsSimulated);
+    ContentBlocking.onSecurityChange(aOldState, this._state, aWebProgress, aIsSimulated,
+                                     aContentBlockingLogJSON);
   },
 
   // simulate all change notifications after switching tabs
   onUpdateCurrentBrowser: function XWB_onUpdateCurrentBrowser(aStateFlags, aStatus, aMessage, aTotalProgress) {
     if (FullZoom.updateBackgroundTabs)
       FullZoom.onLocationChange(gBrowser.currentURI, true);
 
     CombinedStopReload.onTabSwitch();
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -939,18 +939,23 @@ window._gBrowser = {
     this._callProgressListeners(null, "onLocationChange",
                                 [webProgress, null, newBrowser.currentURI, 0, true],
                                 true, false);
 
     let securityUI = newBrowser.securityUI;
     if (securityUI) {
       // Include the true final argument to indicate that this event is
       // simulated (instead of being observed by the webProgressListener).
+      // Note: check state first to make sure the security UI object updates its
+      // state from the docshell correctly.
+      let state = securityUI.state;
+      let oldState = securityUI.oldState;
       this._callProgressListeners(null, "onSecurityChange",
-                                  [webProgress, null, securityUI.state, true],
+                                  [webProgress, null, oldState, state,
+                                   securityUI.contentBlockingLogJSON, true],
                                   true, false);
     }
 
     let listener = this._tabListeners.get(newTab);
     if (listener && listener.mStateFlags) {
       this._callProgressListeners(null, "onUpdateCurrentBrowser",
                                   [listener.mStateFlags, listener.mStatus,
                                    listener.mMessage, listener.mTotalProgress],
@@ -1660,22 +1665,27 @@ window._gBrowser = {
     this._tabListeners.set(tab, listener);
     filter.addProgressListener(listener, Ci.nsIWebProgress.NOTIFY_ALL);
 
     // Restore the progress listener.
     aBrowser.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
 
     // Restore the securityUI state.
     let securityUI = aBrowser.securityUI;
+    // Make sure to call the state getter before the oldState getter to give
+    // the securityUI object a chance to sync its state with the docshell
     let state = securityUI ? securityUI.state :
       Ci.nsIWebProgressListener.STATE_IS_INSECURE;
+    let oldState = securityUI ? securityUI.oldState :
+      Ci.nsIWebProgressListener.STATE_IS_INSECURE;
     // Include the true final argument to indicate that this event is
     // simulated (instead of being observed by the webProgressListener).
     this._callProgressListeners(aBrowser, "onSecurityChange",
-                                [aBrowser.webProgress, null, state, true],
+                                [aBrowser.webProgress, null, oldState, state,
+                                 securityUI.contentBlockingLogJSON, true],
                                 true, false);
 
     if (aShouldBeRemote) {
       // Switching the browser to be remote will connect to a new child
       // process so the browser can no longer be considered to be
       // crashed.
       tab.removeAttribute("crashed");
     } else {
@@ -5055,19 +5065,20 @@ class TabProgressListener {
       return;
 
     this._callProgressListeners("onStatusChange",
                                 [aWebProgress, aRequest, aStatus, aMessage]);
 
     this.mMessage = aMessage;
   }
 
-  onSecurityChange(aWebProgress, aRequest, aState) {
+  onSecurityChange(aWebProgress, aRequest, aOldState, aState, aContentBlockingLogJSON) {
     this._callProgressListeners("onSecurityChange",
-                                [aWebProgress, aRequest, aState]);
+                                [aWebProgress, aRequest, aOldState, aState,
+                                 aContentBlockingLogJSON]);
   }
 
   onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
     return this._callProgressListeners("onRefreshAttempted",
                                        [aWebProgress, aURI, aDelay, aSameURI]);
   }
 }
 TabProgressListener.prototype.QueryInterface = ChromeUtils.generateQI(
--- a/browser/base/content/test/general/browser_alltabslistener.js
+++ b/browser/base/content/test/general/browser_alltabslistener.js
@@ -21,17 +21,18 @@ var gFrontProgressListener = {
     ok(gFrontNotificationsPos < gFrontNotifications.length, "Got an expected notification for the front notifications listener");
     is(state, gFrontNotifications[gFrontNotificationsPos], "Got a notification for the front notifications listener");
     gFrontNotificationsPos++;
   },
 
   onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
   },
 
-  onSecurityChange(aWebProgress, aRequest, aState) {
+  onSecurityChange(aWebProgress, aRequest, aOldState, aState,
+                   aContentBlockingLogJSON) {
     var state = "onSecurityChange";
     info("FrontProgress: " + state + " 0x" + aState.toString(16));
     ok(gFrontNotificationsPos < gFrontNotifications.length, "Got an expected notification for the front notifications listener");
     is(state, gFrontNotifications[gFrontNotificationsPos], "Got a notification for the front notifications listener");
     gFrontNotificationsPos++;
   },
 };
 
@@ -61,17 +62,18 @@ var gAllProgressListener = {
     gAllNotificationsPos++;
   },
 
   onStatusChange(aBrowser, aWebProgress, aRequest, aStatus, aMessage) {
     var state = "onStatusChange";
     ok(aBrowser == gTestBrowser, state + " notification came from the correct browser");
   },
 
-  onSecurityChange(aBrowser, aWebProgress, aRequest, aState) {
+  onSecurityChange(aBrowser, aWebProgress, aRequest, aOldState, aState,
+                   aContentBlockingLogJSON) {
     var state = "onSecurityChange";
     info("AllProgress: " + state + " 0x" + aState.toString(16));
     ok(aBrowser == gTestBrowser, state + " notification came from the correct browser");
     ok(gAllNotificationsPos < gAllNotifications.length, "Got an expected notification for the all notifications listener");
     is(state, gAllNotifications[gAllNotificationsPos], "Got a notification for the all notifications listener");
     gAllNotificationsPos++;
   },
 };
--- a/browser/components/extensions/parent/ext-tabs.js
+++ b/browser/components/extensions/parent/ext-tabs.js
@@ -1196,17 +1196,18 @@ this.tabs = class extends ExtensionAPI {
                 }
                 if (pageSettings.footerRight !== null) {
                   printSettings.footerStrRight = pageSettings.footerRight;
                 }
 
                 let printProgressListener = {
                   onLocationChange(webProgress, request, location, flags) { },
                   onProgressChange(webProgress, request, curSelfProgress, maxSelfProgress, curTotalProgress, maxTotalProgress) { },
-                  onSecurityChange(webProgress, request, state) { },
+                  onSecurityChange(webProgress, request, oldState, state,
+                                   contentBlockingLogJSON) { },
                   onStateChange(webProgress, request, flags, status) {
                     if ((flags & Ci.nsIWebProgressListener.STATE_STOP) && (flags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT)) {
                       resolve(retval == 0 ? "saved" : "replaced");
                     }
                   },
                   onStatusChange: function(webProgress, request, status, message) {
                     if (status != 0) {
                       resolve(retval == 0 ? "not_saved" : "not_replaced");
--- a/browser/components/shell/nsMacShellService.cpp
+++ b/browser/components/shell/nsMacShellService.cpp
@@ -186,17 +186,19 @@ nsMacShellService::OnStatusChange(nsIWeb
                                   const char16_t* aMessage)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsMacShellService::OnSecurityChange(nsIWebProgress* aWebProgress,
                                     nsIRequest* aRequest,
-                                    uint32_t aState)
+                                    uint32_t aOldState,
+                                    uint32_t aState,
+                                    const nsAString& aContentBlockingLogJSON)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsMacShellService::OnStateChange(nsIWebProgress* aWebProgress,
                                  nsIRequest* aRequest,
                                  uint32_t aStateFlags,
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -7003,17 +7003,19 @@ nsDocShell::OnStatusChange(nsIWebProgres
                            nsresult aStatus, const char16_t* aMessage)
 {
   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::OnSecurityChange(nsIWebProgress* aWebProgress,
-                             nsIRequest* aRequest, uint32_t aState)
+                             nsIRequest* aRequest, uint32_t aOldState,
+                             uint32_t aState,
+                             const nsAString& aContentBlockingLogJSON)
 {
   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
 
 nsresult
 nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
                         nsIChannel* aChannel, nsresult aStatus)
--- a/docshell/base/nsDocShellTreeOwner.cpp
+++ b/docshell/base/nsDocShellTreeOwner.cpp
@@ -762,17 +762,19 @@ nsDocShellTreeOwner::OnStatusChange(nsIW
                                     const char16_t* aMessage)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShellTreeOwner::OnSecurityChange(nsIWebProgress* aWebProgress,
                                       nsIRequest* aRequest,
-                                      uint32_t aState)
+                                      uint32_t aOldState,
+                                      uint32_t aState,
+                                      const nsAString& aContentBlockingLogJSON)
 {
   return NS_OK;
 }
 
 //*****************************************************************************
 // nsDocShellTreeOwner: Accessors
 //*****************************************************************************
 
--- a/dom/base/ContentBlockingLog.h
+++ b/dom/base/ContentBlockingLog.h
@@ -2,31 +2,46 @@
 /* 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/JSONWriter.h"
 #include "mozilla/Pair.h"
 #include "mozilla/UniquePtr.h"
 #include "nsClassHashtable.h"
 #include "nsHashKeys.h"
+#include "nsReadableUtils.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;
 
+  struct StringWriteFunc : public JSONWriteFunc
+  {
+    nsAString& mBuffer; // The lifetime of the struct must be bound to the buffer
+    explicit StringWriteFunc(nsAString& aBuffer)
+      : mBuffer(aBuffer)
+    {}
+
+    void Write(const char* aStr) override
+    {
+      mBuffer.Append(NS_ConvertUTF8toUTF16(aStr));
+    }
+  };
+
 public:
   ContentBlockingLog() = default;
   ~ContentBlockingLog() = default;
 
   void RecordLog(const nsAString& aOrigin, uint32_t aType, bool aBlocked)
   {
     if (aOrigin.IsVoid()) {
       return;
@@ -38,16 +53,47 @@ public:
       entry.OrInsert([=] {
         auto log(MakeUnique<OriginLog>());
         log->AppendElement(mozilla::MakePair(aType, aBlocked));
         return log.release();
       });
     }
   }
 
+  nsAutoString Stringify()
+  {
+    nsAutoString buffer;
+
+    JSONWriter w(MakeUnique<StringWriteFunc>(buffer));
+    w.Start();
+
+    for (auto iter = mLog.Iter(); !iter.Done(); iter.Next()) {
+      if (!iter.UserData()) {
+        w.StartArrayProperty(NS_ConvertUTF16toUTF8(iter.Key()).get(), w.SingleLineStyle);
+        w.EndArray();
+        continue;
+      }
+
+      w.StartArrayProperty(NS_ConvertUTF16toUTF8(iter.Key()).get(), w.SingleLineStyle);
+      for (auto& item: *iter.UserData()) {
+        w.StartArrayElement(w.SingleLineStyle);
+        {
+          w.IntElement(item.first());
+          w.BoolElement(item.second());
+        }
+        w.EndArray();
+      }
+      w.EndArray();
+    }
+
+    w.End();
+
+    return buffer;
+  }
+
 private:
   nsClassHashtable<nsStringHashKey, OriginLog> mLog;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -1534,17 +1534,18 @@ BrowserElementChild.prototype = {
             } catch (e) {}
 
             sendAsyncMsg('error', { type: 'other' });
             return;
         }
       }
     },
 
-    onSecurityChange: function(webProgress, request, state) {
+    onSecurityChange: function(webProgress, request, oldState, state,
+                               contentBlockingLogJSON) {
       if (webProgress != docShell) {
         return;
       }
 
       var securityStateDesc;
       if (state & Ci.nsIWebProgressListener.STATE_IS_SECURE) {
         securityStateDesc = 'secure';
       }
--- a/dom/clients/manager/ClientNavigateOpChild.cpp
+++ b/dom/clients/manager/ClientNavigateOpChild.cpp
@@ -123,17 +123,18 @@ public:
                  nsresult aStatus, const char16_t* aMessage) override
   {
     MOZ_CRASH("Unexpected notification.");
     return NS_OK;
   }
 
   NS_IMETHOD
   OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
-                   uint32_t aState) override
+                   uint32_t aOldState, uint32_t aState,
+                   const nsAString& aContentBlockingLogJSON) override
   {
     MOZ_CRASH("Unexpected notification.");
     return NS_OK;
   }
 
   NS_DECL_ISUPPORTS
 };
 
--- a/dom/clients/manager/ClientOpenWindowUtils.cpp
+++ b/dom/clients/manager/ClientOpenWindowUtils.cpp
@@ -128,17 +128,19 @@ public:
   {
     MOZ_ASSERT(false, "Unexpected notification.");
     return NS_OK;
   }
 
   NS_IMETHOD
   OnSecurityChange(nsIWebProgress* aWebProgress,
                    nsIRequest* aRequest,
-                   uint32_t aState) override
+                   uint32_t aOldState,
+                   uint32_t aState,
+                   const nsAString& aContentBlockingLogJSON) override
   {
     MOZ_ASSERT(false, "Unexpected notification.");
     return NS_OK;
   }
 
 private:
   ~WebProgressListener()
   {
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -2126,17 +2126,19 @@ HTMLFormElement::OnStatusChange(nsIWebPr
 {
   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HTMLFormElement::OnSecurityChange(nsIWebProgress* aWebProgress,
                                   nsIRequest* aRequest,
-                                  uint32_t state)
+                                  uint32_t aOldState,
+                                  uint32_t aState,
+                                  const nsAString& aContentBlockingLogJSON)
 {
   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
 
 NS_IMETHODIMP_(int32_t)
 HTMLFormElement::IndexOfControl(nsIFormControl* aControl)
 {
--- a/dom/html/nsHTMLDNSPrefetch.cpp
+++ b/dom/html/nsHTMLDNSPrefetch.cpp
@@ -572,17 +572,19 @@ nsHTMLDNSPrefetch::nsDeferrals::OnStatus
                                                const char16_t* aMessage)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLDNSPrefetch::nsDeferrals::OnSecurityChange(nsIWebProgress *aWebProgress,
                                                  nsIRequest *aRequest,
-                                                 uint32_t state)
+                                                 uint32_t aOldState,
+                                                 uint32_t aState,
+                                                 const nsAString& aContentBlockingLogJSON)
 {
   return NS_OK;
 }
 
 //////////// nsIObserver method
 
 NS_IMETHODIMP
 nsHTMLDNSPrefetch::nsDeferrals::Observe(nsISupports *subject,
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -880,25 +880,42 @@ TabParent::Deactivate()
 
 NS_IMETHODIMP
 TabParent::Init(mozIDOMWindowProxy *window)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
+TabParent::GetOldState(uint32_t *aOldState)
+{
+  NS_ENSURE_ARG(aOldState);
+  NS_WARNING("SecurityState not valid here");
+  *aOldState = 0;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 TabParent::GetState(uint32_t *aState)
 {
   NS_ENSURE_ARG(aState);
   NS_WARNING("SecurityState not valid here");
   *aState = 0;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+TabParent::GetContentBlockingLogJSON(nsAString& aContentBlockingLogJSON)
+{
+  NS_WARNING("ContentBlockingLog not valid here");
+  aContentBlockingLogJSON.SetIsVoid(true);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 TabParent::GetSecInfo(nsITransportSecurityInfo** _result)
 {
   NS_ENSURE_ARG_POINTER(_result);
   NS_WARNING("TransportSecurityInfo not valid here");
   *_result = nullptr;
   return NS_OK;
 }
 
--- a/dom/presentation/PresentationCallbacks.cpp
+++ b/dom/presentation/PresentationCallbacks.cpp
@@ -268,13 +268,15 @@ PresentationResponderLoadingCallback::On
 {
   // Do nothing.
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationResponderLoadingCallback::OnSecurityChange(nsIWebProgress* aWebProgress,
                                                        nsIRequest* aRequest,
-                                                       uint32_t state)
+                                                       uint32_t aOldState,
+                                                       uint32_t aState,
+                                                       const nsAString& aContentBlockingLogJSON)
 {
   // Do nothing.
   return NS_OK;
 }
--- a/editor/composer/nsEditingSession.cpp
+++ b/editor/composer/nsEditingSession.cpp
@@ -808,17 +808,20 @@ nsEditingSession::OnStatusChange(nsIWebP
 
 /*---------------------------------------------------------------------------
 
   OnSecurityChange
 
 ----------------------------------------------------------------------------*/
 NS_IMETHODIMP
 nsEditingSession::OnSecurityChange(nsIWebProgress *aWebProgress,
-                                   nsIRequest *aRequest, uint32_t state)
+                                   nsIRequest *aRequest,
+                                   uint32_t aOldState,
+                                   uint32_t aState,
+                                   const nsAString& aContentBlockingLogJSON)
 {
     MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
     return NS_OK;
 }
 
 
 /*---------------------------------------------------------------------------
 
--- a/editor/composer/test/test_bug434998.xul
+++ b/editor/composer/test/test_bug434998.xul
@@ -82,17 +82,18 @@ https://bugzilla.mozilla.org/show_bug.cg
     onLocationChange : function(aWebProgress, aRequest, aLocation, aFlags)
     {
     },
 
     onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
     {
     },
 
-    onSecurityChange : function(aWebProgress, aRequest, aState)
+    onSecurityChange : function(aWebProgress, aRequest, aOldState, aState,
+                                aContentBlockingLogJSON)
     {
     },
 
     mEditor: null
   };
 
   var progress, progressListener;
 
--- a/editor/libeditor/tests/test_bug607584.xul
+++ b/editor/libeditor/tests/test_bug607584.xul
@@ -88,17 +88,18 @@ https://bugzilla.mozilla.org/show_bug.cg
     onLocationChange : function(aWebProgress, aRequest, aLocation, aFlags)
       {
       },
   
     onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
       {
       },
   
-    onSecurityChange : function(aWebProgress, aRequest, aState)
+    onSecurityChange : function(aWebProgress, aRequest, aOldState, aState,
+                                aContentBlockingLogJSON)
       {
       },
   
       mEditor: null
   };
 
   var progress, progressListener;
 
--- a/editor/libeditor/tests/test_bug616590.xul
+++ b/editor/libeditor/tests/test_bug616590.xul
@@ -77,17 +77,18 @@ https://bugzilla.mozilla.org/show_bug.cg
     onLocationChange : function(aWebProgress, aRequest, aLocation, aFlags)
     {
     },
 
     onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
     {
     },
 
-    onSecurityChange : function(aWebProgress, aRequest, aState)
+    onSecurityChange : function(aWebProgress, aRequest, aOldState, aState,
+                                aContentBlockingLogJSON)
     {
     },
 
     mEditor: null
   };
 
   var progress, progressListener;
 
--- a/editor/libeditor/tests/test_bug780908.xul
+++ b/editor/libeditor/tests/test_bug780908.xul
@@ -86,17 +86,18 @@ adapted from test_bug607584.xul by Kent 
     onLocationChange : function(aWebProgress, aRequest, aLocation, aFlags)
       {
       },
   
     onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
       {
       },
   
-    onSecurityChange : function(aWebProgress, aRequest, aState)
+    onSecurityChange : function(aWebProgress, aRequest, aOldState, aState,
+                                aContentBlockingLogJSON)
       {
       },
   
       mEditor: null
   };
 
   var progress, progressListener;
 
--- a/layout/base/tests/chrome/printpreview_bug396024_helper.xul
+++ b/layout/base/tests/chrome/printpreview_bug396024_helper.xul
@@ -18,17 +18,18 @@ var SimpleTest = window.opener.wrappedJS
 var gWbp;
 function printpreview() {
   gWbp = window.frames[1].docShell.printPreview;
   var listener = {
     onLocationChange: function(webProgress, request, location, flags) { },
     onProgressChange: function(webProgress, request, curSelfProgress, 
                                maxSelfProgress, curTotalProgress,
                                maxTotalProgress) { },
-    onSecurityChange: function(webProgress, request, state) { },
+    onSecurityChange: function(webProgress, request, oldState, state,
+                               contentBlockingLogJSON) { },
     onStateChange: function(webProgress, request, stateFlags, status) { },
     onStatusChange: function(webProgress, request, status, message) { },
     QueryInterface: function(iid) {
       if (iid.equals(Ci.nsIWebProgressListener) ||
           iid.equals(Ci.nsISupportsWeakReference))
             return this;
       throw Cr.NS_NOINTERFACE;
     }
--- a/layout/base/tests/chrome/printpreview_bug482976_helper.xul
+++ b/layout/base/tests/chrome/printpreview_bug482976_helper.xul
@@ -18,17 +18,18 @@ var SimpleTest = window.opener.wrappedJS
 var gWbp;
 function printpreview() {
   gWbp = window.frames[1].docShell.printPreview;
   var listener = {
     onLocationChange: function(webProgress, request, location, flags) { },
     onProgressChange: function(webProgress, request, curSelfProgress, 
                                maxSelfProgress, curTotalProgress,
                                maxTotalProgress) { },
-    onSecurityChange: function(webProgress, request, state) { },
+    onSecurityChange: function(webProgress, request, oldState, state,
+                               contentBlockingLogJSON) { },
     onStateChange: function(webProgress, request, stateFlags, status) { },
     onStatusChange: function(webProgress, request, status, message) { },
     QueryInterface: function(iid) {
       if (iid.equals(Ci.nsIWebProgessListener) ||
           iid.equals(Ci.nsISupportsWeakReference))
             return this;
       throw Cr.NS_NOINTERFACE;
     }
--- a/layout/base/tests/chrome/printpreview_helper.xul
+++ b/layout/base/tests/chrome/printpreview_helper.xul
@@ -25,17 +25,18 @@ filePath = file.path;
 
 function printpreview() {
   gWbp = window.frames[1].docShell.printPreview;
   var listener = {
     onLocationChange: function(webProgress, request, location, flags) { },
     onProgressChange: function(webProgress, request, curSelfProgress, 
                                maxSelfProgress, curTotalProgress,
                                maxTotalProgress) { },
-    onSecurityChange: function(webProgress, request, state) { },
+    onSecurityChange: function(webProgress, request, oldState, state,
+                               contentBlockingLogJSON) { },
     onStateChange: function(webProgress, request, stateFlags, status) { },
     onStatusChange: function(webProgress, request, status, message) { },
     QueryInterface: function(iid) {
       if (iid.equals(Ci.nsIWebProgressListener) ||
           iid.equals(Ci.nsISupportsWeakReference))
             return this;
       throw Cr.NS_NOINTERFACE;
     }
--- a/layout/printing/ipc/RemotePrintJobChild.cpp
+++ b/layout/printing/ipc/RemotePrintJobChild.cpp
@@ -158,17 +158,20 @@ RemotePrintJobChild::OnStatusChange(nsIW
     Unused << SendStatusChange(aStatus);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 RemotePrintJobChild::OnSecurityChange(nsIWebProgress* aProgress,
-                                      nsIRequest* aRequest, uint32_t aState)
+                                      nsIRequest* aRequest,
+                                      uint32_t aState,
+                                      uint32_t aOldState,
+                                      const nsAString& aContentBlockingLogJSON)
 {
   return NS_OK;
 }
 
 // End of nsIWebProgressListener
 
 RemotePrintJobChild::~RemotePrintJobChild()
 {
--- a/layout/printing/nsPrintJob.cpp
+++ b/layout/printing/nsPrintJob.cpp
@@ -2111,17 +2111,19 @@ nsPrintJob::OnStatusChange(nsIWebProgres
 {
   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPrintJob::OnSecurityChange(nsIWebProgress* aWebProgress,
                              nsIRequest* aRequest,
-                             uint32_t aState)
+                             uint32_t aState,
+                             uint32_t aOldState,
+                             const nsAString& aContentBlockingLogJSON)
 {
   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
 
 //-------------------------------------------------------
 
 void
--- a/layout/tools/layout-debug/ui/content/layoutdebug.js
+++ b/layout/tools/layout-debug/ui/content/layoutdebug.js
@@ -72,17 +72,18 @@ nsLDBBrowserContentListener.prototype = 
       this.setButtonEnabled(this.mBackButton, gBrowser.canGoBack);
     },
 
   onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
     {
       this.mStatusText.value = aMessage;
     },
 
-  onSecurityChange : function(aWebProgress, aRequest, aState)
+  onSecurityChange : function(aWebProgress, aRequest, aOldState, aState,
+                              aContentBlockingLogJSON)
     {
     },
 
   // non-interface methods
   setButtonEnabled : function(aButtonElement, aEnabled)
     {
       if (aEnabled)
         aButtonElement.removeAttribute("disabled");
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -4687,17 +4687,18 @@ Tab.prototype = {
       Services.obs.notifyObservers(this.browser, "Session:NotifyLocationChange");
     }
   },
 
   // Properties used to cache security state used to update the UI
   _state: null,
   _hostChanged: false, // onLocationChange will flip this bit
 
-  onSecurityChange: function(aWebProgress, aRequest, aState) {
+  onSecurityChange: function(aWebProgress, aRequest, aOldState, aState,
+                             aContentBlockingLogJSON) {
     // Don't need to do anything if the data we use to update the UI hasn't changed
     if (this._state == aState && !this._hostChanged)
       return;
 
     this._state = aState;
     this._hostChanged = false;
 
     let identity = IdentityHandler.checkIdentity(aState, this.browser);
--- a/mobile/android/modules/geckoview/GeckoViewProgress.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewProgress.jsm
@@ -247,17 +247,18 @@ class GeckoViewProgress extends GeckoVie
         type: "GeckoView:PageStop",
         success: isSuccess
       };
 
       this.eventDispatcher.sendRequest(message);
     }
   }
 
-  onSecurityChange(aWebProgress, aRequest, aState) {
+  onSecurityChange(aWebProgress, aRequest, aOldState, aState,
+                   aContentBlockingLogJSON) {
     debug `onSecurityChange`;
 
     // Don't need to do anything if the data we use to update the UI hasn't changed
     if (this._state === aState && !this._hostChanged) {
       return;
     }
 
     this._state = aState;
--- a/mobile/android/modules/geckoview/GeckoViewTrackingProtection.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewTrackingProtection.jsm
@@ -15,17 +15,18 @@ class GeckoViewTrackingProtection extend
     const flags = Ci.nsIWebProgress.NOTIFY_SECURITY;
     this.progressFilter =
       Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
       .createInstance(Ci.nsIWebProgress);
     this.progressFilter.addProgressListener(this, flags);
     this.browser.addProgressListener(this.progressFilter, flags);
   }
 
-  onSecurityChange(aWebProgress, aRequest, aState) {
+  onSecurityChange(aWebProgress, aRequest, aOldState, aState,
+                   aContentBlockingLogJSON) {
     debug `onSecurityChange`;
 
     if (!(aState & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT) ||
         !aRequest || !(aRequest instanceof Ci.nsIClassifiedChannel)) {
       return;
     }
 
     let channel = aRequest.QueryInterface(Ci.nsIChannel);
--- a/netwerk/base/nsISecureBrowserUI.idl
+++ b/netwerk/base/nsISecureBrowserUI.idl
@@ -11,15 +11,40 @@ interface nsIDocShell;
 interface nsITransportSecurityInfo;
 
 [scriptable, uuid(718c662a-f810-4a80-a6c9-0b1810ecade2)]
 interface nsISecureBrowserUI : nsISupports
 {
     void init(in mozIDOMWindowProxy window);
     void setDocShell(in nsIDocShell docShell);
 
+    // A value composed of the Security State Flags and the Security
+    // Strength Flags defined in nsIWebProgressListener.
+    // Any undefined bits are reserved for future use.
+    // This represents the security state before the change.
+    readonly attribute unsigned long oldState;
+    // A value composed of the Security State Flags and the Security
+    // Strength Flags defined in nsIWebProgressListener.
+    // Any undefined bits are reserved for future use.
+    // This represents the security state after the change.
     readonly attribute unsigned long state;
+    // An optional JSON string representing a log of the content blocking
+    // events happened so far.  This will be a JSON object containing keys
+    // representing origins that content blocking has acted on, with values
+    // being an array of items, each representing one action.  Each action
+    // itself is an an array containing three elements, the first element
+    // being a blocking code from one of the Security State Flags above, and
+    // the second element being a boolean representing whether the origin
+    // was blocked (if true) or unblocked (if false) in that category, and
+    // the third element being the number of times that combination has been
+    // repeated consecutively.
+    // The reason JSON strings was chosen here was that we needed a format
+    // that is transferrable across processes in JS; and also the consumer
+    // of this data (browser-contentblocking.js) would be able to use the
+    // JSON.parse() API to parse out an object representation of the value.
+    readonly attribute AString contentBlockingLogJSON;
+    // Information about the connection security.
     readonly attribute nsITransportSecurityInfo secInfo;
 };
 
 %{C++
 #define NS_SECURE_BROWSER_UI_CONTRACTID "@mozilla.org/secure_browser_ui;1"
 %}
--- a/security/manager/ssl/nsSecureBrowserUIImpl.cpp
+++ b/security/manager/ssl/nsSecureBrowserUIImpl.cpp
@@ -60,16 +60,30 @@ nsSecureBrowserUIImpl::Init(mozIDOMWindo
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   return wp->AddProgressListener(this, nsIWebProgress::NOTIFY_LOCATION);
 }
 
 NS_IMETHODIMP
+nsSecureBrowserUIImpl::GetOldState(uint32_t* aOldState)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  NS_ENSURE_ARG(aOldState);
+
+  MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug, ("GetOldState %p", this));
+  // Only sync our state with the docshell in GetState().
+  MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug, ("  mOldState: %x", mOldState));
+
+  *aOldState = mOldState;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsSecureBrowserUIImpl::GetState(uint32_t* aState)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_ARG(aState);
 
   MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug, ("GetState %p", this));
   // With respect to mixed content and tracking protection, we won't know when
   // the state of our document (or a subdocument) has changed, so we ask the
@@ -77,16 +91,36 @@ nsSecureBrowserUIImpl::GetState(uint32_t
   CheckForBlockedContent();
   MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug, ("  mState: %x", mState));
 
   *aState = mState;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsSecureBrowserUIImpl::GetContentBlockingLogJSON(nsAString& aContentBlockingLogJSON)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug, ("GetContentBlockingLogJSON %p", this));
+  aContentBlockingLogJSON.Truncate();
+  nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocShell);
+  if (docShell) {
+    nsIDocument* doc = docShell->GetDocument();
+    if (doc) {
+      aContentBlockingLogJSON = doc->GetContentBlockingLog()->Stringify();
+    }
+  }
+  MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
+          ("  ContentBlockingLogJSON: %s", NS_ConvertUTF16toUTF8(aContentBlockingLogJSON).get()));
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsSecureBrowserUIImpl::GetSecInfo(nsITransportSecurityInfo** result)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_ARG_POINTER(result);
 
   *result = mTopLevelSecurityInfo;
   NS_IF_ADDREF(*result);
 
@@ -310,17 +344,17 @@ nsSecureBrowserUIImpl::OnLocationChange(
       return rv;
     }
 
     nsCOMPtr<nsISecurityEventSink> eventSink;
     NS_QueryNotificationCallbacks(channel, eventSink);
     if (NS_WARN_IF(!eventSink)) {
       return NS_ERROR_INVALID_ARG;
     }
-    ContentBlockingLog* contentBlockingLog = nullptr;
+    mozilla::dom::ContentBlockingLog* contentBlockingLog = nullptr;
     nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocShell);
     if (docShell) {
       nsIDocument* doc = docShell->GetDocument();
       if (doc) {
         contentBlockingLog = doc->GetContentBlockingLog();
       }
     }
     MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
@@ -360,13 +394,14 @@ nsSecureBrowserUIImpl::OnStatusChange(ns
                                       nsresult,
                                       const char16_t*)
 {
   MOZ_ASSERT_UNREACHABLE("Should have been excluded in AddProgressListener()");
   return NS_OK;
 }
 
 nsresult
-nsSecureBrowserUIImpl::OnSecurityChange(nsIWebProgress*, nsIRequest*, uint32_t)
+nsSecureBrowserUIImpl::OnSecurityChange(nsIWebProgress*, nsIRequest*, uint32_t,
+                                        uint32_t, const nsAString&)
 {
   MOZ_ASSERT_UNREACHABLE("Should have been excluded in AddProgressListener()");
   return NS_OK;
 }
--- a/toolkit/actors/PrintingChild.jsm
+++ b/toolkit/actors/PrintingChild.jsm
@@ -416,10 +416,11 @@ PrintingListener.prototype = {
       maxSelfProgress: aMaxSelfProgress,
       curTotalProgress: aCurTotalProgress,
       maxTotalProgress: aMaxTotalProgress,
     });
   },
 
   onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {},
   onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {},
-  onSecurityChange(aWebProgress, aRequest, aState) {},
+  onSecurityChange(aWebProgress, aRequest, aOldState, aState,
+                   aContentBlockingLogJSON) {},
 };
--- a/toolkit/components/antitracking/test/browser/head.js
+++ b/toolkit/components/antitracking/test/browser/head.js
@@ -591,18 +591,19 @@ this.AntiTracking = {
       await AntiTracking._setupTest(win, options.cookieBehavior,
                                     options.blockingByContentBlocking,
                                     options.blockingByContentBlockingUI,
                                     options.blockingByContentBlockingRTUI,
                                     options.extraPrefs);
 
       let cookieBlocked = 0;
       let listener = {
-        onSecurityChange(webProgress, request, stateFlags, status) {
-          if (stateFlags & Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_TRACKER) {
+        onSecurityChange(webProgress, request, oldState, state,
+                         status, contentBlockingLogJSON) {
+          if (state & Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_TRACKER) {
             ++cookieBlocked;
           }
         },
       };
       win.gBrowser.addProgressListener(listener);
 
       info("Creating a new tab");
       let tab = BrowserTestUtils.addTab(win.gBrowser, TEST_TOP_PAGE);
--- a/toolkit/components/browser/nsWebBrowser.cpp
+++ b/toolkit/components/browser/nsWebBrowser.cpp
@@ -828,20 +828,23 @@ nsWebBrowser::OnStatusChange(nsIWebProgr
                                              aMessage);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWebBrowser::OnSecurityChange(nsIWebProgress* aWebProgress,
                                nsIRequest* aRequest,
-                               uint32_t aState)
+                               uint32_t aOldState,
+                               uint32_t aState,
+                               const nsAString& aContentBlockingLogJSON)
 {
   if (mProgressListener) {
-    return mProgressListener->OnSecurityChange(aWebProgress, aRequest, aState);
+    return mProgressListener->OnSecurityChange(aWebProgress, aRequest, aOldState,
+                                               aState, aContentBlockingLogJSON);
   }
   return NS_OK;
 }
 
 //*****************************************************************************
 // nsWebBrowser::nsIWebBrowserPersist
 //*****************************************************************************
 
--- a/toolkit/components/printing/content/printPreviewProgress.js
+++ b/toolkit/components/printing/content/printPreviewProgress.js
@@ -54,17 +54,18 @@ var progressListener = {
     if (docURLStr != docURL && dialog.title != null) {
       docURL = docURLStr;
       if (docTitle == "")
         dialog.title.value = docURLStr;
     }
   },
 
   onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {},
-  onSecurityChange(aWebProgress, aRequest, state) {},
+  onSecurityChange(aWebProgress, aRequest, aOldState, aState,
+                   aContentBlockingLogJSON) {},
 
   onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
     if (aMessage)
       dialog.title.setAttribute("value", aMessage);
   },
 
   QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener",
                                           "nsISupportsWeakReference"]),
--- a/toolkit/components/printing/content/printProgress.js
+++ b/toolkit/components/printing/content/printProgress.js
@@ -139,17 +139,18 @@ var progressListener = {
       // we can ignore this notification
     },
 
     onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
       if (aMessage != "")
         dialog.title.setAttribute("value", aMessage);
     },
 
-    onSecurityChange(aWebProgress, aRequest, state) {
+    onSecurityChange(aWebProgress, aRequest, aOldState, aState,
+                     aContentBlockingLogJSON) {
       // we can ignore this notification
     },
 
     QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener",
                                             "nsISupportsWeakReference"]),
 };
 
 function getString( stringId ) {
--- a/toolkit/components/printingui/ipc/PrintProgressDialogChild.cpp
+++ b/toolkit/components/printingui/ipc/PrintProgressDialogChild.cpp
@@ -95,17 +95,19 @@ PrintProgressDialogChild::OnStatusChange
                                          const char16_t* aMessage)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PrintProgressDialogChild::OnSecurityChange(nsIWebProgress* aProgress,
                                            nsIRequest* aRequest,
-                                           uint32_t aState)
+                                           uint32_t aOldState,
+                                           uint32_t aState,
+                                           const nsAString& aContentBlockingLogJSON)
 {
   return NS_OK;
 }
 
 // nsIPrintProgressParams
 
 NS_IMETHODIMP
 PrintProgressDialogChild::GetDocTitle(nsAString& aDocTitle)
--- a/toolkit/components/printingui/nsPrintProgress.cpp
+++ b/toolkit/components/printingui/nsPrintProgress.cpp
@@ -239,17 +239,21 @@ NS_IMETHODIMP nsPrintProgress::OnStatusC
     nsCOMPtr<nsIWebProgressListener> progressListener = m_listenerList.SafeObjectAt(i);
     if (progressListener)
       progressListener->OnStatusChange(aWebProgress, aRequest, aStatus, aMessage);
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP nsPrintProgress::OnSecurityChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, uint32_t state)
+NS_IMETHODIMP nsPrintProgress::OnSecurityChange(nsIWebProgress *aWebProgress,
+                                                nsIRequest *aRequest,
+                                                uint32_t aOldState,
+                                                uint32_t aState,
+                                                const nsAString& aContentBlockingLogJSON)
 {
     return NS_OK;
 }
 
 nsresult nsPrintProgress::ReleaseListeners()
 {
   m_listenerList.Clear();
 
--- a/toolkit/components/printingui/nsPrintingPromptService.cpp
+++ b/toolkit/components/printingui/nsPrintingPromptService.cpp
@@ -223,18 +223,21 @@ nsPrintingPromptService::OnStatusChange(
   }
 #endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPrintingPromptService::OnSecurityChange(nsIWebProgress* aWebProgress,
                                           nsIRequest* aRequest,
-                                          uint32_t state)
+                                          uint32_t aOldState,
+                                          uint32_t aState,
+                                          const nsAString& aContentBlockingLogJSON)
 {
 #if !defined(XP_MACOSX)
   if (mWebProgressListener) {
     return mWebProgressListener->OnSecurityChange(
-      aWebProgress, aRequest, state);
+      aWebProgress, aRequest, aOldState, aState,
+      aContentBlockingLogJSON);
   }
 #endif
   return NS_OK;
 }
--- a/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp
+++ b/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp
@@ -260,22 +260,25 @@ nsBrowserStatusFilter::OnStatusChange(ns
     mDelayedStatus = true;
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsBrowserStatusFilter::OnSecurityChange(nsIWebProgress *aWebProgress,
                                         nsIRequest *aRequest,
-                                        uint32_t aState)
+                                        uint32_t aOldState,
+                                        uint32_t aState,
+                                        const nsAString& aContentBlockingLogJSON)
 {
     if (!mListener)
         return NS_OK;
 
-    return mListener->OnSecurityChange(aWebProgress, aRequest, aState);
+    return mListener->OnSecurityChange(aWebProgress, aRequest, aOldState,
+                                       aState, aContentBlockingLogJSON);
 }
 
 //-----------------------------------------------------------------------------
 // nsBrowserStatusFilter::nsIWebProgressListener2
 //-----------------------------------------------------------------------------
 NS_IMETHODIMP
 nsBrowserStatusFilter::OnProgressChange64(nsIWebProgress *aWebProgress,
                                           nsIRequest *aRequest,
--- a/toolkit/components/url-classifier/tests/mochitest/test_threathit_report.html
+++ b/toolkit/components/url-classifier/tests/mochitest/test_threathit_report.html
@@ -189,17 +189,18 @@ function testOnWindow(aTestData) {
 
     (async function() {
       await new Promise(rs => whenDelayedStartupFinished(win, rs));
 
       let expected;
       let browser = win.gBrowser.selectedBrowser;
       let wp = win.gBrowser.contentWindow.docShell.QueryInterface(Ci.nsIWebProgress);
       let progressListener = {
-        onSecurityChange(aWebProgress, aRequest, aState) {
+        onSecurityChange(aWebProgress, aRequest, aOldState, aState,
+                         aContentBlockingLogJSON) {
           expected = aTestData.reportUrl;
         },
         QueryInterface: ChromeUtils.generateQI(["nsISupportsWeakReference"]),
       };
       wp.addProgressListener(progressListener, wp.NOTIFY_SECURITY);
 
       await BrowserTestUtils.loadURI(browser, aTestData.url);
       await BrowserTestUtils.waitForContentEvent(browser, "DOMContentLoaded");
--- a/toolkit/modules/RemoteSecurityUI.jsm
+++ b/toolkit/modules/RemoteSecurityUI.jsm
@@ -2,24 +2,30 @@
 // 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/.
 
 var EXPORTED_SYMBOLS = ["RemoteSecurityUI"];
 
 function RemoteSecurityUI() {
     this._secInfo = null;
+    this._oldState = 0;
     this._state = 0;
+    this._contentBlockingLogJSON = "";
 }
 
 RemoteSecurityUI.prototype = {
   QueryInterface: ChromeUtils.generateQI([Ci.nsISecureBrowserUI]),
 
   // nsISecureBrowserUI
+  get oldState() { return this._oldState; },
   get state() { return this._state; },
+  get contentBlockingLogJSON() { return this._contentBlockingLogJSON; },
   get tooltipText() { return ""; },
   get secInfo() { return this._secInfo; },
 
-  _update(aSecInfo, aState) {
+  _update(aSecInfo, aOldState, aState, aContentBlockingLogJSON) {
     this._secInfo = aSecInfo;
+    this._oldState = aOldState;
     this._state = aState;
+    this._contentBlockingLogJSON = aContentBlockingLogJSON;
   },
 };
--- a/toolkit/modules/RemoteWebProgress.jsm
+++ b/toolkit/modules/RemoteWebProgress.jsm
@@ -104,27 +104,27 @@ RemoteWebProgressManager.prototype = {
     });
   },
 
   removeProgressListener(aListener) {
     this._progressListeners =
       this._progressListeners.filter(l => l.listener != aListener);
   },
 
-  _fixSecInfoAndState(aSecInfo, aState) {
+  _fixSecInfo(aSecInfo) {
     let deserialized = null;
     if (aSecInfo) {
       let helper = Cc["@mozilla.org/network/serialization-helper;1"]
                     .getService(Ci.nsISerializationHelper);
 
       deserialized = helper.deserializeObject(aSecInfo);
       deserialized.QueryInterface(Ci.nsITransportSecurityInfo);
     }
 
-    return [deserialized, aState];
+    return deserialized;
   },
 
   setCurrentURI(aURI) {
     // This function is simpler than nsDocShell::SetCurrentURI since
     // it doesn't have to deal with child docshells.
     let remoteWebNav = this._browser._remoteWebNavigationImpl;
     remoteWebNav._currentURI = aURI;
 
@@ -235,29 +235,33 @@ RemoteWebProgressManager.prototype = {
 
       this._callProgressListeners(
         Ci.nsIWebProgress.NOTIFY_LOCATION, "onLocationChange", webProgress,
         request, location, flags
       );
       break;
 
     case "Content:SecurityChange":
-      let [secInfo, state] = this._fixSecInfoAndState(json.secInfo, json.state);
+      let secInfo = this._fixSecInfo(json.secInfo);
+      let oldState = json.oldState;
+      let state = json.state;
+      let contentBlockingLogJSON = json.contentBlockingLogJSON;
 
       if (isTopLevel) {
         // Invoking this getter triggers the generation of the underlying object,
         // which we need to access with ._securityUI, because .securityUI returns
         // a wrapper that makes _update inaccessible.
         void this._browser.securityUI;
-        this._browser._securityUI._update(secInfo, state);
+        this._browser._securityUI._update(secInfo, oldState, state,
+                                          contentBlockingLogJSON);
       }
 
       this._callProgressListeners(
         Ci.nsIWebProgress.NOTIFY_SECURITY, "onSecurityChange", webProgress,
-        request, state
+        request, oldState, state, contentBlockingLogJSON
       );
       break;
 
     case "Content:StatusChange":
       this._callProgressListeners(
         Ci.nsIWebProgress.NOTIFY_STATUS, "onStatusChange", webProgress, request,
         json.status, json.message
       );
--- a/toolkit/modules/WebProgressChild.jsm
+++ b/toolkit/modules/WebProgressChild.jsm
@@ -208,22 +208,25 @@ class WebProgressChild {
     let secInfo = this.mm.docShell.securityUI.secInfo;
     if (secInfo) {
       return serializationHelper.serializeToString(secInfo);
     }
 
     return null;
   }
 
-  onSecurityChange(aWebProgress, aRequest, aState) {
+  onSecurityChange(aWebProgress, aRequest, aOldState, aState,
+                   aContentBlockingLogJSON) {
     let json = this._setupJSON(aWebProgress, aRequest);
     let objects = this._setupObjects(aWebProgress, aRequest);
 
+    json.oldState = aOldState;
     json.state = aState;
     json.secInfo = this.getSecInfoAsString();
+    json.contentBlockingLogJSON = aContentBlockingLogJSON;
 
     json.matchedList = null;
     if (aRequest && aRequest instanceof Ci.nsIClassifiedChannel) {
       json.matchedList = aRequest.matchedList;
     }
 
     this._send("Content:SecurityChange", json, objects);
   }
--- a/toolkit/mozapps/downloads/nsHelperAppDlg.js
+++ b/toolkit/mozapps/downloads/nsHelperAppDlg.js
@@ -65,17 +65,18 @@ nsUnknownContentTypeDialogProgressListen
 
 
   onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
   },
 
   onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
   },
 
-  onSecurityChange(aWebProgress, aRequest, state) {
+  onSecurityChange(aWebProgress, aRequest, aOldState, aState,
+                   aContentBlockingLogJSON) {
   },
 
   onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
     return true;
   },
 };
 
 // /////////////////////////////////////////////////////////////////////////////
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -2130,17 +2130,18 @@ var gDiscoverView = {
         (!this.homepageURL.schemeIs("https") || aLocation.schemeIs("https")))
       return;
 
     // Canceling the request will send an error to onStateChange which will show
     // the error page
     aRequest.cancel(Cr.NS_BINDING_ABORTED);
   },
 
-  onSecurityChange(aWebProgress, aRequest, aState) {
+  onSecurityChange(aWebProgress, aRequest, aOldState, aState,
+                   aContentBlockingLogJSON) {
     // Don't care about security if the page is not https
     if (!this.homepageURL.schemeIs("https"))
       return;
 
     // If the request was secure then it is ok
     if (aState & Ci.nsIWebProgressListener.STATE_IS_SECURE)
       return;
 
--- a/uriloader/base/nsDocLoader.cpp
+++ b/uriloader/base/nsDocLoader.cpp
@@ -1468,19 +1468,22 @@ NS_IMETHODIMP nsDocLoader::OnSecurityCha
                                             ContentBlockingLog* aContentBlockingLog)
 {
   //
   // Fire progress notifications out to any registered nsIWebProgressListeners.
   //
 
   nsCOMPtr<nsIRequest> request = do_QueryInterface(aContext);
   nsIWebProgress* webProgress = static_cast<nsIWebProgress*>(this);
+  nsAutoString contentBlockingLogJSON(
+    aContentBlockingLog ? aContentBlockingLog->Stringify() : EmptyString());
 
   NOTIFY_LISTENERS(nsIWebProgress::NOTIFY_SECURITY,
-    listener->OnSecurityChange(webProgress, request, aState);
+    listener->OnSecurityChange(webProgress, request, aOldState, aState,
+                               contentBlockingLogJSON);
   );
 
   // Pass the notification up to the parent...
   if (mParent) {
     mParent->OnSecurityChange(aContext, aOldState, aState, aContentBlockingLog);
   }
   return NS_OK;
 }
--- a/uriloader/base/nsIWebProgressListener.idl
+++ b/uriloader/base/nsIWebProgressListener.idl
@@ -469,20 +469,35 @@ interface nsIWebProgressListener : nsISu
    * security transitions (eg HTTP -> HTTPS, HTTPS -> HTTP, FOO -> HTTPS) and
    * after document load completion.  It might also be called if an error
    * occurs during network loading.
    *
    * @param aWebProgress
    *        The nsIWebProgress instance that fired the notification.
    * @param aRequest
    *        The nsIRequest that has new security state.
+   * @param aOldState
+   *        A value composed of the Security State Flags and the Security
+   *        Strength Flags listed above.  Any undefined bits are reserved for
+   *        future use.  This represents the security state before the change.
    * @param aState
    *        A value composed of the Security State Flags and the Security
    *        Strength Flags listed above.  Any undefined bits are reserved for
-   *        future use.
+   *        future use.  This represents the security state after the change.
+   * @param aContentBlockingLog
+   *        An optional JSON string representing a log of the content blocking
+   *        events happened so far.  This will be a JSON object containing keys
+   *        representing origins that content blocking has acted on, with values
+   *        being an array of items, each representing one action.  Each action
+   *        itself is an an array containing two elements, the first element
+   *        being a blocking code from one of the Security State Flags above, and
+   *        the second element being a boolean representing whether the origin
+   *        was blocked (if true) or unblocked (if false) in that category.
    *
    * NOTE: These notifications will only occur if a security package is
    * installed.
    */
   void onSecurityChange(in nsIWebProgress aWebProgress,
                         in nsIRequest aRequest,
-                        in unsigned long aState);
+                        in unsigned long aOldState,
+                        in unsigned long aState,
+                        in AString aContentBlockingLogJSON);
 };
--- a/uriloader/prefetch/nsOfflineCacheUpdateService.cpp
+++ b/uriloader/prefetch/nsOfflineCacheUpdateService.cpp
@@ -217,17 +217,19 @@ nsOfflineCachePendingUpdate::OnStatusCha
 {
     MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsOfflineCachePendingUpdate::OnSecurityChange(nsIWebProgress *aWebProgress,
                                               nsIRequest *aRequest,
-                                              uint32_t state)
+                                              uint32_t aOldState,
+                                              uint32_t aState,
+                                              const nsAString& aContentBlockingLogJSON)
 {
     MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsOfflineCacheUpdateService::nsISupports
 //-----------------------------------------------------------------------------
--- a/uriloader/prefetch/nsPrefetchService.cpp
+++ b/uriloader/prefetch/nsPrefetchService.cpp
@@ -1023,17 +1023,19 @@ nsPrefetchService::OnStatusChange(nsIWeb
 {
     MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
     return NS_OK;
 }
 
 NS_IMETHODIMP 
 nsPrefetchService::OnSecurityChange(nsIWebProgress *aWebProgress, 
                                     nsIRequest *aRequest, 
-                                    uint32_t state)
+                                    uint32_t aOldState,
+                                    uint32_t aState,
+                                    const nsAString& aContentBlockingLogJSON)
 {
     MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsPrefetchService::nsIObserver
 //-----------------------------------------------------------------------------
--- a/xpfe/appshell/nsChromeTreeOwner.cpp
+++ b/xpfe/appshell/nsChromeTreeOwner.cpp
@@ -494,17 +494,19 @@ nsChromeTreeOwner::OnStatusChange(nsIWeb
     return NS_OK;
 }
 
 
 
 NS_IMETHODIMP
 nsChromeTreeOwner::OnSecurityChange(nsIWebProgress *aWebProgress,
                                     nsIRequest *aRequest,
-                                    uint32_t state)
+                                    uint32_t aOldState,
+                                    uint32_t aState,
+                                    const nsAString& aContentBlockingLogJSON)
 {
     return NS_OK;
 }
 
 //*****************************************************************************
 // nsChromeTreeOwner: Helpers
 //*****************************************************************************
 
--- a/xpfe/appshell/nsWebShellWindow.cpp
+++ b/xpfe/appshell/nsWebShellWindow.cpp
@@ -671,17 +671,19 @@ nsWebShellWindow::OnStatusChange(nsIWebP
 {
   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWebShellWindow::OnSecurityChange(nsIWebProgress *aWebProgress,
                                    nsIRequest *aRequest,
-                                   uint32_t state)
+                                   uint32_t aOldState,
+                                   uint32_t aState,
+                                   const nsAString& aContentBlockingLogJSON)
 {
   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
 
 
 /**
  * ExecuteCloseHandler - Run the close handler, if any.