Merge inbound to mozilla-central. a=merge
authorCiure Andrei <aciure@mozilla.com>
Fri, 04 May 2018 00:52:04 +0300
changeset 470432 ce7072c02389db590c487ca5863b7f00ce22336a
parent 470431 06915b0dce57dcbd368f9d456cdceb5278a40238 (current diff)
parent 470430 18937f7c5c2fe8d38b7e479c18f81816368df503 (diff)
child 470433 f92ed48557b01b4eb6f6e8c28629377fd3f44afa
child 470452 1557686206e3b365f580bca4d1f7774ff1429397
push id9182
push usernerli@mozilla.com
push dateFri, 04 May 2018 15:39:56 +0000
treeherdermozilla-beta@01ab1ea4c55a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone61.0a1
first release with
nightly linux32
ce7072c02389 / 61.0a1 / 20180503220110 / files
nightly linux64
ce7072c02389 / 61.0a1 / 20180503220110 / files
nightly mac
ce7072c02389 / 61.0a1 / 20180503220110 / files
nightly win32
ce7072c02389 / 61.0a1 / 20180503220110 / files
nightly win64
ce7072c02389 / 61.0a1 / 20180503220110 / 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 inbound to mozilla-central. a=merge
devtools/client/inspector/changes/changes.js
devtools/client/inspector/changes/components/ChangesApp.js
devtools/client/inspector/changes/reducers/changes.js
devtools/client/inspector/events/components/EventsApp.js
devtools/client/inspector/events/events.js
devtools/client/inspector/events/reducers/events.js
devtools/client/preferences/devtools-client.js
--- a/browser/base/content/test/about/browser_aboutHome_search_searchbar.js
+++ b/browser/base/content/test/about/browser_aboutHome_search_searchbar.js
@@ -12,14 +12,14 @@ add_task(async function() {
   await BrowserTestUtils.withNewTab({ gBrowser, url: "about:home" }, async function(browser) {
     await BrowserTestUtils.synthesizeMouseAtCenter("#brandLogo", {}, browser);
 
     let doc = window.document;
     let searchInput = doc.getElementById("searchbar").textbox.inputField;
     isnot(searchInput, doc.activeElement, "Search bar should not be the active element.");
 
     EventUtils.synthesizeKey("k", { accelKey: true });
-    await promiseWaitForCondition(() => doc.activeElement === searchInput);
+    await TestUtils.waitForCondition(() => doc.activeElement === searchInput);
     is(searchInput, doc.activeElement, "Search bar should be the active element.");
   });
 
   Services.prefs.clearUserPref("browser.search.widget.inNavBar");
 });
--- a/browser/base/content/test/about/head.js
+++ b/browser/base/content/test/about/head.js
@@ -18,22 +18,16 @@ function waitForCondition(condition, nex
     if (conditionPassed) {
       moveOn();
     }
     tries++;
   }, 100);
   var moveOn = function() { clearInterval(interval); nextTest(); };
 }
 
-function promiseWaitForCondition(aConditionFn) {
-  return new Promise(resolve => {
-    waitForCondition(aConditionFn, resolve, "Condition didn't pass.");
-  });
-}
-
 function whenTabLoaded(aTab, aCallback) {
   promiseTabLoadEvent(aTab).then(aCallback);
 }
 
 function promiseTabLoaded(aTab) {
   return new Promise(resolve => {
     whenTabLoaded(aTab, resolve);
   });
--- a/browser/base/content/test/performance/browser_preferences_usage.js
+++ b/browser/base/content/test/performance/browser_preferences_usage.js
@@ -88,20 +88,16 @@ add_task(async function startup() {
     "layout.css.dpi": {
       min: 45,
       max: 75,
     },
     "extensions.getAddons.cache.enabled": {
       min: 10,
       max: 55,
     },
-    "dom.max_chrome_script_run_time": {
-      min: 20,
-      max: 55,
-    },
   };
 
   let startupRecorder = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject;
   await startupRecorder.done;
 
   ok(startupRecorder.data.prefStats, "startupRecorder has prefStats");
 
   checkPrefGetters(startupRecorder.data.prefStats, max, whitelist);
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -808,27 +808,27 @@ var gPrivacyPane = {
    * - determines how long cookies are stored:
    *     0   means keep cookies until they expire
    *     2   means keep cookies until the browser is closed
    */
 
   readKeepCookiesUntil() {
     let privateBrowsing = Preferences.get("browser.privatebrowsing.autostart").value;
     if (privateBrowsing) {
-      return "2";
+      return Ci.nsICookieService.ACCEPT_SESSION;
     }
 
     let lifetimePolicy = Preferences.get("network.cookie.lifetimePolicy").value;
-    if (lifetimePolicy != Ci.nsICookieService.ACCEPT_NORMALLY &&
-      lifetimePolicy != Ci.nsICookieService.ACCEPT_SESSION &&
-      lifetimePolicy != Ci.nsICookieService.ACCEPT_FOR_N_DAYS) {
-      return Ci.nsICookieService.ACCEPT_NORMALLY;
+    if (lifetimePolicy == Ci.nsICookieService.ACCEPT_SESSION) {
+      return Ci.nsICookieService.ACCEPT_SESSION;
     }
 
-    return lifetimePolicy;
+    // network.cookie.lifetimePolicy can be set to any value, but we just
+    // support ACCEPT_SESSION and ACCEPT_NORMALLY. Let's force ACCEPT_NORMALLY.
+    return Ci.nsICookieService.ACCEPT_NORMALLY;
   },
 
   /**
    * Reads the network.cookie.cookieBehavior preference value and
    * enables/disables the rest of the cookie UI accordingly.
    *
    * Returns "0" if cookies are accepted and "2" if they are entirely disabled.
    */
--- a/devtools/client/inspector/boxmodel/box-model.js
+++ b/devtools/client/inspector/boxmodel/box-model.js
@@ -1,25 +1,23 @@
 /* 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 { getCssProperties } = require("devtools/shared/fronts/css-properties");
-
-const { InplaceEditor } = require("devtools/client/shared/inplace-editor");
-
 const {
   updateGeometryEditorEnabled,
   updateLayout,
   updateOffsetParent,
 } = require("./actions/box-model");
 
-const EditingSession = require("./utils/editing-session");
+loader.lazyRequireGetter(this, "EditingSession", "devtools/client/inspector/boxmodel/utils/editing-session");
+loader.lazyRequireGetter(this, "InplaceEditor", "devtools/client/shared/inplace-editor", true);
+loader.lazyRequireGetter(this, "getCssProperties", "devtools/shared/fronts/css-properties", true);
 
 const NUMERIC = /^-?[\d\.]+$/;
 
 /**
  * A singleton instance of the box model controllers.
  *
  * @param  {Inspector} inspector
  *         An instance of the Inspector currently loaded in the toolbox.
--- a/devtools/client/inspector/boxmodel/utils/editing-session.js
+++ b/devtools/client/inspector/boxmodel/utils/editing-session.js
@@ -1,15 +1,15 @@
 /* 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 { getCssProperties } = require("devtools/shared/fronts/css-properties");
+loader.lazyRequireGetter(this, "getCssProperties", "devtools/shared/fronts/css-properties", true);
 
 /**
  * An instance of EditingSession tracks changes that have been made during the
  * modification of box model values. All of these changes can be reverted by
  * calling revert.
  *
  * @param  {InspectorPanel} inspector
  *         The inspector panel.
@@ -17,22 +17,30 @@ const { getCssProperties } = require("de
  *         A DOM document that can be used to test style rules.
  * @param  {Array} rules
  *         An array of the style rules defined for the node being
  *         edited. These should be in order of priority, least
  *         important first.
  */
 function EditingSession({inspector, doc, elementRules}) {
   this._doc = doc;
+  this._inspector = inspector;
   this._rules = elementRules;
   this._modifications = new Map();
-  this._cssProperties = getCssProperties(inspector.toolbox);
 }
 
 EditingSession.prototype = {
+  get cssProperties() {
+    if (!this._cssProperties) {
+      this._cssProperties = getCssProperties(this._inspector.toolbox);
+    }
+
+    return this._cssProperties;
+  },
+
   /**
    * Gets the value of a single property from the CSS rule.
    *
    * @param  {StyleRuleFront} rule
    *         The CSS rule.
    * @param  {String} property
    *         The name of the property.
    * @return {String} the value.
@@ -107,18 +115,17 @@ EditingSession.prototype = {
    * @return {Promise} Resolves when the modifications are complete.
    */
   async setProperties(properties) {
     for (let property of properties) {
       // Get a RuleModificationList or RuleRewriter helper object from the
       // StyleRuleActor to make changes to CSS properties.
       // Note that RuleRewriter doesn't support modifying several properties at
       // once, so we do this in a sequence here.
-      let modifications = this._rules[0].startModifyingProperties(
-        this._cssProperties);
+      let modifications = this._rules[0].startModifyingProperties(this.cssProperties);
 
       // Remember the property so it can be reverted.
       if (!this._modifications.has(property.name)) {
         this._modifications.set(property.name,
           this.getPropertyFromRule(this._rules[0], property.name));
       }
 
       // Find the index of the property to be changed, or get the next index to
@@ -142,18 +149,17 @@ EditingSession.prototype = {
    * Reverts all of the property changes made by this instance.
    *
    * @return {Promise} Resolves when all properties have been reverted.
    */
   async revert() {
     // Revert each property that we modified previously, one by one. See
     // setProperties for information about why.
     for (let [property, value] of this._modifications) {
-      let modifications = this._rules[0].startModifyingProperties(
-        this._cssProperties);
+      let modifications = this._rules[0].startModifyingProperties(this.cssProperties);
 
       // Find the index of the property to be reverted.
       let index = this.getPropertyIndex(property);
 
       if (value != "") {
         // If the property doesn't exist anymore, insert at the beginning of the
         // rule.
         if (index === -1) {
@@ -169,15 +175,19 @@ EditingSession.prototype = {
         modifications.removeProperty(index, property);
       }
 
       await modifications.apply();
     }
   },
 
   destroy: function() {
+    this._modifications.clear();
+
+    this._cssProperties = null;
     this._doc = null;
+    this._inspector = null;
+    this._modifications = null;
     this._rules = null;
-    this._modifications.clear();
   }
 };
 
 module.exports = EditingSession;
--- a/devtools/client/inspector/computed/computed.js
+++ b/devtools/client/inspector/computed/computed.js
@@ -17,20 +17,21 @@ const {gDevTools} = require("devtools/cl
 const {getCssProperties} = require("devtools/shared/fronts/css-properties");
 const {
   VIEW_NODE_SELECTOR_TYPE,
   VIEW_NODE_PROPERTY_TYPE,
   VIEW_NODE_VALUE_TYPE,
   VIEW_NODE_IMAGE_URL_TYPE,
   VIEW_NODE_FONT_TYPE,
 } = require("devtools/client/inspector/shared/node-types");
-const StyleInspectorMenu = require("devtools/client/inspector/shared/style-inspector-menu");
 const TooltipsOverlay = require("devtools/client/inspector/shared/tooltips-overlay");
-const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
-const clipboardHelper = require("devtools/shared/platform/clipboard");
+
+loader.lazyRequireGetter(this, "StyleInspectorMenu", "devtools/client/inspector/shared/style-inspector-menu");
+loader.lazyRequireGetter(this, "KeyShortcuts", "devtools/client/shared/key-shortcuts");
+loader.lazyRequireGetter(this, "clipboardHelper", "devtools/shared/platform/clipboard");
 
 const STYLE_INSPECTOR_PROPERTIES = "devtools/shared/locales/styleinspector.properties";
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const STYLE_INSPECTOR_L10N = new LocalizationHelper(STYLE_INSPECTOR_PROPERTIES);
 
 const FILTER_CHANGED_TIMEOUT = 150;
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 
@@ -198,18 +199,16 @@ function CssComputedView(inspector, docu
   this._prefObserver = new PrefObserver("devtools.");
   this._prefObserver.on("devtools.defaultColorUnit", this._handlePrefChange);
 
   // The element that we're inspecting, and the document that it comes from.
   this._viewedElement = null;
 
   this.createStyleViews();
 
-  this._contextmenu = new StyleInspectorMenu(this, { isRuleView: false });
-
   // Add the tooltips and highlightersoverlay
   this.tooltips = new TooltipsOverlay(this);
 
   this.highlighters.addToView(this);
 }
 
 /**
  * Lookup a l10n string in the shared styleinspector string bundle.
@@ -238,16 +237,24 @@ CssComputedView.prototype = {
   _panelRefreshTimeout: null,
 
   // Toggle for zebra striping
   _darkStripe: true,
 
   // Number of visible properties
   numVisibleProperties: 0,
 
+  get contextMenu() {
+    if (!this._contextMenu) {
+      this._contextMenu = new StyleInspectorMenu(this, { isRuleView: false });
+    }
+
+    return this._contextMenu;
+  },
+
   setPageStyle: function(pageStyle) {
     this.pageStyle = pageStyle;
   },
 
   get includeBrowserStyles() {
     return this.includeBrowserStylesCheckbox.checked;
   },
 
@@ -667,17 +674,17 @@ CssComputedView.prototype = {
   focusWindow: function() {
     this.styleWindow.focus();
   },
 
   /**
    * Context menu handler.
    */
   _onContextMenu: function(event) {
-    this._contextmenu.show(event);
+    this.contextMenu.show(event);
   },
 
   _onClick: function(event) {
     let target = event.target;
 
     if (target.nodeName === "a") {
       event.stopPropagation();
       event.preventDefault();
@@ -728,20 +735,19 @@ CssComputedView.prototype = {
     // Cancel tree construction
     if (this._createViewsProcess) {
       this._createViewsProcess.cancel();
     }
     if (this._refreshProcess) {
       this._refreshProcess.cancel();
     }
 
-    // Remove context menu
-    if (this._contextmenu) {
-      this._contextmenu.destroy();
-      this._contextmenu = null;
+    if (this._contextMenu) {
+      this._contextMenu.destroy();
+      this._contextMenu = null;
     }
 
     this.tooltips.destroy();
     this.highlighters.removeFromView(this);
 
     // Remove bound listeners
     this.styleDocument.removeEventListener("mousedown", this.focusWindow);
     this.element.removeEventListener("click", this._onClick);
--- a/devtools/client/inspector/computed/test/head.js
+++ b/devtools/client/inspector/computed/test/head.js
@@ -159,17 +159,17 @@ function getComputedViewLinkByIndex(view
 /**
  * Trigger the select all action in the computed view.
  *
  * @param {CssComputedView} view
  *        The instance of the computed view panel
  */
 function selectAllText(view) {
   info("Selecting all the text");
-  view._contextmenu._onSelectAll();
+  view.contextMenu._onSelectAll();
 }
 
 /**
  * Select all the text, copy it, and check the content in the clipboard.
  *
  * @param {CssComputedView} view
  *        The instance of the computed view panel
  * @param {String} expectedPattern
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -8,29 +8,29 @@
 
 "use strict";
 
 const Services = require("Services");
 const promise = require("promise");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {executeSoon} = require("devtools/shared/DevToolsUtils");
 const {Toolbox} = require("devtools/client/framework/toolbox");
-const {PrefObserver} = require("devtools/client/shared/prefs");
 const Telemetry = require("devtools/client/shared/telemetry");
 const HighlightersOverlay = require("devtools/client/inspector/shared/highlighters-overlay");
 const ReflowTracker = require("devtools/client/inspector/shared/reflow-tracker");
 const Store = require("devtools/client/inspector/store");
 const InspectorStyleChangeTracker = require("devtools/client/inspector/shared/style-change-tracker");
 
 // Use privileged promise in panel documents to prevent having them to freeze
 // during toolbox destruction. See bug 1402779.
 const Promise = require("Promise");
 
 loader.lazyRequireGetter(this, "initCssProperties", "devtools/shared/fronts/css-properties", true);
 loader.lazyRequireGetter(this, "HTMLBreadcrumbs", "devtools/client/inspector/breadcrumbs", true);
+loader.lazyRequireGetter(this, "ThreePaneOnboardingTooltip", "devtools/client/inspector/shared/three-pane-onboarding-tooltip");
 loader.lazyRequireGetter(this, "KeyShortcuts", "devtools/client/shared/key-shortcuts");
 loader.lazyRequireGetter(this, "InspectorSearch", "devtools/client/inspector/inspector-search", true);
 loader.lazyRequireGetter(this, "ToolSidebar", "devtools/client/inspector/toolsidebar", true);
 loader.lazyRequireGetter(this, "MarkupView", "devtools/client/inspector/markup/markup");
 loader.lazyRequireGetter(this, "nodeConstants", "devtools/shared/dom-node-constants");
 loader.lazyRequireGetter(this, "Menu", "devtools/client/framework/menu");
 loader.lazyRequireGetter(this, "MenuItem", "devtools/client/framework/menu-item");
 loader.lazyRequireGetter(this, "ExtensionSidebar", "devtools/client/inspector/extensions/extension-sidebar");
@@ -50,16 +50,17 @@ const INITIAL_SIDEBAR_SIZE = 350;
 // If the toolbox's width is smaller than the given amount of pixels, the sidebar
 // automatically switches from 'landscape/horizontal' to 'portrait/vertical' mode.
 const PORTRAIT_MODE_WIDTH_THRESHOLD = 700;
 // If the toolbox's width docked to the side is smaller than the given amount of pixels,
 // the sidebar automatically switches from 'landscape/horizontal' to 'portrait/vertical'
 // mode.
 const SIDE_PORTAIT_MODE_WIDTH_THRESHOLD = 1000;
 
+const SHOW_THREE_PANE_ONBOARDING_PREF = "devtools.inspector.show-three-pane-tooltip";
 const SHOW_THREE_PANE_TOGGLE_PREF = "devtools.inspector.three-pane-toggle";
 const THREE_PANE_ENABLED_PREF = "devtools.inspector.three-pane-enabled";
 const THREE_PANE_ENABLED_SCALAR = "devtools.inspector.three_pane_enabled";
 
 /**
  * Represents an open instance of the Inspector for a tab.
  * The inspector controls the breadcrumbs, the markup view, and the sidebar
  * (computed view, rule view, font view and animation inspector).
@@ -106,27 +107,27 @@ function Inspector(toolbox) {
 
   this.store = Store();
 
   // Map [panel id => panel instance]
   // Stores all the instances of sidebar panels like rule view, computed view, ...
   this._panels = new Map();
 
   this.highlighters = new HighlightersOverlay(this);
-  this.prefsObserver = new PrefObserver("devtools.");
   this.reflowTracker = new ReflowTracker(this._target);
   this.styleChangeTracker = new InspectorStyleChangeTracker(this);
   this.telemetry = new Telemetry();
 
   // Store the URL of the target page prior to navigation in order to ensure
   // telemetry counts in the Grid Inspector are not double counted on reload.
   this.previousURL = this.target.url;
 
+  this.is3PaneModeEnabled = Services.prefs.getBoolPref(THREE_PANE_ENABLED_PREF);
   this.show3PaneToggle = Services.prefs.getBoolPref(SHOW_THREE_PANE_TOGGLE_PREF);
-  this.is3PaneModeEnabled = Services.prefs.getBoolPref(THREE_PANE_ENABLED_PREF);
+  this.show3PaneTooltip = Services.prefs.getBoolPref(SHOW_THREE_PANE_ONBOARDING_PREF);
 
   this.nodeMenuTriggerInfo = null;
 
   this._handleRejectionIfNotDestroyed = this._handleRejectionIfNotDestroyed.bind(this);
   this._onContextMenu = this._onContextMenu.bind(this);
   this._onBeforeNavigate = this._onBeforeNavigate.bind(this);
   this._onMarkupFrameLoad = this._onMarkupFrameLoad.bind(this);
   this._updateSearchResultsLabel = this._updateSearchResultsLabel.bind(this);
@@ -291,16 +292,20 @@ Inspector.prototype = {
       this.selection.setNodeFront(defaultSelection, { reason: "inspector-open" });
       await onAllPanelsUpdated;
       await this.markup.expandNode(this.selection.nodeFront);
     }
 
     // Setup the toolbar only now because it may depend on the document.
     await this.setupToolbar();
 
+    if (this.show3PaneTooltip) {
+      this.threePaneTooltip = new ThreePaneOnboardingTooltip(this.toolbox, this.panelDoc);
+    }
+
     // Log the 3 pane inspector setting on inspector open. The question we want to answer
     // is:
     // "What proportion of users use the 3 pane vs 2 pane inspector on inspector open?"
     this.telemetry.logKeyedScalar(THREE_PANE_ENABLED_SCALAR, this.is3PaneModeEnabled, 1);
 
     this.emit("ready");
     return this;
   },
@@ -455,16 +460,32 @@ Inspector.prototype = {
     if (!this._InspectorTabPanel) {
       this._InspectorTabPanel =
         this.React.createFactory(this.browserRequire(
         "devtools/client/inspector/components/InspectorTabPanel"));
     }
     return this._InspectorTabPanel;
   },
 
+  get InspectorSplitBox() {
+    if (!this._InspectorSplitBox) {
+      this._InspectorSplitBox = this.React.createFactory(this.browserRequire(
+        "devtools/client/shared/components/splitter/SplitBox"));
+    }
+    return this._InspectorSplitBox;
+  },
+
+  get TabBar() {
+    if (!this._TabBar) {
+      this._TabBar = this.React.createFactory(this.browserRequire(
+        "devtools/client/shared/components/tabs/TabBar"));
+    }
+    return this._TabBar;
+  },
+
   /**
    * Check if the inspector should use the landscape mode.
    *
    * @return {Boolean} true if the inspector should be in landscape mode.
    */
   useLandscapeMode: function() {
     let { clientWidth } = this.panelDoc.getElementById("inspector-splitter-box");
     return this.is3PaneModeEnabled && this.toolbox.hostType == Toolbox.HostType.SIDE ?
@@ -472,32 +493,30 @@ Inspector.prototype = {
       clientWidth > PORTRAIT_MODE_WIDTH_THRESHOLD;
   },
 
   /**
    * Build Splitter located between the main and side area of
    * the Inspector panel.
    */
   setupSplitter: function() {
-    let SplitBox = this.React.createFactory(this.browserRequire(
-      "devtools/client/shared/components/splitter/SplitBox"));
     let { width, height, splitSidebarWidth } = this.getSidebarSize();
 
-    let splitter = SplitBox({
+    let splitter = this.InspectorSplitBox({
       className: "inspector-sidebar-splitter",
       initialWidth: width,
       initialHeight: height,
       minSize: "10%",
       maxSize: "80%",
       splitterSize: 1,
       endPanelControl: true,
       startPanel: this.InspectorTabPanel({
         id: "inspector-main-content"
       }),
-      endPanel: SplitBox({
+      endPanel: this.InspectorSplitBox({
         initialWidth: splitSidebarWidth,
         minSize: 10,
         maxSize: "80%",
         splitterSize: this.is3PaneModeEnabled ? 1 : 0,
         endPanelControl: this.is3PaneModeEnabled,
         startPanel: this.InspectorTabPanel({
           id: "inspector-rules-container"
         }),
@@ -1324,16 +1343,19 @@ Inspector.prototype = {
 
     if (this.walker) {
       this.walker.off("new-root", this.onNewRoot);
       this.pageStyle = null;
     }
 
     this.cancelUpdate();
 
+    this.selection.off("new-node-front", this.onNewSelection);
+    this.selection.off("detached-front", this.onDetached);
+    this.sidebar.off("select", this.onSidebarSelect);
     this.target.off("will-navigate", this._onBeforeNavigate);
     this.target.off("thread-paused", this.updateDebuggerPausedWarning);
     this.target.off("thread-resumed", this.updateDebuggerPausedWarning);
     this._toolbox.off("select", this.updateDebuggerPausedWarning);
 
     for (let [, panel] of this._panels) {
       panel.destroy();
     }
@@ -1346,54 +1368,51 @@ Inspector.prototype = {
     if (this.fontinspector) {
       this.fontinspector.destroy();
     }
 
     if (this.animationinspector) {
       this.animationinspector.destroy();
     }
 
-    let cssPropertiesDestroyer = this._cssProperties.front.destroy();
+    if (this.threePaneTooltip) {
+      this.threePaneTooltip.destroy();
+    }
 
-    this.sidebar.off("select", this.onSidebarSelect);
+    let cssPropertiesDestroyer = this._cssProperties.front.destroy();
     let sidebarDestroyer = this.sidebar.destroy();
-
     let ruleViewSideBarDestroyer = this.ruleViewSideBar ?
       this.ruleViewSideBar.destroy() : null;
+    let markupDestroyer = this._destroyMarkup();
+    let highlighterDestroyer = this.highlighters.destroy();
 
     this.teardownSplitter();
+    this.teardownToolbar();
 
-    this.teardownToolbar();
     this.breadcrumbs.destroy();
-    this.selection.off("new-node-front", this.onNewSelection);
-    this.selection.off("detached-front", this.onDetached);
-
-    let markupDestroyer = this._destroyMarkup();
-
-    let highlighterDestroyer = this.highlighters.destroy();
-    this.prefsObserver.destroy();
     this.reflowTracker.destroy();
     this.styleChangeTracker.destroy();
     this.search.destroy();
 
     this._toolbox = null;
     this.breadcrumbs = null;
     this.highlighters = null;
     this.is3PaneModeEnabled = null;
     this.panelDoc = null;
     this.panelWin.inspector = null;
     this.panelWin = null;
-    this.prefsObserver = null;
     this.resultsLength = null;
     this.search = null;
     this.searchBox = null;
     this.show3PaneToggle = null;
+    this.show3PaneTooltip = null;
     this.sidebar = null;
     this.store = null;
     this.target = null;
+    this.threePaneTooltip = null;
 
     this._panelDestroyer = promise.all([
       highlighterDestroyer,
       cssPropertiesDestroyer,
       markupDestroyer,
       sidebarDestroyer,
       ruleViewSideBarDestroyer
     ]);
--- a/devtools/client/inspector/rules/rules.js
+++ b/devtools/client/inspector/rules/rules.js
@@ -10,38 +10,39 @@ const promise = require("promise");
 const Services = require("Services");
 const {l10n} = require("devtools/shared/inspector/css-logic");
 const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
 const OutputParser = require("devtools/client/shared/output-parser");
 const {PrefObserver} = require("devtools/client/shared/prefs");
 const ElementStyle = require("devtools/client/inspector/rules/models/element-style");
 const Rule = require("devtools/client/inspector/rules/models/rule");
 const RuleEditor = require("devtools/client/inspector/rules/views/rule-editor");
-const ClassListPreviewer = require("devtools/client/inspector/rules/views/class-list-previewer");
 const {getCssProperties} = require("devtools/shared/fronts/css-properties");
 const {
   VIEW_NODE_SELECTOR_TYPE,
   VIEW_NODE_PROPERTY_TYPE,
   VIEW_NODE_VALUE_TYPE,
   VIEW_NODE_IMAGE_URL_TYPE,
   VIEW_NODE_LOCATION_TYPE,
   VIEW_NODE_SHAPE_POINT_TYPE,
   VIEW_NODE_SHAPE_SWATCH,
   VIEW_NODE_VARIABLE_TYPE,
   VIEW_NODE_FONT_TYPE,
 } = require("devtools/client/inspector/shared/node-types");
-const StyleInspectorMenu = require("devtools/client/inspector/shared/style-inspector-menu");
 const TooltipsOverlay = require("devtools/client/inspector/shared/tooltips-overlay");
 const {createChild, promiseWarn} = require("devtools/client/inspector/shared/utils");
 const {debounce} = require("devtools/shared/debounce");
 const EventEmitter = require("devtools/shared/event-emitter");
-const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
-const clipboardHelper = require("devtools/shared/platform/clipboard");
 const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
 
+loader.lazyRequireGetter(this, "ClassListPreviewer", "devtools/client/inspector/rules/views/class-list-previewer");
+loader.lazyRequireGetter(this, "StyleInspectorMenu", "devtools/client/inspector/shared/style-inspector-menu");
+loader.lazyRequireGetter(this, "KeyShortcuts", "devtools/client/shared/key-shortcuts");
+loader.lazyRequireGetter(this, "clipboardHelper", "devtools/shared/platform/clipboard");
+
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const PREF_UA_STYLES = "devtools.inspector.showUserAgentStyles";
 const PREF_DEFAULT_COLOR_UNIT = "devtools.defaultColorUnit";
 const PREF_FONT_EDITOR = "devtools.inspector.fonteditor.enabled";
 const FILTER_CHANGED_TIMEOUT = 150;
 
 // This is used to parse user input when filtering.
 const FILTER_PROP_RE = /\s*([^:\s]*)\s*:\s*(.*?)\s*;?$/;
@@ -178,37 +179,49 @@ function CssRuleView(inspector, document
   // The popup will be attached to the toolbox document.
   this.popup = new AutocompletePopup(inspector._toolbox.doc, {
     autoSelect: true,
     theme: "auto"
   });
 
   this._showEmpty();
 
-  this._contextmenu = new StyleInspectorMenu(this, { isRuleView: true });
-
   // Add the tooltips and highlighters to the view
   this.tooltips = new TooltipsOverlay(this);
 
   this.highlighters.addToView(this);
-
-  this.classListPreviewer = new ClassListPreviewer(this.inspector, this.classPanel);
 }
 
 CssRuleView.prototype = {
   // The element that we're inspecting.
   _viewedElement: null,
 
   // Used for cancelling timeouts in the style filter.
   _filterChangedTimeout: null,
 
   // Empty, unconnected element of the same type as this node, used
   // to figure out how shorthand properties will be parsed.
   _dummyElement: null,
 
+  get classListPreviewer() {
+    if (!this._classListPreviewer) {
+      this._classListPreviewer = new ClassListPreviewer(this.inspector, this.classPanel);
+    }
+
+    return this._classListPreviewer;
+  },
+
+  get contextMenu() {
+    if (!this._contextMenu) {
+      this._contextMenu = new StyleInspectorMenu(this, { isRuleView: true });
+    }
+
+    return this._contextMenu;
+  },
+
   // Get the dummy elemenet.
   get dummyElement() {
     return this._dummyElement;
   },
 
   // Get the filter search value.
   get searchValue() {
     return this.searchField.value.toLowerCase();
@@ -440,17 +453,17 @@ CssRuleView.prototype = {
         event.originalTarget.closest("input:not([type])") ||
         event.originalTarget.closest("textarea")) {
       return;
     }
 
     event.stopPropagation();
     event.preventDefault();
 
-    this._contextmenu.show(event);
+    this.contextMenu.show(event);
   },
 
   /**
    * Callback for copy event. Copy the selected text.
    *
    * @param {Event} event
    *        copy event object.
    */
@@ -720,25 +733,28 @@ CssRuleView.prototype = {
     this._prefObserver.off(
       PREF_DEFAULT_COLOR_UNIT,
       this._handleDefaultColorUnitPrefChange
     );
     this._prefObserver.destroy();
 
     this._outputParser = null;
 
-    // Remove context menu
-    if (this._contextmenu) {
-      this._contextmenu.destroy();
-      this._contextmenu = null;
+    if (this._classListPreviewer) {
+      this._classListPreviewer.destroy();
+      this._classListPreviewer = null;
+    }
+
+    if (this._contextMenu) {
+      this._contextMenu.destroy();
+      this._contextMenu = null;
     }
 
     this.tooltips.destroy();
     this.highlighters.removeFromView(this);
-    this.classListPreviewer.destroy();
     this.unselectAllRules();
 
     // Remove bound listeners
     this.shortcuts.destroy();
     this.element.removeEventListener("copy", this._onCopy);
     this.element.removeEventListener("contextmenu", this._onContextMenu);
     this.addRuleButton.removeEventListener("click", this._onAddRule);
     this.searchField.removeEventListener("input", this._onFilterStyles);
--- a/devtools/client/inspector/rules/test/browser_rules_select-and-copy-styles.js
+++ b/devtools/client/inspector/rules/test/browser_rules_select-and-copy-styles.js
@@ -85,17 +85,17 @@ async function checkCopySelection(view) 
 async function checkSelectAll(view) {
   info("Testing select-all copy");
 
   let contentDoc = view.styleDocument;
   let prop = contentDoc.querySelector(".ruleview-property");
 
   info("Checking that _SelectAll() then copy returns the correct " +
     "clipboard value");
-  view._contextmenu._onSelectAll();
+  view.contextMenu._onSelectAll();
   let expectedPattern = "element {[\\r\\n]+" +
                         "    margin: 10em;[\\r\\n]+" +
                         "    font-size: 14pt;[\\r\\n]+" +
                         "    font-family: helvetica, sans-serif;[\\r\\n]+" +
                         "    color: #AAA;[\\r\\n]+" +
                         "}[\\r\\n]+" +
                         "html {[\\r\\n]+" +
                         "    color: #000000;[\\r\\n]+" +
--- a/devtools/client/inspector/rules/views/class-list-previewer.js
+++ b/devtools/client/inspector/rules/views/class-list-previewer.js
@@ -222,16 +222,18 @@ function ClassListPreviewer(inspector, c
   this.classesEl = this.doc.createElement("div");
   this.classesEl.classList.add("classes");
   this.containerEl.appendChild(this.classesEl);
 
   // Start listening for interesting events.
   this.inspector.selection.on("new-node-front", this.onNewSelection);
   this.containerEl.addEventListener("input", this.onCheckBoxChanged);
   this.model.on("current-node-class-changed", this.onCurrentNodeClassChanged);
+
+  this.onNewSelection();
 }
 
 ClassListPreviewer.prototype = {
   destroy() {
     this.inspector.selection.off("new-node-front", this.onNewSelection);
     this.addEl.removeEventListener("keypress", this.onKeyPress);
     this.containerEl.removeEventListener("input", this.onCheckBoxChanged);
 
--- a/devtools/client/inspector/shared/moz.build
+++ b/devtools/client/inspector/shared/moz.build
@@ -6,13 +6,14 @@
 
 DevToolsModules(
     'dom-node-preview.js',
     'highlighters-overlay.js',
     'node-types.js',
     'reflow-tracker.js',
     'style-change-tracker.js',
     'style-inspector-menu.js',
+    'three-pane-onboarding-tooltip.js',
     'tooltips-overlay.js',
     'utils.js'
 )
 
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
--- a/devtools/client/inspector/shared/style-inspector-menu.js
+++ b/devtools/client/inspector/shared/style-inspector-menu.js
@@ -2,28 +2,27 @@
 /* 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 Services = require("Services");
-
-const Menu = require("devtools/client/framework/menu");
-const MenuItem = require("devtools/client/framework/menu-item");
-
 const {
   VIEW_NODE_SELECTOR_TYPE,
   VIEW_NODE_PROPERTY_TYPE,
   VIEW_NODE_VALUE_TYPE,
   VIEW_NODE_IMAGE_URL_TYPE,
   VIEW_NODE_LOCATION_TYPE,
 } = require("devtools/client/inspector/shared/node-types");
-const clipboardHelper = require("devtools/shared/platform/clipboard");
+
+loader.lazyRequireGetter(this, "Menu", "devtools/client/framework/menu");
+loader.lazyRequireGetter(this, "MenuItem", "devtools/client/framework/menu-item");
+loader.lazyRequireGetter(this, "clipboardHelper", "devtools/shared/platform/clipboard");
 
 const STYLE_INSPECTOR_PROPERTIES = "devtools/shared/locales/styleinspector.properties";
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const STYLE_INSPECTOR_L10N = new LocalizationHelper(STYLE_INSPECTOR_PROPERTIES);
 
 const PREF_ORIG_SOURCES = "devtools.source-map.client-service.enabled";
 
 /**
--- a/devtools/client/inspector/shared/test/browser_styleinspector_context-menu-copy-color_01.js
+++ b/devtools/client/inspector/shared/test/browser_styleinspector_context-menu-copy-color_01.js
@@ -63,23 +63,23 @@ function testIsColorPopupOnAllNodes(view
  * @param object view
  *               A CSSRuleView or CssComputedView instance.
  * @param Node node
  *             A node to check.
  */
 function testIsColorPopupOnNode(view, node) {
   info("Testing node " + node);
   view.styleDocument.popupNode = node;
-  view._contextmenu._colorToCopy = "";
+  view.contextMenu._colorToCopy = "";
 
-  let result = view._contextmenu._isColorPopup();
+  let result = view.contextMenu._isColorPopup();
   let correct = isColorValueNode(node);
 
   is(result, correct, "_isColorPopup returned the expected value " + correct);
-  is(view._contextmenu._colorToCopy, (correct) ? "rgb(18, 58, 188)" : "",
+  is(view.contextMenu._colorToCopy, (correct) ? "rgb(18, 58, 188)" : "",
      "_colorToCopy was set to the expected value");
 }
 
 /**
  * Check if a node is part of color value i.e. it has parent with a 'data-color'
  * attribute.
  */
 function isColorValueNode(node) {
--- a/devtools/client/inspector/shared/test/browser_styleinspector_context-menu-copy-color_02.js
+++ b/devtools/client/inspector/shared/test/browser_styleinspector_context-menu-copy-color_02.js
@@ -62,17 +62,17 @@ async function testManualEdit(inspector,
   await wait(1);
 
   let colorValueElement = getRuleViewProperty(view, "div", "color")
     .valueSpan.firstChild;
   is(colorValueElement.dataset.color, newColor, "data-color was updated");
 
   view.styleDocument.popupNode = colorValueElement;
 
-  let contextMenu = view._contextmenu;
+  let contextMenu = view.contextMenu;
   contextMenu._isColorPopup();
   is(contextMenu._colorToCopy, newColor, "_colorToCopy has the new value");
 }
 
 async function testColorPickerEdit(inspector, view) {
   info("Testing colors edited via color picker");
   await selectNode("div", inspector);
 
@@ -88,12 +88,12 @@ async function testColorPickerEdit(inspe
   let rgbaColor = [83, 183, 89, 1];
   let rgbaColorText = "rgba(83, 183, 89, 1)";
   await simulateColorPickerChange(view, picker, rgbaColor);
 
   is(swatchElement.parentNode.dataset.color, rgbaColorText,
     "data-color was updated");
   view.styleDocument.popupNode = swatchElement;
 
-  let contextMenu = view._contextmenu;
+  let contextMenu = view.contextMenu;
   contextMenu._isColorPopup();
   is(contextMenu._colorToCopy, rgbaColorText, "_colorToCopy has the new value");
 }
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/shared/three-pane-onboarding-tooltip.js
@@ -0,0 +1,110 @@
+/* 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 Services = require("Services");
+const { openWebLink } = require("devtools/client/shared/link");
+const { HTMLTooltip } = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
+
+const { LocalizationHelper } = require("devtools/shared/l10n");
+const L10N = new LocalizationHelper("devtools/client/locales/inspector.properties");
+
+const SHOW_THREE_PANE_ONBOARDING_PREF = "devtools.inspector.show-three-pane-tooltip";
+
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+const CONTAINER_WIDTH = 300;
+const LEARN_MORE_LINK = "https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/Use_the_3-pane_inspector?utm_source=devtools&utm_medium=3-pane-onboarding";
+
+/**
+ * Three pane inspector onboarding tooltip that is shown on the 3 pane inspector toggle
+ * button when the pref is on.
+ */
+class ThreePaneOnboardingTooltip {
+  constructor(toolbox, doc) {
+    this.toolbox = toolbox;
+    this.doc = doc;
+    this.tooltip = new HTMLTooltip(this.toolbox.doc, {
+      type: "arrow",
+      useXulWrapper: true,
+    });
+
+    this.onCloseButtonClick = this.onCloseButtonClick.bind(this);
+    this.onLearnMoreLinkClick = this.onLearnMoreLinkClick.bind(this);
+
+    const container = doc.createElementNS(XHTML_NS, "div");
+    container.className = "three-pane-onboarding-container";
+
+    const icon = doc.createElementNS(XHTML_NS, "span");
+    icon.className = "three-pane-onboarding-icon";
+    container.appendChild(icon);
+
+    const content = doc.createElementNS(XHTML_NS, "div");
+    content.className = "three-pane-onboarding-content";
+    container.appendChild(content);
+
+    const message = doc.createElementNS(XHTML_NS, "div");
+    const learnMoreString = L10N.getStr("inspector.threePaneOnboarding.learnMoreLink");
+    const messageString = L10N.getFormatStr("inspector.threePaneOnboarding.content",
+      learnMoreString);
+    const learnMoreStartIndex = messageString.indexOf(learnMoreString);
+
+    message.append(messageString.substring(0, learnMoreStartIndex));
+
+    this.learnMoreLink = doc.createElementNS(XHTML_NS, "a");
+    this.learnMoreLink.className = "three-pane-onboarding-link";
+    this.learnMoreLink.href = "#";
+    this.learnMoreLink.textContent = learnMoreString;
+
+    message.append(this.learnMoreLink);
+    message.append(messageString.substring(learnMoreStartIndex + learnMoreString.length));
+    content.append(message);
+
+    this.closeButton = doc.createElementNS(XHTML_NS, "button");
+    this.closeButton.className = "three-pane-onboarding-close-button devtools-button";
+    container.appendChild(this.closeButton);
+
+    this.closeButton.addEventListener("click", this.onCloseButtonClick);
+    this.learnMoreLink.addEventListener("click", this.onLearnMoreLinkClick);
+
+    this.tooltip.setContent(container, { width: CONTAINER_WIDTH });
+    this.tooltip.show(this.doc.querySelector("#inspector-sidebar .sidebar-toggle"), {
+      position: "top",
+    });
+  }
+
+  destroy() {
+    this.closeButton.removeEventListener("click", this.onCloseButtonClick);
+    this.learnMoreLink.removeEventListener("click", this.onLearnMoreLinkClick);
+
+    this.tooltip.destroy();
+
+    this.closeButton = null;
+    this.doc = null;
+    this.learnMoreLink = null;
+    this.toolbox = null;
+    this.tooltip = null;
+  }
+
+  /**
+   * Handler for the "click" event on the close button. Hides the onboarding tooltip
+   * and sets the show three pane onboarding tooltip pref to false.
+   */
+  onCloseButtonClick() {
+    Services.prefs.setBoolPref(SHOW_THREE_PANE_ONBOARDING_PREF, false);
+    this.tooltip.hide();
+  }
+
+  /**
+   * Handler for the "click" event on the learn more button. Hides the onboarding tooltip
+   * and opens the link to the mdn page in a new tab.
+   */
+  onLearnMoreLinkClick() {
+    Services.prefs.setBoolPref(SHOW_THREE_PANE_ONBOARDING_PREF, false);
+    this.tooltip.hide();
+    openWebLink(LEARN_MORE_LINK);
+  }
+}
+
+module.exports = ThreePaneOnboardingTooltip;
--- a/devtools/client/inspector/test/shared-head.js
+++ b/devtools/client/inspector/test/shared-head.js
@@ -589,17 +589,17 @@ function buildContextMenuItems(menu) {
 
 /**
  * Open the style editor context menu and return all of it's items in a flat array
  * @param {CssRuleView} view
  *        The instance of the rule-view panel
  * @return An array of MenuItems
  */
 function openStyleContextMenuAndGetAllItems(view, target) {
-  const menu = view._contextmenu._openMenu({target: target});
+  const menu = view.contextMenu._openMenu({target: target});
   return buildContextMenuItems(menu);
 }
 
 /**
  * Open the inspector menu and return all of it's items in a flat array
  * @param {InspectorPanel} inspector
  * @param {Object} options to pass into openMenu
  * @return An array of MenuItems
--- a/devtools/client/inspector/toolsidebar.js
+++ b/devtools/client/inspector/toolsidebar.js
@@ -63,23 +63,24 @@ ToolSidebar.prototype = {
   get browserRequire() {
     return this._toolPanel.browserRequire;
   },
 
   get InspectorTabPanel() {
     return this._toolPanel.InspectorTabPanel;
   },
 
+  get TabBar() {
+    return this._toolPanel.TabBar;
+  },
+
   // Rendering
 
   render: function() {
-    let Tabbar = this.React.createFactory(this.browserRequire(
-      "devtools/client/shared/components/tabs/TabBar"));
-
-    let sidebar = Tabbar({
+    let sidebar = this.TabBar({
       menuDocument: this._toolPanel._toolbox.doc,
       showAllTabsMenu: true,
       sidebarToggleButton: this._options.sidebarToggleButton,
       onSelect: this.handleSelectionChange.bind(this),
     });
 
     this._tabbar = this.ReactDOM.render(sidebar, this._tabbox);
   },
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -112,16 +112,17 @@ devtools.jar:
     skin/toolbars.css (themes/toolbars.css)
     skin/toolbox.css (themes/toolbox.css)
     skin/tooltips.css (themes/tooltips.css)
     skin/images/accessibility.svg (themes/images/accessibility.svg)
     skin/images/add.svg (themes/images/add.svg)
     skin/images/breadcrumbs-divider.svg (themes/images/breadcrumbs-divider.svg)
     skin/images/filters.svg (themes/images/filters.svg)
     skin/images/filter-swatch.svg (themes/images/filter-swatch.svg)
+    skin/images/fox-smiling.svg (themes/images/fox-smiling.svg)
     skin/images/grid.svg (themes/images/grid.svg)
     skin/images/angle-swatch.svg (themes/images/angle-swatch.svg)
     skin/images/pseudo-class.svg (themes/images/pseudo-class.svg)
     skin/images/controls.png (themes/images/controls.png)
     skin/images/controls@2x.png (themes/images/controls@2x.png)
     skin/images/copy.svg (themes/images/copy.svg)
     skin/images/animation-fast-track.svg (themes/images/animation-fast-track.svg)
     skin/images/performance-details-waterfall.svg (themes/images/performance-details-waterfall.svg)
--- a/devtools/client/locales/en-US/inspector.properties
+++ b/devtools/client/locales/en-US/inspector.properties
@@ -459,8 +459,16 @@ inspector.classPanel.newClass.placeholde
 # LOCALIZATION NOTE (inspector.classPanel.noClasses): This is the text displayed in the
 # class panel when the current element has no classes applied.
 inspector.classPanel.noClasses=No classes on this element
 
 # LOCALIZATION NOTE (inspector.noProperties): In the case where there are no CSS
 # properties to display e.g. due to search criteria this message is
 # displayed.
 inspector.noProperties=No CSS properties found.
+
+# LOCALIZATION NOTE (inspector.threePaneOnboarding.content,
+# inspector.threePaneOnboarding.learnMoreLink): This is the content shown in the 3 pane
+# inspector onboarding tooltip that is displayed on top of the 3 pane inspector toggle
+# button. %S in the content will be replaced by a link at run time with the learnMoreLink
+# string.
+inspector.threePaneOnboarding.content=New: 3-pane mode lets you see both CSS rules and Layout tools. Click this button to toggle. %S
+inspector.threePaneOnboarding.learnMoreLink=Learn more
--- a/devtools/client/preferences/devtools-client.js
+++ b/devtools/client/preferences/devtools-client.js
@@ -52,16 +52,22 @@ pref("devtools.inspector.remote", false)
 // Enable the 3 pane mode toggle in the inspector
 #if defined(NIGHTLY_BUILD)
 pref("devtools.inspector.three-pane-toggle", true);
 #else
 pref("devtools.inspector.three-pane-toggle", false);
 #endif
 // Enable the 3 pane mode in the inspector
 pref("devtools.inspector.three-pane-enabled", false);
+// Show the 3 pane onboarding tooltip in the inspector
+#if defined(NIGHTLY_BUILD)
+pref("devtools.inspector.show-three-pane-tooltip", true);
+#else
+pref("devtools.inspector.show-three-pane-tooltip", false);
+#endif
 // Collapse pseudo-elements by default in the rule-view
 pref("devtools.inspector.show_pseudo_elements", false);
 // The default size for image preview tooltips in the rule-view/computed-view/markup-view
 pref("devtools.inspector.imagePreviewTooltipSize", 300);
 // Enable user agent style inspection in rule-view
 pref("devtools.inspector.showUserAgentStyles", false);
 // Show all native anonymous content (like controls in <video> tags)
 pref("devtools.inspector.showAllAnonymousContent", false);
--- a/devtools/client/shared/test/shared-head.js
+++ b/devtools/client/shared/test/shared-head.js
@@ -103,18 +103,20 @@ function loadFrameScriptUtils(browser = 
   info("Loading the helper frame script " + frameURL);
   mm.loadFrameScript(frameURL, false);
   SimpleTest.registerCleanupFunction(() => {
     mm = null;
   });
   return mm;
 }
 
+Services.prefs.setBoolPref("devtools.inspector.show-three-pane-tooltip", false);
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.dump.emit");
+  Services.prefs.clearUserPref("devtools.inspector.show-three-pane-tooltip");
   Services.prefs.clearUserPref("devtools.toolbox.host");
   Services.prefs.clearUserPref("devtools.toolbox.previousHost");
   Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
   Services.prefs.clearUserPref("devtools.toolbox.splitconsoleHeight");
 });
 
 registerCleanupFunction(async function cleanup() {
   while (gBrowser.tabs.length > 1) {
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/fox-smiling.svg
@@ -0,0 +1,37 @@
+<!-- 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/. -->
+<svg version="1.0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="72px" height="72px" viewBox="0 0 72 72" enable-background="new 0 0 72 72" xml:space="preserve">
+<path fill="#FFD8CA" d="M30.4,61C15.5,60,7.7,54.7,0.3,41.4C0.1,41.1,0,40.7,0,40.4h8.5l0-3.2h0c0-0.2,0-0.4,0-0.6
+	c0-4.9,3.9-8.8,8.8-8.8c4.9,0,8.8,3.9,8.8,8.8c0,0.2,0,0.4,0,0.6h0v15.7C26.1,56.2,27.8,59.2,30.4,61z M63.5,40.4v-3.2h0
+	c0-0.2,0-0.5,0-0.7c0-4.9-3.9-8.8-8.8-8.8c-4.8,0-8.7,3.8-8.8,8.5v0.5c0,0.2,0,0.3,0,0.5h0v15.7c0,3.4-1.7,6.3-4.3,8.1
+	c14.9-1,22.7-6.2,30.1-19.5c0.2-0.3,0.3-0.7,0.2-1.1H63.5z"/>
+<path fill="#FF6F1F" d="M42.5,60.3c0.1,0,0.1-0.1,0.2-0.1c0.2-0.2,0.4-0.4,0.6-0.6c0.1-0.1,0.1-0.1,0.2-0.2c0.2-0.2,0.4-0.5,0.6-0.7
+	c0,0,0.1-0.1,0.1-0.1c0.2-0.3,0.4-0.6,0.6-1c0,0,0,0,0,0c0.2-0.3,0.3-0.6,0.4-1c0-0.1,0.1-0.2,0.1-0.3c0.1-0.2,0.2-0.5,0.2-0.7
+	c0-0.1,0.1-0.2,0.1-0.4c0.1-0.3,0.1-0.5,0.1-0.8c0-0.1,0-0.2,0.1-0.3c0-0.4,0.1-0.8,0.1-1.1v-1.2V37.1h0c0-0.2,0-0.3,0-0.5v-0.5
+	c0.1-4.7,4-8.5,8.8-8.5c4.9,0,8.8,3.9,8.8,8.8c0,0.2,0,0.5,0,0.7h0v3.2H72c0-0.4-0.1-0.8-0.3-1.1c-2-3.7-4.1-7.2-6.3-10.4
+	c1.6-1.2,2.9-2.8,4-4.6c3-5.2,2.8-11.3,0.1-16.2c-0.2-0.3-0.5-0.5-0.9-0.5c-5.5,0-10.4,3-13.4,8.2c-0.4,0.6-0.7,1.3-1,1.9
+	C49.2,14.7,43.4,13,36,13s-13.2,1.8-18.1,4.8c-0.3-0.7-0.6-1.3-1-1.9c-3-5.2-7.9-8.2-13.4-8.2c-0.4,0-0.7,0.2-0.9,0.5
+	c-2.7,4.8-2.9,11,0.1,16.2c1.1,1.8,2.4,3.3,4,4.6C4.4,32,2.3,35.5,0.3,39.3C0.1,39.6,0,40,0,40.4h8.5l0-3.2h0c0-0.2,0-0.4,0-0.6
+	c0-4.9,3.9-8.8,8.8-8.8c4.9,0,8.8,3.9,8.8,8.8c0,0.2,0,0.4,0,0.6h0v14.5v1.2c0,0.4,0,0.8,0.1,1.1c0,0.1,0,0.2,0.1,0.3
+	c0,0.3,0.1,0.5,0.1,0.8c0,0.1,0.1,0.2,0.1,0.4c0.1,0.3,0.2,0.5,0.2,0.7c0,0.1,0.1,0.2,0.1,0.3c0.1,0.3,0.3,0.6,0.4,1c0,0,0,0,0,0
+	c0.2,0.3,0.4,0.7,0.6,1c0,0,0.1,0.1,0.1,0.1c0.2,0.3,0.4,0.5,0.6,0.7c0.1,0.1,0.1,0.1,0.2,0.2c0.2,0.2,0.4,0.4,0.6,0.6
+	c0.1,0,0.1,0.1,0.2,0.1c0.3,0.2,0.6,0.5,0.9,0.7c1.6,1.1,3.5,1.7,5.6,1.7c2.1,0,4-0.6,5.6-1.7C41.9,60.8,42.2,60.6,42.5,60.3z"/>
+<g>
+	<path fill="#2B3B47" d="M32.9,45.7c0,0,0.4,0,1,0c0.6,0,1.3,0,2.1,0c0.8,0,1.5,0,2.1,0c0.6,0,1-0.1,1-0.1c1.2-0.1,2.3,0.8,2.4,2.1
+		c0.1,0.6-0.2,1.3-0.6,1.7l-1.9,2.1c0,0-0.4,0.4-1,0.7c-0.3,0.2-0.6,0.3-1,0.4c-0.2,0.1-0.4,0.1-0.6,0.1c-0.2,0-0.4,0-0.6,0
+		c-0.2,0-0.4,0-0.6,0c-0.2,0-0.4-0.1-0.6-0.1c-0.2,0-0.4-0.1-0.5-0.2c-0.2,0-0.3-0.1-0.5-0.2c-0.6-0.3-1-0.6-1-0.6l-1.8-1.8
+		c-0.9-0.9-0.9-2.4,0-3.3C31.6,45.9,32.3,45.6,32.9,45.7z"/>
+</g>
+<path fill="#2B3B47" stroke="#2B3B47" stroke-width="1.0904" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
+	M30.6,55c0,0,2.1,1.3,5.4,1.3s5.4-1.3,5.4-1.3s-0.3,4.9-5.4,4.9S30.6,55,30.6,55z"/>
+<path fill="#2B3B47" d="M17.3,32.5c1.7,0,3,1.3,3,3v6.8c0,1.7-1.3,3-3,3l0,0c-1.7,0-3-1.3-3-3v-6.8C14.3,33.8,15.7,32.5,17.3,32.5
+	L17.3,32.5z"/>
+<path fill="#2B3B47" d="M54.8,32.5c1.7,0,3,1.3,3,3v6.8c0,1.7-1.3,3-3,3l0,0c-1.7,0-3-1.3-3-3v-6.8C51.8,33.8,53.1,32.5,54.8,32.5
+	L54.8,32.5z"/>
+<path fill="#2B3B47" d="M57.3,20.1c3.6-8,10-11.5,11.5-12.3c0,0,0-0.1,0-0.1c-0.1,0-0.2,0-0.3,0c-0.1,0-0.3,0-0.4,0
+	c-0.2,0-0.4,0-0.6,0c-0.1,0-0.1,0-0.2,0c-5.1,0.4-9.5,3.3-12.3,8.1c-0.4,0.6-0.7,1.3-1,1.9C55.2,18.5,56.3,19.2,57.3,20.1z"/>
+<path fill="#2B3B47" d="M14.7,20.1c1.1-0.9,2.3-1.8,3.6-2.5c-0.2-0.7-0.5-1.3-0.9-1.8c-3.1-5.2-8.6-8.1-14.2-8.1
+	c-0.1,0-0.1,0.1,0,0.2C4.7,8.5,11.1,12.1,14.7,20.1z"/>
+</svg>
--- a/devtools/client/themes/tooltips.css
+++ b/devtools/client/themes/tooltips.css
@@ -8,21 +8,25 @@
 @import url(chrome://devtools/content/shared/widgets/filter-widget.css);
 @import url(chrome://devtools/content/shared/widgets/spectrum.css);
 
 /* Tooltip specific theme variables */
 
 .theme-dark {
   --bezier-diagonal-color: #eee;
   --bezier-grid-color: rgba(0, 0, 0, 0.2);
+  --onboarding-link-color: var(--theme-highlight-blue);
+  --onboarding-link-active-color: var(--blue-40);
 }
 
 .theme-light {
   --bezier-diagonal-color: rgba(0, 0, 0, 0.2);
   --bezier-grid-color: rgba(0, 0, 0, 0.05);
+  --onboarding-link-color: var(--blue-60);
+  --onboarding-link-active-color: var(--blue-70);
 }
 
 /* Tooltip widget (see devtools/client/shared/widgets/tooltip/Tooltip.js) */
 
 .devtools-tooltip .panel-arrowcontent {
   padding: 4px;
 }
 
@@ -475,8 +479,58 @@
 
 /* Tooltip: Image tooltip */
 
 .devtools-tooltip-image-broken {
   box-sizing: border-box;
   height: 100%;
   padding: 7px;
 }
+
+/* Tooltip: 3 Pane Inspecot Onboarding Tooltip */
+
+.three-pane-onboarding-container {
+  align-items: center;
+  background-color: var(--theme-toolbar-background);
+  box-sizing: border-box;
+  color: var(--theme-body-color);
+  display: flex;
+  font-size: 12px;
+  padding: 7px;
+  width: 100%;
+  -moz-user-select: none;
+}
+
+.three-pane-onboarding-icon {
+  display: inline-block;
+  background-size: 21px;
+  width: 21px;
+  height: 21px;
+  margin: 8px;
+  background-image: url("chrome://devtools/skin/images/fox-smiling.svg");
+}
+
+.three-pane-onboarding-content {
+  flex: 1;
+  padding-inline-start: 5px;
+}
+
+.three-pane-onboarding-link {
+  color: var(--onboarding-link-color);
+  cursor: pointer;
+}
+
+.three-pane-onboarding-link:hover {
+  text-decoration: underline;
+}
+
+.three-pane-onboarding-link:active {
+  color: var(--onboarding-link-active-color);
+}
+
+.three-pane-onboarding-close-button {
+  align-self: flex-start;
+}
+
+.three-pane-onboarding-close-button::before {
+  background-image: url("chrome://devtools/skin/images/close.svg");
+  margin: -6px 0 0 -6px;
+}
--- a/devtools/client/themes/variables.css
+++ b/devtools/client/themes/variables.css
@@ -209,16 +209,17 @@
   --animation-curve: cubic-bezier(.07,.95,0,1);
 
   /* Firefox Colors CSS Variables v1.0.3
    * Colors are taken from: https://github.com/FirefoxUX/design-tokens */
   --magenta-65: #dd00a9;
 
   --purple-60: #8000d7;
 
+  --blue-40: #45a1ff;
   --blue-50: #0a84ff;
   --blue-55: #0074e8;
   --blue-60: #0060df;
   --blue-70: #003eaa;
 
   --red-70: #a4000f;
 
   --green-50: #30e60b;
--- a/devtools/server/actors/accessibility-parent.js
+++ b/devtools/server/actors/accessibility-parent.js
@@ -31,16 +31,17 @@ class AccessibilityParent {
       // get GC'ed away.
       this.accService = Cc["@mozilla.org/accessibilityService;1"].getService(
         Ci.nsIAccessibilityService);
     }
 
     this.messageManager.sendAsyncMessage(`${this._msgName}:event`, {
       topic: "initialized",
       data: {
+        enabled: this.enabled,
         canBeDisabled: this.canBeDisabled,
         canBeEnabled: this.canBeEnabled
       }
     });
   }
 
   /**
    * Set up message manager listener to listen for messages coming from the
--- a/devtools/server/actors/accessibility.js
+++ b/devtools/server/actors/accessibility.js
@@ -1020,16 +1020,25 @@ const AccessibilityActor = ActorClassWit
 
   onMessage(msg) {
     let { topic, data } = msg.data;
 
     switch (topic) {
       case "initialized":
         this._canBeEnabled = data.canBeEnabled;
         this._canBeDisabled = data.canBeDisabled;
+
+        // Sometimes when the tool is reopened content process accessibility service is
+        // not shut down yet because GC did not run in that process (though it did in
+        // parent process and the service was shut down there). We need to sync the two
+        // services if possible.
+        if (!data.enabled && this.enabled && data.canBeEnabled) {
+          this.messageManager.sendAsyncMessage(this._msgName, { action: "enable" });
+        }
+
         this.initializedDeferred.resolve();
         break;
       case "can-be-disabled-change":
         this._canBeDisabled = data;
         events.emit(this, "can-be-disabled-change", this.canBeDisabled);
         break;
 
       case "can-be-enabled-change":
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -3241,17 +3241,17 @@ Element::GetEventTargetParentForLinks(Ev
     aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
     MOZ_FALLTHROUGH;
   case eFocus: {
     InternalFocusEvent* focusEvent = aVisitor.mEvent->AsFocusEvent();
     if (!focusEvent || !focusEvent->mIsRefocus) {
       nsAutoString target;
       GetLinkTarget(target);
       nsContentUtils::TriggerLink(this, aVisitor.mPresContext, absURI, target,
-                                  false, true, true);
+                                  /* click */ false, /* isTrusted */ true);
       // Make sure any ancestor links don't also TriggerLink
       aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
     }
     break;
   }
   case eMouseOut:
     aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
     MOZ_FALLTHROUGH;
@@ -3353,17 +3353,17 @@ Element::PostHandleEventForLinks(EventCh
   case eLegacyDOMActivate:
     {
       if (aVisitor.mEvent->mOriginalTarget == this) {
         nsAutoString target;
         GetLinkTarget(target);
         const InternalUIEvent* activeEvent = aVisitor.mEvent->AsUIEvent();
         MOZ_ASSERT(activeEvent);
         nsContentUtils::TriggerLink(this, aVisitor.mPresContext, absURI, target,
-                                    true, true, activeEvent->IsTrustable());
+                                    /* click */ true, activeEvent->IsTrustable());
         aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
       }
     }
     break;
 
   case eKeyPress:
     {
       WidgetKeyboardEvent* keyEvent = aVisitor.mEvent->AsKeyboardEvent();
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -5486,18 +5486,17 @@ nsContentUtils::CombineResourcePrincipal
   *aResourcePrincipal = sSystemPrincipal;
   return true;
 }
 
 /* static */
 void
 nsContentUtils::TriggerLink(nsIContent *aContent, nsPresContext *aPresContext,
                             nsIURI *aLinkURI, const nsString &aTargetSpec,
-                            bool aClick, bool aIsUserTriggered,
-                            bool aIsTrusted)
+                            bool aClick, bool aIsTrusted)
 {
   NS_ASSERTION(aPresContext, "Need a nsPresContext");
   NS_PRECONDITION(aLinkURI, "No link URI");
 
   if (aContent->IsEditable()) {
     return;
   }
 
@@ -5510,20 +5509,17 @@ nsContentUtils::TriggerLink(nsIContent *
     handler->OnOverLink(aContent, aLinkURI, aTargetSpec.get());
     return;
   }
 
   // Check that this page is allowed to load this URI.
   nsresult proceed = NS_OK;
 
   if (sSecurityManager) {
-    uint32_t flag =
-      aIsUserTriggered ?
-      (uint32_t)nsIScriptSecurityManager::STANDARD :
-      (uint32_t)nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT;
+    uint32_t flag = static_cast<uint32_t>(nsIScriptSecurityManager::STANDARD);
     proceed =
       sSecurityManager->CheckLoadURIWithPrincipal(aContent->NodePrincipal(),
                                                   aLinkURI, flag);
   }
 
   // Only pass off the click event if the script security manager says it's ok.
   // We need to rest aTargetSpec for forced downloads.
   if (NS_SUCCEEDED(proceed)) {
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -1857,26 +1857,22 @@ public:
    * security check using aContent's principal.
    *
    * @param aContent the node on which a link was triggered.
    * @param aPresContext the pres context, must be non-null.
    * @param aLinkURI the URI of the link, must be non-null.
    * @param aTargetSpec the target (like target=, may be empty).
    * @param aClick whether this was a click or not (if false, this method
    *               assumes you just hovered over the link).
-   * @param aIsUserTriggered whether the user triggered the link. This would be
-   *                         false for loads from auto XLinks or from the
-   *                         click() method if we ever implement it.
    * @param aIsTrusted If false, JS Context will be pushed to stack
    *                   when the link is triggered.
    */
   static void TriggerLink(nsIContent *aContent, nsPresContext *aPresContext,
                           nsIURI *aLinkURI, const nsString& aTargetSpec,
-                          bool aClick, bool aIsUserTriggered,
-                          bool aIsTrusted);
+                          bool aClick, bool aIsTrusted);
 
   /**
    * Get the link location.
    */
   static void GetLinkLocation(mozilla::dom::Element* aElement,
                               nsString& aLocationString);
 
   /**
--- a/dom/file/ipc/IPCBlobInputStream.cpp
+++ b/dom/file/ipc/IPCBlobInputStream.cpp
@@ -156,161 +156,191 @@ IPCBlobInputStream::~IPCBlobInputStream(
   Close();
 }
 
 // nsIInputStream interface
 
 NS_IMETHODIMP
 IPCBlobInputStream::Available(uint64_t* aLength)
 {
-  // We don't have a remoteStream yet: let's return 0.
-  if (mState == eInit || mState == ePending) {
-    *aLength = 0;
-    return NS_OK;
-  }
+  nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
+  {
+    MutexAutoLock lock(mMutex);
 
-  if (mState == eRunning) {
+    // We don't have a remoteStream yet: let's return 0.
+    if (mState == eInit || mState == ePending) {
+      *aLength = 0;
+      return NS_OK;
+    }
+
+    if (mState == eClosed) {
+      return NS_BASE_STREAM_CLOSED;
+    }
+
+    MOZ_ASSERT(mState == eRunning);
     MOZ_ASSERT(mRemoteStream || mAsyncRemoteStream);
 
-    nsresult rv = EnsureAsyncRemoteStream();
+    nsresult rv = EnsureAsyncRemoteStream(lock);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    MOZ_ASSERT(mAsyncRemoteStream);
-    return mAsyncRemoteStream->Available(aLength);
+    asyncRemoteStream = mAsyncRemoteStream;
   }
 
-  MOZ_ASSERT(mState == eClosed);
-  return NS_BASE_STREAM_CLOSED;
+  MOZ_ASSERT(asyncRemoteStream);
+  return asyncRemoteStream->Available(aLength);
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount)
 {
-  // Read is not available is we don't have a remoteStream.
-  if (mState == eInit || mState == ePending) {
-    return NS_BASE_STREAM_WOULD_BLOCK;
-  }
+  nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
+  {
+    MutexAutoLock lock(mMutex);
 
-  if (mState == eRunning) {
+    // Read is not available is we don't have a remoteStream.
+    if (mState == eInit || mState == ePending) {
+      return NS_BASE_STREAM_WOULD_BLOCK;
+    }
+
+    if (mState == eClosed) {
+      return NS_BASE_STREAM_CLOSED;
+    }
+
+    MOZ_ASSERT(mState == eRunning);
     MOZ_ASSERT(mRemoteStream || mAsyncRemoteStream);
 
-    nsresult rv = EnsureAsyncRemoteStream();
+    nsresult rv = EnsureAsyncRemoteStream(lock);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    MOZ_ASSERT(mAsyncRemoteStream);
-    return mAsyncRemoteStream->Read(aBuffer, aCount, aReadCount);
+    asyncRemoteStream = mAsyncRemoteStream;
   }
 
-  MOZ_ASSERT(mState == eClosed);
-  return NS_BASE_STREAM_CLOSED;
+  MOZ_ASSERT(asyncRemoteStream);
+  return asyncRemoteStream->Read(aBuffer, aCount, aReadCount);
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
                                  uint32_t aCount, uint32_t *aResult)
 {
-  // ReadSegments is not available is we don't have a remoteStream.
-  if (mState == eInit || mState == ePending) {
-    return NS_BASE_STREAM_WOULD_BLOCK;
-  }
+  nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
+  {
+    MutexAutoLock lock(mMutex);
 
-  if (mState == eRunning) {
+    // ReadSegments is not available is we don't have a remoteStream.
+    if (mState == eInit || mState == ePending) {
+      return NS_BASE_STREAM_WOULD_BLOCK;
+    }
+
+    if (mState == eClosed) {
+      return NS_BASE_STREAM_CLOSED;
+    }
+
+    MOZ_ASSERT(mState == eRunning);
     MOZ_ASSERT(mRemoteStream || mAsyncRemoteStream);
 
-    nsresult rv = EnsureAsyncRemoteStream();
+    nsresult rv = EnsureAsyncRemoteStream(lock);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    MOZ_ASSERT(mAsyncRemoteStream);
-    return mAsyncRemoteStream->ReadSegments(aWriter, aClosure, aCount, aResult);
+    asyncRemoteStream = mAsyncRemoteStream;
   }
 
-  MOZ_ASSERT(mState == eClosed);
-  return NS_BASE_STREAM_CLOSED;
+  MOZ_ASSERT(asyncRemoteStream);
+  return asyncRemoteStream->ReadSegments(aWriter, aClosure, aCount, aResult);
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::IsNonBlocking(bool* aNonBlocking)
 {
   *aNonBlocking = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::Close()
 {
-  if (mActor) {
-    mActor->ForgetStream(this);
-    mActor = nullptr;
-  }
-
-  if (mAsyncRemoteStream) {
-    mAsyncRemoteStream->CloseWithStatus(NS_BASE_STREAM_CLOSED);
-    mAsyncRemoteStream = nullptr;
-  }
-
-  if (mRemoteStream) {
-    mRemoteStream->Close();
-    mRemoteStream = nullptr;
-  }
-
+  nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
+  nsCOMPtr<nsIInputStream> remoteStream;
   {
     MutexAutoLock lock(mMutex);
 
+    if (mActor) {
+      mActor->ForgetStream(this);
+      mActor = nullptr;
+    }
+
+    asyncRemoteStream.swap(mAsyncRemoteStream);
+    remoteStream.swap(mRemoteStream);
+
     mInputStreamCallback = nullptr;
     mInputStreamCallbackEventTarget = nullptr;
+
+    mFileMetadataCallback = nullptr;
+    mFileMetadataCallbackEventTarget = nullptr;
+
+    mState = eClosed;
   }
 
-  mFileMetadataCallback = nullptr;
-  mFileMetadataCallbackEventTarget = nullptr;
+  if (asyncRemoteStream) {
+    asyncRemoteStream->CloseWithStatus(NS_BASE_STREAM_CLOSED);
+  }
 
-  mState = eClosed;
+  if (remoteStream) {
+    remoteStream->Close();
+  }
+
   return NS_OK;
 }
 
 // nsICloneableInputStream interface
 
 NS_IMETHODIMP
 IPCBlobInputStream::GetCloneable(bool* aCloneable)
 {
+  MutexAutoLock lock(mMutex);
   *aCloneable = mState != eClosed;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::Clone(nsIInputStream** aResult)
 {
+  MutexAutoLock lock(mMutex);
+
   if (mState == eClosed) {
     return NS_BASE_STREAM_CLOSED;
   }
 
   MOZ_ASSERT(mActor);
 
   RefPtr<IPCBlobInputStream> stream = mActor->CreateStream();
   if (!stream) {
     return NS_ERROR_FAILURE;
   }
 
-  stream->InitWithExistingRange(mStart, mLength);
+  stream->InitWithExistingRange(mStart, mLength, lock);
 
   stream.forget(aResult);
   return NS_OK;
 }
 
 // nsICloneableInputStreamWithRange interface
 
 NS_IMETHODIMP
 IPCBlobInputStream::CloneWithRange(uint64_t aStart, uint64_t aLength,
                                    nsIInputStream** aResult)
 {
+  MutexAutoLock lock(mMutex);
+
   if (mState == eClosed) {
     return NS_BASE_STREAM_CLOSED;
   }
 
   // Too short or out of range.
   if (aLength == 0 || aStart >= mLength) {
     return NS_NewCStringInputStream(aResult, EmptyCString());
   }
@@ -327,17 +357,17 @@ IPCBlobInputStream::CloneWithRange(uint6
   if (!streamSize.isValid()) {
     return NS_ERROR_FAILURE;
   }
 
   if (aLength > streamSize.value()) {
     aLength = streamSize.value();
   }
 
-  stream->InitWithExistingRange(aStart + mStart, aLength);
+  stream->InitWithExistingRange(aStart + mStart, aLength, lock);
 
   stream.forget(aResult);
   return NS_OK;
 }
 
 // nsIAsyncInputStream interface
 
 NS_IMETHODIMP
@@ -346,138 +376,151 @@ IPCBlobInputStream::CloseWithStatus(nsre
   return Close();
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
                               uint32_t aFlags, uint32_t aRequestedCount,
                               nsIEventTarget* aEventTarget)
 {
-  // See IPCBlobInputStream.h for more information about this state machine.
-
-  switch (mState) {
-  // First call, we need to retrieve the stream from the parent actor.
-  case eInit:
-    MOZ_ASSERT(mActor);
-
-    mInputStreamCallback = aCallback;
-    mInputStreamCallbackEventTarget = aEventTarget;
-    mState = ePending;
-
-    mActor->StreamNeeded(this, aEventTarget);
-    return NS_OK;
-
-  // We are still waiting for the remote inputStream
-  case ePending: {
+  nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
+  {
     MutexAutoLock lock(mMutex);
 
-    if (mInputStreamCallback && aCallback) {
-      return NS_ERROR_FAILURE;
+    // See IPCBlobInputStream.h for more information about this state machine.
+
+    switch (mState) {
+    // First call, we need to retrieve the stream from the parent actor.
+    case eInit:
+      MOZ_ASSERT(mActor);
+
+      mInputStreamCallback = aCallback;
+      mInputStreamCallbackEventTarget = aEventTarget;
+      mState = ePending;
+
+      mActor->StreamNeeded(this, aEventTarget);
+      return NS_OK;
+
+    // We are still waiting for the remote inputStream
+    case ePending: {
+      if (mInputStreamCallback && aCallback) {
+        return NS_ERROR_FAILURE;
+      }
+
+      mInputStreamCallback = aCallback;
+      mInputStreamCallbackEventTarget = aEventTarget;
+      return NS_OK;
     }
 
-    mInputStreamCallback = aCallback;
-    mInputStreamCallbackEventTarget = aEventTarget;
-    return NS_OK;
-  }
+    // We have the remote inputStream, let's check if we can execute the callback.
+    case eRunning: {
+      if (mInputStreamCallback && aCallback) {
+        return NS_ERROR_FAILURE;
+      }
 
-  // We have the remote inputStream, let's check if we can execute the callback.
-  case eRunning: {
-    {
-      MutexAutoLock lock(mMutex);
+      nsresult rv = EnsureAsyncRemoteStream(lock);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
       mInputStreamCallback = aCallback;
       mInputStreamCallbackEventTarget = aEventTarget;
+
+      asyncRemoteStream = mAsyncRemoteStream;
+      break;
     }
 
-    nsresult rv = EnsureAsyncRemoteStream();
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
+    // Stream is closed.
+    default:
+      MOZ_ASSERT(mState == eClosed);
+      return NS_BASE_STREAM_CLOSED;
     }
-
-    MOZ_ASSERT(mAsyncRemoteStream);
-    return mAsyncRemoteStream->AsyncWait(aCallback ? this : nullptr,
-                                         0, 0, aEventTarget);
   }
 
-  // Stream is closed.
-  default:
-    MOZ_ASSERT(mState == eClosed);
-    return NS_BASE_STREAM_CLOSED;
-  }
+  MOZ_ASSERT(asyncRemoteStream);
+  return asyncRemoteStream->AsyncWait(aCallback ? this : nullptr,
+                                      0, 0, aEventTarget);
 }
 
 void
 IPCBlobInputStream::StreamReady(already_AddRefed<nsIInputStream> aInputStream)
 {
   nsCOMPtr<nsIInputStream> inputStream = Move(aInputStream);
 
-  // We have been closed in the meantime.
-  if (mState == eClosed) {
-    if (inputStream) {
-      inputStream->Close();
-    }
-    return;
-  }
-
   // If inputStream is null, it means that the serialization went wrong or the
   // stream is not available anymore. We keep the state as pending just to block
   // any additional operation.
 
   if (!inputStream) {
     return;
   }
 
-  // Now it's the right time to apply a slice if needed.
-  if (mStart > 0 || mLength < mActor->Size()) {
-    inputStream =
-      new SlicedInputStream(inputStream.forget(), mStart, mLength);
-  }
+  nsCOMPtr<nsIFileMetadataCallback> fileMetadataCallback;
+  nsCOMPtr<nsIEventTarget> fileMetadataCallbackEventTarget;
+  nsCOMPtr<nsIInputStreamCallback> inputStreamCallback;
+  nsCOMPtr<nsIEventTarget> inputStreamCallbackEventTarget;
+  nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
+  {
+    MutexAutoLock lock(mMutex);
 
-  mRemoteStream = inputStream;
+    // We have been closed in the meantime.
+    if (mState == eClosed) {
+      if (inputStream) {
+        MutexAutoUnlock unlock(mMutex);
+        inputStream->Close();
+      }
+      return;
+    }
 
-  MOZ_ASSERT(mState == ePending);
-  mState = eRunning;
+    // Now it's the right time to apply a slice if needed.
+    if (mStart > 0 || mLength < mActor->Size()) {
+      inputStream =
+        new SlicedInputStream(inputStream.forget(), mStart, mLength);
+    }
+
+    mRemoteStream = inputStream;
+
+    MOZ_ASSERT(mState == ePending);
+    mState = eRunning;
+
+    fileMetadataCallback.swap(mFileMetadataCallback);
+    fileMetadataCallbackEventTarget.swap(mFileMetadataCallbackEventTarget);
 
-  nsCOMPtr<nsIFileMetadataCallback> fileMetadataCallback;
-  fileMetadataCallback.swap(mFileMetadataCallback);
+    inputStreamCallback = mInputStreamCallback ? this : nullptr;
+    inputStreamCallbackEventTarget = mInputStreamCallbackEventTarget;
 
-  nsCOMPtr<nsIEventTarget> fileMetadataCallbackEventTarget;
-  fileMetadataCallbackEventTarget.swap(mFileMetadataCallbackEventTarget);
+    if (inputStreamCallback) {
+      nsresult rv = EnsureAsyncRemoteStream(lock);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return;
+      }
+
+      MOZ_ASSERT(mAsyncRemoteStream);
+      asyncRemoteStream = mAsyncRemoteStream;
+    }
+  }
 
   if (fileMetadataCallback) {
     FileMetadataCallbackRunnable::Execute(fileMetadataCallback,
                                           fileMetadataCallbackEventTarget,
                                           this);
   }
 
-  nsCOMPtr<nsIInputStreamCallback> inputStreamCallback = this;
-  nsCOMPtr<nsIEventTarget> inputStreamCallbackEventTarget;
-  {
-    MutexAutoLock lock(mMutex);
-    inputStreamCallbackEventTarget = mInputStreamCallbackEventTarget;
-    if (!mInputStreamCallback) {
-      inputStreamCallback = nullptr;
-    }
-  }
+  if (inputStreamCallback) {
+    MOZ_ASSERT(asyncRemoteStream);
 
-  if (inputStreamCallback) {
-    nsresult rv = EnsureAsyncRemoteStream();
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return;
-    }
-
-    MOZ_ASSERT(mAsyncRemoteStream);
-
-    rv = mAsyncRemoteStream->AsyncWait(inputStreamCallback, 0, 0,
-                                       inputStreamCallbackEventTarget);
+    nsresult rv = asyncRemoteStream->AsyncWait(inputStreamCallback, 0, 0,
+                                               inputStreamCallbackEventTarget);
     Unused << NS_WARN_IF(NS_FAILED(rv));
   }
 }
 
 void
-IPCBlobInputStream::InitWithExistingRange(uint64_t aStart, uint64_t aLength)
+IPCBlobInputStream::InitWithExistingRange(uint64_t aStart, uint64_t aLength,
+                                          const MutexAutoLock& aProofOfLock)
 {
   MOZ_ASSERT(mActor->Size() >= aStart + aLength);
   mStart = aStart;
   mLength = aLength;
 
   // In the child, we slice in StreamReady() when we set mState to eRunning.
   // But in the parent, we start out eRunning, so it's necessary to slice the
   // stream as soon as we have the information during the initialization phase
@@ -524,16 +567,18 @@ IPCBlobInputStream::OnInputStreamReady(n
 }
 
 // nsIIPCSerializableInputStream
 
 void
 IPCBlobInputStream::Serialize(mozilla::ipc::InputStreamParams& aParams,
                               FileDescriptorArray& aFileDescriptors)
 {
+  MutexAutoLock lock(mMutex);
+
   mozilla::ipc::IPCBlobInputStreamParams params;
   params.id() = mActor->ID();
   params.start() = mStart;
   params.length() = mLength;
 
   aParams = params;
 }
 
@@ -549,99 +594,119 @@ mozilla::Maybe<uint64_t>
 IPCBlobInputStream::ExpectedSerializedLength()
 {
   return mozilla::Nothing();
 }
 
 // nsIAsyncFileMetadata
 
 NS_IMETHODIMP
-IPCBlobInputStream::AsyncWait(nsIFileMetadataCallback* aCallback,
-                              nsIEventTarget* aEventTarget)
+IPCBlobInputStream::AsyncFileMetadataWait(nsIFileMetadataCallback* aCallback,
+                                          nsIEventTarget* aEventTarget)
 {
   MOZ_ASSERT(!!aCallback == !!aEventTarget);
 
   // If we have the callback, we must have the event target.
   if (NS_WARN_IF(!!aCallback != !!aEventTarget)) {
     return NS_ERROR_FAILURE;
   }
 
   // See IPCBlobInputStream.h for more information about this state machine.
 
-  switch (mState) {
-  // First call, we need to retrieve the stream from the parent actor.
-  case eInit:
-    MOZ_ASSERT(mActor);
+  {
+    MutexAutoLock lock(mMutex);
+
+    switch (mState) {
+    // First call, we need to retrieve the stream from the parent actor.
+    case eInit:
+      MOZ_ASSERT(mActor);
+
+      mFileMetadataCallback = aCallback;
+      mFileMetadataCallbackEventTarget = aEventTarget;
+      mState = ePending;
+
+      mActor->StreamNeeded(this, aEventTarget);
+      return NS_OK;
 
-    mFileMetadataCallback = aCallback;
-    mFileMetadataCallbackEventTarget = aEventTarget;
-    mState = ePending;
+    // We are still waiting for the remote inputStream
+    case ePending:
+      if (mFileMetadataCallback && aCallback) {
+        return NS_ERROR_FAILURE;
+      }
 
-    mActor->StreamNeeded(this, aEventTarget);
-    return NS_OK;
+      mFileMetadataCallback = aCallback;
+      mFileMetadataCallbackEventTarget = aEventTarget;
+      return NS_OK;
 
-  // We are still waiting for the remote inputStream
-  case ePending:
-    if (mFileMetadataCallback && aCallback) {
-      return NS_ERROR_FAILURE;
+    // We have the remote inputStream, let's check if we can execute the callback.
+    case eRunning:
+      break;
+
+    // Stream is closed.
+    default:
+      MOZ_ASSERT(mState == eClosed);
+      return NS_BASE_STREAM_CLOSED;
     }
 
-    mFileMetadataCallback = aCallback;
-    mFileMetadataCallbackEventTarget = aEventTarget;
-    return NS_OK;
+    MOZ_ASSERT(mState == eRunning);
+  }
 
-  // We have the remote inputStream, let's check if we can execute the callback.
-  case eRunning:
-    FileMetadataCallbackRunnable::Execute(aCallback, aEventTarget, this);
-    return NS_OK;
-
-  // Stream is closed.
-  default:
-    MOZ_ASSERT(mState == eClosed);
-    return NS_BASE_STREAM_CLOSED;
-  }
+  FileMetadataCallbackRunnable::Execute(aCallback, aEventTarget, this);
+  return NS_OK;
 }
 
 // nsIFileMetadata
 
 NS_IMETHODIMP
 IPCBlobInputStream::GetSize(int64_t* aRetval)
 {
-  nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(mRemoteStream);
-  if (!fileMetadata) {
-    return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
+  nsCOMPtr<nsIFileMetadata> fileMetadata;
+  {
+    MutexAutoLock lock(mMutex);
+    fileMetadata = do_QueryInterface(mRemoteStream);
+    if (!fileMetadata) {
+      return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
+    }
   }
 
   return fileMetadata->GetSize(aRetval);
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::GetLastModified(int64_t* aRetval)
 {
-  nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(mRemoteStream);
-  if (!fileMetadata) {
-    return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
+  nsCOMPtr<nsIFileMetadata> fileMetadata;
+  {
+    MutexAutoLock lock(mMutex);
+    fileMetadata = do_QueryInterface(mRemoteStream);
+    if (!fileMetadata) {
+      return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
+    }
   }
 
   return fileMetadata->GetLastModified(aRetval);
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::GetFileDescriptor(PRFileDesc** aRetval)
 {
-  nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(mRemoteStream);
-  if (!fileMetadata) {
-    return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
+  nsCOMPtr<nsIFileMetadata> fileMetadata;
+  {
+    MutexAutoLock lock(mMutex);
+    fileMetadata = do_QueryInterface(mRemoteStream);
+    if (!fileMetadata) {
+      return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
+    }
   }
 
   return fileMetadata->GetFileDescriptor(aRetval);
 }
 
 nsresult
-IPCBlobInputStream::EnsureAsyncRemoteStream()
+IPCBlobInputStream::EnsureAsyncRemoteStream(const MutexAutoLock& aProofOfLock)
 {
   // We already have an async remote stream.
   if (mAsyncRemoteStream) {
     return NS_OK;
   }
 
   if (!mRemoteStream) {
     return NS_ERROR_FAILURE;
--- a/dom/file/ipc/IPCBlobInputStream.h
+++ b/dom/file/ipc/IPCBlobInputStream.h
@@ -40,20 +40,21 @@ public:
 
   void
   StreamReady(already_AddRefed<nsIInputStream> aInputStream);
 
 private:
   ~IPCBlobInputStream();
 
   nsresult
-  EnsureAsyncRemoteStream();
+  EnsureAsyncRemoteStream(const MutexAutoLock& aProofOfLock);
 
   void
-  InitWithExistingRange(uint64_t aStart, uint64_t aLength);
+  InitWithExistingRange(uint64_t aStart, uint64_t aLength,
+                        const MutexAutoLock& aProofOfLock);
 
   RefPtr<IPCBlobInputStreamChild> mActor;
 
   // This is the list of possible states.
   enum {
     // The initial state. Only ::Available() can be used without receiving an
     // error. The available size is known by the actor.
     eInit,
@@ -75,23 +76,24 @@ private:
 
   uint64_t mStart;
   uint64_t mLength;
 
   nsCOMPtr<nsIInputStream> mRemoteStream;
   nsCOMPtr<nsIAsyncInputStream> mAsyncRemoteStream;
 
   // These 2 values are set only if mState is ePending.
-  // They are protected by mutex.
   nsCOMPtr<nsIInputStreamCallback> mInputStreamCallback;
   nsCOMPtr<nsIEventTarget> mInputStreamCallbackEventTarget;
 
   // These 2 values are set only if mState is ePending.
   nsCOMPtr<nsIFileMetadataCallback> mFileMetadataCallback;
   nsCOMPtr<nsIEventTarget> mFileMetadataCallbackEventTarget;
 
+  // Any member of this class is protected by mutex because touched on
+  // multiple threads.
   Mutex mMutex;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ipc_IPCBlobInputStream_h
--- a/dom/file/nsHostObjectProtocolHandler.cpp
+++ b/dom/file/nsHostObjectProtocolHandler.cpp
@@ -429,17 +429,17 @@ public:
   static void
   Create(const nsACString& aURI, bool aBroadcastToOtherProcesses)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     RefPtr<ReleasingTimerHolder> holder =
       new ReleasingTimerHolder(aURI, aBroadcastToOtherProcesses);
 
-    auto raii = mozilla::MakeScopeExit([&] {
+    auto raii = mozilla::MakeScopeExit([holder] {
       holder->CancelTimerAndRevokeURI();
     });
 
     nsresult rv = NS_NewTimerWithCallback(getter_AddRefs(holder->mTimer),
                                           holder, RELEASING_TIMER,
                                           nsITimer::TYPE_ONE_SHOT,
                                           SystemGroup::EventTargetFor(TaskCategory::Other));
     NS_ENSURE_SUCCESS_VOID(rv);
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -4809,30 +4809,30 @@ nsresult HTMLMediaElement::InitializeDec
 
   DecoderDoctorDiagnostics diagnostics;
 
   nsAutoCString mimeType;
   aChannel->GetContentType(mimeType);
   NS_ASSERTION(!mimeType.IsEmpty(), "We should have the Content-Type.");
   NS_ConvertUTF8toUTF16 mimeUTF16(mimeType);
 
-  HTMLMediaElement* self = this;
-  auto reportCanPlay = [&](bool aCanPlay) {
+  RefPtr<HTMLMediaElement> self = this;
+  auto reportCanPlay = [&, self](bool aCanPlay) {
     diagnostics.StoreFormatDiagnostics(
       self->OwnerDoc(), mimeUTF16, aCanPlay, __func__);
     if (!aCanPlay) {
       nsAutoString src;
       self->GetCurrentSrc(src);
       const char16_t* params[] = { mimeUTF16.get(), src.get() };
       self->ReportLoadError(
         "MediaLoadUnsupportedMimeType", params, ArrayLength(params));
     }
   };
 
-  auto onExit = MakeScopeExit([&] {
+  auto onExit = MakeScopeExit([self] {
     if (self->mChannelLoader) {
       self->mChannelLoader->Done();
       self->mChannelLoader = nullptr;
     }
   });
 
   Maybe<MediaContainerType> containerType = MakeMediaContainerType(mimeType);
   if (!containerType) {
--- a/dom/indexedDB/ActorsChild.cpp
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -3550,17 +3550,18 @@ BackgroundRequestChild::
 PreprocessHelper::WaitForStreamReady(nsIInputStream* aInputStream)
 {
   MOZ_ASSERT(!IsOnOwningThread());
   MOZ_ASSERT(aInputStream);
 
   nsCOMPtr<nsIAsyncFileMetadata> asyncFileMetadata =
     do_QueryInterface(aInputStream);
   if (asyncFileMetadata) {
-    nsresult rv = asyncFileMetadata->AsyncWait(this, mTaskQueueEventTarget);
+    nsresult rv =
+      asyncFileMetadata->AsyncFileMetadataWait(this, mTaskQueueEventTarget);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     return NS_OK;
   }
 
   nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aInputStream);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -4265,17 +4265,17 @@ private:
 };
 
 mozilla::ipc::IPCResult
 ContentParent::RecvRequestAnonymousTemporaryFile(const uint64_t& aID)
 {
   // Make sure to send a callback to the child if we bail out early.
   nsresult rv = NS_OK;
   RefPtr<ContentParent> self(this);
-  auto autoNotifyChildOnError = MakeScopeExit([&]() {
+  auto autoNotifyChildOnError = MakeScopeExit([&, self]() {
     if (NS_FAILED(rv)) {
       FileDescOrError result(rv);
       Unused << self->SendProvideAnonymousTemporaryFile(aID, result);
     }
   });
 
   // We use a helper runnable to open the anonymous temporary file on the IO
   // thread.  The same runnable will call us back on the main thread when the
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -10,16 +10,17 @@
 #include "nsIContentPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIDocShell.h"
 #include "nsIDOMDocument.h"
 #include "nsIHttpChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIInputStreamPump.h"
 #include "nsIIOService.h"
+#include "nsIOService.h"
 #include "nsIProtocolHandler.h"
 #include "nsIScriptError.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIStreamLoader.h"
 #include "nsIStreamListenerTee.h"
 #include "nsIThreadRetargetableRequest.h"
 #include "nsIURI.h"
 
@@ -149,16 +150,28 @@ ChannelFromScriptURL(nsIPrincipal* princ
   if (parentDoc && parentDoc->NodePrincipal() != principal) {
     parentDoc = nullptr;
   }
 
   aLoadFlags |= nsIChannel::LOAD_CLASSIFY_URI;
   uint32_t secFlags = aIsMainScript ? nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED
                                     : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
 
+  bool inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
+    principal, uri, true /* aInheritForAboutBlank */, false /* aForceInherit */);
+
+  bool isData = false;
+  rv = uri->SchemeIs("data", &isData);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool isURIUniqueOrigin = nsIOService::IsDataURIUniqueOpaqueOrigin() && isData;
+  if (inheritAttrs && !isURIUniqueOrigin) {
+    secFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
+  }
+
   if (aWorkerScriptType == DebuggerScript) {
     // A DebuggerScript needs to be a local resource like chrome: or resource:
     bool isUIResource = false;
     rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_IS_UI_RESOURCE,
                              &isUIResource);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
@@ -167,18 +180,17 @@ ChannelFromScriptURL(nsIPrincipal* princ
       return NS_ERROR_DOM_SECURITY_ERR;
     }
 
     secFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
   }
 
   // Note: this is for backwards compatibility and goes against spec.
   // We should find a better solution.
-  bool isData = false;
-  if (aIsMainScript && NS_SUCCEEDED(uri->SchemeIs("data", &isData)) && isData) {
+  if (aIsMainScript && isData) {
     secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL;
   }
 
   nsContentPolicyType contentPolicyType =
     aIsMainScript ? aMainScriptContentPolicyType
                   : nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS;
 
   // The main service worker script should never be loaded over the network
--- a/dom/workers/WorkerHolder.cpp
+++ b/dom/workers/WorkerHolder.cpp
@@ -38,16 +38,18 @@ WorkerHolder::~WorkerHolder()
 }
 
 bool
 WorkerHolder::HoldWorker(WorkerPrivate* aWorkerPrivate,
                          WorkerStatus aFailStatus)
 {
   AssertOnOwningThread(mThread);
   MOZ_ASSERT(aWorkerPrivate);
+  MOZ_ASSERT(aFailStatus >= Terminating);
+
   aWorkerPrivate->AssertIsOnWorkerThread();
 
   if (!aWorkerPrivate->AddHolder(this, aFailStatus)) {
     return false;
   }
 
   mWorkerPrivate = aWorkerPrivate;
   return true;
--- a/dom/workers/WorkerHolder.h
+++ b/dom/workers/WorkerHolder.h
@@ -13,42 +13,44 @@ namespace mozilla {
 namespace dom {
 
 class WorkerPrivate;
 
 /**
  * Use this chart to help figure out behavior during each of the closing
  * statuses. Details below.
  *
- * +=============================================+
- * |             Closing Statuses                |
- * +=============+=============+=================+
- * |    status   | clear queue | abort execution |
- * +=============+=============+=================+
- * |   Closing   |     yes     |       no        |
- * +-------------+-------------+-----------------+
- * | Terminating |     yes     |       yes       |
- * +-------------+-------------+-----------------+
- * |  Canceling  |     yes     |       yes       |
- * +-------------+-------------+-----------------+
- * |   Killing   |     yes     |       yes       |
- * +-------------+-------------+-----------------+
+ * +========================================================+
+ * |                     Closing Statuses                   |
+ * +=============+=============+=================+==========+
+ * |    status   | clear queue | abort execution | notified |
+ * +=============+=============+=================+==========+
+ * |   Closing   |     yes     |       no        |    no    |
+ * +-------------+-------------+-----------------+----------+
+ * | Terminating |     yes     |       yes       |   yes    |
+ * +-------------+-------------+-----------------+----------+
+ * |  Canceling  |     yes     |       yes       |   yes    |
+ * +-------------+-------------+-----------------+----------+
+ * |   Killing   |     yes     |       yes       |   yes    |
+ * +-------------+-------------+-----------------+----------+
  */
 
 enum WorkerStatus
 {
   // Not yet scheduled.
   Pending = 0,
 
   // This status means that the worker is active.
   Running,
 
   // Inner script called close() on the worker global scope. Setting this
   // status causes the worker to clear its queue of events but does not abort
-  // the currently running script.
+  // the currently running script. WorkerHolder/WorkerRef objects are not going
+  // to be notified because the behavior of APIs/Components should not change
+  // during this status yet.
   Closing,
 
   // Outer script called terminate() on the worker or the worker object was
   // garbage collected in its outer script. Setting this status causes the
   // worker to abort immediately and clear its queue of events.
   Terminating,
 
   // Either the user navigated away from the owning page or the owning page fell
--- a/dom/workers/WorkerLoadInfo.cpp
+++ b/dom/workers/WorkerLoadInfo.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/LoadContext.h"
 #include "nsContentUtils.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsINetworkInterceptController.h"
 #include "nsIProtocolHandler.h"
 #include "nsITabChild.h"
+#include "nsScriptSecurityManager.h"
 #include "nsNetUtil.h"
 
 namespace mozilla {
 
 using namespace ipc;
 
 namespace dom {
 
@@ -374,21 +375,30 @@ WorkerLoadInfo::PrincipalURIMatchesScrip
     return true;
   }
 
   nsCOMPtr<nsIURI> principalURI;
   rv = mPrincipal->GetURI(getter_AddRefs(principalURI));
   NS_ENSURE_SUCCESS(rv, false);
   NS_ENSURE_TRUE(principalURI, false);
 
-  bool equal = false;
-  rv = principalURI->Equals(mBaseURI, &equal);
-  NS_ENSURE_SUCCESS(rv, false);
+  if (nsScriptSecurityManager::SecurityCompareURIs(mBaseURI, principalURI)) {
+    return true;
+  }
 
-  return equal;
+  // If strict file origin policy is in effect, local files will always fail
+  // SecurityCompareURIs unless they are identical. Explicitly check file origin
+  // policy, in that case.
+  if (nsScriptSecurityManager::GetStrictFileOriginPolicy() &&
+      NS_URIIsLocalFile(mBaseURI) &&
+      NS_RelaxStrictFileOriginPolicy(mBaseURI, principalURI)) {
+    return true;
+  }
+
+  return false;
 }
 #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
 
 bool
 WorkerLoadInfo::ProxyReleaseMainThreadObjects(WorkerPrivate* aWorkerPrivate)
 {
   nsCOMPtr<nsILoadGroup> nullLoadGroup;
   return ProxyReleaseMainThreadObjects(aWorkerPrivate, nullLoadGroup);
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -4053,21 +4053,17 @@ WorkerPrivate::RemoveHolder(WorkerHolder
   }
 }
 
 void
 WorkerPrivate::NotifyHolders(WorkerStatus aStatus)
 {
   AssertIsOnWorkerThread();
 
-  NS_ASSERTION(aStatus > Running, "Bad status!");
-
-  if (aStatus >= Closing) {
-    CancelAllTimeouts();
-  }
+  NS_ASSERTION(aStatus > Closing, "Bad status!");
 
   nsTObserverArray<WorkerHolder*>::ForwardIterator iter(mHolders);
   while (iter.HasMore()) {
     WorkerHolder* holder = iter.GetNext();
     if (!holder->Notify(aStatus)) {
       NS_WARNING("Failed to notify holder!");
     }
   }
@@ -4561,18 +4557,24 @@ WorkerPrivate::NotifyInternal(WorkerStat
     // dispatched after we clear the queue below.
     if (aStatus == Closing) {
       Close();
     }
   }
 
   MOZ_ASSERT(previousStatus != Pending);
 
+  if (aStatus >= Closing) {
+    CancelAllTimeouts();
+  }
+
   // Let all our holders know the new status.
-  NotifyHolders(aStatus);
+  if (aStatus > Closing) {
+    NotifyHolders(aStatus);
+  }
 
   // If this is the first time our status has changed then we need to clear the
   // main event queue.
   if (previousStatus == Running) {
     // NB: If we're in a sync loop, we can't clear the queue immediately,
     // because this is the wrong queue. So we have to defer it until later.
     if (!mSyncLoopStack.IsEmpty()) {
       mPendingEventQueueClearing = true;
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -62,21 +62,22 @@ UNIFIED_SOURCES += [
     'WorkerPrivate.cpp',
     'WorkerRef.cpp',
     'WorkerRunnable.cpp',
     'WorkerScope.cpp',
     'WorkerThread.cpp',
 ]
 
 LOCAL_INCLUDES += [
-    '../base',
-    '../system',
+    '/caps',
     '/dom/base',
     '/dom/bindings',
+    '/dom/system',
     '/js/xpconnect/loader',
+    '/netwerk/base',
     '/xpcom/build',
     '/xpcom/threads',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
--- a/dom/workers/test/browser.ini
+++ b/dom/workers/test/browser.ini
@@ -4,8 +4,10 @@ support-files =
   bug1047663_worker.sjs
   frame_script.js
   head.js
   !/dom/base/test/file_empty.html
 
 [browser_bug1047663.js]
 [browser_bug1104623.js]
 run-if = buildapp == 'browser'
+[browser_fileURL.js]
+support-files = empty.html empty_worker.js
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/browser_fileURL.js
@@ -0,0 +1,127 @@
+/* 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 WORKER_BODY = "postMessage(42);\n";
+
+// file:// tests.
+add_task(async function() {
+  info("Creating the tmp directory.");
+  let parent = Cc["@mozilla.org/file/directory_service;1"]
+                 .getService(Ci.nsIDirectoryService)
+                 .QueryInterface(Ci.nsIProperties)
+                 .get('TmpD', Ci.nsIFile);
+  parent.append('worker-dir-test');
+  parent.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o700);
+
+  let dir_a = parent.clone();
+  dir_a.append('a');
+  dir_a.create(Ci.nsIFile.DIRECTORY_TYPE, 0o700);
+
+  let page_a = dir_a.clone();
+  page_a.append('empty.html');
+  page_a.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);
+
+  let url_a = Services.io.newFileURI(page_a)
+
+  let worker = dir_a.clone();
+  worker.append('worker.js');
+
+  let stream = Cc["@mozilla.org/network/file-output-stream;1"]
+                    .createInstance(Ci.nsIFileOutputStream);
+  stream.init(worker, 0x02 | 0x08 | 0x20, // write, create, truncate
+               0o666, 0);
+  stream.write(WORKER_BODY, WORKER_BODY.length);
+  stream.close();
+
+  let url_worker = Services.io.newFileURI(worker);
+
+  let dir_b = parent.clone();
+  dir_b.append('b');
+  dir_b.create(Ci.nsIFile.DIRECTORY_TYPE, 0o700);
+
+  let page_b = dir_b.clone();
+  page_b.append('empty.html');
+  page_b.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);
+
+  let url_b = Services.io.newFileURI(page_b)
+
+  let tab = BrowserTestUtils.addTab(gBrowser, url_a.spec);
+  gBrowser.selectedTab = tab;
+
+  let browser = gBrowser.getBrowserForTab(tab);
+  await BrowserTestUtils.browserLoaded(browser);
+
+  await ContentTask.spawn(browser, url_worker.spec, function(spec) {
+    return new content.Promise((resolve, reject) => {
+      let w = new content.window.Worker(spec);
+      w.onerror = _ => { reject(); }
+      w.onmessage = _ => { resolve() };
+    });
+  });
+  ok(true, "The worker is loaded when the script is on the same directory.");
+
+  BrowserTestUtils.removeTab(tab);
+
+  tab = BrowserTestUtils.addTab(gBrowser, url_b.spec);
+  gBrowser.selectedTab = tab;
+
+  browser = gBrowser.getBrowserForTab(tab);
+  await BrowserTestUtils.browserLoaded(browser);
+
+  await ContentTask.spawn(browser, url_worker.spec, function(spec) {
+    return new content.Promise((resolve, reject) => {
+      let w = new content.window.Worker(spec);
+      w.onerror = _ => { resolve(); }
+      w.onmessage = _ => { reject() };
+    });
+  });
+  ok(true, "The worker is not loaded when the script is on a different directory.");
+
+  BrowserTestUtils.removeTab(tab);
+
+  info("Removing the tmp directory.");
+  parent.remove(true);
+});
+
+const EMPTY_URL = "/browser/dom/workers/test/empty.html";
+const WORKER_URL = "/browser/dom/workers/test/empty_worker.js";
+
+add_task(async function() {
+  let tab = BrowserTestUtils.addTab(gBrowser, "http://mochi.test:8888" + EMPTY_URL);
+  gBrowser.selectedTab = tab;
+
+  let browser = gBrowser.getBrowserForTab(tab);
+  await BrowserTestUtils.browserLoaded(browser);
+
+  await ContentTask.spawn(browser, "http://example.org" + WORKER_URL, function(spec) {
+    return new content.Promise((resolve, reject) => {
+      let w = new content.window.Worker(spec);
+      w.onerror = _ => { resolve(); }
+      w.onmessage = _ => { reject() };
+    });
+  });
+  ok(true, "The worker is not loaded when the script is from different origin.");
+
+  BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function() {
+  let tab = BrowserTestUtils.addTab(gBrowser, "https://example.org" + EMPTY_URL);
+  gBrowser.selectedTab = tab;
+
+  let browser = gBrowser.getBrowserForTab(tab);
+  await BrowserTestUtils.browserLoaded(browser);
+
+  await ContentTask.spawn(browser, "http://example.org" + WORKER_URL, function(spec) {
+    return new content.Promise((resolve, reject) => {
+      let w = new content.window.Worker(spec);
+      w.onerror = _ => { resolve(); }
+      w.onmessage = _ => { reject() };
+    });
+  });
+  ok(true, "The worker is not loaded when the script is from different origin.");
+
+  BrowserTestUtils.removeTab(tab);
+});
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/empty_worker.js
@@ -0,0 +1,1 @@
+postMessage(42);
--- a/gfx/layers/PaintThread.cpp
+++ b/gfx/layers/PaintThread.cpp
@@ -160,17 +160,20 @@ PaintThread::Init()
 
   RefPtr<nsIThread> thread;
   nsresult rv = NS_NewNamedThread("PaintThread", getter_AddRefs(thread));
   if (NS_FAILED(rv)) {
     return false;
   }
   sThread = thread;
 
-  if (gfxPlatform::GetPlatform()->UsesTiling()) {
+  // Only create paint workers for tiling if we are using tiling or could
+  // expect to dynamically switch to tiling in the future
+  if (gfxPlatform::GetPlatform()->UsesTiling() ||
+      gfxPrefs::LayersTilesEnabledIfSkiaPOMTP()) {
     int32_t paintWorkerCount = PaintThread::CalculatePaintWorkerCount();
     mPaintWorkers = SharedThreadPool::Get(NS_LITERAL_CSTRING("PaintWorker"), paintWorkerCount);
   }
 
   nsCOMPtr<nsIRunnable> paintInitTask =
     NewRunnableMethod("PaintThread::InitOnPaintThread",
                       this, &PaintThread::InitOnPaintThread);
   SyncRunnable::DispatchToThread(sThread, paintInitTask);
--- a/gfx/src/nsRect.h
+++ b/gfx/src/nsRect.h
@@ -17,17 +17,21 @@
 #include "mozilla/gfx/Logging.h"
 #include "nsCoord.h"                    // for nscoord, etc
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsPoint.h"                    // for nsIntPoint, nsPoint
 #include "nsMargin.h"                   // for nsIntMargin, nsMargin
 #include "nsSize.h"                     // for IntSize, nsSize
 #include "nscore.h"                     // for NS_BUILD_REFCNT_LOGGING
 #if !defined(ANDROID) && (defined(__SSE2__) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2))
+#if defined(_MSC_VER) && !defined(__clang__)
 #include "smmintrin.h"
+#else
+#include "emmintrin.h"
+#endif
 #endif
 
 typedef mozilla::gfx::IntRect nsIntRect;
 
 struct nsRect :
   public mozilla::gfx::BaseRect<nscoord, nsRect, nsPoint, nsSize, nsMargin> {
   typedef mozilla::gfx::BaseRect<nscoord, nsRect, nsPoint, nsSize, nsMargin> Super;
 
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -615,17 +615,17 @@ private:
   DECL_GFX_PREF(Once, "layers.mlgpu.enable-cpu-occlusion",     AdvancedLayersEnableCPUOcclusion, bool, true);
   DECL_GFX_PREF(Once, "layers.mlgpu.enable-depth-buffer",      AdvancedLayersEnableDepthBuffer, bool, false);
   DECL_GFX_PREF(Live, "layers.mlgpu.enable-invalidation",      AdvancedLayersUseInvalidation, bool, true);
   DECL_GFX_PREF(Once, "layers.mlgpu.enable-on-windows7",       AdvancedLayersEnableOnWindows7, bool, false);
   DECL_GFX_PREF(Once, "layers.mlgpu.enable-container-resizing", AdvancedLayersEnableContainerResizing, bool, true);
   DECL_GFX_PREF(Once, "layers.offmainthreadcomposition.force-disabled", LayersOffMainThreadCompositionForceDisabled, bool, false);
   DECL_GFX_PREF(Live, "layers.offmainthreadcomposition.frame-rate", LayersCompositionFrameRate, int32_t,-1);
   DECL_GFX_PREF(Live, "layers.omtp.dump-capture",              LayersOMTPDumpCapture, bool, false);
-  DECL_GFX_PREF(Live, "layers.omtp.paint-workers",             LayersOMTPPaintWorkers, int32_t, 1);
+  DECL_GFX_PREF(Once, "layers.omtp.paint-workers",             LayersOMTPPaintWorkers, int32_t, 1);
   DECL_GFX_PREF(Live, "layers.omtp.release-capture-on-main-thread", LayersOMTPReleaseCaptureOnMainThread, bool, false);
   DECL_GFX_PREF(Live, "layers.orientation.sync.timeout",       OrientationSyncMillis, uint32_t, (uint32_t)0);
   DECL_GFX_PREF(Once, "layers.prefer-opengl",                  LayersPreferOpenGL, bool, false);
   DECL_GFX_PREF(Live, "layers.progressive-paint",              ProgressivePaint, bool, false);
   DECL_GFX_PREF(Live, "layers.shared-buffer-provider.enabled", PersistentBufferProviderSharedEnabled, bool, false);
   DECL_GFX_PREF(Live, "layers.single-tile.enabled",            LayersSingleTileEnabled, bool, true);
   DECL_GFX_PREF(Live, "layers.force-synchronous-resize",       LayersForceSynchronousResize, bool, true);
 
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -297,17 +297,17 @@ HadSimulatedInterrupt()
             ReportOverRecursed(cx);                                           \
             return false;                                                     \
         }                                                                     \
     } while (0)
 
 #  define JS_INTERRUPT_POSSIBLY_FAIL()                                        \
     do {                                                                      \
         if (MOZ_UNLIKELY(js::oom::ShouldFailWithInterrupt())) {               \
-            cx->interrupt_ = true;                                            \
+            cx->requestInterrupt(js::InterruptReason::CallbackUrgent);        \
             return cx->handleInterrupt();                                     \
         }                                                                     \
     } while (0)
 
 # else
 
 #  define JS_OOM_POSSIBLY_FAIL() do {} while(0)
 #  define JS_OOM_POSSIBLY_FAIL_BOOL() do {} while(0)
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -559,34 +559,78 @@ class MOZ_STACK_CLASS AutoInitializeSour
     { }
 
     ~AutoInitializeSourceObject() {
         if (sourceObjectOut_)
             *sourceObjectOut_ = compiler_.sourceObjectPtr();
     }
 };
 
+// RAII class to check the frontend reports an exception when it fails to
+// compile a script.
+class MOZ_RAII AutoAssertReportedException
+{
+#ifdef DEBUG
+    JSContext* cx_;
+    bool check_;
+
+  public:
+    explicit AutoAssertReportedException(JSContext* cx)
+      : cx_(cx),
+        check_(true)
+    {}
+    void reset() {
+        check_ = false;
+    }
+    ~AutoAssertReportedException() {
+        if (!check_)
+            return;
+
+        if (!cx_->helperThread()) {
+            MOZ_ASSERT(cx_->isExceptionPending());
+            return;
+        }
+
+        ParseTask* task = cx_->helperThread()->parseTask();
+        MOZ_ASSERT(task->outOfMemory ||
+                   task->overRecursed ||
+                   !task->errors.empty());
+    }
+#else
+  public:
+    explicit AutoAssertReportedException(JSContext*) {}
+    void reset() {}
+#endif
+};
+
 JSScript*
 frontend::CompileGlobalScript(JSContext* cx, LifoAlloc& alloc, ScopeKind scopeKind,
                               const ReadOnlyCompileOptions& options,
                               SourceBufferHolder& srcBuf,
                               ScriptSourceObject** sourceObjectOut)
 {
     MOZ_ASSERT(scopeKind == ScopeKind::Global || scopeKind == ScopeKind::NonSyntactic);
+    AutoAssertReportedException assertException(cx);
     BytecodeCompiler compiler(cx, alloc, options, srcBuf, /* enclosingScope = */ nullptr);
     AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
-    return compiler.compileGlobalScript(scopeKind);
+    JSScript* script = compiler.compileGlobalScript(scopeKind);
+    if (!script)
+        return nullptr;
+    assertException.reset();
+    return script;
 }
 
 #if defined(JS_BUILD_BINAST)
 
 JSScript*
 frontend::CompileGlobalBinASTScript(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
                                     const uint8_t* src, size_t len)
 {
+    AutoAssertReportedException assertException(cx);
+
     frontend::UsedNameTracker usedNames(cx);
     if (!usedNames.init())
         return nullptr;
 
     RootedObject sourceObj(cx, CreateScriptSourceObject(cx, options));
 
     if (!sourceObj)
         return nullptr;
@@ -612,77 +656,96 @@ frontend::CompileGlobalBinASTScript(JSCo
 
     ParseNode *pn = parsed.unwrap();
     if (!bce.emitScript(pn))
         return nullptr;
 
     if (!NameFunctions(cx, pn))
         return nullptr;
 
+    assertException.reset();
     return script;
 }
 
 #endif // JS_BUILD_BINAST
 
 JSScript*
 frontend::CompileEvalScript(JSContext* cx, LifoAlloc& alloc,
                             HandleObject environment, HandleScope enclosingScope,
                             const ReadOnlyCompileOptions& options,
                             SourceBufferHolder& srcBuf,
                             ScriptSourceObject** sourceObjectOut)
 {
+    AutoAssertReportedException assertException(cx);
     BytecodeCompiler compiler(cx, alloc, options, srcBuf, enclosingScope);
     AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
-    return compiler.compileEvalScript(environment, enclosingScope);
+    JSScript* script = compiler.compileEvalScript(environment, enclosingScope);
+    if (!script)
+        return nullptr;
+    assertException.reset();
+    return script;
+
 }
 
 ModuleObject*
 frontend::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& optionsInput,
                         SourceBufferHolder& srcBuf, LifoAlloc& alloc,
                         ScriptSourceObject** sourceObjectOut)
 {
     MOZ_ASSERT(srcBuf.get());
     MOZ_ASSERT_IF(sourceObjectOut, *sourceObjectOut == nullptr);
 
+    AutoAssertReportedException assertException(cx);
+
     CompileOptions options(cx, optionsInput);
     options.maybeMakeStrictMode(true); // ES6 10.2.1 Module code is always strict mode code.
     options.setIsRunOnce(true);
     options.allowHTMLComments = false;
 
     RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
     BytecodeCompiler compiler(cx, alloc, options, srcBuf, emptyGlobalScope);
     AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
-    return compiler.compileModule();
+    ModuleObject* module = compiler.compileModule();
+    if (!module)
+        return nullptr;
+
+    assertException.reset();
+    return module;
 }
 
 ModuleObject*
 frontend::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
                         SourceBufferHolder& srcBuf)
 {
+    AutoAssertReportedException assertException(cx);
+
     if (!GlobalObject::ensureModulePrototypesCreated(cx, cx->global()))
         return nullptr;
 
     LifoAlloc& alloc = cx->tempLifoAlloc();
     RootedModuleObject module(cx, CompileModule(cx, options, srcBuf, alloc));
     if (!module)
         return nullptr;
 
     // This happens in GlobalHelperThreadState::finishModuleParseTask() when a
     // module is compiled off thread.
     if (!ModuleObject::Freeze(cx, module))
         return nullptr;
 
+    assertException.reset();
     return module;
 }
 
 bool
 frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length)
 {
     MOZ_ASSERT(cx->compartment() == lazy->functionNonDelazifying()->compartment());
 
+    AutoAssertReportedException assertException(cx);
+
     CompileOptions options(cx);
     options.setMutedErrors(lazy->mutedErrors())
            .setFileAndLine(lazy->filename(), lazy->lineno())
            .setColumn(lazy->column())
            .setScriptSourceOffset(lazy->sourceStart())
            .setNoScriptRval(false)
            .setSelfHostingMode(false);
 
@@ -740,69 +803,102 @@ frontend::CompileLazyFunction(JSContext*
         return false;
 
     if (!bce.emitFunctionScript(pn->pn_body))
         return false;
 
     if (!NameFunctions(cx, pn))
         return false;
 
+    assertException.reset();
     return true;
 }
 
 bool
 frontend::CompileStandaloneFunction(JSContext* cx, MutableHandleFunction fun,
                                     const ReadOnlyCompileOptions& options,
                                     JS::SourceBufferHolder& srcBuf,
                                     const Maybe<uint32_t>& parameterListEnd,
                                     HandleScope enclosingScope /* = nullptr */)
 {
+    AutoAssertReportedException assertException(cx);
+
     RootedScope scope(cx, enclosingScope);
     if (!scope)
         scope = &cx->global()->emptyGlobalScope();
 
     BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, scope);
-    return compiler.compileStandaloneFunction(fun, GeneratorKind::NotGenerator,
-                                              FunctionAsyncKind::SyncFunction,
-                                              parameterListEnd);
+    if (!compiler.compileStandaloneFunction(fun, GeneratorKind::NotGenerator,
+                                            FunctionAsyncKind::SyncFunction,
+                                            parameterListEnd))
+    {
+        return false;
+    }
+
+    assertException.reset();
+    return true;
 }
 
 bool
 frontend::CompileStandaloneGenerator(JSContext* cx, MutableHandleFunction fun,
                                      const ReadOnlyCompileOptions& options,
                                      JS::SourceBufferHolder& srcBuf,
                                      const Maybe<uint32_t>& parameterListEnd)
 {
+    AutoAssertReportedException assertException(cx);
+
     RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
 
     BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope);
-    return compiler.compileStandaloneFunction(fun, GeneratorKind::Generator,
-                                              FunctionAsyncKind::SyncFunction,
-                                              parameterListEnd);
+    if (!compiler.compileStandaloneFunction(fun, GeneratorKind::Generator,
+                                            FunctionAsyncKind::SyncFunction,
+                                            parameterListEnd))
+    {
+        return false;
+    }
+
+    assertException.reset();
+    return true;
 }
 
 bool
 frontend::CompileStandaloneAsyncFunction(JSContext* cx, MutableHandleFunction fun,
                                          const ReadOnlyCompileOptions& options,
                                          JS::SourceBufferHolder& srcBuf,
                                          const Maybe<uint32_t>& parameterListEnd)
 {
+    AutoAssertReportedException assertException(cx);
+
     RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
 
     BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope);
-    return compiler.compileStandaloneFunction(fun, GeneratorKind::NotGenerator,
-                                              FunctionAsyncKind::AsyncFunction,
-                                              parameterListEnd);
+    if (!compiler.compileStandaloneFunction(fun, GeneratorKind::NotGenerator,
+                                            FunctionAsyncKind::AsyncFunction,
+                                            parameterListEnd))
+    {
+        return false;
+    }
+
+    assertException.reset();
+    return true;
 }
 
 bool
 frontend::CompileStandaloneAsyncGenerator(JSContext* cx, MutableHandleFunction fun,
                                           const ReadOnlyCompileOptions& options,
                                           JS::SourceBufferHolder& srcBuf,
                                           const Maybe<uint32_t>& parameterListEnd)
 {
+    AutoAssertReportedException assertException(cx);
+
     RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
 
     BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope);
-    return compiler.compileStandaloneFunction(fun, GeneratorKind::Generator,
-                                              FunctionAsyncKind::AsyncFunction,
-                                              parameterListEnd);
+    if (!compiler.compileStandaloneFunction(fun, GeneratorKind::Generator,
+                                            FunctionAsyncKind::AsyncFunction,
+                                            parameterListEnd))
+    {
+        return false;
+    }
+
+    assertException.reset();
+    return true;
 }
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -309,17 +309,17 @@ GCRuntime::gcIfNeededAtAllocation(JSCont
 {
 #ifdef JS_GC_ZEAL
     if (needZealousGC())
         runDebugGC();
 #endif
 
     // Invoking the interrupt callback can fail and we can't usefully
     // handle that here. Just check in case we need to collect instead.
-    if (cx->hasPendingInterrupt())
+    if (cx->hasAnyPendingInterrupt())
         gcIfRequested();
 
     // If we have grown past our GC heap threshold while in the middle of
     // an incremental GC, we're growing faster than we're GCing, so stop
     // the world and do a full, non-incremental GC right now, if possible.
     if (isIncrementalGCInProgress() &&
         cx->zone()->usage.gcBytes() > cx->zone()->threshold.gcTriggerBytes())
     {
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -1316,18 +1316,18 @@ GCRuntime::finish()
     }
 
     /*
      * Wait until the background finalization and allocation stops and the
      * helper thread shuts down before we forcefully release any remaining GC
      * memory.
      */
     helperState.finish();
-    allocTask.cancel(GCParallelTask::CancelAndWait);
-    decommitTask.cancel(GCParallelTask::CancelAndWait);
+    allocTask.cancelAndWait();
+    decommitTask.cancelAndWait();
 
 #ifdef JS_GC_ZEAL
     /* Free memory associated with GC verification. */
     finishVerifier();
 #endif
 
     /* Delete all remaining zones. */
     if (rt->gcInitialized) {
@@ -3280,36 +3280,30 @@ void
 GCRuntime::requestMajorGC(JS::gcreason::Reason reason)
 {
     MOZ_ASSERT(!CurrentThreadIsPerformingGC());
 
     if (majorGCRequested())
         return;
 
     majorGCTriggerReason = reason;
-
-    // There's no need to use RequestInterruptUrgent here. It's slower because
-    // it has to interrupt (looping) Ion code, but loops in Ion code that
-    // affect GC will have an explicit interrupt check.
-    rt->mainContextFromOwnThread()->requestInterrupt(JSContext::RequestInterruptCanWait);
+    rt->mainContextFromOwnThread()->requestInterrupt(InterruptReason::GC);
 }
 
 void
 Nursery::requestMinorGC(JS::gcreason::Reason reason) const
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime()));
     MOZ_ASSERT(!CurrentThreadIsPerformingGC());
 
     if (minorGCRequested())
         return;
 
     minorGCTriggerReason_ = reason;
-
-    // See comment in requestMajorGC.
-    runtime()->mainContextFromOwnThread()->requestInterrupt(JSContext::RequestInterruptCanWait);
+    runtime()->mainContextFromOwnThread()->requestInterrupt(InterruptReason::GC);
 }
 
 bool
 GCRuntime::triggerGC(JS::gcreason::Reason reason)
 {
     /*
      * Don't trigger GCs if this is being called off the main thread from
      * onTooMuchMalloc().
@@ -7460,17 +7454,17 @@ GCRuntime::gcCycle(bool nonincrementalBy
             assertBackgroundSweepingFinished();
             MOZ_ASSERT(!decommitTask.isRunning());
         }
 
         // We must also wait for background allocation to finish so we can
         // avoid taking the GC lock when manipulating the chunks during the GC.
         // The background alloc task can run between slices, so we must wait
         // for it at the start of every slice.
-        allocTask.cancel(GCParallelTask::CancelAndWait);
+        allocTask.cancelAndWait();
     }
 
     // We don't allow off-thread parsing to start while we're doing an
     // incremental GC.
     MOZ_ASSERT_IF(rt->activeGCInAtomsZone(), !rt->hasHelperThreadZones());
 
     auto result = budgetIncrementalGC(nonincrementalByAPI, reason, budget, session);
 
@@ -7783,17 +7777,17 @@ js::PrepareForDebugGC(JSRuntime* rt)
     if (!ZonesSelected(rt))
         JS::PrepareForFullGC(rt->mainContextFromOwnThread());
 }
 
 void
 GCRuntime::onOutOfMallocMemory()
 {
     // Stop allocating new chunks.
-    allocTask.cancel(GCParallelTask::CancelAndWait);
+    allocTask.cancelAndWait();
 
     // Make sure we release anything queued for release.
     decommitTask.join();
 
     // Wait for background free of nursery huge slots to finish.
     nursery().waitBackgroundFreeEnd();
 
     AutoLockGC lock(rt);
--- a/js/src/gc/GCParallelTask.h
+++ b/js/src/gc/GCParallelTask.h
@@ -15,39 +15,44 @@ namespace js {
 // A generic task used to dispatch work to the helper thread system.
 // Users should derive from GCParallelTask add what data they need and
 // override |run|.
 class GCParallelTask
 {
     JSRuntime* const runtime_;
 
     // The state of the parallel computation.
-    enum TaskState {
+    enum class State {
         NotStarted,
         Dispatched,
-        Finished,
+        Finished
     };
-    UnprotectedData<TaskState> state;
+    UnprotectedData<State> state_;
 
     // Amount of time this task took to execute.
     MainThreadOrGCTaskData<mozilla::TimeDuration> duration_;
 
     explicit GCParallelTask(const GCParallelTask&) = delete;
 
   protected:
     // A flag to signal a request for early completion of the off-thread task.
-    mozilla::Atomic<bool> cancel_;
+    mozilla::Atomic<bool, mozilla::MemoryOrdering::ReleaseAcquire> cancel_;
 
     virtual void run() = 0;
 
   public:
-    explicit GCParallelTask(JSRuntime* runtime) : runtime_(runtime), state(NotStarted), duration_(nullptr) {}
+    explicit GCParallelTask(JSRuntime* runtime)
+      : runtime_(runtime),
+        state_(State::NotStarted),
+        duration_(nullptr),
+        cancel_(false)
+    {}
     GCParallelTask(GCParallelTask&& other)
       : runtime_(other.runtime_),
-        state(other.state),
+        state_(other.state_),
         duration_(nullptr),
         cancel_(false)
     {}
 
     // Derived classes must override this to ensure that join() gets called
     // before members get destructed.
     virtual ~GCParallelTask();
 
@@ -64,27 +69,55 @@ class GCParallelTask
     // efficient to take the helper thread lock once and use these methods.
     bool startWithLockHeld(AutoLockHelperThreadState& locked);
     void joinWithLockHeld(AutoLockHelperThreadState& locked);
 
     // Instead of dispatching to a helper, run the task on the current thread.
     void runFromMainThread(JSRuntime* rt);
 
     // Dispatch a cancelation request.
-    enum CancelMode { CancelNoWait, CancelAndWait};
-    void cancel(CancelMode mode = CancelNoWait) {
+    void cancelAndWait() {
         cancel_ = true;
-        if (mode == CancelAndWait)
-            join();
+        join();
     }
 
     // Check if a task is actively running.
-    bool isRunningWithLockHeld(const AutoLockHelperThreadState& locked) const;
+    bool isRunningWithLockHeld(const AutoLockHelperThreadState& lock) const {
+        return isDispatched(lock);
+    }
     bool isRunning() const;
 
+  private:
+    void assertNotStarted() const {
+        // Don't lock here because that adds extra synchronization in debug
+        // builds that may hide bugs. There's no race if the assertion passes.
+        MOZ_ASSERT(state_ == State::NotStarted);
+    }
+    bool isNotStarted(const AutoLockHelperThreadState& lock) const {
+        return state_ == State::NotStarted;
+    }
+    bool isDispatched(const AutoLockHelperThreadState& lock) const {
+        return state_ == State::Dispatched;
+    }
+    bool isFinished(const AutoLockHelperThreadState& lock) const {
+        return state_ == State::Finished;
+    }
+    void setDispatched(const AutoLockHelperThreadState& lock) {
+        MOZ_ASSERT(state_ == State::NotStarted);
+        state_ = State::Dispatched;
+    }
+    void setFinished(const AutoLockHelperThreadState& lock) {
+        MOZ_ASSERT(state_ == State::Dispatched);
+        state_ = State::Finished;
+    }
+    void setNotStarted(const AutoLockHelperThreadState& lock) {
+        MOZ_ASSERT(state_ == State::Finished);
+        state_ = State::NotStarted;
+    }
+
     // This should be friended to HelperThread, but cannot be because it
     // would introduce several circular dependencies.
   public:
     void runFromHelperThread(AutoLockHelperThreadState& locked);
 };
 
 } /* namespace js */
 #endif /* gc_GCParallelTask_h */
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -313,17 +313,17 @@ class GCRuntime
     // Internal public interface
     State state() const { return incrementalState; }
     bool isHeapCompacting() const { return state() == State::Compact; }
     bool isForegroundSweeping() const { return state() == State::Sweep; }
     bool isBackgroundSweeping() { return helperState.isBackgroundSweeping(); }
     void waitBackgroundSweepEnd() { helperState.waitBackgroundSweepEnd(); }
     void waitBackgroundSweepOrAllocEnd() {
         helperState.waitBackgroundSweepEnd();
-        allocTask.cancel(GCParallelTask::CancelAndWait);
+        allocTask.cancelAndWait();
     }
 
 #ifdef DEBUG
     bool onBackgroundThread() { return helperState.onBackgroundThread(); }
 #endif // DEBUG
 
     void lockGC() {
         lock.lock();
--- a/js/src/irregexp/NativeRegExpMacroAssembler.cpp
+++ b/js/src/irregexp/NativeRegExpMacroAssembler.cpp
@@ -561,22 +561,24 @@ NativeRegExpMacroAssembler::AdvanceRegis
         masm.addPtr(Imm32(by), register_location(reg));
 }
 
 void
 NativeRegExpMacroAssembler::Backtrack()
 {
     JitSpew(SPEW_PREFIX "Backtrack");
 
-    // Check for an interrupt.
+    // Check for an urgent interrupt. We don't want to leave JIT code and enter
+    // the regex interpreter for non-urgent interrupts. Note that interruptBits_
+    // is a bitfield.
     Label noInterrupt;
-    masm.branch32(Assembler::Equal,
-                  AbsoluteAddress(cx->addressOfInterruptRegExpJit()),
-                  Imm32(0),
-                  &noInterrupt);
+    masm.branchTest32(Assembler::Zero,
+                      AbsoluteAddress(cx->addressOfInterruptBits()),
+                      Imm32(uint32_t(InterruptReason::CallbackUrgent)),
+                      &noInterrupt);
     masm.movePtr(ImmWord(RegExpRunStatus_Error), temp0);
     masm.jump(&exit_label_);
     masm.bind(&noInterrupt);
 
     // Pop code location from backtrack stack and jump to location.
     PopBacktrack(temp0);
     masm.jump(temp0);
 }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/regress/baseline-nops-jumptable.js
@@ -0,0 +1,12 @@
+// |jit-test| --arm-asm-nop-fill=1
+var f = wasmEvalText(`(module (func (result i32) (param i32)
+      (block $0
+       (block $1
+        (block $2
+         (block $default
+          (br_table $0 $1 $2 $default (get_local 0))))))
+      (return (i32.const 0)))
+    (export "" 0)
+)`).exports[""];
+
+f(0);
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -696,17 +696,17 @@ static const VMFunction InterruptCheckIn
 
 bool
 BaselineCompiler::emitInterruptCheck()
 {
     frame.syncStack(0);
 
     Label done;
     masm.branch32(Assembler::Equal,
-                  AbsoluteAddress(cx->addressOfInterrupt()), Imm32(0),
+                  AbsoluteAddress(cx->addressOfInterruptBits()), Imm32(0),
                   &done);
 
     prepareVMCall();
     if (!callVM(InterruptCheckInfo))
         return false;
 
     masm.bind(&done);
     return true;
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -12867,17 +12867,17 @@ CodeGenerator::visitAssertRangeV(LAssert
     masm.bind(&done);
 }
 
 void
 CodeGenerator::visitInterruptCheck(LInterruptCheck* lir)
 {
     OutOfLineCode* ool = oolCallVM(InterruptCheckInfo, lir, ArgList(), StoreNothing());
 
-    const void* interruptAddr = gen->runtime->addressOfInterrupt();
+    const void* interruptAddr = gen->runtime->addressOfInterruptBits();
     masm.branch32(Assembler::NotEqual, AbsoluteAddress(interruptAddr), Imm32(0), ool->entry());
     masm.bind(ool->rejoin());
 }
 
 void
 CodeGenerator::visitWasmInterruptCheck(LWasmInterruptCheck* lir)
 {
     MOZ_ASSERT(gen->compilingWasm());
@@ -13040,17 +13040,17 @@ CodeGenerator::visitNewTarget(LNewTarget
 
     Register argvLen = output.scratchReg();
 
     Address actualArgsPtr(masm.getStackPointer(), frameSize() + JitFrameLayout::offsetOfNumActualArgs());
     masm.loadPtr(actualArgsPtr, argvLen);
 
     Label useNFormals;
 
-    size_t numFormalArgs = ins->mirRaw()->block()->info().funMaybeLazy()->nargs();
+    size_t numFormalArgs = ins->mirRaw()->block()->info().nargs();
     masm.branchPtr(Assembler::Below, argvLen, Imm32(numFormalArgs),
                    &useNFormals);
 
     size_t argsOffset = frameSize() + JitFrameLayout::offsetOfActualArgs();
     {
         BaseValueIndex newTarget(masm.getStackPointer(), argvLen, argsOffset);
         masm.loadValue(newTarget, output);
         masm.jump(&done);
--- a/js/src/jit/CompileInfo.h
+++ b/js/src/jit/CompileInfo.h
@@ -242,23 +242,24 @@ class CompileInfo
                     break;
                 }
             }
         }
 
         // If the script uses an environment in body, the environment chain
         // will need to be observable.
         needsBodyEnvironmentObject_ = script->needsBodyEnvironment();
+        funNeedsSomeEnvironmentObject_ = fun ? fun->needsSomeEnvironmentObject() : false;
     }
 
     explicit CompileInfo(unsigned nlocals)
       : script_(nullptr), fun_(nullptr), osrPc_(nullptr),
         analysisMode_(Analysis_None), scriptNeedsArgsObj_(false),
         mayReadFrameArgsDirectly_(false), inlineScriptTree_(nullptr),
-        needsBodyEnvironmentObject_(false)
+        needsBodyEnvironmentObject_(false), funNeedsSomeEnvironmentObject_(false)
     {
         nimplicit_ = 0;
         nargs_ = 0;
         nlocals_ = nlocals;
         nstack_ = 1;  /* For FunctionCompiler::pushPhiInput/popPhiOutput */
         nslots_ = nlocals_ + nstack_;
     }
 
@@ -479,17 +480,17 @@ class CompileInfo
 
         // The |this| frame slot in derived class constructors should never be
         // optimized out, as a Debugger might need to perform TDZ checks on it
         // via, e.g., an exceptionUnwind handler. The TDZ check is required
         // for correctness if the handler decides to continue execution.
         if (thisSlotForDerivedClassConstructor_ && *thisSlotForDerivedClassConstructor_ == slot)
             return true;
 
-        if (funMaybeLazy()->needsSomeEnvironmentObject() && slot == environmentChainSlot())
+        if (funNeedsSomeEnvironmentObject_ && slot == environmentChainSlot())
             return true;
 
         // If the function may need an arguments object, then make sure to
         // preserve the env chain, because it may be needed to construct the
         // arguments object during bailout. If we've already created an
         // arguments object (or got one via OSR), preserve that as well.
         if (hasArguments() && (slot == environmentChainSlot() || slot == argsObjSlot()))
             return true;
@@ -569,14 +570,15 @@ class CompileInfo
 
     bool mayReadFrameArgsDirectly_;
 
     InlineScriptTree* inlineScriptTree_;
 
     // Whether a script needs environments within its body. This informs us
     // that the environment chain is not easy to reconstruct.
     bool needsBodyEnvironmentObject_;
+    bool funNeedsSomeEnvironmentObject_;
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_CompileInfo_h */
--- a/js/src/jit/CompileWrappers.cpp
+++ b/js/src/jit/CompileWrappers.cpp
@@ -109,19 +109,19 @@ CompileRuntime::mainContextPtr()
 
 const void*
 CompileRuntime::addressOfJitStackLimit()
 {
     return runtime()->mainContextFromAnyThread()->addressOfJitStackLimit();
 }
 
 const void*
-CompileRuntime::addressOfInterrupt()
+CompileRuntime::addressOfInterruptBits()
 {
-    return runtime()->mainContextFromAnyThread()->addressOfInterrupt();
+    return runtime()->mainContextFromAnyThread()->addressOfInterruptBits();
 }
 
 #ifdef DEBUG
 bool
 CompileRuntime::isInsideNursery(gc::Cell* cell)
 {
     return UninlinedIsInsideNursery(cell);
 }
--- a/js/src/jit/CompileWrappers.h
+++ b/js/src/jit/CompileWrappers.h
@@ -44,17 +44,17 @@ class CompileRuntime
     const PropertyName* emptyString();
     const StaticStrings& staticStrings();
     const Value& NaNValue();
     const Value& positiveInfinityValue();
     const WellKnownSymbols& wellKnownSymbols();
 
     const void* mainContextPtr();
     const void* addressOfJitStackLimit();
-    const void* addressOfInterrupt();
+    const void* addressOfInterruptBits();
 
 #ifdef DEBUG
     bool isInsideNursery(gc::Cell* cell);
 #endif
 
     // DOM callbacks must be threadsafe (and will hopefully be removed soon).
     const DOMCallbacks* DOMcallbacks();
 
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -2751,16 +2751,28 @@ Assembler::enterNoPool(size_t maxInst)
 }
 
 void
 Assembler::leaveNoPool()
 {
     m_buffer.leaveNoPool();
 }
 
+void
+Assembler::enterNoNops()
+{
+    m_buffer.enterNoNops();
+}
+
+void
+Assembler::leaveNoNops()
+{
+    m_buffer.leaveNoNops();
+}
+
 ptrdiff_t
 Assembler::GetBranchOffset(const Instruction* i_)
 {
     MOZ_ASSERT(i_->is<InstBranchImm>());
     InstBranchImm* i = i_->as<InstBranchImm>();
     BOffImm dest;
     i->extractImm(&dest);
     return dest.decode();
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -1953,16 +1953,18 @@ class Assembler : public AssemblerShared
     // END API
 
     // Move our entire pool into the instruction stream. This is to force an
     // opportunistic dump of the pool, prefferably when it is more convenient to
     // do a dump.
     void flushBuffer();
     void enterNoPool(size_t maxInst);
     void leaveNoPool();
+    void enterNoNops();
+    void leaveNoNops();
     // This should return a BOffImm, but we didn't want to require everyplace
     // that used the AssemblerBuffer to make that class.
     static ptrdiff_t GetBranchOffset(const Instruction* i);
     static void RetargetNearBranch(Instruction* i, int offset, Condition cond, bool final = true);
     static void RetargetNearBranch(Instruction* i, int offset, bool final = true);
     static void RetargetFarBranch(Instruction* i, uint8_t** slot, uint8_t* dest, Condition cond);
 
     static void WritePoolHeader(uint8_t* start, Pool* p, bool isNatural);
@@ -2482,12 +2484,25 @@ class AutoForbidPools
         masm_->enterNoPool(maxInst);
     }
 
     ~AutoForbidPools() {
         masm_->leaveNoPool();
     }
 };
 
+// Forbids nop filling for testing purposes. Not nestable.
+class AutoForbidNops
+{
+    Assembler* masm_;
+  public:
+    explicit AutoForbidNops(Assembler* masm) : masm_(masm) {
+        masm_->enterNoNops();
+    }
+    ~AutoForbidNops() {
+        masm_->leaveNoNops();
+    }
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm_Assembler_arm_h */
--- a/js/src/jit/arm64/Assembler-arm64.h
+++ b/js/src/jit/arm64/Assembler-arm64.h
@@ -516,25 +516,38 @@ Imm64::secondHalf() const
 }
 
 void PatchJump(CodeLocationJump& jump_, CodeLocationLabel label);
 
 // Forbids pool generation during a specified interval. Not nestable.
 class AutoForbidPools
 {
     Assembler* asm_;
-
   public:
     AutoForbidPools(Assembler* asm_, size_t maxInst)
       : asm_(asm_)
     {
         asm_->enterNoPool(maxInst);
     }
-
     ~AutoForbidPools() {
         asm_->leaveNoPool();
     }
 };
 
+// Forbids nop filling for testing purposes. Not nestable.
+class AutoForbidNops
+{
+    Assembler* asm_;
+  public:
+    explicit AutoForbidNops(Assembler* asm_)
+      : asm_(asm_)
+    {
+        asm_->enterNoNops();
+    }
+    ~AutoForbidNops() {
+        asm_->leaveNoNops();
+    }
+};
+
 } // namespace jit
 } // namespace js
 
 #endif // A64_ASSEMBLER_A64_H_
--- a/js/src/jit/arm64/vixl/MozBaseAssembler-vixl.h
+++ b/js/src/jit/arm64/vixl/MozBaseAssembler-vixl.h
@@ -281,16 +281,23 @@ class MozBaseAssembler : public js::jit:
     armbuffer_.enterNoPool(maxInst);
   }
 
   // Marks the end of a no-pool region.
   void leaveNoPool() {
     armbuffer_.leaveNoPool();
   }
 
+  void enterNoNops() {
+    armbuffer_.enterNoNops();
+  }
+  void leaveNoNops() {
+    armbuffer_.leaveNoNops();
+  }
+
  public:
   // Static interface used by IonAssemblerBufferWithConstantPools.
   static void InsertIndexIntoTag(uint8_t* load, uint32_t index);
   static bool PatchConstantPoolLoad(void* loadAddr, void* constPoolAddr);
   static void PatchShortRangeBranchToVeneer(ARMBuffer*, unsigned rangeIdx, BufferOffset deadline,
                                             BufferOffset veneer);
   static uint32_t PlaceConstantPoolBarrier(int offset);
 
--- a/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h
+++ b/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h
@@ -637,16 +637,17 @@ struct AssemblerBufferWithConstantPools 
     const uint32_t alignFillInst_;
 
     // Insert a number of NOP instructions between each requested instruction at
     // all locations at which a pool can potentially spill. This is useful for
     // checking that instruction locations are correctly referenced and/or
     // followed.
     const uint32_t nopFillInst_;
     const unsigned nopFill_;
+
     // For inhibiting the insertion of fill NOPs in the dynamic context in which
     // they are being inserted.
     bool inhibitNops_;
 
   public:
     // A unique id within each JitContext, to identify pools in the debug
     // spew. Set by the MacroAssembler, see getNextAssemblerId().
     int id;
@@ -1078,17 +1079,27 @@ struct AssemblerBufferWithConstantPools 
         canNotPlacePool_ = true;
     }
 
     void leaveNoPool() {
         MOZ_ASSERT(canNotPlacePool_);
         canNotPlacePool_ = false;
 
         // Validate the maxInst argument supplied to enterNoPool().
-        MOZ_ASSERT(this->nextOffset().getOffset() - canNotPlacePoolStartOffset_ <= canNotPlacePoolMaxInst_ * InstSize);
+        MOZ_ASSERT(this->nextOffset().getOffset() - canNotPlacePoolStartOffset_ <=
+                   canNotPlacePoolMaxInst_ * InstSize);
+    }
+
+    void enterNoNops() {
+        MOZ_ASSERT(!inhibitNops_);
+        inhibitNops_ = true;
+    }
+    void leaveNoNops() {
+        MOZ_ASSERT(inhibitNops_);
+        inhibitNops_ = false;
     }
 
     void align(unsigned alignment) {
         MOZ_ASSERT(mozilla::IsPowerOfTwo(alignment));
         MOZ_ASSERT(alignment >= InstSize);
 
         // A pool many need to be dumped at this point, so insert NOP fill here.
         insertNopFill();
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5720,23 +5720,23 @@ JS_PUBLIC_API(void)
 JS::InitConsumeStreamCallback(JSContext* cx, ConsumeStreamCallback callback)
 {
     cx->runtime()->consumeStreamCallback = callback;
 }
 
 JS_PUBLIC_API(void)
 JS_RequestInterruptCallback(JSContext* cx)
 {
-    cx->requestInterrupt(JSContext::RequestInterruptUrgent);
+    cx->requestInterrupt(InterruptReason::CallbackUrgent);
 }
 
 JS_PUBLIC_API(void)
 JS_RequestInterruptCallbackCanWait(JSContext* cx)
 {
-    cx->requestInterrupt(JSContext::RequestInterruptCanWait);
+    cx->requestInterrupt(InterruptReason::CallbackCanWait);
 }
 
 JS::AutoSetAsyncStackForNewCalls::AutoSetAsyncStackForNewCalls(
   JSContext* cx, HandleObject stack, const char* asyncCause,
   JS::AutoSetAsyncStackForNewCalls::AsyncCallKind kind)
   : cx(cx),
     oldAsyncStack(cx, cx->asyncStackForNewActivations()),
     oldAsyncCause(cx->asyncCauseForNewActivations),
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -1448,61 +1448,56 @@ GlobalHelperThreadState::canStartGCParal
 }
 
 js::GCParallelTask::~GCParallelTask()
 {
     // Only most-derived classes' destructors may do the join: base class
     // destructors run after those for derived classes' members, so a join in a
     // base class can't ensure that the task is done using the members. All we
     // can do now is check that someone has previously stopped the task.
-#ifdef DEBUG
-    Maybe<AutoLockHelperThreadState> helperLock;
-    if (!HelperThreadState().isLockedByCurrentThread())
-        helperLock.emplace();
-    MOZ_ASSERT(state == NotStarted);
-#endif
+    assertNotStarted();
 }
 
 bool
 js::GCParallelTask::startWithLockHeld(AutoLockHelperThreadState& lock)
 {
-    // Tasks cannot be started twice.
-    MOZ_ASSERT(state == NotStarted);
+    assertNotStarted();
 
     // If we do the shutdown GC before running anything, we may never
     // have initialized the helper threads. Just use the serial path
     // since we cannot safely intialize them at this point.
     if (!HelperThreadState().threads)
         return false;
 
     if (!HelperThreadState().gcParallelWorklist(lock).append(this))
         return false;
-    state = Dispatched;
+    setDispatched(lock);
 
     HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
 
     return true;
 }
 
 bool
 js::GCParallelTask::start()
 {
     AutoLockHelperThreadState helperLock;
     return startWithLockHeld(helperLock);
 }
 
 void
-js::GCParallelTask::joinWithLockHeld(AutoLockHelperThreadState& locked)
+js::GCParallelTask::joinWithLockHeld(AutoLockHelperThreadState& lock)
 {
-    if (state == NotStarted)
+    if (isNotStarted(lock))
         return;
 
-    while (state != Finished)
-        HelperThreadState().wait(locked, GlobalHelperThreadState::CONSUMER);
-    state = NotStarted;
+    while (!isFinished(lock))
+        HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER);
+
+    setNotStarted(lock);
     cancel_ = false;
 }
 
 void
 js::GCParallelTask::join()
 {
     AutoLockHelperThreadState helperLock;
     joinWithLockHeld(helperLock);
@@ -1518,66 +1513,62 @@ TimeSince(TimeStamp prev)
     if (now < prev)
         now = prev;
     return now - prev;
 }
 
 void
 js::GCParallelTask::runFromMainThread(JSRuntime* rt)
 {
-    MOZ_ASSERT(state == NotStarted);
+    assertNotStarted();
     MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(rt));
     TimeStamp timeStart = TimeStamp::Now();
     run();
     duration_ = TimeSince(timeStart);
 }
 
 void
-js::GCParallelTask::runFromHelperThread(AutoLockHelperThreadState& locked)
+js::GCParallelTask::runFromHelperThread(AutoLockHelperThreadState& lock)
 {
+    MOZ_ASSERT(isDispatched(lock));
+
     AutoSetContextRuntime ascr(runtime());
     gc::AutoSetThreadIsPerformingGC performingGC;
 
     {
-        AutoUnlockHelperThreadState parallelSection(locked);
+        AutoUnlockHelperThreadState parallelSection(lock);
         TimeStamp timeStart = TimeStamp::Now();
         TlsContext.get()->heapState = JS::HeapState::MajorCollecting;
         run();
         TlsContext.get()->heapState = JS::HeapState::Idle;
         duration_ = TimeSince(timeStart);
     }
 
-    state = Finished;
-    HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
-}
-
-bool
-js::GCParallelTask::isRunningWithLockHeld(const AutoLockHelperThreadState& locked) const
-{
-    return state == Dispatched;
+    setFinished(lock);
+    HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, lock);
 }
 
 bool
 js::GCParallelTask::isRunning() const
 {
-    AutoLockHelperThreadState helperLock;
-    return isRunningWithLockHeld(helperLock);
+    AutoLockHelperThreadState lock;
+    return isRunningWithLockHeld(lock);
 }
 
 void
-HelperThread::handleGCParallelWorkload(AutoLockHelperThreadState& locked)
+HelperThread::handleGCParallelWorkload(AutoLockHelperThreadState& lock)
 {
-    MOZ_ASSERT(HelperThreadState().canStartGCParallelTask(locked));
+    MOZ_ASSERT(HelperThreadState().canStartGCParallelTask(lock));
     MOZ_ASSERT(idle());
 
     TraceLoggerThread* logger = TraceLoggerForCurrentThread();
     AutoTraceLog logCompile(logger, TraceLogger_GC);
 
-    currentTask.emplace(HelperThreadState().gcParallelWorklist(locked).popCopy());
-    gcParallelTask()->runFromHelperThread(locked);
+    currentTask.emplace(HelperThreadState().gcParallelWorklist(lock).popCopy());
+    gcParallelTask()->runFromHelperThread(lock);
     currentTask.reset();
 }
 
 static void
 LeaveParseTaskZone(JSRuntime* rt, ParseTask* task)
 {
     // Mark the zone as no longer in use by a helper thread, and available
     // to be collected by the GC.
@@ -1911,25 +1902,23 @@ HelperThread::handleIonWorkload(AutoLock
                              jit::CompileCompartment::get(builder->script()->compartment()),
                              &builder->alloc());
         builder->setBackgroundCodegen(jit::CompileBackEnd(builder));
     }
 
     FinishOffThreadIonCompile(builder, locked);
 
     // Ping the main thread so that the compiled code can be incorporated at the
-    // next interrupt callback. Don't interrupt Ion code for this, as this
-    // incorporation can be delayed indefinitely without affecting performance
-    // as long as the main thread is actually executing Ion code.
+    // next interrupt callback.
     //
     // This must happen before the current task is reset. DestroyContext
     // cancels in progress Ion compilations before destroying its target
     // context, and after we reset the current task we are no longer considered
     // to be Ion compiling.
-    rt->mainContextFromAnyThread()->requestInterrupt(JSContext::RequestInterruptCanWait);
+    rt->mainContextFromAnyThread()->requestInterrupt(InterruptReason::AttachIonCompilations);
 
     currentTask.reset();
 
     // Notify the main thread in case it is waiting for the compilation to finish.
     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
 }
 
 void
--- a/js/src/vm/JSContext-inl.h
+++ b/js/src/vm/JSContext-inl.h
@@ -387,17 +387,17 @@ CallJSDeletePropertyOp(JSContext* cx, JS
 }
 
 MOZ_ALWAYS_INLINE bool
 CheckForInterrupt(JSContext* cx)
 {
     MOZ_ASSERT(!cx->isExceptionPending());
     // Add an inline fast-path since we have to check for interrupts in some hot
     // C++ loops of library builtins.
-    if (MOZ_UNLIKELY(cx->hasPendingInterrupt()))
+    if (MOZ_UNLIKELY(cx->hasAnyPendingInterrupt()))
         return cx->handleInterrupt();
 
     JS_INTERRUPT_POSSIBLY_FAIL();
 
     return true;
 }
 
 }  /* namespace js */
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -1281,18 +1281,17 @@ JSContext::JSContext(JSRuntime* runtime,
     generatingError(false),
     cycleDetectorVector_(this),
     data(nullptr),
     outstandingRequests(0),
     jitIsBroken(false),
     asyncCauseForNewActivations(nullptr),
     asyncCallIsExplicit(false),
     interruptCallbackDisabled(false),
-    interrupt_(false),
-    interruptRegExpJit_(false),
+    interruptBits_(0),
     osrTempData_(nullptr),
     ionReturnOverride_(MagicValue(JS_ARG_POISON)),
     jitStackLimit(UINTPTR_MAX),
     jitStackLimitNoInterrupt(UINTPTR_MAX),
     getIncumbentGlobalCallback(nullptr),
     enqueuePromiseJobCallback(nullptr),
     enqueuePromiseJobCallbackData(nullptr),
     jobQueue(nullptr),
--- a/js/src/vm/JSContext.h
+++ b/js/src/vm/JSContext.h
@@ -87,16 +87,24 @@ enum class ContextKind
     HelperThread
 };
 
 #ifdef DEBUG
 bool
 CurrentThreadIsParseThread();
 #endif
 
+enum class InterruptReason : uint32_t
+{
+    GC = 1 << 0,
+    AttachIonCompilations = 1 << 1,
+    CallbackUrgent = 1 << 2,
+    CallbackCanWait = 1 << 3,
+};
+
 } /* namespace js */
 
 /*
  * A JSContext encapsulates the thread local state used when using the JS
  * runtime.
  */
 struct JSContext : public JS::RootingContext,
                    public js::MallocProvider<JSContext>
@@ -782,57 +790,52 @@ struct JSContext : public JS::RootingCon
 
   private:
     js::ThreadData<InterruptCallbackVector> interruptCallbacks_;
   public:
     InterruptCallbackVector& interruptCallbacks() { return interruptCallbacks_.ref(); }
 
     js::ThreadData<bool> interruptCallbackDisabled;
 
-    mozilla::Atomic<uint32_t, mozilla::Relaxed> interrupt_;
-    mozilla::Atomic<uint32_t, mozilla::Relaxed> interruptRegExpJit_;
-
-    enum InterruptMode {
-        RequestInterruptUrgent,
-        RequestInterruptCanWait
-    };
+    // Bitfield storing InterruptReason values.
+    mozilla::Atomic<uint32_t, mozilla::Relaxed> interruptBits_;
 
     // Any thread can call requestInterrupt() to request that this thread
-    // stop running and call the interrupt callback (allowing the interrupt
-    // callback to halt execution). To stop this thread, requestInterrupt
-    // sets two fields: interrupt_ (set to true) and jitStackLimit_ (set to
+    // stop running. To stop this thread, requestInterrupt sets two fields:
+    // interruptBits_ (a bitset of InterruptReasons) and jitStackLimit_ (set to
     // UINTPTR_MAX). The JS engine must continually poll one of these fields
-    // and call handleInterrupt if either field has the interrupt value. (The
-    // point of setting jitStackLimit_ to UINTPTR_MAX is that JIT code already
-    // needs to guard on jitStackLimit_ in every function prologue to avoid
-    // stack overflow, so we avoid a second branch on interrupt_ by setting
-    // jitStackLimit_ to a value that is guaranteed to fail the guard.)
+    // and call handleInterrupt if either field has the interrupt value.
     //
-    // Note that the writes to interrupt_ and jitStackLimit_ use a Relaxed
+    // The point of setting jitStackLimit_ to UINTPTR_MAX is that JIT code
+    // already needs to guard on jitStackLimit_ in every function prologue to
+    // avoid stack overflow, so we avoid a second branch on interruptBits_ by
+    // setting jitStackLimit_ to a value that is guaranteed to fail the guard.)
+    //
+    // Note that the writes to interruptBits_ and jitStackLimit_ use a Relaxed
     // Atomic so, while the writes are guaranteed to eventually be visible to
     // this thread, it can happen in any order. handleInterrupt calls the
     // interrupt callback if either is set, so it really doesn't matter as long
     // as the JS engine is continually polling at least one field. In corner
     // cases, this relaxed ordering could lead to an interrupt handler being
     // called twice in succession after a single requestInterrupt call, but
     // that's fine.
-    void requestInterrupt(InterruptMode mode);
+    void requestInterrupt(js::InterruptReason reason);
     bool handleInterrupt();
 
-    MOZ_ALWAYS_INLINE bool hasPendingInterrupt() const {
-        static_assert(sizeof(interrupt_) == sizeof(uint32_t), "Assumed by JIT callers");
-        return interrupt_;
+    MOZ_ALWAYS_INLINE bool hasAnyPendingInterrupt() const {
+        static_assert(sizeof(interruptBits_) == sizeof(uint32_t), "Assumed by JIT callers");
+        return interruptBits_ != 0;
+    }
+    bool hasPendingInterrupt(js::InterruptReason reason) const {
+        return interruptBits_ & uint32_t(reason);
     }
 
   public:
-    void* addressOfInterrupt() {
-        return &interrupt_;
-    }
-    void* addressOfInterruptRegExpJit() {
-        return &interruptRegExpJit_;
+    void* addressOfInterruptBits() {
+        return &interruptBits_;
     }
     void* addressOfJitStackLimit() {
         return &jitStackLimit;
     }
     void* addressOfJitStackLimitNoInterrupt() {
         return &jitStackLimitNoInterrupt;
     }
 
--- a/js/src/vm/JSFunction.h
+++ b/js/src/vm/JSFunction.h
@@ -73,16 +73,18 @@ class JSFunction : public js::NativeObje
                                        decompilable nor constructible. */
         HAS_INFERRED_NAME = 0x0100, /* function had no explicit name, but a name was
                                        set by SetFunctionName at compile time or
                                        SetFunctionNameIfNoOwnName at runtime. See
                                        atom_ for more info about this flag. */
         INTERPRETED_LAZY = 0x0200,  /* function is interpreted but doesn't have a script yet */
         RESOLVED_LENGTH  = 0x0400,  /* f.length has been resolved (see fun_resolve). */
         RESOLVED_NAME    = 0x0800,  /* f.name has been resolved (see fun_resolve). */
+        NEW_SCRIPT_CLEARED  = 0x1000, /* For a function used as an interpreted constructor, whether
+                                         a 'new' type had constructor information cleared. */
 
         FUNCTION_KIND_SHIFT = 13,
         FUNCTION_KIND_MASK  = 0x7 << FUNCTION_KIND_SHIFT,
 
         ASMJS_KIND = AsmJS << FUNCTION_KIND_SHIFT,
         ARROW_KIND = Arrow << FUNCTION_KIND_SHIFT,
         METHOD_KIND = Method << FUNCTION_KIND_SHIFT,
         CLASSCONSTRUCTOR_KIND = ClassConstructor << FUNCTION_KIND_SHIFT,
@@ -364,16 +366,24 @@ class JSFunction : public js::NativeObje
     void setResolvedLength() {
         flags_ |= RESOLVED_LENGTH;
     }
 
     void setResolvedName() {
         flags_ |= RESOLVED_NAME;
     }
 
+    // Mark a function as having its 'new' script information cleared.
+    bool wasNewScriptCleared() const {
+        return flags_ & NEW_SCRIPT_CLEARED;
+    }
+    void setNewScriptCleared() {
+        flags_ |= NEW_SCRIPT_CLEARED;
+    }
+
     void setAsyncKind(js::FunctionAsyncKind asyncKind) {
         if (isInterpretedLazy())
             lazyScript()->setAsyncKind(asyncKind);
         else
             nonLazyScript()->setAsyncKind(asyncKind);
     }
 
     static bool getUnresolvedLength(JSContext* cx, js::HandleFunction fun,
--- a/js/src/vm/JSObject.cpp
+++ b/js/src/vm/JSObject.cpp
@@ -3562,18 +3562,16 @@ JSObject::dump(js::GenericPrinter& out) 
         if (nobj->inDictionaryMode())
             out.put(" inDictionaryMode");
         if (nobj->hasShapeTable())
             out.put(" hasShapeTable");
         if (nobj->hadElementsAccess())
             out.put(" had_elements_access");
         if (nobj->isIndexed())
             out.put(" indexed");
-        if (nobj->wasNewScriptCleared())
-            out.put(" new_script_cleared");
     } else {
         out.put(" not_native\n");
     }
     out.putChar('\n');
 
     out.put("  proto ");
     TaggedProto proto = obj->taggedProto();
     if (proto.isDynamic())
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -762,24 +762,16 @@ class NativeObject : public ShapedObject
     /*
      * Whether SETLELEM was used to access this object. See also the comment near
      * PropertyTree::MAX_HEIGHT.
      */
     bool hadElementsAccess() const {
         return hasAllFlags(js::BaseShape::HAD_ELEMENTS_ACCESS);
     }
 
-    // Mark an object as having its 'new' script information cleared.
-    bool wasNewScriptCleared() const {
-        return hasAllFlags(js::BaseShape::NEW_SCRIPT_CLEARED);
-    }
-    static bool setNewScriptCleared(JSContext* cx, HandleNativeObject obj) {
-        return setFlags(cx, obj, js::BaseShape::NEW_SCRIPT_CLEARED);
-    }
-
     bool hasInterestingSymbol() const {
         return hasAllFlags(js::BaseShape::HAS_INTERESTING_SYMBOL);
     }
 
     /*
      * Grow or shrink slots immediately before changing the slot span.
      * The number of allocated slots is not stored explicitly, and changes to
      * the slots must track changes in the slot span.
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -426,27 +426,31 @@ JSRuntime::addSizeOfIncludingThis(mozill
         for (auto builder : jitRuntime_->ionLazyLinkList(this))
             rtSizes->jitLazyLink += builder->sizeOfExcludingThis(mallocSizeOf);
     }
 
     rtSizes->wasmRuntime += wasmInstances.lock()->sizeOfExcludingThis(mallocSizeOf);
 }
 
 static bool
-InvokeInterruptCallback(JSContext* cx)
+HandleInterrupt(JSContext* cx, bool invokeCallback)
 {
     MOZ_ASSERT(cx->requestDepth >= 1);
     MOZ_ASSERT(!cx->compartment()->isAtomsCompartment());
 
     cx->runtime()->gc.gcIfRequested();
 
     // A worker thread may have requested an interrupt after finishing an Ion
     // compilation.
     jit::AttachFinishedCompilations(cx);
 
+    // Don't call the interrupt callback if we only interrupted for GC or Ion.
+    if (!invokeCallback)
+        return true;
+
     // Important: Additional callbacks can occur inside the callback handler
     // if it re-enters the JS engine. The embedding must ensure that the
     // callback is disconnected before attempting such re-entry.
     if (cx->interruptCallbackDisabled)
         return true;
 
     bool stop = false;
     for (JSInterruptCallback cb : cx->interruptCallbacks()) {
@@ -497,43 +501,44 @@ InvokeInterruptCallback(JSContext* cx)
         chars = u"(stack not available)";
     JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, GetErrorMessage, nullptr,
                                    JSMSG_TERMINATED, chars);
 
     return false;
 }
 
 void
-JSContext::requestInterrupt(InterruptMode mode)
+JSContext::requestInterrupt(InterruptReason reason)
 {
-    interrupt_ = true;
+    interruptBits_ |= uint32_t(reason);
     jitStackLimit = UINTPTR_MAX;
 
-    if (mode == JSContext::RequestInterruptUrgent) {
+    if (reason == InterruptReason::CallbackUrgent) {
         // If this interrupt is urgent (slow script dialog for instance), take
         // additional steps to interrupt corner cases where the above fields are
-        // not regularly polled. Wake Atomics.wait() and irregexp JIT code.
-        interruptRegExpJit_ = true;
+        // not regularly polled.
         FutexThread::lock();
         if (fx.isWaiting())
             fx.wake(FutexThread::WakeForJSInterrupt);
         fx.unlock();
         wasm::InterruptRunningCode(this);
     }
 }
 
 bool
 JSContext::handleInterrupt()
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime()));
-    if (interrupt_ || jitStackLimit == UINTPTR_MAX) {
-        interrupt_ = false;
-        interruptRegExpJit_ = false;
+    if (hasAnyPendingInterrupt() || jitStackLimit == UINTPTR_MAX) {
+        bool invokeCallback =
+            hasPendingInterrupt(InterruptReason::CallbackUrgent) ||
+            hasPendingInterrupt(InterruptReason::CallbackCanWait);
+        interruptBits_ = 0;
         resetJitStackLimit();
-        return InvokeInterruptCallback(this);
+        return HandleInterrupt(this, invokeCallback);
     }
     return true;
 }
 
 bool
 JSRuntime::setDefaultLocale(const char* locale)
 {
     if (!locale)
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -479,20 +479,17 @@ class BaseShape : public gc::TenuredCell
         NEW_GROUP_UNKNOWN   =  0x400,
         UNCACHEABLE_PROTO   =  0x800,
         IMMUTABLE_PROTOTYPE = 0x1000,
 
         // See JSObject::isQualifiedVarObj().
         QUALIFIED_VAROBJ    = 0x2000,
 
         // 0x4000 is unused.
-
-        // For a function used as an interpreted constructor, whether a 'new'
-        // type had constructor information cleared.
-        NEW_SCRIPT_CLEARED  = 0x8000,
+        // 0x8000 is unused.
 
         OBJECT_FLAG_MASK    = 0xfff8
     };
 
   private:
     const Class*        clasp_;        /* Class of referring object. */
     uint32_t            flags;          /* Vector of above flags. */
     uint32_t            slotSpan_;      /* Object slot span for BaseShapes at
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -3066,19 +3066,17 @@ ObjectGroup::clearNewScript(JSContext* c
     AutoEnterAnalysis enter(cx);
 
     if (!replacement) {
         // Invalidate any Ion code constructing objects of this type.
         setFlags(sweep, cx, OBJECT_FLAG_NEW_SCRIPT_CLEARED);
 
         // Mark the constructing function as having its 'new' script cleared, so we
         // will not try to construct another one later.
-        RootedFunction fun(cx, newScript->function());
-        if (!NativeObject::setNewScriptCleared(cx, fun))
-            cx->recoverFromOutOfMemory();
+        newScript->function()->setNewScriptCleared();
     }
 
     detachNewScript(/* writeBarrier = */ true, replacement);
 
     if (!cx->helperThread()) {
         bool found = newScript->rollbackPartiallyInitializedObjects(cx, this);
 
         // If we managed to rollback any partially initialized objects, then
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -3830,16 +3830,20 @@ class BaseCompiler final : public BaseCo
         masm.wasmInterruptCheck(tmp, bytecodeOffset());
     }
 
     void jumpTable(const LabelVector& labels, Label* theTable) {
         // Flush constant pools to ensure that the table is never interrupted by
         // constant pool entries.
         masm.flush();
 
+#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
+        // Prevent nop sequences to appear in the jump table.
+        AutoForbidNops afn(&masm);
+#endif
         masm.bind(theTable);
 
         for (uint32_t i = 0; i < labels.length(); i++) {
             CodeLabel cl;
             masm.writeCodePointer(&cl);
             cl.target()->bind(labels[i].offset());
             masm.addCodeLabel(cl);
         }
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -2079,17 +2079,17 @@ nsImageFrame::HandleEvent(nsPresContext*
           NS_ENSURE_SUCCESS(rv, rv);
 
           bool clicked = false;
           if (aEvent->mMessage == eMouseClick && !aEvent->DefaultPrevented()) {
             *aEventStatus = nsEventStatus_eConsumeDoDefault;
             clicked = true;
           }
           nsContentUtils::TriggerLink(anchorNode, aPresContext, uri, target,
-                                      clicked, true, true);
+                                      clicked, /* isTrusted */ true);
         }
       }
     }
   }
 
   return nsAtomicContainerFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
 }
 
--- a/netwerk/base/nsIFileStreams.idl
+++ b/netwerk/base/nsIFileStreams.idl
@@ -215,22 +215,23 @@ interface nsIAsyncFileMetadata : nsIFile
      *
      * @param aCallback The callback will be used when the stream is ready to
      *                  return File metadata. Use a nullptr to cancel a
      *                  previous operation.
      *
      * @param aEventTarget The event target where aCallback will be executed.
      *                     If aCallback is passed, aEventTarget cannot be null.
      */
-    void asyncWait(in nsIFileMetadataCallback aCallback,
-                   in nsIEventTarget aEventTarget);
+    void asyncFileMetadataWait(in nsIFileMetadataCallback aCallback,
+                               in nsIEventTarget aEventTarget);
 };
 
 /**
- * This is a companion interface for nsIAsyncFileMetadata::asyncWait.
+ * This is a companion interface for
+ * nsIAsyncFileMetadata::asyncFileMetadataWait.
  */
 [function, scriptable, uuid(d01c7ead-7ba3-4726-b399-618ec8ec7057)]
 interface nsIFileMetadataCallback : nsISupports
 {
     /**
      * Called to indicate that the nsIFileMetadata object is ready.
      */
     void onFileMetadataReady(in nsIAsyncFileMetadata aObject);
--- a/security/manager/ssl/StaticHPKPins.h
+++ b/security/manager/ssl/StaticHPKPins.h
@@ -1158,9 +1158,9 @@ static const TransportSecurityPreload kP
   { "za.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
   { "zh.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
 };
 
 // Pinning Preload List Length = 485;
 
 static const int32_t kUnknownId = -1;
 
-static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1533550046602000);
+static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1533809451638000);
--- a/security/manager/ssl/nsSTSPreloadList.inc
+++ b/security/manager/ssl/nsSTSPreloadList.inc
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*****************************************************************************/
 /* This is an automatically generated file. If you're not                    */
 /* nsSiteSecurityService.cpp, you shouldn't be #including it.                */
 /*****************************************************************************/
 
 #include <stdint.h>
-const PRTime gPreloadListExpirationTime = INT64_C(1535969034685000);
+const PRTime gPreloadListExpirationTime = INT64_C(1536228415037000);
 %%
 0-1.party, 1
 0.me.uk, 1
 0005pay.com, 1
 00100010.net, 1
 0010100.net, 1
 00120012.net, 1
 00130013.net, 1
--- a/testing/web-platform/tests/fetch/api/abort/serviceworker-intercepted.https.html
+++ b/testing/web-platform/tests/fetch/api/abort/serviceworker-intercepted.https.html
@@ -8,35 +8,28 @@
   <script src="../../../service-workers/service-worker/resources/test-helpers.sub.js"></script>
 </head>
 <body>
 <script>
   // Duplicating this resource to make service worker scoping simpler.
   const SCOPE = '../resources/basic.html';
   const BODY_METHODS = ['arrayBuffer', 'blob', 'formData', 'json', 'text'];
 
-  async function cleanup() {
-    for (const iframe of document.querySelectorAll('.test-iframe')) {
-      iframe.parentNode.removeChild(iframe);
-    }
-
-    const reg = await navigator.serviceWorker.getRegistration(SCOPE);
-    if (reg) await reg.unregister();
-  }
-
-  async function setupRegistration(t) {
-    await cleanup();
-    const reg = await navigator.serviceWorker.register('../resources/sw-intercept.js', { scope: SCOPE });
+  async function setupRegistration(t, scope) {
+    const reg = await navigator.serviceWorker.register('../resources/sw-intercept.js', { scope });
     await wait_for_state(t, reg.installing, 'activated');
+    add_completion_callback(_ => reg.unregister());
     return reg;
   }
 
   promise_test(async t => {
-    await setupRegistration(t);
-    const iframe = await with_iframe(SCOPE);
+    const scope = SCOPE + "?q=aborted-not-intercepted";
+    await setupRegistration(t, scope);
+    const iframe = await with_iframe(scope);
+    add_completion_callback(_ => iframe.remove());
     const w = iframe.contentWindow;
 
     const controller = new w.AbortController();
     const signal = controller.signal;
     controller.abort();
 
     const nextData = new Promise(resolve => {
       w.navigator.serviceWorker.addEventListener('message', function once(event) {
@@ -51,18 +44,20 @@
 
     await w.fetch('data.json?no-abort');
 
     assert_true((await nextData).endsWith('?no-abort'), "Aborted request does not go through service worker");
   }, "Already aborted request does not land in service worker");
 
   for (const bodyMethod of BODY_METHODS) {
     promise_test(async t => {
-      await setupRegistration(t);
-      const iframe = await with_iframe(SCOPE);
+      const scope = SCOPE + "?q=aborted-" + bodyMethod + "-rejects";
+      await setupRegistration(t, scope);
+      const iframe = await with_iframe(scope);
+      add_completion_callback(_ => iframe.remove());
       const w = iframe.contentWindow;
 
       const controller = new w.AbortController();
       const signal = controller.signal;
 
       const log = [];
       const response = await w.fetch('data.json', { signal });
 
@@ -77,18 +72,20 @@
 
       await promise_rejects(t, "AbortError", bodyPromise);
 
       assert_array_equals(log, [`${bodyMethod}-reject`, 'next-microtask']);
     }, `response.${bodyMethod}() rejects if already aborted`);
   }
 
   promise_test(async t => {
-    await setupRegistration(t);
-    const iframe = await with_iframe(SCOPE);
+    const scope = SCOPE + "?q=aborted-stream-errors";
+    await setupRegistration(t, scope);
+    const iframe = await with_iframe(scope);
+    add_completion_callback(_ => iframe.remove());
     const w = iframe.contentWindow;
 
     const controller = new w.AbortController();
     const signal = controller.signal;
 
     const response = await w.fetch('data.json', { signal });
     const reader = response.body.getReader();
 
--- a/toolkit/components/telemetry/tests/python/test_histogramtools_non_strict.py
+++ b/toolkit/components/telemetry/tests/python/test_histogramtools_non_strict.py
@@ -1,11 +1,11 @@
 # 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/.import json
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import json
 import mozunit
 import sys
 import unittest
 from os import path
 
 TELEMETRY_ROOT_PATH = path.abspath(path.join(path.dirname(__file__), path.pardir, path.pardir))
--- a/toolkit/components/telemetry/tests/python/test_histogramtools_strict.py
+++ b/toolkit/components/telemetry/tests/python/test_histogramtools_strict.py
@@ -1,11 +1,11 @@
 # 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/.import json
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import mozunit
 import sys
 import unittest
 from os import path
 from test_histogramtools_non_strict import load_histogram
 
 TELEMETRY_ROOT_PATH = path.abspath(path.join(path.dirname(__file__), path.pardir, path.pardir))