merge mozilla-inbound to mozilla-central + UPGRADE_NSS_RELEASE a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 30 May 2017 11:37:46 +0200
changeset 409337 286f71223256cbb3a769432fd860f563c4886e81
parent 409296 0c712d76d598ec92a7adaaf7180fd4e559f9abc0 (current diff)
parent 409336 2a5ce0b6f2cd2d6567e307942990329697661ac1 (diff)
child 409338 9d24ed37fccaf8e43a7770f2cab390b56c4e0b0d
child 409365 5235f24cdc6bb4add5bfeeb8f7551aa355f3f9fb
child 409377 b5c3bb245c4e770eb128b0e2e803321d17e5239f
child 409416 07f07bc1e39a02f1d9b2e0381ca4935ded3ca13e
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly linux32
286f71223256 / 55.0a1 / 20170530100155 / files
nightly linux64
286f71223256 / 55.0a1 / 20170530100155 / files
nightly mac
286f71223256 / 55.0a1 / 20170530030204 / files
nightly win32
286f71223256 / 55.0a1 / 20170530030204 / files
nightly win64
286f71223256 / 55.0a1 / 20170530030204 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central + UPGRADE_NSS_RELEASE a=merge
devtools/client/jar.mn
dom/media/eme/MediaKeySystemAccess.cpp
gfx/thebes/gfxFont.cpp
layout/style/ServoBindingList.h
layout/style/ServoStyleSheet.cpp
toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
toolkit/components/extensions/test/xpcshell/xpcshell.ini
toolkit/components/telemetry/Histograms.json
--- a/browser/components/contextualidentity/test/browser/browser_eme.js
+++ b/browser/components/contextualidentity/test/browser/browser_eme.js
@@ -98,17 +98,17 @@ add_task(async function test() {
   // Generate the key info for the default container.
   let keyInfo = generateKeyInfo(TESTKEY);
 
   // Update the media key for the default container.
   let result = await ContentTask.spawn(defaultContainer.browser, keyInfo, async function(aKeyInfo) {
     let access = await content.navigator.requestMediaKeySystemAccess("org.w3.clearkey",
                                                                      [{
                                                                        initDataTypes: [aKeyInfo.initDataType],
-                                                                       videoCapabilities: [{contentType: 'video/webm; codecs="vp9"'}],
+                                                                       videoCapabilities: [{contentType: "video/webm"}],
                                                                        sessionTypes: ["persistent-license"],
                                                                        persistentState: "required",
                                                                      }]);
     let mediaKeys = await access.createMediaKeys();
     let session = mediaKeys.createSession(aKeyInfo.sessionType);
     let res = {};
 
     // Insert the media key.
@@ -151,17 +151,17 @@ add_task(async function test() {
 
   // Open a tab with personal container.
   let personalContainer = await openTabInUserContext(TEST_URL + "empty_file.html", USER_ID_PERSONAL);
 
   await ContentTask.spawn(personalContainer.browser, keyInfo, async function(aKeyInfo) {
     let access = await content.navigator.requestMediaKeySystemAccess("org.w3.clearkey",
                                                                      [{
                                                                        initDataTypes: [aKeyInfo.initDataType],
-                                                                       videoCapabilities: [{contentType: 'video/webm; codecs="vp9"'}],
+                                                                       videoCapabilities: [{contentType: "video/webm"}],
                                                                        sessionTypes: ["persistent-license"],
                                                                        persistentState: "required",
                                                                      }]);
     let mediaKeys = await access.createMediaKeys();
     let session = mediaKeys.createSession(aKeyInfo.sessionType);
 
     // First, load the session to check that mediakeys do not share with
     // default container.
--- a/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js
+++ b/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js
@@ -93,17 +93,17 @@ async function setupEMEKey(browser) {
   // Generate the key info.
   let keyInfo = generateKeyInfo(TEST_EME_KEY);
 
   // Setup the EME key.
   let result = await ContentTask.spawn(browser, keyInfo, async function(aKeyInfo) {
     let access = await content.navigator.requestMediaKeySystemAccess("org.w3.clearkey",
                                                                      [{
                                                                        initDataTypes: [aKeyInfo.initDataType],
-                                                                       videoCapabilities: [{contentType: 'video/webm; codecs="vp9"'}],
+                                                                       videoCapabilities: [{contentType: "video/webm"}],
                                                                        sessionTypes: ["persistent-license"],
                                                                        persistentState: "required",
                                                                      }]);
     let mediaKeys = await access.createMediaKeys();
     let session = mediaKeys.createSession(aKeyInfo.sessionType);
     let res = {};
 
     // Insert the EME key.
@@ -148,17 +148,17 @@ async function checkEMEKey(browser, emeS
   // Generate the key info.
   let keyInfo = generateKeyInfo(TEST_EME_KEY);
   keyInfo.sessionId = emeSessionId;
 
   await ContentTask.spawn(browser, keyInfo, async function(aKeyInfo) {
     let access = await content.navigator.requestMediaKeySystemAccess("org.w3.clearkey",
                                                                      [{
                                                                        initDataTypes: [aKeyInfo.initDataType],
-                                                                       videoCapabilities: [{contentType: 'video/webm; codecs="vp9"'}],
+                                                                       videoCapabilities: [{contentType: "video/webm"}],
                                                                        sessionTypes: ["persistent-license"],
                                                                        persistentState: "required",
                                                                      }]);
     let mediaKeys = await access.createMediaKeys();
     let session = mediaKeys.createSession(aKeyInfo.sessionType);
 
     // First, load the session with the sessionId.
     await session.load(aKeyInfo.sessionId);
--- a/browser/components/extensions/moz.build
+++ b/browser/components/extensions/moz.build
@@ -22,9 +22,17 @@ EXTRA_JS_MODULES += [
 DIRS += ['schemas']
 
 BROWSER_CHROME_MANIFESTS += [
     'test/browser/browser-remote.ini',
     'test/browser/browser.ini',
 ]
 
 MOCHITEST_MANIFESTS += ['test/mochitest/mochitest.ini']
-XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini']
+
+if CONFIG['OS_ARCH'] != 'Darwin':
+    XPCSHELL_TESTS_MANIFESTS += [
+        'test/xpcshell/xpcshell-remote.ini',
+    ]
+
+XPCSHELL_TESTS_MANIFESTS += [
+    'test/xpcshell/xpcshell.ini',
+]
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -32,16 +32,17 @@ support-files =
 [browser_ext_browserAction_area.js]
 [browser_ext_browserAction_context.js]
 [browser_ext_browserAction_contextMenu.js]
 [browser_ext_browserAction_disabled.js]
 [browser_ext_browserAction_pageAction_icon.js]
 [browser_ext_browserAction_pageAction_icon_permissions.js]
 [browser_ext_browserAction_popup.js]
 [browser_ext_browserAction_popup_preload.js]
+skip-if = (os == 'win' && !debug) # bug 1352668
 [browser_ext_browserAction_popup_resize.js]
 [browser_ext_browserAction_simple.js]
 [browser_ext_browsingData_formData.js]
 [browser_ext_browsingData_history.js]
 [browser_ext_browsingData_pluginData.js]
 [browser_ext_browsingData_serviceWorkers.js]
 [browser_ext_commands_execute_browser_action.js]
 [browser_ext_commands_execute_page_action.js]
--- a/browser/components/extensions/test/browser/head.js
+++ b/browser/components/extensions/test/browser/head.js
@@ -36,16 +36,17 @@ const {CustomizableUI} = Cu.import("reso
 
 // We run tests under two different configurations, from browser.ini and
 // browser-remote.ini. When running from browser-remote.ini, the tests are
 // copied to the sub-directory "test-oop-extensions", which we detect here, and
 // use to select our configuration.
 if (gTestPath.includes("test-oop-extensions")) {
   SpecialPowers.pushPrefEnv({set: [
     ["extensions.webextensions.remote", true],
+    ["layers.popups.compositing.enabled", true],
   ]});
   // We don't want to reset this at the end of the test, so that we don't have
   // to spawn a new extension child process for each test unit.
   SpecialPowers.setIntPref("dom.ipc.keepProcessesAlive.extension", 1);
 }
 
 // Bug 1239884: Our tests occasionally hit a long GC pause at unpredictable
 // times in debug builds, which results in intermittent timeouts. Until we have
--- a/browser/components/extensions/test/xpcshell/head.js
+++ b/browser/components/extensions/test/xpcshell/head.js
@@ -1,14 +1,15 @@
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 /* exported createHttpServer, promiseConsoleOutput  */
 
+Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
                                   "resource://gre/modules/AppConstants.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Extension",
                                   "resource://gre/modules/Extension.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ExtensionData",
                                   "resource://gre/modules/Extension.jsm");
@@ -19,21 +20,21 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "HttpServer",
                                   "resource://testing-common/httpd.js");
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
                                   "resource://gre/modules/Schemas.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Services",
-                                  "resource://gre/modules/Services.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TestUtils",
                                   "resource://testing-common/TestUtils.jsm");
 
+Services.prefs.setBoolPref("extensions.webextensions.remote", false);
+
 ExtensionTestUtils.init(this);
 
 
 /**
  * Creates a new HttpServer for testing, and begins listening on the
  * specified port. Automatically shuts down the server when the test
  * unit ends.
  *
copy from toolkit/components/extensions/test/xpcshell/head_remote.js
copy to browser/components/extensions/test/xpcshell/head_remote.js
--- a/toolkit/components/extensions/test/xpcshell/head_remote.js
+++ b/browser/components/extensions/test/xpcshell/head_remote.js
@@ -1,5 +1,4 @@
 "use strict";
 
-/* globals ExtensionTestUtils */
-
-ExtensionTestUtils.remoteContentScripts = true;
+Services.prefs.setBoolPref("extensions.webextensions.remote", true);
+Services.prefs.setIntPref("dom.ipc.keepProcessesAlive.extension", 1);
copy from browser/components/extensions/test/xpcshell/xpcshell.ini
copy to browser/components/extensions/test/xpcshell/xpcshell-common.ini
--- a/browser/components/extensions/test/xpcshell/xpcshell.ini
+++ b/browser/components/extensions/test/xpcshell/xpcshell-common.ini
@@ -1,16 +1,8 @@
-[DEFAULT]
-head = head.js
-firefox-appdir = browser
-tags = webextensions
-
 [test_ext_bookmarks.js]
 [test_ext_browsingData.js]
 [test_ext_browsingData_cookies_cache.js]
 [test_ext_browsingData_downloads.js]
 [test_ext_browsingData_passwords.js]
 [test_ext_browsingData_settings.js]
 [test_ext_history.js]
-[test_ext_manifest_commands.js]
-[test_ext_manifest_omnibox.js]
-[test_ext_manifest_permissions.js]
 [test_ext_geckoProfiler_control.js]
copy from browser/components/extensions/test/xpcshell/xpcshell.ini
copy to browser/components/extensions/test/xpcshell/xpcshell-remote.ini
--- a/browser/components/extensions/test/xpcshell/xpcshell.ini
+++ b/browser/components/extensions/test/xpcshell/xpcshell-remote.ini
@@ -1,16 +1,7 @@
 [DEFAULT]
-head = head.js
+head = head.js head_remote.js
 firefox-appdir = browser
-tags = webextensions
+tags = webextensions remote-webextensions
+dupe-manifest =
 
-[test_ext_bookmarks.js]
-[test_ext_browsingData.js]
-[test_ext_browsingData_cookies_cache.js]
-[test_ext_browsingData_downloads.js]
-[test_ext_browsingData_passwords.js]
-[test_ext_browsingData_settings.js]
-[test_ext_history.js]
-[test_ext_manifest_commands.js]
-[test_ext_manifest_omnibox.js]
-[test_ext_manifest_permissions.js]
-[test_ext_geckoProfiler_control.js]
+[include:xpcshell-common.ini]
--- a/browser/components/extensions/test/xpcshell/xpcshell.ini
+++ b/browser/components/extensions/test/xpcshell/xpcshell.ini
@@ -1,16 +1,23 @@
 [DEFAULT]
 head = head.js
 firefox-appdir = browser
-tags = webextensions
+tags = webextensions in-process-webextensions
+dupe-manifest =
 
-[test_ext_bookmarks.js]
-[test_ext_browsingData.js]
-[test_ext_browsingData_cookies_cache.js]
-[test_ext_browsingData_downloads.js]
-[test_ext_browsingData_passwords.js]
-[test_ext_browsingData_settings.js]
-[test_ext_history.js]
+# This file contains tests which are not affected by multi-process
+# configuration, or do not support out-of-process content or extensions
+# for one reason or another.
+#
+# Tests which are affected by remote content or remote extensions should
+# go in one of:
+#
+#  - xpcshell-common.ini
+#    For tests which should run in all configurations.
+#  - xpcshell-remote.ini
+#    For tests which should only run with both remote extensions and remote content.
+
 [test_ext_manifest_commands.js]
 [test_ext_manifest_omnibox.js]
 [test_ext_manifest_permissions.js]
-[test_ext_geckoProfiler_control.js]
+
+[include:xpcshell-common.ini]
--- a/devtools/client/inspector/boxmodel/box-model.js
+++ b/devtools/client/inspector/boxmodel/box-model.js
@@ -122,41 +122,45 @@ BoxModel.prototype = {
    */
   updateBoxModel(reason) {
     this._updateReasons = this._updateReasons || [];
     if (reason) {
       this._updateReasons.push(reason);
     }
 
     let lastRequest = Task.spawn((function* () {
-      if (!(this.isPanelVisible() &&
-          this.inspector.selection.isConnected() &&
-          this.inspector.selection.isElementNode())) {
+      if (!this.inspector ||
+          !this.isPanelVisible() ||
+          !this.inspector.selection.isConnected() ||
+          !this.inspector.selection.isElementNode()) {
         return null;
       }
 
       let node = this.inspector.selection.nodeFront;
+
       let layout = yield this.inspector.pageStyle.getLayout(node, {
         autoMargins: true,
       });
+
       let styleEntries = yield this.inspector.pageStyle.getApplied(node, {
         // We don't need styles applied to pseudo elements of the current node.
         skipPseudo: true
       });
       this.elementRules = styleEntries.map(e => e.rule);
 
       // Update the layout properties with whether or not the element's position is
       // editable with the geometry editor.
       let isPositionEditable = yield this.inspector.pageStyle.isPositionEditable(node);
+
       layout = Object.assign({}, layout, {
         isPositionEditable,
       });
 
-      const actorCanGetOffSetParent
-        = yield this.inspector.target.actorHasMethod("domwalker", "getOffsetParent");
+      const actorCanGetOffSetParent =
+        yield this.inspector.target.actorHasMethod("domwalker", "getOffsetParent");
 
       if (actorCanGetOffSetParent) {
         // Update the redux store with the latest offset parent DOM node
         let offsetParent = yield this.inspector.walker.getOffsetParent(node);
         this.store.dispatch(updateOffsetParent(offsetParent));
       }
 
       // Update the redux store with the latest layout properties and update the box
--- a/devtools/client/inspector/grids/components/GridDisplaySettings.js
+++ b/devtools/client/inspector/grids/components/GridDisplaySettings.js
@@ -58,32 +58,34 @@ module.exports = createClass({
         dom.li(
           {
             className: "grid-settings-item",
           },
           dom.label(
             {},
             dom.input(
               {
+                id: "grid-setting-extend-grid-lines",
                 type: "checkbox",
                 checked: highlighterSettings.showInfiniteLines,
                 onChange: this.onShowInfiniteLinesCheckboxClick,
               }
             ),
             getStr("layout.extendGridLinesInfinitely")
           )
         ),
         dom.li(
           {
             className: "grid-settings-item",
           },
           dom.label(
             {},
             dom.input(
               {
+                id: "grid-setting-show-grid-line-numbers",
                 type: "checkbox",
                 checked: highlighterSettings.showGridLineNumbers,
                 onChange: this.onShowGridLineNumbersCheckboxClick,
               }
             ),
             getStr("layout.displayNumbersOnLines")
           )
         )
--- a/devtools/client/inspector/grids/components/GridList.js
+++ b/devtools/client/inspector/grids/components/GridList.js
@@ -43,17 +43,19 @@ module.exports = createClass({
       {
         className: "grid-container",
       },
       dom.span(
         {},
         getStr("layout.overlayGrid")
       ),
       dom.ul(
-        {},
+        {
+          id: "grid-list",
+        },
         grids.map(grid => GridItem({
           key: grid.id,
           getSwatchColorPickerTooltip,
           grid,
           setSelectedNode,
           onHideBoxModelHighlighter,
           onSetGridOverlayColor,
           onShowBoxModelHighlighterForNode,
--- a/devtools/client/inspector/grids/grid-inspector.js
+++ b/devtools/client/inspector/grids/grid-inspector.js
@@ -248,23 +248,37 @@ GridInspector.prototype = {
   updateGridPanel: Task.async(function* (gridFronts) {
     // Stop refreshing if the inspector or store is already destroyed.
     if (!this.inspector || !this.store) {
       return;
     }
 
     // Get all the GridFront from the server if no gridFronts were provided.
     if (!gridFronts) {
-      gridFronts = yield this.layoutInspector.getAllGrids(this.walker.rootNode);
+      try {
+        gridFronts = yield this.layoutInspector.getAllGrids(this.walker.rootNode);
+      } catch (e) {
+        // This call might fail if called asynchrously after the toolbox is finished
+        // closing.
+        return;
+      }
     }
 
     let grids = [];
     for (let i = 0; i < gridFronts.length; i++) {
       let grid = gridFronts[i];
-      let nodeFront = yield this.walker.getNodeFromActor(grid.actorID, ["containerEl"]);
+
+      let nodeFront;
+      try {
+        nodeFront = yield this.walker.getNodeFromActor(grid.actorID, ["containerEl"]);
+      } catch (e) {
+        // This call might fail if called asynchrously after the toolbox is finished
+        // closing.
+        return;
+      }
 
       let fallbackColor = GRID_COLORS[i % GRID_COLORS.length];
       let color = this.getInitialGridColor(nodeFront, fallbackColor);
 
       grids.push({
         id: i,
         color,
         gridFragments: grid.gridFragments,
@@ -295,17 +309,17 @@ GridInspector.prototype = {
    * @param  {Event} event
    *         Event that was triggered.
    * @param  {NodeFront} nodeFront
    *         The NodeFront of the grid container element for which the grid highlighter
    *         is shown for.
    * @param  {Object} options
    *         The highlighter options used for the highlighter being shown/hidden.
    */
-  onHighlighterChange(event, nodeFront, options) {
+  onHighlighterChange(event, nodeFront, options = {}) {
     let highlighted = event === "grid-highlighter-shown";
     let { color } = options;
 
     // Only tell the store that the highlighter changed if it did change.
     // If we're still highlighting the same node, with the same color, no need to force
     // a refresh.
     if (this.lastHighlighterState !== highlighted ||
         this.lastHighlighterNode !== nodeFront) {
--- a/devtools/client/inspector/grids/moz.build
+++ b/devtools/client/inspector/grids/moz.build
@@ -10,8 +10,10 @@ DIRS += [
     'reducers',
     'utils',
 ]
 
 DevToolsModules(
     'grid-inspector.js',
     'types.js',
 )
+
+BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
--- a/devtools/client/inspector/grids/reducers/grids.js
+++ b/devtools/client/inspector/grids/reducers/grids.js
@@ -12,17 +12,17 @@ const {
 
 const INITIAL_GRIDS = [];
 
 let reducers = {
 
   [UPDATE_GRID_COLOR](grids, { nodeFront, color }) {
     let newGrids = grids.map(g => {
       if (g.nodeFront == nodeFront) {
-        g.color = color;
+        g = Object.assign({}, g, { color });
       }
 
       return g;
     });
 
     return newGrids;
   },
 
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/test/.eslintrc.js
@@ -0,0 +1,9 @@
+"use strict";
+
+module.exports = {
+  // Extend from the shared list of defined globals for mochitests.
+  "extends": "../../../../.eslintrc.mochitests.js",
+  "globals": {
+    "waitUntilState": true
+  }
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/test/browser.ini
@@ -0,0 +1,23 @@
+[DEFAULT]
+tags = devtools
+subsuite = devtools
+support-files =
+  head.js
+  !/devtools/client/commandline/test/helpers.js
+  !/devtools/client/framework/test/shared-head.js
+  !/devtools/client/inspector/test/head.js
+  !/devtools/client/inspector/test/shared-head.js
+  !/devtools/client/shared/test/test-actor.js
+  !/devtools/client/shared/test/test-actor-registry.js
+  !/devtools/client/framework/test/shared-redux-head.js
+
+[browser_grids_display-setting-extend-grid-lines.js]
+[browser_grids_display-setting-show-grid-line-numbers.js]
+[browser_grids_grid-list-color-picker-on-ESC.js]
+[browser_grids_grid-list-color-picker-on-RETURN.js]
+[browser_grids_grid-list-element-rep.js]
+[browser_grids_grid-list-no-grids.js]
+[browser_grids_grid-list-on-mutation-element-added.js]
+[browser_grids_grid-list-on-mutation-element-removed.js]
+[browser_grids_grid-list-toggle-multiple-grids.js]
+[browser_grids_grid-list-toggle-single-grid.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/test/browser_grids_display-setting-extend-grid-lines.js
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the 'Extend grid lines infinitely' grid highlighter setting will update
+// the redux store and pref setting.
+
+const TEST_URI = `
+  <style type='text/css'>
+    #grid {
+      display: grid;
+    }
+  </style>
+  <div id="grid">
+    <div id="cell1">cell1</div>
+    <div id="cell2">cell2</div>
+  </div>
+`;
+
+const SHOW_INFINITE_LINES_PREF = "devtools.gridinspector.showInfiniteLines";
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let { inspector, gridInspector } = yield openLayoutView();
+  let { document: doc } = gridInspector;
+  let { store } = inspector;
+
+  yield selectNode("#grid", inspector);
+  let checkbox = doc.getElementById("grid-setting-extend-grid-lines");
+
+  ok(!Services.prefs.getBoolPref(SHOW_INFINITE_LINES_PREF),
+    "'Extend grid lines infinitely' is pref off by default.");
+
+  info("Toggling ON the 'Extend grid lines infinitely' setting.");
+  let onCheckboxChange = waitUntilState(store, state =>
+    state.highlighterSettings.showInfiniteLines);
+  checkbox.click();
+  yield onCheckboxChange;
+
+  info("Toggling OFF the 'Extend grid lines infinitely' setting.");
+  onCheckboxChange = waitUntilState(store, state =>
+    !state.highlighterSettings.showInfiniteLines);
+  checkbox.click();
+  yield onCheckboxChange;
+
+  ok(!Services.prefs.getBoolPref(SHOW_INFINITE_LINES_PREF),
+    "'Extend grid lines infinitely' is pref off.");
+
+  Services.prefs.clearUserPref(SHOW_INFINITE_LINES_PREF);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/test/browser_grids_display-setting-show-grid-line-numbers.js
@@ -0,0 +1,55 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the 'Display numbers on lines' grid highlighter setting will update
+// the redux store and pref setting.
+
+const TEST_URI = `
+  <style type='text/css'>
+    #grid {
+      display: grid;
+    }
+  </style>
+  <div id="grid">
+    <div id="cell1">cell1</div>
+    <div id="cell2">cell2</div>
+  </div>
+`;
+
+const SHOW_GRID_LINE_NUMBERS = "devtools.gridinspector.showGridLineNumbers";
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let { inspector, gridInspector } = yield openLayoutView();
+  let { document: doc } = gridInspector;
+  let { store } = inspector;
+
+  yield selectNode("#grid", inspector);
+  let checkbox = doc.getElementById("grid-setting-show-grid-line-numbers");
+
+  info("Checking the initial state of the CSS grid highlighter setting.");
+  ok(!Services.prefs.getBoolPref(SHOW_GRID_LINE_NUMBERS),
+    "'Display numbers on lines' is pref off by default.");
+
+  info("Toggling ON the 'Display numbers on lines' setting.");
+  let onCheckboxChange = waitUntilState(store, state =>
+    state.highlighterSettings.showGridLineNumbers);
+  checkbox.click();
+  yield onCheckboxChange;
+
+  ok(Services.prefs.getBoolPref(SHOW_GRID_LINE_NUMBERS),
+    "'Display numbers on lines' is pref on.");
+
+  info("Toggling OFF the 'Display numbers on lines' setting.");
+  onCheckboxChange = waitUntilState(store, state =>
+    !state.highlighterSettings.showGridLineNumbers);
+  checkbox.click();
+  yield onCheckboxChange;
+
+  ok(!Services.prefs.getBoolPref(SHOW_GRID_LINE_NUMBERS),
+    "'Display numbers on lines' is pref off.");
+
+  Services.prefs.clearUserPref(SHOW_GRID_LINE_NUMBERS);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-color-picker-on-ESC.js
@@ -0,0 +1,58 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the grid item's color change in the colorpicker is reverted when ESCAPE is
+// pressed.
+
+const TEST_URI = `
+  <style type='text/css'>
+    #grid {
+      display: grid;
+    }
+  </style>
+  <div id="grid">
+    <div id="cell1">cell1</div>
+    <div id="cell2">cell2</div>
+  </div>
+`;
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let { inspector, gridInspector } = yield openLayoutView();
+  let { document: doc } = gridInspector;
+  let { store } = inspector;
+  let cPicker = gridInspector.getSwatchColorPickerTooltip();
+  let spectrum = cPicker.spectrum;
+  let swatch = doc.querySelector(".grid-color-swatch");
+
+  info("Checking the initial state of the Grid Inspector.");
+  is(swatch.style.backgroundColor, "rgb(75, 0, 130)",
+    "The color swatch's background is correct.");
+  is(store.getState().grids[0].color, "#4B0082", "The grid color state is correct.");
+
+  info("Scrolling into view of the #grid color swatch.");
+  swatch.scrollIntoView();
+
+  info("Opening the color picker by clicking on the #grid color swatch.");
+  let onColorPickerReady = cPicker.once("ready");
+  swatch.click();
+  yield onColorPickerReady;
+
+  yield simulateColorPickerChange(cPicker, [0, 255, 0, .5]);
+
+  is(swatch.style.backgroundColor, "rgba(0, 255, 0, 0.5)",
+    "The color swatch's background was updated.");
+
+  info("Pressing ESCAPE to close the tooltip.");
+  let onGridColorUpdate = waitUntilState(store, state =>
+    state.grids[0].color === "#4B0082");
+  let onColorPickerHidden = cPicker.tooltip.once("hidden");
+  focusAndSendKey(spectrum.element.ownerDocument.defaultView, "ESCAPE");
+  yield onColorPickerHidden;
+  yield onGridColorUpdate;
+
+  is(swatch.style.backgroundColor, "rgb(75, 0, 130)",
+    "The color swatch's background was reverted after ESCAPE.");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-color-picker-on-RETURN.js
@@ -0,0 +1,58 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the grid item's color change in the colorpicker is committed when RETURN is
+// pressed.
+
+const TEST_URI = `
+  <style type='text/css'>
+    #grid {
+      display: grid;
+    }
+  </style>
+  <div id="grid">
+    <div id="cell1">cell1</div>
+    <div id="cell2">cell2</div>
+  </div>
+`;
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let { inspector, gridInspector } = yield openLayoutView();
+  let { document: doc } = gridInspector;
+  let { store } = inspector;
+  let cPicker = gridInspector.getSwatchColorPickerTooltip();
+  let spectrum = cPicker.spectrum;
+  let swatch = doc.querySelector(".grid-color-swatch");
+
+  info("Checking the initial state of the Grid Inspector.");
+  is(swatch.style.backgroundColor, "rgb(75, 0, 130)",
+    "The color swatch's background is correct.");
+  is(store.getState().grids[0].color, "#4B0082", "The grid color state is correct.");
+
+  info("Scrolling into view of the #grid color swatch.");
+  swatch.scrollIntoView();
+
+  info("Opening the color picker by clicking on the #grid color swatch.");
+  let onColorPickerReady = cPicker.once("ready");
+  swatch.click();
+  yield onColorPickerReady;
+
+  yield simulateColorPickerChange(cPicker, [0, 255, 0, .5]);
+
+  is(swatch.style.backgroundColor, "rgba(0, 255, 0, 0.5)",
+    "The color swatch's background was updated.");
+
+  info("Pressing RETURN to commit the color change.");
+  let onGridColorUpdate = waitUntilState(store, state =>
+    state.grids[0].color === "#00FF0080");
+  let onColorPickerHidden = cPicker.tooltip.once("hidden");
+  focusAndSendKey(spectrum.element.ownerDocument.defaultView, "RETURN");
+  yield onColorPickerHidden;
+  yield onGridColorUpdate;
+
+  is(swatch.style.backgroundColor, "rgba(0, 255, 0, 0.5)",
+    "The color swatch's background was kept after RETURN.");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-element-rep.js
@@ -0,0 +1,49 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the grid item's element rep will display the box model higlighter on hover
+// and select the node on click.
+
+const TEST_URI = `
+  <style type='text/css'>
+    #grid {
+      display: grid;
+    }
+  </style>
+  <div id="grid">
+    <div id="cell1">cell1</div>
+    <div id="cell2">cell2</div>
+  </div>
+`;
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let { inspector, gridInspector, toolbox } = yield openLayoutView();
+  let { document: doc } = gridInspector;
+  let { store } = inspector;
+
+  let gridList = doc.querySelector("#grid-list");
+  let elementRep = gridList.children[0].querySelector(".open-inspector");
+  info("Scrolling into the view the #grid element node rep.");
+  elementRep.scrollIntoView();
+
+  info("Listen to node-highlight event and mouse over the widget");
+  let onHighlight = toolbox.once("node-highlight");
+  EventUtils.synthesizeMouse(elementRep, 10, 5, {type: "mouseover"}, doc.defaultView);
+  let nodeFront = yield onHighlight;
+
+  ok(nodeFront, "nodeFront was returned from highlighting the node.");
+  is(nodeFront.tagName, "DIV", "The highlighted node has the correct tagName.");
+  is(nodeFront.attributes[0].name, "id",
+    "The highlighted node has the correct attributes.");
+  is(nodeFront.attributes[0].value, "grid", "The highlighted node has the correct id.");
+
+  let onSelection = inspector.selection.once("new-node-front");
+  EventUtils.sendMouseEvent({type: "click"}, elementRep, doc.defaultView);
+  yield onSelection;
+
+  is(inspector.selection.nodeFront, store.getState().grids[0].nodeFront,
+    "The selected node is the one stored on the grid item's state.");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-no-grids.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that no grid list items and a "no grids available" message is displayed when
+// there are no grid containers on the page.
+
+const TEST_URI = `
+  <style type='text/css'>
+  </style>
+  <div id="grid">
+    <div id="cell1">cell1</div>
+    <div id="cell2">cell2</div>
+  </div>
+`;
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let { inspector, gridInspector } = yield openLayoutView();
+  let { document: doc } = gridInspector;
+  let { highlighters } = inspector;
+
+  yield selectNode("#grid", inspector);
+  let noGridList = doc.querySelector(".layout-no-grids");
+  let gridList = doc.querySelector("#grid-list");
+
+  info("Checking the initial state of the Grid Inspector.");
+  ok(noGridList, "The message no grid containers is displayed.");
+  ok(!gridList, "No grid containers are listed.");
+  ok(!highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "No CSS grid highlighter exists in the highlighters overlay.");
+  ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown.");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-on-mutation-element-added.js
@@ -0,0 +1,93 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the grid list updates when a new grid container is added to the page.
+
+const TEST_URI = `
+  <style type='text/css'>
+    .grid {
+      display: grid;
+    }
+  </style>
+  <div id="grid1" class="grid">
+    <div class="cell1">cell1</div>
+    <div class="cell2">cell2</div>
+  </div>
+  <div id="grid2">
+    <div class="cell1">cell1</div>
+    <div class="cell2">cell2</div>
+  </div>
+`;
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let { inspector, gridInspector, testActor } = yield openLayoutView();
+  let { document: doc } = gridInspector;
+  let { highlighters, store } = inspector;
+
+  yield selectNode("#grid", inspector);
+  let gridList = doc.querySelector("#grid-list");
+  let checkbox1 = gridList.children[0].querySelector("input");
+
+  info("Checking the initial state of the Grid Inspector.");
+  is(gridList.childNodes.length, 1, "One grid container is listed.");
+  ok(!highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "No CSS grid highlighter exists in the highlighters overlay.");
+  ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown.");
+
+  info("Toggling ON the CSS grid highlighter from the layout panel.");
+  let onHighlighterShown = highlighters.once("grid-highlighter-shown");
+  checkbox1.click();
+  yield onHighlighterShown;
+
+  info("Checking the CSS grid highlighter is created.");
+  ok(highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "CSS grid highlighter is created in the highlighters overlay.");
+  ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown.");
+
+  info("Adding the #grid2 container in the content page.");
+  let onGridListUpdate = waitUntilState(store, state =>
+    state.grids.length == 2 &&
+    state.grids[0].highlighted &&
+    !state.grids[1].highlighted);
+  testActor.eval(`
+    content.document.getElementById("grid2").classList.add("grid");
+  `);
+  yield onGridListUpdate;
+
+  info("Checking the new Grid Inspector state.");
+  is(gridList.childNodes.length, 2, "Two grid containers are listed.");
+  ok(highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "CSS grid highlighter is created in the highlighters overlay.");
+  ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown.");
+
+  let checkbox2 = gridList.children[1].querySelector("input");
+
+  info("Toggling ON the CSS grid highlighter for #grid2.");
+  onHighlighterShown = highlighters.once("grid-highlighter-shown");
+  let onCheckboxChange = waitUntilState(store, state =>
+    state.grids.length == 2 &&
+    !state.grids[0].highlighted &&
+    state.grids[1].highlighted);
+  checkbox2.click();
+  yield onHighlighterShown;
+  yield onCheckboxChange;
+
+  info("Checking the CSS grid highlighter is still shown.");
+  ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown.");
+
+  info("Toggling OFF the CSS grid highlighter from the layout panel.");
+  let onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
+  onCheckboxChange = waitUntilState(store, state =>
+    state.grids.length == 2 &&
+    !state.grids[0].highlighted &&
+    !state.grids[1].highlighted);
+  checkbox2.click();
+  yield onHighlighterHidden;
+  yield onCheckboxChange;
+
+  info("Checking the CSS grid highlighter is not shown.");
+  ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown.");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-on-mutation-element-removed.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the grid item is removed from the grid list when the grid container is
+// removed from the page.
+
+const TEST_URI = `
+  <style type='text/css'>
+    #grid {
+      display: grid;
+    }
+  </style>
+  <div id="grid">
+    <div id="cell1">cell1</div>
+    <div id="cell2">cell2</div>
+  </div>
+`;
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let { inspector, gridInspector, testActor } = yield openLayoutView();
+  let { document: doc } = gridInspector;
+  let { highlighters, store } = inspector;
+
+  yield selectNode("#grid", inspector);
+  let gridList = doc.querySelector("#grid-list");
+  let checkbox = gridList.children[0].querySelector("input");
+
+  info("Checking the initial state of the Grid Inspector.");
+  is(gridList.childNodes.length, 1, "One grid container is listed.");
+  ok(!highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "No CSS grid highlighter exists in the highlighters overlay.");
+  ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown.");
+
+  info("Toggling ON the CSS grid highlighter from the layout panel.");
+  let onHighlighterShown = highlighters.once("grid-highlighter-shown");
+  let onCheckboxChange = waitUntilState(store, state =>
+    state.grids.length == 1 && state.grids[0].highlighted);
+  checkbox.click();
+  yield onHighlighterShown;
+  yield onCheckboxChange;
+
+  info("Checking the CSS grid highlighter is created.");
+  ok(highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "CSS grid highlighter is created in the highlighters overlay.");
+  ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown.");
+
+  info("Removing the #grid container in the content page.");
+  let onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
+  onCheckboxChange = waitUntilState(store, state => state.grids.length == 0);
+  testActor.eval(`
+    content.document.getElementById("grid").remove();
+  `);
+  yield onHighlighterHidden;
+  yield onCheckboxChange;
+
+  info("Checking the CSS grid highlighter is not shown.");
+  ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown.");
+  let noGridList = doc.querySelector(".layout-no-grids");
+  ok(noGridList, "The message no grid containers is displayed.");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-multiple-grids.js
@@ -0,0 +1,84 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test toggling the grid highlighter in the grid inspector panel with multiple grids in
+// the page.
+
+const TEST_URI = `
+  <style type='text/css'>
+    .grid {
+      display: grid;
+    }
+  </style>
+  <div id="grid1" class="grid">
+    <div class="cell1">cell1</div>
+    <div class="cell2">cell2</div>
+  </div>
+  <div id="grid2" class="grid">
+    <div class="cell1">cell1</div>
+    <div class="cell2">cell2</div>
+  </div>
+`;
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let { inspector, gridInspector } = yield openLayoutView();
+  let { document: doc } = gridInspector;
+  let { highlighters, store } = inspector;
+
+  yield selectNode("#grid1", inspector);
+  let gridList = doc.querySelector("#grid-list");
+  let checkbox1 = gridList.children[0].querySelector("input");
+  let checkbox2 = gridList.children[1].querySelector("input");
+
+  info("Checking the initial state of the Grid Inspector.");
+  is(gridList.childNodes.length, 2, "2 grid containers are listed.");
+  ok(!checkbox1.checked, `Grid item ${checkbox1.value} is unchecked in the grid list.`);
+  ok(!checkbox2.checked, `Grid item ${checkbox2.value} is unchecked in the grid list.`);
+  ok(!highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "No CSS grid highlighter exists in the highlighters overlay.");
+  ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown.");
+
+  info("Toggling ON the CSS grid highlighter for #grid1.");
+  let onHighlighterShown = highlighters.once("grid-highlighter-shown");
+  let onCheckboxChange = waitUntilState(store, state =>
+    state.grids.length == 2 &&
+    state.grids[0].highlighted &&
+    !state.grids[1].highlighted);
+  checkbox1.click();
+  yield onHighlighterShown;
+  yield onCheckboxChange;
+
+  info("Checking the CSS grid highlighter is created.");
+  ok(highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "CSS grid highlighter is created in the highlighters overlay.");
+  ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown.");
+
+  info("Toggling ON the CSS grid highlighter for #grid2.");
+  onHighlighterShown = highlighters.once("grid-highlighter-shown");
+  onCheckboxChange = waitUntilState(store, state =>
+    state.grids.length == 2 &&
+    !state.grids[0].highlighted &&
+    state.grids[1].highlighted);
+  checkbox2.click();
+  yield onHighlighterShown;
+  yield onCheckboxChange;
+
+  info("Checking the CSS grid highlighter is still shown.");
+  ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown.");
+
+  info("Toggling OFF the CSS grid highlighter from the layout panel.");
+  let onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
+  onCheckboxChange = waitUntilState(store, state =>
+    state.grids.length == 2 &&
+    !state.grids[0].highlighted &&
+    !state.grids[1].highlighted);
+  checkbox2.click();
+  yield onHighlighterHidden;
+  yield onCheckboxChange;
+
+  info("Checking the CSS grid highlighter is not shown.");
+  ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown.");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-single-grid.js
@@ -0,0 +1,62 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests toggling ON/OFF the grid highlighter from the grid ispector panel.
+
+const TEST_URI = `
+  <style type='text/css'>
+    #grid {
+      display: grid;
+    }
+  </style>
+  <div id="grid">
+    <div id="cell1">cell1</div>
+    <div id="cell2">cell2</div>
+  </div>
+`;
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let { gridInspector, inspector } = yield openLayoutView();
+  let { document: doc } = gridInspector;
+  let { highlighters, store } = inspector;
+
+  yield selectNode("#grid", inspector);
+  let gridList = doc.querySelector("#grid-list");
+  let checkbox = gridList.children[0].querySelector("input");
+
+  info("Checking the initial state of the Grid Inspector.");
+  is(gridList.childNodes.length, 1, "One grid container is listed.");
+  ok(!checkbox.checked, `Grid item ${checkbox.value} is unchecked in the grid list.`);
+  ok(!highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "No CSS grid highlighter exists in the highlighters overlay.");
+  ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown.");
+
+  info("Toggling ON the CSS grid highlighter from the layout panel.");
+  let onHighlighterShown = highlighters.once("grid-highlighter-shown");
+  let onCheckboxChange = waitUntilState(store, state =>
+    state.grids.length == 1 &&
+    state.grids[0].highlighted);
+  checkbox.click();
+  yield onHighlighterShown;
+  yield onCheckboxChange;
+
+  info("Checking the CSS grid highlighter is created.");
+  ok(highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "CSS grid highlighter is created in the highlighters overlay.");
+  ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown.");
+
+  info("Toggling OFF the CSS grid highlighter from the layout panel.");
+  let onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
+  onCheckboxChange = waitUntilState(store, state =>
+    state.grids.length == 1 &&
+    !state.grids[0].highlighted);
+  checkbox.click();
+  yield onHighlighterHidden;
+  yield onCheckboxChange;
+
+  info("Checking the CSS grid highlighter is not shown.");
+  ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown.");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/test/head.js
@@ -0,0 +1,74 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
+/* import-globals-from ../../../framework/test/shared-head.js */
+/* import-globals-from ../../test/head.js */
+"use strict";
+
+// Import the inspector's head.js first (which itself imports shared-head.js).
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/inspector/test/head.js",
+  this);
+
+// Load the shared Redux helpers into this compartment.
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/framework/test/shared-redux-head.js",
+  this);
+
+Services.prefs.setBoolPref("devtools.layoutview.enabled", true);
+Services.prefs.setIntPref("devtools.toolbox.footer.height", 350);
+registerCleanupFunction(() => {
+  Services.prefs.clearUserPref("devtools.layoutview.enabled");
+  Services.prefs.clearUserPref("devtools.toolbox.footer.height");
+});
+
+const HIGHLIGHTER_TYPE = "CssGridHighlighter";
+
+/**
+ * Open the toolbox, with the inspector tool visible, and the layout view
+ * sidebar tab selected to display the box model view with properties.
+ *
+ * @return {Promise} a promise that resolves when the inspector is ready and the box model
+ *         view is visible and ready.
+ */
+function openLayoutView() {
+  return openInspectorSidebarTab("layoutview").then(data => {
+    // The actual highligher show/hide methods are mocked in box model tests.
+    // The highlighter is tested in devtools/inspector/test.
+    function mockHighlighter({highlighter}) {
+      highlighter.showBoxModel = function () {
+        return promise.resolve();
+      };
+      highlighter.hideBoxModel = function () {
+        return promise.resolve();
+      };
+    }
+    mockHighlighter(data.toolbox);
+
+    return {
+      toolbox: data.toolbox,
+      inspector: data.inspector,
+      gridInspector: data.inspector.gridInspector,
+      testActor: data.testActor
+    };
+  });
+}
+
+/**
+ * Simulate a color change in a given color picker tooltip.
+ *
+ * @param  {Spectrum|ColorWidget} colorPicker
+ *         The color picker widget.
+ * @param  {Array} newRgba
+ *         Array of the new rgba values to be set in the color widget.
+ */
+var simulateColorPickerChange = Task.async(function* (colorPicker, newRgba) {
+  info("Getting the spectrum colorpicker object");
+  let spectrum = yield colorPicker.spectrum;
+  info("Setting the new color");
+  spectrum.rgb = newRgba;
+  info("Applying the change");
+  spectrum.updateUI();
+  spectrum.onChange();
+});
--- a/devtools/client/inspector/rules/test/head.js
+++ b/devtools/client/inspector/rules/test/head.js
@@ -493,26 +493,16 @@ function waitForStyleModification(inspec
  */
 function* clickSelectorIcon(icon, view) {
   let onToggled = view.once("ruleview-selectorhighlighter-toggled");
   EventUtils.synthesizeMouseAtCenter(icon, {}, view.styleWindow);
   yield onToggled;
 }
 
 /**
- * Make sure window is properly focused before sending a key event.
- * @param {Window} win
- * @param {Event} key
- */
-function focusAndSendKey(win, key) {
-  win.document.documentElement.focus();
-  EventUtils.sendKey(key, win);
-}
-
-/**
  * Toggle one of the checkboxes inside the class-panel. Resolved after the DOM mutation
  * has been recorded.
  * @param {CssRuleView} view The rule-view instance.
  * @param {String} name The class name to find the checkbox.
  */
 function* toggleClassPanelCheckBox(view, name) {
   info(`Clicking on checkbox for class ${name}`);
   const checkBox = [...view.classPanel.querySelectorAll("[type=checkbox]")].find(box => {
--- a/devtools/client/inspector/shared/highlighters-overlay.js
+++ b/devtools/client/inspector/shared/highlighters-overlay.js
@@ -1,17 +1,16 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const promise = require("promise");
 const {Task} = require("devtools/shared/task");
 const EventEmitter = require("devtools/shared/event-emitter");
 const { VIEW_NODE_VALUE_TYPE } = require("devtools/client/inspector/shared/node-types");
 
 const DEFAULT_GRID_COLOR = "#4B0082";
 
 /**
  * Highlighters overlay is a singleton managing all highlighters in the Inspector.
@@ -262,28 +261,36 @@ HighlightersOverlay.prototype = {
 
   /**
    * Get a highlighter front given a type. It will only be initialized once.
    *
    * @param  {String} type
    *         The highlighter type. One of this.highlighters.
    * @return {Promise} that resolves to the highlighter
    */
-  _getHighlighter: function (type) {
+  _getHighlighter: Task.async(function* (type) {
     let utils = this.highlighterUtils;
 
     if (this.highlighters[type]) {
-      return promise.resolve(this.highlighters[type]);
+      return this.highlighters[type];
     }
 
-    return utils.getHighlighterByType(type).then(highlighter => {
-      this.highlighters[type] = highlighter;
-      return highlighter;
-    });
-  },
+    let highlighter;
+
+    try {
+      highlighter = yield utils.getHighlighterByType(type);
+    } catch (e) {}
+
+    if (!highlighter) {
+      return null;
+    }
+
+    this.highlighters[type] = highlighter;
+    return highlighter;
+  }),
 
   _handleRejection: function (error) {
     if (!this.destroyed) {
       console.error(error);
     }
   },
 
   /**
--- a/devtools/client/inspector/test/head.js
+++ b/devtools/client/inspector/test/head.js
@@ -639,16 +639,29 @@ var waitForTab = Task.async(function* ()
  */
 function synthesizeKeys(input, win) {
   for (let key of input.split("")) {
     EventUtils.synthesizeKey(key, {}, win);
   }
 }
 
 /**
+ * Make sure window is properly focused before sending a key event.
+ *
+ * @param {Window} win
+ *        The window containing the panel
+ * @param {String} key
+ *        The string value to input
+ */
+function focusAndSendKey(win, key) {
+  win.document.documentElement.focus();
+  EventUtils.sendKey(key, win);
+}
+
+/**
  * Given a Tooltip instance, fake a mouse event on the `target` DOM Element
  * and assert that the `tooltip` is correctly displayed.
  *
  * @param {Tooltip} tooltip
  *        The tooltip instance
  * @param {DOMElement} target
  *        The DOM Element on which a tooltip should appear
  *
--- a/dom/animation/test/chrome/test_restyles.html
+++ b/dom/animation/test/chrome/test_restyles.html
@@ -57,16 +57,17 @@ function observeStyling(frameCount, onFr
 
 function ensureElementRemoval(aElement) {
   return new Promise(function(resolve) {
     aElement.remove();
     waitForAllPaintsFlushed(resolve);
   });
 }
 
+SimpleTest.expectAssertions(0, 1); // bug 1332970
 SimpleTest.waitForExplicitFinish();
 
 var omtaEnabled = isOMTAEnabled();
 
 var isAndroid = !!navigator.userAgent.includes("Android");
 
 function add_task_if_omta_enabled(test) {
   if (!omtaEnabled) {
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -10005,19 +10005,19 @@ nsIDocument::RegisterPendingLinkUpdate(L
     return;
   }
 
   aLink->SetHasPendingLinkUpdate();
 
   if (!mHasLinksToUpdateRunnable) {
     nsCOMPtr<nsIRunnable> event =
       NewRunnableMethod(this, &nsIDocument::FlushPendingLinkUpdatesFromRunnable);
+    // Do this work in a second in the worst case.
     nsresult rv =
-      Dispatch("nsIDocument::FlushPendingLinkUpdatesFromRunnable",
-               TaskCategory::Other, event.forget());
+      NS_IdleDispatchToCurrentThread(event.forget(), 1000);
     if (NS_FAILED(rv)) {
       // If during shutdown posting a runnable doesn't succeed, we probably
       // don't need to update link states.
       return;
     }
     mHasLinksToUpdateRunnable = true;
   }
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -862,17 +862,21 @@ nsGlobalWindow::ExecuteIdleRequest(TimeS
 
   if (Performance* perf = GetPerformance()) {
     deadline = perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline);
   }
 
   mIdleRequestExecutor->MaybeUpdateIdlePeriodLimit();
   nsresult result = RunIdleRequest(request, deadline, false);
 
-  mIdleRequestExecutor->MaybeDispatch();
+  // Running the idle callback could've suspended the window, in which
+  // case mIdleRequestExecutor will be null.
+  if (mIdleRequestExecutor) {
+    mIdleRequestExecutor->MaybeDispatch();
+  }
   return result;
 }
 
 class IdleRequestTimeoutHandler final : public TimeoutHandler
 {
 public:
   IdleRequestTimeoutHandler(JSContext* aCx,
                             IdleRequest* aIdleRequest,
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -141,16 +141,17 @@ nsJSUtils::ExecutionContext::ExecutionCo
 #endif
     mCx(aCx)
   , mCompartment(aCx, aGlobal)
   , mRetValue(aCx)
   , mScopeChain(aCx)
   , mRv(NS_OK)
   , mSkip(false)
   , mCoerceToString(false)
+  , mEncodeBytecode(false)
 #ifdef DEBUG
   , mWantsReturnValue(false)
   , mExpectScopeChain(false)
 #endif
 {
   MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(nsContentUtils::IsInMicroTask());
@@ -199,69 +200,106 @@ nsJSUtils::ExecutionContext::JoinAndExec
   if (mSkip) {
     return mRv;
   }
 
   MOZ_ASSERT(!mWantsReturnValue);
   MOZ_ASSERT(!mExpectScopeChain);
   aScript.set(JS::FinishOffThreadScript(mCx, *aOffThreadToken));
   *aOffThreadToken = nullptr; // Mark the token as having been finished.
-  if (!aScript || !JS_ExecuteScript(mCx, mScopeChain, aScript)) {
+  if (!aScript) {
+    mSkip = true;
+    mRv = EvaluationExceptionToNSResult(mCx);
+    return mRv;
+  }
+
+  if (mEncodeBytecode && !StartIncrementalEncoding(mCx, aScript)) {
+    mSkip = true;
+    mRv = EvaluationExceptionToNSResult(mCx);
+    return mRv;
+  }
+
+  if (!JS_ExecuteScript(mCx, mScopeChain, aScript)) {
     mSkip = true;
     mRv = EvaluationExceptionToNSResult(mCx);
     return mRv;
   }
 
   return NS_OK;
 }
 
 nsresult
 nsJSUtils::ExecutionContext::CompileAndExec(JS::CompileOptions& aCompileOptions,
-                                            JS::SourceBufferHolder& aSrcBuf)
+                                            JS::SourceBufferHolder& aSrcBuf,
+                                            JS::MutableHandle<JSScript*> aScript)
 {
   if (mSkip) {
     return mRv;
   }
 
   MOZ_ASSERT_IF(aCompileOptions.versionSet,
                 aCompileOptions.version != JSVERSION_UNKNOWN);
   MOZ_ASSERT(aSrcBuf.get());
   MOZ_ASSERT(mRetValue.isUndefined());
 #ifdef DEBUG
   mWantsReturnValue = !aCompileOptions.noScriptRval;
 #endif
+
+  bool compiled = true;
+  if (mScopeChain.length() == 0) {
+    compiled = JS::Compile(mCx, aCompileOptions, aSrcBuf, aScript);
+  } else {
+    compiled = JS::CompileForNonSyntacticScope(mCx, aCompileOptions, aSrcBuf, aScript);
+  }
+
+  MOZ_ASSERT_IF(compiled, aScript);
+  if (!compiled) {
+    mSkip = true;
+    mRv = EvaluationExceptionToNSResult(mCx);
+    return mRv;
+  }
+
+  if (mEncodeBytecode && !StartIncrementalEncoding(mCx, aScript)) {
+    mSkip = true;
+    mRv = EvaluationExceptionToNSResult(mCx);
+    return mRv;
+  }
+
   MOZ_ASSERT(!mCoerceToString || mWantsReturnValue);
-  if (!JS::Evaluate(mCx, mScopeChain, aCompileOptions, aSrcBuf, &mRetValue)) {
+  if (!JS_ExecuteScript(mCx, mScopeChain, aScript, &mRetValue)) {
     mSkip = true;
     mRv = EvaluationExceptionToNSResult(mCx);
     return mRv;
   }
 
   return NS_OK;
 }
 
 nsresult
 nsJSUtils::ExecutionContext::CompileAndExec(JS::CompileOptions& aCompileOptions,
                                             const nsAString& aScript)
 {
+  MOZ_ASSERT(!mEncodeBytecode, "A JSScript is needed for calling FinishIncrementalEncoding");
   if (mSkip) {
     return mRv;
   }
 
   const nsPromiseFlatString& flatScript = PromiseFlatString(aScript);
   JS::SourceBufferHolder srcBuf(flatScript.get(), aScript.Length(),
                                 JS::SourceBufferHolder::NoOwnership);
-  return CompileAndExec(aCompileOptions, srcBuf);
+  JS::Rooted<JSScript*> script(mCx);
+  return CompileAndExec(aCompileOptions, srcBuf, &script);
 }
 
 nsresult
 nsJSUtils::ExecutionContext::DecodeAndExec(JS::CompileOptions& aCompileOptions,
                                            mozilla::Vector<uint8_t>& aBytecodeBuf,
                                            size_t aBytecodeIndex)
 {
+  MOZ_ASSERT(!mEncodeBytecode, "A JSScript is needed for calling FinishIncrementalEncoding");
   if (mSkip) {
     return mRv;
   }
 
   MOZ_ASSERT(!mWantsReturnValue);
   JS::Rooted<JSScript*> script(mCx);
   JS::TranscodeResult tr = JS::DecodeScript(mCx, aBytecodeBuf, &script, aBytecodeIndex);
   // These errors are external parameters which should be handled before the
@@ -301,44 +339,16 @@ nsJSUtils::ExecutionContext::DecodeJoinA
     mRv = EvaluationExceptionToNSResult(mCx);
     return mRv;
   }
 
   return NS_OK;
 }
 
 nsresult
-nsJSUtils::ExecutionContext::JoinEncodeAndExec(void **aOffThreadToken,
-                                               JS::MutableHandle<JSScript*> aScript)
-{
-  MOZ_ASSERT_IF(aOffThreadToken, !mWantsReturnValue);
-  aScript.set(JS::FinishOffThreadScript(mCx, *aOffThreadToken));
-  *aOffThreadToken = nullptr; // Mark the token as having been finished.
-  if (!aScript) {
-    mSkip = true;
-    mRv = EvaluationExceptionToNSResult(mCx);
-    return mRv;
-  }
-
-  if (!StartIncrementalEncoding(mCx, aScript)) {
-    mSkip = true;
-    mRv = EvaluationExceptionToNSResult(mCx);
-    return mRv;
-  }
-
-  if (!JS_ExecuteScript(mCx, mScopeChain, aScript)) {
-    mSkip = true;
-    mRv = EvaluationExceptionToNSResult(mCx);
-    return mRv;
-  }
-
-  return mRv;
-}
-
-nsresult
 nsJSUtils::ExecutionContext::ExtractReturnValue(JS::MutableHandle<JS::Value> aRetValue)
 {
   MOZ_ASSERT(aRetValue.isUndefined());
   if (mSkip) {
     // Repeat earlier result, as NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW are not
     // failures cases.
 #ifdef DEBUG
     mWantsReturnValue = false;
--- a/dom/base/nsJSUtils.h
+++ b/dom/base/nsJSUtils.h
@@ -90,16 +90,19 @@ public:
 
     // Used to skip upcoming phases in case of a failure.  In such case the
     // result is carried by mRv.
     bool mSkip;
 
     // Should the result be serialized before being returned.
     bool mCoerceToString;
 
+    // Encode the bytecode before it is being executed.
+    bool mEncodeBytecode;
+
 #ifdef DEBUG
     // Should we set the return value.
     bool mWantsReturnValue;
 
     bool mExpectScopeChain;
 #endif
 
    public:
@@ -119,16 +122,25 @@ public:
 
     // The returned value would be converted to a string if the
     // |aCoerceToString| is flag set.
     ExecutionContext& SetCoerceToString(bool aCoerceToString) {
       mCoerceToString = aCoerceToString;
       return *this;
     }
 
+    // When set, this flag records and encodes the bytecode as soon as it is
+    // being compiled, and before it is being executed. The bytecode can then be
+    // requested by using |JS::FinishIncrementalEncoding| with the mutable
+    // handle |aScript| argument of |CompileAndExec| or |JoinAndExec|.
+    ExecutionContext& SetEncodeBytecode(bool aEncodeBytecode) {
+      mEncodeBytecode = aEncodeBytecode;
+      return *this;
+    }
+
     // Set the scope chain in which the code should be executed.
     void SetScopeChain(const JS::AutoObjectVector& aScopeChain);
 
     // Copy the returned value in the mutable handle argument, in case of a
     // evaluation failure either during the execution or the conversion of the
     // result to a string, the nsresult would be set to the corresponding result
     // code, and the mutable handle argument would remain unchanged.
     //
@@ -144,36 +156,32 @@ public:
     // thread before starting the execution of the script.
     //
     // The compiled script would be returned in the |aScript| out-param.
     MOZ_MUST_USE nsresult JoinAndExec(void **aOffThreadToken,
                                       JS::MutableHandle<JSScript*> aScript);
 
     // Compile a script contained in a SourceBuffer, and execute it.
     nsresult CompileAndExec(JS::CompileOptions& aCompileOptions,
-                            JS::SourceBufferHolder& aSrcBuf);
+                            JS::SourceBufferHolder& aSrcBuf,
+                            JS::MutableHandle<JSScript*> aScript);
 
     // Compile a script contained in a string, and execute it.
     nsresult CompileAndExec(JS::CompileOptions& aCompileOptions,
                             const nsAString& aScript);
 
     // Decode a script contained in a buffer, and execute it.
     MOZ_MUST_USE nsresult DecodeAndExec(JS::CompileOptions& aCompileOptions,
                                         mozilla::Vector<uint8_t>& aBytecodeBuf,
                                         size_t aBytecodeIndex);
 
     // After getting a notification that an off-thread decoding terminated, this
     // function will get the result of the decoder by moving it to the main
     // thread before starting the execution of the script.
     MOZ_MUST_USE nsresult DecodeJoinAndExec(void **aOffThreadToken);
-
-    // Similar to JoinAndExec, except that in addition to fecthing the source,
-    // we register the fact that we plan to encode its bytecode later.
-    MOZ_MUST_USE nsresult JoinEncodeAndExec(void **aOffThreadToken,
-                                            JS::MutableHandle<JSScript*> aScript);
   };
 
   static nsresult CompileModule(JSContext* aCx,
                                 JS::SourceBufferHolder& aSrcBuf,
                                 JS::Handle<JSObject*> aEvaluationGlobal,
                                 JS::CompileOptions &aCompileOptions,
                                 JS::MutableHandle<JSObject*> aModule);
 
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -755,17 +755,16 @@ skip-if = toolkit == 'android'
 [test_script_loader_crossorigin_data_url.html]
 [test_script_loader_js_cache.html]
 support-files =
   file_js_cache.html
   file_js_cache_with_sri.html
   file_js_cache.js
   file_js_cache_save_after_load.html
   file_js_cache_save_after_load.js
-skip-if = (os == 'linux' || toolkit == 'android') # mochitest are executed on a single core
 [test_setInterval_uncatchable_exception.html]
 skip-if = debug == false
 [test_settimeout_extra_arguments.html]
 [test_settimeout_inner.html]
 [test_setTimeoutWith0.html]
 [test_setting_opener.html]
 [test_simplecontentpolicy.html]
 skip-if = e10s # Bug 1156489.
--- a/dom/base/test/test_script_loader_js_cache.html
+++ b/dom/base/test/test_script_loader_js_cache.html
@@ -13,32 +13,36 @@
   <script src="/resources/testharness.js"></script>
   <script src="/resources/testharnessreport.js"></script>
   <script type="application/javascript">
     // This is the state machine of the trace events produced by the
     // ScriptLoader. This state machine is used to give a name to each
     // code path, such that we can assert each code path with a single word.
     var scriptLoaderStateMachine = {
       "scriptloader_load_source": {
-        "scriptloader_encode_and_execute": {
-          "scriptloader_bytecode_saved": "bytecode_saved",
-          "scriptloader_bytecode_failed": "bytecode_failed"
-        },
-        "scriptloader_execute": "source_exec"
+        "scriptloader_execute": {
+          "scriptloader_encode": {
+            "scriptloader_bytecode_saved": "bytecode_saved",
+            "scriptloader_bytecode_failed": "bytecode_failed"
+          },
+          "scriptloader_no_encode": "source_exec"
+        }
       },
       "scriptloader_load_bytecode": {
         "scriptloader_fallback": {
           // Replicate the top-level state machine without
           // "scriptloader_load_bytecode" transition.
           "scriptloader_load_source": {
-            "scriptloader_encode_and_execute": {
-              "scriptloader_bytecode_saved": "fallback_bytecode_saved",
-              "scriptloader_bytecode_failed": "fallback_bytecode_failed"
-            },
-            "scriptloader_execute": "fallback_source_exec"
+            "scriptloader_execute": {
+              "scriptloader_encode": {
+                "scriptloader_bytecode_saved": "fallback_bytecode_saved",
+                "scriptloader_bytecode_failed": "fallback_bytecode_failed"
+              },
+              "scriptloader_no_encode": "fallback_source_exec"
+            }
           }
         },
         "scriptloader_execute": "bytecode_exec"
       }
     };
 
     function flushNeckoCache() {
       return new Promise (resolve => {
@@ -88,17 +92,18 @@
         }
       }
 
       var iwin = iframe.contentWindow;
       iwin.addEventListener("scriptloader_load_source", log_event);
       iwin.addEventListener("scriptloader_load_bytecode", log_event);
       iwin.addEventListener("scriptloader_generate_bytecode", log_event);
       iwin.addEventListener("scriptloader_execute", log_event);
-      iwin.addEventListener("scriptloader_encode_and_execute", log_event);
+      iwin.addEventListener("scriptloader_encode", log_event);
+      iwin.addEventListener("scriptloader_no_encode", log_event);
       iwin.addEventListener("scriptloader_bytecode_saved", log_event);
       iwin.addEventListener("scriptloader_bytecode_failed", log_event);
       iwin.addEventListener("scriptloader_fallback", log_event);
       iwin.addEventListener("ping", (evt) => {
         ping += 1;
         dump(`## Content event: ${evt.type} (=${ping})\n`);
       });
       iframe.src = url;
--- a/dom/media/eme/MediaKeySystemAccess.cpp
+++ b/dom/media/eme/MediaKeySystemAccess.cpp
@@ -662,18 +662,31 @@ GetSupportedCapabilities(const CodecType
     // parameters. The case-sensitivity of string comparisons is determined by
     // the appropriate RFC or other specification.
     // (Note: codecs array is 'parameter').
 
     // If media types is empty:
     if (codecs.IsEmpty()) {
       // If container normatively implies a specific set of codecs and codec constraints:
       // Let parameters be that set.
+      if (isMP4) {
+        if (aCodecType == Audio) {
+          codecs.AppendElement(EME_CODEC_AAC);
+        } else if (aCodecType == Video) {
+          codecs.AppendElement(EME_CODEC_H264);
+        }
+      } else if (isWebM) {
+        if (aCodecType == Audio) {
+          codecs.AppendElement(EME_CODEC_VORBIS);
+        } else if (aCodecType == Video) {
+          codecs.AppendElement(EME_CODEC_VP8);
+        }
+      }
       // Otherwise: Continue to the next iteration.
-      continue;
+      // (Note: all containers we support have implied codecs, so don't continue here.)
     }
 
     // If container type is not strictly a audio/video type, continue to the next iteration.
     const auto majorType = GetMajorType(containerType.Type());
     if (majorType == Invalid) {
       EME_LOG("MediaKeySystemConfiguration (label='%s') "
               "MediaKeySystemMediaCapability('%s','%s') unsupported; "
               "MIME type is not an audio or video MIME type.",
@@ -935,23 +948,18 @@ GetSupportedConfig(const KeySystemConfig
       config.mPersistentState = MediaKeysRequirement::Required;
     }
   }
   // Set the sessionTypes member of accumulated configuration to session types.
   config.mSessionTypes.Construct(Move(sessionTypes));
 
   // If the videoCapabilities and audioCapabilities members in candidate
   // configuration are both empty, return NotSupported.
-  if (aCandidate.mAudioCapabilities.IsEmpty() &&
-      aCandidate.mVideoCapabilities.IsEmpty()) {
-    EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; "
-            "no supported audio or video capabilities specified",
-            NS_ConvertUTF16toUTF8(aCandidate.mLabel).get());
-    return false;
-  }
+  // TODO: Most sites using EME still don't pass capabilities, so we
+  // can't reject on it yet without breaking them. So add this later.
 
   // If the videoCapabilities member in candidate configuration is non-empty:
   if (!aCandidate.mVideoCapabilities.IsEmpty()) {
     // Let video capabilities be the result of executing the Get Supported
     // Capabilities for Audio/Video Type algorithm on Video, candidate
     // configuration's videoCapabilities member, accumulated configuration,
     // and restrictions.
     Sequence<MediaKeySystemMediaCapability> caps =
--- a/dom/media/test/eme.js
+++ b/dom/media/test/eme.js
@@ -1,14 +1,14 @@
 const CLEARKEY_KEYSYSTEM = "org.w3.clearkey";
 
 const gCencMediaKeySystemConfig = [{
   initDataTypes: ['cenc'],
-  videoCapabilities: [{ contentType: 'video/mp4; codecs="avc1.42E01E"' }],
-  audioCapabilities: [{ contentType: 'audio/mp4; codecs="mp4a.40.2"' }],
+  videoCapabilities: [{ contentType: 'video/mp4' }],
+  audioCapabilities: [{ contentType: 'audio/mp4' }],
 }];
 
 function IsMacOSSnowLeopardOrEarlier() {
   var re = /Mac OS X (\d+)\.(\d+)/;
   var ver = navigator.userAgent.match(re);
   if (!ver || ver.length != 3) {
     return false;
   }
--- a/dom/media/test/test_eme_initDataTypes.html
+++ b/dom/media/test/test_eme_initDataTypes.html
@@ -89,17 +89,21 @@ function PrepareInitData(initDataType, i
     return new TextEncoder().encode(initData);
   } else if (initDataType == "webm") {
     return StringToArrayBuffer(atob(initData));
   }
 }
 
 function Test(test) {
   return new Promise(function(resolve, reject) {
-    navigator.requestMediaKeySystemAccess('org.w3.clearkey', gCencMediaKeySystemConfig)
+    var configs = [{
+      initDataTypes: [test.initDataType],
+      videoCapabilities: [{contentType: 'video/mp4' }],
+    }];
+    navigator.requestMediaKeySystemAccess('org.w3.clearkey', configs)
       .then((access) => access.createMediaKeys())
       .then((mediaKeys) => {
           var session = mediaKeys.createSession(test.sessionType);
           session.addEventListener("message", function(event) {
             is(event.messageType, "license-request", "'" + test.name + "' MediaKeyMessage type should be license-request.");
             var text = new TextDecoder().decode(event.message);
             is(text, test.expectedRequest, "'" + test.name + "' got expected response.");
             is(text == test.expectedRequest, test.expectPass,
--- a/dom/media/test/test_eme_missing_pssh.html
+++ b/dom/media/test/test_eme_missing_pssh.html
@@ -30,18 +30,18 @@
         0x83, 0xe9, 0xbf, 0x6e, 0x5e, 0x49, 0x3e, 0x53,
         0x00, 0x00, 0x00, 0x00                         // Size of Data (0)
       ];
 
       var audio = document.getElementById("audio");
 
       function LoadEME() {
         var options = [{
-          initDataTypes: ['cenc'],
-          audioCapabilities: [{ contentType: 'audio/mp4; codecs="mp4a.40.2"' }],
+          initDataType: 'cenc',
+          audioType: 'audio/mp4; codecs="mp4a.40.2"',
         }];
         navigator.requestMediaKeySystemAccess("org.w3.clearkey", options)
           .then((keySystemAccess) => {
             return keySystemAccess.createMediaKeys();
           }, bail("Failed to request key system access."))
 
           .then((mediaKeys) => {
             audio.setMediaKeys(mediaKeys);
--- a/dom/media/test/test_eme_requestKeySystemAccess.html
+++ b/dom/media/test/test_eme_requestKeySystemAccess.html
@@ -65,31 +65,24 @@ function Test(test) {
           info(name + " failed: " + ex);
         }
         ok(!test.shouldPass, name + " failed and was expected to " + (test.shouldPass ? "pass" : "fail"));
         resolve();
       });
   });
 }
 
-const AUDIO_WEBM_VORBIS = 'audio/webm; codecs="vorbis"';
-const VIDEO_WEBM_VP8 = 'video/webm; codecs="vp8"';
-const VIDEO_WEBM_VP9 = 'video/webm; codecs="vp9"';
-
-const AUDIO_MP4_AAC = 'audio/mp4; codecs="mp4a.40.2"';
-const VIDEO_MP4_H264 = 'video/mp4; codecs="avc1.42E01E"';
-
 var tests = [
   {
     name: 'Empty keySystem string',
     keySystem: '',
     options: [
       {
         initDataTypes: ['cenc'],
-        videoCapabilities: [{contentType: VIDEO_MP4_H264}],
+        videoCapabilities: [{contentType: 'video/mp4'}],
       }
     ],
     shouldPass: false,
   },
   {
     name: 'Empty options specified',
     options: [ ],
     shouldPass: false,
@@ -99,62 +92,62 @@ var tests = [
     shouldPass: false,
   },
   {
     name: 'Basic MP4 cenc',
     options: [
       {
         label: SUPPORTED_LABEL,
         initDataTypes: ['cenc'],
-        audioCapabilities: [{contentType: AUDIO_MP4_AAC}],
-        videoCapabilities: [{contentType: VIDEO_MP4_H264}],
+        audioCapabilities: [{contentType: 'audio/mp4'}],
+        videoCapabilities: [{contentType: 'video/mp4'}],
       }
     ],
     expectedConfig: {
       label: SUPPORTED_LABEL,
       initDataTypes: ['cenc'],
-      audioCapabilities: [{contentType: AUDIO_MP4_AAC}],
-      videoCapabilities: [{contentType: VIDEO_MP4_H264}],
+      audioCapabilities: [{contentType: 'audio/mp4'}],
+      videoCapabilities: [{contentType: 'video/mp4'}],
     },
     shouldPass: true,
   },
   {
     name: 'Invalid keysystem failure',
     keySystem: 'bogusKeySystem',
     options: [
       {
         initDataTypes: ['cenc'],
-        videoCapabilities: [{contentType: VIDEO_MP4_H264}],
+        videoCapabilities: [{contentType: 'video/mp4'}],
       }
     ],
     shouldPass: false,
   },
   {
     name: 'Invalid initDataType',
     options: [
       {
         initDataTypes: ['bogus'],
-        audioCapabilities: [{contentType: AUDIO_MP4_AAC}],
+        audioCapabilities: [{contentType: 'audio/mp4'}],
       }
     ],
     shouldPass: false,
   },
   {
     name: 'Valid initDataType after invalid',
     options: [
       {
         label: SUPPORTED_LABEL,
         initDataTypes: ['bogus', 'invalid', 'cenc'],
-        audioCapabilities: [{contentType: AUDIO_MP4_AAC}],
+        audioCapabilities: [{contentType: 'audio/mp4'}],
       }
     ],
     expectedConfig: {
       label: SUPPORTED_LABEL,
       initDataTypes: ['cenc'],
-      audioCapabilities: [{contentType: AUDIO_MP4_AAC}],
+      audioCapabilities: [{contentType: 'audio/mp4'}],
     },
     shouldPass: true,
   },
   {
     name: 'Invalid videoType',
     options: [
       {
         initDataTypes: ['cenc'],
@@ -163,145 +156,145 @@ var tests = [
     ],
     shouldPass: false,
   },
   {
     name: 'Invalid distinctiveIdentifier fails',
     options: [
       {
         initDataTypes: ['cenc'],
-        videoCapabilities: [{contentType: VIDEO_MP4_H264}],
+        videoCapabilities: [{contentType: 'video/mp4'}],
         distinctiveIdentifier: 'bogus',
         persistentState: 'bogus',
       }
     ],
     shouldPass: false,
   },
   {
     name: 'distinctiveIdentifier is prohibited for ClearKey',
     options: [
       {
         initDataTypes: ['cenc'],
-        videoCapabilities: [{contentType: VIDEO_MP4_H264}],
+        videoCapabilities: [{contentType: 'video/mp4'}],
         distinctiveIdentifier: 'required',
       }
     ],
     shouldPass: false,
   },
   {
     name: 'Invalid persistentState fails',
     options: [
       {
         initDataTypes: ['cenc'],
-        videoCapabilities: [{contentType: VIDEO_MP4_H264}],
+        videoCapabilities: [{contentType: 'video/mp4'}],
         persistentState: 'bogus',
       }
     ],
     shouldPass: false,
   },
   {
     name: 'Invalid robustness unsupported',
     options: [
       {
         initDataTypes: ['cenc'],
-        videoCapabilities: [{contentType: VIDEO_MP4_H264, robustness: 'very much so'}],
+        videoCapabilities: [{contentType: 'video/mp4', robustness: 'very much so'}],
       }
     ],
     shouldPass: false,
   },
   {
     name: 'Unexpected config entry should be ignored',
     options: [
       {
         label: SUPPORTED_LABEL,
         initDataTypes: ['cenc'],
-        videoCapabilities: [{contentType: VIDEO_MP4_H264}],
+        videoCapabilities: [{contentType: 'video/mp4'}],
         unexpectedEntry: 'this should be ignored',
       }
     ],
     expectedConfig: {
       label: SUPPORTED_LABEL,
       initDataTypes: ['cenc'],
-      videoCapabilities: [{contentType: VIDEO_MP4_H264}],
+      videoCapabilities: [{contentType: 'video/mp4'}],
     },
     shouldPass: true,
   },
   {
     name: 'Invalid option followed by valid',
     options: [
       {
         label: "this config should not be supported",
         initDataTypes: ['bogus'],
       },
       {
         label: SUPPORTED_LABEL,
         initDataTypes: ['cenc'],
-        videoCapabilities: [{contentType: VIDEO_MP4_H264}],
+        videoCapabilities: [{contentType: 'video/mp4'}],
       }
     ],
     expectedConfig: {
       label: SUPPORTED_LABEL,
       initDataTypes: ['cenc'],
-      videoCapabilities: [{contentType: VIDEO_MP4_H264}],
+      videoCapabilities: [{contentType: 'video/mp4'}],
     },
     shouldPass: true,
   },
   {
     name: 'Persistent-license should not be supported by ClearKey',
     options: [
       {
         initDataTypes: ['cenc'],
-        videoCapabilities: [{contentType: VIDEO_MP4_H264}],
+        videoCapabilities: [{contentType: 'video/mp4'}],
         sessionTypes: ['persistent-license'],
         persistentState: 'optional',
       }
     ],
     shouldPass: false,
   },  
   {
     name: 'Persistent-usage-record should not be supported by ClearKey',
     options: [
       {
         initDataTypes: ['cenc'],
-        videoCapabilities: [{contentType: VIDEO_MP4_H264}],
+        videoCapabilities: [{contentType: 'video/mp4'}],
         sessionTypes: ['persistent-usage-record'],
         persistentState: 'optional',
       }
     ],
     shouldPass: false,
   },
   {
     name: 'MP4 audio container',
     options: [
       {
         label: SUPPORTED_LABEL,
         initDataTypes: ['cenc'],
-        audioCapabilities: [{contentType: AUDIO_MP4_AAC}],
+        audioCapabilities: [{contentType: 'audio/mp4'}],
       }
     ],
     expectedConfig: {
       label: SUPPORTED_LABEL,
       initDataTypes: ['cenc'],
-      audioCapabilities: [{contentType: AUDIO_MP4_AAC}],
+      audioCapabilities: [{contentType: 'audio/mp4'}],
     },
     shouldPass: true,
   },
   {
     name: 'MP4 audio container with AAC-LC',
     options: [
       {
         label: SUPPORTED_LABEL,
         initDataTypes: ['cenc'],
-        audioCapabilities: [{contentType: AUDIO_MP4_AAC}],
+        audioCapabilities: [{contentType: 'audio/mp4; codecs="mp4a.40.2"'}],
       }
     ],
     expectedConfig: {
       label: SUPPORTED_LABEL,
       initDataTypes: ['cenc'],
-      audioCapabilities: [{contentType: AUDIO_MP4_AAC}],
+      audioCapabilities: [{contentType: 'audio/mp4; codecs="mp4a.40.2"'}],
     },
     shouldPass: true,
   },
   {
     name: 'MP4 audio container with invalid codecs',
     options: [
       {
         initDataTypes: ['cenc'],
@@ -341,23 +334,23 @@ var tests = [
     shouldPass: false,
   },
   {
     name: 'MP4 video container with constrained baseline h.264',
     options: [
       {
         label: SUPPORTED_LABEL,
         initDataTypes: ['cenc'],
-        videoCapabilities: [{contentType: VIDEO_MP4_H264}],
+        videoCapabilities: [{contentType: 'video/mp4; codecs="avc1.42E01E"'}],
       }
     ],
     expectedConfig: {
       label: SUPPORTED_LABEL,
       initDataTypes: ['cenc'],
-      videoCapabilities: [{contentType: VIDEO_MP4_H264}],
+      videoCapabilities: [{contentType: 'video/mp4; codecs="avc1.42E01E"'}],
     },
     shouldPass: true,
   },
   {
     name: 'MP4 video container with invalid codecs',
     options: [
       {
         initDataTypes: ['cenc'],
@@ -377,115 +370,106 @@ var tests = [
     shouldPass: false,
   },
   {
     name: 'MP4 audio and video type both specified',
     options: [
       {
         label: SUPPORTED_LABEL,
         initDataTypes: ['cenc'],
-        audioCapabilities: [{contentType: AUDIO_MP4_AAC}],
-        videoCapabilities: [{contentType: VIDEO_MP4_H264}],
+        videoCapabilities: [{contentType: 'video/mp4; codecs="avc1.42E01E"'}],
+        audioCapabilities: [{contentType: 'audio/mp4; codecs="mp4a.40.2"'}],
       }
     ],
     expectedConfig: {
       label: SUPPORTED_LABEL,
       initDataTypes: ['cenc'],
-      audioCapabilities: [{contentType: AUDIO_MP4_AAC}],
-      videoCapabilities: [{contentType: VIDEO_MP4_H264}],
+      videoCapabilities: [{contentType: 'video/mp4; codecs="avc1.42E01E"'}],
+      audioCapabilities: [{contentType: 'audio/mp4; codecs="mp4a.40.2"'}],
     },
     shouldPass: true,
   },
   {
     name: 'Basic WebM video',
     options: [
       {
         label: SUPPORTED_LABEL,
         initDataTypes: ['webm'],
-        videoCapabilities: [{contentType: VIDEO_WEBM_VP9}],
+        videoCapabilities: [{contentType: 'video/webm'}],
       }
     ],
     expectedConfig: {
       label: SUPPORTED_LABEL,
       initDataTypes: ['webm'],
-      videoCapabilities: [{contentType: VIDEO_WEBM_VP9}],
+      videoCapabilities: [{contentType: 'video/webm'}],
     },
     shouldPass: true,
   },
   {
     name: 'Basic WebM audio',
     options: [
       {
         label: SUPPORTED_LABEL,
         initDataTypes: ['webm'],
-        audioCapabilities: [{contentType: AUDIO_WEBM_VORBIS}],
+        audioCapabilities: [{contentType: 'audio/webm'}],
       }
     ],
     expectedConfig: {
       label: SUPPORTED_LABEL,
       initDataTypes: ['webm'],
-      audioCapabilities: [{contentType: AUDIO_WEBM_VORBIS}],
+      audioCapabilities: [{contentType: 'audio/webm'}],
     },
     shouldPass: true,
   },
   {
     name: 'Webm with Vorbis audio and VP8 video.',
     options: [
       {
         label: SUPPORTED_LABEL,
         initDataTypes: ['webm'],
-        videoCapabilities: [{contentType: VIDEO_WEBM_VP8}],
-        audioCapabilities: [{contentType: AUDIO_WEBM_VORBIS}],
+        videoCapabilities: [{contentType: 'video/webm;codecs="vp8"'}],
+        audioCapabilities: [{contentType: 'audio/webm;codecs="vorbis"'}],
       }
     ],
     expectedConfig: {
       label: SUPPORTED_LABEL,
       initDataTypes: ['webm'],
-      videoCapabilities: [{contentType: VIDEO_WEBM_VP8}],
-      audioCapabilities: [{contentType: AUDIO_WEBM_VORBIS}],
+      videoCapabilities: [{contentType: 'video/webm;codecs="vp8"'}],
+      audioCapabilities: [{contentType: 'audio/webm;codecs="vorbis"'}],
     },
     shouldPass: true,
   },
   {
     name: 'Webm with Vorbis audio and VP9 video.',
     options: [
       {
         label: SUPPORTED_LABEL,
         initDataTypes: ['webm'],
-        videoCapabilities: [{contentType: VIDEO_WEBM_VP9}],
-        audioCapabilities: [{contentType: AUDIO_WEBM_VORBIS}],
+        videoCapabilities: [{contentType: 'video/webm;codecs="vp9"'}],
+        audioCapabilities: [{contentType: 'audio/webm;codecs="vorbis"'}],
       }
     ],
     expectedConfig: {
       label: SUPPORTED_LABEL,
       initDataTypes: ['webm'],
-      videoCapabilities: [{contentType: VIDEO_WEBM_VP9}],
-      audioCapabilities: [{contentType: AUDIO_WEBM_VORBIS}],
+      videoCapabilities: [{contentType: 'video/webm;codecs="vp9"'}],
+      audioCapabilities: [{contentType: 'audio/webm;codecs="vorbis"'}],
     },
     shouldPass: true,
   },
   {
     name: 'Webm with bogus video.',
     options: [
       {
         initDataTypes: ['webm'],
         videoCapabilities: [{contentType: 'video/webm;codecs="bogus"'}],
       }
     ],
     shouldPass: false,
   },
-  {
-    name: 'Unspecified capabilities',
-    options: [
-      {
-        initDataTypes: ['cenc'],
-      }
-    ],
-    shouldPass: false,
-  },
 ];
 
 function beginTest() {
   Promise.all(tests.map(Test)).then(function() { SimpleTest.finish(); });
 }
 
 if (!IsMacOSSnowLeopardOrEarlier()) {
   SimpleTest.waitForExplicitFinish();
--- a/dom/media/test/test_eme_sample_groups_playback.html
+++ b/dom/media/test/test_eme_sample_groups_playback.html
@@ -53,18 +53,18 @@
       video.addEventListener("waitingforkey", () => {
         Log(test.name, "waitingforkey");
         ok(false, test.name + " Video is waitingforkey, indicating that the samples are not being assigned the correct id from the sgpd box!");
         SimpleTest.finish();
       });
 
       function LoadEME() {
         var options = [{
-          initDataTypes: ['cenc'],
-          videoCapabilities: [{contentType: test.track.type}],
+          initDataType: "cenc",
+          videoType: test.track.type,
         }];
 
         return navigator.requestMediaKeySystemAccess("org.w3.clearkey", options)
           .then((keySystemAccess) => {
             return keySystemAccess.createMediaKeys();
           }, bail("Failed to request key system access."))
 
           .then((mediaKeys) => {
@@ -138,9 +138,9 @@
       }
 
       SetupEMEPref(LoadMSE);
 
   </script>
   </pre>
 </body>
 
-</html>
+</html>
\ No newline at end of file
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -1874,23 +1874,16 @@ ScriptLoader::FillCompileOptionsForReque
   MOZ_ASSERT(aRequest->mElement);
   if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, aRequest->mElement,
                                               &elementVal,
                                               /* aAllowWrapping = */ true))) {
     MOZ_ASSERT(elementVal.isObject());
     aOptions->setElement(&elementVal.toObject());
   }
 
-  // At the moment, the bytecode cache is only triggered if a script is large
-  // enough to be parsed out of the main thread.  Thus, for testing purposes, we
-  // force parsing any script out of the main thread.
-  if (IsEagerBytecodeCache()) {
-    aOptions->forceAsync = true;
-  }
-
   return NS_OK;
 }
 
 nsresult
 ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest)
 {
   MOZ_ASSERT(aRequest->IsReadyToRun());
 
@@ -1988,59 +1981,56 @@ ScriptLoader::EvaluateScript(ScriptLoadR
             rv = exec.DecodeAndExec(options, aRequest->mScriptBytecode,
                                     aRequest->mBytecodeOffset);
           }
           // We do not expect to be saving anything when we already have some
           // bytecode.
           MOZ_ASSERT(!aRequest->mCacheInfo);
         } else {
           MOZ_ASSERT(aRequest->IsSource());
-          if (aRequest->mOffThreadToken) {
-            // Off-main-thread parsing.
-            LOG(("ScriptLoadRequest (%p): Join (off-thread parsing) and Execute",
-                 aRequest));
-            {
-              nsJSUtils::ExecutionContext exec(aes.cx(), global);
-              JS::Rooted<JSScript*> script(aes.cx());
-              if (!aRequest->mCacheInfo) {
-                TRACE_FOR_TEST(aRequest->mElement, "scriptloader_execute");
-                rv = exec.JoinAndExec(&aRequest->mOffThreadToken, &script);
-                LOG(("ScriptLoadRequest (%p): Cannot cache anything (cacheInfo = nullptr)",
-                     aRequest));
-              } else {
-                TRACE_FOR_TEST(aRequest->mElement, "scriptloader_encode_and_execute");
-                MOZ_ASSERT(aRequest->mBytecodeOffset ==
-                           aRequest->mScriptBytecode.length());
-                rv = exec.JoinEncodeAndExec(&aRequest->mOffThreadToken,
-                                            &script);
-                // Queue the current script load request to later save the bytecode.
-                if (NS_SUCCEEDED(rv)) {
-                  aRequest->mScript = script;
-                  HoldJSObjects(aRequest);
-                  RegisterForBytecodeEncoding(aRequest);
-                } else {
-                  LOG(("ScriptLoadRequest (%p): Cannot cache anything (rv = %X, script = %p, cacheInfo = %p)",
-                       aRequest, unsigned(rv), script.get(), aRequest->mCacheInfo.get()));
-                  TRACE_FOR_TEST_NONE(aRequest->mElement, "scriptloader_bytecode_failed");
-                  aRequest->mCacheInfo = nullptr;
-                }
-              }
+          JS::Rooted<JSScript*> script(aes.cx());
+
+          bool encodeBytecode = false;
+          if (aRequest->mCacheInfo) {
+            MOZ_ASSERT(aRequest->mBytecodeOffset ==
+                       aRequest->mScriptBytecode.length());
+            encodeBytecode = IsEagerBytecodeCache(); // Heuristic!
+          }
+
+          {
+            nsJSUtils::ExecutionContext exec(aes.cx(), global);
+            exec.SetEncodeBytecode(encodeBytecode);
+            TRACE_FOR_TEST(aRequest->mElement, "scriptloader_execute");
+            if (aRequest->mOffThreadToken) {
+              // Off-main-thread parsing.
+              LOG(("ScriptLoadRequest (%p): Join (off-thread parsing) and Execute",
+                   aRequest));
+              rv = exec.JoinAndExec(&aRequest->mOffThreadToken, &script);
+            } else {
+              // Main thread parsing (inline and small scripts)
+              LOG(("ScriptLoadRequest (%p): Compile And Exec", aRequest));
+              nsAutoString inlineData;
+              SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData);
+              rv = exec.CompileAndExec(options, srcBuf, &script);
             }
+          }
+
+          // Queue the current script load request to later save the bytecode.
+          if (NS_SUCCEEDED(rv) && encodeBytecode) {
+            aRequest->mScript = script;
+            HoldJSObjects(aRequest);
+            TRACE_FOR_TEST(aRequest->mElement, "scriptloader_encode");
+            RegisterForBytecodeEncoding(aRequest);
           } else {
-            // Main thread parsing (inline and small scripts)
-            LOG(("ScriptLoadRequest (%p): Compile And Exec", aRequest));
-            nsJSUtils::ExecutionContext exec(aes.cx(), global);
-            nsAutoString inlineData;
-            SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData);
-            TRACE_FOR_TEST(aRequest->mElement, "scriptloader_execute");
-            rv = exec.CompileAndExec(options, srcBuf);
+            LOG(("ScriptLoadRequest (%p): Cannot cache anything (rv = %X, script = %p, cacheInfo = %p)",
+                 aRequest, unsigned(rv), script.get(), aRequest->mCacheInfo.get()));
+            TRACE_FOR_TEST_NONE(aRequest->mElement, "scriptloader_no_encode");
             aRequest->mCacheInfo = nullptr;
           }
         }
-
       }
     }
 
     // Even if we are not saving the bytecode of the current script, we have
     // to trigger the encoding of the bytecode, as the current script can
     // call functions of a script for which we are recording the bytecode.
     MaybeTriggerBytecodeEncoding();
   }
--- a/gfx/thebes/gfxASurface.cpp
+++ b/gfx/thebes/gfxASurface.cpp
@@ -467,17 +467,20 @@ public:
     static void AdjustUsedMemory(gfxSurfaceType aType, int32_t aBytes)
     {
         // A read-modify-write operation like += would require a memory barrier
         // here, which would defeat the purpose of using relaxed memory
         // ordering.  So separate out the read and write operations.
         sSurfaceMemoryUsed[size_t(aType)] = sSurfaceMemoryUsed[size_t(aType)] + aBytes;
     };
 
-    NS_DECL_ISUPPORTS
+    // This memory reporter is sometimes allocated on the compositor thread,
+    // but always released on the main thread, so its refcounting needs to be
+    // threadsafe.
+    NS_DECL_THREADSAFE_ISUPPORTS
 
     NS_IMETHOD CollectReports(nsIHandleReportCallback *aHandleReport,
                               nsISupports *aData, bool aAnonymize) override
     {
         const size_t len = ArrayLength(sSurfaceMemoryReporterAttrs);
         for (size_t i = 0; i < len; i++) {
             int64_t amount = sSurfaceMemoryUsed[i];
 
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -2599,34 +2599,26 @@ gfxFont::GetShapedWord(DrawTarget *aDraw
 
     CacheHashEntry *entry = mWordCache->PutEntry(key);
     if (!entry) {
         NS_WARNING("failed to create word cache entry - expect missing text");
         return nullptr;
     }
     gfxShapedWord* sw = entry->mShapedWord.get();
 
-    bool isContent = !mStyle.systemFont;
-
     if (sw) {
         sw->ResetAge();
-        Telemetry::Accumulate((isContent ? Telemetry::WORD_CACHE_HITS_CONTENT :
-                                   Telemetry::WORD_CACHE_HITS_CHROME),
-                              aLength);
 #ifndef RELEASE_OR_BETA
         if (aTextPerf) {
             aTextPerf->current.wordCacheHit++;
         }
 #endif
         return sw;
     }
 
-    Telemetry::Accumulate((isContent ? Telemetry::WORD_CACHE_MISSES_CONTENT :
-                               Telemetry::WORD_CACHE_MISSES_CHROME),
-                          aLength);
 #ifndef RELEASE_OR_BETA
     if (aTextPerf) {
         aTextPerf->current.wordCacheMiss++;
     }
 #endif
 
     sw = gfxShapedWord::Create(aText, aLength, aRunScript, aAppUnitsPerDevUnit,
                                aFlags, aRounding);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1355573.js
@@ -0,0 +1,6 @@
+// |jit-test| error:overflow
+if (getBuildConfiguration().debug === true)
+    throw "overflow";
+function f(){};
+Object.defineProperty(f, "name", {value: "a".repeat((1<<28)-1)});
+len = f.bind().name.length;
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -340,32 +340,34 @@ AtomizeAndCopyChars(JSContext* cx, const
         if (pp) {
             JSAtom* atom = pp->asPtr(cx);
             if (zonePtr)
                 mozilla::Unused << zone->atomCache().add(*zonePtr, AtomStateEntry(atom, false));
             return atom;
         }
     }
 
+    // Validate the length before taking the exclusive access lock, as throwing
+    // an exception here may reenter this code.
+    if (MOZ_UNLIKELY(!JSString::validateLength(cx, length)))
+        return nullptr;
+
     AutoLockForExclusiveAccess lock(cx);
 
     AtomSet& atoms = cx->atoms(lock);
     AtomSet::AddPtr p = atoms.lookupForAdd(lookup);
     if (p) {
         JSAtom* atom = p->asPtr(cx);
         p->setPinned(bool(pin));
         cx->atomMarking().inlinedMarkAtom(cx, atom);
         if (zonePtr)
             mozilla::Unused << zone->atomCache().add(*zonePtr, AtomStateEntry(atom, false));
         return atom;
     }
 
-    if (!JSString::validateLength(cx, length))
-        return nullptr;
-
     JSAtom* atom;
     {
         AutoAtomsCompartment ac(cx, lock);
 
         JSFlatString* flat = NewStringCopyN<NoGC>(cx, tbchars, length);
         if (!flat) {
             // Grudgingly forgo last-ditch GC. The alternative would be to release
             // the lock, manually GC here, and retry from the top. If you fix this,
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -42,16 +42,18 @@ SERVO_BINDING_FUNC(Servo_StyleSheet_Clea
                    RawGeckoURLExtraData* extra_data,
                    uint32_t line_number_offset)
 SERVO_BINDING_FUNC(Servo_StyleSheet_HasRules, bool,
                    RawServoStyleSheetBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSheet_GetRules, ServoCssRulesStrong,
                    RawServoStyleSheetBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSheet_Clone, RawServoStyleSheetStrong,
                    RawServoStyleSheetBorrowed sheet)
+SERVO_BINDING_FUNC(Servo_StyleSheet_SizeOfIncludingThis, size_t,
+                   mozilla::MallocSizeOf malloc_size_of, RawServoStyleSheetBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSet_Init, RawServoStyleSetOwned, RawGeckoPresContextOwned pres_context)
 SERVO_BINDING_FUNC(Servo_StyleSet_Clear, void,
                    RawServoStyleSetBorrowed set)
 SERVO_BINDING_FUNC(Servo_StyleSet_RebuildData, void,
                    RawServoStyleSetBorrowed set)
 SERVO_BINDING_FUNC(Servo_StyleSet_Drop, void, RawServoStyleSetOwned set)
 SERVO_BINDING_FUNC(Servo_StyleSet_AppendStyleSheet, void,
                    RawServoStyleSetBorrowed set,
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -316,16 +316,17 @@ raw-lines = [
     "type nsACString_internal = nsACString;",
     "type nsAString_internal = nsAString;",
 ]
 whitelist-functions = ["Servo_.*", "Gecko_.*"]
 structs-types = [
     "mozilla::css::GridTemplateAreasValue",
     "mozilla::css::ImageValue",
     "mozilla::css::URLValue",
+    "mozilla::MallocSizeOf",
     "mozilla::Side",
     "RawGeckoAnimationPropertySegment",
     "RawGeckoComputedTiming",
     "RawGeckoCSSPropertyIDList",
     "RawGeckoDocument",
     "RawGeckoElement",
     "RawGeckoKeyframeList",
     "RawGeckoComputedKeyframeValuesList",
--- a/layout/style/ServoStyleSheet.cpp
+++ b/layout/style/ServoStyleSheet.cpp
@@ -54,23 +54,23 @@ ServoStyleSheetInner::~ServoStyleSheetIn
 
 StyleSheetInfo*
 ServoStyleSheetInner::CloneFor(StyleSheet* aPrimarySheet)
 {
   return new ServoStyleSheetInner(*this,
                                   static_cast<ServoStyleSheet*>(aPrimarySheet));
 }
 
+MOZ_DEFINE_MALLOC_SIZE_OF(ServoStyleSheetMallocSizeOf)
+
 size_t
 ServoStyleSheetInner::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t n = aMallocSizeOf(this);
-
-  // XXX: need to measure mSheet
-
+  n += Servo_StyleSheet_SizeOfIncludingThis(ServoStyleSheetMallocSizeOf, mSheet);
   return n;
 }
 
 ServoStyleSheet::ServoStyleSheet(css::SheetParsingMode aParsingMode,
                                  CORSMode aCORSMode,
                                  net::ReferrerPolicy aReferrerPolicy,
                                  const dom::SRIMetadata& aIntegrity)
   : StyleSheet(StyleBackendType::Servo, aParsingMode)
--- a/layout/svg/crashtests/crashtests.list
+++ b/layout/svg/crashtests/crashtests.list
@@ -100,17 +100,17 @@ load 515288-1.html
 load 522394-1.svg
 load 522394-2.svg
 load 522394-3.svg
 load 566216-1.svg
 load 587336-1.html
 load 590291-1.svg
 load 601999-1.html
 load 605626-1.svg
-asserts(2) load 606914.xhtml # bug 606914, bug 1340561 tracks the stylo bit
+asserts(2) load 606914.xhtml # bug 606914
 load 610594-1.html
 load 610954-1.html
 load 612662-1.svg
 load 612662-2.svg
 load 612736-1.svg
 load 612736-2.svg
 load 614367-1.svg
 load 620034-1.html
--- a/layout/xul/test/browser.ini
+++ b/layout/xul/test/browser.ini
@@ -1,8 +1,8 @@
 [DEFAULT]
 
 [browser_bug685470.js]
 [browser_bug703210.js]
 [browser_bug706743.js]
-skip-if = (os == 'linux') # Bug 1157576
+skip-if = (os == 'linux') || e10s # Bug 1157576
 [browser_bug1163304.js]
 skip-if = os != 'linux' && os != 'win' // Due to testing menubar behavior with keyboard
--- a/media/libstagefright/moz.build
+++ b/media/libstagefright/moz.build
@@ -5,17 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "Audio/Video: Playback")
 
 DEFINES['ANDROID_SMP'] = 0
 DEFINES['LOG_NDEBUG'] = 1
 
-if CONFIG['OS_TARGET'] != 'WINNT':
+if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['OS_TARGET'] != 'SunOS':
    DEFINES['_GLIBCXX_OS_DEFINES'] = True
 
 if CONFIG['OS_TARGET'] == 'WINNT':
     if CONFIG['_MSC_VER']:
         DEFINES['ssize_t'] = 'intptr_t'
         DEFINES['off64_t'] = 'int64_t'
         DEFINES['strcasecmp'] = 'stricmp'
         DEFINES['strncasecmp'] = 'strnicmp'
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4838,16 +4838,18 @@ pref("extensions.webextensions.keepStora
 pref("extensions.webextensions.keepUuidOnUninstall", false);
 // Redirect basedomain used by identity api
 pref("extensions.webextensions.identity.redirectDomain", "extensions.allizom.org");
 // Whether or not webextension themes are supported.
 pref("extensions.webextensions.themes.enabled", false);
 pref("extensions.webextensions.themes.icons.enabled", false);
 pref("extensions.webextensions.remote", false);
 
+pref("layers.popups.compositing.enabled", false);
+
 // Report Site Issue button
 pref("extensions.webcompat-reporter.newIssueEndpoint", "https://webcompat.com/issues/new");
 #ifdef NIGHTLY_BUILD
 pref("extensions.webcompat-reporter.enabled", true);
 #else
 pref("extensions.webcompat-reporter.enabled", false);
 #endif
 
--- a/netwerk/cookie/CookieServiceChild.cpp
+++ b/netwerk/cookie/CookieServiceChild.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/net/CookieServiceChild.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/BasePrincipal.h"
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "mozilla/net/NeckoChild.h"
 #include "nsIChannel.h"
 #include "nsIURI.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsServiceManagerUtils.h"
 
@@ -42,16 +43,22 @@ NS_IMPL_ISUPPORTS(CookieServiceChild,
                   nsISupportsWeakReference)
 
 CookieServiceChild::CookieServiceChild()
   : mCookieBehavior(nsICookieService::BEHAVIOR_ACCEPT)
   , mThirdPartySession(false)
 {
   NS_ASSERTION(IsNeckoChild(), "not a child process");
 
+  mozilla::dom::ContentChild* cc =
+    static_cast<mozilla::dom::ContentChild*>(gNeckoChild->Manager());
+  if (cc->IsShuttingDown()) {
+    return;
+  }
+
   // This corresponds to Release() in DeallocPCookieService.
   NS_ADDREF_THIS();
 
   // Create a child PCookieService actor.
   NeckoChild::InitNeckoChild();
   gNeckoChild->SendPCookieServiceConstructor(this);
 
   // Init our prefs and observer.
--- a/netwerk/dns/DNSRequestChild.cpp
+++ b/netwerk/dns/DNSRequestChild.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set sw=2 ts=8 et tw=80 : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/net/ChildDNSService.h"
 #include "mozilla/net/DNSRequestChild.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/SystemGroup.h"
 #include "mozilla/Unused.h"
 #include "nsIDNSRecord.h"
 #include "nsHostResolver.h"
 #include "nsTArray.h"
@@ -218,16 +219,22 @@ DNSRequestChild::StartRequest()
     return;
   }
 
   nsCOMPtr<nsIEventTarget> systemGroupEventTarget
     = SystemGroup::EventTargetFor(TaskCategory::Other);
 
   gNeckoChild->SetEventTargetForActor(this, systemGroupEventTarget);
 
+  mozilla::dom::ContentChild* cc =
+    static_cast<mozilla::dom::ContentChild*>(gNeckoChild->Manager());
+  if (cc->IsShuttingDown()) {
+    return;
+  }
+
   // Send request to Parent process.
   gNeckoChild->SendPDNSRequestConstructor(this, mHost, mOriginAttributes,
                                           mFlags, mNetworkInterface);
   mIPCOpen = true;
 
   // IPDL holds a reference until IPDL channel gets destroyed
   AddIPDLReference();
 }
--- a/netwerk/ipc/NeckoChild.cpp
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -61,16 +61,19 @@ NeckoChild::~NeckoChild()
 void NeckoChild::InitNeckoChild()
 {
   MOZ_ASSERT(IsNeckoChild(), "InitNeckoChild called by non-child!");
 
   if (!gNeckoChild) {
     mozilla::dom::ContentChild * cpc = 
       mozilla::dom::ContentChild::GetSingleton();
     NS_ASSERTION(cpc, "Content Protocol is NULL!");
+    if (NS_WARN_IF(cpc->IsShuttingDown())) {
+      return;
+    }
     gNeckoChild = cpc->SendPNeckoConstructor(); 
     NS_ASSERTION(gNeckoChild, "PNecko Protocol init failed!");
   }
 }
 
 PHttpChannelChild*
 NeckoChild::AllocPHttpChannelChild(const PBrowserOrId& browser,
                                    const SerializedLoadContext& loadContext,
--- a/netwerk/protocol/data/DataChannelChild.cpp
+++ b/netwerk/protocol/data/DataChannelChild.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=4 sw=4 sts=4 et tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DataChannelChild.h"
 
 #include "mozilla/Unused.h"
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/net/NeckoChild.h"
 
 namespace mozilla {
 namespace net {
 
 NS_IMPL_ISUPPORTS_INHERITED(DataChannelChild, nsDataChannel, nsIChildChannel)
 
 DataChannelChild::DataChannelChild(nsIURI* aURI)
@@ -22,16 +23,22 @@ DataChannelChild::DataChannelChild(nsIUR
 
 DataChannelChild::~DataChannelChild()
 {
 }
 
 NS_IMETHODIMP
 DataChannelChild::ConnectParent(uint32_t aId)
 {
+    mozilla::dom::ContentChild* cc =
+        static_cast<mozilla::dom::ContentChild*>(gNeckoChild->Manager());
+    if (cc->IsShuttingDown()) {
+        return NS_ERROR_FAILURE;
+    }
+
     if (!gNeckoChild->SendPDataChannelConstructor(this, aId)) {
         return NS_ERROR_FAILURE;
     }
 
     // IPC now has a ref to us.
     AddIPDLReference();
     return NS_OK;
 }
--- a/netwerk/protocol/file/FileChannelChild.cpp
+++ b/netwerk/protocol/file/FileChannelChild.cpp
@@ -2,32 +2,39 @@
 /* vim: set ts=2 sw=2 sts=2 et tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FileChannelChild.h"
 
 #include "mozilla/Unused.h"
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/net/NeckoChild.h"
 
 namespace mozilla {
 namespace net {
 
 NS_IMPL_ISUPPORTS_INHERITED(FileChannelChild, nsFileChannel, nsIChildChannel)
 
 FileChannelChild::FileChannelChild(nsIURI *uri)
   : nsFileChannel(uri)
   , mIPCOpen(false)
 {
 }
 
 NS_IMETHODIMP
 FileChannelChild::ConnectParent(uint32_t id)
 {
+  mozilla::dom::ContentChild* cc =
+    static_cast<mozilla::dom::ContentChild*>(gNeckoChild->Manager());
+  if (cc->IsShuttingDown()) {
+    return NS_ERROR_FAILURE;
+  }
+
   if (!gNeckoChild->SendPFileChannelConstructor(this, id)) {
     return NS_ERROR_FAILURE;
   }
 
   AddIPDLReference();
   return NS_OK;
 }
 
--- a/netwerk/protocol/ftp/FTPChannelChild.cpp
+++ b/netwerk/protocol/ftp/FTPChannelChild.cpp
@@ -19,16 +19,17 @@
 #include "nsNetUtil.h"
 #include "base/compiler_specific.h"
 #include "mozilla/ipc/IPCStreamUtils.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "SerializedLoadContext.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "nsIPrompt.h"
 
+using mozilla::dom::ContentChild;
 using namespace mozilla::ipc;
 
 #undef LOG
 #define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
 
 namespace mozilla {
 namespace net {
 
@@ -148,16 +149,18 @@ FTPChannelChild::GetUploadStream(nsIInpu
 }
 
 NS_IMETHODIMP
 FTPChannelChild::AsyncOpen(::nsIStreamListener* listener, nsISupports* aContext)
 {
   LOG(("FTPChannelChild::AsyncOpen [this=%p]\n", this));
 
   NS_ENSURE_TRUE((gNeckoChild), NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(!static_cast<ContentChild*>(gNeckoChild->Manager())->
+                   IsShuttingDown(), NS_ERROR_FAILURE);
   NS_ENSURE_ARG_POINTER(listener);
   NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
   NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
 
   // Port checked in parent, but duplicate here so we can return with error
   // immediately, as we've done since before e10s.
   nsresult rv;
   rv = NS_CheckPortSafety(nsBaseChannel::URI()); // Need to disambiguate,
@@ -183,17 +186,17 @@ FTPChannelChild::AsyncOpen(::nsIStreamLi
   mListenerContext = aContext;
 
   // add ourselves to the load group. 
   if (mLoadGroup)
     mLoadGroup->AddRequest(this, nullptr);
 
   mozilla::ipc::AutoIPCStream autoStream;
   autoStream.Serialize(mUploadStream,
-                       static_cast<mozilla::dom::ContentChild*>(gNeckoChild->Manager()));
+                       static_cast<ContentChild*>(gNeckoChild->Manager()));
 
   FTPChannelOpenArgs openArgs;
   SerializeURI(nsBaseChannel::URI(), openArgs.uri());
   openArgs.startPos() = mStartPos;
   openArgs.entityID() = mEntityID;
   openArgs.uploadStream() = autoStream.TakeOptionalValue();
 
   nsCOMPtr<nsILoadInfo> loadInfo;
@@ -868,16 +871,20 @@ FTPChannelChild::Resume()
 
 //-----------------------------------------------------------------------------
 // FTPChannelChild::nsIChildChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 FTPChannelChild::ConnectParent(uint32_t id)
 {
+  NS_ENSURE_TRUE((gNeckoChild), NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(!static_cast<ContentChild*>(gNeckoChild->Manager())->
+                   IsShuttingDown(), NS_ERROR_FAILURE);
+
   LOG(("FTPChannelChild::ConnectParent [this=%p]\n", this));
 
   mozilla::dom::TabChild* tabChild = nullptr;
   nsCOMPtr<nsITabChild> iTabChild;
   NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
                                 NS_GET_IID(nsITabChild),
                                 getter_AddRefs(iTabChild));
   GetCallback(iTabChild);
@@ -931,16 +938,18 @@ FTPChannelChild::CompleteRedirectSetup(n
 // FTPChannelChild::nsIDivertableChannel
 //-----------------------------------------------------------------------------
 NS_IMETHODIMP
 FTPChannelChild::DivertToParent(ChannelDiverterChild **aChild)
 {
   MOZ_RELEASE_ASSERT(aChild);
   MOZ_RELEASE_ASSERT(gNeckoChild);
   MOZ_RELEASE_ASSERT(!mDivertingToParent);
+  NS_ENSURE_TRUE(!static_cast<ContentChild*>(gNeckoChild->Manager())->
+                   IsShuttingDown(), NS_ERROR_FAILURE);
 
   LOG(("FTPChannelChild::DivertToParent [this=%p]\n", this));
 
   // We must fail DivertToParent() if there's no parent end of the channel (and
   // won't be!) due to early failure.
   if (NS_FAILED(mStatus) && !mIPCOpen) {
     return mStatus;
   }
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1818,16 +1818,21 @@ HttpChannelChild::ConnectParent(uint32_t
   if (MissingRequiredTabChild(tabChild, "http")) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   if (tabChild && !tabChild->IPCOpen()) {
     return NS_ERROR_FAILURE;
   }
 
+  ContentChild* cc = static_cast<ContentChild*>(gNeckoChild->Manager());
+  if (cc->IsShuttingDown()) {
+    return NS_ERROR_FAILURE;
+  }
+
   HttpBaseChannel::SetDocshellUserAgentOverride();
 
   // The socket transport in the chrome process now holds a logical ref to us
   // until OnStopRequest, or we do a redirect, or we hit an IPDL error.
   AddIPDLReference();
 
   // This must happen before the constructor message is sent. Otherwise messages
   // from the parent could arrive quickly and be delivered to the wrong event
@@ -2660,16 +2665,19 @@ HttpChannelChild::GetAlternativeDataType
 NS_IMETHODIMP
 HttpChannelChild::OpenAlternativeOutputStream(const nsACString & aType, nsIOutputStream * *_retval)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Main thread only");
 
   if (!mIPCOpen) {
     return NS_ERROR_NOT_AVAILABLE;
   }
+  if (static_cast<ContentChild*>(gNeckoChild->Manager())->IsShuttingDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
 
   RefPtr<AltDataOutputStreamChild> stream =
     static_cast<AltDataOutputStreamChild*>(gNeckoChild->SendPAltDataOutputStreamConstructor(nsCString(aType), this));
   stream.forget(_retval);
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
@@ -2991,16 +2999,19 @@ HttpChannelChild::DivertToParent(Channel
 
   // Once this is set, it should not be unset before the child is taken down.
   mDivertingToParent = true;
 
   rv = Suspend();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
+  if (static_cast<ContentChild*>(gNeckoChild->Manager())->IsShuttingDown()) {
+    return NS_ERROR_FAILURE;
+  }
 
   HttpChannelDiverterArgs args;
   args.mChannelChild() = this;
   args.mApplyConversion() = mApplyConversion;
 
   PChannelDiverterChild* diverter =
     gNeckoChild->SendPChannelDiverterConstructor(args);
   MOZ_RELEASE_ASSERT(diverter);
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-57e38a8407b3
+29290a4a9bd0
new file mode 100644
new file mode 100644
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/security/nss/automation/abi-check/expected-report-libnss3.so.txt
@@ -0,0 +1,12 @@
+Functions changes summary: 0 Removed, 0 Changed, 6 Added functions
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+6 Added functions:
+
+  'function SECStatus CERT_GetCertIsPerm(const CERTCertificate*, PRBool*)'    {CERT_GetCertIsPerm@@NSS_3.31}
+  'function SECStatus CERT_GetCertIsTemp(const CERTCertificate*, PRBool*)'    {CERT_GetCertIsTemp@@NSS_3.31}
+  'function CERTCertificate* PK11_FindCertFromURI(const char*, void*)'    {PK11_FindCertFromURI@@NSS_3.31}
+  'function CERTCertList* PK11_FindCertsFromURI(const char*, void*)'    {PK11_FindCertsFromURI@@NSS_3.31}
+  'function char* PK11_GetModuleURI(SECMODModule*)'    {PK11_GetModuleURI@@NSS_3.31}
+  'function char* PK11_GetTokenURI()'    {PK11_GetTokenURI@@NSS_3.31}
+
new file mode 100644
new file mode 100644
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/security/nss/automation/abi-check/expected-report-libnssutil3.so.txt
@@ -0,0 +1,12 @@
+Functions changes summary: 0 Removed, 0 Changed, 6 Added functions
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+6 Added functions:
+
+  'function void PK11URI_CreateURI(size_t, size_t)'    {PK11URI_CreateURI@@NSSUTIL_3.31}
+  'function void PK11URI_DestroyURI()'    {PK11URI_DestroyURI@@NSSUTIL_3.31}
+  'function char* PK11URI_FormatURI()'    {PK11URI_FormatURI@@NSSUTIL_3.31}
+  'function const char* PK11URI_GetPathAttribute(const char*)'    {PK11URI_GetPathAttribute@@NSSUTIL_3.31}
+  'function const char* PK11URI_GetQueryAttribute(const char*)'    {PK11URI_GetQueryAttribute@@NSSUTIL_3.31}
+  'function void PK11URI_ParseURI(const char*)'    {PK11URI_ParseURI@@NSSUTIL_3.31}
+
new file mode 100644
new file mode 100644
new file mode 100644
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/security/nss/automation/abi-check/expected-report-libssl3.so.txt
@@ -0,0 +1,14 @@
+Functions changes summary: 0 Removed, 1 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+1 function with some indirect sub-type change:
+
+  [C]'function SECStatus SSL_GetPreliminaryChannelInfo(SSLPreliminaryChannelInfo*, PRUintn)' at sslinfo.c:115:1 has some indirect sub-type changes:
+    parameter 1 of type 'SSLPreliminaryChannelInfo*' has sub-type changes:
+      in pointed to type 'typedef SSLPreliminaryChannelInfo' at sslt.h:318:1:
+        underlying type 'struct SSLPreliminaryChannelInfoStr' at sslt.h:287:1 changed:
+          type size changed from 128 to 160 bits
+          1 data member insertion:
+            'PRUint32 SSLPreliminaryChannelInfoStr::maxEarlyDataSize', at offset 128 (in bits) at sslt.h:314:1
+
+
new file mode 100644
--- /dev/null
+++ b/security/nss/automation/abi-check/previous-nss-release
@@ -0,0 +1,1 @@
+NSS_3_30_BRANCH
--- a/security/nss/automation/buildbot-slave/build.sh
+++ b/security/nss/automation/buildbot-slave/build.sh
@@ -14,16 +14,19 @@ proc_args()
 
         case $OPT in
             "--build-nss")
                 BUILD_NSS=1
                 ;;
             "--test-nss")
                 TEST_NSS=1
                 ;;
+            "--check-abi")
+                CHECK_ABI=1
+                ;;
             "--build-jss")
                 BUILD_JSS=1
                 ;;
             "--test-jss")
                 TEST_JSS=1
                 ;;
             "--memtest")
                 NSS_TESTS="memleak"
@@ -35,16 +38,17 @@ proc_args()
             *)
                 echo "Usage: $0 ..."
                 echo "    --memtest   - run the memory leak tests"
                 echo "    --nojsssign - try to sign jss"
                 echo "    --build-nss"
                 echo "    --build-jss"
                 echo "    --test-nss"
                 echo "    --test-jss"
+                echo "    --check-abi"
                 exit 1
                 ;;
         esac 
 
         shift
     done
 }
 
@@ -210,16 +214,75 @@ test_nss()
     print_log "######## details of detected failures (if any) ########"
     grep -B50 FAILED ${OUTPUTFILE}
     [ $? -eq 1 ] || RET=1
 
     print_result "NSS - tests - ${BITS} bits - ${OPT}" ${RET} 0
     return ${RET}
 }
 
+check_abi()
+{
+    print_log "######## NSS ABI CHECK - ${BITS} bits - ${OPT} ########"
+    print_log "######## creating temporary HG clones ########"
+
+    rm -rf ${HGDIR}/baseline
+    mkdir ${HGDIR}/baseline
+    BASE_NSS=`cat ${HGDIR}/nss/automation/abi-check/previous-nss-release`
+    hg clone -u "${BASE_NSS}" "${HGDIR}/nss" "${HGDIR}/baseline/nss"
+    if [ $? -ne 0 ]; then
+        echo "invalid tag in automation/abi-check/previous-nss-release"
+        return 1
+    fi
+
+    BASE_NSPR=NSPR_$(head -1 ${HGDIR}/baseline/nss/automation/release/nspr-version.txt | cut -d . -f 1-2 | tr . _)_BRANCH
+    hg clone -u "${BASE_NSPR}" "${HGDIR}/nspr" "${HGDIR}/baseline/nspr"
+    if [ $? -ne 0 ]; then
+        echo "invalid tag ${BASE_NSPR} derived from ${BASE_NSS} automation/release/nspr-version.txt"
+        return 1
+    fi
+
+    print_log "######## building older NSPR/NSS ########"
+    pushd ${HGDIR}/baseline/nss
+
+    print_log "$ ${MAKE} ${NSS_BUILD_TARGET}"
+    ${MAKE} ${NSS_BUILD_TARGET} 2>&1 | tee -a ${LOG_ALL}
+    RET=$?
+    print_result "NSS - build - ${BITS} bits - ${OPT}" ${RET} 0
+    if [ ${RET} -ne 0 ]; then
+        tail -100 ${LOG_ALL}
+        return ${RET}
+    fi
+    popd
+
+    ABI_REPORT=${OUTPUTDIR}/abi-diff.txt
+    rm -f ${ABI_REPORT}
+    PREVDIST=${HGDIR}/baseline/dist
+    NEWDIST=${HGDIR}/dist
+    ALL_SOs="libfreebl3.so libfreeblpriv3.so libnspr4.so libnss3.so libnssckbi.so libnssdbm3.so libnsssysinit.so libnssutil3.so libplc4.so libplds4.so libsmime3.so libsoftokn3.so libssl3.so"
+    for SO in ${ALL_SOs}; do
+        if [ ! -f nss/automation/abi-check/expected-report-$SO.txt ]; then
+            touch nss/automation/abi-check/expected-report-$SO.txt
+        fi
+        abidiff --hd1 $PREVDIST/public/ --hd2 $NEWDIST/public \
+            $PREVDIST/*/lib/$SO $NEWDIST/*/lib/$SO \
+            > nss/automation/abi-check/new-report-$SO.txt
+        diff -u nss/automation/abi-check/expected-report-$SO.txt \
+                nss/automation/abi-check/new-report-$SO.txt >> ${ABI_REPORT}
+    done
+
+    if [ -s ${ABI_REPORT} ]; then
+        print_log "FAILED: there are new unexpected ABI changes"
+        cat ${ABI_REPORT}
+        return 1
+    fi
+
+    return 0
+}
+
 test_jss()
 {
     print_log "######## JSS - tests - ${BITS} bits - ${OPT} ########"
 
     print_log "$ cd ${HGDIR}/jss"
     cd ${HGDIR}/jss
 
     print_log "$ ${MAKE} platform"
@@ -283,16 +346,21 @@ build_and_test()
         [ $? -eq 0 ] || return 1
     fi
 
     if [ -n "${TEST_NSS}" ]; then
         test_nss
         [ $? -eq 0 ] || return 1
     fi
 
+    if [ -n "${CHECK_ABI}" ]; then
+        check_abi
+        [ $? -eq 0 ] || return 1
+    fi
+
     if [ -n "${BUILD_JSS}" ]; then
         create_objdir_dist_link
         build_jss
         [ $? -eq 0 ] || return 1
     fi
 
     if [ -n "${TEST_JSS}" ]; then
         test_jss
@@ -355,38 +423,42 @@ run_all()
     move_results
     return ${RESULT}
 }
 
 main()
 {
     VALID=0
     RET=1
+    FAIL=0
 
     for BITS in 32 64; do
         echo ${RUN_BITS} | grep ${BITS} > /dev/null
         [ $? -eq 0 ] || continue
         for OPT in DBG OPT; do
             echo ${RUN_OPT} | grep ${OPT} > /dev/null
             [ $? -eq 0 ] || continue
 
             VALID=1
             set_env
             run_all
             RET=$?
-	    print_log "### result of run_all is ${RET}"
+            print_log "### result of run_all is ${RET}"
+            if [ ${RET} -ne 0 ]; then
+                FAIL=${RET}
+            fi
         done
     done
 
     if [ ${VALID} -ne 1 ]; then
         echo "Need to set valid bits/opt values."
         return 1
     fi
 
-    return ${RET}
+    return ${FAIL}
 }
 
 #function killallsub()
 #{
 #    FINAL_RET=$?
 #    for proc in `jobs -p`
 #    do
 #        kill -9 $proc
@@ -404,11 +476,13 @@ main()
 
 #touch $IS_RUNNING_FILE
 
 echo "tinderbox args: $0 $@"
 . ${ENVVARS}
 proc_args "$@"
 main
 
-#RET=$?
+RET=$?
+print_log "### result of main is ${RET}"
+
 #rm $IS_RUNNING_FILE
-#exit ${RET}
+exit ${RET}
new file mode 100644
--- /dev/null
+++ b/security/nss/automation/release/nspr-version.txt
@@ -0,0 +1,10 @@
+4.15
+
+# The first line of this file must contain the human readable NSPR
+# version number, which is the minimum required version of NSPR
+# that is supported by this version of NSS.
+#
+# This information is used by release automation,
+# when creating an NSS source archive.
+#
+# All other lines in this file are ignored.
--- a/security/nss/automation/release/nss-release-helper.py
+++ b/security/nss/automation/release/nss-release-helper.py
@@ -166,21 +166,23 @@ def set_4_digit_release_number():
     major = args[1].strip()
     minor = args[2].strip()
     patch = args[3].strip()
     build = args[4].strip()
     version = major + '.' + minor + '.' + patch + '.' + build
     set_all_lib_versions(version, major, minor, patch, build)
 
 def create_nss_release_archive():
-    ensure_arguments_after_action(4, "nss_release_version  nss_hg_release_tag  nspr_release_version  path_to_stage_directory")
+    ensure_arguments_after_action(3, "nss_release_version  nss_hg_release_tag  path_to_stage_directory")
     nssrel = args[1].strip() #e.g. 3.19.3
     nssreltag = args[2].strip() #e.g. NSS_3_19_3_RTM
-    nsprrel = args[3].strip() #e.g. 4.10.8
-    stagedir = args[4].strip() #e.g. ../stage
+    stagedir = args[3].strip() #e.g. ../stage
+
+    with open('automation/release/nspr-version.txt') as nspr_version_file:
+        nsprrel = next(nspr_version_file).strip()
 
     nspr_tar = "nspr-" + nsprrel + ".tar.gz"
     nsprtar_with_path= stagedir + "/v" + nsprrel + "/src/" + nspr_tar
     if (not os.path.exists(nsprtar_with_path)):
         exit_with_failure("cannot find nspr archive at expected location " + nsprtar_with_path)
 
     nss_stagedir= stagedir + "/" + nssreltag + "/src"
     if (os.path.exists(nss_stagedir)):
--- a/security/nss/automation/taskcluster/docker-fuzz/setup.sh
+++ b/security/nss/automation/taskcluster/docker-fuzz/setup.sh
@@ -17,25 +17,34 @@ apt_packages+=('git')
 apt_packages+=('gyp')
 apt_packages+=('libssl-dev')
 apt_packages+=('libxml2-utils')
 apt_packages+=('locales')
 apt_packages+=('ninja-build')
 apt_packages+=('pkg-config')
 apt_packages+=('zlib1g-dev')
 
+# 32-bit builds
+apt_packages+=('gcc-multilib')
+apt_packages+=('g++-multilib')
+
 # Latest Mercurial.
 apt_packages+=('mercurial')
 apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 41BD8711B1F0EC2B0D85B91CF59CE3A8323293EE
 echo "deb http://ppa.launchpad.net/mercurial-ppa/releases/ubuntu xenial main" > /etc/apt/sources.list.d/mercurial.list
 
 # Install packages.
 apt-get -y update
 apt-get install -y --no-install-recommends ${apt_packages[@]}
 
+# 32-bit builds
+dpkg --add-architecture i386
+apt-get -y update
+apt-get install -y --no-install-recommends libssl-dev:i386
+
 # Install LLVM/clang-4.0.
 mkdir clang-tmp
 git clone -n --depth 1 https://chromium.googlesource.com/chromium/src/tools/clang clang-tmp/clang
 git -C clang-tmp/clang checkout HEAD scripts/update.py
 clang-tmp/clang/scripts/update.py
 rm -fr clang-tmp
 
 locale-gen en_US.UTF-8
--- a/security/nss/automation/taskcluster/graph/src/extend.js
+++ b/security/nss/automation/taskcluster/graph/src/extend.js
@@ -58,21 +58,16 @@ queue.filter(task => {
     return false;
   }
 
   if (task.group == "Test") {
     // Don't run test builds on old make platforms
     if (task.collection == "make") {
       return false;
     }
-
-    // Disable mpi tests for now on 32-bit builds (bug 1362392)
-    if (task.platform == "linux32") {
-      return false;
-    }
   }
 
 
   // Don't run additional hardware tests on ARM (we don't have anything there).
   if (task.group == "Cipher" && task.platform == "aarch64" && task.env &&
       (task.env.NSS_DISABLE_PCLMUL == "1" || task.env.NSS_DISABLE_HW_AES == "1"
        || task.env.NSS_DISABLE_AVX == "1")) {
     return false;
@@ -163,16 +158,17 @@ export default async function main() {
     env: {BUILD_OPT: "1"}
   });
 
   await scheduleWindows("Windows 2012 64 (debug)", {
     collection: "debug"
   });
 
   await scheduleFuzzing();
+  await scheduleFuzzing32();
 
   await scheduleTools();
 
   let aarch64_base = {
     image: "franziskus/nss-aarch64-ci",
     provisioner: "localprovisioner",
     workerType: "nss-aarch64",
     platform: "aarch64",
@@ -410,16 +406,120 @@ async function scheduleFuzzing() {
   scheduleFuzzingRun(tls_fm_base, "TLS Client", "tls-client", 20000, "client");
   scheduleFuzzingRun(tls_fm_base, "TLS Server", "tls-server", 20000, "server");
   scheduleFuzzingRun(tls_fm_base, "DTLS Client", "dtls-client", 20000, "dtls-client");
   scheduleFuzzingRun(tls_fm_base, "DTLS Server", "dtls-server", 20000, "dtls-server");
 
   return queue.submit();
 }
 
+async function scheduleFuzzing32() {
+  let base = {
+    env: {
+      ASAN_OPTIONS: "allocator_may_return_null=1:detect_stack_use_after_return=1",
+      UBSAN_OPTIONS: "print_stacktrace=1",
+      NSS_DISABLE_ARENA_FREE_LIST: "1",
+      NSS_DISABLE_UNLOAD: "1",
+      CC: "clang",
+      CCC: "clang++"
+    },
+    features: ["allowPtrace"],
+    platform: "linux32",
+    collection: "fuzz",
+    image: FUZZ_IMAGE
+  };
+
+  // Build base definition.
+  let build_base = merge({
+    command: [
+      "/bin/bash",
+      "-c",
+      "bin/checkout.sh && " +
+      "nss/automation/taskcluster/scripts/build_gyp.sh -g -v --fuzz -m32"
+    ],
+    artifacts: {
+      public: {
+        expires: 24 * 7,
+        type: "directory",
+        path: "/home/worker/artifacts"
+      }
+    },
+    kind: "build",
+    symbol: "B"
+  }, base);
+
+  // The task that builds NSPR+NSS.
+  let task_build = queue.scheduleTask(merge(build_base, {
+    name: "Linux 32 (debug, fuzz)"
+  }));
+
+  // The task that builds NSPR+NSS (TLS fuzzing mode).
+  let task_build_tls = queue.scheduleTask(merge(build_base, {
+    name: "Linux 32 (debug, TLS fuzz)",
+    symbol: "B",
+    group: "TLS",
+    command: [
+      "/bin/bash",
+      "-c",
+      "bin/checkout.sh && " +
+      "nss/automation/taskcluster/scripts/build_gyp.sh -g -v --fuzz=tls -m32"
+    ],
+  }));
+
+  // Schedule tests.
+  queue.scheduleTask(merge(base, {
+    parent: task_build_tls,
+    name: "Gtests",
+    command: [
+      "/bin/bash",
+      "-c",
+      "bin/checkout.sh && nss/automation/taskcluster/scripts/run_tests.sh"
+    ],
+    env: {GTESTFILTER: "*Fuzz*"},
+    tests: "ssl_gtests gtests",
+    cycle: "standard",
+    symbol: "Gtest",
+    kind: "test"
+  }));
+
+  // Schedule fuzzing runs.
+  let run_base = merge(base, {parent: task_build, kind: "test"});
+  scheduleFuzzingRun(run_base, "CertDN", "certDN", 4096);
+  scheduleFuzzingRun(run_base, "QuickDER", "quickder", 10000);
+
+  // Schedule MPI fuzzing runs.
+  let mpi_base = merge(run_base, {group: "MPI"});
+  let mpi_names = ["add", "addmod", "div", "expmod", "mod", "mulmod", "sqr",
+                   "sqrmod", "sub", "submod"];
+  for (let name of mpi_names) {
+    scheduleFuzzingRun(mpi_base, `MPI (${name})`, `mpi-${name}`, 4096, name);
+  }
+  scheduleFuzzingRun(mpi_base, `MPI (invmod)`, `mpi-invmod`, 256, "invmod");
+
+  // Schedule TLS fuzzing runs (non-fuzzing mode).
+  let tls_base = merge(run_base, {group: "TLS"});
+  scheduleFuzzingRun(tls_base, "TLS Client", "tls-client", 20000, "client-nfm",
+                     "tls-client-no_fuzzer_mode");
+  scheduleFuzzingRun(tls_base, "TLS Server", "tls-server", 20000, "server-nfm",
+                     "tls-server-no_fuzzer_mode");
+  scheduleFuzzingRun(tls_base, "DTLS Client", "dtls-client", 20000,
+                     "dtls-client-nfm", "dtls-client-no_fuzzer_mode");
+  scheduleFuzzingRun(tls_base, "DTLS Server", "dtls-server", 20000,
+                     "dtls-server-nfm", "dtls-server-no_fuzzer_mode");
+
+  // Schedule TLS fuzzing runs (fuzzing mode).
+  let tls_fm_base = merge(tls_base, {parent: task_build_tls});
+  scheduleFuzzingRun(tls_fm_base, "TLS Client", "tls-client", 20000, "client");
+  scheduleFuzzingRun(tls_fm_base, "TLS Server", "tls-server", 20000, "server");
+  scheduleFuzzingRun(tls_fm_base, "DTLS Client", "dtls-client", 20000, "dtls-client");
+  scheduleFuzzingRun(tls_fm_base, "DTLS Server", "dtls-server", 20000, "dtls-server");
+
+  return queue.submit();
+}
+
 /*****************************************************************************/
 
 async function scheduleTestBuilds(base, args = "") {
   // Build base definition.
   let build = merge({
     command: [
       "/bin/bash",
       "-c",
--- a/security/nss/automation/taskcluster/graph/src/try_syntax.js
+++ b/security/nss/automation/taskcluster/graph/src/try_syntax.js
@@ -18,17 +18,17 @@ function parseOptions(opts) {
 
   // If the given value is nonsense default to debug and opt builds.
   if (builds.length == 0) {
     builds = ["d", "o"];
   }
 
   // Parse platforms.
   let allPlatforms = ["linux", "linux64", "linux64-asan", "win64",
-                      "linux64-make", "linux-make", "linux64-fuzz", "aarch64"];
+                      "linux64-make", "linux-make", "linux-fuzz", "linux64-fuzz", "aarch64"];
   let platforms = intersect(opts.platform.split(/\s*,\s*/), allPlatforms);
 
   // If the given value is nonsense or "none" default to all platforms.
   if (platforms.length == 0 && opts.platform != "none") {
     platforms = allPlatforms;
   }
 
   // Parse unit tests.
@@ -99,32 +99,33 @@ function filter(opts) {
     }
 
     let coll = name => name == (task.collection || "opt");
 
     // Filter by platform.
     let found = opts.platforms.some(platform => {
       let aliases = {
         "linux": "linux32",
+        "linux-fuzz": "linux32",
         "linux64-asan": "linux64",
         "linux64-fuzz": "linux64",
         "linux64-make": "linux64",
         "linux-make": "linux32",
         "win64": "windows2012-64"
       };
 
       // Check the platform name.
       let keep = (task.platform == (aliases[platform] || platform));
 
       // Additional checks.
       if (platform == "linux64-asan") {
         keep &= coll("asan");
       } else if (platform == "linux64-make" || platform == "linux-make") {
         keep &= coll("make");
-      } else if (platform == "linux64-fuzz") {
+      } else if (platform == "linux64-fuzz" || platform == "linux-fuzz") {
         keep &= coll("fuzz");
       } else {
         keep &= coll("opt") || coll("debug");
       }
 
       return keep;
     });
 
--- a/security/nss/cmd/crmftest/testcrmf.c
+++ b/security/nss/cmd/crmftest/testcrmf.c
@@ -1256,21 +1256,23 @@ DoChallengeResponse(SECKEYPrivateKey *pr
     for (i = 0; i < numChallengesSet; i++) {
         publicValue = CMMF_POPODecKeyChallContentGetPublicValue(chalContent, i);
         if (publicValue == NULL) {
             printf("Could not get the public value for challenge at index %d\n",
                    i);
             return 908;
         }
         keyID = PK11_MakeIDFromPubKey(publicValue);
+        SECITEM_FreeItem(publicValue, PR_TRUE);
         if (keyID == NULL) {
             printf("Could not make the keyID from the public value\n");
             return 909;
         }
         foundPrivKey = PK11_FindKeyByKeyID(privKey->pkcs11Slot, keyID, &pwdata);
+        SECITEM_FreeItem(keyID, PR_TRUE);
         if (foundPrivKey == NULL) {
             printf("Could not find the private key corresponding to the public"
                    " value.\n");
             return 910;
         }
         rv = CMMF_POPODecKeyChallContDecryptChallenge(chalContent, i,
                                                       foundPrivKey);
         if (rv != SECSuccess) {
--- a/security/nss/cmd/mpitests/mpitests.gyp
+++ b/security/nss/cmd/mpitests/mpitests.gyp
@@ -26,14 +26,25 @@
         '<(DEPTH)/lib/pki/pki.gyp:nsspki',
       ]
     }
   ],
   'target_defaults': {
     'include_dirs': [
       '<(DEPTH)/lib/freebl/mpi',
       '<(DEPTH)/lib/util',
-    ]
+    ],
+    # This uses test builds and has to set defines for MPI.
+    'conditions': [
+      [ 'target_arch=="ia32"', {
+        'defines': [
+          'MP_USE_UINT_DIGIT',
+          'MP_ASSEMBLY_MULTIPLY',
+          'MP_ASSEMBLY_SQUARE',
+          'MP_ASSEMBLY_DIV_2DX1D',
+        ],
+      }],
+    ],
   },
   'variables': {
     'module': 'nss'
   }
 }
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,9 +5,8 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
-
--- a/security/nss/coreconf/fuzz.sh
+++ b/security/nss/coreconf/fuzz.sh
@@ -19,17 +19,20 @@ gyp_params+=(-Dtest_build=1 -Dfuzz=1 -Ds
 
 # Add debug symbols even for opt builds.
 nspr_params+=(--enable-debug-symbols)
 
 if [ "$fuzz_oss" = 1 ]; then
   gyp_params+=(-Dno_zdefs=1 -Dfuzz_oss=1)
 else
   enable_sanitizer asan
-  enable_ubsan
+  # Ubsan doesn't build on 32-bit at the moment. Disable it.
+  if [ "$build_64" = 1 ]; then
+    enable_ubsan
+  fi
   enable_sancov
 fi
 
 if [ "$fuzz_tls" = 1 ]; then
   gyp_params+=(-Dfuzz_tls=1)
 fi
 
 if [ ! -f "/usr/lib/libFuzzingEngine.a" ]; then
--- a/security/nss/fuzz/fuzz.gyp
+++ b/security/nss/fuzz/fuzz.gyp
@@ -83,16 +83,25 @@
             'libraries': [
               '/usr/lib/x86_64-linux-gnu/libcrypto.a',
             ],
           }, {
             'libraries': [
               '-lcrypto',
             ],
           }],
+          # For test builds we have to set MPI defines.
+          [ 'target_arch=="ia32"', {
+            'defines': [
+              'MP_USE_UINT_DIGIT',
+              'MP_ASSEMBLY_MULTIPLY',
+              'MP_ASSEMBLY_SQUARE',
+              'MP_ASSEMBLY_DIV_2DX1D',
+            ],
+          }],
         ],
       },
     },
     {
       'target_name': 'nssfuzz-pkcs8',
       'type': 'executable',
       'sources': [
         'asn1_mutators.cc',
--- a/security/nss/gtests/freebl_gtest/freebl_gtest.gyp
+++ b/security/nss/gtests/freebl_gtest/freebl_gtest.gyp
@@ -24,23 +24,16 @@
         '<(DEPTH)/lib/cryptohi/cryptohi.gyp:cryptohi',
         '<(DEPTH)/lib/certhigh/certhigh.gyp:certhi',
         '<(DEPTH)/lib/certdb/certdb.gyp:certdb',
         '<(DEPTH)/lib/base/base.gyp:nssb',
         '<(DEPTH)/lib/dev/dev.gyp:nssdev',
         '<(DEPTH)/lib/pki/pki.gyp:nsspki',
         '<(DEPTH)/lib/ssl/ssl.gyp:ssl',
       ],
-      'conditions': [
-        [ 'ct_verif==1', {
-          'defines': [
-            'CT_VERIF',
-          ],
-        }],
-      ],
     },
     {
       'target_name': 'prng_gtest',
       'type': 'executable',
       'sources': [
         'prng_kat_unittest.cc',
       ],
       'dependencies': [
@@ -52,19 +45,45 @@
         '<(DEPTH)/lib/cryptohi/cryptohi.gyp:cryptohi',
         '<(DEPTH)/lib/certhigh/certhigh.gyp:certhi',
         '<(DEPTH)/lib/certdb/certdb.gyp:certdb',
         '<(DEPTH)/lib/base/base.gyp:nssb',
         '<(DEPTH)/lib/dev/dev.gyp:nssdev',
         '<(DEPTH)/lib/pki/pki.gyp:nsspki',
         '<(DEPTH)/lib/ssl/ssl.gyp:ssl',
       ],
+      'conditions': [
+        [ 'OS=="win"', {
+          'libraries': [
+            'advapi32.lib',
+          ],
+        }],
+      ],
+      'defines': [
+        'NSS_USE_STATIC_LIBS'
+      ],
     },
   ],
   'target_defaults': {
     'include_dirs': [
       '<(DEPTH)/lib/freebl/mpi',
-    ]
+    ],
+    # For test builds we have to set MPI defines.
+    'conditions': [
+      [ 'ct_verif==1', {
+        'defines': [
+          'CT_VERIF',
+        ],
+      }],
+      [ 'target_arch=="ia32"', {
+        'defines': [
+          'MP_USE_UINT_DIGIT',
+          'MP_ASSEMBLY_MULTIPLY',
+          'MP_ASSEMBLY_SQUARE',
+          'MP_ASSEMBLY_DIV_2DX1D',
+        ],
+      }],
+    ],
   },
   'variables': {
     'module': 'nss'
   }
 }
--- a/security/nss/gtests/freebl_gtest/mpi_unittest.cc
+++ b/security/nss/gtests/freebl_gtest/mpi_unittest.cc
@@ -48,23 +48,49 @@ class MPITest : public ::testing::Test {
 
     mp_read_radix(&a, a_string.c_str(), 16);
     mp_read_radix(&b, b_string.c_str(), 16);
     EXPECT_EQ(result, mp_cmp(&a, &b));
 
     mp_clear(&a);
     mp_clear(&b);
   }
+
+  void TestDiv(const std::string a_string, const std::string b_string,
+               const std::string result) {
+    mp_int a, b, c;
+    MP_DIGITS(&a) = 0;
+    MP_DIGITS(&b) = 0;
+    MP_DIGITS(&c) = 0;
+    ASSERT_EQ(MP_OKAY, mp_init(&a));
+    ASSERT_EQ(MP_OKAY, mp_init(&b));
+    ASSERT_EQ(MP_OKAY, mp_init(&c));
+
+    mp_read_radix(&a, a_string.c_str(), 16);
+    mp_read_radix(&b, b_string.c_str(), 16);
+    mp_read_radix(&c, result.c_str(), 16);
+    EXPECT_EQ(MP_OKAY, mp_div(&a, &b, &a, &b));
+    EXPECT_EQ(0, mp_cmp(&a, &c));
+
+    mp_clear(&a);
+    mp_clear(&b);
+    mp_clear(&c);
+  }
 };
 
 TEST_F(MPITest, MpiCmp01Test) { TestCmp("0", "1", -1); }
 TEST_F(MPITest, MpiCmp10Test) { TestCmp("1", "0", 1); }
 TEST_F(MPITest, MpiCmp00Test) { TestCmp("0", "0", 0); }
 TEST_F(MPITest, MpiCmp11Test) { TestCmp("1", "1", 0); }
+TEST_F(MPITest, MpiDiv32ErrorTest) {
+  TestDiv("FFFF00FFFFFFFF000000000000", "FFFF00FFFFFFFFFF", "FFFFFFFFFF");
+}
 
+#ifdef NSS_X64
+// This tests assumes 64-bit mp_digits.
 TEST_F(MPITest, MpiCmpUnalignedTest) {
   mp_int a, b, c;
   MP_DIGITS(&a) = 0;
   MP_DIGITS(&b) = 0;
   MP_DIGITS(&c) = 0;
   ASSERT_EQ(MP_OKAY, mp_init(&a));
   ASSERT_EQ(MP_OKAY, mp_init(&b));
   ASSERT_EQ(MP_OKAY, mp_init(&c));
@@ -85,16 +111,17 @@ TEST_F(MPITest, MpiCmpUnalignedTest) {
   char c_tmp[40];
   ASSERT_EQ(MP_OKAY, mp_toradix(&c, c_tmp, 16));
   ASSERT_TRUE(strncmp(c_tmp, "feffffffffffffff100000000000000", 31));
 
   mp_clear(&a);
   mp_clear(&b);
   mp_clear(&c);
 }
+#endif
 
 // This test is slow. Disable it by default so we can run these tests on CI.
 class DISABLED_MPITest : public ::testing::Test {};
 
 TEST_F(DISABLED_MPITest, MpiCmpConstTest) {
   mp_int a, b, c;
   MP_DIGITS(&a) = 0;
   MP_DIGITS(&b) = 0;
--- a/security/nss/gtests/util_gtest/util_gtest.gyp
+++ b/security/nss/gtests/util_gtest/util_gtest.gyp
@@ -8,33 +8,43 @@
   ],
   'targets': [
     {
       'target_name': 'util_gtest',
       'type': 'executable',
       'sources': [
         'util_utf8_unittest.cc',
         'util_b64_unittest.cc',
-	'util_pkcs11uri_unittest.cc',
+        'util_pkcs11uri_unittest.cc',
         '<(DEPTH)/gtests/common/gtests.cc',
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports',
         '<(DEPTH)/gtests/google_test/google_test.gyp:gtest',
         '<(DEPTH)/lib/util/util.gyp:nssutil',
         '<(DEPTH)/lib/nss/nss.gyp:nss_static',
         '<(DEPTH)/lib/pk11wrap/pk11wrap.gyp:pk11wrap_static',
         '<(DEPTH)/lib/cryptohi/cryptohi.gyp:cryptohi',
         '<(DEPTH)/lib/certhigh/certhigh.gyp:certhi',
         '<(DEPTH)/lib/certdb/certdb.gyp:certdb',
         '<(DEPTH)/lib/base/base.gyp:nssb',
         '<(DEPTH)/lib/dev/dev.gyp:nssdev',
         '<(DEPTH)/lib/pki/pki.gyp:nsspki',
         '<(DEPTH)/lib/ssl/ssl.gyp:ssl',
-      ]
+      ],
+      'conditions': [
+        [ 'OS=="win"', {
+          'libraries': [
+            'advapi32.lib',
+          ],
+        }],
+      ],
+      'defines': [
+        'NSS_USE_STATIC_LIBS'
+      ],
     }
   ],
   'target_defaults': {
     'include_dirs': [
       '../../lib/util'
     ]
   },
   'variables': {
--- a/security/nss/gtests/util_gtest/util_pkcs11uri_unittest.cc
+++ b/security/nss/gtests/util_gtest/util_pkcs11uri_unittest.cc
@@ -26,35 +26,41 @@ class PK11URITest : public ::testing::Te
                           const PK11URIAttribute *qattrs, size_t num_qattrs) {
     ScopedPK11URI tmp(
         PK11URI_CreateURI(pattrs, num_pattrs, qattrs, num_qattrs));
     ASSERT_TRUE(tmp);
 
     size_t i;
     for (i = 0; i < num_pattrs; i++) {
       const char *value = PK11URI_GetPathAttribute(tmp.get(), pattrs[i].name);
-      ASSERT_TRUE(value);
-      ASSERT_EQ(std::string(value), std::string(pattrs[i].value));
+      EXPECT_TRUE(value);
+      if (value) {
+        EXPECT_EQ(std::string(value), std::string(pattrs[i].value));
+      }
     }
     for (i = 0; i < num_qattrs; i++) {
       const char *value = PK11URI_GetQueryAttribute(tmp.get(), qattrs[i].name);
-      ASSERT_TRUE(value);
-      ASSERT_EQ(std::string(value), std::string(qattrs[i].value));
+      EXPECT_TRUE(value);
+      if (value) {
+        EXPECT_EQ(std::string(value), std::string(qattrs[i].value));
+      }
     }
   }
 
   void TestCreateFormat(const PK11URIAttribute *pattrs, size_t num_pattrs,
                         const PK11URIAttribute *qattrs, size_t num_qattrs,
                         const std::string &formatted) {
     ScopedPK11URI tmp(
         PK11URI_CreateURI(pattrs, num_pattrs, qattrs, num_qattrs));
     ASSERT_TRUE(tmp);
     char *out = PK11URI_FormatURI(nullptr, tmp.get());
-    ASSERT_TRUE(out);
-    ASSERT_EQ(std::string(out), formatted);
+    EXPECT_TRUE(out);
+    if (out) {
+      EXPECT_EQ(std::string(out), formatted);
+    }
     PORT_Free(out);
   }
 
   bool TestParse(const std::string &str) {
     ScopedPK11URI tmp(PK11URI_ParseURI(str.c_str()));
     return tmp != nullptr;
   }
 
@@ -62,33 +68,39 @@ class PK11URITest : public ::testing::Te
                          size_t num_pattrs, const PK11URIAttribute *qattrs,
                          size_t num_qattrs) {
     ScopedPK11URI tmp(PK11URI_ParseURI(str.c_str()));
     ASSERT_TRUE(tmp);
 
     size_t i;
     for (i = 0; i < num_pattrs; i++) {
       const char *value = PK11URI_GetPathAttribute(tmp.get(), pattrs[i].name);
-      ASSERT_TRUE(value);
-      ASSERT_EQ(std::string(value), std::string(pattrs[i].value));
+      EXPECT_TRUE(value);
+      if (value) {
+        EXPECT_EQ(std::string(value), std::string(pattrs[i].value));
+      }
     }
     for (i = 0; i < num_qattrs; i++) {
       const char *value = PK11URI_GetQueryAttribute(tmp.get(), qattrs[i].name);
-      ASSERT_TRUE(value);
-      ASSERT_EQ(std::string(value), std::string(qattrs[i].value));
+      EXPECT_TRUE(value);
+      if (value) {
+        EXPECT_EQ(std::string(value), std::string(qattrs[i].value));
+      }
     }
   }
 
   void TestParseFormat(const std::string &str, const std::string &formatted) {
     ScopedPK11URI tmp(PK11URI_ParseURI(str.c_str()));
     ASSERT_TRUE(tmp);
     char *out = PK11URI_FormatURI(nullptr, tmp.get());
-    ASSERT_TRUE(out);
-    ASSERT_EQ(std::string(out), formatted);
-    PORT_Free(out);
+    EXPECT_TRUE(out);
+    if (out) {
+      EXPECT_EQ(std::string(out), formatted);
+      PORT_Free(out);
+    }
   }
 
  protected:
 };
 
 const PK11URIAttribute pattrs[] = {
     {"token", "aaa"}, {"manufacturer", "bbb"}, {"vendor", "ccc"}};
 
--- a/security/nss/lib/freebl/mpi/mpi.c
+++ b/security/nss/lib/freebl/mpi/mpi.c
@@ -2854,16 +2854,19 @@ s_mp_clamp(mp_int *mp)
 
 /* {{{ s_mp_exch(a, b) */
 
 /* Exchange the data for a and b; (b, a) = (a, b)                         */
 void
 s_mp_exch(mp_int *a, mp_int *b)
 {
     mp_int tmp;
+    if (!a || !b) {
+        return;
+    }
 
     tmp = *a;
     *a = *b;
     *b = tmp;
 
 } /* end s_mp_exch() */
 
 /* }}} */
@@ -4081,17 +4084,17 @@ s_mpv_sqr_add_prop(const mp_digit *pa, m
         carry += s_i;
         *ps++ = carry;
         carry = carry < s_i;
     }
 #endif
 }
 #endif
 
-#if (defined(MP_NO_MP_WORD) || defined(MP_NO_DIV_WORD)) && !defined(MP_ASSEMBLY_DIV_2DX1D)
+#if !defined(MP_ASSEMBLY_DIV_2DX1D)
 /*
 ** Divide 64-bit (Nhi,Nlo) by 32-bit divisor, which must be normalized
 ** so its high bit is 1.   This code is from NSPR.
 */
 mp_err
 s_mpv_div_2dx1d(mp_digit Nhi, mp_digit Nlo, mp_digit divisor,
                 mp_digit *qp, mp_digit *rp)
 {
@@ -4159,21 +4162,17 @@ s_mp_sqr(mp_int *a)
   Compute a = a / b and b = a mod b.  Assumes b > a.
  */
 
 mp_err s_mp_div(mp_int *rem,  /* i: dividend, o: remainder */
                 mp_int *div,  /* i: divisor                */
                 mp_int *quot) /* i: 0;        o: quotient  */
 {
     mp_int part, t;
-#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_DIV_WORD)
-    mp_word q_msd;
-#else
     mp_digit q_msd;
-#endif
     mp_err res;
     mp_digit d;
     mp_digit div_msd;
     int ix;
 
     if (mp_cmp_z(div) == 0)
         return MP_RANGE;
 
@@ -4208,90 +4207,83 @@ mp_err s_mp_div(mp_int *rem,  /* i: divi
         int partExtended = 0; /* set to true if we need to extend part */
 
         unusedRem = MP_USED(rem) - MP_USED(div);
         MP_DIGITS(&part) = MP_DIGITS(rem) + unusedRem;
         MP_ALLOC(&part) = MP_ALLOC(rem) - unusedRem;
         MP_USED(&part) = MP_USED(div);
 
         /* We have now truncated the part of the remainder to the same length as
-     * the divisor. If part is smaller than div, extend part by one digit. */
+         * the divisor. If part is smaller than div, extend part by one digit. */
         if (s_mp_cmp(&part, div) < 0) {
             --unusedRem;
 #if MP_ARGCHK == 2
             assert(unusedRem >= 0);
 #endif
             --MP_DIGITS(&part);
             ++MP_USED(&part);
             ++MP_ALLOC(&part);
             partExtended = 1;
         }
 
         /* Compute a guess for the next quotient digit       */
         q_msd = MP_DIGIT(&part, MP_USED(&part) - 1);
         div_msd = MP_DIGIT(div, MP_USED(div) - 1);
         if (!partExtended) {
             /* In this case, q_msd /= div_msd is always 1. First, since div_msd is
-       * normalized to have the high bit set, 2*div_msd > MP_DIGIT_MAX. Since
-       * we didn't extend part, q_msd >= div_msd. Therefore we know that
-       * div_msd <= q_msd <= MP_DIGIT_MAX < 2*div_msd. Dividing by div_msd we
-       * get 1 <= q_msd/div_msd < 2. So q_msd /= div_msd must be 1. */
+             * normalized to have the high bit set, 2*div_msd > MP_DIGIT_MAX. Since
+             * we didn't extend part, q_msd >= div_msd. Therefore we know that
+             * div_msd <= q_msd <= MP_DIGIT_MAX < 2*div_msd. Dividing by div_msd we
+             * get 1 <= q_msd/div_msd < 2. So q_msd /= div_msd must be 1. */
             q_msd = 1;
         } else {
-#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_DIV_WORD)
-            q_msd = (q_msd << MP_DIGIT_BIT) | MP_DIGIT(&part, MP_USED(&part) - 2);
-            q_msd /= div_msd;
-            if (q_msd == RADIX)
-                --q_msd;
-#else
             if (q_msd == div_msd) {
                 q_msd = MP_DIGIT_MAX;
             } else {
                 mp_digit r;
                 MP_CHECKOK(s_mpv_div_2dx1d(q_msd, MP_DIGIT(&part, MP_USED(&part) - 2),
                                            div_msd, &q_msd, &r));
             }
-#endif
         }
 #if MP_ARGCHK == 2
         assert(q_msd > 0); /* This case should never occur any more. */
 #endif
         if (q_msd <= 0)
             break;
 
         /* See what that multiplies out to                   */
         mp_copy(div, &t);
-        MP_CHECKOK(s_mp_mul_d(&t, (mp_digit)q_msd));
+        MP_CHECKOK(s_mp_mul_d(&t, q_msd));
 
         /*
-       If it's too big, back it off.  We should not have to do this
-       more than once, or, in rare cases, twice.  Knuth describes a
-       method by which this could be reduced to a maximum of once, but
-       I didn't implement that here.
-     * When using s_mpv_div_2dx1d, we may have to do this 3 times.
-     */
+           If it's too big, back it off.  We should not have to do this
+           more than once, or, in rare cases, twice.  Knuth describes a
+           method by which this could be reduced to a maximum of once, but
+           I didn't implement that here.
+           When using s_mpv_div_2dx1d, we may have to do this 3 times.
+         */
         for (i = 4; s_mp_cmp(&t, &part) > 0 && i > 0; --i) {
             --q_msd;
             MP_CHECKOK(s_mp_sub(&t, div)); /* t -= div */
         }
         if (i < 0) {
             res = MP_RANGE;
             goto CLEANUP;
         }
 
         /* At this point, q_msd should be the right next digit   */
         MP_CHECKOK(s_mp_sub(&part, &t)); /* part -= t */
         s_mp_clamp(rem);
 
         /*
-      Include the digit in the quotient.  We allocated enough memory
-      for any quotient we could ever possibly get, so we should not
-      have to check for failures here
-     */
-        MP_DIGIT(quot, unusedRem) = (mp_digit)q_msd;
+          Include the digit in the quotient.  We allocated enough memory
+          for any quotient we could ever possibly get, so we should not
+          have to check for failures here
+         */
+        MP_DIGIT(quot, unusedRem) = q_msd;
     }
 
     /* Denormalize remainder                */
     if (d) {
         s_mp_div_2d(rem, d);
     }
 
     s_mp_clamp(quot);
--- a/security/nss/lib/pk11wrap/pk11cert.c
+++ b/security/nss/lib/pk11wrap/pk11cert.c
@@ -760,17 +760,22 @@ find_certs_from_nickname(const char *nic
         if (token) {
             slot = PK11_ReferenceSlot(token->pk11slot);
         } else {
             PORT_SetError(SEC_ERROR_NO_TOKEN);
         }
         *delimit = ':';
     } else {
         slot = PK11_GetInternalKeySlot();
-        token = nssToken_AddRef(PK11Slot_GetNSSToken(slot));
+        token = PK11Slot_GetNSSToken(slot);
+        if (token) {
+            nssToken_AddRef(token);
+        } else {
+            PORT_SetError(SEC_ERROR_NO_TOKEN);
+        }
     }
     if (token) {
         nssList *certList;
         nssCryptokiObject **instances;
         nssPKIObjectCollection *collection;
         nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly;
         if (!PK11_IsPresent(slot)) {
             goto loser;
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -33,23 +33,16 @@
 #include "secmod.h"
 #include "blapi.h"
 
 #include <stdio.h>
 #ifdef NSS_SSL_ENABLE_ZLIB
 #include "zlib.h"
 #endif
 
-#ifndef PK11_SETATTRS
-#define PK11_SETATTRS(x, id, v, l) \
-    (x)->type = (id);              \
-    (x)->pValue = (v);             \
-    (x)->ulValueLen = (l);
-#endif
-
 static PK11SymKey *ssl3_GenerateRSAPMS(sslSocket *ss, ssl3CipherSpec *spec,
                                        PK11SlotInfo *serverKeySlot);
 static SECStatus ssl3_DeriveMasterSecret(sslSocket *ss, PK11SymKey *pms);
 static SECStatus ssl3_DeriveConnectionKeys(sslSocket *ss);
 static SECStatus ssl3_HandshakeFailure(sslSocket *ss);
 static SECStatus ssl3_SendCertificate(sslSocket *ss);
 static SECStatus ssl3_SendCertificateRequest(sslSocket *ss);
 static SECStatus ssl3_SendNextProto(sslSocket *ss);
--- a/security/nss/lib/ssl/ssl3ecc.c
+++ b/security/nss/lib/ssl/ssl3ecc.c
@@ -26,23 +26,16 @@
 #include "prthread.h"
 #include "prinit.h"
 
 #include "pk11func.h"
 #include "secmod.h"
 
 #include <stdio.h>
 
-#ifndef PK11_SETATTRS
-#define PK11_SETATTRS(x, id, v, l) \
-    (x)->type = (id);              \
-    (x)->pValue = (v);             \
-    (x)->ulValueLen = (l);
-#endif
-
 SECStatus
 ssl_NamedGroup2ECParams(PLArenaPool *arena, const sslNamedGroupDef *ecGroup,
                         SECKEYECParams *params)
 {
     SECOidData *oidData = NULL;
 
     if (!params) {
         PORT_Assert(0);
--- a/security/nss/lib/ssl/ssl3exthandle.c
+++ b/security/nss/lib/ssl/ssl3exthandle.c
@@ -11,24 +11,26 @@
 #include "sslimpl.h"
 #include "pk11pub.h"
 #include "blapit.h"
 #include "prinit.h"
 #include "ssl3ext.h"
 #include "ssl3exthandle.h"
 #include "tls13exthandle.h" /* For tls13_ServerSendStatusRequestXtn. */
 
-static SECStatus ssl3_ParseEncryptedSessionTicket(sslSocket *ss,
-                                                  SECItem *data, EncryptedSessionTicket *enc_session_ticket);
 static SECStatus ssl3_AppendToItem(SECItem *item, const unsigned char *buf,
                                    PRUint32 bytes);
-static SECStatus ssl3_ConsumeFromItem(SECItem *item, unsigned char **buf, PRUint32 bytes);
+static SECStatus ssl3_ConsumeFromItem(SECItem *item, unsigned char **buf,
+                                      PRUint32 bytes);
 static SECStatus ssl3_AppendNumberToItem(SECItem *item, PRUint32 num,
                                          PRInt32 lenSize);
-static SECStatus ssl3_ConsumeFromItem(SECItem *item, unsigned char **buf, PRUint32 bytes);
+
+PRUint32 ssl_ticket_lifetime = 2 * 24 * 60 * 60; /* 2 days in seconds */
+#define TLS_EX_SESS_TICKET_VERSION (0x0105)
+#define TLS_EX_SESS_TICKET_MAC_LENGTH 32
 
 /*
  * Write bytes.  Using this function means the SECItem structure
  * cannot be freed.  The caller is expected to call this function
  * on a shallow copy of the structure.
  */
 static SECStatus
 ssl3_AppendToItem(SECItem *item, const unsigned char *buf, PRUint32 bytes)
@@ -324,36 +326,38 @@ ssl3_SendSessionTicketXtn(
     return extension_length;
 
 loser:
     xtnData->ticketTimestampVerified = PR_FALSE;
     return -1;
 }
 
 static SECStatus
-ssl3_ParseEncryptedSessionTicket(sslSocket *ss, SECItem *data,
-                                 EncryptedSessionTicket *enc_session_ticket)
+ssl3_ParseEncryptedSessionTicket(sslSocket *ss, const SECItem *data,
+                                 EncryptedSessionTicket *encryptedTicket)
 {
-    if (ssl3_ConsumeFromItem(data, &enc_session_ticket->key_name,
+    SECItem copy = *data;
+
+    if (ssl3_ConsumeFromItem(&copy, &encryptedTicket->key_name,
                              SESS_TICKET_KEY_NAME_LEN) !=
         SECSuccess)
         return SECFailure;
-    if (ssl3_ConsumeFromItem(data, &enc_session_ticket->iv,
+    if (ssl3_ConsumeFromItem(&copy, &encryptedTicket->iv,
                              AES_BLOCK_SIZE) !=
         SECSuccess)
         return SECFailure;
-    if (ssl3_ConsumeHandshakeVariable(ss, &enc_session_ticket->encrypted_state,
-                                      2, &data->data, &data->len) !=
+    if (ssl3_ConsumeHandshakeVariable(ss, &encryptedTicket->encrypted_state,
+                                      2, &copy.data, &copy.len) !=
         SECSuccess)
         return SECFailure;
-    if (ssl3_ConsumeFromItem(data, &enc_session_ticket->mac,
+    if (ssl3_ConsumeFromItem(&copy, &encryptedTicket->mac,
                              TLS_EX_SESS_TICKET_MAC_LENGTH) !=
         SECSuccess)
         return SECFailure;
-    if (data->len != 0) /* Make sure that we have consumed all bytes. */
+    if (copy.len != 0) /* Make sure that we have consumed all bytes. */
         return SECFailure;
 
     return SECSuccess;
 }
 
 PRBool
 ssl_AlpnTagAllowed(const sslSocket *ss, const SECItem *tag)
 {
@@ -872,19 +876,16 @@ ssl3_ClientHandleStatusRequestXtn(const 
         return SECFailure;
     }
 
     /* Keep track of negotiated extensions. */
     xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
     return SECSuccess;
 }
 
-PRUint32 ssl_ticket_lifetime = 2 * 24 * 60 * 60; /* 2 days in seconds */
-#define TLS_EX_SESS_TICKET_VERSION (0x0104)
-
 /*
  * Called from ssl3_SendNewSessionTicket, tls13_SendNewSessionTicket
  */
 SECStatus
 ssl3_EncodeSessionTicket(sslSocket *ss,
                          const NewSessionTicket *ticket,
                          SECItem *ticket_data)
 {
@@ -903,39 +904,36 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
     PRUint32 padding_length;
     PRUint32 ticket_length;
     PRUint32 cert_length = 0;
     PRUint8 length_buf[4];
     PRUint32 now;
     unsigned char key_name[SESS_TICKET_KEY_NAME_LEN];
     PK11SymKey *aes_key = NULL;
     PK11SymKey *mac_key = NULL;
-    CK_MECHANISM_TYPE cipherMech = CKM_AES_CBC;
     PK11Context *aes_ctx;
-    CK_MECHANISM_TYPE macMech = CKM_SHA256_HMAC;
     PK11Context *hmac_ctx = NULL;
     unsigned char computed_mac[TLS_EX_SESS_TICKET_MAC_LENGTH];
     unsigned int computed_mac_length;
     unsigned char iv[AES_BLOCK_SIZE];
     SECItem ivItem;
     SECItem *srvName = NULL;
-    PRUint32 srvNameLen = 0;
     CK_MECHANISM_TYPE msWrapMech = 0; /* dummy default value,
                                           * must be >= 0 */
     ssl3CipherSpec *spec;
-    SECItem alpnSelection = { siBuffer, NULL, 0 };
+    SECItem *alpnSelection = NULL;
 
     SSL_TRC(3, ("%d: SSL3[%d]: send session_ticket handshake",
                 SSL_GETPID(), ss->fd));
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     if (ss->opt.requestCertificate && ss->sec.ci.sid->peerCert) {
-        cert_length = 3 + ss->sec.ci.sid->peerCert->derCert.len;
+        cert_length = 2 + ss->sec.ci.sid->peerCert->derCert.len;
     }
 
     /* Get IV and encryption keys */
     ivItem.data = iv;
     ivItem.len = sizeof(iv);
     rv = PK11_GenerateRandom(iv, sizeof(iv));
     if (rv != SECSuccess)
         goto loser;
@@ -971,60 +969,56 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
         } else {
             /* TODO: else send an empty ticket. */
             goto loser;
         }
         ms_is_wrapped = PR_TRUE;
     }
     /* Prep to send negotiated name */
     srvName = &ss->sec.ci.sid->u.ssl3.srvName;
-    if (srvName->data && srvName->len) {
-        srvNameLen = 2 + srvName->len; /* len bytes + name len */
-    }
 
-    if (ss->xtnData.nextProtoState != SSL_NEXT_PROTO_NO_SUPPORT &&
-        ss->xtnData.nextProto.data) {
-        alpnSelection = ss->xtnData.nextProto;
-    }
+    PORT_Assert(ss->xtnData.nextProtoState == SSL_NEXT_PROTO_SELECTED ||
+                ss->xtnData.nextProtoState == SSL_NEXT_PROTO_NEGOTIATED ||
+                ss->xtnData.nextProto.len == 0);
+    alpnSelection = &ss->xtnData.nextProto;
 
     ciphertext_length =
-        sizeof(PRUint16)                       /* ticket_version */
+        sizeof(PRUint16)                       /* ticket version */
         + sizeof(SSL3ProtocolVersion)          /* ssl_version */
         + sizeof(ssl3CipherSuite)              /* ciphersuite */
         + 1                                    /* compression */
         + 10                                   /* cipher spec parameters */
         + 1                                    /* certType arguments */
         + 1                                    /* SessionTicket.ms_is_wrapped */
         + 4                                    /* msWrapMech */
         + 2                                    /* master_secret.length */
         + ms_item.len                          /* master_secret */
         + 1                                    /* client_auth_type */
         + cert_length                          /* cert */
-        + 1                                    /* server name type */
-        + srvNameLen                           /* name len + length field */
+        + 2 + srvName->len                     /* name len + length field */
         + 1                                    /* extendedMasterSecretUsed */
         + sizeof(ticket->ticket_lifetime_hint) /* ticket lifetime hint */
         + sizeof(ticket->flags)                /* ticket flags */
-        + 1 + alpnSelection.len                /* npn value + length field. */
+        + 1 + alpnSelection->len               /* alpn value + length field */
         + 4;                                   /* maxEarlyData */
 #ifdef UNSAFE_FUZZER_MODE
     padding_length = 0;
 #else
     padding_length = AES_BLOCK_SIZE -
                      (ciphertext_length %
                       AES_BLOCK_SIZE);
 #endif
     ciphertext_length += padding_length;
 
     if (SECITEM_AllocItem(NULL, &plaintext_item, ciphertext_length) == NULL)
         goto loser;
 
     plaintext = plaintext_item;
 
-    /* ticket_version */
+    /* ticket version */
     rv = ssl3_AppendNumberToItem(&plaintext, TLS_EX_SESS_TICKET_VERSION,
                                  sizeof(PRUint16));
     if (rv != SECSuccess)
         goto loser;
 
     /* ssl_version */
     rv = ssl3_AppendNumberToItem(&plaintext, ss->version,
                                  sizeof(SSL3ProtocolVersion));
@@ -1079,23 +1073,23 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
         goto loser;
     rv = ssl3_AppendNumberToItem(&plaintext, ms_item.len, 2);
     if (rv != SECSuccess)
         goto loser;
     rv = ssl3_AppendToItem(&plaintext, ms_item.data, ms_item.len);
     if (rv != SECSuccess)
         goto loser;
 
-    /* client_identity */
+    /* client identity */
     if (ss->opt.requestCertificate && ss->sec.ci.sid->peerCert) {
         rv = ssl3_AppendNumberToItem(&plaintext, CLIENT_AUTH_CERTIFICATE, 1);
         if (rv != SECSuccess)
             goto loser;
         rv = ssl3_AppendNumberToItem(&plaintext,
-                                     ss->sec.ci.sid->peerCert->derCert.len, 3);
+                                     ss->sec.ci.sid->peerCert->derCert.len, 2);
         if (rv != SECSuccess)
             goto loser;
         rv = ssl3_AppendToItem(&plaintext,
                                ss->sec.ci.sid->peerCert->derCert.data,
                                ss->sec.ci.sid->peerCert->derCert.len);
         if (rv != SECSuccess)
             goto loser;
     } else {
@@ -1106,54 +1100,46 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
 
     /* timestamp */
     now = ssl_Time();
     rv = ssl3_AppendNumberToItem(&plaintext, now,
                                  sizeof(ticket->ticket_lifetime_hint));
     if (rv != SECSuccess)
         goto loser;
 
-    if (srvNameLen) {
-        /* Name Type (sni_host_name) */
-        rv = ssl3_AppendNumberToItem(&plaintext, srvName->type, 1);
-        if (rv != SECSuccess)
-            goto loser;
-        /* HostName (length and value) */
-        rv = ssl3_AppendNumberToItem(&plaintext, srvName->len, 2);
-        if (rv != SECSuccess)
-            goto loser;
+    /* HostName (length and value) */
+    rv = ssl3_AppendNumberToItem(&plaintext, srvName->len, 2);
+    if (rv != SECSuccess)
+        goto loser;
+    if (srvName->len) {
         rv = ssl3_AppendToItem(&plaintext, srvName->data, srvName->len);
         if (rv != SECSuccess)
             goto loser;
-    } else {
-        /* No Name */
-        rv = ssl3_AppendNumberToItem(&plaintext, (char)TLS_STE_NO_SERVER_NAME, 1);
-        if (rv != SECSuccess)
-            goto loser;
     }
 
     /* extendedMasterSecretUsed */
     rv = ssl3_AppendNumberToItem(
         &plaintext, ss->sec.ci.sid->u.ssl3.keys.extendedMasterSecretUsed, 1);
     if (rv != SECSuccess)
         goto loser;
 
     /* Flags */
     rv = ssl3_AppendNumberToItem(&plaintext, ticket->flags,
                                  sizeof(ticket->flags));
     if (rv != SECSuccess)
         goto loser;
 
-    /* NPN value. */
-    PORT_Assert(alpnSelection.len < 256);
-    rv = ssl3_AppendNumberToItem(&plaintext, alpnSelection.len, 1);
+    /* ALPN value. */
+    PORT_Assert(alpnSelection->len < 256);
+    rv = ssl3_AppendNumberToItem(&plaintext, alpnSelection->len, 1);
     if (rv != SECSuccess)
         goto loser;
-    if (alpnSelection.len) {
-        rv = ssl3_AppendToItem(&plaintext, alpnSelection.data, alpnSelection.len);
+    if (alpnSelection->len) {
+        rv = ssl3_AppendToItem(&plaintext, alpnSelection->data,
+                               alpnSelection->len);
         if (rv != SECSuccess)
             goto loser;
     }
 
     rv = ssl3_AppendNumberToItem(&plaintext, ssl_max_early_data_size, 4);
     if (rv != SECSuccess)
         goto loser;
 
@@ -1167,17 +1153,17 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
     }
 
     /* Generate encrypted portion of ticket. */
     PORT_Assert(aes_key);
 #ifdef UNSAFE_FUZZER_MODE
     ciphertext.len = plaintext_item.len;
     PORT_Memcpy(ciphertext.data, plaintext_item.data, plaintext_item.len);
 #else
-    aes_ctx = PK11_CreateContextBySymKey(cipherMech, CKA_ENCRYPT, aes_key, &ivItem);
+    aes_ctx = PK11_CreateContextBySymKey(CKM_AES_CBC, CKA_ENCRYPT, aes_key, &ivItem);
     if (!aes_ctx)
         goto loser;
 
     rv = PK11_CipherOp(aes_ctx, ciphertext.data,
                        (int *)&ciphertext.len, ciphertext.len,
                        plaintext_item.data, plaintext_item.len);
     PK11_Finalize(aes_ctx);
     PK11_DestroyContext(aes_ctx, PR_TRUE);
@@ -1185,17 +1171,18 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
         goto loser;
 #endif
 
     /* Convert ciphertext length to network order. */
     (void)ssl_EncodeUintX(ciphertext.len, 2, length_buf);
 
     /* Compute MAC. */
     PORT_Assert(mac_key);
-    hmac_ctx = PK11_CreateContextBySymKey(macMech, CKA_SIGN, mac_key, &macParam);
+    hmac_ctx = PK11_CreateContextBySymKey(CKM_SHA256_HMAC, CKA_SIGN, mac_key,
+                                          &macParam);
     if (!hmac_ctx)
         goto loser;
 
     rv = PK11_DigestBegin(hmac_ctx);
     if (rv != SECSuccess)
         goto loser;
     rv = PK11_DigestOp(hmac_ctx, key_name, SESS_TICKET_KEY_NAME_LEN);
     if (rv != SECSuccess)
@@ -1280,465 +1267,535 @@ ssl3_ClientHandleSessionTicketXtn(const 
         return SECSuccess; /* Ignore the extension. */
     }
 
     /* Keep track of negotiated extensions. */
     xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
     return SECSuccess;
 }
 
-/* Generic ticket processing code, common to TLS 1.0-1.2 and
- * TLS 1.3. */
+static SECStatus
+ssl_DecryptSessionTicket(sslSocket *ss, const SECItem *rawTicket,
+                         const EncryptedSessionTicket *encryptedTicket,
+                         SECItem *decryptedTicket)
+{
+    SECStatus rv;
+    unsigned char keyName[SESS_TICKET_KEY_NAME_LEN];
+
+    PK11SymKey *macKey = NULL;
+    PK11Context *hmacCtx;
+    unsigned char computedMac[TLS_EX_SESS_TICKET_MAC_LENGTH];
+    unsigned int computedMacLength;
+    SECItem macParam = { siBuffer, NULL, 0 };
+
+    PK11SymKey *aesKey = NULL;
+#ifndef UNSAFE_FUZZER_MODE
+    PK11Context *aesCtx;
+    SECItem ivItem;
+
+    unsigned int i;
+    SSL3Opaque *padding;
+    SSL3Opaque paddingLength;
+#endif
+
+    PORT_Assert(!decryptedTicket->data);
+    PORT_Assert(!decryptedTicket->len);
+    if (rawTicket->len < TLS_EX_SESS_TICKET_MAC_LENGTH) {
+        PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+        return SECFailure;
+    }
+
+    /* Get session ticket keys. */
+    rv = ssl_GetSessionTicketKeys(ss, keyName, &aesKey, &macKey);
+    if (rv != SECSuccess) {
+        SSL_DBG(("%d: SSL[%d]: Unable to get/generate session ticket keys.",
+                 SSL_GETPID(), ss->fd));
+        return SECFailure; /* code already set */
+    }
+
+    /* If the ticket sent by the client was generated under a key different from
+     * the one we have, bypass ticket processing.  This reports success, meaning
+     * that the handshake completes, but doesn't resume.
+     */
+    if (PORT_Memcmp(encryptedTicket->key_name, keyName,
+                    SESS_TICKET_KEY_NAME_LEN) != 0) {
+#ifndef UNSAFE_FUZZER_MODE
+        SSL_DBG(("%d: SSL[%d]: Session ticket key_name sent mismatch.",
+                 SSL_GETPID(), ss->fd));
+        return SECSuccess;
+#endif
+    }
+
+    /* Verify the MAC on the ticket. */
+    PORT_Assert(macKey);
+    hmacCtx = PK11_CreateContextBySymKey(CKM_SHA256_HMAC, CKA_SIGN, macKey,
+                                         &macParam);
+    if (!hmacCtx) {
+        SSL_DBG(("%d: SSL[%d]: Unable to create HMAC context: %d.",
+                 SSL_GETPID(), ss->fd, PORT_GetError()));
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+
+    SSL_DBG(("%d: SSL[%d]: Successfully created HMAC context.",
+             SSL_GETPID(), ss->fd));
+    do {
+        rv = PK11_DigestBegin(hmacCtx);
+        if (rv != SECSuccess) {
+            break;
+        }
+        rv = PK11_DigestOp(hmacCtx, rawTicket->data,
+                           rawTicket->len - TLS_EX_SESS_TICKET_MAC_LENGTH);
+        if (rv != SECSuccess) {
+            break;
+        }
+        rv = PK11_DigestFinal(hmacCtx, computedMac, &computedMacLength,
+                              sizeof(computedMac));
+    } while (0);
+    PK11_DestroyContext(hmacCtx, PR_TRUE);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+
+    if (NSS_SecureMemcmp(computedMac, encryptedTicket->mac,
+                         computedMacLength) != 0) {
+#ifndef UNSAFE_FUZZER_MODE
+        SSL_DBG(("%d: SSL[%d]: Session ticket MAC mismatch.",
+                 SSL_GETPID(), ss->fd));
+        PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+        return SECFailure;
+#endif
+    }
+
+    /* Decrypt the ticket. */
+
+    /* Plaintext is shorter than the ciphertext due to padding. */
+    if (!SECITEM_AllocItem(NULL, decryptedTicket,
+                           encryptedTicket->encrypted_state.len)) {
+        return SECFailure; /* code already set */
+    }
+
+    PORT_Assert(aesKey);
+#ifdef UNSAFE_FUZZER_MODE
+    decryptedTicket->len = encryptedTicket->encrypted_state.len;
+    PORT_Memcpy(decryptedTicket->data,
+                encryptedTicket->encrypted_state.data,
+                encryptedTicket->encrypted_state.len);
+#else
+    ivItem.data = encryptedTicket->iv;
+    ivItem.len = AES_BLOCK_SIZE;
+    aesCtx = PK11_CreateContextBySymKey(CKM_AES_CBC, CKA_DECRYPT, aesKey,
+                                        &ivItem);
+    if (!aesCtx) {
+        SSL_DBG(("%d: SSL[%d]: Unable to create AES context.",
+                 SSL_GETPID(), ss->fd));
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+
+    do {
+        rv = PK11_CipherOp(aesCtx, decryptedTicket->data,
+                           (int *)&decryptedTicket->len, decryptedTicket->len,
+                           encryptedTicket->encrypted_state.data,
+                           encryptedTicket->encrypted_state.len);
+        if (rv != SECSuccess) {
+            break;
+        }
+        rv = PK11_Finalize(aesCtx);
+        if (rv != SECSuccess) {
+            break;
+        }
+    } while (0);
+    PK11_DestroyContext(aesCtx, PR_TRUE);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+
+    /* Check padding. */
+    paddingLength = decryptedTicket->data[decryptedTicket->len - 1];
+    if (paddingLength == 0 || paddingLength > AES_BLOCK_SIZE) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+
+    padding = &decryptedTicket->data[decryptedTicket->len - paddingLength];
+    for (i = 0; i < paddingLength; i++, padding++) {
+        if (paddingLength != *padding) {
+            PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+            return SECFailure;
+        }
+    }
+    decryptedTicket->len -= paddingLength;
+#endif /* UNSAFE_FUZZER_MODE */
+
+    return SECSuccess;
+}
+
+static SECStatus
+ssl_ParseSessionTicket(sslSocket *ss, const SECItem *decryptedTicket,
+                       SessionTicket *parsedTicket)
+{
+    PRUint32 temp;
+    SECStatus rv;
+
+    SSL3Opaque *buffer = decryptedTicket->data;
+    unsigned int len = decryptedTicket->len;
+
+    PORT_Memset(parsedTicket, 0, sizeof(*parsedTicket));
+    parsedTicket->valid = PR_FALSE;
+
+    /* If the decrypted ticket is empty, then report success, but leave the
+     * ticket marked as invalid. */
+    if (decryptedTicket->len == 0) {
+        return SECSuccess;
+    }
+
+    /* Read ticket version. */
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &len);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+
+    /* Skip the ticket if the version is wrong.  This won't result in a
+     * handshake failure, just a failure to resume. */
+    if (temp != TLS_EX_SESS_TICKET_VERSION) {
+        return SECSuccess;
+    }
+
+    /* Read SSLVersion. */
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &len);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    parsedTicket->ssl_version = (SSL3ProtocolVersion)temp;
+    if (!ssl3_VersionIsSupported(ss->protocolVariant,
+                                 parsedTicket->ssl_version)) {
+        /* This socket doesn't support the version from the ticket. */
+        return SECSuccess;
+    }
+
+    /* Read cipher_suite. */
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &len);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    parsedTicket->cipher_suite = (ssl3CipherSuite)temp;
+
+    /* Read compression_method. */
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    parsedTicket->compression_method = (SSLCompressionMethod)temp;
+
+    /* Read cipher spec parameters. */
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    parsedTicket->authType = (SSLAuthType)temp;
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    parsedTicket->authKeyBits = temp;
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    parsedTicket->keaType = (SSLKEAType)temp;
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    parsedTicket->keaKeyBits = temp;
+
+    /* Read the optional named curve. */
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    if (parsedTicket->authType == ssl_auth_ecdsa ||
+        parsedTicket->authType == ssl_auth_ecdh_rsa ||
+        parsedTicket->authType == ssl_auth_ecdh_ecdsa) {
+        const sslNamedGroupDef *group =
+            ssl_LookupNamedGroup((SSLNamedGroup)temp);
+        if (!group || group->keaType != ssl_kea_ecdh) {
+            PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+            return SECFailure;
+        }
+        parsedTicket->namedCurve = group;
+    }
+
+    /* Read the master secret (and how it is wrapped). */
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    PORT_Assert(temp == PR_TRUE || temp == PR_FALSE);
+    parsedTicket->ms_is_wrapped = (PRBool)temp;
+
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    parsedTicket->msWrapMech = (CK_MECHANISM_TYPE)temp;
+
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &len);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    if (temp == 0 || temp > sizeof(parsedTicket->master_secret)) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    parsedTicket->ms_length = (PRUint16)temp;
+
+    /* Read the master secret. */
+    rv = ssl3_ExtConsumeHandshake(ss, parsedTicket->master_secret,
+                                  parsedTicket->ms_length, &buffer, &len);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    /* Read client identity */
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    parsedTicket->client_auth_type = (ClientAuthenticationType)temp;
+    switch (parsedTicket->client_auth_type) {
+        case CLIENT_AUTH_ANONYMOUS:
+            break;
+        case CLIENT_AUTH_CERTIFICATE:
+            rv = ssl3_ExtConsumeHandshakeVariable(ss, &parsedTicket->peer_cert, 2,
+                                                  &buffer, &len);
+            if (rv != SECSuccess) {
+                PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+                return SECFailure;
+            }
+            break;
+        default:
+            PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+            return SECFailure;
+    }
+    /* Read timestamp. */
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    parsedTicket->timestamp = temp;
+
+    /* Read server name */
+    rv = ssl3_ExtConsumeHandshakeVariable(ss, &parsedTicket->srvName, 2,
+                                          &buffer, &len);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+
+    /* Read extendedMasterSecretUsed */
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    PORT_Assert(temp == PR_TRUE || temp == PR_FALSE);
+    parsedTicket->extendedMasterSecretUsed = (PRBool)temp;
+
+    rv = ssl3_ExtConsumeHandshake(ss, &temp, 4, &buffer, &len);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    parsedTicket->flags = PR_ntohl(temp);
+
+    rv = ssl3_ExtConsumeHandshakeVariable(ss, &parsedTicket->alpnSelection, 1,
+                                          &buffer, &len);
+    PORT_Assert(parsedTicket->alpnSelection.len < 256);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
+    if (rv != SECSuccess) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    parsedTicket->maxEarlyData = temp;
+
+#ifndef UNSAFE_FUZZER_MODE
+    /* Done parsing.  Check that all bytes have been consumed. */
+    if (len != 0) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+#endif
+
+    parsedTicket->valid = PR_TRUE;
+    return SECSuccess;
+}
+
+static SECStatus
+ssl_CreateSIDFromTicket(sslSocket *ss, const SECItem *rawTicket,
+                        SessionTicket *parsedTicket, sslSessionID **out)
+{
+    sslSessionID *sid;
+    SECStatus rv;
+
+    sid = ssl3_NewSessionID(ss, PR_TRUE);
+    if (sid == NULL) {
+        return SECFailure;
+    }
+
+    /* Copy over parameters. */
+    sid->version = parsedTicket->ssl_version;
+    sid->u.ssl3.cipherSuite = parsedTicket->cipher_suite;
+    sid->u.ssl3.compression = parsedTicket->compression_method;
+    sid->authType = parsedTicket->authType;
+    sid->authKeyBits = parsedTicket->authKeyBits;
+    sid->keaType = parsedTicket->keaType;
+    sid->keaKeyBits = parsedTicket->keaKeyBits;
+    sid->namedCurve = parsedTicket->namedCurve;
+
+    rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.locked.sessionTicket.ticket,
+                          rawTicket);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+    sid->u.ssl3.locked.sessionTicket.flags = parsedTicket->flags;
+    sid->u.ssl3.locked.sessionTicket.max_early_data_size =
+        parsedTicket->maxEarlyData;
+
+    if (parsedTicket->ms_length >
+        sizeof(sid->u.ssl3.keys.wrapped_master_secret)) {
+        goto loser;
+    }
+    PORT_Memcpy(sid->u.ssl3.keys.wrapped_master_secret,
+                parsedTicket->master_secret, parsedTicket->ms_length);
+    sid->u.ssl3.keys.wrapped_master_secret_len = parsedTicket->ms_length;
+    sid->u.ssl3.masterWrapMech = parsedTicket->msWrapMech;
+    sid->u.ssl3.keys.msIsWrapped = parsedTicket->ms_is_wrapped;
+    sid->u.ssl3.masterValid = PR_TRUE;
+    sid->u.ssl3.keys.resumable = PR_TRUE;
+    sid->u.ssl3.keys.extendedMasterSecretUsed = parsedTicket->extendedMasterSecretUsed;
+
+    /* Copy over client cert from session ticket if there is one. */
+    if (parsedTicket->peer_cert.data != NULL) {
+        PORT_Assert(!sid->peerCert);
+        sid->peerCert = CERT_NewTempCertificate(ss->dbHandle,
+                                                &parsedTicket->peer_cert,
+                                                NULL, PR_FALSE, PR_TRUE);
+        if (!sid->peerCert) {
+            goto loser;
+        }
+    }
+
+    /* Transfer ownership of the remaining items. */
+    if (parsedTicket->srvName.data != NULL) {
+        SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE);
+        rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.srvName,
+                              &parsedTicket->srvName);
+        if (rv != SECSuccess) {
+            goto loser;
+        }
+    }
+    if (parsedTicket->alpnSelection.data != NULL) {
+        rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.alpnSelection,
+                              &parsedTicket->alpnSelection);
+        if (rv != SECSuccess) {
+            goto loser;
+        }
+    }
+
+    *out = sid;
+    return SECSuccess;
+
+loser:
+    ssl_FreeSID(sid);
+    return SECFailure;
+}
+
+/* Generic ticket processing code, common to all TLS versions. */
 SECStatus
 ssl3_ProcessSessionTicketCommon(sslSocket *ss, SECItem *data)
 {
+    EncryptedSessionTicket encryptedTicket;
+    SECItem decryptedTicket = { siBuffer, NULL, 0 };
+    SessionTicket parsedTicket;
     SECStatus rv;
-    SECItem *decrypted_state = NULL;
-    SessionTicket *parsed_session_ticket = NULL;
-    sslSessionID *sid = NULL;
-    SSL3Statistics *ssl3stats;
-    PRUint32 i;
-    SECItem extension_data;
-    EncryptedSessionTicket enc_session_ticket;
-    unsigned char computed_mac[TLS_EX_SESS_TICKET_MAC_LENGTH];
-    unsigned int computed_mac_length;
-    unsigned char key_name[SESS_TICKET_KEY_NAME_LEN];
-    PK11SymKey *aes_key = NULL;
-    PK11SymKey *mac_key = NULL;
-    PK11Context *hmac_ctx;
-    CK_MECHANISM_TYPE macMech = CKM_SHA256_HMAC;
-    PK11Context *aes_ctx;
-    CK_MECHANISM_TYPE cipherMech = CKM_AES_CBC;
-    unsigned char *padding;
-    PRUint32 padding_length;
-    unsigned char *buffer;
-    unsigned int buffer_len;
-    PRUint32 temp;
-    SECItem cert_item;
-    PRUint32 nameType;
-    SECItem macParam = { siBuffer, NULL, 0 };
-    SECItem alpn_item;
-    SECItem ivItem;
 
-    /* Turn off stateless session resumption if the client sends a
-     * SessionTicket extension, even if the extension turns out to be
-     * malformed (ss->sec.ci.sid is non-NULL when doing session
-     * renegotiation.)
-     */
     if (ss->sec.ci.sid != NULL) {
         ss->sec.uncache(ss->sec.ci.sid);
         ssl_FreeSID(ss->sec.ci.sid);
         ss->sec.ci.sid = NULL;
     }
 
-    extension_data.data = data->data; /* Keep a copy for future use. */
-    extension_data.len = data->len;
-
-    if (ssl3_ParseEncryptedSessionTicket(ss, data, &enc_session_ticket) !=
-        SECSuccess) {
-        return SECSuccess; /* Pretend it isn't there */
-    }
-
-    /* Get session ticket keys. */
-    rv = ssl_GetSessionTicketKeys(ss, key_name, &aes_key, &mac_key);
+    rv = ssl3_ParseEncryptedSessionTicket(ss, data, &encryptedTicket);
     if (rv != SECSuccess) {
-        SSL_DBG(("%d: SSL[%d]: Unable to get/generate session ticket keys.",
-                 SSL_GETPID(), ss->fd));
-        goto loser;
-    }
-
-    /* If the ticket sent by the client was generated under a key different
-     * from the one we have, bypass ticket processing.
-     */
-    if (PORT_Memcmp(enc_session_ticket.key_name, key_name,
-                    SESS_TICKET_KEY_NAME_LEN) != 0) {
-#ifndef UNSAFE_FUZZER_MODE
-        SSL_DBG(("%d: SSL[%d]: Session ticket key_name sent mismatch.",
-                 SSL_GETPID(), ss->fd));
-        goto no_ticket;
-#endif
+        PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+        return SECFailure;
     }
 
-    /* Verify the MAC on the ticket.  MAC verification may also
-     * fail if the MAC key has been recently refreshed.
-     */
-    PORT_Assert(mac_key);
-    hmac_ctx = PK11_CreateContextBySymKey(macMech, CKA_SIGN, mac_key, &macParam);
-    if (!hmac_ctx) {
-        SSL_DBG(("%d: SSL[%d]: Unable to create HMAC context: %d.",
-                 SSL_GETPID(), ss->fd, PORT_GetError()));
-        goto no_ticket;
-    } else {
-        SSL_DBG(("%d: SSL[%d]: Successfully created HMAC context.",
-                 SSL_GETPID(), ss->fd));
-    }
-    rv = PK11_DigestBegin(hmac_ctx);
-    if (rv != SECSuccess) {
-        PK11_DestroyContext(hmac_ctx, PR_TRUE);
-        goto no_ticket;
-    }
-    rv = PK11_DigestOp(hmac_ctx, extension_data.data,
-                       extension_data.len -
-                           TLS_EX_SESS_TICKET_MAC_LENGTH);
+    rv = ssl_DecryptSessionTicket(ss, data, &encryptedTicket,
+                                  &decryptedTicket);
     if (rv != SECSuccess) {
-        PK11_DestroyContext(hmac_ctx, PR_TRUE);
-        goto no_ticket;
-    }
-    rv = PK11_DigestFinal(hmac_ctx, computed_mac,
-                          &computed_mac_length, sizeof(computed_mac));
-    PK11_DestroyContext(hmac_ctx, PR_TRUE);
-    if (rv != SECSuccess)
-        goto no_ticket;
-
-    if (NSS_SecureMemcmp(computed_mac, enc_session_ticket.mac,
-                         computed_mac_length) !=
-        0) {
-#ifndef UNSAFE_FUZZER_MODE
-        SSL_DBG(("%d: SSL[%d]: Session ticket MAC mismatch.",
-                 SSL_GETPID(), ss->fd));
-        goto no_ticket;
-#endif
-    }
-
-    /* We ignore key_name for now.
-     * This is ok as MAC verification succeeded.
-     */
-
-    /* Decrypt the ticket. */
-
-    /* Plaintext is shorter than the ciphertext due to padding. */
-    decrypted_state = SECITEM_AllocItem(NULL, NULL,
-                                        enc_session_ticket.encrypted_state.len);
-
-    PORT_Assert(aes_key);
-#ifdef UNSAFE_FUZZER_MODE
-    decrypted_state->len = enc_session_ticket.encrypted_state.len;
-    PORT_Memcpy(decrypted_state->data,
-                enc_session_ticket.encrypted_state.data,
-                enc_session_ticket.encrypted_state.len);
-#else
-    ivItem.data = enc_session_ticket.iv;
-    ivItem.len = AES_BLOCK_SIZE;
-    aes_ctx = PK11_CreateContextBySymKey(cipherMech, CKA_DECRYPT,
-                                         aes_key, &ivItem);
-    if (!aes_ctx) {
-        SSL_DBG(("%d: SSL[%d]: Unable to create AES context.",
-                 SSL_GETPID(), ss->fd));
-        goto no_ticket;
-    }
-
-    rv = PK11_CipherOp(aes_ctx, decrypted_state->data,
-                       (int *)&decrypted_state->len, decrypted_state->len,
-                       enc_session_ticket.encrypted_state.data,
-                       enc_session_ticket.encrypted_state.len);
-    PK11_Finalize(aes_ctx);
-    PK11_DestroyContext(aes_ctx, PR_TRUE);
-    if (rv != SECSuccess)
-        goto no_ticket;
-
-    /* Check padding. */
-    padding_length =
-        (PRUint32)decrypted_state->data[decrypted_state->len - 1];
-    if (padding_length == 0 || padding_length > AES_BLOCK_SIZE)
-        goto no_ticket;
-
-    padding = &decrypted_state->data[decrypted_state->len - padding_length];
-    for (i = 0; i < padding_length; i++, padding++) {
-        if (padding_length != (PRUint32)*padding)
-            goto no_ticket;
-    }
-#endif
-
-    /* Deserialize session state. */
-    buffer = decrypted_state->data;
-    buffer_len = decrypted_state->len;
-
-    parsed_session_ticket = PORT_ZAlloc(sizeof(SessionTicket));
-    if (parsed_session_ticket == NULL) {
-        rv = SECFailure;
-        goto loser;
+        PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+        return SECFailure;
     }
 
-    /* Read ticket_version and reject if the version is wrong */
-    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &buffer_len);
-    if (rv != SECSuccess || temp != TLS_EX_SESS_TICKET_VERSION)
-        goto no_ticket;
-
-    parsed_session_ticket->ticket_version = (SSL3ProtocolVersion)temp;
-
-    /* Read SSLVersion. */
-    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &buffer_len);
-    if (rv != SECSuccess)
-        goto no_ticket;
-    parsed_session_ticket->ssl_version = (SSL3ProtocolVersion)temp;
-
-    /* Read cipher_suite. */
-    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &buffer_len);
-    if (rv != SECSuccess)
-        goto no_ticket;
-    parsed_session_ticket->cipher_suite = (ssl3CipherSuite)temp;
-
-    /* Read compression_method. */
-    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len);
-    if (rv != SECSuccess)
-        goto no_ticket;
-    parsed_session_ticket->compression_method = (SSLCompressionMethod)temp;
+    rv = ssl_ParseSessionTicket(ss, &decryptedTicket, &parsedTicket);
+    if (rv != SECSuccess) {
+        SSL3Statistics *ssl3stats;
 
-    /* Read cipher spec parameters. */
-    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len);
-    if (rv != SECSuccess)
-        goto no_ticket;
-    parsed_session_ticket->authType = (SSLAuthType)temp;
-    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &buffer_len);
-    if (rv != SECSuccess)
-        goto no_ticket;
-    parsed_session_ticket->authKeyBits = temp;
-    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len);
-    if (rv != SECSuccess)
-        goto no_ticket;
-    parsed_session_ticket->keaType = (SSLKEAType)temp;
-    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &buffer_len);
-    if (rv != SECSuccess)
-        goto no_ticket;
-    parsed_session_ticket->keaKeyBits = temp;
-
-    /* Read the optional named curve. */
-    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len);
-    if (rv != SECSuccess)
-        goto no_ticket;
-    if (parsed_session_ticket->authType == ssl_auth_ecdsa ||
-        parsed_session_ticket->authType == ssl_auth_ecdh_rsa ||
-        parsed_session_ticket->authType == ssl_auth_ecdh_ecdsa) {
-        const sslNamedGroupDef *group =
-            ssl_LookupNamedGroup((SSLNamedGroup)temp);
-        if (!group || group->keaType != ssl_kea_ecdh) {
-            goto no_ticket;
-        }
-        parsed_session_ticket->namedCurve = group;
+        SSL_DBG(("%d: SSL[%d]: Session ticket parsing failed.",
+                 SSL_GETPID(), ss->fd));
+        ssl3stats = SSL_GetStatistics();
+        SSL_AtomicIncrementLong(&ssl3stats->hch_sid_ticket_parse_failures);
+        goto loser; /* code already set */
     }
 
-    /* Read wrapped master_secret. */
-    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len);
-    if (rv != SECSuccess)
-        goto no_ticket;
-    parsed_session_ticket->ms_is_wrapped = (PRBool)temp;
-
-    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &buffer_len);
-    if (rv != SECSuccess)
-        goto no_ticket;
-    parsed_session_ticket->msWrapMech = (CK_MECHANISM_TYPE)temp;
-
-    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &buffer_len);
-    if (rv != SECSuccess)
-        goto no_ticket;
-    parsed_session_ticket->ms_length = (PRUint16)temp;
-    if (parsed_session_ticket->ms_length == 0 || /* sanity check MS. */
-        parsed_session_ticket->ms_length >
-            sizeof(parsed_session_ticket->master_secret))
-        goto no_ticket;
-
-    /* Allow for the wrapped master secret to be longer. */
-    if (buffer_len < parsed_session_ticket->ms_length)
-        goto no_ticket;
-    PORT_Memcpy(parsed_session_ticket->master_secret, buffer,
-                parsed_session_ticket->ms_length);
-    buffer += parsed_session_ticket->ms_length;
-    buffer_len -= parsed_session_ticket->ms_length;
-
-    /* Read client_identity */
-    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len);
-    if (rv != SECSuccess)
-        goto no_ticket;
-    parsed_session_ticket->client_identity.client_auth_type =
-        (ClientAuthenticationType)temp;
-    switch (parsed_session_ticket->client_identity.client_auth_type) {
-        case CLIENT_AUTH_ANONYMOUS:
-            break;
-        case CLIENT_AUTH_CERTIFICATE:
-            rv = ssl3_ExtConsumeHandshakeVariable(ss, &cert_item, 3,
-                                                  &buffer, &buffer_len);
-            if (rv != SECSuccess)
-                goto no_ticket;
-            rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->peer_cert,
-                                  &cert_item);
-            if (rv != SECSuccess)
-                goto no_ticket;
-            break;
-        default:
-            goto no_ticket;
-    }
-    /* Read timestamp. */
-    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &buffer_len);
-    if (rv != SECSuccess)
-        goto no_ticket;
-    parsed_session_ticket->timestamp = temp;
-
-    /* Read server name */
-    rv = ssl3_ExtConsumeHandshakeNumber(ss, &nameType, 1, &buffer, &buffer_len);
-    if (rv != SECSuccess)
-        goto no_ticket;
-    if ((PRInt8)nameType != TLS_STE_NO_SERVER_NAME) {
-        SECItem name_item;
-        rv = ssl3_ExtConsumeHandshakeVariable(ss, &name_item, 2, &buffer,
-                                              &buffer_len);
-        if (rv != SECSuccess)
-            goto no_ticket;
-        rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->srvName,
-                              &name_item);
-        if (rv != SECSuccess)
-            goto no_ticket;
-        parsed_session_ticket->srvName.type = (PRUint8)nameType;
-    }
-
-    /* Read extendedMasterSecretUsed */
-    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len);
-    if (rv != SECSuccess)
-        goto no_ticket;
-    PORT_Assert(temp == PR_TRUE || temp == PR_FALSE);
-    parsed_session_ticket->extendedMasterSecretUsed = (PRBool)temp;
-
-    rv = ssl3_ExtConsumeHandshake(ss, &parsed_session_ticket->flags, 4,
-                                  &buffer, &buffer_len);
-    if (rv != SECSuccess)
-        goto no_ticket;
-    parsed_session_ticket->flags = PR_ntohl(parsed_session_ticket->flags);
+    /* Use the ticket if it is valid and unexpired. */
+    if (parsedTicket.valid &&
+        parsedTicket.timestamp + ssl_ticket_lifetime > ssl_Time()) {
+        sslSessionID *sid;
 
-    rv = ssl3_ExtConsumeHandshakeVariable(ss, &alpn_item, 1, &buffer, &buffer_len);
-    if (rv != SECSuccess)
-        goto no_ticket;
-    if (alpn_item.len != 0) {
-        rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->alpnSelection,
-                              &alpn_item);
-        if (rv != SECSuccess)
-            goto no_ticket;
-        if (alpn_item.len >= 256)
-            goto no_ticket;
-    }
-
-    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &buffer_len);
-    if (rv != SECSuccess) {
-        goto no_ticket;
-    }
-    parsed_session_ticket->maxEarlyData = temp;
-
-#ifndef UNSAFE_FUZZER_MODE
-    /* Done parsing.  Check that all bytes have been consumed. */
-    if (buffer_len != padding_length) {
-        goto no_ticket;
-    }
-#endif
-
-    /* Use the ticket if it has not expired, otherwise free the allocated
-     * memory since the ticket is of no use.
-     */
-    if (parsed_session_ticket->timestamp != 0 &&
-        parsed_session_ticket->timestamp + ssl_ticket_lifetime >
-            ssl_Time()) {
-
-        sid = ssl3_NewSessionID(ss, PR_TRUE);
-        if (sid == NULL) {
-            rv = SECFailure;
-            goto loser;
-        }
-
-        /* Copy over parameters. */
-        sid->version = parsed_session_ticket->ssl_version;
-        sid->u.ssl3.cipherSuite = parsed_session_ticket->cipher_suite;
-        sid->u.ssl3.compression = parsed_session_ticket->compression_method;
-        sid->authType = parsed_session_ticket->authType;
-        sid->authKeyBits = parsed_session_ticket->authKeyBits;
-        sid->keaType = parsed_session_ticket->keaType;
-        sid->keaKeyBits = parsed_session_ticket->keaKeyBits;
-        sid->namedCurve = parsed_session_ticket->namedCurve;
-
-        if (SECITEM_CopyItem(NULL, &sid->u.ssl3.locked.sessionTicket.ticket,
-                             &extension_data) != SECSuccess)
-            goto no_ticket;
-        sid->u.ssl3.locked.sessionTicket.flags = parsed_session_ticket->flags;
-        sid->u.ssl3.locked.sessionTicket.max_early_data_size =
-            parsed_session_ticket->maxEarlyData;
-
-        if (parsed_session_ticket->ms_length >
-            sizeof(sid->u.ssl3.keys.wrapped_master_secret))
-            goto no_ticket;
-        PORT_Memcpy(sid->u.ssl3.keys.wrapped_master_secret,
-                    parsed_session_ticket->master_secret,
-                    parsed_session_ticket->ms_length);
-        sid->u.ssl3.keys.wrapped_master_secret_len =
-            parsed_session_ticket->ms_length;
-        sid->u.ssl3.masterWrapMech = parsed_session_ticket->msWrapMech;
-        sid->u.ssl3.keys.msIsWrapped =
-            parsed_session_ticket->ms_is_wrapped;
-        sid->u.ssl3.masterValid = PR_TRUE;
-        sid->u.ssl3.keys.resumable = PR_TRUE;
-        sid->u.ssl3.keys.extendedMasterSecretUsed = parsed_session_ticket->extendedMasterSecretUsed;
-
-        /* Copy over client cert from session ticket if there is one. */
-        if (parsed_session_ticket->peer_cert.data != NULL) {
-            if (sid->peerCert != NULL)
-                CERT_DestroyCertificate(sid->peerCert);
-            sid->peerCert = CERT_NewTempCertificate(ss->dbHandle,
-                                                    &parsed_session_ticket->peer_cert, NULL, PR_FALSE, PR_TRUE);
-            if (sid->peerCert == NULL) {
-                rv = SECFailure;
-                goto loser;
-            }
-        }
-        if (parsed_session_ticket->srvName.data != NULL) {
-            if (sid->u.ssl3.srvName.data) {
-                SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE);
-            }
-            sid->u.ssl3.srvName = parsed_session_ticket->srvName;
-            parsed_session_ticket->srvName.data = NULL;
-        }
-        if (parsed_session_ticket->alpnSelection.data != NULL) {
-            sid->u.ssl3.alpnSelection = parsed_session_ticket->alpnSelection;
-            /* So we don't free below. */
-            parsed_session_ticket->alpnSelection.data = NULL;
+        rv = ssl_CreateSIDFromTicket(ss, data, &parsedTicket, &sid);
+        if (rv != SECSuccess) {
+            goto loser; /* code already set */
         }
         ss->statelessResume = PR_TRUE;
         ss->sec.ci.sid = sid;
     }
 
-    if (0) {
-    no_ticket:
-        SSL_DBG(("%d: SSL[%d]: Session ticket parsing failed.",
-                 SSL_GETPID(), ss->fd));
-        ssl3stats = SSL_GetStatistics();
-        SSL_AtomicIncrementLong(&ssl3stats->hch_sid_ticket_parse_failures);
-    }
-    rv = SECSuccess;
+    SECITEM_ZfreeItem(&decryptedTicket, PR_FALSE);
+    PORT_Memset(&parsedTicket, 0, sizeof(parsedTicket));
+    return SECSuccess;
 
 loser:
-    /* ss->sec.ci.sid == sid if it did NOT come here via goto statement
-     * in that case do not free sid
-     */
-    if (sid && (ss->sec.ci.sid != sid)) {
-        ssl_FreeSID(sid);
-        sid = NULL;
-    }
-    if (decrypted_state != NULL) {
-        SECITEM_FreeItem(decrypted_state, PR_TRUE);
-        decrypted_state = NULL;
-    }
-
-    if (parsed_session_ticket != NULL) {
-        if (parsed_session_ticket->peer_cert.data) {
-            SECITEM_FreeItem(&parsed_session_ticket->peer_cert, PR_FALSE);
-        }
-        if (parsed_session_ticket->alpnSelection.data) {
-            SECITEM_FreeItem(&parsed_session_ticket->alpnSelection, PR_FALSE);
-        }
-        if (parsed_session_ticket->srvName.data) {
-            SECITEM_FreeItem(&parsed_session_ticket->srvName, PR_FALSE);
-        }
-        PORT_ZFree(parsed_session_ticket, sizeof(SessionTicket));
-    }
-
-    return rv;
+    SECITEM_ZfreeItem(&decryptedTicket, PR_FALSE);
+    PORT_Memset(&parsedTicket, 0, sizeof(parsedTicket));
+    return SECFailure;
 }
 
 SECStatus
 ssl3_ServerHandleSessionTicketXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
                                   SECItem *data)
 {
 
     /* Ignore the SessionTicket extension if processing is disabled. */
--- a/security/nss/lib/ssl/ssl3prot.h
+++ b/security/nss/lib/ssl/ssl3prot.h
@@ -300,32 +300,21 @@ typedef enum {
     tls13_psk_dh_ke = 1
 } TLS13PskKEModes;
 
 typedef enum {
     CLIENT_AUTH_ANONYMOUS = 0,
     CLIENT_AUTH_CERTIFICATE = 1
 } ClientAuthenticationType;
 
-typedef struct {
-    ClientAuthenticationType client_auth_type;
-    union {
-        SSL3Opaque *certificate_list;
-    } identity;
-} ClientIdentity;
-
 #define SESS_TICKET_KEY_NAME_LEN 16
 #define SESS_TICKET_KEY_NAME_PREFIX "NSS!"
 #define SESS_TICKET_KEY_NAME_PREFIX_LEN 4
 #define SESS_TICKET_KEY_VAR_NAME_LEN 12
 
 typedef struct {
     unsigned char *key_name;
     unsigned char *iv;
     SECItem encrypted_state;
     unsigned char *mac;
 } EncryptedSessionTicket;
 
-#define TLS_EX_SESS_TICKET_MAC_LENGTH 32
-
-#define TLS_STE_NO_SERVER_NAME -1
-
 #endif /* __ssl3proto_h_ */
--- a/security/nss/lib/ssl/sslimpl.h
+++ b/security/nss/lib/ssl/sslimpl.h
@@ -987,17 +987,17 @@ typedef struct SSLWrappedSymWrappingKeyS
     /* mechanism used to wrap the SymmetricWrappingKey using
      * server's public and/or private keys. */
     PRInt16 wrapMechIndex;
     PRUint16 wrapKeyIndex;
     PRUint16 wrappedSymKeyLen;
 } SSLWrappedSymWrappingKey;
 
 typedef struct SessionTicketStr {
-    PRUint16 ticket_version;
+    PRBool valid;
     SSL3ProtocolVersion ssl_version;
     ssl3CipherSuite cipher_suite;
     SSLCompressionMethod compression_method;
     SSLAuthType authType;
     PRUint32 authKeyBits;
     SSLKEAType keaType;
     PRUint32 keaKeyBits;
     const sslNamedGroupDef *namedCurve; /* For certificate lookup. */
@@ -1005,17 +1005,17 @@ typedef struct SessionTicketStr {
     /*
      * msWrapMech contains a meaningful value only if ms_is_wrapped is true.
      */
     PRUint8 ms_is_wrapped;
     CK_MECHANISM_TYPE msWrapMech;
     PRUint16 ms_length;
     SSL3Opaque master_secret[48];
     PRBool extendedMasterSecretUsed;
-    ClientIdentity client_identity;
+    ClientAuthenticationType client_auth_type;
     SECItem peer_cert;
     PRUint32 timestamp;
     PRUint32 flags;
     SECItem srvName; /* negotiated server name */
     SECItem alpnSelection;
     PRUint32 maxEarlyData;
 } SessionTicket;
 
--- a/security/nss/lib/ssl/sslsnce.c
+++ b/security/nss/lib/ssl/sslsnce.c
@@ -1803,16 +1803,17 @@ ssl_GetSessionTicketKeys(sslSocket *ss, 
     if (PR_SUCCESS != PR_CallOnceWithArg(&ssl_session_ticket_keys.setup,
                                          &ssl_GenerateSessionTicketKeysOnce,
                                          ss->pkcs11PinArg)) {
         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
         return SECFailure;
     }
 
     if (!ssl_session_ticket_keys.encKey || !ssl_session_ticket_keys.macKey) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
         return SECFailure;
     }
 
     PORT_Memcpy(keyName, ssl_session_ticket_keys.keyName,
                 sizeof(ssl_session_ticket_keys.keyName));
     *encKey = ssl_session_ticket_keys.encKey;
     *macKey = ssl_session_ticket_keys.macKey;
     return SECSuccess;
--- a/security/nss/lib/util/pkcs11uri.c
+++ b/security/nss/lib/util/pkcs11uri.c
@@ -676,17 +676,17 @@ PK11URI_ParseURI(const char *string)
 
     if (strncmp("pkcs11:", p, 7) != 0) {
         return NULL;
     }
     p += 7;
 
     result = pk11uri_AllocURI();
     if (result == NULL) {
-        goto fail;
+        return NULL;
     }
 
     /* Parse the path component and its attributes. */
     ret = pk11uri_ParseAttributes(&p, "?", ';', PK11URI_PCHAR,
                                   pattr_names, PR_ARRAY_SIZE(pattr_names),
                                   &result->pattrs, &result->vpattrs,
                                   pk11uri_ComparePathAttributeName,
                                   PR_FALSE, PR_FALSE);
--- a/security/nss/nss-tool/db/dbtool.cc
+++ b/security/nss/nss-tool/db/dbtool.cc
@@ -2,17 +2,16 @@
  * 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/. */
 
 #include "dbtool.h"
 #include "argparse.h"
 #include "scoped_ptrs.h"
 #include "util.h"
 
-#include <dirent.h>
 #include <iomanip>
 #include <iostream>
 #include <regex>
 #include <sstream>
 
 #include <cert.h>
 #include <certdb.h>
 #include <nss.h>
@@ -166,34 +165,34 @@ bool DBTool::Run(const std::vector<std::
 
   return ret;
 }
 
 bool DBTool::PathHasDBFiles(std::string path) {
   std::regex certDBPattern("cert.*\\.db");
   std::regex keyDBPattern("key.*\\.db");
 
-  DIR *dir;
-  if (!(dir = opendir(path.c_str()))) {
+  PRDir *dir = PR_OpenDir(path.c_str());
+  if (!dir) {
     std::cerr << "Directory " << path << " could not be accessed!" << std::endl;
     return false;
   }
 
-  struct dirent *ent;
+  PRDirEntry *ent;
   bool dbFileExists = false;
-  while ((ent = readdir(dir))) {
-    if (std::regex_match(ent->d_name, certDBPattern) ||
-        std::regex_match(ent->d_name, keyDBPattern) ||
-        "secmod.db" == std::string(ent->d_name)) {
+  while ((ent = PR_ReadDir(dir, PR_SKIP_BOTH))) {
+    if (std::regex_match(ent->name, certDBPattern) ||
+        std::regex_match(ent->name, keyDBPattern) ||
+        "secmod.db" == std::string(ent->name)) {
       dbFileExists = true;
       break;
     }
   }
 
-  closedir(dir);
+  (void)PR_CloseDir(dir);
   return dbFileExists;
 }
 
 void DBTool::ListCertificates() {
   ScopedCERTCertList list(PK11_ListCerts(PK11CertListAll, nullptr));
   CERTCertListNode *node;
 
   std::cout << std::setw(60) << std::left << "Certificate Nickname"
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -103904,16 +103904,22 @@
     ]
    ],
    "html/webappapis/idle-callbacks/callback-timeout.html": [
     [
      "/html/webappapis/idle-callbacks/callback-timeout.html",
      {}
     ]
    ],
+   "html/webappapis/idle-callbacks/callback-xhr-sync.html": [
+    [
+     "/html/webappapis/idle-callbacks/callback-xhr-sync.html",
+     {}
+    ]
+   ],
    "html/webappapis/idle-callbacks/cancel-invoked.html": [
     [
      "/html/webappapis/idle-callbacks/cancel-invoked.html",
      {}
     ]
    ],
    "html/webappapis/idle-callbacks/idlharness.html": [
     [
@@ -191747,16 +191753,20 @@
   "html/webappapis/idle-callbacks/callback-timeout-with-raf.html": [
    "febb81c38f530c81d9e9837df2c6d603225ecfd1",
    "testharness"
   ],
   "html/webappapis/idle-callbacks/callback-timeout.html": [
    "ba76964575cdf9b433f26c8a5d7a8183ab5c16e9",
    "testharness"
   ],
+  "html/webappapis/idle-callbacks/callback-xhr-sync.html": [
+   "79b4a278f0e35646cfdffeebf8f0523e2772bc9b",
+   "testharness"
+  ],
   "html/webappapis/idle-callbacks/cancel-invoked.html": [
    "30787d765fa435c1392bd852559042bf3c2e2553",
    "testharness"
   ],
   "html/webappapis/idle-callbacks/idlharness.html": [
    "520ee58982b43875f3caa08d7f46b9c6311be0b6",
    "testharness"
   ],
--- a/testing/web-platform/tests/encrypted-media/scripts/requestmediakeysystemaccess.js
+++ b/testing/web-platform/tests/encrypted-media/scripts/requestmediakeysystemaccess.js
@@ -79,17 +79,17 @@ function runTest(config, qualifier) {
     }
 
     if (config.keysystem !== config.keysystem.toLowerCase()) {
         expect_error(config.keysystem.toLowerCase(), [{}], 'NotSupportedError', 'Key System name is case sensitive');
     }
 
     // Tests for trivial configurations.
     expect_error(config.keysystem, [], 'TypeError', 'Empty supportedConfigurations');
-    expect_error(config.keysystem, [{}], 'NotSupportedError', 'Empty configuration');
+    expect_config(config.keysystem, [{}], {}, 'Empty configuration');
 
     // Various combinations of supportedConfigurations.
     expect_config(config.keysystem, [{
         initDataTypes: [config.initDataType],
         audioCapabilities: [{contentType: config.audioType}],
         videoCapabilities: [{contentType: config.videoType}],
     }], {
         initDataTypes: [config.initDataType],
@@ -151,23 +151,23 @@ function runTest(config, qualifier) {
 
     expect_error(config.keysystem, [{
         audioCapabilities: [
             {contentType: 'audio/mp4; codecs=vorbis'}
         ],
     }], 'NotSupportedError', 'Mismatched audio container/codec (%audiocontenttype)');
 
     expect_config(config.keysystem, [
-        {initDataTypes: ['fakeidt'], videoCapabilities: [{contentType: config.videoType}] },
-        {initDataTypes: [config.initDataType], videoCapabilities: [{contentType: config.videoType}]}
-    ], {initDataTypes: [config.initDataType], videoCapabilities: [{contentType: config.videoType}]}, 'Two configurations, one supported');
+        {initDataTypes: ['fakeidt']},
+        {initDataTypes: [config.initDataType]}
+    ], {initDataTypes: [config.initDataType]}, 'Two configurations, one supported');
 
     expect_config(config.keysystem, [
-        {initDataTypes: [config.initDataType], videoCapabilities: [{contentType: config.videoType}]},
-        {videoCapabilities: [{contentType: config.videoType}]}
+        {initDataTypes: [config.initDataType]},
+        {}
     ], {initDataTypes: [config.initDataType]}, 'Two configurations, both supported');
 
     // Audio MIME type does not support video codecs.
     expect_error(config.keysystem, [{
         audioCapabilities: [
             {contentType: 'audio/webm; codecs="vp8,vorbis"'},
             {contentType: 'audio/webm; codecs="vorbis, vp8"'},
             {contentType: 'audio/webm; codecs="vp8"'}
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/idle-callbacks/callback-xhr-sync.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+  async_test(function (t) {
+    requestIdleCallback(function() {
+      requestIdleCallback(t.step_func_done(function () {}))
+      var xhr = new XMLHttpRequest();
+      xhr.open("GET", "www.emample.com", false);
+      xhr.onload = t.step_func(function () {});
+      xhr.send(null);
+    });
+  }, "re-schedule idle callbacks after sync xhr");
+</script>
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -706,16 +706,21 @@ this.Extension = class extends Extension
       StartupCache.clearAddonData(addonData.id);
     }
 
     this.remote = ExtensionManagement.useRemoteWebExtensions;
 
     if (this.remote && processCount !== 1) {
       throw new Error("Out-of-process WebExtensions are not supported with multiple child processes");
     }
+    if (this.remote && !Services.prefs.getBoolPref("layers.popups.compositing.enabled", false)) {
+      Cu.reportError(new Error("Remote extensions should not be enabled without also setting " +
+                               "the layers.popups.compositing.enabled preference to true"));
+    }
+
     // This is filled in the first time an extension child is created.
     this.parentMessageManager = null;
 
     this.id = addonData.id;
     this.baseURI = NetUtil.newURI(this.getURL("")).QueryInterface(Ci.nsIURL);
     this.principal = this.createPrincipal();
 
     this.onStartup = null;
@@ -1090,16 +1095,21 @@ this.Extension = class extends Extension
 
     ParentAPIManager.shutdownExtension(this.id);
 
     Management.emit("shutdown", this);
     this.emit("shutdown");
 
     Services.ppmm.broadcastAsyncMessage("Extension:Shutdown", {id: this.id});
 
+    if (this.rootURI instanceof Ci.nsIJARURI) {
+      let file = this.rootURI.JARFile.QueryInterface(Ci.nsIFileURL).file;
+      Services.ppmm.broadcastAsyncMessage("Extension:FlushJarCache", {path: file.path});
+    }
+
     MessageChannel.abortResponses({extensionId: this.id});
 
     ExtensionManagement.shutdownExtension(this.uuid);
 
     return this.cleanupGeneratedFile();
   }
 
   observe(subject, topic, data) {
--- a/toolkit/components/extensions/moz.build
+++ b/toolkit/components/extensions/moz.build
@@ -53,11 +53,12 @@ BROWSER_CHROME_MANIFESTS += [
 
 MOCHITEST_MANIFESTS += [
     'test/mochitest/mochitest-remote.ini',
     'test/mochitest/mochitest.ini'
 ]
 MOCHITEST_CHROME_MANIFESTS += ['test/mochitest/chrome.ini']
 XPCSHELL_TESTS_MANIFESTS += [
     'test/xpcshell/native_messaging.ini',
+    'test/xpcshell/xpcshell-e10s.ini',
     'test/xpcshell/xpcshell-remote.ini',
     'test/xpcshell/xpcshell.ini',
 ]
--- a/toolkit/components/extensions/test/mochitest/head.js
+++ b/toolkit/components/extensions/test/mochitest/head.js
@@ -6,16 +6,17 @@ var {AppConstants} = SpecialPowers.Cu.im
 
 // We run tests under two different configurations, from mochitest.ini and
 // mochitest-remote.ini. When running from mochitest-remote.ini, the tests are
 // copied to the sub-directory "test-oop-extensions", which we detect here, and
 // use to select our configuration.
 if (location.pathname.includes("test-oop-extensions")) {
   SpecialPowers.pushPrefEnv({set: [
     ["extensions.webextensions.remote", true],
+    ["layers.popups.compositing.enabled", true],
   ]});
   // We don't want to reset this at the end of the test, so that we don't have
   // to spawn a new extension child process for each test unit.
   SpecialPowers.setIntPref("dom.ipc.keepProcessesAlive.extension", 1);
 }
 
 {
   let chromeScript = SpecialPowers.loadChromeScript(
--- a/toolkit/components/extensions/test/xpcshell/head.js
+++ b/toolkit/components/extensions/test/xpcshell/head.js
@@ -1,15 +1,16 @@
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 /* exported createHttpServer, promiseConsoleOutput, cleanupDir */
 
 Components.utils.import("resource://gre/modules/AppConstants.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Timer.jsm");
 Components.utils.import("resource://testing-common/AddonTestUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "ContentTask",
                                   "resource://testing-common/ContentTask.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Extension",
                                   "resource://gre/modules/Extension.jsm");
@@ -22,18 +23,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "HttpServer",
                                   "resource://testing-common/httpd.js");
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
                                   "resource://gre/modules/Schemas.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Services",
-                                  "resource://gre/modules/Services.jsm");
+
+Services.prefs.setBoolPref("extensions.webextensions.remote", false);
 
 ExtensionTestUtils.init(this);
 
 /**
  * Creates a new HttpServer for testing, and begins listening on the
  * specified port. Automatically shuts down the server when the test
  * unit ends.
  *
copy from toolkit/components/extensions/test/xpcshell/head_remote.js
copy to toolkit/components/extensions/test/xpcshell/head_e10s.js
--- a/toolkit/components/extensions/test/xpcshell/head_remote.js
+++ b/toolkit/components/extensions/test/xpcshell/head_remote.js
@@ -1,5 +1,4 @@
 "use strict";
 
-/* globals ExtensionTestUtils */
-
-ExtensionTestUtils.remoteContentScripts = true;
+Services.prefs.setBoolPref("extensions.webextensions.remote", true);
+Services.prefs.setIntPref("dom.ipc.keepProcessesAlive.extension", 1);
copy from toolkit/components/extensions/test/xpcshell/xpcshell.ini
copy to toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
--- a/toolkit/components/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
@@ -1,93 +1,60 @@
-[DEFAULT]
-head = head.js
-firefox-appdir = browser
-skip-if = appname == "thunderbird"
-dupe-manifest =
-support-files =
-  data/**
-  head_sync.js
-  xpcshell-content.ini
-tags = webextensions
-
-[test_csp_custom_policies.js]
-[test_csp_validator.js]
 [test_ext_alarms.js]
 [test_ext_alarms_does_not_fire.js]
 [test_ext_alarms_periodic.js]
 [test_ext_alarms_replaces.js]
-[test_ext_api_permissions.js]
 [test_ext_background_generated_load_events.js]
 [test_ext_background_generated_reload.js]
 [test_ext_background_global_history.js]
 skip-if = os == "android" # Android does not use Places for history.
 [test_ext_background_private_browsing.js]
 [test_ext_background_runtime_connect_params.js]
 [test_ext_background_sub_windows.js]
 [test_ext_background_telemetry.js]
 [test_ext_background_window_properties.js]
 skip-if = os == "android"
-[test_ext_contexts.js]
 [test_ext_contextual_identities.js]
 skip-if = os == "android" # Containers are not exposed to android.
 [test_ext_debugging_utils.js]
 [test_ext_downloads.js]
 [test_ext_downloads_download.js]
 skip-if = os == "android"
 [test_ext_downloads_misc.js]
 skip-if = os == "android" || (os=='linux' && bits==32) # linux32: bug 1324870
 [test_ext_downloads_search.js]
 skip-if = os == "android"
 [test_ext_experiments.js]
 skip-if = release_or_beta
 [test_ext_extension.js]
 [test_ext_extensionPreferencesManager.js]
 [test_ext_extensionSettingsStore.js]
 [test_ext_idle.js]
-[test_ext_json_parser.js]
 [test_ext_localStorage.js]
 [test_ext_management.js]
 [test_ext_management_uninstall_self.js]
-[test_ext_manifest_content_security_policy.js]
-[test_ext_manifest_incognito.js]
-[test_ext_manifest_minimum_chrome_version.js]
-[test_ext_manifest_themes.js]
 [test_ext_onmessage_removelistener.js]
 skip-if = true # This test no longer tests what it is meant to test.
-[test_ext_permissions.js]
-skip-if = os == "android" # Bug 1350559
 [test_ext_privacy.js]
 [test_ext_privacy_disable.js]
 [test_ext_privacy_update.js]
 [test_ext_runtime_connect_no_receiver.js]
 [test_ext_runtime_getBrowserInfo.js]
 [test_ext_runtime_getPlatformInfo.js]
 [test_ext_runtime_onInstalled_and_onStartup.js]
 [test_ext_runtime_sendMessage.js]
 [test_ext_runtime_sendMessage_args.js]
 [test_ext_runtime_sendMessage_errors.js]
 [test_ext_runtime_sendMessage_no_receiver.js]
 [test_ext_runtime_sendMessage_self.js]
-[test_ext_schemas.js]
-[test_ext_schemas_async.js]
-[test_ext_schemas_allowed_contexts.js]
-[test_ext_schemas_revoke.js]
 [test_ext_shutdown_cleanup.js]
 [test_ext_simple.js]
 [test_ext_startup_cache.js]
 [test_ext_storage.js]
 [test_ext_storage_sync.js]
 head = head.js head_sync.js
 skip-if = os == "android"
 [test_ext_storage_sync_crypto.js]
 skip-if = os == "android"
-[test_ext_themes_supported_properties.js]
 [test_ext_topSites.js]
 skip-if = os == "android"
-[test_ext_legacy_extension_context.js]
-[test_ext_legacy_extension_embedding.js]
-[test_locale_converter.js]
-[test_locale_data.js]
 [test_native_messaging.js]
 skip-if = os == "android"
-[test_proxy_scripts.js]
-[include:xpcshell-content.ini]
copy from toolkit/components/extensions/test/xpcshell/xpcshell-remote.ini
copy to toolkit/components/extensions/test/xpcshell/xpcshell-e10s.ini
--- a/toolkit/components/extensions/test/xpcshell/xpcshell-remote.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell-e10s.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-head = head.js head_remote.js
+head = head.js head_e10s.js
 tail =
 firefox-appdir = browser
 skip-if = appname == "thunderbird" || os == "android"
 dupe-manifest =
 support-files =
   data/**
   xpcshell-content.ini
 tags = webextensions webextensions-e10s
--- a/toolkit/components/extensions/test/xpcshell/xpcshell-remote.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell-remote.ini
@@ -1,12 +1,13 @@
 [DEFAULT]
-head = head.js head_remote.js
+head = head.js head_remote.js head_e10s.js
 tail =
 firefox-appdir = browser
 skip-if = appname == "thunderbird" || os == "android"
 dupe-manifest =
 support-files =
   data/**
   xpcshell-content.ini
-tags = webextensions webextensions-e10s
+tags = webextensions remote-webextensions
 
+[include:xpcshell-common.ini]
 [include:xpcshell-content.ini]
--- a/toolkit/components/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell.ini
@@ -2,92 +2,53 @@
 head = head.js
 firefox-appdir = browser
 skip-if = appname == "thunderbird"
 dupe-manifest =
 support-files =
   data/**
   head_sync.js
   xpcshell-content.ini
-tags = webextensions
+tags = webextensions in-process-webextensions
+
+# This file contains tests which are not affected by multi-process
+# configuration, or do not support out-of-process content or extensions
+# for one reason or another.
+#
+# Tests which are affected by remote content or remote extensions should
+# go in one of:
+#
+#  - xpcshell-common.ini
+#    For tests which should run in all configurations.
+#  - xpcshell-remote.ini
+#    For tests which should only run with both remote extensions and remote content.
+#  - xpcshell-content.ini
+#    For tests which rely on content pages, and should run in all configurations.
+#  - xpcshell-e10s.ini
+#    For tests which rely on conetn pages, and should only run with remote content
+#    but in-process extensions.
 
 [test_csp_custom_policies.js]
 [test_csp_validator.js]
-[test_ext_alarms.js]
-[test_ext_alarms_does_not_fire.js]
-[test_ext_alarms_periodic.js]
-[test_ext_alarms_replaces.js]
-[test_ext_api_permissions.js]
-[test_ext_background_generated_load_events.js]
-[test_ext_background_generated_reload.js]
-[test_ext_background_global_history.js]
-skip-if = os == "android" # Android does not use Places for history.
-[test_ext_background_private_browsing.js]
-[test_ext_background_runtime_connect_params.js]
-[test_ext_background_sub_windows.js]
-[test_ext_background_telemetry.js]
-[test_ext_background_window_properties.js]
-skip-if = os == "android"
 [test_ext_contexts.js]
-[test_ext_contextual_identities.js]
-skip-if = os == "android" # Containers are not exposed to android.
-[test_ext_debugging_utils.js]
-[test_ext_downloads.js]
-[test_ext_downloads_download.js]
-skip-if = os == "android"
-[test_ext_downloads_misc.js]
-skip-if = os == "android" || (os=='linux' && bits==32) # linux32: bug 1324870
-[test_ext_downloads_search.js]
-skip-if = os == "android"
-[test_ext_experiments.js]
-skip-if = release_or_beta
-[test_ext_extension.js]
-[test_ext_extensionPreferencesManager.js]
-[test_ext_extensionSettingsStore.js]
-[test_ext_idle.js]
 [test_ext_json_parser.js]
-[test_ext_localStorage.js]
-[test_ext_management.js]
-[test_ext_management_uninstall_self.js]
 [test_ext_manifest_content_security_policy.js]
 [test_ext_manifest_incognito.js]
 [test_ext_manifest_minimum_chrome_version.js]
 [test_ext_manifest_themes.js]
-[test_ext_onmessage_removelistener.js]
-skip-if = true # This test no longer tests what it is meant to test.
-[test_ext_permissions.js]
-skip-if = os == "android" # Bug 1350559
-[test_ext_privacy.js]
-[test_ext_privacy_disable.js]
-[test_ext_privacy_update.js]
-[test_ext_runtime_connect_no_receiver.js]
-[test_ext_runtime_getBrowserInfo.js]
-[test_ext_runtime_getPlatformInfo.js]
-[test_ext_runtime_onInstalled_and_onStartup.js]
-[test_ext_runtime_sendMessage.js]
-[test_ext_runtime_sendMessage_args.js]
-[test_ext_runtime_sendMessage_errors.js]
-[test_ext_runtime_sendMessage_no_receiver.js]
-[test_ext_runtime_sendMessage_self.js]
 [test_ext_schemas.js]
 [test_ext_schemas_async.js]
 [test_ext_schemas_allowed_contexts.js]
 [test_ext_schemas_revoke.js]
-[test_ext_shutdown_cleanup.js]
-[test_ext_simple.js]
-[test_ext_startup_cache.js]
-[test_ext_storage.js]
-[test_ext_storage_sync.js]
-head = head.js head_sync.js
-skip-if = os == "android"
-[test_ext_storage_sync_crypto.js]
-skip-if = os == "android"
 [test_ext_themes_supported_properties.js]
-[test_ext_topSites.js]
-skip-if = os == "android"
+[test_locale_converter.js]
+[test_locale_data.js]
+
+[test_ext_permissions.js]
+skip-if = os == "android" # Bug 1350559
+[test_ext_api_permissions.js]
+
 [test_ext_legacy_extension_context.js]
 [test_ext_legacy_extension_embedding.js]
-[test_locale_converter.js]
-[test_locale_data.js]
-[test_native_messaging.js]
-skip-if = os == "android"
 [test_proxy_scripts.js]
+
+[include:xpcshell-common.ini]
 [include:xpcshell-content.ini]
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -1645,48 +1645,16 @@
   "GRADIENT_RETENTION_TIME": {
     "record_in_processes": ["main", "content"],
     "expires_in_version": "never",
     "kind": "linear",
     "high": 10000,
     "n_buckets": 20,
     "description": "Maximum retention time for the gradient cache. (ms)"
   },
-  "WORD_CACHE_HITS_CONTENT": {
-    "record_in_processes": ["main", "content"],
-    "expires_in_version": "never",
-    "kind": "exponential",
-    "high": 256,
-    "n_buckets": 30,
-    "description": "Word cache hits, content text (chars)"
-  },
-  "WORD_CACHE_HITS_CHROME": {
-    "record_in_processes": ["main", "content"],
-    "expires_in_version": "never",
-    "kind": "exponential",
-    "high": 256,
-    "n_buckets": 30,
-    "description": "Word cache hits, chrome text (chars)"
-  },
-  "WORD_CACHE_MISSES_CONTENT": {
-    "record_in_processes": ["main", "content"],
-    "expires_in_version": "never",
-    "kind": "exponential",
-    "high": 256,
-    "n_buckets": 30,
-    "description": "Word cache misses, content text (chars)"
-  },
-  "WORD_CACHE_MISSES_CHROME": {
-    "record_in_processes": ["main", "content"],
-    "expires_in_version": "never",
-    "kind": "exponential",
-    "high": 256,
-    "n_buckets": 30,
-    "description": "Word cache misses, chrome text (chars)"
-  },
   "FONT_CACHE_HIT": {
     "record_in_processes": ["main", "content"],
     "expires_in_version": "never",
     "kind": "boolean",
     "description": "font cache hit"
   },
   "BAD_FALLBACK_FONT": {
     "record_in_processes": ["main", "content"],
--- a/toolkit/components/telemetry/Telemetry.h
+++ b/toolkit/components/telemetry/Telemetry.h
@@ -186,16 +186,17 @@ public:
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
   }
 
   explicit AutoTimer(const nsCString& aKey, TimeStamp aStart = TimeStamp::Now() MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : start(aStart)
     , key(aKey)
   {
+    MOZ_ASSERT(!aKey.IsEmpty(), "The key must not be empty.");
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
   }
 
   ~AutoTimer() {
     if (key.IsEmpty()) {
       AccumulateDelta_impl<res>::compute(id, start);
     } else {
       AccumulateDelta_impl<res>::compute(id, key, start);
--- a/toolkit/components/telemetry/histogram-whitelists.json
+++ b/toolkit/components/telemetry/histogram-whitelists.json
@@ -621,20 +621,16 @@
     "WEBCRYPTO_ALG",
     "WEBCRYPTO_EXTRACTABLE_ENC",
     "WEBCRYPTO_EXTRACTABLE_GENERATE",
     "WEBCRYPTO_EXTRACTABLE_IMPORT",
     "WEBCRYPTO_EXTRACTABLE_SIG",
     "WEBCRYPTO_METHOD",
     "WEBCRYPTO_RESOLVED",
     "WEBSOCKETS_HANDSHAKE_TYPE",
-    "WORD_CACHE_HITS_CHROME",
-    "WORD_CACHE_HITS_CONTENT",
-    "WORD_CACHE_MISSES_CHROME",
-    "WORD_CACHE_MISSES_CONTENT",
     "XMLHTTPREQUEST_ASYNC_OR_SYNC",
     "XUL_CACHE_DISABLED"
   ],
   "bug_numbers": [
     "A11Y_CONSUMERS",
     "A11Y_IATABLE_USAGE_FLAG",
     "A11Y_INSTANTIATED_FLAG",
     "A11Y_ISIMPLEDOM_USAGE_FLAG",
@@ -1598,20 +1594,16 @@
     "WEBRTC_VIDEO_QUALITY_INBOUND_PACKETLOSS_RATE",
     "WEBRTC_VIDEO_QUALITY_OUTBOUND_BANDWIDTH_KBITS",
     "WEBRTC_VIDEO_QUALITY_OUTBOUND_JITTER",
     "WEBRTC_VIDEO_QUALITY_OUTBOUND_PACKETLOSS_RATE",
     "WEBRTC_VIDEO_QUALITY_OUTBOUND_RTT",
     "WEBRTC_VIDEO_RECOVERY_AFTER_ERROR_PER_MIN",
     "WEBRTC_VIDEO_RECOVERY_BEFORE_ERROR_PER_MIN",
     "WEBSOCKETS_HANDSHAKE_TYPE",
-    "WORD_CACHE_HITS_CHROME",
-    "WORD_CACHE_HITS_CONTENT",
-    "WORD_CACHE_MISSES_CHROME",
-    "WORD_CACHE_MISSES_CONTENT",
     "XMLHTTPREQUEST_ASYNC_OR_SYNC",
     "XUL_CACHE_DISABLED"
   ],
   "n_buckets": [
     "MEMORY_JS_GC_HEAP",
     "MEMORY_HEAP_ALLOCATED",
     "SYSTEM_FONT_FALLBACK_SCRIPT",
     "HTTP_REQUEST_PER_PAGE_FROM_CACHE",
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -364,16 +364,19 @@ static const int32_t kGlassMarginAdjustm
 // this is the minimum amount of space along the edge of resizable windows
 // we will always display a resize cursor in, regardless of the underlying
 // content.
 static const int32_t kResizableBorderMinSize = 3;
 
 // Cached pointer events enabler value, True if pointer events are enabled.
 static bool gIsPointerEventsEnabled = false;
 
+// True if we should use compositing for popup widgets.
+static bool gIsPopupCompositingEnabled = false;
+
 // We should never really try to accelerate windows bigger than this. In some
 // cases this might lead to no D3D9 acceleration where we could have had it
 // but D3D9 does not reliably report when it supports bigger windows. 8192
 // is as safe as we can get, we know at least D3D10 hardware always supports
 // this, other hardware we expect to report correctly in D3D9.
 #define MAX_ACCELERATED_DIMENSION 8192
 
 // On window open (as well as after), Windows has an unfortunate habit of
@@ -666,16 +669,20 @@ nsWindow::nsWindow()
     RedirectedKeyDownMessageManager::Forget();
     if (mPointerEvents.ShouldEnableInkCollector()) {
       InkCollector::sInkCollector = new InkCollector();
     }
 
     Preferences::AddBoolVarCache(&gIsPointerEventsEnabled,
                                  "dom.w3c_pointer_events.enabled",
                                  gIsPointerEventsEnabled);
+
+    Preferences::AddBoolVarCache(&gIsPopupCompositingEnabled,
+                                 "layers.popups.compositing.enabled",
+                                 gIsPopupCompositingEnabled);
   } // !sInstanceCount
 
   mIdleService = nullptr;
 
   mSizeConstraintsScale = GetDefaultScale().scale;
 
   sInstanceCount++;
 }
@@ -7164,17 +7171,17 @@ nsWindow::IsPopup()
 }
 
 bool
 nsWindow::ShouldUseOffMainThreadCompositing()
 {
   // We don't currently support using an accelerated layer manager with
   // transparent windows so don't even try. I'm also not sure if we even
   // want to support this case. See bug 593471
-  if (mTransparencyMode == eTransparencyTransparent) {
+  if (!(HasRemoteContent() && gIsPopupCompositingEnabled) && mTransparencyMode == eTransparencyTransparent) {
     return false;
   }
 
   return nsBaseWidget::ShouldUseOffMainThreadCompositing();
 }
 
 void
 nsWindow::WindowUsesOMTC()
--- a/xpcom/threads/ThrottledEventQueue.cpp
+++ b/xpcom/threads/ThrottledEventQueue.cpp
@@ -91,29 +91,25 @@ class ThrottledEventQueue::Inner final :
   nsEventQueue mEventQueue;
 
   // written on main thread, read on any thread
   nsCOMPtr<nsIEventTarget> mBaseTarget;
 
   // any thread, protected by mutex
   nsCOMPtr<nsIRunnable> mExecutor;
 
-  // any thread, atomic
-  Atomic<uint32_t> mExecutionDepth;
-
   // any thread, protected by mutex
   bool mShutdownStarted;
 
   explicit Inner(nsIEventTarget* aBaseTarget)
     : mMutex("ThrottledEventQueue")
     , mIdleCondVar(mMutex, "ThrottledEventQueue:Idle")
     , mEventsAvailable(mMutex, "[ThrottledEventQueue::Inner.mEventsAvailable]")
     , mEventQueue(mEventsAvailable, nsEventQueue::eNormalQueue)
     , mBaseTarget(aBaseTarget)
-    , mExecutionDepth(0)
     , mShutdownStarted(false)
   {
   }
 
   ~Inner()
   {
     MOZ_ASSERT(!mExecutor);
     MOZ_ASSERT(mShutdownStarted);
@@ -134,22 +130,17 @@ class ThrottledEventQueue::Inner final :
       MutexAutoLock lock(mMutex);
 
       // We only check the name of an executor runnable when we know there is something
       // in the queue, so this should never fail.
       MOZ_ALWAYS_TRUE(mEventQueue.PeekEvent(getter_AddRefs(event), lock));
     }
 
     if (nsCOMPtr<nsINamed> named = do_QueryInterface(event)) {
-      // Increase mExecutionDepth here so that GetName is allowed to call
-      // IsOnCurrentThread on us and have it be true (in the case when we are on
-      // the right thread).
-      mExecutionDepth++;
       nsresult rv = named->GetName(aName);
-      mExecutionDepth--;
       return rv;
     }
 
     aName.AssignLiteral("non-nsINamed ThrottledEventQueue runnable");
     return NS_OK;
   }
 
   void
@@ -191,19 +182,17 @@ class ThrottledEventQueue::Inner final :
         shouldShutdown = mShutdownStarted;
         // Note, this breaks a ref cycle.
         mExecutor = nullptr;
         mIdleCondVar.NotifyAll();
       }
     }
 
     // Execute the event now that we have unlocked.
-    ++mExecutionDepth;
     Unused << event->Run();
-    --mExecutionDepth;
 
     // If shutdown was started and the queue is now empty we can now
     // finalize the shutdown.  This is performed separately at the end
     // of the method in order to wait for the event to finish running.
     if (shouldShutdown) {
       MOZ_ASSERT(IsEmpty());
       NS_DispatchToMainThread(NewRunnableMethod("ThrottledEventQueue::Inner::ShutdownComplete",
                                                 this, &Inner::ShutdownComplete));
@@ -372,37 +361,17 @@ public:
     // The base target may implement this, but we don't.  Always fail
     // to provide consistent behavior.
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   nsresult
   IsOnCurrentThread(bool* aResult)
   {
-    // Any thread
-
-    bool shutdownAndIdle = false;
-    {
-      MutexAutoLock lock(mMutex);
-      shutdownAndIdle = mShutdownStarted && mEventQueue.Count(lock) == 0;
-    }
-
-    bool onBaseTarget = false;
-    nsresult rv = mBaseTarget->IsOnCurrentThread(&onBaseTarget);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-
-    // We consider the current stack on this event target if are on
-    // the base target and one of the following is true
-    //  1) We are currently running an event OR
-    //  2) We are both shutting down and the queue is idle
-    *aResult = onBaseTarget && (mExecutionDepth || shutdownAndIdle);
-
-    return NS_OK;
+    return mBaseTarget->IsOnCurrentThread(aResult);
   }
 
   NS_DECL_THREADSAFE_ISUPPORTS
 };
 
 NS_IMPL_ISUPPORTS(ThrottledEventQueue::Inner, nsIObserver);
 
 NS_IMPL_ISUPPORTS(ThrottledEventQueue, ThrottledEventQueue, nsIEventTarget);
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -1041,17 +1041,18 @@ NS_IMETHODIMP
 nsThread::HasPendingEvents(bool* aResult)
 {
   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
     return NS_ERROR_NOT_SAME_THREAD;
   }
 
   {
     MutexAutoLock lock(mLock);
-    *aResult = mEvents->HasPendingEvent(lock);
+    *aResult = mEvents->HasPendingEvent(lock) ||
+               mIdleEvents.HasPendingEvent(lock);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsThread::RegisterIdlePeriod(already_AddRefed<nsIIdlePeriod> aIdlePeriod)
 {
   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {