Merge mozilla-inbound to mozilla-central. a=merge
authorCosmin Sabou <csabou@mozilla.com>
Tue, 22 Jan 2019 05:56:10 +0200
changeset 511900 956bd26eec5a9174753b63931d1a927a59cd4716
parent 511883 ada22b635f8d13adb7725c2fcac51d0e12e3ed59 (current diff)
parent 511899 d94203c1f6b52716801969622868a56361af7cb5 (diff)
child 511901 62fee61d444bc1ad93d24cc3f27f62831100086a
child 511910 44be6c1eb581bdd91023174d70f5b49052904945
push id10563
push userarchaeopteryx@coole-files.de
push dateThu, 24 Jan 2019 17:31:37 +0000
treeherdermozilla-beta@c0b43d952f70 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone66.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
Merge mozilla-inbound to mozilla-central. a=merge
.git-blame-ignore-revs
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -1,30 +1,44 @@
-# Bug 1204606 - Reformat of dom/media r=jya
+# Bug 1204606 - Reformat of dom/media
 # https://hg.mozilla.org/mozilla-central/rev/0ceae9db9ec0be18daa1a279511ad305723185d4
 abd6d77c618998827e5ffc3dab12f1a34d6ed03d # cinnabar
 804b8b8883ba2a6795b0fcf65ebcb18306b6416b # gecko-dev
 
-# Bug 1511181 - Reformat everything to the Google coding style r=ehsan
+# Bug 1511181 - Reformat everything to the Google coding style
 # https://hg.mozilla.org/mozilla-central/rev/6f3709b3878117466168c40affa7bca0b60cf75b
 0e0308d10a5fd4a8dcf0601978776342a2abf2df # cinnabar
 265e6721798a455604328ed5262f430cfcc37c2f # gecko-dev
 
-# Bug 1516555 - Reformat everything to the Google coding style r=Ehsan
+# Bug 1516555 - Reformat everything to the Google coding style
 # https://hg.mozilla.org/mozilla-central/rev/d57dde190f67
 cf6442a0664caf8eb39eb75b77963f648597cbd8 # cinnabar
 cccdda3c2aec019cc138d410fc0e4aa3e307b6ab # gecko-dev
 
-# Bug 1512961 - Reformat recent changes to the Google coding style r=Ehsan
+# Bug 1512961 - Reformat recent changes to the Google coding style
 # https://hg.mozilla.org/integration/autoland/rev/8869368a3f30
 20061c7bfce850ba0158c7b8a439ac3ecc62a262 # cinnabar
 ad75e912fbc1d64ed63c1d0a51d7157566de1887 # gecko-dev
 
 # Bug 1513205 - Also update the tests to match the Google coding style
 # https://hg.mozilla.org/integration/autoland/rev/09c71a7cf75a
 13452f36fb36ad09d81f6b53eaf7928ee63f05e3 # cinnabar
 6f45c666bc7f98c3e3250a7c2cf8dc3fb95d12d7 # gecko-dev
 
+# Bug 1519636 - Reformat everything to the Google coding style
+# https://hg.mozilla.org/mozilla-central/rev/d54846d01280026138135bde6e0bb1bcfe58feae
+abc28896717209d5f0ed2a6b0a46f8e03880ad19 # cinnabar
+47a5dd1fb80fa5bbe608c32db426f1ed29b7fd79 # gecko-dev
+
+# Bug 1521000 - Part 1: Reformat the tree to ensure everything is forma…
+# https://hg.mozilla.org/mozilla-central/rev/9916e7d6e327
+4205caa5c71ba3b111ac494d32d46ff76882b795 # cinnabar
+06c3d29113e60ab1a316fbd5a16c667202d012fb # gecko-dev
+
+# Bug 1521000 - Part 2: Adjust our clang-format rules to include spaces…
+# https://hg.mozilla.org/mozilla-central/rev/5f4630838d46
+d25e4032d4d15e732a78082505d62b6d6ffa1445 # cinnabar
+e5e885ae3128a6878d08df4ff6baaf3fc4a4b9e8 # gecko-dev
 # Bug 1513205 - Ride along, update some code to match the Google coding style
 # https://hg.mozilla.org/integration/autoland/rev/039a7c0c18fb
 1ce955dcb2e09fd9ce19c1ec6c470f75db486bd2 # cinnabar
 7cf43b9bc01d650f87deceb65f336cdac7c0e78f # gecko-dev
 
--- a/accessible/base/DocManager.cpp
+++ b/accessible/base/DocManager.cpp
@@ -303,16 +303,23 @@ DocManager::OnStatusChange(nsIWebProgres
 
 NS_IMETHODIMP
 DocManager::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
                              uint32_t aState) {
   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
 
+NS_IMETHODIMP
+DocManager::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
+                                   nsIRequest* aRequest, uint32_t aEvent) {
+  MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
+  return NS_OK;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // nsIDOMEventListener
 
 NS_IMETHODIMP
 DocManager::HandleEvent(Event* aEvent) {
   nsAutoString type;
   aEvent->GetType(type);
 
--- a/browser/base/content/browser-contentblocking.js
+++ b/browser/base/content/browser-contentblocking.js
@@ -807,17 +807,17 @@ var ContentBlocking = {
 
   shieldHistogramAdd(value) {
     if (PrivateBrowsingUtils.isWindowPrivate(window)) {
       return;
     }
     Services.telemetry.getHistogramById("TRACKING_PROTECTION_SHIELD").add(value);
   },
 
-  onSecurityChange(state, webProgress, isSimulated) {
+  onContentBlockingEvent(event, webProgress, isSimulated) {
     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;
@@ -826,19 +826,19 @@ var ContentBlocking = {
     let anyDetected = false;
     let anyBlocking = false;
 
     for (let blocker of this.blockers) {
       // Store data on whether the blocker is activated in the current document for
       // reporting it using the "report breakage" dialog. Under normal circumstances this
       // dialog should only be able to open in the currently selected tab and onSecurityChange
       // runs on tab switch, so we can avoid associating the data with the document directly.
-      blocker.activated = blocker.isBlocking(state);
+      blocker.activated = blocker.isBlocking(event);
       blocker.categoryItem.classList.toggle("blocked", blocker.enabled);
-      let detected = blocker.isDetected(state);
+      let detected = blocker.isDetected(event);
       blocker.categoryItem.hidden = !detected;
       anyDetected = anyDetected || detected;
       anyBlocking = anyBlocking || blocker.activated;
     }
 
     let isBrowserPrivate = PrivateBrowsingUtils.isBrowserPrivate(gBrowser.selectedBrowser);
 
     // Check whether the user has added an exception for this site.
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4893,51 +4893,73 @@ var XULBrowserWindow = {
   onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
     this.status = aMessage;
     StatusPanel.update();
   },
 
   // Properties used to cache security state used to update the UI
   _state: null,
   _lastLocation: null,
+  _event: null,
+  _lastLocationForEvent: null,
+
+  // This is called in multiple ways:
+  //  1. Due to the nsIWebProgressListener.onContentBlockingEvent notification.
+  //  2. Called by tabbrowser.xml when updating the current browser.
+  //  3. Called directly during this object's initializations.
+  //  4. Due to the nsIWebProgressListener.onLocationChange notification.
+  // 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).
+  onContentBlockingEvent(aWebProgress, aRequest, aEvent, 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._event == aEvent &&
+        this._lastLocationForEvent == spec) {
+      return;
+    }
+    this._event = aEvent;
+    this._lastLocationForEvent = spec;
+
+    if (typeof(aIsSimulated) != "boolean" && typeof(aIsSimulated) != "undefined") {
+      throw "onContentBlockingEvent: aIsSimulated receieved an unexpected type";
+    }
+
+    ContentBlocking.onContentBlockingEvent(this._event, aWebProgress, aIsSimulated);
+  },
 
   // 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).
+  // aRequest will be null always in case 2 and 3, and sometimes in case 1.
   onSecurityChange(aWebProgress, aRequest, aState, 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.
       gIdentityHandler.refreshIdentityBlock();
       return;
     }
     this._state = aState;
     this._lastLocation = spec;
 
-    if (typeof(aIsSimulated) != "boolean" && typeof(aIsSimulated) != "undefined") {
-      throw "onSecurityChange: aIsSimulated receieved an unexpected type";
-    }
-
     // 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);
   },
 
   // 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
@@ -963,20 +963,23 @@ window._gBrowser = {
     // Update the URL bar.
     let webProgress = newBrowser.webProgress;
     this._callProgressListeners(null, "onLocationChange",
                                 [webProgress, null, newBrowser.currentURI, 0, true],
                                 true, false);
 
     let securityUI = newBrowser.securityUI;
     if (securityUI) {
+      this._callProgressListeners(null, "onSecurityChange",
+                                  [webProgress, null, securityUI.state],
+                                  true, false);
       // Include the true final argument to indicate that this event is
       // simulated (instead of being observed by the webProgressListener).
-      this._callProgressListeners(null, "onSecurityChange",
-                                  [webProgress, null, securityUI.state, true],
+      this._callProgressListeners(null, "onContentBlockingEvent",
+                                  [webProgress, null, securityUI.contentBlockingEvent, 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],
@@ -1713,20 +1716,24 @@ window._gBrowser = {
 
     // Restore the progress listener.
     aBrowser.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
 
     // Restore the securityUI state.
     let securityUI = aBrowser.securityUI;
     let state = securityUI ? securityUI.state :
       Ci.nsIWebProgressListener.STATE_IS_INSECURE;
+    this._callProgressListeners(aBrowser, "onSecurityChange",
+                                [aBrowser.webProgress, null, state],
+                                true, false);
+    let event = securityUI ? securityUI.contentBlockingEvent : 0;
     // 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],
+    this._callProgressListeners(aBrowser, "onContentBlockingEvent",
+                                [aBrowser.webProgress, null, event, 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 {
@@ -5187,16 +5194,20 @@ class TabProgressListener {
           gBrowser._getSwitcher().cleanUpTabAfterEviction(this.mTab);
         }
       }
     }
 
     if (!this.mBlank) {
       this._callProgressListeners("onLocationChange",
                                   [aWebProgress, aRequest, aLocation, aFlags]);
+      // Include the true final argument to indicate that this event is
+      // simulated (instead of being observed by the webProgressListener).
+      this._callProgressListeners("onContentBlockingEvent",
+                                  [aWebProgress, null, 0, true]);
     }
 
     if (topLevel) {
       this.mBrowser.lastURI = aLocation;
       this.mBrowser.lastLocationChange = Date.now();
     }
   }
 
@@ -5210,16 +5221,21 @@ class TabProgressListener {
     this.mMessage = aMessage;
   }
 
   onSecurityChange(aWebProgress, aRequest, aState) {
     this._callProgressListeners("onSecurityChange",
                                 [aWebProgress, aRequest, aState]);
   }
 
+  onContentBlockingEvent(aWebProgress, aRequest, aEvent) {
+    this._callProgressListeners("onContentBlockingEvent",
+                                [aWebProgress, aRequest, aEvent]);
+  }
+
   onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
     return this._callProgressListeners("onRefreshAttempted",
                                        [aWebProgress, aURI, aDelay, aSameURI]);
   }
 }
 TabProgressListener.prototype.QueryInterface = ChromeUtils.generateQI(
   ["nsIWebProgressListener",
    "nsIWebProgressListener2",
--- a/browser/base/content/test/trackingUI/browser_trackingUI_animation_2.js
+++ b/browser/base/content/test/trackingUI/browser_trackingUI_animation_2.js
@@ -64,29 +64,31 @@ async function testTrackingProtectionAni
   securityChanged = waitForSecurityChange(1, tabbrowser.ownerGlobal);
   tabbrowser.selectedTab = trackingCookiesTab;
   await securityChanged;
 
   ok(ContentBlocking.iconBox.hasAttribute("active"), "iconBox active");
   ok(!ContentBlocking.iconBox.hasAttribute("animate"), "iconBox not animating");
 
   info("Reload tracking cookies tab");
-  securityChanged = waitForSecurityChange(2, tabbrowser.ownerGlobal);
+  securityChanged = waitForSecurityChange(1, tabbrowser.ownerGlobal);
+  let contentBlockingEvent = waitForContentBlockingEvent(2, tabbrowser.ownerGlobal);
   tabbrowser.reload();
-  await securityChanged;
+  await Promise.all([securityChanged, contentBlockingEvent]);
 
   ok(ContentBlocking.iconBox.hasAttribute("active"), "iconBox active");
   ok(ContentBlocking.iconBox.hasAttribute("animate"), "iconBox animating");
   await BrowserTestUtils.waitForEvent(ContentBlocking.animatedIcon, "animationend");
 
   info("Reload tracking tab");
-  securityChanged = waitForSecurityChange(3, tabbrowser.ownerGlobal);
+  securityChanged = waitForSecurityChange(2, tabbrowser.ownerGlobal);
+  contentBlockingEvent = waitForContentBlockingEvent(3, tabbrowser.ownerGlobal);
   tabbrowser.selectedTab = trackingTab;
   tabbrowser.reload();
-  await securityChanged;
+  await Promise.all([securityChanged, contentBlockingEvent]);
 
   ok(ContentBlocking.iconBox.hasAttribute("active"), "iconBox active");
   ok(ContentBlocking.iconBox.hasAttribute("animate"), "iconBox animating");
   await BrowserTestUtils.waitForEvent(ContentBlocking.animatedIcon, "animationend");
 
   info("Inject tracking cookie inside tracking tab");
   securityChanged = waitForSecurityChange(1, tabbrowser.ownerGlobal);
   let timeoutPromise = new Promise(resolve => setTimeout(resolve, 500));
--- a/browser/base/content/test/trackingUI/browser_trackingUI_fetch.js
+++ b/browser/base/content/test/trackingUI/browser_trackingUI_fetch.js
@@ -1,23 +1,23 @@
 const URL = "http://mochi.test:8888/browser/browser/base/content/test/trackingUI/file_trackingUI_fetch.html";
 
 add_task(async function test_fetch() {
   await SpecialPowers.pushPrefEnv({ set: [
     ["privacy.trackingprotection.enabled", true],
   ]});
 
   await BrowserTestUtils.withNewTab({ gBrowser, url: URL }, async function(newTabBrowser) {
-    let securityChange = waitForSecurityChange();
+    let contentBlockingEvent = waitForContentBlockingEvent();
     await ContentTask.spawn(newTabBrowser, null, async function() {
       await content.wrappedJSObject.test_fetch()
                    .then(response => Assert.ok(false, "should have denied the request"))
                    .catch(e => Assert.ok(true, `Caught exception: ${e}`));
     });
-    await securityChange;
+    await contentBlockingEvent;
 
     let ContentBlocking = newTabBrowser.ownerGlobal.ContentBlocking;
     ok(ContentBlocking, "got CB object");
 
     ok(ContentBlocking.content.hasAttribute("detected"), "has detected content blocking");
     ok(ContentBlocking.iconBox.hasAttribute("active"), "icon box is active");
     is(ContentBlocking.iconBox.getAttribute("tooltiptext"),
        gNavigatorBundle.getString("trackingProtection.icon.activeTooltip"), "correct tooltip");
--- a/browser/base/content/test/trackingUI/browser_trackingUI_telemetry.js
+++ b/browser/base/content/test/trackingUI/browser_trackingUI_telemetry.js
@@ -49,17 +49,17 @@ add_task(async function testShieldHistog
   // Reset these to make counting easier
   getShieldHistogram().clear();
 
   await promiseTabLoadEvent(tab, BENIGN_PAGE);
   is(getShieldCounts()[0], 1, "Page loads without tracking");
 
   await promiseTabLoadEvent(tab, TRACKING_PAGE);
   // Note that right now the shield histogram is not measuring what
-  // you might think.  Since onSecurityChange fires twice for a tracking page,
+  // you might think.  Since onContentBlockingEvent fires twice for a tracking page,
   // the total page loads count is double counting, and the shield count
   // (which is meant to measure times when the shield wasn't shown) fires even
   // when tracking elements exist on the page.
   todo_is(getShieldCounts()[0], 1, "FIXME: TOTAL PAGE LOADS WITHOUT TRACKING IS DOUBLE COUNTING");
 
   info("Disable TP for the page (which reloads the page)");
   let tabReloadPromise = promiseTabLoadEvent(tab);
   document.querySelector("#tracking-action-unblock").doCommand();
--- a/browser/base/content/test/trackingUI/head.js
+++ b/browser/base/content/test/trackingUI/head.js
@@ -54,8 +54,28 @@ function waitForSecurityChange(numChange
           win.gBrowser.removeProgressListener(listener);
           resolve(n);
         }
       },
     };
     win.gBrowser.addProgressListener(listener);
   });
 }
+
+function waitForContentBlockingEvent(numChanges = 1, win = null) {
+  if (!win) {
+    win = window;
+  }
+  return new Promise(resolve => {
+    let n = 0;
+    let listener = {
+      onContentBlockingEvent() {
+        n = n + 1;
+        info("Received onContentBlockingEvent event " + n + " of " + numChanges);
+        if (n >= numChanges) {
+          win.gBrowser.removeProgressListener(listener);
+          resolve(n);
+        }
+      },
+    };
+    win.gBrowser.addProgressListener(listener);
+  });
+}
--- a/browser/components/enterprisepolicies/tests/browser/disable_app_update/browser_policy_disable_app_update.js
+++ b/browser/components/enterprisepolicies/tests/browser/disable_app_update/browser_policy_disable_app_update.js
@@ -19,8 +19,52 @@ add_task(async function test_update_pref
   await ContentTask.spawn(tab.linkedBrowser, null, async function() {
     let updateRadioGroup = content.document.getElementById("updateRadioGroup");
     is(updateRadioGroup.hidden, true,
        "Update choices should be diabled when app update is locked by policy");
   });
 
   BrowserTestUtils.removeTab(tab);
 });
+
+add_task(async function test_update_about_ui() {
+  let aboutDialog = await waitForAboutDialog();
+  let panelId = "policyDisabled";
+
+  await BrowserTestUtils.waitForCondition(() =>
+    (aboutDialog.document.getElementById("updateDeck").selectedPanel &&
+     aboutDialog.document.getElementById("updateDeck").selectedPanel.id == panelId),
+    "Waiting for expected panel ID - expected \"" + panelId + "\"");
+  is(aboutDialog.document.getElementById("updateDeck").selectedPanel.id, panelId,
+     "The About Dialog panel Id should equal " + panelId);
+
+  aboutDialog.close();
+});
+
+/**
+ * Waits for the About Dialog to load.
+ *
+ * @return A promise that returns the domWindow for the About Dialog and
+ *         resolves when the About Dialog loads.
+ */
+function waitForAboutDialog() {
+  return new Promise(resolve => {
+    var listener = {
+      onOpenWindow: aXULWindow => {
+        Services.wm.removeListener(listener);
+
+         async function aboutDialogOnLoad() {
+          domwindow.removeEventListener("load", aboutDialogOnLoad, true);
+          let chromeURI = "chrome://browser/content/aboutDialog.xul";
+          is(domwindow.document.location.href, chromeURI, "About dialog appeared");
+          resolve(domwindow);
+        }
+
+        var domwindow = aXULWindow.docShell.domWindow;
+        domwindow.addEventListener("load", aboutDialogOnLoad, true);
+      },
+      onCloseWindow: aXULWindow => {},
+    };
+
+    Services.wm.addListener(listener);
+    openAboutDialog();
+  });
+}
--- a/browser/components/extensions/parent/ext-tabs.js
+++ b/browser/components/extensions/parent/ext-tabs.js
@@ -1239,16 +1239,17 @@ 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) { },
+                  onContentBlockingEvent(webProgress, request, event) { },
                   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/originattributes/test/browser/browser.ini
+++ b/browser/components/originattributes/test/browser/browser.ini
@@ -9,16 +9,17 @@ support-files =
   file_favicon.png
   file_favicon.png^headers^
   file_favicon_cache.html
   file_favicon_cache.png
   file_favicon_thirdParty.html
   file_firstPartyBasic.html
   file_postMessage.html
   file_postMessageSender.html
+  file_saveAs.sjs
   file_sharedworker.html
   file_sharedworker.js
   file_thirdPartyChild.audio.ogg
   file_thirdPartyChild.embed.png
   file_thirdPartyChild.fetch.html
   file_thirdPartyChild.iframe.html
   file_thirdPartyChild.img.png
   file_thirdPartyChild.import.js
@@ -54,29 +55,31 @@ support-files =
   test_firstParty_postMessage.html
   test_form.html
   window.html
   window2.html
   window3.html
   window_redirect.html
   worker_blobify.js
   worker_deblobify.js
+  !/toolkit/content/tests/browser/common/mockTransfer.js
 
 [browser_broadcastChannel.js]
 [browser_cache.js]
 skip-if = verify
 [browser_cookieIsolation.js]
 [browser_favicon_firstParty.js]
 [browser_favicon_userContextId.js]
 [browser_firstPartyIsolation.js]
 skip-if = debug #Bug 1345346
 [browser_firstPartyIsolation_about_newtab.js]
 [browser_firstPartyIsolation_aboutPages.js]
 [browser_firstPartyIsolation_blobURI.js]
 [browser_firstPartyIsolation_js_uri.js]
+[browser_firstPartyIsolation_saveAs.js]
 [browser_localStorageIsolation.js]
 [browser_blobURLIsolation.js]
 skip-if = (verify && debug && (os == 'win'))
 [browser_imageCacheIsolation.js]
 [browser_sharedworker.js]
 [browser_httpauth.js]
 [browser_clientAuth.js]
 skip-if = verify
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/browser_firstPartyIsolation_saveAs.js
@@ -0,0 +1,266 @@
+/**
+ * Bug 1508355 - A test case for ensuring the saving channel has a correct first
+ *               party domain when going through different "Save ... AS."
+ */
+
+"use strict";
+
+/* import-globals-from ../../../../../toolkit/content/tests/browser/common/mockTransfer.js */
+Services.scriptloader
+        .loadSubScript("chrome://mochitests/content/browser/toolkit/content/tests/browser/common/mockTransfer.js",
+                       this);
+
+const TEST_FIRST_PARTY = "example.com";
+const TEST_ORIGIN = `http://${TEST_FIRST_PARTY}`;
+const TEST_BASE_PATH = "/browser/browser/components/originattributes/test/browser/";
+const TEST_PATH = `${TEST_BASE_PATH}file_saveAs.sjs`;
+const TEST_PATH_VIDEO = `${TEST_BASE_PATH}file_thirdPartyChild.video.ogv`;
+const TEST_PATH_IMAGE = `${TEST_BASE_PATH}file_favicon.png`;
+
+// For the "Save Page As" test, we will check the channel of the sub-resource
+// within the page. In this case, it is a image.
+const TEST_PATH_PAGE = `${TEST_BASE_PATH}file_favicon.png`;
+
+// For the "Save Frame As" test, we will check the channel of the sub-resource
+// within the frame. In this case, it is a image.
+const TEST_PATH_FRAME = `${TEST_BASE_PATH}file_favicon.png`;
+
+let MockFilePicker = SpecialPowers.MockFilePicker;
+MockFilePicker.init(window);
+
+add_task(async function setup() {
+  info("Setting the prefs.");
+
+  await SpecialPowers.pushPrefEnv({"set": [
+    ["privacy.firstparty.isolate", true],
+  ]});
+
+  info("Setting MockFilePicker.");
+  let tempDir = createTemporarySaveDirectory();
+
+  MockFilePicker.displayDirectory = tempDir;
+  MockFilePicker.showCallback = fp => {
+    info("MockFilePicker showCallback");
+
+    let fileName = fp.defaultString;
+    let destFile = tempDir.clone();
+    destFile.append(fileName);
+
+    MockFilePicker.setFiles([destFile]);
+    MockFilePicker.filterIndex = 0; // kSaveAsType_Complete
+
+    info("MockFilePicker showCallback done");
+  };
+
+  mockTransferRegisterer.register();
+
+  registerCleanupFunction(function() {
+    mockTransferRegisterer.unregister();
+    MockFilePicker.cleanup();
+    tempDir.remove(true);
+  });
+});
+
+function createTemporarySaveDirectory() {
+  let saveDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
+  saveDir.append("testsavedir");
+  saveDir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+  return saveDir;
+}
+
+function createPromiseForObservingChannel(aURL, aFirstParty) {
+  return new Promise(resolve => {
+    let observer = (aSubject, aTopic) => {
+      if (aTopic === "http-on-modify-request") {
+        let httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
+        let reqLoadInfo = httpChannel.loadInfo;
+
+        // Make sure this is the request which we want to check.
+        if (!httpChannel.URI.spec.endsWith(aURL)) {
+          return;
+        }
+
+        info(`Checking loadInfo for URI: ${httpChannel.URI.spec}\n`);
+        is(reqLoadInfo.originAttributes.firstPartyDomain, aFirstParty,
+          "The loadInfo has correct first party domain");
+
+        Services.obs.removeObserver(observer, "http-on-modify-request");
+        resolve();
+      }
+    };
+
+    Services.obs.addObserver(observer, "http-on-modify-request");
+  });
+}
+
+function createPromiseForTransferComplete() {
+  return new Promise((resolve) => {
+    function onTransferComplete(downloadSuccess) {
+      ok(downloadSuccess, "File should have been downloaded successfully");
+      // Clear the callback for now.
+      mockTransferCallback = () => { };
+      resolve();
+    }
+
+    mockTransferCallback = onTransferComplete;
+  });
+}
+
+async function doCommandForFrameType() {
+  info("Opening the frame sub-menu under the context menu.");
+  let contextMenu = document.getElementById("contentAreaContextMenu");
+  let frameMenuPopup = contextMenu.querySelector("#frame").menupopup;
+  let frameMenuPopupPromise = BrowserTestUtils.waitForEvent(frameMenuPopup,
+                                                            "popupshown");
+
+  frameMenuPopup.openPopup();
+  await frameMenuPopupPromise;
+
+  info("Triggering the save process.");
+  let saveFrameCommand = contextMenu.querySelector("#context-saveframe");
+  saveFrameCommand.doCommand();
+}
+
+add_task(async function testContextMenuSaveAs() {
+  const TEST_DATA = [
+    { type: "link", path: TEST_PATH, target: "#link1" },
+    { type: "video", path: TEST_PATH_VIDEO, target: "#video1" },
+    { type: "image", path: TEST_PATH_IMAGE, target: "#image1" },
+    { type: "page", path: TEST_PATH_PAGE, target: "body" },
+    { type: "frame", path: TEST_PATH_FRAME, target: "#frame1",
+      doCommandFunc: doCommandForFrameType },
+  ];
+
+  for (const data of TEST_DATA) {
+    info(`Open a new tab for testing "Save ${data.type} as" in context menu.`);
+    let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser,
+      `${TEST_ORIGIN}${TEST_PATH}?${data.type}=1`);
+
+    let popupShownPromise = BrowserTestUtils.waitForEvent(document, "popupshown");
+
+    info("Open the context menu.");
+    await BrowserTestUtils.synthesizeMouseAtCenter(data.target, {
+      type: "contextmenu",
+      button: 2,
+      },
+      gBrowser.selectedBrowser);
+
+    await popupShownPromise;
+
+    let transferCompletePromise = createPromiseForTransferComplete();
+    let observerPromise = createPromiseForObservingChannel(data.path, TEST_FIRST_PARTY);
+
+    // Select "Save As" option from context menu.
+    if (!data.doCommandFunc) {
+      let saveElement = document.getElementById(`context-save${data.type}`);
+      info("Triggering the save process.");
+      saveElement.doCommand();
+    } else {
+      await data.doCommandFunc();
+    }
+
+    info("Waiting for the channel.");
+    await observerPromise;
+
+    info("Close the context menu.");
+    let contextMenu = document.getElementById("contentAreaContextMenu");
+    let popupHiddenPromise = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
+    contextMenu.hidePopup();
+    await popupHiddenPromise;
+
+    info("Wait until the save is finished.");
+    await transferCompletePromise;
+
+    BrowserTestUtils.removeTab(tab);
+  }
+});
+
+add_task(async function testFileMenuSavePageAs() {
+  info(`Open a new tab for testing "Save Page AS" in the file menu.`);
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser,
+    `${TEST_ORIGIN}${TEST_PATH}?page=1`);
+
+  let transferCompletePromise = createPromiseForTransferComplete();
+  let observerPromise = createPromiseForObservingChannel(TEST_PATH_PAGE, TEST_FIRST_PARTY);
+
+  let menubar = document.getElementById("main-menubar");
+  let filePopup = document.getElementById("menu_FilePopup");
+
+  // We only use the shortcut keys to open the file menu in Windows and Linux.
+  // Mac doesn't have a shortcut to only open the file menu. Instead, we directly
+  // trigger the save in MAC without any UI interactions.
+  if (Services.appinfo.OS !== "Darwin") {
+    let menubarActive = BrowserTestUtils.waitForEvent(menubar, "DOMMenuBarActive");
+    EventUtils.synthesizeKey("KEY_F10");
+    await menubarActive;
+
+    let popupShownPromise = BrowserTestUtils.waitForEvent(filePopup, "popupshown");
+    // In window, it still needs one extra down key to open the file menu.
+    if (Services.appinfo.OS === "WINNT") {
+      EventUtils.synthesizeKey("KEY_ArrowDown");
+    }
+    await popupShownPromise;
+  }
+
+  info("Triggering the save process.");
+  let fileSavePageAsElement = document.getElementById("menu_savePage");
+  fileSavePageAsElement.doCommand();
+
+  info("Waiting for the channel.");
+  await observerPromise;
+
+  // Close the file menu.
+  if (Services.appinfo.OS !== "Darwin") {
+    let popupHiddenPromise = BrowserTestUtils.waitForEvent(filePopup, "popuphidden");
+    filePopup.hidePopup();
+    await popupHiddenPromise;
+  }
+
+  info("Wait until the save is finished.");
+  await transferCompletePromise;
+
+  BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function testPageInfoMediaSaveAs() {
+  info(`Open a new tab for testing "Save AS" in the media panel of the page info.`);
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser,
+    `${TEST_ORIGIN}${TEST_PATH}?pageinfo=1`);
+
+  info("Open the media panel of the pageinfo.");
+  let pageInfo = BrowserPageInfo(gBrowser.selectedBrowser.currentURI.spec,
+    "mediaTab");
+
+  await BrowserTestUtils.waitForEvent(pageInfo, "load");
+  await new Promise(resolve => pageInfo.onFinished.push(() => executeSoon(resolve)));
+
+  let imageTree = pageInfo.document.getElementById("imagetree");
+  let imageRowsNum = imageTree.view.rowCount;
+
+  is(imageRowsNum, 2, "There should be two media items here.");
+
+  for (let i = 0; i < imageRowsNum; i++) {
+    imageTree.view.selection.select(i);
+    imageTree.ensureRowIsVisible(i);
+    imageTree.focus();
+
+    let url = pageInfo.gImageView.data[i][0]; // COL_IMAGE_ADDRESS
+    info(`Start to save the media item with URL: ${url}`);
+
+    let transferCompletePromise = createPromiseForTransferComplete();
+    let observerPromise = createPromiseForObservingChannel(url, TEST_FIRST_PARTY);
+
+    info("Triggering the save process.");
+    let saveElement = pageInfo.document.getElementById("imagesaveasbutton");
+    saveElement.doCommand();
+
+    info("Waiting for the channel.");
+    await observerPromise;
+
+    info("Wait until the save is finished.");
+    await transferCompletePromise;
+  }
+
+  pageInfo.close();
+  BrowserTestUtils.removeTab(tab);
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_saveAs.sjs
@@ -0,0 +1,40 @@
+const HTTP_ORIGIN = "http://example.com";
+const SECOND_ORIGIN = "http://example.org";
+const URI_PATH = "/browser/browser/components/originattributes/test/browser/";
+const LINK_PATH = `${URI_PATH}file_saveAs.sjs`;
+// Reusing existing ogv file for testing.
+const VIDEO_PATH = `${URI_PATH}file_thirdPartyChild.video.ogv`;
+// Reusing existing png file for testing.
+const IMAGE_PATH = `${URI_PATH}file_favicon.png`;
+const FRAME_PATH = `${SECOND_ORIGIN}${URI_PATH}file_saveAs.sjs?image=1`
+
+Components.utils.importGlobalProperties(["URLSearchParams"]);
+
+function handleRequest(aRequest, aResponse) {
+  var params = new URLSearchParams(aRequest.queryString);
+  aResponse.setStatusLine(aRequest.httpVersion, 200);
+  aResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
+  let contentBody = "";
+
+  if (params.has("link")) {
+    contentBody = `<a href="${LINK_PATH}" id="link1">this is a link</a>`;
+  } else if (params.has("video")) {
+    contentBody = `<video src="${VIDEO_PATH}" id="video1"> </video>`;
+  } else if (params.has("image")) {
+    contentBody = `<img src="${IMAGE_PATH}" id="image1">`;
+  } else if (params.has("page")) {
+    // We need at least one resource, like a img, a link or a script, to trigger
+    // downloading resources in "Save Page As". Otherwise, it will output the
+    // document directly without any network request.
+    contentBody = `<img src="${IMAGE_PATH}">`;
+  } else if (params.has("frame")) {
+    // Like "Save Page As", we need to put at least one resource in the frame.
+    // Here we also use a image.
+    contentBody = `<iframe src="${FRAME_PATH}" id="frame1"></iframe>`;
+  } else if (params.has("pageinfo")) {
+    contentBody = `<img src="${IMAGE_PATH}" id="image1">
+                   <video src="${VIDEO_PATH}" id="video1"> </video>`;
+  }
+
+  aResponse.write(`<html><body>${contentBody}</body></html>`);
+}
--- a/browser/components/shell/nsMacShellService.cpp
+++ b/browser/components/shell/nsMacShellService.cpp
@@ -184,16 +184,23 @@ nsMacShellService::OnStatusChange(nsIWeb
 
 NS_IMETHODIMP
 nsMacShellService::OnSecurityChange(nsIWebProgress* aWebProgress,
                                     nsIRequest* aRequest, uint32_t aState) {
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsMacShellService::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
+                                          nsIRequest* aRequest,
+                                          uint32_t aEvent) {
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsMacShellService::OnStateChange(nsIWebProgress* aWebProgress,
                                  nsIRequest* aRequest, uint32_t aStateFlags,
                                  nsresult aStatus) {
   if (aStateFlags & STATE_STOP) {
     nsCOMPtr<nsIObserverService> os(
         do_GetService("@mozilla.org/observer-service;1"));
     if (os)
       os->NotifyObservers(nullptr, "shell:desktop-background-changed", nullptr);
--- a/browser/modules/PermissionUI.jsm
+++ b/browser/modules/PermissionUI.jsm
@@ -858,16 +858,17 @@ StorageAccessPermissionPrompt.prototype 
     return host;
   },
 
   get popupOptions() {
     return {
       displayURI: false,
       name: this.prettifyHostPort(this.principal.URI),
       secondName: this.prettifyHostPort(this.topLevelPrincipal.URI),
+      escAction: "buttoncommand",
     };
   },
 
   onShown() {
     let document = this.browser.ownerDocument;
     let label =
       gBrowserBundle.formatStringFromName("storageAccess.description.label",
                                           [this.prettifyHostPort(this.request.principal.URI), "<>"], 2);
--- a/browser/modules/test/browser/browser_UsageTelemetry_domains.js
+++ b/browser/modules/test/browser/browser_UsageTelemetry_domains.js
@@ -19,16 +19,17 @@ Services.obs.notifyObservers(null, TELEM
  * @resolves When navigating to a non-error page.
  */
 function browserLocationChanged(browser) {
   return new Promise(resolve => {
     let wpl = {
       onStateChange() {},
       onSecurityChange() {},
       onStatusChange() {},
+      onContentBlockingEvent() {},
       onLocationChange(aWebProgress, aRequest, aURI, aFlags) {
         if (!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE)) {
           browser.webProgress.removeProgressListener(filter);
           filter.removeProgressListener(wpl);
           resolve();
         }
       },
       QueryInterface: ChromeUtils.generateQI([
--- a/devtools/client/inspector/rules/components/Declaration.js
+++ b/devtools/client/inspector/rules/components/Declaration.js
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
+const { getStr } = require("../utils/l10n");
 const Types = require("../types");
 
 class Declaration extends PureComponent {
   static get propTypes() {
     return {
       declaration: PropTypes.shape(Types.declaration).isRequired,
       onToggleDeclaration: PropTypes.func.isRequired,
     };
@@ -40,31 +41,29 @@ class Declaration extends PureComponent 
 
   onToggleDeclarationClick(event) {
     event.stopPropagation();
     const { id, ruleId } = this.props.declaration;
     this.props.onToggleDeclaration(ruleId, id);
   }
 
   renderComputedPropertyList() {
-    const { computedProperties } = this.props.declaration;
-
-    if (!computedProperties.length) {
+    if (!this.state.isComputedListExpanded) {
       return null;
     }
 
     return (
       dom.ul(
         {
           className: "ruleview-computedlist",
           style: {
-            display: this.state.isComputedListExpanded ? "block" : "",
+            display: "block",
           },
         },
-        computedProperties.map(({ name, value, isOverridden }) => {
+        this.props.declaration.computedProperties.map(({ name, value, isOverridden }) => {
           return (
             dom.li(
               {
                 key: `${name}${value}`,
                 className: "ruleview-computed" +
                            (isOverridden ? " ruleview-overridden" : ""),
               },
               dom.span({ className: "ruleview-namecontainer" },
@@ -78,29 +77,30 @@ class Declaration extends PureComponent 
             )
           );
         })
       )
     );
   }
 
   renderShorthandOverriddenList() {
-    const { declaration } = this.props;
+    if (this.state.isComputedListExpanded || this.props.declaration.isOverridden) {
+      return null;
+    }
 
-    if (this.state.isComputedListExpanded || declaration.isOverridden) {
+    const overriddenComputedProperties = this.props.declaration.computedProperties
+      .filter(prop => prop.isOverridden);
+
+    if (!overriddenComputedProperties.length) {
       return null;
     }
 
     return (
       dom.ul({ className: "ruleview-overridden-items" },
-        declaration.computedProperties.map(({ name, value, isOverridden }) => {
-          if (!isOverridden) {
-            return null;
-          }
-
+        overriddenComputedProperties.map(({ name, value }) => {
           return (
             dom.li(
               {
                 key: `${name}${value}`,
                 className: "ruleview-overridden-item ruleview-overridden",
               },
               dom.span({ className: "ruleview-namecontainer" },
                 dom.span({ className: "ruleview-propertyname theme-fg-color5" }, name),
@@ -113,25 +113,26 @@ class Declaration extends PureComponent 
             )
           );
         })
       )
     );
   }
 
   render() {
-    const { declaration } = this.props;
     const {
       computedProperties,
+      isDeclarationValid,
       isEnabled,
       isKnownProperty,
+      isNameValid,
       isOverridden,
       name,
       value,
-    } = declaration;
+    } = this.props.declaration;
 
     return (
       dom.li(
         {
           className: "ruleview-property" +
                      (!isEnabled || !isKnownProperty || isOverridden ?
                       " ruleview-overridden" : ""),
         },
@@ -150,17 +151,28 @@ class Declaration extends PureComponent 
             className: "ruleview-expander theme-twisty" +
                        (this.state.isComputedListExpanded ? " open" : ""),
             onClick: this.onComputedExpanderClick,
             style: { display: computedProperties.length ? "inline-block" : "none" },
           }),
           dom.span({ className: "ruleview-propertyvaluecontainer" },
             dom.span({ className: "ruleview-propertyvalue theme-fg-color1" }, value),
             ";"
-          )
+          ),
+          dom.div({
+            className: "ruleview-warning" +
+                       (isDeclarationValid ? " hidden" : ""),
+            title: isNameValid ?
+                   getStr("rule.warningName.title") : getStr("rule.warning.title"),
+          }),
+          dom.div({
+            className: "ruleview-overridden-rule-filter" +
+                       (!isDeclarationValid || !isOverridden ? " hidden" : ""),
+            title: getStr("rule.filterProperty.title"),
+          })
         ),
         this.renderComputedPropertyList(),
         this.renderShorthandOverriddenList()
       )
     );
   }
 }
 
--- a/devtools/client/inspector/rules/components/Rule.js
+++ b/devtools/client/inspector/rules/components/Rule.js
@@ -27,24 +27,30 @@ class Rule extends PureComponent {
   render() {
     const {
       onToggleDeclaration,
       onToggleSelectorHighlighter,
       rule,
     } = this.props;
     const {
       declarations,
+      isUnmatched,
+      isUserAgentStyle,
       selector,
       sourceLink,
       type,
     } = rule;
 
     return (
       dom.div(
-        { className: "ruleview-rule devtools-monospace" },
+        {
+          className: "ruleview-rule devtools-monospace" +
+                     (isUnmatched ? " unmatched" : "") +
+                     (isUserAgentStyle ? " uneditable" : ""),
+        },
         SourceLink({ sourceLink }),
         dom.div({ className: "ruleview-code" },
           dom.div({},
             Selector({
               selector,
               type,
             }),
             type !== CSSRule.KEYFRAME_RULE ?
--- a/devtools/client/inspector/rules/reducers/rules.js
+++ b/devtools/client/inspector/rules/reducers/rules.js
@@ -27,20 +27,25 @@ const INITIAL_RULES = {
  * @return {Object} containing the properties needed to render a CSS declaration.
  */
 function getDeclarationState(declaration, ruleId) {
   return {
     // Array of the computed properties for a CSS declaration.
     computedProperties: declaration.computedProperties,
     // An unique CSS declaration id.
     id: declaration.id,
+    // Whether or not the declaration is valid. (Does it make sense for this value
+    // to be assigned to this property name?)
+    isDeclarationValid: declaration.isValid(),
     // Whether or not the declaration is enabled.
     isEnabled: declaration.enabled,
     // Whether or not the declaration's property name is known.
     isKnownProperty: declaration.isKnownProperty,
+    // Whether or not the property name is valid.
+    isNameValid: declaration.isNameValid(),
     // Whether or not the the declaration is overridden.
     isOverridden: !!declaration.overridden,
     // The declaration's property name.
     name: declaration.name,
     // The declaration's priority (either "important" or an empty string).
     priority: declaration.priority,
     // The CSS rule id that is associated with this CSS declaration.
     ruleId,
--- a/devtools/client/inspector/rules/types.js
+++ b/devtools/client/inspector/rules/types.js
@@ -20,22 +20,29 @@ const declaration = exports.declaration 
     priority: PropTypes.string,
     // The computed property value.
     value: PropTypes.string,
   })),
 
   // An unique CSS declaration id.
   id: PropTypes.string,
 
+  // Whether or not the declaration is valid. (Does it make sense for this value
+  // to be assigned to this property name?)
+  isDeclarationValid: PropTypes.bool,
+
   // Whether or not the declaration is enabled.
   isEnabled: PropTypes.bool,
 
   // Whether or not the declaration's property name is known.
   isKnownProperty: PropTypes.bool,
 
+  // Whether or not the property name is valid.
+  isNameValid: PropTypes.bool,
+
   // Whether or not the the declaration is overridden.
   isOverridden: PropTypes.bool,
 
   // The declaration's property name.
   name: PropTypes.string,
 
   // The declaration's priority (either "important" or an empty string).
   priority: PropTypes.string,
--- a/devtools/client/responsive.html/browser/tunnel.js
+++ b/devtools/client/responsive.html/browser/tunnel.js
@@ -417,16 +417,17 @@ MessageManagerTunnel.prototype = {
     "PageStyle:StyleSheets",
     // Messages sent to RemoteWebProgress.jsm
     "Content:LoadURIResult",
     "Content:LocationChange",
     "Content:ProgressChange",
     "Content:SecurityChange",
     "Content:StateChange",
     "Content:StatusChange",
+    "Content:ContentBlockingEvent",
     // Messages sent to browser.js
     "DOMTitleChanged",
     "ImageDocumentLoaded",
     "Forms:ShowDropDown",
     "Forms:HideDropDown",
     "InPermitUnload",
     "PermitUnload",
     // Messages sent to tabbrowser.xml
--- a/devtools/client/themes/rules.css
+++ b/devtools/client/themes/rules.css
@@ -167,17 +167,19 @@
 .ruleview-propertyvaluecontainer a {
   color: var(--theme-highlight-purple);
   cursor: pointer;
 }
 
 .ruleview-computedlist,
 .ruleview-expandable-container[hidden],
 .ruleview-overridden-items[hidden],
+.ruleview-overridden-rule-filter.hidden,
 .ruleview-overridden-rule-filter[hidden],
+.ruleview-warning.hidden,
 .ruleview-warning[hidden],
 .ruleview-overridden .ruleview-grid {
   display: none;
 }
 
 .ruleview-computedlist[user-open],
 .ruleview-computedlist[filter-open],
 .ruleview-overridden-items {
@@ -252,41 +254,51 @@
 #ruleview-container-focusable > .ruleview-rule:last-child {
   border-bottom: none;
 }
 
 /**
  * Display rules that don't match the current selected element and uneditable
  * user agent styles differently
  */
+.ruleview-rule.unmatched,
+.ruleview-rule.uneditable,
 .ruleview-rule[unmatched=true],
 .ruleview-rule[uneditable=true] {
   background: var(--theme-tab-toolbar-background);
 }
 
+.ruleview-rule.unmatched,
 .ruleview-rule[unmatched=true] {
   opacity: 0.5;
 }
 
+.ruleview-rule.uneditable :focus,
 .ruleview-rule[uneditable=true] :focus {
   outline: none;
 }
 
+.ruleview-rule.uneditable .theme-link,
 .ruleview-rule[uneditable=true] .theme-link {
   color: var(--theme-highlight-bluegrey);
 }
 
+.ruleview-rule.uneditable .ruleview-enableproperty,
 .ruleview-rule[uneditable=true] .ruleview-enableproperty {
   visibility: hidden;
 }
 
+.ruleview-rule.uneditable .ruleview-swatch,
 .ruleview-rule[uneditable=true] .ruleview-swatch {
   cursor: default;
 }
 
+.ruleview-rule.uneditable .ruleview-namecontainer > .ruleview-propertyname,
+.ruleview-rule.uneditable .ruleview-propertyvaluecontainer >
+.ruleview-propertyvalue,
 .ruleview-rule[uneditable=true] .ruleview-namecontainer > .ruleview-propertyname,
 .ruleview-rule[uneditable=true] .ruleview-propertyvaluecontainer >
 .ruleview-propertyvalue {
   border-bottom-color: transparent;
 }
 
 .ruleview-overridden-rule-filter {
   background-image: url(chrome://devtools/skin/images/filter.svg#filterinput);
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -6543,16 +6543,23 @@ nsDocShell::OnStatusChange(nsIWebProgres
 
 NS_IMETHODIMP
 nsDocShell::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
                              uint32_t aState) {
   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDocShell::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
+                                   nsIRequest* aRequest, uint32_t aEvent) {
+  MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
+  return NS_OK;
+}
+
 nsresult nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
                                  nsIChannel* aChannel, nsresult aStatus) {
   if (!aChannel) {
     return NS_ERROR_NULL_POINTER;
   }
 
   // Make sure to discard the initial client if we never created the initial
   // about:blank document.  Do this before possibly returning from the method
--- a/docshell/base/nsDocShellTreeOwner.cpp
+++ b/docshell/base/nsDocShellTreeOwner.cpp
@@ -695,16 +695,23 @@ nsDocShellTreeOwner::OnStatusChange(nsIW
 }
 
 NS_IMETHODIMP
 nsDocShellTreeOwner::OnSecurityChange(nsIWebProgress* aWebProgress,
                                       nsIRequest* aRequest, uint32_t aState) {
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDocShellTreeOwner::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
+                                            nsIRequest* aRequest,
+                                            uint32_t aEvent) {
+  return NS_OK;
+}
+
 //*****************************************************************************
 // nsDocShellTreeOwner: Accessors
 //*****************************************************************************
 
 void nsDocShellTreeOwner::WebBrowser(nsWebBrowser* aWebBrowser) {
   if (!aWebBrowser) {
     RemoveChromeListeners();
   }
--- a/docshell/test/browser/browser_onbeforeunload_navigation.js
+++ b/docshell/test/browser/browser_onbeforeunload_navigation.js
@@ -150,16 +150,17 @@ var tabStateListener = {
     if ((stateFlags & startDocumentFlags) == startDocumentFlags) {
       loadStarted = true;
     }
   },
   onStatusChange: () => {},
   onLocationChange: () => {},
   onSecurityChange: () => {},
   onProgressChange: () => {},
+  onContentBlockingEvent: () => {},
   QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener])
 };
 
 function onTabModalDialogLoaded(node) {
   let mm = testTab.linkedBrowser.messageManager;
   mm.sendAsyncMessage("test-beforeunload:dialog");
 
   if (gMultiProcessBrowser) {
--- a/dom/base/nsContentSink.h
+++ b/dom/base/nsContentSink.h
@@ -241,16 +241,18 @@ class nsContentSink : public nsICSSLoade
   // Start layout.  If aIgnorePendingSheets is true, this will happen even if
   // we still have stylesheet loads pending.  Otherwise, we'll wait until the
   // stylesheets are all done loading.
  public:
   void StartLayout(bool aIgnorePendingSheets);
 
   static void NotifyDocElementCreated(Document* aDoc);
 
+  Document* GetDocument() { return mDocument; }
+
  protected:
   void FavorPerformanceHint(bool perfOverStarvation, uint32_t starvationDelay);
 
   inline int32_t GetNotificationInterval() {
     if (mDynamicLowerValue) {
       return 1000;
     }
 
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -5333,17 +5333,17 @@ void nsGlobalWindowOuter::FirePopupBlock
   RefPtr<PopupBlockedEvent> event = PopupBlockedEvent::Constructor(
       aDoc, NS_LITERAL_STRING("DOMPopupBlocked"), init);
 
   event->SetTrusted(true);
 
   aDoc->DispatchEvent(*event);
 }
 
-void nsGlobalWindowOuter::NotifyContentBlockingState(unsigned aState,
+void nsGlobalWindowOuter::NotifyContentBlockingEvent(unsigned aEvent,
                                                      nsIChannel* aChannel,
                                                      bool aBlocked,
                                                      nsIURI* aURIHint) {
   MOZ_ASSERT(aURIHint);
 
   nsCOMPtr<nsIDocShell> docShell = GetDocShell();
   if (!docShell) {
     return;
@@ -5351,105 +5351,105 @@ void nsGlobalWindowOuter::NotifyContentB
   nsCOMPtr<Document> doc = docShell->GetDocument();
   NS_ENSURE_TRUE_VOID(doc);
 
   // This event might come after the user has navigated to another page.
   // To prevent showing the TrackingProtection UI on the wrong page, we need to
   // check that the loading URI for the channel is the same as the URI currently
   // loaded in the document.
   if (!SameLoadingURI(doc, aChannel) &&
-      aState == nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT) {
+      aEvent == nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT) {
     return;
   }
 
-  // Notify nsIWebProgressListeners of this security event.
+  // Notify nsIWebProgressListeners of this content blocking event.
   // Can be used to change the UI state.
   nsresult rv = NS_OK;
   nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell, &rv);
   NS_ENSURE_SUCCESS_VOID(rv);
-  uint32_t state = 0;
+  uint32_t event = 0;
   nsCOMPtr<nsISecureBrowserUI> securityUI;
   docShell->GetSecurityUI(getter_AddRefs(securityUI));
   if (!securityUI) {
     return;
   }
-  securityUI->GetState(&state);
+  securityUI->GetContentBlockingEvent(&event);
   nsAutoCString origin;
   nsContentUtils::GetASCIIOrigin(aURIHint, origin);
 
   bool blockedValue = aBlocked;
   bool unblocked = false;
-  if (aState == nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT) {
+  if (aEvent == nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT) {
     doc->SetHasTrackingContentBlocked(aBlocked, origin);
     if (!aBlocked) {
       unblocked = !doc->GetHasTrackingContentBlocked();
     }
-  } else if (aState == nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT) {
+  } else if (aEvent == nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT) {
     doc->SetHasTrackingContentLoaded(aBlocked, origin);
     if (!aBlocked) {
       unblocked = !doc->GetHasTrackingContentLoaded();
     }
-  } else if (aState ==
+  } else if (aEvent ==
              nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION) {
     doc->SetHasCookiesBlockedByPermission(aBlocked, origin);
     if (!aBlocked) {
       unblocked = !doc->GetHasCookiesBlockedByPermission();
     }
-  } else if (aState == nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER) {
+  } else if (aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER) {
     doc->SetHasTrackingCookiesBlocked(aBlocked, origin);
     if (!aBlocked) {
       unblocked = !doc->GetHasTrackingCookiesBlocked();
     }
-  } else if (aState == nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL) {
+  } else if (aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL) {
     doc->SetHasAllCookiesBlocked(aBlocked, origin);
     if (!aBlocked) {
       unblocked = !doc->GetHasAllCookiesBlocked();
     }
-  } else if (aState == nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN) {
+  } else if (aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN) {
     doc->SetHasForeignCookiesBlocked(aBlocked, origin);
     if (!aBlocked) {
       unblocked = !doc->GetHasForeignCookiesBlocked();
     }
-  } else if (aState == nsIWebProgressListener::STATE_COOKIES_LOADED) {
+  } else if (aEvent == nsIWebProgressListener::STATE_COOKIES_LOADED) {
     MOZ_ASSERT(!aBlocked,
                "We don't expected to see blocked STATE_COOKIES_LOADED");
     // Note that the logic in this branch is the logical negation of the logic
     // in other branches, since the Document API we have is phrased in
     // "loaded" terms as opposed to "blocked" terms.
     blockedValue = !aBlocked;
     doc->SetHasCookiesLoaded(blockedValue, origin);
     if (!aBlocked) {
       unblocked = !doc->GetHasCookiesLoaded();
     }
   } else {
     // Ignore nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT;
   }
-  const uint32_t oldState = state;
+  const uint32_t oldEvent = event;
   if (blockedValue) {
-    state |= aState;
+    event |= aEvent;
   } else if (unblocked) {
-    state &= ~aState;
-  }
-
-  if (state == oldState
+    event &= ~aEvent;
+  }
+
+  if (event == oldEvent
 #ifdef ANDROID
       // GeckoView always needs to notify about blocked trackers, since the
       // GeckoView API always needs to report the URI and type of any blocked
       // tracker.
       // We use a platform-dependent code path here because reporting this
       // notification on desktop platforms isn't necessary and doing so can have
       // a big performance cost.
-      && aState != nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT
+      && aEvent != nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT
 #endif
   ) {
     // Avoid dispatching repeated notifications when nothing has changed
     return;
   }
 
-  eventSink->OnSecurityChange(aChannel, state);
+  eventSink->OnContentBlockingEvent(aChannel, event);
 }
 
 // static
 bool nsGlobalWindowOuter::SameLoadingURI(Document* aDoc, nsIChannel* aChannel) {
   nsCOMPtr<nsIURI> docURI = aDoc->GetDocumentURI();
   nsCOMPtr<nsILoadInfo> channelLoadInfo = aChannel->GetLoadInfo();
   if (!channelLoadInfo || !docURI) {
     return false;
--- a/dom/base/nsGlobalWindowOuter.h
+++ b/dom/base/nsGlobalWindowOuter.h
@@ -453,17 +453,17 @@ class nsGlobalWindowOuter final : public
   bool HadOriginalOpener() const { return mHadOriginalOpener; }
 
   bool IsTopLevelWindow();
 
   virtual void FirePopupBlockedEvent(
       Document* aDoc, nsIURI* aPopupURI, const nsAString& aPopupWindowName,
       const nsAString& aPopupWindowFeatures) override;
 
-  virtual void NotifyContentBlockingState(unsigned aState, nsIChannel* aChannel,
+  virtual void NotifyContentBlockingEvent(unsigned aEvent, nsIChannel* aChannel,
                                           bool aBlocked,
                                           nsIURI* aURIHint) override;
 
   void AddSizeOfIncludingThis(nsWindowSizes& aWindowSizes) const;
 
   void AllowScriptsToClose() { mAllowScriptsToClose = true; }
 
   // Outer windows only.
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -1018,17 +1018,17 @@ class nsPIDOMWindowOuter : public mozIDO
 
   /**
    * Fire a popup blocked event on the document.
    */
   virtual void FirePopupBlockedEvent(Document* aDoc, nsIURI* aPopupURI,
                                      const nsAString& aPopupWindowName,
                                      const nsAString& aPopupWindowFeatures) = 0;
 
-  virtual void NotifyContentBlockingState(unsigned aState, nsIChannel* aChannel,
+  virtual void NotifyContentBlockingEvent(unsigned aEvent, nsIChannel* aChannel,
                                           bool aBlocked, nsIURI* aURIHint) = 0;
 
   // WebIDL-ish APIs
   void MarkUncollectableForCCGeneration(uint32_t aGeneration) {
     mMarkedCCGeneration = aGeneration;
   }
 
   uint32_t GetMarkedCCGeneration() { return mMarkedCCGeneration; }
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -1196,47 +1196,34 @@ BrowserElementChild.prototype = {
       else if (state & Ci.nsIWebProgressListener.STATE_IS_INSECURE) {
         securityStateDesc = 'insecure';
       }
       else {
         debug("Unexpected securitychange state!");
         securityStateDesc = '???';
       }
 
-      var trackingStateDesc;
-      if (state & Ci.nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT) {
-        trackingStateDesc = 'loaded_tracking_content';
-      }
-      else if (state & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT) {
-        trackingStateDesc = 'blocked_tracking_content';
-      }
-
       var mixedStateDesc;
       if (state & Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT) {
         mixedStateDesc = 'blocked_mixed_active_content';
       }
       else if (state & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT) {
         // Note that STATE_LOADED_MIXED_ACTIVE_CONTENT implies STATE_IS_BROKEN
         mixedStateDesc = 'loaded_mixed_active_content';
       }
 
       var isEV = !!(state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL);
-      var isTrackingContent = !!(state &
-        (Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT |
-        Ci.nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT));
       var isMixedContent = !!(state &
         (Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT |
         Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT));
 
       sendAsyncMsg('securitychange', {
         state: securityStateDesc,
-        trackingState: trackingStateDesc,
         mixedState: mixedStateDesc,
         extendedValidation: isEV,
-        trackingContent: isTrackingContent,
         mixedContent: isMixedContent,
       });
     },
   },
 
   // Expose the message manager for WebApps and others.
   _messageManagerPublic: {
     sendAsyncMessage: global.sendAsyncMessage.bind(global),
--- a/dom/browser-element/mochitest/browserElement_SecurityChange.js
+++ b/dom/browser-element/mochitest/browserElement_SecurityChange.js
@@ -4,19 +4,16 @@
 // Bug 763694 - Test that <iframe mozbrowser> delivers proper
 // mozbrowsersecuritychange events.
 
 "use strict";
 SimpleTest.waitForExplicitFinish();
 browserElementTestHelpers.setEnabledPref(true);
 browserElementTestHelpers.addPermission();
 
-const { Services } = SpecialPowers.Cu.import('resource://gre/modules/Services.jsm');
-const { UrlClassifierTestUtils } = SpecialPowers.Cu.import('resource://testing-common/UrlClassifierTestUtils.jsm', {});
-
 function runTest() {
   var iframe = document.createElement('iframe');
   iframe.setAttribute('mozbrowser', 'true');
 
   var lastSecurityState;
   iframe.addEventListener('mozbrowsersecuritychange', function(e) {
     lastSecurityState = e.detail;
   });
@@ -25,58 +22,37 @@ function runTest() {
 
   var count = 0;
   iframe.addEventListener('mozbrowserloadend', function(e) {
     count++;
     switch (count) {
     case 1:
       is(lastSecurityState.state, 'secure');
       is(lastSecurityState.extendedValidation, false);
-      is(lastSecurityState.trackingContent, false);
       is(lastSecurityState.mixedContent, false);
       iframe.src = "http://example.com/" + filepath;
       break;
     case 2:
       is(lastSecurityState.state, 'insecure');
       is(lastSecurityState.extendedValidation, false);
-      is(lastSecurityState.trackingContent, false);
       is(lastSecurityState.mixedContent, false);
       iframe.src = 'https://example.com:443/' + filepath + '?broken';
       break;
     case 3:
       is(lastSecurityState.state, 'broken');
       is(lastSecurityState.extendedValidation, false);
-      is(lastSecurityState.trackingContent, false);
       is(lastSecurityState.mixedContent, true);
-      iframe.src = "http://example.com/" + filepath + '?tracking';
+      SimpleTest.finish();
       break;
-    case 4:
-      is(lastSecurityState.state, 'insecure');
-      is(lastSecurityState.extendedValidation, false);
-      // TODO: I'm having trouble getting the tracking protection
-      // test changes to be enabled in the child process, so this
-      // isn't currently blocked in tests, but it works when
-      // manually testing.
-      // is(lastSecurityState.trackingContent, true);
-      is(lastSecurityState.mixedContent, false);
-      SimpleTest.finish();
     }
   });
 
   iframe.src = "https://example.com/" + filepath;
   document.body.appendChild(iframe);
 }
 
 addEventListener('testready', function() {
-  SimpleTest.registerCleanupFunction(UrlClassifierTestUtils.cleanupTestTrackers);
   SpecialPowers.pushPrefEnv({"set" : [
-    ["privacy.trackingprotection.enabled", true],
-    ["privacy.trackingprotection.pbmode.enabled", false],
     ["browser.safebrowsing.phishing.enabled", false],
     ["browser.safebrowsing.malware.enabled", false],
-  ]}, () => {
-     SimpleTest.registerCleanupFunction(UrlClassifierTestUtils.cleanupTestTrackers);
-     UrlClassifierTestUtils.addTestTrackers().then(() => {
-       runTest();
-     });
-  });
+  ]}, runTest);
 });
 
--- a/dom/browser-element/mochitest/file_browserElement_SecurityChange.html
+++ b/dom/browser-element/mochitest/file_browserElement_SecurityChange.html
@@ -2,20 +2,16 @@
 <head>
 
 <script>
 if (location.search === '?broken') {
   // Load something non-https.
   var s = document.createElement('script');
   s.src = 'http://example.com/dom/browser-element/mochitest/file_empty_script.js';
   document.head.appendChild(s);
-} else if (location.search === '?tracking') {
-  var img = document.createElement('img');
-  img.src = 'http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg';
-  document.body.appendChild(img);
 }
 </script>
 </head>
 
 <body>
 file_browserElement_SecurityChange.html.
 </body>
 </html>
--- a/dom/clients/manager/ClientNavigateOpChild.cpp
+++ b/dom/clients/manager/ClientNavigateOpChild.cpp
@@ -121,16 +121,23 @@ class NavigateLoadListener final : publi
 
   NS_IMETHOD
   OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
                    uint32_t aState) override {
     MOZ_CRASH("Unexpected notification.");
     return NS_OK;
   }
 
+  NS_IMETHOD
+  OnContentBlockingEvent(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
+                         uint32_t aEvent) override {
+    MOZ_CRASH("Unexpected notification.");
+    return NS_OK;
+  }
+
   NS_DECL_ISUPPORTS
 };
 
 NS_IMPL_ISUPPORTS(NavigateLoadListener, nsIWebProgressListener,
                   nsISupportsWeakReference);
 
 }  // anonymous namespace
 
--- a/dom/clients/manager/ClientOpenWindowUtils.cpp
+++ b/dom/clients/manager/ClientOpenWindowUtils.cpp
@@ -117,16 +117,23 @@ class WebProgressListener final : public
 
   NS_IMETHOD
   OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
                    uint32_t aState) override {
     MOZ_ASSERT(false, "Unexpected notification.");
     return NS_OK;
   }
 
+  NS_IMETHOD
+  OnContentBlockingEvent(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
+                         uint32_t aEvent) override {
+    MOZ_ASSERT(false, "Unexpected notification.");
+    return NS_OK;
+  }
+
  private:
   ~WebProgressListener() {
     if (mPromise) {
       mPromise->Reject(NS_ERROR_ABORT, __func__);
       mPromise = nullptr;
     }
   }
 
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -1969,17 +1969,24 @@ HTMLFormElement::OnStatusChange(nsIWebPr
                                 nsIRequest* aRequest, nsresult aStatus,
                                 const char16_t* aMessage) {
   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HTMLFormElement::OnSecurityChange(nsIWebProgress* aWebProgress,
-                                  nsIRequest* aRequest, uint32_t state) {
+                                  nsIRequest* aRequest, uint32_t aState) {
+  MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HTMLFormElement::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
+                                        nsIRequest* aRequest, uint32_t aEvent) {
   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
 
 NS_IMETHODIMP_(int32_t)
 HTMLFormElement::IndexOfControl(nsIFormControl* aControl) {
   int32_t index = 0;
   return mControls->IndexOfControl(aControl, &index) == NS_OK ? index : 0;
--- a/dom/html/nsHTMLDNSPrefetch.cpp
+++ b/dom/html/nsHTMLDNSPrefetch.cpp
@@ -501,17 +501,23 @@ nsHTMLDNSPrefetch::nsDeferrals::OnStatus
                                                nsresult aStatus,
                                                const char16_t *aMessage) {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLDNSPrefetch::nsDeferrals::OnSecurityChange(nsIWebProgress *aWebProgress,
                                                  nsIRequest *aRequest,
-                                                 uint32_t state) {
+                                                 uint32_t aState) {
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHTMLDNSPrefetch::nsDeferrals::OnContentBlockingEvent(
+    nsIWebProgress *aWebProgress, nsIRequest *aRequest, uint32_t aEvent) {
   return NS_OK;
 }
 
 //////////// nsIObserver method
 
 NS_IMETHODIMP
 nsHTMLDNSPrefetch::nsDeferrals::Observe(nsISupports *subject, const char *topic,
                                         const char16_t *data) {
--- a/dom/presentation/PresentationCallbacks.cpp
+++ b/dom/presentation/PresentationCallbacks.cpp
@@ -230,12 +230,19 @@ PresentationResponderLoadingCallback::On
     nsIWebProgress* aWebProgress, nsIRequest* aRequest, nsresult aStatus,
     const char16_t* aMessage) {
   // Do nothing.
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationResponderLoadingCallback::OnSecurityChange(
-    nsIWebProgress* aWebProgress, nsIRequest* aRequest, uint32_t state) {
+    nsIWebProgress* aWebProgress, nsIRequest* aRequest, uint32_t aState) {
   // Do nothing.
   return NS_OK;
 }
+
+NS_IMETHODIMP
+PresentationResponderLoadingCallback::OnContentBlockingEvent(
+    nsIWebProgress* aWebProgress, nsIRequest* aRequest, uint32_t aEvent) {
+  // Do nothing.
+  return NS_OK;
+}
--- a/editor/composer/nsEditingSession.cpp
+++ b/editor/composer/nsEditingSession.cpp
@@ -783,17 +783,30 @@ nsEditingSession::OnStatusChange(nsIWebP
 
 /*---------------------------------------------------------------------------
 
   OnSecurityChange
 
 ----------------------------------------------------------------------------*/
 NS_IMETHODIMP
 nsEditingSession::OnSecurityChange(nsIWebProgress* aWebProgress,
-                                   nsIRequest* aRequest, uint32_t state) {
+                                   nsIRequest* aRequest, uint32_t aState) {
+  MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
+  return NS_OK;
+}
+
+/*---------------------------------------------------------------------------
+
+  OnContentBlockingEvent
+
+----------------------------------------------------------------------------*/
+NS_IMETHODIMP
+nsEditingSession::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
+                                         nsIRequest* aRequest,
+                                         uint32_t aEvent) {
   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
 
 /*---------------------------------------------------------------------------
 
   IsProgressForTargetDocument
 
--- a/editor/composer/test/test_bug434998.xul
+++ b/editor/composer/test/test_bug434998.xul
@@ -86,16 +86,20 @@ https://bugzilla.mozilla.org/show_bug.cg
     onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
     {
     },
 
     onSecurityChange : function(aWebProgress, aRequest, aState)
     {
     },
 
+    onContentBlockingEvent : function(aWebProgress, aRequest, aEvent)
+    {
+    },
+
     mEditor: null
   };
 
   var progress, progressListener;
 
   function runTest() {
     var newEditorElement = document.getElementById("editor");
     newEditorElement.makeEditable("html", true);
--- a/editor/libeditor/tests/test_bug607584.xul
+++ b/editor/libeditor/tests/test_bug607584.xul
@@ -91,16 +91,20 @@ https://bugzilla.mozilla.org/show_bug.cg
   
     onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
       {
       },
   
     onSecurityChange : function(aWebProgress, aRequest, aState)
       {
       },
+
+    onContentBlockingEvent : function(aWebProgress, aRequest, aEvent)
+      {
+      },
   
       mEditor: null
   };
 
   var progress, progressListener;
 
   function runTest() {
     var newEditorElement = document.getElementById("editor");
--- a/editor/libeditor/tests/test_bug616590.xul
+++ b/editor/libeditor/tests/test_bug616590.xul
@@ -81,16 +81,20 @@ https://bugzilla.mozilla.org/show_bug.cg
     onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
     {
     },
 
     onSecurityChange : function(aWebProgress, aRequest, aState)
     {
     },
 
+    onContentBlockingEvent : function(aWebProgress, aRequest, aEvent)
+    {
+    },
+
     mEditor: null
   };
 
   var progress, progressListener;
 
   function runTest() {
     var editorElement = document.getElementById("editor");
     editorElement.makeEditable("htmlmail", true);
--- a/editor/libeditor/tests/test_bug780908.xul
+++ b/editor/libeditor/tests/test_bug780908.xul
@@ -90,16 +90,20 @@ adapted from test_bug607584.xul by Kent 
     onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
       {
       },
   
     onSecurityChange : function(aWebProgress, aRequest, aState)
       {
       },
   
+    onContentBlockingEvent : function(aWebProgress, aRequest, aEvent)
+      {
+      },
+  
       mEditor: null
   };
 
   var progress, progressListener;
 
   function runTest() {
     var newEditorElement = document.getElementById("editor");
     newEditorElement.makeEditable("html", true);
--- a/layout/base/tests/chrome/printpreview_bug396024_helper.xul
+++ b/layout/base/tests/chrome/printpreview_bug396024_helper.xul
@@ -21,16 +21,17 @@ function printpreview() {
   var listener = {
     onLocationChange: function(webProgress, request, location, flags) { },
     onProgressChange: function(webProgress, request, curSelfProgress, 
                                maxSelfProgress, curTotalProgress,
                                maxTotalProgress) { },
     onSecurityChange: function(webProgress, request, state) { },
     onStateChange: function(webProgress, request, stateFlags, status) { },
     onStatusChange: function(webProgress, request, status, message) { },
+    onContentBlockingEvent: function(webProgress, request, event) { },
     QueryInterface: function(iid) {
       if (iid.equals(Ci.nsIWebProgressListener) ||
           iid.equals(Ci.nsISupportsWeakReference))
             return this;
       throw Cr.NS_NOINTERFACE;
     }
   }
   var prefs = Cc["@mozilla.org/preferences-service;1"]
--- a/layout/base/tests/chrome/printpreview_bug482976_helper.xul
+++ b/layout/base/tests/chrome/printpreview_bug482976_helper.xul
@@ -21,16 +21,17 @@ function printpreview() {
   var listener = {
     onLocationChange: function(webProgress, request, location, flags) { },
     onProgressChange: function(webProgress, request, curSelfProgress, 
                                maxSelfProgress, curTotalProgress,
                                maxTotalProgress) { },
     onSecurityChange: function(webProgress, request, state) { },
     onStateChange: function(webProgress, request, stateFlags, status) { },
     onStatusChange: function(webProgress, request, status, message) { },
+    onContentBlockingEvent: function(webProgress, request, event) { },
     QueryInterface: function(iid) {
       if (iid.equals(Ci.nsIWebProgessListener) ||
           iid.equals(Ci.nsISupportsWeakReference))
             return this;
       throw Cr.NS_NOINTERFACE;
     }
   }
   var prefs = Cc["@mozilla.org/preferences-service;1"]
--- a/layout/base/tests/chrome/printpreview_helper.xul
+++ b/layout/base/tests/chrome/printpreview_helper.xul
@@ -28,16 +28,17 @@ function printpreview() {
   var listener = {
     onLocationChange: function(webProgress, request, location, flags) { },
     onProgressChange: function(webProgress, request, curSelfProgress, 
                                maxSelfProgress, curTotalProgress,
                                maxTotalProgress) { },
     onSecurityChange: function(webProgress, request, state) { },
     onStateChange: function(webProgress, request, stateFlags, status) { },
     onStatusChange: function(webProgress, request, status, message) { },
+    onContentBlockingEvent: function(webProgress, request, event) { },
     QueryInterface: function(iid) {
       if (iid.equals(Ci.nsIWebProgressListener) ||
           iid.equals(Ci.nsISupportsWeakReference))
             return this;
       throw Cr.NS_NOINTERFACE;
     }
   }
   var prefs = Cc["@mozilla.org/preferences-service;1"]
--- a/layout/printing/ipc/RemotePrintJobChild.cpp
+++ b/layout/printing/ipc/RemotePrintJobChild.cpp
@@ -139,16 +139,23 @@ RemotePrintJobChild::OnStatusChange(nsIW
 }
 
 NS_IMETHODIMP
 RemotePrintJobChild::OnSecurityChange(nsIWebProgress* aProgress,
                                       nsIRequest* aRequest, uint32_t aState) {
   return NS_OK;
 }
 
+NS_IMETHODIMP
+RemotePrintJobChild::OnContentBlockingEvent(nsIWebProgress* aProgress,
+                                            nsIRequest* aRequest,
+                                            uint32_t aEvent) {
+  return NS_OK;
+}
+
 // End of nsIWebProgressListener
 
 RemotePrintJobChild::~RemotePrintJobChild() {}
 
 void RemotePrintJobChild::ActorDestroy(ActorDestroyReason aWhy) {
   mPagePrintTimer = nullptr;
   mPrintJob = nullptr;
 
--- a/layout/printing/nsPrintJob.cpp
+++ b/layout/printing/nsPrintJob.cpp
@@ -2016,16 +2016,23 @@ nsPrintJob::OnStatusChange(nsIWebProgres
 
 NS_IMETHODIMP
 nsPrintJob::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
                              uint32_t aState) {
   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsPrintJob::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
+                                   nsIRequest* aRequest, uint32_t aEvent) {
+  MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
+  return NS_OK;
+}
+
 //-------------------------------------------------------
 
 void nsPrintJob::UpdateZoomRatio(nsPrintObject* aPO, bool aSetPixelScale) {
   // Here is where we set the shrinkage value into the DC
   // and this is what actually makes it shrink
   if (aSetPixelScale && aPO->mFrameType != eIFrame) {
     float ratio;
     if (mPrt->mPrintFrameType == nsIPrintSettings::kFramesAsIs ||
--- a/layout/tools/layout-debug/ui/content/layoutdebug.js
+++ b/layout/tools/layout-debug/ui/content/layoutdebug.js
@@ -76,16 +76,20 @@ nsLDBBrowserContentListener.prototype = 
     {
       this.mStatusText.value = aMessage;
     },
 
   onSecurityChange : function(aWebProgress, aRequest, aState)
     {
     },
 
+  onContentBlockingEvent : function(aWebProgress, aRequest, aEvent)
+    {
+    },
+
   // non-interface methods
   setButtonEnabled : function(aButtonElement, aEnabled)
     {
       if (aEnabled)
         aButtonElement.removeAttribute("disabled");
       else
         aButtonElement.setAttribute("disabled", "true");
     },
--- a/layout/tools/reftest/reftest-content.js
+++ b/layout/tools/reftest/reftest-content.js
@@ -272,16 +272,17 @@ function printToPdf(callback) {
                 stateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
                 callback(status, file.path);
             }
         },
         onProgressChange: function () {},
         onLocationChange: function () {},
         onStatusChange: function () {},
         onSecurityChange: function () {},
+        onContentBlockingEvent: function () {},
     });
 }
 
 function attrOrDefault(element, attr, def) {
     return element.hasAttribute(attr) ? Number(element.getAttribute(attr)) : def;
 }
 
 function setupViewport(contentRootElement) {
new file mode 100644
--- /dev/null
+++ b/media/libjpeg/1520760-avx2-detection.diff
@@ -0,0 +1,32 @@
+Bug 1520760 - Fix AVX2 detection to ensure we have all required CPU parameters.
+
+diff --git simd/i386/jsimdcpu.asm simd/i386/jsimdcpu.asm
+--- simd/i386/jsimdcpu.asm
++++ simd/i386/jsimdcpu.asm
+@@ -87,8 +87,10 @@ EXTN(jpeg_simd_cpu_support):
+     mov         eax, 1
+     xor         ecx, ecx
+     cpuid
+-    test        ecx, 1<<27
++    test        ecx, 1<<26
+     jz          short .no_avx2          ; O/S does not support XSAVE
++    test        ecx, 1<<27
++    jz          short .no_avx2          ; O/S does not support OSXSAVE
+     test        ecx, 1<<28
+     jz          short .no_avx2          ; CPU does not support AVX2
+ 
+diff --git simd/x86_64/jsimdcpu.asm simd/x86_64/jsimdcpu.asm
+--- simd/x86_64/jsimdcpu.asm
++++ simd/x86_64/jsimdcpu.asm
+@@ -53,8 +53,10 @@ EXTN(jpeg_simd_cpu_support):
+     mov         rax, 1
+     xor         rcx, rcx
+     cpuid
+-    test        rcx, 1<<27
++    test        rcx, 1<<26
+     jz          short .return           ; O/S does not support XSAVE
++    test        rcx, 1<<27
++    jz          short .return           ; O/S does not support OSXSAVE
+     test        rcx, 1<<28
+     jz          short .return           ; CPU does not support AVX2
+ 
--- a/media/libjpeg/simd/i386/jsimdcpu.asm
+++ b/media/libjpeg/simd/i386/jsimdcpu.asm
@@ -82,18 +82,20 @@ EXTN(jpeg_simd_cpu_support):
     mov         eax, ebx
     test        eax, 1<<5               ; bit5:AVX2
     jz          short .no_avx2
 
     ; Check for AVX2 O/S support
     mov         eax, 1
     xor         ecx, ecx
     cpuid
+    test        ecx, 1<<26
+    jz          short .no_avx2          ; O/S does not support XSAVE
     test        ecx, 1<<27
-    jz          short .no_avx2          ; O/S does not support XSAVE
+    jz          short .no_avx2          ; O/S does not support OSXSAVE
     test        ecx, 1<<28
     jz          short .no_avx2          ; CPU does not support AVX2
 
     xor         ecx, ecx
     xgetbv
     and         eax, 6
     cmp         eax, 6                  ; O/S does not manage XMM/YMM state
                                         ; using XSAVE
--- a/media/libjpeg/simd/x86_64/jsimdcpu.asm
+++ b/media/libjpeg/simd/x86_64/jsimdcpu.asm
@@ -48,18 +48,20 @@ EXTN(jpeg_simd_cpu_support):
     or          rdi, JSIMD_SSE
     test        rax, 1<<5               ; bit5:AVX2
     jz          short .return
 
     ; Check for AVX2 O/S support
     mov         rax, 1
     xor         rcx, rcx
     cpuid
+    test        rcx, 1<<26
+    jz          short .return           ; O/S does not support XSAVE
     test        rcx, 1<<27
-    jz          short .return           ; O/S does not support XSAVE
+    jz          short .return           ; O/S does not support OSXSAVE
     test        rcx, 1<<28
     jz          short .return           ; CPU does not support AVX2
 
     xor         rcx, rcx
     xgetbv
     and         rax, 6
     cmp         rax, 6                  ; O/S does not manage XMM/YMM state
                                         ; using XSAVE
--- a/media/libwebp/AUTHORS
+++ b/media/libwebp/AUTHORS
@@ -2,17 +2,17 @@ Contributors:
 - Alan Browning (browning at google dot com)
 - Charles Munger (clm at google dot com)
 - Christian Duvivier (cduvivier at google dot com)
 - Djordje Pesut (djordje dot pesut at imgtec dot com)
 - Hui Su (huisu at google dot com)
 - James Zern (jzern at google dot com)
 - Jan Engelhardt (jengelh at medozas dot de)
 - Jehan (jehan at girinstud dot io)
-- Johann (johann dot koenig at duck dot com)
+- Johann Koenig (johann dot koenig at duck dot com)
 - Jovan Zelincevic (jovan dot zelincevic at imgtec dot com)
 - Jyrki Alakuijala (jyrki at google dot com)
 - Konstantin Ivlev (tomskside at gmail dot com)
 - Lode Vandevenne (lode at google dot com)
 - Lou Quillio (louquillio at google dot com)
 - Mans Rullgard (mans at mansr dot com)
 - Marcin Kowalczyk (qrczak at google dot com)
 - Martin Olsson (mnemo at minimum dot se)
--- a/media/libwebp/MOZCHANGES
+++ b/media/libwebp/MOZCHANGES
@@ -1,5 +1,7 @@
 Changes made to pristine libwebp source by mozilla.org developers.
 
+2018/01/21  -- Synced with libwebp-1.0.2 (bug 1521478).
+
 2018/11/26  -- Synced with libwebp-1.0.1 (bug 1509878).
 
 2018/10/04  -- Synced with libwebp-1.0.0 (bug #1294490).
--- a/media/libwebp/NEWS
+++ b/media/libwebp/NEWS
@@ -1,8 +1,16 @@
+- 1/14/2019: version 1.0.2
+  This is a binary compatible release.
+  * (Windows) unicode file support in the tools (linux and mac already had
+    support, issue #398)
+  * lossless encoder speedups
+  * lossy encoder speedup on ARM
+  * lossless multi-threaded security fix (chromium:917029)
+
 - 11/2/2018: version 1.0.1
   This is a binary compatible release.
   * lossless encoder speedups
   * big-endian fix for alpha decoding (issue #393)
   * gif2webp fix for loop count=65535 transcode (issue #382)
   * further security related hardening in libwebp & libwebpmux
     (issues #383, #385, #386, #387, #388, #391)
     (oss-fuzz #9099, #9100, #9105, #9106, #9111, #9112, #9119, #9123, #9170,
--- a/media/libwebp/README
+++ b/media/libwebp/README
@@ -1,15 +1,15 @@
           __   __  ____  ____  ____
          /  \\/  \/  _ \/  _ )/  _ \
          \       /   __/  _  \   __/
           \__\__/\____/\_____/__/ ____  ___
                 / _/ /    \    \ /  _ \/ _/
                /  \_/   / /   \ \   __/  \__
-               \____/____/\_____/_____/____/v1.0.1
+               \____/____/\_____/_____/____/v1.0.2
 
 Description:
 ============
 
 WebP codec: library to encode and decode images in WebP format. This package
 contains the library that can be used in other programs to add WebP support,
 as well as the command line tools 'cwebp' and 'dwebp'.
 
@@ -131,16 +131,18 @@ make install
 
 If you also want any of the executables, you will need to enable them through
 CMake, e.g.:
 
 cmake -DWEBP_BUILD_CWEBP=ON -DWEBP_BUILD_DWEBP=ON ../
 
 or through your favorite interface (like ccmake or cmake-qt-gui).
 
+Use option -DWEBP_UNICODE=ON for Unicode support on Windows (with chcp 65001).
+
 Finally, once installed, you can also use WebP in your CMake project by doing:
 
 find_package(WebP)
 
 which will define the CMake variables WebP_INCLUDE_DIRS and WebP_LIBRARIES.
 
 Gradle:
 -------
--- a/media/libwebp/README.mux
+++ b/media/libwebp/README.mux
@@ -1,12 +1,12 @@
           __   __  ____  ____  ____  __ __  _     __ __
          /  \\/  \/  _ \/  _ \/  _ \/  \  \/ \___/_ / _\
          \       /   __/  _  \   __/      /  /  (_/  /__
-          \__\__/\_____/_____/__/  \__//_/\_____/__/___/v1.0.1
+          \__\__/\_____/_____/__/  \__//_/\_____/__/___/v1.0.2
 
 
 Description:
 ============
 
 WebPMux: set of two libraries 'Mux' and 'Demux' for creation, extraction and
 manipulation of an extended format WebP file, which can have features like
 color profile, metadata and animation. Reference command-line tools 'webpmux'
--- a/media/libwebp/src/dec/vp8i_dec.h
+++ b/media/libwebp/src/dec/vp8i_dec.h
@@ -27,17 +27,17 @@ extern "C" {
 #endif
 
 //------------------------------------------------------------------------------
 // Various defines and enums
 
 // version numbers
 #define DEC_MAJ_VERSION 1
 #define DEC_MIN_VERSION 0
-#define DEC_REV_VERSION 1
+#define DEC_REV_VERSION 2
 
 // YUV-cache parameters. Cache is 32-bytes wide (= one cacheline).
 // Constraints are: We need to store one 16x16 block of luma samples (y),
 // and two 8x8 chroma blocks (u/v). These are better be 16-bytes aligned,
 // in order to be SIMD-friendly. We also need to store the top, left and
 // top-left samples (from previously decoded blocks), along with four
 // extra top-right samples for luma (intra4x4 prediction only).
 // One possible layout is, using 32 * (17 + 9) bytes:
--- a/media/libwebp/src/demux/demux.c
+++ b/media/libwebp/src/demux/demux.c
@@ -20,17 +20,17 @@
 
 #include "src/utils/utils.h"
 #include "src/webp/decode.h"     // WebPGetFeatures
 #include "src/webp/demux.h"
 #include "src/webp/format_constants.h"
 
 #define DMUX_MAJ_VERSION 1
 #define DMUX_MIN_VERSION 0
-#define DMUX_REV_VERSION 1
+#define DMUX_REV_VERSION 2
 
 typedef struct {
   size_t start_;        // start location of the data
   size_t end_;          // end location
   size_t riff_end_;     // riff chunk end location, can be > end_.
   size_t buf_size_;     // size of the buffer
   const uint8_t* buf_;
 } MemBuffer;
--- a/media/libwebp/src/enc/vp8i_enc.h
+++ b/media/libwebp/src/enc/vp8i_enc.h
@@ -27,17 +27,17 @@ extern "C" {
 #endif
 
 //------------------------------------------------------------------------------
 // Various defines and enums
 
 // version numbers
 #define ENC_MAJ_VERSION 1
 #define ENC_MIN_VERSION 0
-#define ENC_REV_VERSION 1
+#define ENC_REV_VERSION 2
 
 enum { MAX_LF_LEVELS = 64,       // Maximum loop filter level
        MAX_VARIABLE_LEVEL = 67,  // last (inclusive) level with variable cost
        MAX_LEVEL = 2047          // max level (note: max codable is 2047 + 67)
      };
 
 typedef enum {   // Rate-distortion optimization levels
   RD_OPT_NONE        = 0,  // no rd-opt
--- a/media/libwebp/src/utils/utils.h
+++ b/media/libwebp/src/utils/utils.h
@@ -102,29 +102,16 @@ static WEBP_INLINE void PutLE24(uint8_t*
   data[2] = (val >> 16);
 }
 
 static WEBP_INLINE void PutLE32(uint8_t* const data, uint32_t val) {
   PutLE16(data, (int)(val & 0xffff));
   PutLE16(data + 2, (int)(val >> 16));
 }
 
-// Returns 31 ^ clz(n) = log2(n). This is the default C-implementation, either
-// based on table or not. Can be used as fallback if clz() is not available.
-#define WEBP_NEED_LOG_TABLE_8BIT
-extern const uint8_t WebPLogTable8bit[256];
-static WEBP_INLINE int WebPLog2FloorC(uint32_t n) {
-  int log_value = 0;
-  while (n >= 256) {
-    log_value += 8;
-    n >>= 8;
-  }
-  return log_value + WebPLogTable8bit[n];
-}
-
 // Returns (int)floor(log2(n)). n must be > 0.
 // use GNU builtins where available.
 #if defined(__GNUC__) && \
     ((__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || __GNUC__ >= 4)
 static WEBP_INLINE int BitsLog2Floor(uint32_t n) {
   return 31 ^ __builtin_clz(n);
 }
 #elif defined(_MSC_VER) && _MSC_VER > 1310 && \
@@ -133,16 +120,29 @@ static WEBP_INLINE int BitsLog2Floor(uin
 #pragma intrinsic(_BitScanReverse)
 
 static WEBP_INLINE int BitsLog2Floor(uint32_t n) {
   unsigned long first_set_bit;
   _BitScanReverse(&first_set_bit, n);
   return first_set_bit;
 }
 #else   // default: use the C-version.
+// Returns 31 ^ clz(n) = log2(n). This is the default C-implementation, either
+// based on table or not. Can be used as fallback if clz() is not available.
+#define WEBP_NEED_LOG_TABLE_8BIT
+extern const uint8_t WebPLogTable8bit[256];
+static WEBP_INLINE int WebPLog2FloorC(uint32_t n) {
+  int log_value = 0;
+  while (n >= 256) {
+    log_value += 8;
+    n >>= 8;
+  }
+  return log_value + WebPLogTable8bit[n];
+}
+
 static WEBP_INLINE int BitsLog2Floor(uint32_t n) { return WebPLog2FloorC(n); }
 #endif
 
 //------------------------------------------------------------------------------
 // Pixel copying.
 
 struct WebPPicture;
 
--- a/media/libwebp/src/webp/decode.h
+++ b/media/libwebp/src/webp/decode.h
@@ -37,16 +37,22 @@ typedef struct WebPDecoderConfig WebPDec
 // Return the decoder's version number, packed in hexadecimal using 8bits for
 // each of major/minor/revision. E.g: v2.5.7 is 0x020507.
 WEBP_EXTERN int WebPGetDecoderVersion(void);
 
 // Retrieve basic header information: width, height.
 // This function will also validate the header, returning true on success,
 // false otherwise. '*width' and '*height' are only valid on successful return.
 // Pointers 'width' and 'height' can be passed NULL if deemed irrelevant.
+// Note: The following chunk sequences (before the raw VP8/VP8L data) are
+// considered valid by this function:
+// RIFF + VP8(L)
+// RIFF + VP8X + (optional chunks) + VP8(L)
+// ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
+// VP8(L)     <-- Not a valid WebP format: only allowed for internal purpose.
 WEBP_EXTERN int WebPGetInfo(const uint8_t* data, size_t data_size,
                             int* width, int* height);
 
 // Decodes WebP images pointed to by 'data' and returns RGBA samples, along
 // with the dimensions in *width and *height. The ordering of samples in
 // memory is R, G, B, A, R, G, B, A... in scan order (endian-independent).
 // The returned pointer should be deleted calling WebPFree().
 // Returns NULL in case of error.
@@ -420,16 +426,22 @@ struct WebPBitstreamFeatures {
 WEBP_EXTERN VP8StatusCode WebPGetFeaturesInternal(
     const uint8_t*, size_t, WebPBitstreamFeatures*, int);
 
 // Retrieve features from the bitstream. The *features structure is filled
 // with information gathered from the bitstream.
 // Returns VP8_STATUS_OK when the features are successfully retrieved. Returns
 // VP8_STATUS_NOT_ENOUGH_DATA when more data is needed to retrieve the
 // features from headers. Returns error in other cases.
+// Note: The following chunk sequences (before the raw VP8/VP8L data) are
+// considered valid by this function:
+// RIFF + VP8(L)
+// RIFF + VP8X + (optional chunks) + VP8(L)
+// ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
+// VP8(L)     <-- Not a valid WebP format: only allowed for internal purpose.
 static WEBP_INLINE VP8StatusCode WebPGetFeatures(
     const uint8_t* data, size_t data_size,
     WebPBitstreamFeatures* features) {
   return WebPGetFeaturesInternal(data, data_size, features,
                                  WEBP_DECODER_ABI_VERSION);
 }
 
 // Decoding options
--- a/media/update-libjpeg.sh
+++ b/media/update-libjpeg.sh
@@ -14,19 +14,20 @@ rm -rf $srcdir/libjpeg
 repo=$1
 tag=${2-HEAD}
 
 (cd $repo; git archive --prefix=media/libjpeg/ $tag) | (cd $srcdir/..; tar xf -)
 
 cd $srcdir/libjpeg
 cp win/jsimdcfg.inc simd/
 
-revert_files="1050342.diff assembly-tables.diff externalize-table.diff jconfig.h jconfigint.h jpeg_nbits_table.c moz.build MOZCHANGES mozilla.diff simd/jsimdcfg.inc"
+revert_files="1050342.diff assembly-tables.diff externalize-table.diff 1520760-avx2-detection.diff jconfig.h jconfigint.h jpeg_nbits_table.c moz.build MOZCHANGES mozilla.diff simd/jsimdcfg.inc"
 if test -d ${topsrcdir}/.hg; then
     hg revert --no-backup $revert_files
 elif test -d ${topsrcdir}/.git; then
     git checkout HEAD -- $revert_files
 fi
 
 patch -p0 -i mozilla.diff
 patch -p0 -i 1050342.diff
 patch -p3 -i externalize-table.diff
 patch -p3 -i assembly-tables.diff
+patch -p0 -i 1520760-avx2-detection.diff
--- a/mobile/android/base/java/org/mozilla/gecko/SiteIdentity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/SiteIdentity.java
@@ -86,18 +86,16 @@ public class SiteIdentity {
         }
 
         final GeckoBundle mode = identityData.getBundle("mode");
 
         mMixedModeDisplay = getEnumValue(
                 MixedMode.class, mode.getString("mixed_display"), MixedMode.UNKNOWN);
         mMixedModeActive = getEnumValue(
                 MixedMode.class, mode.getString("mixed_active"), MixedMode.UNKNOWN);
-        mTrackingMode = getEnumValue(
-                TrackingMode.class, mode.getString("tracking"), TrackingMode.UNKNOWN);
 
         if (!mode.containsKey("identity") || !identityData.containsKey("origin")) {
             resetIdentity();
             return;
         }
 
         mSecurityMode = getEnumValue(
                 SecurityMode.class, mode.getString("identity"), SecurityMode.UNKNOWN);
@@ -107,16 +105,20 @@ public class SiteIdentity {
         mOwner = identityData.getString("owner");
         mSupplemental = identityData.getString("supplemental");
         mCountry = identityData.getString("country");
         mVerifier = identityData.getString("verifier");
         mSecure = identityData.getBoolean("secure");
         mSecurityException = identityData.getBoolean("securityException");
     }
 
+    /* package */ void updateTrackingMode(final String trackingEvent) {
+        mTrackingMode = getEnumValue(TrackingMode.class, trackingEvent, TrackingMode.UNKNOWN);
+    }
+
     public SecurityMode getSecurityMode() {
         return mSecurityMode;
     }
 
     public String getOrigin() {
         return mOrigin;
     }
 
--- a/mobile/android/base/java/org/mozilla/gecko/Tab.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Tab.java
@@ -499,16 +499,20 @@ public class Tab {
     public void setLoadedFromCache(boolean loadedFromCache) {
         mLoadedFromCache = loadedFromCache;
     }
 
     public void updateIdentityData(final GeckoBundle identityData) {
         mSiteIdentity.update(identityData);
     }
 
+    public void updateTracking(String statusCode) {
+        mSiteIdentity.updateTrackingMode(statusCode);
+    }
+
     public void setSiteLogins(SiteLogins siteLogins) {
         mSiteLogins = siteLogins;
     }
 
     void updateBookmark() {
         if (getURL() == null) {
             return;
         }
--- a/mobile/android/base/java/org/mozilla/gecko/Tabs.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Tabs.java
@@ -149,16 +149,17 @@ public class Tabs implements BundleEvent
         EventDispatcher.getInstance().registerGeckoThreadListener(this,
             "Tab:GetNextTabId",
             null);
 
         EventDispatcher.getInstance().registerUiThreadListener(this,
             "Content:LocationChange",
             "Content:SubframeNavigation",
             "Content:SecurityChange",
+            "Content:ContentBlockingEvent",
             "Content:StateChange",
             "Content:LoadError",
             "Content:DOMContentLoaded",
             "Content:PageShow",
             "Content:DOMTitleChanged",
             "DesktopMode:Changed",
             "Link:Favicon",
             "Link:Feed",
@@ -615,16 +616,20 @@ public class Tabs implements BundleEvent
             tab.handleButtonStateChange(message);
             notifyListeners(tab, TabEvents.LOCATION_CHANGE, tab.getURL());
 
         } else if ("Content:SecurityChange".equals(event)) {
             tab.updateIdentityData(message.getBundle("identity"));
             tab.updatePageAction();
             notifyListeners(tab, TabEvents.SECURITY_CHANGE);
 
+        } else if ("Content:ContentBlockingEvent".equals(event)) {
+            tab.updateTracking(message.getString("tracking"));
+            notifyListeners(tab, TabEvents.TRACKING_CHANGE);
+
         } else if ("Content:StateChange".equals(event)) {
             final int state = message.getInt("state");
             if ((state & GeckoAppShell.WPL_STATE_IS_NETWORK) == 0) {
                 return;
             }
             if ((state & GeckoAppShell.WPL_STATE_START) != 0) {
                 Log.i(LOGTAG, "zerdatime " + SystemClock.elapsedRealtime() +
                       " - page load start");
@@ -759,16 +764,17 @@ public class Tabs implements BundleEvent
         ADDED,
         RESTORED,
         MOVED,
         LOCATION_CHANGE,
         MENU_UPDATED,
         PAGE_SHOW,
         LINK_FEED,
         SECURITY_CHANGE,
+        TRACKING_CHANGE,
         DESKTOP_MODE_CHANGE,
         RECORDING_CHANGE,
         BOOKMARK_ADDED,
         BOOKMARK_REMOVED,
         AUDIO_PLAYING_CHANGE,
         OPENED_FROM_TABS_TRAY,
         MEDIA_PLAYING_CHANGE,
         MEDIA_PLAYING_RESUME,
--- a/mobile/android/base/java/org/mozilla/gecko/toolbar/BrowserToolbar.java
+++ b/mobile/android/base/java/org/mozilla/gecko/toolbar/BrowserToolbar.java
@@ -520,16 +520,17 @@ public abstract class BrowserToolbar ext
                     flags.add(UpdateFlags.TITLE);
                     break;
 
                 case FAVICON:
                     flags.add(UpdateFlags.FAVICON);
                     break;
 
                 case SECURITY_CHANGE:
+                case TRACKING_CHANGE:
                     flags.add(UpdateFlags.SITE_IDENTITY);
                     break;
             }
 
             if (!flags.isEmpty() && tab != null) {
                 updateDisplayLayout(tab, flags);
             }
         }
--- a/mobile/android/chrome/content/PrintHelper.js
+++ b/mobile/android/chrome/content/PrintHelper.js
@@ -57,12 +57,13 @@ var PrintHelper = {
               reject();
             }
           }
         },
         onProgressChange: function() {},
         onLocationChange: function() {},
         onStatusChange: function() {},
         onSecurityChange: function() {},
+        onContentBlockingEvent: function() {},
       });
     });
   },
 };
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -3767,17 +3767,18 @@ Tab.prototype = {
         isPrivate: isPrivate,
         stub: stub
       };
       GlobalEventDispatcher.sendRequest(message);
     }
 
     let flags = Ci.nsIWebProgress.NOTIFY_STATE_ALL |
                 Ci.nsIWebProgress.NOTIFY_LOCATION |
-                Ci.nsIWebProgress.NOTIFY_SECURITY;
+                Ci.nsIWebProgress.NOTIFY_SECURITY |
+                Ci.nsIWebProgress.NOTIFY_CONTENT_BLOCKING;
     this.filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"].createInstance(Ci.nsIWebProgress);
     this.filter.addProgressListener(this, flags)
     this.browser.addProgressListener(this.filter, flags);
     this.browser.sessionHistory.legacySHistory.addSHistoryListener(this);
 
     this.browser.addEventListener("DOMContentLoaded", this, true);
     this.browser.addEventListener("DOMFormHasPassword", this, true);
     this.browser.addEventListener("DOMInputPasswordAdded", this, true);
@@ -4751,16 +4752,36 @@ Tab.prototype = {
       type: "Content:SecurityChange",
       tabID: this.id,
       identity: identity
     };
 
     GlobalEventDispatcher.sendRequest(message);
   },
 
+  // Cache last tracking event to limit firings and only propagate changes
+  _tracking: null,
+
+  onContentBlockingEvent: function(aWebProgress, aRequest, aState) {
+    let trackingMode = IdentityHandler.getTrackingMode(aState, this.browser);
+    if (this._tracking == trackingMode) {
+        return;
+    } else {
+        this._tracking = trackingMode;
+    }
+
+    let message = {
+      type: "Content:ContentBlockingEvent",
+      tabID: this.id,
+      tracking: trackingMode
+    };
+
+    GlobalEventDispatcher.sendRequest(message);
+  },
+
   OnHistoryNewEntry: function(newURI, oldIndex) {
     Services.obs.notifyObservers(this.browser, "Content:HistoryChange");
   },
 
   OnHistoryReload: function(reloadURI, reloadFlags) {
     Services.obs.notifyObservers(this.browser, "Content:HistoryChange");
     return true;
   },
@@ -5810,24 +5831,22 @@ var IdentityHandler = {
     this._uri = aBrowser.currentURI;
     try {
       this._uri = Services.uriFixup.createExposableURI(this._uri);
     } catch (e) {}
 
     let identityMode = this.getIdentityMode(aState, this._uri);
     let mixedDisplay = this.getMixedDisplayMode(aState);
     let mixedActive = this.getMixedActiveMode(aState);
-    let trackingMode = this.getTrackingMode(aState, aBrowser);
     let result = {
       origin: locationObj.origin,
       mode: {
         identity: identityMode,
         mixed_display: mixedDisplay,
         mixed_active: mixedActive,
-        tracking: trackingMode
       }
     };
 
     result.host = this.getEffectiveHost();
 
     // Don't show identity data for pages with an unknown identity or if any
     // mixed content is loaded (mixed display content is loaded by default).
     // We also return for CHROMEUI pages since they don't have any certificate
--- a/mobile/android/modules/geckoview/GeckoViewTrackingProtection.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewTrackingProtection.jsm
@@ -7,28 +7,28 @@
 var EXPORTED_SYMBOLS = ["GeckoViewTrackingProtection"];
 
 ChromeUtils.import("resource://gre/modules/GeckoViewModule.jsm");
 
 class GeckoViewTrackingProtection extends GeckoViewModule {
   onEnable() {
     debug `onEnable`;
 
-    const flags = Ci.nsIWebProgress.NOTIFY_SECURITY;
+    const flags = Ci.nsIWebProgress.NOTIFY_CONTENT_BLOCKING;
     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) {
-    debug `onSecurityChange`;
+  onContentBlockingEvent(aWebProgress, aRequest, aEvent) {
+    debug `onContentBlockingEvent`;
 
-    if (!(aState & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT) ||
+    if (!(aEvent & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT) ||
         !aRequest || !(aRequest instanceof Ci.nsIClassifiedChannel)) {
       return;
     }
 
     let channel = aRequest.QueryInterface(Ci.nsIChannel);
     let uri = channel.URI && channel.URI.spec;
     let classChannel = aRequest.QueryInterface(Ci.nsIClassifiedChannel);
 
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testTrackingProtection.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testTrackingProtection.java
@@ -9,48 +9,46 @@ import static org.mozilla.gecko.tests.he
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.GeckoApp;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
 
 public class testTrackingProtection extends JavascriptTest implements BundleEventListener {
-    private String mLastTracking;
+    private String mLastTracking = "unknown";   // Default value if no events received yet
 
     public testTrackingProtection() {
         super("testTrackingProtection.js");
     }
 
     @Override // BundleEventListener
     public void handleMessage(final String event, final GeckoBundle message,
                               final EventCallback callback) {
-        if ("Content:SecurityChange".equals(event)) {
-            final GeckoBundle identity = message.getBundle("identity");
-            final GeckoBundle mode = identity.getBundle("mode");
-            mLastTracking = mode.getString("tracking");
+        if ("Content:ContentBlockingEvent".equals(event)) {
+            mLastTracking = message.getString("tracking");
             mAsserter.dumpLog("Security change (tracking): " + mLastTracking);
 
         } else if ("Test:Expected".equals(event)) {
             final String expected = message.getString("expected");
+            mAsserter.dumpLog("Testing (tracking): 2" + mLastTracking + " = " + expected);
             mAsserter.is(mLastTracking, expected, "Tracking matched expectation");
-            mAsserter.dumpLog("Testing (tracking): " + mLastTracking + " = " + expected);
         }
     }
 
     @Override
     public void setUp() throws Exception {
         super.setUp();
 
         EventDispatcher.getInstance().registerUiThreadListener(this,
-                                                               "Content:SecurityChange",
+                                                               "Content:ContentBlockingEvent",
                                                                "Test:Expected");
     }
 
     @Override
     public void tearDown() throws Exception {
         super.tearDown();
 
         EventDispatcher.getInstance().unregisterUiThreadListener(this,
-                                                                 "Content:SecurityChange",
+                                                                 "Content:ContentBlockingEvent",
                                                                  "Test:Expected");
     }
 }
--- a/mobile/android/tests/browser/robocop/testTrackingProtection.js
+++ b/mobile/android/tests/browser/robocop/testTrackingProtection.js
@@ -31,17 +31,17 @@ function promiseLoadEvent(browser, url, 
     }
     if (url) {
       browser.loadURI(url);
     }
   });
 }
 
 // Test that the Tracking Protection is active and has the correct state when
-// tracking content is blocked (Bug 1063831)
+// tracking content is blocked (Bug 1063831 + Bug 1520520)
 
 // Code is mostly stolen from:
 // http://dxr.mozilla.org/mozilla-central/source/browser/base/content/test/general/browser_trackingUI.js
 
 var TABLE = "urlclassifier.trackingTable";
 
 // Update tracking database
 function doUpdate() {
--- a/netwerk/base/nsISecureBrowserUI.idl
+++ b/netwerk/base/nsISecureBrowserUI.idl
@@ -10,14 +10,15 @@ interface nsIDocShell;
 interface nsITransportSecurityInfo;
 
 [scriptable, uuid(718c662a-f810-4a80-a6c9-0b1810ecade2)]
 interface nsISecureBrowserUI : nsISupports
 {
     void init(in nsIDocShell docShell);
 
     readonly attribute unsigned long state;
+    readonly attribute unsigned long contentBlockingEvent;
     readonly attribute nsITransportSecurityInfo secInfo;
 };
 
 %{C++
 #define NS_SECURE_BROWSER_UI_CONTRACTID "@mozilla.org/secure_browser_ui;1"
 %}
--- a/netwerk/base/nsISecurityEventSink.idl
+++ b/netwerk/base/nsISecurityEventSink.idl
@@ -14,13 +14,22 @@ interface nsISecurityEventSink : nsISupp
       * Fired when a security change occurs due to page transitions,
       * or end document load. This interface should be called by
       * a security package (eg Netscape Personal Security Manager)
       * to notify nsIWebProgressListeners that security state has
       * changed. State flags are in nsIWebProgressListener.idl
       */
 
     void onSecurityChange(in nsISupports i_Context, in unsigned long state);
+
+    /**
+      * Fired when a content blocking event occurs during the time
+      * when a document is alive.  This interface should be called
+      * by Gecko to notify nsIWebProgressListeners that there is a
+      * new content blocking event.  Content blocking events are in
+      * nsIWebProgressListeners.idl.
+      */
+    void onContentBlockingEvent(in nsISupports i_Context, in unsigned long event);
 };
 
 
 
 
--- a/netwerk/url-classifier/UrlClassifierCommon.cpp
+++ b/netwerk/url-classifier/UrlClassifierCommon.cpp
@@ -79,17 +79,17 @@ LazyLogModule UrlClassifierCommon::sLog(
   if (!docShell) {
     return;
   }
   RefPtr<dom::Document> doc = docShell->GetDocument();
   NS_ENSURE_TRUE_VOID(doc);
 
   nsCOMPtr<nsIURI> uri;
   aChannel->GetURI(getter_AddRefs(uri));
-  pwin->NotifyContentBlockingState(aBlockedReason, aChannel, true, uri);
+  pwin->NotifyContentBlockingEvent(aBlockedReason, aChannel, true, uri);
 }
 
 /* static */ bool UrlClassifierCommon::ShouldEnableClassifier(
     nsIChannel* aChannel,
     AntiTrackingCommon::ContentBlockingAllowListPurpose aBlockingPurpose) {
   MOZ_ASSERT(aChannel);
   MOZ_ASSERT(aBlockingPurpose == AntiTrackingCommon::eTrackingProtection ||
              aBlockingPurpose == AntiTrackingCommon::eTrackingAnnotations ||
--- a/security/manager/ssl/nsSecureBrowserUIImpl.cpp
+++ b/security/manager/ssl/nsSecureBrowserUIImpl.cpp
@@ -17,17 +17,17 @@
 #include "nsISecurityEventSink.h"
 #include "nsITransportSecurityInfo.h"
 #include "nsIWebProgress.h"
 
 using namespace mozilla;
 
 LazyLogModule gSecureBrowserUILog("nsSecureBrowserUI");
 
-nsSecureBrowserUIImpl::nsSecureBrowserUIImpl() : mState(0) {
+nsSecureBrowserUIImpl::nsSecureBrowserUIImpl() : mState(0), mEvent(0) {
   MOZ_ASSERT(NS_IsMainThread());
 }
 
 NS_IMPL_ISUPPORTS(nsSecureBrowserUIImpl, nsISecureBrowserUI,
                   nsIWebProgressListener, nsISupportsWeakReference)
 
 NS_IMETHODIMP
 nsSecureBrowserUIImpl::Init(nsIDocShell* aDocShell) {
@@ -62,58 +62,81 @@ 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
   // docShell.
-  CheckForBlockedContent();
+  CheckForMixedContent();
   MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug, ("  mState: %x", mState));
 
   *aState = mState;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsSecureBrowserUIImpl::GetContentBlockingEvent(uint32_t* aEvent) {
+  MOZ_ASSERT(NS_IsMainThread());
+  NS_ENSURE_ARG(aEvent);
+
+  MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
+          ("GetContentBlockingEvent %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
+  // docShell.
+  CheckForContentBlockingEvents();
+  MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug, ("  mEvent: %x", mEvent));
+
+  *aEvent = mEvent;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsSecureBrowserUIImpl::GetSecInfo(nsITransportSecurityInfo** result) {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_ARG_POINTER(result);
 
   *result = mTopLevelSecurityInfo;
   NS_IF_ADDREF(*result);
 
   return NS_OK;
 }
 
-// Ask the docShell if we've blocked or loaded any mixed or tracking content.
-void nsSecureBrowserUIImpl::CheckForBlockedContent() {
+already_AddRefed<dom::Document>
+nsSecureBrowserUIImpl::PrepareForContentChecks() {
   nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocShell);
   if (!docShell) {
-    return;
+    return nullptr;
   }
 
   // For content docShells, the mixed content security state is set on the root
   // docShell.
   if (docShell->ItemType() == nsIDocShellTreeItem::typeContent) {
     nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(docShell);
     nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
     Unused << docShellTreeItem->GetSameTypeRootTreeItem(
         getter_AddRefs(sameTypeRoot));
     MOZ_ASSERT(
         sameTypeRoot,
         "No document shell root tree item from document shell tree item!");
     docShell = do_QueryInterface(sameTypeRoot);
     if (!docShell) {
-      return;
+      return nullptr;
     }
   }
 
   RefPtr<dom::Document> doc = docShell->GetDocument();
+  return doc.forget();
+}
+
+// Ask the docShell if we've blocked or loaded any mixed content.
+void nsSecureBrowserUIImpl::CheckForMixedContent() {
+  RefPtr<dom::Document> doc = PrepareForContentChecks();
   if (!doc) {
     // If the docshell has no document, then there is no need to update mState.
     return;
   }
 
   // Has mixed content been loaded or blocked in nsMixedContentBlocker?
   // This only applies to secure documents even if they're affected by mixed
   // content blocking in which case the STATE_IS_BROKEN bit would be set rather
@@ -132,44 +155,53 @@ void nsSecureBrowserUIImpl::CheckForBloc
     if (doc->GetHasMixedActiveContentBlocked()) {
       mState |= STATE_BLOCKED_MIXED_ACTIVE_CONTENT;
     }
 
     if (doc->GetHasMixedDisplayContentBlocked()) {
       mState |= STATE_BLOCKED_MIXED_DISPLAY_CONTENT;
     }
   }
+}
+
+// Ask the docShell if we have any content blocking events.
+void nsSecureBrowserUIImpl::CheckForContentBlockingEvents() {
+  RefPtr<dom::Document> doc = PrepareForContentChecks();
+  if (!doc) {
+    // If the docshell has no document, then there is no need to update mState.
+    return;
+  }
 
   // Has tracking content been blocked or loaded?
   if (doc->GetHasTrackingContentBlocked()) {
-    mState |= STATE_BLOCKED_TRACKING_CONTENT;
+    mEvent |= STATE_BLOCKED_TRACKING_CONTENT;
   }
 
   if (doc->GetHasTrackingContentLoaded()) {
-    mState |= STATE_LOADED_TRACKING_CONTENT;
+    mEvent |= STATE_LOADED_TRACKING_CONTENT;
   }
 
   if (doc->GetHasCookiesBlockedByPermission()) {
-    mState |= STATE_COOKIES_BLOCKED_BY_PERMISSION;
+    mEvent |= STATE_COOKIES_BLOCKED_BY_PERMISSION;
   }
 
   if (doc->GetHasTrackingCookiesBlocked()) {
-    mState |= STATE_COOKIES_BLOCKED_TRACKER;
+    mEvent |= STATE_COOKIES_BLOCKED_TRACKER;
   }
 
   if (doc->GetHasForeignCookiesBlocked()) {
-    mState |= STATE_COOKIES_BLOCKED_FOREIGN;
+    mEvent |= STATE_COOKIES_BLOCKED_FOREIGN;
   }
 
   if (doc->GetHasAllCookiesBlocked()) {
-    mState |= STATE_COOKIES_BLOCKED_ALL;
+    mEvent |= STATE_COOKIES_BLOCKED_ALL;
   }
 
   if (doc->GetHasCookiesLoaded()) {
-    mState |= STATE_COOKIES_LOADED;
+    mEvent |= STATE_COOKIES_LOADED;
   }
 }
 
 // Helper function to determine if the given URI can be considered secure.
 // Essentially, only "https" URIs can be considered secure. However, the URI we
 // have may be e.g. view-source:https://example.com or
 // wyciwyg://https://example.com, in which case we have to evaluate the
 // innermost URI.
@@ -316,17 +348,17 @@ nsresult nsSecureBrowserUIImpl::UpdateSt
 
 // We receive this notification for the nsIWebProgress we added ourselves to
 // (i.e. the window we were passed in Init, which should be the top-level
 // window or whatever corresponds to an <iframe mozbrowser> element). In some
 // cases, we also receive it from nsIWebProgress instances that are children of
 // that nsIWebProgress. We ignore notifications from children because they don't
 // change the top-level state (if children load mixed or tracking content, the
 // docShell will know and will tell us in GetState when we call
-// CheckForBlockedContent).
+// CheckForMixedContent).
 // When we receive a notification from the top-level nsIWebProgress, we extract
 // any relevant security information and set our state accordingly. We then call
 // OnSecurityChange on the docShell corresponding to the nsIWebProgress we were
 // initialized with to notify any downstream listeners of the security state.
 NS_IMETHODIMP
 nsSecureBrowserUIImpl::OnLocationChange(nsIWebProgress* aWebProgress,
                                         nsIRequest* aRequest, nsIURI* aLocation,
                                         uint32_t aFlags) {
@@ -352,16 +384,17 @@ nsSecureBrowserUIImpl::OnLocationChange(
 
   // If this is a same-document location change, we don't need to update our
   // state or notify anyone.
   if (aFlags & LOCATION_CHANGE_SAME_DOCUMENT) {
     return NS_OK;
   }
 
   mState = 0;
+  mEvent = 0;
   mTopLevelSecurityInfo = nullptr;
 
   if (aFlags & LOCATION_CHANGE_ERROR_PAGE) {
     mState = STATE_IS_INSECURE;
     mTopLevelSecurityInfo = nullptr;
   } else {
     // NB: aRequest may be null. It may also not be QI-able to nsIChannel.
     nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
@@ -417,8 +450,14 @@ nsSecureBrowserUIImpl::OnStatusChange(ns
   return NS_OK;
 }
 
 nsresult nsSecureBrowserUIImpl::OnSecurityChange(nsIWebProgress*, nsIRequest*,
                                                  uint32_t) {
   MOZ_ASSERT_UNREACHABLE("Should have been excluded in AddProgressListener()");
   return NS_OK;
 }
+
+nsresult nsSecureBrowserUIImpl::OnContentBlockingEvent(nsIWebProgress*,
+                                                       nsIRequest*, uint32_t) {
+  MOZ_ASSERT_UNREACHABLE("Should have been excluded in AddProgressListener()");
+  return NS_OK;
+}
--- a/security/manager/ssl/nsSecureBrowserUIImpl.h
+++ b/security/manager/ssl/nsSecureBrowserUIImpl.h
@@ -9,16 +9,22 @@
 #include "nsCOMPtr.h"
 #include "nsISecureBrowserUI.h"
 #include "nsIWebProgressListener.h"
 #include "nsWeakReference.h"
 
 class nsITransportSecurityInfo;
 class nsIChannel;
 
+namespace mozilla {
+namespace dom {
+class Document;
+}
+}  // namespace mozilla
+
 #define NS_SECURE_BROWSER_UI_CID                     \
   {                                                  \
     0xcc75499a, 0x1dd1, 0x11b2, {                    \
       0x8a, 0x82, 0xca, 0x41, 0x0a, 0xc9, 0x07, 0xb8 \
     }                                                \
   }
 
 class nsSecureBrowserUIImpl : public nsISecureBrowserUI,
@@ -29,21 +35,25 @@ class nsSecureBrowserUIImpl : public nsI
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIWEBPROGRESSLISTENER
   NS_DECL_NSISECUREBROWSERUI
 
  protected:
   virtual ~nsSecureBrowserUIImpl(){};
 
-  // Do mixed content and tracking protection checks. May update mState.
-  void CheckForBlockedContent();
+  already_AddRefed<mozilla::dom::Document> PrepareForContentChecks();
+  // Do mixed content checks. May update mState.
+  void CheckForMixedContent();
+  // Do Content Blocking checks. May update mEvent.
+  void CheckForContentBlockingEvents();
   // Given some information about a request from an OnLocationChange event,
   // update mState and mTopLevelSecurityInfo.
   nsresult UpdateStateAndSecurityInfo(nsIChannel* channel, nsIURI* uri);
 
   uint32_t mState;
+  uint32_t mEvent;
   nsWeakPtr mDocShell;
   nsWeakPtr mWebProgress;
   nsCOMPtr<nsITransportSecurityInfo> mTopLevelSecurityInfo;
 };
 
 #endif  // nsSecureBrowserUIImpl_h
new file mode 100644
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/mingw_noexports_casts.patch
@@ -0,0 +1,41 @@
+# HG changeset patch
+# User Tom Ritter <tom@mozilla.com>
+# Date 1526498300 18000
+#      Wed May 16 14:18:20 2018 -0500
+# Node ID dd3f4940aeb0c4e00e8bcf1c238f2355ad793489
+# Parent  cf646c80b9545db7ab548f88a482378734ee2f78
+Bug 1462100 Cast to void* to avoid conversion errors on MinGW, which does not do the automatic conversion like msvc r?bobowen
+
+MozReview-Commit-ID: 8fO9Nu9gaxh
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/interception.h b/security/sandbox/chromium/sandbox/win/src/interception.h
+--- a/security/sandbox/chromium/sandbox/win/src/interception.h
++++ b/security/sandbox/chromium/sandbox/win/src/interception.h
+@@ -264,25 +264,25 @@ class InterceptionManager {
+ #define MAKE_SERVICE_NAME(service) &Target##service##64
+ #else
+ #define MAKE_SERVICE_NAME(service) &Target##service
+ #endif
+ 
+ #define ADD_NT_INTERCEPTION(service, id, num_params) \
+   AddToPatchedFunctions(kNtdllName, #service, \
+                         sandbox::INTERCEPTION_SERVICE_CALL, \
+-                        MAKE_SERVICE_NAME(service), id)
++                        (void*)MAKE_SERVICE_NAME(service), id)
+ 
+ #define INTERCEPT_NT(manager, service, id, num_params) \
+   manager->ADD_NT_INTERCEPTION(service, id, num_params)
+ 
+ // When intercepting the EAT it is important that the patched version of the
+ // function not call any functions imported from system libraries unless
+ // |TargetServices::InitCalled()| returns true, because it is only then that
+ // we are guaranteed that our IAT has been initialized.
+ #define INTERCEPT_EAT(manager, dll, function, id, num_params) \
+   manager->AddToPatchedFunctions(dll, #function, sandbox::INTERCEPTION_EAT, \
+-                                 MAKE_SERVICE_NAME(function), id)
++                                 (void*)MAKE_SERVICE_NAME(function), id)
+ #endif  // SANDBOX_EXPORTS
+ 
+ }  // namespace sandbox
+ 
+ #endif  // SANDBOX_SRC_INTERCEPTION_H_
--- a/security/sandbox/chromium-shim/patches/with_update/patch_order.txt
+++ b/security/sandbox/chromium-shim/patches/with_update/patch_order.txt
@@ -13,10 +13,11 @@ allow_read_only_all_paths_rule.patch
 revert_TargetNtSetInformationThread_change.patch
 mingw_base_win_get_caller.patch
 mingw_duplicate_instatinations.patch
 mingw_copy_s.patch
 mingw_operator_new.patch
 mingw_cast_getprocaddress.patch
 mingw_capitalization.patch
 mingw_disable_one_try.patch
+mingw_noexports_casts.patch
 mingw_offsetof.patch
 add_aarch64_windows_support.patch
--- a/security/sandbox/chromium/sandbox/win/src/interception.h
+++ b/security/sandbox/chromium/sandbox/win/src/interception.h
@@ -264,25 +264,25 @@ class InterceptionManager {
 #define MAKE_SERVICE_NAME(service) &Target##service##64
 #else
 #define MAKE_SERVICE_NAME(service) &Target##service
 #endif
 
 #define ADD_NT_INTERCEPTION(service, id, num_params) \
   AddToPatchedFunctions(kNtdllName, #service, \
                         sandbox::INTERCEPTION_SERVICE_CALL, \
-                        MAKE_SERVICE_NAME(service), id)
+                        (void*)MAKE_SERVICE_NAME(service), id)
 
 #define INTERCEPT_NT(manager, service, id, num_params) \
   manager->ADD_NT_INTERCEPTION(service, id, num_params)
 
 // When intercepting the EAT it is important that the patched version of the
 // function not call any functions imported from system libraries unless
 // |TargetServices::InitCalled()| returns true, because it is only then that
 // we are guaranteed that our IAT has been initialized.
 #define INTERCEPT_EAT(manager, dll, function, id, num_params) \
   manager->AddToPatchedFunctions(dll, #function, sandbox::INTERCEPTION_EAT, \
-                                 MAKE_SERVICE_NAME(function), id)
+                                 (void*)MAKE_SERVICE_NAME(function), id)
 #endif  // SANDBOX_EXPORTS
 
 }  // namespace sandbox
 
 #endif  // SANDBOX_SRC_INTERCEPTION_H_
--- a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
+++ b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
@@ -429,16 +429,17 @@ var BrowserTestUtils = {
               BrowserTestUtils._webProgressListeners.delete(wpl);
               resolve();
             }
           }
         },
         onSecurityChange() {},
         onStatusChange() {},
         onLocationChange() {},
+        onContentBlockingEvent() {},
         QueryInterface: ChromeUtils.generateQI([
           Ci.nsIWebProgressListener,
           Ci.nsIWebProgressListener2,
           Ci.nsISupportsWeakReference,
         ]),
       };
       browser.addProgressListener(wpl);
       this._webProgressListeners.add(wpl);
--- a/toolkit/actors/PrintingChild.jsm
+++ b/toolkit/actors/PrintingChild.jsm
@@ -417,9 +417,10 @@ PrintingListener.prototype = {
       curTotalProgress: aCurTotalProgress,
       maxTotalProgress: aMaxTotalProgress,
     });
   },
 
   onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {},
   onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {},
   onSecurityChange(aWebProgress, aRequest, aState) {},
+  onContentBlockingEvent(aWebProgress, aRequest, aEvent) {},
 };
--- a/toolkit/components/antitracking/AntiTrackingCommon.cpp
+++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp
@@ -618,17 +618,17 @@ AntiTrackingCommon::AddFirstPartyStorage
     topInnerWindow->SaveStorageAccessGranted(permissionKey);
 
     // Let's inform the parent window.
     parentWindow->StorageAccessGranted();
 
     nsIChannel* channel =
         pwin->GetCurrentInnerWindow()->GetExtantDoc()->GetChannel();
 
-    pwin->NotifyContentBlockingState(blockReason, channel, false, trackingURI);
+    pwin->NotifyContentBlockingEvent(blockReason, channel, false, trackingURI);
 
     ReportUnblockingConsole(parentWindow, NS_ConvertUTF8toUTF16(trackingOrigin),
                             NS_ConvertUTF8toUTF16(origin), aReason);
 
     if (XRE_IsParentProcess()) {
       LOG(("Saving the permission: trackingOrigin=%s, grantedOrigin=%s",
            trackingOrigin.get(), origin.get()));
 
@@ -1478,22 +1478,22 @@ nsresult AntiTrackingCommon::IsOnContent
   if (!pwin) {
     return;
   }
 
   nsCOMPtr<nsIURI> uri;
   aChannel->GetURI(getter_AddRefs(uri));
 
   if (aDecision == BlockingDecision::eBlock) {
-    pwin->NotifyContentBlockingState(aRejectedReason, aChannel, true, uri);
+    pwin->NotifyContentBlockingEvent(aRejectedReason, aChannel, true, uri);
 
     ReportBlockingToConsole(pwin, uri, aRejectedReason);
   }
 
-  pwin->NotifyContentBlockingState(nsIWebProgressListener::STATE_COOKIES_LOADED,
+  pwin->NotifyContentBlockingEvent(nsIWebProgressListener::STATE_COOKIES_LOADED,
                                    aChannel, false, uri);
 }
 
 /* static */ void AntiTrackingCommon::NotifyBlockingDecision(
     nsPIDOMWindowInner* aWindow, BlockingDecision aDecision,
     uint32_t aRejectedReason) {
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(
@@ -1527,22 +1527,22 @@ nsresult AntiTrackingCommon::IsOnContent
 
   Document* document = aWindow->GetExtantDoc();
   if (!document) {
     return;
   }
   nsIURI* uri = document->GetDocumentURI();
 
   if (aDecision == BlockingDecision::eBlock) {
-    pwin->NotifyContentBlockingState(aRejectedReason, channel, true, uri);
+    pwin->NotifyContentBlockingEvent(aRejectedReason, channel, true, uri);
 
     ReportBlockingToConsole(pwin, uri, aRejectedReason);
   }
 
-  pwin->NotifyContentBlockingState(nsIWebProgressListener::STATE_COOKIES_LOADED,
+  pwin->NotifyContentBlockingEvent(nsIWebProgressListener::STATE_COOKIES_LOADED,
                                    channel, false, uri);
 }
 
 /* static */ void AntiTrackingCommon::StoreUserInteractionFor(
     nsIPrincipal* aPrincipal) {
   if (XRE_IsParentProcess()) {
     nsCOMPtr<nsIURI> uri;
     Unused << aPrincipal->GetURI(getter_AddRefs(uri));
--- a/toolkit/components/antitracking/test/browser/browser.ini
+++ b/toolkit/components/antitracking/test/browser/browser.ini
@@ -13,23 +13,24 @@ support-files =
   3rdParty.html
   3rdPartySVG.html
   3rdPartyUI.html
   3rdPartyWO.html
   3rdPartyOpen.html
   3rdPartyOpenUI.html
   empty.js
   popup.html
+  server.sjs
   storageAccessAPIHelpers.js
+  !/browser/modules/test/browser/head.js
 
 [browser_allowListSeparationInPrivateAndNormalWindows.js]
 skip-if = os == "mac" && !debug # Bug 1503778
 [browser_backgroundImageAssertion.js]
 [browser_blockingCookies.js]
-support-files = server.sjs
 [browser_blockingDOMCache.js]
 skip-if = (os == "win" && os_version == "6.1" && bits == 32 && !debug) # Bug 1491937
 [browser_blockingIndexedDb.js]
 [browser_blockingIndexedDbInWorkers.js]
 [browser_blockingLocalStorage.js]
 skip-if = serviceworker_e10s
 [browser_blockingSessionStorage.js]
 skip-if = serviceworker_e10s
--- a/toolkit/components/antitracking/test/browser/browser_storageAccessDoorHanger.js
+++ b/toolkit/components/antitracking/test/browser/browser_storageAccessDoorHanger.js
@@ -4,22 +4,23 @@ ChromeUtils.import("resource://gre/modul
 const CHROME_BASE = "chrome://mochitests/content/browser/browser/modules/test/browser/";
 Services.scriptloader.loadSubScript(CHROME_BASE + "head.js", this);
 /* import-globals-from ../../../../../browser/modules/test/browser/head.js */
 
 const BLOCK = 0;
 const ALLOW = 1;
 const ALLOW_ON_ANY_SITE = 2;
 
-async function testDoorHanger(choice, showPrompt, topPage, maxConcurrent) {
+async function testDoorHanger(choice, showPrompt, useEscape, topPage, maxConcurrent) {
   info(`Running doorhanger test with choice #${choice}, showPrompt: ${showPrompt} and ` +
-       `topPage: ${topPage}, maxConcurrent: ${maxConcurrent}`);
+       `useEscape: ${useEscape}, topPage: ${topPage}, maxConcurrent: ${maxConcurrent}`);
 
   if (!showPrompt) {
     is(choice, ALLOW, "When not showing a prompt, we can only auto-grant");
+    ok(!useEscape, "When not showing a prompt, we should not be trying to use the Esc key");
   }
 
   await SpecialPowers.flushPrefEnv();
   await SpecialPowers.pushPrefEnv({"set": [
     ["browser.contentblocking.allowlist.annotations.enabled", true],
     ["browser.contentblocking.allowlist.storage.enabled", true],
     [ContentBlocking.prefIntroCount, ContentBlocking.MAX_INTROS],
     ["dom.storage_access.auto_grants", true],
@@ -45,16 +46,17 @@ async function testDoorHanger(choice, sh
     // We need to repeat this constant here since runChecks is stringified
     // and sent to the content process.
     const BLOCK = 0;
 
     await new Promise(resolve => {
       addEventListener("message", function onMessage(e) {
         if (e.data.startsWith("choice:")) {
           window.choice = e.data.split(":")[1];
+          window.useEscape = e.data.split(":")[3];
           removeEventListener("message", onMessage);
           resolve();
         }
       }, false);
       parent.postMessage("getchoice", "*");
     });
 
     /* import-globals-from storageAccessAPIHelpers.js */
@@ -126,32 +128,37 @@ async function testDoorHanger(choice, sh
         resolve(notification);
         return;
       }
       setTimeout(poll, 10);
     });
     Assert.ok(notification, "Should have gotten the notification");
 
     if (choice == BLOCK) {
-      await clickMainAction();
+      if (useEscape) {
+        EventUtils.synthesizeKey("KEY_Escape", {}, window);
+      } else {
+        await clickMainAction();
+      }
     } else if (choice == ALLOW) {
       await clickSecondaryAction(choice - 1);
     } else if (choice == ALLOW_ON_ANY_SITE) {
       await clickSecondaryAction(choice - 1);
     }
     if (choice != BLOCK) {
       await permChanged;
     }
   });
 
   let url = TEST_3RD_PARTY_PAGE + "?disableWaitUntilPermission";
   let ct = ContentTask.spawn(browser,
                              { page: url,
                                callback: runChecks.toString(),
                                choice,
+                               useEscape,
                              },
                              async function(obj) {
     await new content.Promise(resolve => {
       let ifr = content.document.createElement("iframe");
       ifr.onload = function() {
         info("Sending code to the 3rd party content");
         ifr.contentWindow.postMessage(obj.callback, "*");
       };
@@ -169,17 +176,18 @@ async function testDoorHanger(choice, sh
         }
 
         if (event.data.type == "info") {
           info(event.data.msg);
           return;
         }
 
         if (event.data == "getchoice") {
-          ifr.contentWindow.postMessage("choice:" + obj.choice, "*");
+          ifr.contentWindow.postMessage("choice:" + obj.choice +
+                                        ":useEscape:" + obj.useEscape, "*");
           return;
         }
 
         ok(false, "Unknown message");
       });
 
       content.document.body.appendChild(ifr);
       ifr.src = obj.page;
@@ -298,26 +306,29 @@ async function cleanUp() {
   await new Promise(resolve => {
     Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value => resolve());
   });
 }
 
 async function runRound(topPage, showPrompt, maxConcurrent) {
   if (showPrompt) {
     await preparePermissionsFromOtherSites(topPage);
-    await testDoorHanger(BLOCK, showPrompt, topPage, maxConcurrent);
+    await testDoorHanger(BLOCK, showPrompt, true, topPage, maxConcurrent);
+    await cleanUp();
+    await preparePermissionsFromOtherSites(topPage);
+    await testDoorHanger(BLOCK, showPrompt, false, topPage, maxConcurrent);
     await cleanUp();
     await preparePermissionsFromOtherSites(topPage);
-    await testDoorHanger(ALLOW, showPrompt, topPage, maxConcurrent);
+    await testDoorHanger(ALLOW, showPrompt, false, topPage, maxConcurrent);
     await cleanUp();
     await preparePermissionsFromOtherSites(topPage);
-    await testDoorHanger(ALLOW_ON_ANY_SITE, showPrompt, topPage, maxConcurrent);
+    await testDoorHanger(ALLOW_ON_ANY_SITE, showPrompt, false, topPage, maxConcurrent);
   } else {
     await preparePermissionsFromOtherSites(topPage);
-    await testDoorHanger(ALLOW, showPrompt, topPage, maxConcurrent);
+    await testDoorHanger(ALLOW, showPrompt, false, topPage, maxConcurrent);
   }
   await cleanUp();
 }
 
 add_task(async function() {
   await runRound(TEST_TOP_PAGE, false, 1);
   await runRound(TEST_TOP_PAGE_2, true, 1);
   await runRound(TEST_TOP_PAGE, false, 5);
--- a/toolkit/components/antitracking/test/browser/head.js
+++ b/toolkit/components/antitracking/test/browser/head.js
@@ -294,18 +294,18 @@ this.AntiTracking = {
       }
 
       await AntiTracking._setupTest(win, options.cookieBehavior,
                                     options.blockingByContentBlockingRTUI,
                                     options.extraPrefs);
 
       let cookieBlocked = 0;
       let listener = {
-        onSecurityChange(webProgress, request, state) {
-          if ((state & options.expectedBlockingNotifications)) {
+        onContentBlockingEvent(webProgress, request, event) {
+          if ((event & options.expectedBlockingNotifications)) {
             ++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
@@ -701,16 +701,26 @@ NS_IMETHODIMP
 nsWebBrowser::OnSecurityChange(nsIWebProgress* aWebProgress,
                                nsIRequest* aRequest, uint32_t aState) {
   if (mProgressListener) {
     return mProgressListener->OnSecurityChange(aWebProgress, aRequest, aState);
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsWebBrowser::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
+                                     nsIRequest* aRequest, uint32_t aEvent) {
+  if (mProgressListener) {
+    return mProgressListener->OnContentBlockingEvent(aWebProgress, aRequest,
+                                                     aEvent);
+  }
+  return NS_OK;
+}
+
 //*****************************************************************************
 // nsWebBrowser::nsIWebBrowserPersist
 //*****************************************************************************
 
 NS_IMETHODIMP
 nsWebBrowser::GetPersistFlags(uint32_t* aPersistFlags) {
   NS_ENSURE_ARG_POINTER(aPersistFlags);
   nsresult rv = NS_OK;
--- a/toolkit/components/downloads/DownloadCore.jsm
+++ b/toolkit/components/downloads/DownloadCore.jsm
@@ -2673,16 +2673,17 @@ this.DownloadPDFSaver.prototype = {
           onProgressChange(webProgress, request, curSelfProgress,
                                      maxSelfProgress, curTotalProgress,
                                      maxTotalProgress) {
             aSetProgressBytesFn(curTotalProgress, maxTotalProgress, false);
           },
           onLocationChange() {},
           onStatusChange() {},
           onSecurityChange() {},
+          onContentBlockingEvent() {},
         });
       });
     } finally {
       // Remove the print object to avoid leaks
       this._webBrowserPrint = null;
     }
 
     let fileInfo = await OS.File.stat(targetPath);
--- a/toolkit/components/downloads/DownloadLegacy.js
+++ b/toolkit/components/downloads/DownloadLegacy.js
@@ -162,16 +162,18 @@ DownloadLegacyTransfer.prototype = {
       this._promiseDownload.then(download => {
         download.saver.onTransferFinished(aStatus);
       }).catch(Cu.reportError);
     }
   },
 
   onSecurityChange() { },
 
+  onContentBlockingEvent() { },
+
   // nsIWebProgressListener2
   onProgressChange64: function DLT_onProgressChange64(aWebProgress, aRequest,
                                                       aCurSelfProgress,
                                                       aMaxSelfProgress,
                                                       aCurTotalProgress,
                                                       aMaxTotalProgress) {
     // Since this progress function is invoked frequently, we use a slightly
     // more complex solution that optimizes the case where we already have an
--- a/toolkit/components/printing/content/printPreviewProgress.js
+++ b/toolkit/components/printing/content/printPreviewProgress.js
@@ -55,16 +55,17 @@ var progressListener = {
       docURL = docURLStr;
       if (docTitle == "")
         dialog.title.value = docURLStr;
     }
   },
 
   onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {},
   onSecurityChange(aWebProgress, aRequest, state) {},
+  onContentBlockingEvent(aWebProgress, aRequest, event) {},
 
   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,16 +139,20 @@ var progressListener = {
       if (aMessage != "")
         dialog.title.setAttribute("value", aMessage);
     },
 
     onSecurityChange(aWebProgress, aRequest, state) {
       // we can ignore this notification
     },
 
+    onContentBlockingEvent(aWebProgress, aRequest, event) {
+      // we can ignore this notification
+    },
+
     QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener",
                                             "nsISupportsWeakReference"]),
 };
 
 function getString( stringId ) {
    // Check if we've fetched this string already.
    if (!(stringId in dialog.strings)) {
       // Try to get it.
--- a/toolkit/components/printingui/ipc/PrintProgressDialogChild.cpp
+++ b/toolkit/components/printingui/ipc/PrintProgressDialogChild.cpp
@@ -83,16 +83,23 @@ PrintProgressDialogChild::OnStatusChange
 
 NS_IMETHODIMP
 PrintProgressDialogChild::OnSecurityChange(nsIWebProgress* aProgress,
                                            nsIRequest* aRequest,
                                            uint32_t aState) {
   return NS_OK;
 }
 
+NS_IMETHODIMP
+PrintProgressDialogChild::OnContentBlockingEvent(nsIWebProgress* aProgress,
+                                                 nsIRequest* aRequest,
+                                                 uint32_t aEvent) {
+  return NS_OK;
+}
+
 // nsIPrintProgressParams
 
 NS_IMETHODIMP
 PrintProgressDialogChild::GetDocTitle(nsAString& aDocTitle) {
   aDocTitle = mDocTitle;
   return NS_OK;
 }
 
--- a/toolkit/components/printingui/nsPrintProgress.cpp
+++ b/toolkit/components/printingui/nsPrintProgress.cpp
@@ -240,17 +240,22 @@ NS_IMETHODIMP nsPrintProgress::OnStatusC
                                        aMessage);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP nsPrintProgress::OnSecurityChange(nsIWebProgress *aWebProgress,
                                                 nsIRequest *aRequest,
-                                                uint32_t state) {
+                                                uint32_t aState) {
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsPrintProgress::OnContentBlockingEvent(
+    nsIWebProgress *aWebProgress, nsIRequest *aRequest, uint32_t aEvent) {
   return NS_OK;
 }
 
 nsresult nsPrintProgress::ReleaseListeners() {
   m_listenerList.Clear();
 
   return NS_OK;
 }
--- a/toolkit/components/printingui/nsPrintingPromptService.cpp
+++ b/toolkit/components/printingui/nsPrintingPromptService.cpp
@@ -200,17 +200,30 @@ nsPrintingPromptService::OnStatusChange(
   }
 #endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPrintingPromptService::OnSecurityChange(nsIWebProgress* aWebProgress,
                                           nsIRequest* aRequest,
-                                          uint32_t state) {
+                                          uint32_t aState) {
 #if !defined(XP_MACOSX)
   if (mWebProgressListener) {
     return mWebProgressListener->OnSecurityChange(aWebProgress, aRequest,
-                                                  state);
+                                                  aState);
   }
 #endif
   return NS_OK;
 }
+
+NS_IMETHODIMP
+nsPrintingPromptService::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
+                                                nsIRequest* aRequest,
+                                                uint32_t aEvent) {
+#if !defined(XP_MACOSX)
+  if (mWebProgressListener) {
+    return mWebProgressListener->OnContentBlockingEvent(aWebProgress, aRequest,
+                                                        aEvent);
+  }
+#endif
+  return NS_OK;
+}
--- a/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp
+++ b/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp
@@ -236,16 +236,25 @@ nsBrowserStatusFilter::OnStatusChange(ns
 NS_IMETHODIMP
 nsBrowserStatusFilter::OnSecurityChange(nsIWebProgress *aWebProgress,
                                         nsIRequest *aRequest, uint32_t aState) {
   if (!mListener) return NS_OK;
 
   return mListener->OnSecurityChange(aWebProgress, aRequest, aState);
 }
 
+NS_IMETHODIMP
+nsBrowserStatusFilter::OnContentBlockingEvent(nsIWebProgress *aWebProgress,
+                                              nsIRequest *aRequest,
+                                              uint32_t aEvent) {
+  if (!mListener) return NS_OK;
+
+  return mListener->OnContentBlockingEvent(aWebProgress, aRequest, aEvent);
+}
+
 //-----------------------------------------------------------------------------
 // nsBrowserStatusFilter::nsIWebProgressListener2
 //-----------------------------------------------------------------------------
 NS_IMETHODIMP
 nsBrowserStatusFilter::OnProgressChange64(nsIWebProgress *aWebProgress,
                                           nsIRequest *aRequest,
                                           int64_t aCurSelfProgress,
                                           int64_t aMaxSelfProgress,
--- a/toolkit/components/url-classifier/tests/mochitest/test_threathit_report.html
+++ b/toolkit/components/url-classifier/tests/mochitest/test_threathit_report.html
@@ -189,22 +189,22 @@ 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) {
+        onContentBlockingEvent(aWebProgress, aRequest, aEvent) {
           expected = aTestData.reportUrl;
         },
         QueryInterface: ChromeUtils.generateQI(["nsISupportsWeakReference"]),
       };
-      wp.addProgressListener(progressListener, wp.NOTIFY_SECURITY);
+      wp.addProgressListener(progressListener, wp.NOTIFY_CONTENT_BLOCKING);
 
       await BrowserTestUtils.loadURI(browser, aTestData.url);
       await BrowserTestUtils.waitForContentEvent(browser, "DOMContentLoaded");
       checkResults(aTestData, expected);
       win.close();
       resolve();
     })();
   });
--- a/toolkit/components/windowcreator/test/browser_bug1204626.js
+++ b/toolkit/components/windowcreator/test/browser_bug1204626.js
@@ -40,16 +40,17 @@ function one_test(delay, continuation) {
       }
     });
 
     wbp.progressListener = {
       onProgressChange() {},
       onLocationChange() {},
       onStatusChange() {},
       onSecurityChange() {},
+      onContentBlockingEvent() {},
       onStateChange(_wbp, _req, state, _status) {
         if ((state & Ci.nsIWebProgressListener.STATE_STOP) == 0) {
           return;
         }
         ok(true, "Finished save (" + delayStr + ") but might have crashed.");
         continuation();
       },
     };
--- a/toolkit/components/windowcreator/test/test_bug1170334_wbp_xmlstyle.html
+++ b/toolkit/components/windowcreator/test/test_bug1170334_wbp_xmlstyle.html
@@ -36,16 +36,17 @@ iframe.onload = function iframe_onload1(
   tmpDir.append(nameStem + "_files");
 
   // When the document in the iframe is saved, try to load the result.
   wbp.progressListener = {
     onProgressChange() {},
     onLocationChange() {},
     onStatusChange() {},
     onSecurityChange() {},
+    onContentBlockingEvent() {},
     onStateChange: function wbp_stateChange(_wbp, _req, state, status) {
       if ((state & Ci.nsIWebProgressListener.STATE_STOP) == 0) {
         return;
       }
       is(status, Cr.NS_OK, "nsWebBrowserPersist status");
       iframe.onload = function iframe_onload2() {
         let elem = iframe.contentDocument.documentElement;
         is(elem && elem.tagName, "thing", "document element tag");
--- a/toolkit/components/windowcreator/test/test_bug1192654.html
+++ b/toolkit/components/windowcreator/test/test_bug1192654.html
@@ -42,16 +42,17 @@ iframe.onload = function iframe_onload1(
   let tmpDir = tmp.clone();
   tmpDir.append(nameStem + "_files");
 
   wbp.progressListener = {
     onProgressChange() {},
     onLocationChange() {},
     onStatusChange() {},
     onSecurityChange() {},
+    onContentBlockingEvent() {},
     onStateChange: wbp_stateChange,
   };
   SimpleTest.registerCleanupFunction(cleanUp);
 
   wbp.saveDocument(doc, tmpFile, tmpDir, null, 0, 0);
 
   function wbp_stateChange(_wbp, _req, state, status) {
     if ((state & Ci.nsIWebProgressListener.STATE_STOP) == 0) {
--- a/toolkit/content/tests/browser/common/mockTransfer.js
+++ b/toolkit/content/tests/browser/common/mockTransfer.js
@@ -39,16 +39,17 @@ MockTransfer.prototype = {
   onLocationChange() {},
   onStatusChange: function MTFC_onStatusChange(aWebProgress, aRequest, aStatus,
                                                aMessage) {
     // If at least one notification reported an error, the download failed.
     if (!Components.isSuccessCode(aStatus))
       this._downloadIsSuccessful = false;
   },
   onSecurityChange() {},
+  onContentBlockingEvent() {},
 
   /* nsIWebProgressListener2 */
   onProgressChange64() {},
   onRefreshAttempted() {},
 
   /* nsITransfer */
   init() {},
   setSha256Hash() {},
--- a/toolkit/modules/PopupNotifications.jsm
+++ b/toolkit/modules/PopupNotifications.jsm
@@ -248,17 +248,18 @@ function PopupNotifications(tabbrowser, 
 
     // If the chrome window has a focused element, let it handle the ESC key instead.
     if (!focusedElement ||
         focusedElement == doc.body ||
         focusedElement == this.tabbrowser.selectedBrowser ||
         // Ignore focused elements inside the notification.
         getNotificationFromElement(focusedElement) == notification ||
         notification.contains(focusedElement)) {
-      this._onButtonEvent(aEvent, "secondarybuttoncommand", "esc-press", notification);
+      let escAction = notification.notification.options.escAction;
+      this._onButtonEvent(aEvent, escAction, "esc-press", notification);
     }
   };
 
   let documentElement = this.window.document.documentElement;
   let locationBarHidden = documentElement.getAttribute("chromehidden").includes("location");
   let isFullscreen = !!this.window.document.fullscreenElement;
 
   this.panel.setAttribute("followanchor", !locationBarHidden && !isFullscreen);
@@ -462,16 +463,21 @@ PopupNotifications.prototype = {
    *                     An optional string formatted to look bold and used in the
    *                     notifiation description header text. Usually a host name or
    *                     addon name.
    *        secondName:
    *                     An optional string formatted to look bold and used in the
    *                     notification description header text. Usually a host name or
    *                     addon name. This is similar to name, and only used in case
    *                     where message contains two "<>" placeholders.
+   *        escAction:
+   *                     An optional string indicating the action to take when the
+   *                     Esc key is pressed. This should be set to the name of the
+   *                     command to run. If not provided, "secondarybuttoncommand"
+   *                     will be used.
    * @returns the Notification object corresponding to the added notification.
    */
   show: function PopupNotifications_show(browser, id, message, anchorID,
                                          mainAction, secondaryActions, options) {
     function isInvalidAction(a) {
       return !a || !(typeof(a.callback) == "function") || !a.label || !a.accessKey;
     }
 
@@ -482,16 +488,25 @@ PopupNotifications.prototype = {
     if (mainAction && isInvalidAction(mainAction))
       throw "PopupNotifications_show: invalid mainAction";
     if (secondaryActions && secondaryActions.some(isInvalidAction))
       throw "PopupNotifications_show: invalid secondaryActions";
 
     let notification = new Notification(id, message, anchorID, mainAction,
                                         secondaryActions, browser, this, options);
 
+    if (options) {
+      let escAction = options.escAction;
+      if (escAction != "buttoncommand" &&
+          escAction != "secondarybuttoncommand") {
+        escAction = "secondarybuttoncommand";
+      }
+      notification.options.escAction = escAction;
+    }
+
     if (options && options.dismissed)
       notification.dismissed = true;
 
     let existingNotification = this.getNotification(id, browser);
     if (existingNotification)
       this._remove(existingNotification);
 
     let notifications = this._getNotificationsForBrowser(browser);
@@ -826,17 +841,17 @@ PopupNotifications.prototype = {
         popupnotification.setAttribute("secondname", desc.secondName);
         popupnotification.setAttribute("secondendlabel", desc.secondEnd);
       }
 
       popupnotification.setAttribute("id", popupnotificationID);
       popupnotification.setAttribute("popupid", n.id);
       popupnotification.setAttribute("oncommand", "PopupNotifications._onCommand(event);");
       if (Services.prefs.getBoolPref("privacy.permissionPrompts.showCloseButton")) {
-        popupnotification.setAttribute("closebuttoncommand", "PopupNotifications._onButtonEvent(event, 'secondarybuttoncommand', 'esc-press');");
+        popupnotification.setAttribute("closebuttoncommand", "PopupNotifications._onButtonEvent(event, '" + n.options.escAction + "', 'esc-press');");
       } else {
         popupnotification.setAttribute("closebuttoncommand", `PopupNotifications._dismiss(event, ${TELEMETRY_STAT_DISMISSAL_CLOSE_BUTTON});`);
       }
       if (n.mainAction) {
         popupnotification.setAttribute("buttonlabel", n.mainAction.label);
         popupnotification.setAttribute("buttonaccesskey", n.mainAction.accessKey);
         popupnotification.toggleAttribute("buttonhighlight", !n.mainAction.disableHighlight);
         popupnotification.setAttribute("buttoncommand", "PopupNotifications._onButtonEvent(event, 'buttoncommand');");
--- a/toolkit/modules/RemoteSecurityUI.jsm
+++ b/toolkit/modules/RemoteSecurityUI.jsm
@@ -3,23 +3,27 @@
 // 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._state = 0;
+    this._event = 0;
 }
 
 RemoteSecurityUI.prototype = {
   QueryInterface: ChromeUtils.generateQI([Ci.nsISecureBrowserUI]),
 
   // nsISecureBrowserUI
   get state() { return this._state; },
-  get tooltipText() { return ""; },
+  get contentBlockingEvent() { return this._event; },
   get secInfo() { return this._secInfo; },
 
   _update(aSecInfo, aState) {
     this._secInfo = aSecInfo;
     this._state = aState;
   },
+  _updateContentBlockingEvent(aEvent) {
+    this._event = aEvent;
+  },
 };
--- a/toolkit/modules/RemoteWebProgress.jsm
+++ b/toolkit/modules/RemoteWebProgress.jsm
@@ -30,27 +30,28 @@ function RemoteWebProgress(aManager, aIs
 
   this._isLoadingDocument = false;
   this._DOMWindowID = 0;
   this._isTopLevel = aIsTopLevel;
   this._loadType = 0;
 }
 
 RemoteWebProgress.prototype = {
-  NOTIFY_STATE_REQUEST:  0x00000001,
-  NOTIFY_STATE_DOCUMENT: 0x00000002,
-  NOTIFY_STATE_NETWORK:  0x00000004,
-  NOTIFY_STATE_WINDOW:   0x00000008,
-  NOTIFY_STATE_ALL:      0x0000000f,
-  NOTIFY_PROGRESS:       0x00000010,
-  NOTIFY_STATUS:         0x00000020,
-  NOTIFY_SECURITY:       0x00000040,
-  NOTIFY_LOCATION:       0x00000080,
-  NOTIFY_REFRESH:        0x00000100,
-  NOTIFY_ALL:            0x000001ff,
+  NOTIFY_STATE_REQUEST:    0x00000001,
+  NOTIFY_STATE_DOCUMENT:   0x00000002,
+  NOTIFY_STATE_NETWORK:    0x00000004,
+  NOTIFY_STATE_WINDOW:     0x00000008,
+  NOTIFY_STATE_ALL:        0x0000000f,
+  NOTIFY_PROGRESS:         0x00000010,
+  NOTIFY_STATUS:           0x00000020,
+  NOTIFY_SECURITY:         0x00000040,
+  NOTIFY_LOCATION:         0x00000080,
+  NOTIFY_REFRESH:          0x00000100,
+  NOTIFY_CONTENT_BLOCKING: 0x00000200,
+  NOTIFY_ALL:              0x000003ff,
 
   get isLoadingDocument() { return this._isLoadingDocument; },
   get DOMWindow() {
     throw Cr.NS_ERROR_NOT_AVAILABLE;
   },
   get DOMWindowID() { return this._DOMWindowID; },
   get isTopLevel() { return this._isTopLevel; },
   get loadType() { return this._loadType; },
@@ -75,26 +76,28 @@ RemoteWebProgressManager.prototype = {
   swapBrowser(aBrowser) {
     if (this._messageManager) {
       this._messageManager.removeMessageListener("Content:StateChange", this);
       this._messageManager.removeMessageListener("Content:LocationChange", this);
       this._messageManager.removeMessageListener("Content:SecurityChange", this);
       this._messageManager.removeMessageListener("Content:StatusChange", this);
       this._messageManager.removeMessageListener("Content:ProgressChange", this);
       this._messageManager.removeMessageListener("Content:LoadURIResult", this);
+      this._messageManager.removeMessageListener("Content:ContentBlockingEvent", this);
     }
 
     this._browser = aBrowser;
     this._messageManager = aBrowser.messageManager;
     this._messageManager.addMessageListener("Content:StateChange", this);
     this._messageManager.addMessageListener("Content:LocationChange", this);
     this._messageManager.addMessageListener("Content:SecurityChange", this);
     this._messageManager.addMessageListener("Content:StatusChange", this);
     this._messageManager.addMessageListener("Content:ProgressChange", this);
     this._messageManager.addMessageListener("Content:LoadURIResult", this);
+    this._messageManager.addMessageListener("Content:ContentBlockingEvent", this);
   },
 
   swapListeners(aOtherRemoteWebProgressManager) {
     let temp = aOtherRemoteWebProgressManager.progressListeners;
     aOtherRemoteWebProgressManager._progressListeners = this._progressListeners;
     this._progressListeners = temp;
   },
 
@@ -255,16 +258,31 @@ RemoteWebProgressManager.prototype = {
       }
 
       this._callProgressListeners(
         Ci.nsIWebProgress.NOTIFY_SECURITY, "onSecurityChange", webProgress,
         request, state
       );
       break;
 
+    case "Content:ContentBlockingEvent":
+      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._updateContentBlockingEvent(json.event);
+      }
+
+      this._callProgressListeners(
+        Ci.nsIWebProgress.NOTIFY_CONTENT_BLOCKING, "onContentBlockingEvent",
+        webProgress, request, json.event
+      );
+      break;
+
     case "Content:StatusChange":
       this._callProgressListeners(
         Ci.nsIWebProgress.NOTIFY_STATUS, "onStatusChange", webProgress, request,
         json.status, json.message
       );
       break;
 
     case "Content:ProgressChange":
--- a/toolkit/modules/WebProgressChild.jsm
+++ b/toolkit/modules/WebProgressChild.jsm
@@ -189,22 +189,29 @@ class WebProgressChild {
   }
 
   onSecurityChange(aWebProgress, aRequest, aState) {
     let json = this._setupJSON(aWebProgress, aRequest);
 
     json.state = aState;
     json.secInfo = this.getSecInfoAsString();
 
+    this._send("Content:SecurityChange", json);
+  }
+
+  onContentBlockingEvent(aWebProgress, aRequest, aEvent) {
+    let json = this._setupJSON(aWebProgress, aRequest);
+
+    json.event = aEvent;
     json.matchedList = null;
     if (aRequest && aRequest instanceof Ci.nsIClassifiedChannel) {
       json.matchedList = aRequest.matchedList;
     }
 
-    this._send("Content:SecurityChange", json);
+    this._send("Content:ContentBlockingEvent", json);
   }
 
   onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
     return true;
   }
 
   sendLoadCallResult() {
     this.mm.sendAsyncMessage("Content:LoadURIResult");
--- a/toolkit/moz.configure
+++ b/toolkit/moz.configure
@@ -865,17 +865,17 @@ def skia_includes(skia):
         ]
     return includes
 
 set_config('SKIA_INCLUDES', skia_includes)
 
 option('--with-system-webp',
        help='Use system libwebp (located with pkgconfig)')
 
-system_webp = pkg_check_modules('MOZ_WEBP', 'libwebp >= 1.0.1 libwebpdemux >= 1.0.1',
+system_webp = pkg_check_modules('MOZ_WEBP', 'libwebp >= 1.0.2 libwebpdemux >= 1.0.2',
                                 when='--with-system-webp')
 
 set_config('MOZ_SYSTEM_WEBP', depends(when=system_webp)(lambda: True))
 
 # Build Freetype in the tree
 # ==============================================================
 @depends(target, skia_pdf)
 def tree_freetype(target, skia_pdf):
--- a/toolkit/mozapps/downloads/nsHelperAppDlg.js
+++ b/toolkit/mozapps/downloads/nsHelperAppDlg.js
@@ -40,17 +40,17 @@ nsUnknownContentTypeDialogProgressListen
       // Close the dialog.
       this.helperAppDlg.onCancel();
       if ( this.helperAppDlg.mDialog ) {
         this.helperAppDlg.mDialog.close();
       }
     }
   },
 
-  // Ignore onProgressChange, onProgressChange64, onStateChange, onLocationChange, onSecurityChange, and onRefreshAttempted notifications.
+  // Ignore onProgressChange, onProgressChange64, onStateChange, onLocationChange, onSecurityChange, onContentBlockingEvent and onRefreshAttempted notifications.
   onProgressChange(aWebProgress,
                    aRequest,
                    aCurSelfProgress,
                    aMaxSelfProgress,
                    aCurTotalProgress,
                    aMaxTotalProgress) {
   },
 
@@ -65,17 +65,20 @@ nsUnknownContentTypeDialogProgressListen
 
 
   onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
   },
 
   onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
   },
 
-  onSecurityChange(aWebProgress, aRequest, state) {
+  onSecurityChange(aWebProgress, aRequest, aState) {
+  },
+
+  onContentBlockingEvent(aWebProgress, aRequest, aEvent) {
   },
 
   onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
     return true;
   },
 };
 
 // /////////////////////////////////////////////////////////////////////////////
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -2220,16 +2220,18 @@ var gDiscoverView = {
     if (aState & Ci.nsIWebProgressListener.STATE_IS_SECURE)
       return;
 
     // Canceling the request will send an error to onStateChange which will show
     // the error page
     aRequest.cancel(Cr.NS_BINDING_ABORTED);
   },
 
+  onContentBlockingEvent(aWebProgress, aRequest, aEvent) {},
+
   onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
     let transferStart = Ci.nsIWebProgressListener.STATE_IS_DOCUMENT |
                         Ci.nsIWebProgressListener.STATE_IS_REQUEST |
                         Ci.nsIWebProgressListener.STATE_TRANSFERRING;
     // Once transferring begins show the content
     if ((aStateFlags & transferStart) === transferStart)
       this.node.selectedPanel = this._browser;
 
--- a/toolkit/mozapps/extensions/internal/LightweightThemePersister.jsm
+++ b/toolkit/mozapps/extensions/internal/LightweightThemePersister.jsm
@@ -105,19 +105,20 @@ function _persistImage(sourceURL, localF
   persist.saveURI(sourceURI, sourcePrincipal, 0,
                   null, Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
                   null, null, targetURI, null);
 }
 
 function _persistProgressListener(successCallback) {
   this.onLocationChange = function() {};
   this.onProgressChange = function() {};
-  this.onStatusChange   = function() {};
+  this.onStatusChange = function() {};
   this.onSecurityChange = function() {};
-  this.onStateChange    = function(aWebProgress, aRequest, aStateFlags, aStatus) {
+  this.onContentBlockingEvent = function() {};
+  this.onStateChange = function(aWebProgress, aRequest, aStateFlags, aStatus) {
     if (aRequest &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
       // LWTs used to get their image files from the network…
       if (aRequest instanceof Ci.nsIHttpChannel &&
           aRequest.QueryInterface(Ci.nsIHttpChannel).requestSucceeded ||
           // … but static themes usually include the image data inside the
           // extension package.
--- a/toolkit/mozapps/extensions/test/browser/browser_bug562797.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug562797.js
@@ -24,16 +24,17 @@ var gProgressListener = {
       executeSoon(gLoadCompleteCallback);
     gLoadCompleteCallback = null;
   },
 
   onLocationChange() { },
   onSecurityChange() { },
   onProgressChange() { },
   onStatusChange() { },
+  onContentBlockingEvent() { },
 
   QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener,
                                           Ci.nsISupportsWeakReference]),
 };
 
 function waitForLoad(aManager, aCallback) {
   let promise = new Promise(resolve => {
     var browser = aManager.document.getElementById("discover-browser");
--- a/toolkit/mozapps/extensions/test/browser/browser_discovery.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_discovery.js
@@ -23,16 +23,17 @@ var gProgressListener = {
       executeSoon(gLoadCompleteCallback);
     gLoadCompleteCallback = null;
   },
 
   onLocationChange() { },
   onSecurityChange() { },
   onProgressChange() { },
   onStatusChange() { },
+  onContentBlockingEvent() { },
 
   QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener,
                                           Ci.nsISupportsWeakReference]),
 };
 
 function test() {
   // Switch to a known url
   Services.prefs.setCharPref(PREF_DISCOVERURL, MAIN_URL);
--- a/uriloader/base/nsDocLoader.cpp
+++ b/uriloader/base/nsDocLoader.cpp
@@ -1407,16 +1407,36 @@ NS_IMETHODIMP nsDocLoader::OnSecurityCha
 
   // Pass the notification up to the parent...
   if (mParent) {
     mParent->OnSecurityChange(aContext, aState);
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP nsDocLoader::OnContentBlockingEvent(nsISupports* aContext,
+                                                  uint32_t aEvent) {
+  //
+  // Fire progress notifications out to any registered nsIWebProgressListeners.
+  //
+
+  nsCOMPtr<nsIRequest> request = do_QueryInterface(aContext);
+  nsIWebProgress* webProgress = static_cast<nsIWebProgress*>(this);
+
+  NOTIFY_LISTENERS(
+      nsIWebProgress::NOTIFY_CONTENT_BLOCKING,
+      listener->OnContentBlockingEvent(webProgress, request, aEvent););
+
+  // Pass the notification up to the parent...
+  if (mParent) {
+    mParent->OnContentBlockingEvent(aContext, aEvent);
+  }
+  return NS_OK;
+}
+
 /*
  * Implementation of nsISupportsPriority methods...
  *
  * The priority of the DocLoader _is_ the priority of its LoadGroup.
  *
  * XXX(darin): Once we start storing loadgroups in loadgroups, this code will
  * go away.
  */
--- a/uriloader/base/nsIWebProgress.idl
+++ b/uriloader/base/nsIWebProgress.idl
@@ -76,30 +76,34 @@ interface nsIWebProgress : nsISupports
    *   Receive onStatusChange events.
    *
    * NOTIFY_SECURITY
    *   Receive onSecurityChange events.
    *
    * NOTIFY_LOCATION
    *   Receive onLocationChange events.
    *
+   * NOTIFY_CONTENT_BLOCKING
+   *   Receive onContentBlockingEvent events.
+   *
    * NOTIFY_REFRESH
    *   Receive onRefreshAttempted events.
    *   This is defined on nsIWebProgressListener2.
    */
-  const unsigned long NOTIFY_PROGRESS       = 0x00000010;
-  const unsigned long NOTIFY_STATUS         = 0x00000020;
-  const unsigned long NOTIFY_SECURITY       = 0x00000040;
-  const unsigned long NOTIFY_LOCATION       = 0x00000080;
-  const unsigned long NOTIFY_REFRESH        = 0x00000100;
+  const unsigned long NOTIFY_PROGRESS         = 0x00000010;
+  const unsigned long NOTIFY_STATUS           = 0x00000020;
+  const unsigned long NOTIFY_SECURITY         = 0x00000040;
+  const unsigned long NOTIFY_LOCATION         = 0x00000080;
+  const unsigned long NOTIFY_REFRESH          = 0x00000100;
+  const unsigned long NOTIFY_CONTENT_BLOCKING = 0x00000200;
 
   /**
    * This flag enables all notifications.
    */
-  const unsigned long NOTIFY_ALL            = 0x000001ff;
+  const unsigned long NOTIFY_ALL              = 0x000003ff;
 
   /**
    * Registers a listener to receive web progress events.
    *
    * @param aListener
    *        The listener interface to be called when a progress event occurs.
    *        This object must also implement nsISupportsWeakReference.
    * @param aNotifyMask
--- a/uriloader/base/nsIWebProgressListener.idl
+++ b/uriloader/base/nsIWebProgressListener.idl
@@ -205,38 +205,16 @@ interface nsIWebProgressListener : nsISu
    *   Mixed display content has been blocked from loading.
    *
    * STATE_LOADED_MIXED_DISPLAY_CONTENT
    *   Mixed display content has been loaded. State should be STATE_IS_BROKEN.
    */
   const unsigned long STATE_BLOCKED_MIXED_DISPLAY_CONTENT = 0x00000100;
   const unsigned long STATE_LOADED_MIXED_DISPLAY_CONTENT  = 0x00000200;
 
-   /**
-   *  Safe Browsing blocking content flags
-   *
-   * NOTE: IF YOU ARE ADDING MORE OF THESE FLAGS, MAKE SURE TO EDIT
-   * nsSecureBrowserUIImpl::CheckForBlockedContent().
-   *
-   * May be set in addition to the State security Flags, to indicate that
-   * tracking or unsafe content has been encountered.
-   *
-   * STATE_BLOCKED_TRACKING_CONTENT
-   *   Tracking content has been blocked from loading.
-   *
-   * STATE_LOADED_TRACKING_CONTENT
-   *   Tracking content has been loaded.
-   *
-   * STATE_BLOCKED_UNSAFE_CONTENT
-   *   Content which againts SafeBrowsing list has been blocked from loading.
-   */
-  const unsigned long STATE_BLOCKED_TRACKING_CONTENT         = 0x00001000;
-  const unsigned long STATE_LOADED_TRACKING_CONTENT          = 0x00002000;
-  const unsigned long STATE_BLOCKED_UNSAFE_CONTENT           = 0x00004000;
-
   /**
    * Diagnostic flags
    *
    * NOTE: IF YOU ARE ADDING MORE OF THESE FLAGS, MAKE SURE TO EDIT
    * nsSecureBrowserUIImpl::CheckForBlockedContent().
    *
    * May be set in addition to other security state flags to indicate that
    * some state is countered that deserves a warning or error, but does not
@@ -274,36 +252,48 @@ interface nsIWebProgressListener : nsISu
     * STATE_CERT_USER_OVERRIDDEN
     *   The user has added a security exception for the site.
     */
   const unsigned long STATE_USES_SSL_3                = 0x01000000;
   const unsigned long STATE_USES_WEAK_CRYPTO          = 0x02000000;
   const unsigned long STATE_CERT_USER_OVERRIDDEN      = 0x04000000;
 
   /**
-   * Cookie Jar blocking
+   * Content Blocking Event flags
    *
    * NOTE: IF YOU ARE ADDING MORE OF THESE FLAGS, MAKE SURE TO EDIT
    * nsSecureBrowserUIImpl::CheckForBlockedContent().
    *
    * These flags describe the reason of cookie jar rejection.
    *
+   * STATE_BLOCKED_TRACKING_CONTENT
+   *   Tracking content has been blocked from loading.
+   *
+   * STATE_LOADED_TRACKING_CONTENT
+   *   Tracking content has been loaded.
+   *
+   * STATE_BLOCKED_UNSAFE_CONTENT
+   *   Content which againts SafeBrowsing list has been blocked from loading.
+   *
    * STATE_COOKIES_BLOCKED_BY_PERMISSION
    *   Rejected for custom site permission.
    *
    * STATE_COOKIES_BLOCKED_TRACKER
    *   Rejected because the resource is a tracker and cookie policy doesn't
    *   allow its loading.
    *
    * STATE_COOKIES_BLOCKED_ALL
    *   Rejected because cookie policy blocks all cookies.
    *
    * STATE_COOKIES_BLOCKED_FOREIGN
    *   Rejected because cookie policy blocks 3rd party cookies.
    */
+  const unsigned long STATE_BLOCKED_TRACKING_CONTENT      = 0x00001000;
+  const unsigned long STATE_LOADED_TRACKING_CONTENT       = 0x00002000;
+  const unsigned long STATE_BLOCKED_UNSAFE_CONTENT        = 0x00004000;
   const unsigned long STATE_COOKIES_LOADED                = 0x00008000;
   const unsigned long STATE_COOKIES_BLOCKED_BY_PERMISSION = 0x10000000;
   const unsigned long STATE_COOKIES_BLOCKED_TRACKER       = 0x20000000;
   const unsigned long STATE_COOKIES_BLOCKED_ALL           = 0x40000000;
   const unsigned long STATE_COOKIES_BLOCKED_FOREIGN       = 0x00000080;
 
   /**
    * Notification indicating the state has changed for one of the requests
@@ -457,9 +447,26 @@ interface nsIWebProgressListener : nsISu
    *        future use.
    *
    * NOTE: These notifications will only occur if a security package is
    * installed.
    */
   void onSecurityChange(in nsIWebProgress aWebProgress,
                         in nsIRequest aRequest,
                         in unsigned long aState);
+
+  /**
+   * Notification called for content blocking events.  This method will be
+   * called when content gets allowed/blocked for various reasons per the
+   * Content Blocking rules.
+   *
+   * @param aWebProgress
+   *        The nsIWebProgress instance that fired the notification.
+   * @param aRequest
+   *        The nsIRequest that has new security state.
+   * @param aEvent
+   *        A value composed of the Content Blocking Event Flags listed above.
+   *        Any undefined bits are reserved for future use.
+   */
+  void onContentBlockingEvent(in nsIWebProgress aWebProgress,
+                              in nsIRequest aRequest,
+                              in unsigned long aEvent);
 };
--- a/uriloader/prefetch/nsOfflineCacheUpdateService.cpp
+++ b/uriloader/prefetch/nsOfflineCacheUpdateService.cpp
@@ -207,17 +207,24 @@ nsOfflineCachePendingUpdate::OnStatusCha
                                             const char16_t *aMessage) {
   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsOfflineCachePendingUpdate::OnSecurityChange(nsIWebProgress *aWebProgress,
                                               nsIRequest *aRequest,
-                                              uint32_t state) {
+                                              uint32_t aState) {
+  MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCachePendingUpdate::OnContentBlockingEvent(
+    nsIWebProgress *aWebProgress, nsIRequest *aRequest, uint32_t aEvent) {
   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsOfflineCacheUpdateService::nsISupports
 //-----------------------------------------------------------------------------
 
--- a/uriloader/prefetch/nsPrefetchService.cpp
+++ b/uriloader/prefetch/nsPrefetchService.cpp
@@ -914,17 +914,25 @@ nsPrefetchService::OnStatusChange(nsIWeb
                                   nsIRequest *aRequest, nsresult aStatus,
                                   const char16_t *aMessage) {
   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPrefetchService::OnSecurityChange(nsIWebProgress *aWebProgress,
-                                    nsIRequest *aRequest, uint32_t state) {
+                                    nsIRequest *aRequest, uint32_t aState) {
+  MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPrefetchService::OnContentBlockingEvent(nsIWebProgress *aWebProgress,
+                                          nsIRequest *aRequest,
+                                          uint32_t aEvent) {
   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsPrefetchService::nsIObserver
 //-----------------------------------------------------------------------------
 
--- a/xpfe/appshell/nsChromeTreeOwner.cpp
+++ b/xpfe/appshell/nsChromeTreeOwner.cpp
@@ -440,17 +440,24 @@ NS_IMETHODIMP
 nsChromeTreeOwner::OnStatusChange(nsIWebProgress* aWebProgress,
                                   nsIRequest* aRequest, nsresult aStatus,
                                   const char16_t* aMessage) {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsChromeTreeOwner::OnSecurityChange(nsIWebProgress* aWebProgress,
-                                    nsIRequest* aRequest, uint32_t state) {
+                                    nsIRequest* aRequest, uint32_t aState) {
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
+                                          nsIRequest* aRequest,
+                                          uint32_t aEvent) {
   return NS_OK;
 }
 
 //*****************************************************************************
 // nsChromeTreeOwner: Helpers
 //*****************************************************************************
 
 //*****************************************************************************
--- a/xpfe/appshell/nsWebShellWindow.cpp
+++ b/xpfe/appshell/nsWebShellWindow.cpp
@@ -628,17 +628,25 @@ nsWebShellWindow::OnStatusChange(nsIWebP
                                  nsIRequest* aRequest, nsresult aStatus,
                                  const char16_t* aMessage) {
   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWebShellWindow::OnSecurityChange(nsIWebProgress* aWebProgress,
-                                   nsIRequest* aRequest, uint32_t state) {
+                                   nsIRequest* aRequest, uint32_t aState) {
+  MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWebShellWindow::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
+                                         nsIRequest* aRequest,
+                                         uint32_t aEvent) {
   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
 
 /**
  * ExecuteCloseHandler - Run the close handler, if any.
  * @return true iff we found a close handler to run.
  */