Bug 1493563 - Part 5: Present the old state and the content blocking log to the web progress listeners; r=baku
authorEhsan Akhgari <ehsan@mozilla.com>
Sat, 22 Sep 2018 20:29:07 -0400
changeset 487091 f8849239da42a6d2aae6415871ea9ed3b7ae4992
parent 487090 d0997972e4d417f275fe39815c73e092023ccbdd
child 487092 acbf4450b55e5b8f863ef5c1b2eb6b3f49adaa5b
push id246
push userfmarier@mozilla.com
push dateSat, 13 Oct 2018 00:15:40 +0000
reviewersbaku
bugs1493563
milestone64.0a1
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/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
@@ -4984,17 +4984,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.
@@ -5011,17 +5012,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
@@ -7002,17 +7002,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/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
@@ -4699,17 +4699,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
@@ -9,15 +9,40 @@
 interface nsIDocShell;
 interface nsITransportSecurityInfo;
 
 [scriptable, uuid(718c662a-f810-4a80-a6c9-0b1810ecade2)]
 interface nsISecureBrowserUI : nsISupports
 {
     void init(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
@@ -58,16 +58,30 @@ nsSecureBrowserUIImpl::Init(nsIDocShell*
   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
@@ -75,16 +89,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);
 
@@ -298,17 +332,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,
@@ -348,13 +382,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
@@ -590,18 +590,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.