Merge inbound to mozilla-central. a=merge
authorBrindusan Cristian <cbrindusan@mozilla.com>
Fri, 25 May 2018 03:04:03 +0300
changeset 476476 0df854d34c01bafb3c88d595f8464ed381b95d03
parent 476443 9a8839ee6c9c11e9dde87ac0261d8dbb97cd8578 (current diff)
parent 476475 f38805d4a3898e56052231f6a14a785094205270 (diff)
child 476477 1bdf8e7d1cfe6d8b9bfa891f43473345210ee281
child 476501 d2c504627834215bc4665d820ae01d28cfd7eb5f
child 476523 7fe1e764c547936aa5a47757dcf70b2d400785ae
push id1757
push userffxbld-merge
push dateFri, 24 Aug 2018 17:02:43 +0000
treeherdermozilla-release@736023aebdb1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone62.0a1
first release with
nightly linux32
0df854d34c01 / 62.0a1 / 20180525005138 / files
nightly linux64
0df854d34c01 / 62.0a1 / 20180525005138 / files
nightly mac
0df854d34c01 / 62.0a1 / 20180525005138 / files
nightly win32
0df854d34c01 / 62.0a1 / 20180525005138 / files
nightly win64
0df854d34c01 / 62.0a1 / 20180525005138 / 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
testing/web-platform/meta/MANIFEST.json
testing/web-platform/meta/navigation-timing/nav2_test_attributes_exist.html.ini
testing/web-platform/meta/navigation-timing/nav2_test_navigation_type_navigate.html.ini
testing/web-platform/meta/navigation-timing/nav2_test_redirect_none.html.ini
testing/web-platform/meta/navigation-timing/nav2_test_redirect_server.html.ini
testing/web-platform/meta/navigation-timing/nav2_test_unique_nav_instances.html.ini
testing/web-platform/meta/performance-timeline/po-observe.html.ini
--- a/devtools/client/debugger/new/README.mozilla
+++ b/devtools/client/debugger/new/README.mozilla
@@ -1,13 +1,13 @@
 This is the debugger.html project output.
 See https://github.com/devtools-html/debugger.html
 
-Version 57
+Version 58
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-56...release-57
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-57...release-58
 
 Packages:
 - babel-plugin-transform-es2015-modules-commonjs @6.26.2
 - babel-preset-react @6.24.1
 - react @16.2.0
 - react-dom @16.2.0
 - webpack @3.12.0
--- a/devtools/client/debugger/new/dist/debugger.css
+++ b/devtools/client/debugger/new/dist/debugger.css
@@ -1128,22 +1128,27 @@ html .toggle-button.end.vertical svg {
 }
 
 .search-field input.empty {
   color: var(--theme-highlight-orange);
 }
 
 .search-field .summary {
   line-height: 27px;
+  text-align: center;
   padding-right: 10px;
   color: var(--theme-body-color-inactive);
+  align-self: center;
+  padding-top: 1px;
+  white-space: nowrap;
 }
 
 .search-field.big .summary {
-  line-height: 40px;
+  padding: 5px 0 5px 0;
+  line-height: 2rem;
 }
 
 .search-field .search-nav-buttons {
   display: flex;
   user-select: none;
 }
 
 .search-field .search-nav-buttons .nav-btn {
--- a/devtools/client/debugger/new/dist/vendors.js
+++ b/devtools/client/debugger/new/dist/vendors.js
@@ -7938,16 +7938,20 @@ class Tree extends Component {
         depth,
         renderItem: this.props.renderItem,
         focused: focused === item,
         expanded: this.props.isExpanded(item),
         isExpandable: this._nodeIsExpandable(item),
         onExpand: this._onExpand,
         onCollapse: this._onCollapse,
         onClick: e => {
+          // We can stop the propagation since click handler on the node can be
+          // created in `renderItem`.
+          e.stopPropagation();
+
           // Since the user just clicked the node, there's no need to check if
           // it should be scrolled into view.
           this._focus(item, { preventAutoScroll: true });
           if (this.props.isExpanded(item)) {
             this.props.onCollapse(item);
           } else {
             this.props.onExpand(item, e.altKey);
           }
--- a/devtools/client/debugger/new/src/actions/pause/extra.js
+++ b/devtools/client/debugger/new/src/actions/pause/extra.js
@@ -55,17 +55,17 @@ async function getImmutableProps(express
 async function getExtraProps(getState, expression, result, evaluate) {
   const props = {};
   const component = (0, _selectors.inComponent)(getState());
 
   if (component) {
     props.react = await getReactProps(evaluate, component);
   }
 
-  if ((0, _preview.isImmutable)(result)) {
+  if ((0, _preview.isImmutablePreview)(result)) {
     props.immutable = await getImmutableProps(expression, evaluate);
   }
 
   return props;
 }
 
 function fetchExtra() {
   return async function ({
--- a/devtools/client/debugger/new/src/components/Editor/Preview/Popup.js
+++ b/devtools/client/debugger/new/src/components/Editor/Preview/Popup.js
@@ -26,16 +26,18 @@ var _Popover = require("../../shared/Pop
 var _Popover2 = _interopRequireDefault(_Popover);
 
 var _PreviewFunction = require("../../shared/PreviewFunction");
 
 var _PreviewFunction2 = _interopRequireDefault(_PreviewFunction);
 
 var _editor = require("../../../utils/editor/index");
 
+var _preview = require("../../../utils/preview");
+
 var _Svg = require("devtools/client/debugger/new/dist/vendors").vendored["Svg"];
 
 var _Svg2 = _interopRequireDefault(_Svg);
 
 var _firefox = require("../../../client/firefox");
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
@@ -130,32 +132,41 @@ class Popup extends _react.Component {
       name: expression,
       path: expression,
       contents: {
         value: rootValue
       }
     });
   }
 
-  getChildren() {
+  getObjectProperties() {
     const {
       popupObjectProperties
     } = this.props;
     const root = this.getRoot();
     const value = getValue(root);
-    const actor = value ? value.actor : null;
-    const loadedRootProperties = popupObjectProperties[actor];
+
+    if (!value) {
+      return null;
+    }
 
-    if (!loadedRootProperties) {
+    return popupObjectProperties[value.actor];
+  }
+
+  getChildren() {
+    const properties = this.getObjectProperties();
+    const root = this.getRoot();
+
+    if (!properties) {
       return null;
     }
 
     const children = getChildren({
       item: root,
-      loadedProperties: new Map([[root.path, loadedRootProperties]])
+      loadedProperties: new Map([[root.path, properties]])
     });
 
     if (children.length > 0) {
       return children;
     }
 
     return null;
   }
@@ -216,22 +227,22 @@ class Popup extends _react.Component {
     let roots = this.getChildren();
 
     if (!Array.isArray(roots) || roots.length === 0) {
       return null;
     }
 
     let header = null;
 
-    if (extra.immutable) {
+    if ((0, _preview.isImmutable)(this.getObjectProperties())) {
       header = this.renderImmutable(extra.immutable);
       roots = roots.filter(r => r.type != NODE_TYPES.PROTOTYPE);
     }
 
-    if (extra.react) {
+    if ((0, _preview.isReactComponent)(this.getObjectProperties())) {
       header = this.renderReact(extra.react);
       roots = roots.filter(r => ["state", "props"].includes(r.name));
     }
 
     return _react2.default.createElement("div", {
       className: "preview-popup"
     }, header, this.renderObjectInspector(roots));
   }
--- a/devtools/client/debugger/new/src/components/Editor/Tab.js
+++ b/devtools/client/debugger/new/src/components/Editor/Tab.js
@@ -19,16 +19,18 @@ var _SourceIcon2 = _interopRequireDefaul
 var _Button = require("../shared/Button/index");
 
 var _actions = require("../../actions/index");
 
 var _actions2 = _interopRequireDefault(_actions);
 
 var _source = require("../../utils/source");
 
+var _devtoolsModules = require("devtools/client/debugger/new/dist/vendors").vendored["devtools-modules"];
+
 var _clipboard = require("../../utils/clipboard");
 
 var _tabs = require("../../utils/tabs");
 
 var _selectors = require("../../selectors/index");
 
 var _classnames = require("devtools/client/debugger/new/dist/vendors").vendored["classnames"];
 
@@ -168,17 +170,17 @@ class Tab extends _react.PureComponent {
       onMouseUp: handleTabClick,
       onContextMenu: e => this.onTabContextMenu(e, sourceId),
       title: (0, _source.getFileURL)(source)
     }, _react2.default.createElement(_SourceIcon2.default, {
       source: source,
       shouldHide: icon => ["file", "javascript"].includes(icon)
     }), _react2.default.createElement("div", {
       className: "filename"
-    }, filename), _react2.default.createElement(_Button.CloseButton, {
+    }, (0, _devtoolsModules.getUnicodeUrlPath)(filename)), _react2.default.createElement(_Button.CloseButton, {
       handleClick: onClickClose,
       tooltip: L10N.getStr("sourceTabs.closeTabButtonTooltip")
     }));
   }
 
 }
 
 const mapStateToProps = (state, {
--- a/devtools/client/debugger/new/src/components/QuickOpenModal.js
+++ b/devtools/client/debugger/new/src/components/QuickOpenModal.js
@@ -284,20 +284,16 @@ class QuickOpenModal extends _react.Comp
 
       if (e.key === "Enter") {
         if (this.isGotoQuery()) {
           const location = (0, _quickOpen.parseLineColumn)(query);
           return this.gotoLocation(location);
         }
 
         if (results) {
-          if (this.isShortcutQuery()) {
-            return this.setModifier(results[selectedIndex]);
-          }
-
           return this.selectResultItem(e, results[selectedIndex]);
         }
       }
 
       if (e.key === "Tab") {
         return this.closeModal();
       }
 
@@ -355,28 +351,16 @@ class QuickOpenModal extends _react.Comp
       newQuery = query.replace(/[@:#?]/gi, " ");
       return results.map(result => {
         return _objectSpread({}, result, {
           title: this.renderHighlight(result.title, (0, _path.basename)(newQuery), "title")
         });
       });
     };
 
-    this.renderLoading = () => {
-      const {
-        symbolsLoading
-      } = this.props;
-
-      if ((this.isFunctionQuery() || this.isVariableQuery()) && symbolsLoading) {
-        return _react2.default.createElement("div", {
-          className: "loading-indicator"
-        }, L10N.getStr("loadingText"));
-      }
-    };
-
     this.state = {
       results: null,
       selectedIndex: 0
     };
   }
 
   componentDidMount() {
     const {
@@ -411,16 +395,28 @@ class QuickOpenModal extends _react.Comp
 
     if (this.isGotoQuery()) {
       return !/^:\d*$/.test(query);
     }
 
     return !this.getResultCount() && !!query;
   }
 
+  getSummaryMessage() {
+    let summaryMsg = "";
+
+    if (this.isGotoQuery()) {
+      summaryMsg = L10N.getStr("shortcuts.gotoLine");
+    } else if ((this.isFunctionQuery() || this.isVariableQuery()) && this.props.symbolsLoading) {
+      summaryMsg = L10N.getStr("loadingText");
+    }
+
+    return summaryMsg;
+  }
+
   render() {
     const {
       enabled,
       query
     } = this.props;
     const {
       selectedIndex,
       results
@@ -428,36 +424,35 @@ class QuickOpenModal extends _react.Comp
 
     if (!enabled) {
       return null;
     }
 
     const newResults = results && results.slice(0, 100);
     const items = this.highlightMatching(query, newResults || []);
     const expanded = !!items && items.length > 0;
-    const summaryMsg = this.isGotoQuery() ? L10N.getStr("shortcuts.gotoLine") : "";
     return _react2.default.createElement(_Modal2.default, {
       "in": enabled,
       handleClose: this.closeModal
     }, _react2.default.createElement(_SearchInput2.default, _extends({
       query: query,
       hasPrefix: true,
       count: this.getResultCount(),
       placeholder: L10N.getStr("sourceSearch.search"),
-      summaryMsg: summaryMsg,
+      summaryMsg: this.getSummaryMessage(),
       showErrorEmoji: this.shouldShowErrorEmoji(),
       onChange: this.onChange,
       onKeyDown: this.onKeyDown,
       handleClose: this.closeModal,
       expanded: expanded,
       showClose: false,
       selectedItemId: expanded && items[selectedIndex] ? items[selectedIndex].id : ""
     }, this.isSourceSearch() ? {
       size: "big"
-    } : {})), this.renderLoading(), newResults && _react2.default.createElement(_ResultList2.default, _extends({
+    } : {})), newResults && _react2.default.createElement(_ResultList2.default, _extends({
       key: "results",
       items: items,
       selected: selectedIndex,
       selectItem: this.selectResultItem,
       ref: "resultList",
       expanded: expanded
     }, this.isSourceSearch() ? {
       size: "big"
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/WhyPaused.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/WhyPaused.js
@@ -18,17 +18,17 @@ function _interopRequireDefault(obj) { r
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 function renderExceptionSummary(exception) {
   if (typeof exception === "string") {
     return exception;
   }
 
   const preview = exception.preview;
 
-  if (!preview) {
+  if (!preview || !preview.name || !preview.message) {
     return;
   }
 
   return `${preview.name}: ${preview.message}`;
 }
 
 function renderMessage(why) {
   if (why.type == "exception" && why.exception) {
--- a/devtools/client/debugger/new/src/components/shared/SearchInput.js
+++ b/devtools/client/debugger/new/src/components/shared/SearchInput.js
@@ -106,16 +106,30 @@ class SearchInput extends _react.Compone
   renderArrowButtons() {
     const {
       handleNext,
       handlePrev
     } = this.props;
     return [arrowBtn(handleNext, "arrow-down", (0, _classnames2.default)("nav-btn", "next"), L10N.getFormatStr("editor.searchResults.nextResult")), arrowBtn(handlePrev, "arrow-up", (0, _classnames2.default)("nav-btn", "prev"), L10N.getFormatStr("editor.searchResults.prevResult"))];
   }
 
+  renderSummaryMsg() {
+    const {
+      summaryMsg
+    } = this.props;
+
+    if (!summaryMsg) {
+      return null;
+    }
+
+    return _react2.default.createElement("div", {
+      className: "summary"
+    }, summaryMsg);
+  }
+
   renderNav() {
     const {
       count,
       handleNext,
       handlePrev
     } = this.props;
 
     if (!handleNext && !handlePrev || !count || count == 1) {
@@ -134,17 +148,16 @@ class SearchInput extends _react.Compone
       onChange,
       onKeyDown,
       onKeyUp,
       placeholder,
       query,
       selectedItemId,
       showErrorEmoji,
       size,
-      summaryMsg,
       showClose
     } = this.props;
     const inputProps = {
       className: (0, _classnames2.default)({
         empty: showErrorEmoji
       }),
       onChange,
       onKeyDown,
@@ -164,19 +177,17 @@ class SearchInput extends _react.Compone
         focused: this.state.inputFocused
       })
     }, _react2.default.createElement("div", {
       className: (0, _classnames2.default)("search-field", size),
       role: "combobox",
       "aria-haspopup": "listbox",
       "aria-owns": "result-list",
       "aria-expanded": expanded
-    }, this.renderSvg(), _react2.default.createElement("input", inputProps), summaryMsg && _react2.default.createElement("div", {
-      className: "summary"
-    }, summaryMsg), this.renderNav(), showClose && _react2.default.createElement(_Button.CloseButton, {
+    }, this.renderSvg(), _react2.default.createElement("input", inputProps), this.renderSummaryMsg(), this.renderNav(), showClose && _react2.default.createElement(_Button.CloseButton, {
       handleClick: handleClose,
       buttonClass: size
     })));
   }
 
 }
 
 SearchInput.defaultProps = {
--- a/devtools/client/debugger/new/src/utils/preview.js
+++ b/devtools/client/debugger/new/src/utils/preview.js
@@ -1,45 +1,41 @@
 "use strict";
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
+exports.isImmutablePreview = isImmutablePreview;
 exports.isImmutable = isImmutable;
 exports.isReactComponent = isReactComponent;
 exports.isConsole = isConsole;
 
 /* 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/>. */
 const IMMUTABLE_FIELDS = ["_root", "__ownerID", "__altered", "__hash"];
+const REACT_FIELDS = ["_reactInternalInstance", "_reactInternalFiber"];
+
+function isImmutablePreview(result) {
+  return result && isImmutable(result.preview);
+}
 
 function isImmutable(result) {
-  if (!result || !result.preview) {
+  if (!result || typeof result.ownProperties != "object") {
     return;
   }
 
-  const ownProperties = result.preview.ownProperties;
-
-  if (!ownProperties) {
-    return;
-  }
-
+  const ownProperties = result.ownProperties;
   return IMMUTABLE_FIELDS.every(field => Object.keys(ownProperties).includes(field));
 }
 
 function isReactComponent(result) {
-  if (!result || !result.preview) {
+  if (!result || typeof result.ownProperties != "object") {
     return;
   }
 
-  const ownProperties = result.preview.ownProperties;
-
-  if (!ownProperties) {
-    return;
-  }
-
-  return Object.keys(ownProperties).includes("_reactInternalInstance") || Object.keys(ownProperties).includes("_reactInternalFiber");
+  const ownProperties = result.ownProperties;
+  return REACT_FIELDS.some(field => Object.keys(ownProperties).includes(field));
 }
 
 function isConsole(expression) {
   return /^console/.test(expression);
 }
\ No newline at end of file
--- a/devtools/client/debugger/new/src/utils/source.js
+++ b/devtools/client/debugger/new/src/utils/source.js
@@ -35,16 +35,18 @@ exports.getSourceClassnames = getSourceC
 var _devtoolsSourceMap = require("devtools/client/shared/source-map/index.js");
 
 var _utils = require("./utils");
 
 var _path = require("./path");
 
 var _url = require("devtools/client/debugger/new/dist/vendors").vendored["url"];
 
+var _devtoolsModules = require("devtools/client/debugger/new/dist/vendors").vendored["devtools-modules"];
+
 var _sourcesTree = require("./sources-tree/index");
 
 const sourceTypes = exports.sourceTypes = {
   coffee: "coffeescript",
   js: "javascript",
   jsx: "react",
   ts: "typescript"
 };
@@ -146,28 +148,35 @@ function getRawSourceURL(url) {
   return url ? url.replace(/:formatted$/, "") : url;
 }
 
 function resolveFileURL(url, transformUrl = initialUrl => initialUrl) {
   url = getRawSourceURL(url || "");
   const name = transformUrl(url);
   return (0, _utils.endTruncateStr)(name, 50);
 }
+/**
+ * Gets a readable filename from a URL for display purposes.
+ *
+ * @memberof utils/source
+ * @static
+ */
+
 
 function getFilenameFromURL(url) {
-  return resolveFileURL(url, initialUrl => (0, _path.basename)(initialUrl) || "(index)");
+  return resolveFileURL(url, initialUrl => (0, _devtoolsModules.getUnicodeUrlPath)((0, _path.basename)(initialUrl)) || "(index)");
 }
 
 function getFormattedSourceId(id) {
   const sourceId = id.split("/")[1];
   return `SOURCE${sourceId}`;
 }
 /**
- * Show a source url's filename.
- * If the source does not have a url, use the source id.
+ * Gets a readable filename from a source URL for display purposes.
+ * If the source does not have a URL, the source ID will be returned instead.
  *
  * @memberof utils/source
  * @static
  */
 
 
 function getFilename(source) {
   const {
@@ -184,35 +193,35 @@ function getFilename(source) {
 
   if (qMarkIdx > 0) {
     filename = filename.slice(0, qMarkIdx);
   }
 
   return filename;
 }
 /**
- * Show a source url.
- * If the source does not have a url, use the source id.
+ * Gets a readable source URL for display purposes.
+ * If the source does not have a URL, the source ID will be returned instead.
  *
  * @memberof utils/source
  * @static
  */
 
 
 function getFileURL(source) {
   const {
     url,
     id
   } = source;
 
   if (!url) {
     return getFormattedSourceId(id);
   }
 
-  return resolveFileURL(url);
+  return resolveFileURL(url, _devtoolsModules.getUnicodeUrl);
 }
 
 const contentTypeModeMap = {
   "text/javascript": {
     name: "javascript"
   },
   "text/typescript": {
     name: "javascript",
--- a/devtools/client/debugger/new/src/utils/sources-tree/getURL.js
+++ b/devtools/client/debugger/new/src/utils/sources-tree/getURL.js
@@ -5,16 +5,18 @@ Object.defineProperty(exports, "__esModu
 });
 exports.getFilenameFromPath = getFilenameFromPath;
 exports.getURL = getURL;
 
 var _url = require("devtools/client/debugger/new/dist/vendors").vendored["url"];
 
 var _lodash = require("devtools/client/shared/vendor/lodash");
 
+var _devtoolsModules = require("devtools/client/debugger/new/dist/vendors").vendored["devtools-modules"];
+
 /* 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/>. */
 function getFilenameFromPath(pathname) {
   let filename = "";
 
   if (pathname) {
     filename = pathname.substring(pathname.lastIndexOf("/") + 1); // This file does not have a name. Default should be (index).
@@ -106,17 +108,17 @@ function getURL(sourceUrl, debuggeeUrl =
       }
 
       break;
 
     case "http:":
     case "https:":
       return (0, _lodash.merge)(def, {
         path: pathname,
-        group: host,
+        group: (0, _devtoolsModules.getUnicodeHostname)(host),
         filename: filename
       });
   }
 
   return (0, _lodash.merge)(def, {
     path: path,
     group: protocol ? `${protocol}//` : "",
     filename: filename
--- a/devtools/client/inspector/boxmodel/box-model.js
+++ b/devtools/client/inspector/boxmodel/box-model.js
@@ -21,17 +21,16 @@ const NUMERIC = /^-?[\d\.]+$/;
  *
  * @param  {Inspector} inspector
  *         An instance of the Inspector currently loaded in the toolbox.
  * @param  {Window} window
  *         The document window of the toolbox.
  */
 function BoxModel(inspector, window) {
   this.document = window.document;
-  this.highlighters = inspector.highlighters;
   this.inspector = inspector;
   this.store = inspector.store;
 
   this.updateBoxModel = this.updateBoxModel.bind(this);
 
   this.onHideBoxModelHighlighter = this.onHideBoxModelHighlighter.bind(this);
   this.onHideGeometryEditor = this.onHideGeometryEditor.bind(this);
   this.onMarkupViewLeave = this.onMarkupViewLeave.bind(this);
@@ -53,22 +52,31 @@ BoxModel.prototype = {
    * and cleans up references.
    */
   destroy() {
     this.inspector.selection.off("new-node-front", this.onNewSelection);
     this.inspector.sidebar.off("select", this.onSidebarSelect);
 
     this.untrackReflows();
 
+    this._highlighters = null;
     this.document = null;
-    this.highlighters = null;
     this.inspector = null;
     this.walker = null;
   },
 
+  get highlighters() {
+    if (!this._highlighters) {
+      // highlighters is a lazy getter in the inspector.
+      this._highlighters = this.inspector.highlighters;
+    }
+
+    return this._highlighters;
+  },
+
   /**
    * Returns an object containing the box model's handler functions used in the box
    * model's React component props.
    */
   getComponentProps() {
     return {
       onHideBoxModelHighlighter: this.onHideBoxModelHighlighter,
       onShowBoxModelEditor: this.onShowBoxModelEditor,
--- a/devtools/client/inspector/computed/computed.js
+++ b/devtools/client/inspector/computed/computed.js
@@ -145,49 +145,51 @@ UpdateProcess.prototype = {
  * @param {Document} document
  *        The document that will contain the computed view.
  * @param {PageStyleFront} pageStyle
  *        Front for the page style actor that will be providing
  *        the style information.
  */
 function CssComputedView(inspector, document, pageStyle) {
   this.inspector = inspector;
-  this.highlighters = inspector.highlighters;
   this.styleDocument = document;
   this.styleWindow = this.styleDocument.defaultView;
   this.pageStyle = pageStyle;
 
   this.propertyViews = [];
 
   let cssProperties = getCssProperties(inspector.toolbox);
   this._outputParser = new OutputParser(document, cssProperties);
 
   // Create bound methods.
   this.focusWindow = this.focusWindow.bind(this);
+  this._onClearSearch = this._onClearSearch.bind(this);
+  this._onClick = this._onClick.bind(this);
   this._onContextMenu = this._onContextMenu.bind(this);
-  this._onClick = this._onClick.bind(this);
   this._onCopy = this._onCopy.bind(this);
   this._onFilterStyles = this._onFilterStyles.bind(this);
-  this._onClearSearch = this._onClearSearch.bind(this);
   this._onIncludeBrowserStyles = this._onIncludeBrowserStyles.bind(this);
 
   let doc = this.styleDocument;
   this.element = doc.getElementById("computed-property-container");
   this.searchField = doc.getElementById("computed-searchbox");
   this.searchClearButton = doc.getElementById("computed-searchinput-clear");
   this.includeBrowserStylesCheckbox = doc.getElementById("browser-style-checkbox");
 
   this.shortcuts = new KeyShortcuts({ window: this.styleWindow });
   this._onShortcut = this._onShortcut.bind(this);
   this.shortcuts.on("CmdOrCtrl+F", event => this._onShortcut("CmdOrCtrl+F", event));
   this.shortcuts.on("Escape", event => this._onShortcut("Escape", event));
   this.styleDocument.addEventListener("copy", this._onCopy);
   this.styleDocument.addEventListener("mousedown", this.focusWindow);
   this.element.addEventListener("click", this._onClick);
   this.element.addEventListener("contextmenu", this._onContextMenu);
+  this.element.addEventListener("mousemove", () => {
+    this.addHighlightersToView();
+  }, { once: true });
   this.searchField.addEventListener("input", this._onFilterStyles);
   this.searchClearButton.addEventListener("click", this._onClearSearch);
   this.includeBrowserStylesCheckbox.addEventListener("input",
     this._onIncludeBrowserStyles);
 
   this.searchClearButton.hidden = true;
 
   // No results text.
@@ -201,18 +203,16 @@ function CssComputedView(inspector, docu
 
   // The element that we're inspecting, and the document that it comes from.
   this._viewedElement = null;
 
   this.createStyleViews();
 
   // Add the tooltips and highlightersoverlay
   this.tooltips = new TooltipsOverlay(this);
-
-  this.highlighters.addToView(this);
 }
 
 /**
  * Lookup a l10n string in the shared styleinspector string bundle.
  *
  * @param {String} name
  *        The key to lookup.
  * @returns {String} localized version of the given key.
@@ -245,16 +245,26 @@ CssComputedView.prototype = {
   get contextMenu() {
     if (!this._contextMenu) {
       this._contextMenu = new StyleInspectorMenu(this, { isRuleView: false });
     }
 
     return this._contextMenu;
   },
 
+  // Get the highlighters overlay from the Inspector.
+  get highlighters() {
+    if (!this._highlighters) {
+      // highlighters is a lazy getter in the inspector.
+      this._highlighters = this.inspector.highlighters;
+    }
+
+    return this._highlighters;
+  },
+
   setPageStyle: function(pageStyle) {
     this.pageStyle = pageStyle;
   },
 
   get includeBrowserStyles() {
     return this.includeBrowserStylesCheckbox.checked;
   },
 
@@ -718,16 +728,24 @@ CssComputedView.prototype = {
 
       clipboardHelper.copyString(text);
     } catch (e) {
       console.error(e);
     }
   },
 
   /**
+   * Adds the highlighters overlay to the computed view. This is called by the "mousemove"
+   * event handler and in shared-head.js when opening and selecting the computed view.
+   */
+  addHighlightersToView() {
+    this.highlighters.addToView(this);
+  },
+
+  /**
    * Destructor for CssComputedView.
    */
   destroy: function() {
     this._viewedElement = null;
     this._outputParser = null;
 
     this._prefObserver.off("devtools.defaultColorUnit", this._handlePrefChange);
     this._prefObserver.destroy();
@@ -740,43 +758,46 @@ CssComputedView.prototype = {
       this._refreshProcess.cancel();
     }
 
     if (this._contextMenu) {
       this._contextMenu.destroy();
       this._contextMenu = null;
     }
 
+    if (this._highlighters) {
+      this._highlighters.removeFromView(this);
+      this._highlighters = null;
+    }
+
     this.tooltips.destroy();
-    this.highlighters.removeFromView(this);
 
     // Remove bound listeners
-    this.styleDocument.removeEventListener("mousedown", this.focusWindow);
     this.element.removeEventListener("click", this._onClick);
-    this.styleDocument.removeEventListener("copy", this._onCopy);
     this.element.removeEventListener("contextmenu", this._onContextMenu);
     this.searchField.removeEventListener("input", this._onFilterStyles);
     this.searchClearButton.removeEventListener("click", this._onClearSearch);
+    this.styleDocument.removeEventListener("copy", this._onCopy);
+    this.styleDocument.removeEventListener("mousedown", this.focusWindow);
     this.includeBrowserStylesCheckbox.removeEventListener("input",
       this._onIncludeBrowserStyles);
 
     // Nodes used in templating
     this.element = null;
     this.searchField = null;
     this.searchClearButton = null;
     this.includeBrowserStylesCheckbox = null;
 
     // Property views
     for (let propView of this.propertyViews) {
       propView.destroy();
     }
     this.propertyViews = null;
 
     this.inspector = null;
-    this.highlighters = null;
     this.styleDocument = null;
     this.styleWindow = null;
 
     this._isDestroyed = true;
   }
 };
 
 function PropertyInfo(tree, name) {
--- a/devtools/client/inspector/flexbox/flexbox.js
+++ b/devtools/client/inspector/flexbox/flexbox.js
@@ -10,64 +10,79 @@ const {
   clearFlexbox,
   updateFlexbox,
   updateFlexboxHighlighted,
 } = require("./actions/flexbox");
 
 class FlexboxInspector {
   constructor(inspector, window) {
     this.document = window.document;
-    this.highlighters = inspector.highlighters;
     this.inspector = inspector;
     this.store = inspector.store;
     this.walker = inspector.walker;
 
     this.onHighlighterShown = this.onHighlighterShown.bind(this);
     this.onHighlighterHidden = this.onHighlighterHidden.bind(this);
     this.onReflow = throttle(this.onReflow, 500, this);
     this.onSidebarSelect = this.onSidebarSelect.bind(this);
     this.onToggleFlexboxHighlighter = this.onToggleFlexboxHighlighter.bind(this);
     this.onUpdatePanel = this.onUpdatePanel.bind(this);
 
     this.init();
   }
 
+  // Get the highlighters overlay from the Inspector.
+  get highlighters() {
+    if (!this._highlighters) {
+      // highlighters is a lazy getter in the inspector.
+      this._highlighters = this.inspector.highlighters;
+    }
+
+    return this._highlighters;
+  }
+
   async init() {
     if (!this.inspector) {
       return;
     }
     try {
       this.hasGetCurrentFlexbox = await this.inspector.target.actorHasMethod("layout",
         "getCurrentFlexbox");
       this.layoutInspector = await this.walker.getLayoutInspector();
     } catch (e) {
       // These calls might fail if called asynchrously after the toolbox is finished
       // closing.
       return;
     }
 
-    this.highlighters.on("flexbox-highlighter-hidden", this.onHighlighterHidden);
-    this.highlighters.on("flexbox-highlighter-shown", this.onHighlighterShown);
+    this.document.addEventListener("mousemove", () => {
+      this.highlighters.on("flexbox-highlighter-hidden", this.onHighlighterHidden);
+      this.highlighters.on("flexbox-highlighter-shown", this.onHighlighterShown);
+    }, { once: true });
+
     this.inspector.sidebar.on("select", this.onSidebarSelect);
 
     this.onSidebarSelect();
   }
 
   destroy() {
-    this.highlighters.off("flexbox-highlighter-hidden", this.onHighlighterHidden);
-    this.highlighters.off("flexbox-highlighter-shown", this.onHighlighterShown);
+    if (this._highlighters) {
+      this.highlighters.off("flexbox-highlighter-hidden", this.onHighlighterHidden);
+      this.highlighters.off("flexbox-highlighter-shown", this.onHighlighterShown);
+    }
+
     this.inspector.selection.off("new-node-front", this.onUpdatePanel);
     this.inspector.sidebar.off("select", this.onSidebarSelect);
     this.inspector.off("new-root", this.onUpdatePanel);
 
     this.inspector.reflowTracker.untrackReflows(this, this.onReflow);
 
+    this._highlighters = null;
     this.document = null;
     this.hasGetCurrentFlexbox = null;
-    this.highlighters = null;
     this.inspector = null;
     this.layoutInspector = null;
     this.store = null;
     this.walker = null;
   }
 
   getComponentProps() {
     return {
@@ -269,17 +284,20 @@ class FlexboxInspector {
           ["containerEl"]);
       } catch (e) {
         // This call might fail if called asynchrously after the toolbox is finished
         // closing.
         return;
       }
     }
 
+    let highlighted = this._highlighters &&
+      nodeFront == this.highlighters.flexboxHighlighterShown;
+
     this.store.dispatch(updateFlexbox({
       actorID: flexboxFront.actorID,
-      highlighted: nodeFront == this.highlighters.flexboxHighlighterShown,
+      highlighted,
       nodeFront,
     }));
   }
 }
 
 module.exports = FlexboxInspector;
--- a/devtools/client/inspector/grids/grid-inspector.js
+++ b/devtools/client/inspector/grids/grid-inspector.js
@@ -47,17 +47,16 @@ const GRID_COLORS = [
   "#058B00",
   "#A47F00",
   "#005A71"
 ];
 
 class GridInspector {
   constructor(inspector, window) {
     this.document = window.document;
-    this.highlighters = inspector.highlighters;
     this.inspector = inspector;
     this.store = inspector.store;
     this.telemetry = inspector.telemetry;
     this.walker = this.inspector.walker;
 
     this.getSwatchColorPickerTooltip = this.getSwatchColorPickerTooltip.bind(this);
     this.updateGridPanel = this.updateGridPanel.bind(this);
 
@@ -71,16 +70,24 @@ class GridInspector {
     this.onToggleGridHighlighter = this.onToggleGridHighlighter.bind(this);
     this.onToggleShowGridAreas = this.onToggleShowGridAreas.bind(this);
     this.onToggleShowGridLineNumbers = this.onToggleShowGridLineNumbers.bind(this);
     this.onToggleShowInfiniteLines = this.onToggleShowInfiniteLines.bind(this);
 
     this.init();
   }
 
+  get highlighters() {
+    if (!this._highlighters) {
+      this._highlighters = this.inspector.highlighters;
+    }
+
+    return this._highlighters;
+  }
+
   /**
    * Initializes the grid inspector by fetching the LayoutFront from the walker, loading
    * the highlighter settings and initalizing the SwatchColorPicker instance.
    */
   async init() {
     if (!this.inspector) {
       return;
     }
@@ -97,44 +104,50 @@ class GridInspector {
     this.swatchColorPickerTooltip = new SwatchColorPickerTooltip(
       this.inspector.toolbox.doc,
       this.inspector,
       {
         supportsCssColor4ColorFunction: () => false
       }
     );
 
-    this.highlighters.on("grid-highlighter-hidden", this.onHighlighterHidden);
-    this.highlighters.on("grid-highlighter-shown", this.onHighlighterShown);
+    this.document.addEventListener("mousemove", () => {
+      this.highlighters.on("grid-highlighter-hidden", this.onHighlighterHidden);
+      this.highlighters.on("grid-highlighter-shown", this.onHighlighterShown);
+    }, { once: true });
+
     this.inspector.sidebar.on("select", this.onSidebarSelect);
     this.inspector.on("new-root", this.onNavigate);
 
     this.onSidebarSelect();
   }
 
   /**
    * Destruction function called when the inspector is destroyed. Removes event listeners
    * and cleans up references.
    */
   destroy() {
-    this.highlighters.off("grid-highlighter-hidden", this.onHighlighterHidden);
-    this.highlighters.off("grid-highlighter-shown", this.onHighlighterShown);
+    if (this._highlighters) {
+      this.highlighters.off("grid-highlighter-hidden", this.onHighlighterHidden);
+      this.highlighters.off("grid-highlighter-shown", this.onHighlighterShown);
+    }
+
     this.inspector.sidebar.off("select", this.onSidebarSelect);
     this.inspector.off("new-root", this.onNavigate);
 
     this.inspector.reflowTracker.untrackReflows(this, this.onReflow);
 
     // The color picker may not be ready as `init` function is async,
     // and we do not wait for its completion before calling destroy in tests
     if (this.swatchColorPickerTooltip) {
       this.swatchColorPickerTooltip.destroy();
     }
 
+    this._highlighters = null;
     this.document = null;
-    this.highlighters = null;
     this.inspector = null;
     this.layoutInspector = null;
     this.store = null;
     this.swatchColorPickerTooltip = null;
     this.walker = null;
   }
 
   getComponentProps() {
@@ -158,17 +171,18 @@ class GridInspector {
    * @param  {String} customColor
    *         The color fetched from the custom palette, if it exists.
    * @param  {String} fallbackColor
    *         The color to use if no color could be found for the node front.
    * @return {String} color
    *         The color to use.
    */
   getInitialGridColor(nodeFront, customColor, fallbackColor) {
-    let highlighted = nodeFront == this.highlighters.gridHighlighterShown;
+    let highlighted = this._highlighters &&
+      nodeFront == this.highlighters.gridHighlighterShown;
 
     let color;
     if (customColor) {
       color = customColor;
     } else if (highlighted && this.highlighters.state.grid.options) {
       // If the node front is currently highlighted, use the color from the highlighter
       // options.
       color = this.highlighters.state.grid.options.color;
@@ -290,24 +304,26 @@ class GridInspector {
           // closing.
           return;
         }
       }
 
       let colorForHost = customColors[hostname] ? customColors[hostname][i] : undefined;
       let fallbackColor = GRID_COLORS[i % GRID_COLORS.length];
       let color = this.getInitialGridColor(nodeFront, colorForHost, fallbackColor);
+      let highlighted = this._highlighters &&
+        nodeFront == this.highlighters.gridHighlighterShown;
 
       grids.push({
         id: i,
         actorID: grid.actorID,
         color,
         direction: grid.direction,
         gridFragments: grid.gridFragments,
-        highlighted: nodeFront == this.highlighters.gridHighlighterShown,
+        highlighted,
         nodeFront,
         writingMode: grid.writingMode,
       });
     }
 
     this.store.dispatch(updateGrids(grids));
     this.inspector.emit("grid-panel-updated");
   }
@@ -422,17 +438,18 @@ class GridInspector {
 
     // Otherwise, continue comparing with the new grids.
     const newNodeFronts = newGridFronts.filter(grid => grid.containerNodeFront)
                                        .map(grid => grid.containerNodeFront.actorID);
     if (grids.length === newGridFronts.length &&
         oldNodeFronts.sort().join(",") == newNodeFronts.sort().join(",")) {
       // Same list of containers, but let's check if the geometry of the current grid has
       // changed, if it hasn't we can safely abort.
-      if (!this.highlighters.gridHighlighterShown ||
+      if (!this._highlighters ||
+          !this.highlighters.gridHighlighterShown ||
           (this.highlighters.gridHighlighterShown &&
            !this.haveCurrentFragmentsChanged(newGridFronts))) {
         return;
       }
     }
 
     // Either the list of containers or the current fragments have changed, do update.
     this.updateGridPanel(newGridFronts);
--- a/devtools/client/inspector/inspector-search.js
+++ b/devtools/client/inspector/inspector-search.js
@@ -31,21 +31,20 @@ const MAX_SUGGESTIONS = 15;
  * - search-result: when a search is made and a result is selected
  */
 function InspectorSearch(inspector, input, clearBtn) {
   this.inspector = inspector;
   this.searchBox = input;
   this.searchClearButton = clearBtn;
   this._lastSearched = null;
 
-  this.searchClearButton.hidden = true;
-
   this._onKeyDown = this._onKeyDown.bind(this);
   this._onInput = this._onInput.bind(this);
   this._onClearSearch = this._onClearSearch.bind(this);
+
   this.searchBox.addEventListener("keydown", this._onKeyDown, true);
   this.searchBox.addEventListener("input", this._onInput, true);
   this.searchClearButton.addEventListener("click", this._onClearSearch);
 
   // For testing, we need to be able to wait for the most recent node request
   // to finish.  Tests can watch this promise for that.
   this._lastQuery = promise.resolve(null);
 
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -8,32 +8,32 @@
 
 "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 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, "HighlightersOverlay", "devtools/client/inspector/shared/highlighters-overlay");
 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");
 loader.lazyRequireGetter(this, "CommandUtils", "devtools/client/shared/developer-toolbar", true);
 loader.lazyRequireGetter(this, "clipboardHelper", "devtools/shared/platform/clipboard");
 
 const {LocalizationHelper, localizeMarkup} = require("devtools/shared/l10n");
@@ -107,17 +107,16 @@ function Inspector(toolbox) {
   this.telemetry = toolbox.telemetry;
 
   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.reflowTracker = new ReflowTracker(this._target);
   this.styleChangeTracker = new InspectorStyleChangeTracker(this);
 
   // 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);
@@ -184,16 +183,24 @@ Inspector.prototype = {
   get selection() {
     return this.toolbox.selection;
   },
 
   get highlighter() {
     return this.toolbox.highlighter;
   },
 
+  get highlighters() {
+    if (!this._highlighters) {
+      this._highlighters = new HighlightersOverlay(this);
+    }
+
+    return this._highlighters;
+  },
+
   // Added in 53.
   get canGetCssPath() {
     return this._target.client.traits.getCssPath;
   },
 
   // Added in 56.
   get canGetXPath() {
     return this._target.client.traits.getXPath;
@@ -202,16 +209,24 @@ Inspector.prototype = {
   get notificationBox() {
     if (!this._notificationBox) {
       this._notificationBox = this.toolbox.getNotificationBox();
     }
 
     return this._notificationBox;
   },
 
+  get search() {
+    if (!this._search) {
+      this._search = new InspectorSearch(this, this.searchBox, this.searchClearButton);
+    }
+
+    return this._search;
+  },
+
   /**
    * Handle promise rejections for various asynchronous actions, and only log errors if
    * the inspector panel still exists.
    * This is useful to silence useless errors that happen when the inspector is closed
    * while still initializing (and making protocol requests).
    */
   _handleRejectionIfNotDestroyed: function(e) {
     if (!this._panelDestroyer) {
@@ -365,19 +380,20 @@ Inspector.prototype = {
   /**
    * Hooks the searchbar to show result and auto completion suggestions.
    */
   setupSearchBox: function() {
     this.searchBox = this.panelDoc.getElementById("inspector-searchbox");
     this.searchClearButton = this.panelDoc.getElementById("inspector-searchinput-clear");
     this.searchResultsLabel = this.panelDoc.getElementById("inspector-searchlabel");
 
-    this.search = new InspectorSearch(this, this.searchBox, this.searchClearButton);
-    this.search.on("search-cleared", this._clearSearchResultsLabel);
-    this.search.on("search-result", this._updateSearchResultsLabel);
+    this.searchBox.addEventListener("focus", () => {
+      this.search.on("search-cleared", this._clearSearchResultsLabel);
+      this.search.on("search-result", this._updateSearchResultsLabel);
+    }, { once: true });
 
     let shortcuts = new KeyShortcuts({
       window: this.panelDoc.defaultView,
     });
     let key = INSPECTOR_L10N.getStr("inspector.searchHTML.key");
     shortcuts.on(key, event => {
       // Prevent overriding same shortcut from the computed/rule views
       if (event.target.closest("#sidebar-panel-ruleview") ||
@@ -621,31 +637,31 @@ Inspector.prototype = {
     this.sidebarSplitBox.setState({ width: splitSidebarWidth });
   },
 
   async onSidebarToggle() {
     this.is3PaneModeEnabled = !this.is3PaneModeEnabled;
     Services.prefs.setBoolPref(THREE_PANE_ENABLED_PREF, this.is3PaneModeEnabled);
 
     await this.setupToolbar();
-    await this.addRuleView();
+    await this.addRuleView({ skipQueue: true });
   },
 
   /**
    * Adds the rule view to the middle (in landscape/horizontal mode) or bottom-left panel
    * (in portrait/vertical mode) or inspector sidebar depending on whether or not it is 3
    * pane mode. The default tab specifies whether or not the rule view should be selected.
    * The defaultTab defaults to the rule view when reverting to the 2 pane mode and the
    * rule view is being merged back into the inspector sidebar from middle/bottom-left
    * panel. Otherwise, we specify the default tab when handling the sidebar setup.
    *
    * @params {String} defaultTab
    *         Thie id of the default tab for the sidebar.
    */
-  async addRuleView(defaultTab = "ruleview") {
+  async addRuleView({ defaultTab = "ruleview", skipQueue = false } = {}) {
     const ruleViewSidebar = this.sidebarSplitBox.startPanelContainer;
     const toolboxWidth =
       this.panelDoc.getElementById("inspector-splitter-box").clientWidth;
 
     if (this.is3PaneModeEnabled) {
       // Convert to 3 pane mode by removing the rule view from the inspector sidebar
       // and adding the rule view to the middle (in landscape/horizontal mode) or
       // bottom-left (in portrait/vertical mode) panel.
@@ -724,21 +740,29 @@ Inspector.prototype = {
       this.sidebarSplitBox.setState({
         endPanelControl: false,
         splitterSize: 0,
       });
 
       this.ruleViewSideBar.hide();
       await this.ruleViewSideBar.removeTab("ruleview");
 
-      this.sidebar.addExistingTab(
+      if (skipQueue) {
+        this.sidebar.addExistingTab(
         "ruleview",
         INSPECTOR_L10N.getStr("inspector.sidebar.ruleViewTitle"),
         defaultTab == "ruleview",
         0);
+      } else {
+        this.sidebar.queueExistingTab(
+          "ruleview",
+          INSPECTOR_L10N.getStr("inspector.sidebar.ruleViewTitle"),
+          defaultTab == "ruleview",
+          0);
+      }
     }
 
     this.emit("ruleview-added");
   },
 
   /**
    * Lazily get and create panel instances displayed in the sidebar
    */
@@ -798,23 +822,23 @@ Inspector.prototype = {
     let defaultTab = Services.prefs.getCharPref("devtools.inspector.activeSidebar");
 
     if (this.is3PaneModeEnabled && defaultTab === "ruleview") {
       defaultTab = "computedview";
     }
 
     // Append all side panels
 
-    await this.addRuleView(defaultTab);
+    await this.addRuleView({ defaultTab });
 
     // Inject a lazy loaded react tab by exposing a fake React object
     // with a lazy defined Tab thanks to `panel` being a function
     let layoutId = "layoutview";
     let layoutTitle = INSPECTOR_L10N.getStr("inspector.sidebar.layoutViewTitle2");
-    this.sidebar.addTab(
+    this.sidebar.queueTab(
       layoutId,
       layoutTitle,
       {
         props: {
           id: layoutId,
           title: layoutTitle
         },
         panel: () => {
@@ -824,56 +848,56 @@ Inspector.prototype = {
             this.layoutview = new LayoutView(this, this.panelWin);
           }
 
           return this.layoutview.provider;
         }
       },
       defaultTab == layoutId);
 
-    this.sidebar.addExistingTab(
+    this.sidebar.queueExistingTab(
       "computedview",
       INSPECTOR_L10N.getStr("inspector.sidebar.computedViewTitle"),
       defaultTab == "computedview");
 
     const animationTitle =
       INSPECTOR_L10N.getStr("inspector.sidebar.animationInspectorTitle");
 
     if (Services.prefs.getBoolPref("devtools.new-animationinspector.enabled")) {
       const animationId = "newanimationinspector";
 
-      this.sidebar.addTab(
+      this.sidebar.queueTab(
         animationId,
         animationTitle,
         {
           props: {
             id: animationId,
             title: animationTitle
           },
           panel: () => {
             const AnimationInspector =
               this.browserRequire("devtools/client/inspector/animation/animation");
             this.animationinspector = new AnimationInspector(this, this.panelWin);
             return this.animationinspector.provider;
           }
         },
         defaultTab == animationId);
     } else {
-      this.sidebar.addFrameTab(
+      this.sidebar.queueFrameTab(
         "animationinspector",
         animationTitle,
         "chrome://devtools/content/inspector/animation-old/animation-inspector.xhtml",
         defaultTab == "animationinspector");
     }
 
     // Inject a lazy loaded react tab by exposing a fake React object
     // with a lazy defined Tab thanks to `panel` being a function
     let fontId = "fontinspector";
     let fontTitle = INSPECTOR_L10N.getStr("inspector.sidebar.fontInspectorTitle");
-    this.sidebar.addTab(
+    this.sidebar.queueTab(
       fontId,
       fontTitle,
       {
         props: {
           id: fontId,
           title: fontTitle
         },
         panel: () => {
@@ -883,16 +907,18 @@ Inspector.prototype = {
             this.fontinspector = new FontInspector(this, this.panelWin);
           }
 
           return this.fontinspector.provider;
         }
       },
       defaultTab == fontId);
 
+    this.sidebar.addAllQueuedTabs();
+
     // Persist splitter state in preferences.
     this.sidebar.on("show", this.onSidebarShown);
     this.sidebar.on("hide", this.onSidebarHidden);
     this.sidebar.on("destroy", this.onSidebarHidden);
 
     this.sidebar.show(defaultTab);
   },
 
@@ -1085,20 +1111,22 @@ Inspector.prototype = {
   async onMarkupLoaded() {
     if (!this.markup) {
       return;
     }
 
     let onExpand = this.markup.expandNode(this.selection.nodeFront);
 
     // Restore the highlighter states prior to emitting "new-root".
-    await Promise.all([
-      this.highlighters.restoreFlexboxState(),
-      this.highlighters.restoreGridState()
-    ]);
+    if (this._highlighters) {
+      await Promise.all([
+        this.highlighters.restoreFlexboxState(),
+        this.highlighters.restoreGridState()
+      ]);
+    }
 
     this.emit("new-root");
 
     // Wait for full expand of the selected node in order to ensure
     // the markup view is fully emitted before firing 'reloaded'.
     // 'reloaded' is used to know when the panel is fully updated
     // after a page reload.
     await onExpand;
@@ -1309,51 +1337,56 @@ Inspector.prototype = {
     if (this.animationinspector) {
       this.animationinspector.destroy();
     }
 
     if (this.threePaneTooltip) {
       this.threePaneTooltip.destroy();
     }
 
+    if (this._highlighters) {
+      this._highlighters.destroy();
+      this._highlighters = null;
+    }
+
+    if (this._search) {
+      this._search.destroy();
+      this._search = null;
+    }
+
     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.breadcrumbs.destroy();
     this.reflowTracker.destroy();
     this.styleChangeTracker.destroy();
-    this.search.destroy();
 
     this._notificationBox = null;
     this._target = null;
     this._toolbox = null;
     this.breadcrumbs = null;
-    this.highlighters = null;
     this.is3PaneModeEnabled = null;
     this.panelDoc = null;
     this.panelWin.inspector = null;
     this.panelWin = null;
     this.resultsLength = null;
-    this.search = null;
     this.searchBox = null;
     this.show3PaneTooltip = null;
     this.sidebar = null;
     this.store = null;
     this.telemetry = null;
     this.threePaneTooltip = null;
 
     this._panelDestroyer = promise.all([
-      highlighterDestroyer,
       cssPropertiesDestroyer,
       markupDestroyer,
       sidebarDestroyer,
       ruleViewSideBarDestroyer
     ]);
 
     return this._panelDestroyer;
   },
--- a/devtools/client/inspector/inspector.xhtml
+++ b/devtools/client/inspector/inspector.xhtml
@@ -51,17 +51,17 @@
         <button id="inspector-element-add-button" class="devtools-button"
                 data-localization="title=inspectorAddNode.label"></button>
         <div class="devtools-toolbar-spacer"></div>
         <span id="inspector-searchlabel"></span>
         <div id="inspector-search" class="devtools-searchbox has-clear-btn">
           <input id="inspector-searchbox" class="devtools-searchinput"
                  type="search"
                  data-localization="placeholder=inspectorSearchHTML.label3"/>
-          <button id="inspector-searchinput-clear" class="devtools-searchinput-clear" tabindex="-1"></button>
+          <button id="inspector-searchinput-clear" class="devtools-searchinput-clear" hidden="true" tabindex="-1"></button>
         </div>
         <button id="inspector-eyedropper-toggle" class="devtools-button"></button>
       </div>
 
       <!-- Markup Container -->
       <div id="markup-box"></div>
       <div id="inspector-breadcrumbs-toolbar" class="devtools-toolbar">
         <div id="inspector-breadcrumbs" class="breadcrumbs-widget-container"
--- a/devtools/client/inspector/rules/rules.js
+++ b/devtools/client/inspector/rules/rules.js
@@ -99,17 +99,16 @@ const INSET_POINT_TYPES = ["top", "right
  *        set of disabled properties.
  * @param {PageStyleFront} pageStyle
  *        The PageStyleFront for communicating with the remote server.
  */
 function CssRuleView(inspector, document, store, pageStyle) {
   EventEmitter.decorate(this);
 
   this.inspector = inspector;
-  this.highlighters = inspector.highlighters;
   this.styleDocument = document;
   this.styleWindow = this.styleDocument.defaultView;
   this.store = store || {};
   // References to rules marked by various editors where they intend to write changes.
   // @see selectRule(), unselectRule()
   this.selectedRules = new Map();
   this.pageStyle = pageStyle;
 
@@ -147,16 +146,19 @@ function CssRuleView(inspector, document
   this.shortcuts = new KeyShortcuts({ window: this.styleWindow });
   this._onShortcut = this._onShortcut.bind(this);
   this.shortcuts.on("Escape", event => this._onShortcut("Escape", event));
   this.shortcuts.on("Return", event => this._onShortcut("Return", event));
   this.shortcuts.on("Space", event => this._onShortcut("Space", event));
   this.shortcuts.on("CmdOrCtrl+F", event => this._onShortcut("CmdOrCtrl+F", event));
   this.element.addEventListener("copy", this._onCopy);
   this.element.addEventListener("contextmenu", this._onContextMenu);
+  this.element.addEventListener("mousemove", () => {
+    this.addHighlightersToView();
+  }, { once: true });
   this.addRuleButton.addEventListener("click", this._onAddRule);
   this.searchField.addEventListener("input", this._onFilterStyles);
   this.searchClearButton.addEventListener("click", this._onClearSearch);
   this.pseudoClassToggle.addEventListener("click", this._onTogglePseudoClassPanel);
   this.classToggle.addEventListener("click", this._onToggleClassPanel);
   this.hoverCheckbox.addEventListener("click", this._onTogglePseudoClass);
   this.activeCheckbox.addEventListener("click", this._onTogglePseudoClass);
   this.focusCheckbox.addEventListener("click", this._onTogglePseudoClass);
@@ -171,18 +173,16 @@ function CssRuleView(inspector, document
   this._prefObserver.on(PREF_DEFAULT_COLOR_UNIT, this._handleDefaultColorUnitPrefChange);
 
   this.showUserAgentStyles = Services.prefs.getBoolPref(PREF_UA_STYLES);
 
   this._showEmpty();
 
   // Add the tooltips and highlighters to the view
   this.tooltips = new TooltipsOverlay(this);
-
-  this.highlighters.addToView(this);
 }
 
 CssRuleView.prototype = {
   // The element that we're inspecting.
   _viewedElement: null,
 
   // Used for cancelling timeouts in the style filter.
   _filterChangedTimeout: null,
@@ -219,16 +219,26 @@ CssRuleView.prototype = {
     return this._contextMenu;
   },
 
   // Get the dummy elemenet.
   get dummyElement() {
     return this._dummyElement;
   },
 
+  // Get the highlighters overlay from the Inspector.
+  get highlighters() {
+    if (!this._highlighters) {
+      // highlighters is a lazy getter in the inspector.
+      this._highlighters = this.inspector.highlighters;
+    }
+
+    return this._highlighters;
+  },
+
   // Get the filter search value.
   get searchValue() {
     return this.searchField.value.toLowerCase();
   },
 
   get rules() {
     return this._elementStyle ? this._elementStyle.rules : [];
   },
@@ -740,18 +750,23 @@ CssRuleView.prototype = {
       this._classListPreviewer = null;
     }
 
     if (this._contextMenu) {
       this._contextMenu.destroy();
       this._contextMenu = null;
     }
 
+    if (this._highlighters) {
+      this._highlighters.removeFromView(this);
+      this._highlighters = null;
+    }
+
     this.tooltips.destroy();
-    this.highlighters.removeFromView(this);
+
     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);
@@ -768,17 +783,16 @@ CssRuleView.prototype = {
     this.pseudoClassToggle = null;
     this.classPanel = null;
     this.classToggle = null;
     this.hoverCheckbox = null;
     this.activeCheckbox = null;
     this.focusCheckbox = null;
 
     this.inspector = null;
-    this.highlighters = null;
     this.styleDocument = null;
     this.styleWindow = null;
 
     if (this.element.parentNode) {
       this.element.remove();
     }
 
     if (this._elementStyle) {
@@ -1618,18 +1632,25 @@ CssRuleView.prototype = {
     } else if (name === "Escape" &&
                event.target === this.searchField &&
                this._onClearSearch()) {
       // Handle the search box's keypress event. If the escape key is pressed,
       // clear the search box field.
       event.preventDefault();
       event.stopPropagation();
     }
-  }
+  },
 
+  /**
+   * Adds the highlighters overlay to the rule view. This is called by the "mousemove"
+   * event handler and in shared-head.js when opening and selecting the rule view.
+   */
+  addHighlightersToView() {
+    this.highlighters.addToView(this);
+  },
 };
 
 /**
  * Helper functions
  */
 
 /**
  * Walk up the DOM from a given node until a parent property holder is found.
--- a/devtools/client/inspector/rules/views/rule-editor.js
+++ b/devtools/client/inspector/rules/views/rule-editor.js
@@ -49,19 +49,16 @@ const STYLE_INSPECTOR_L10N = new Localiz
  *        The Rule object we're editing.
  */
 function RuleEditor(ruleView, rule) {
   EventEmitter.decorate(this);
 
   this.ruleView = ruleView;
   this.doc = this.ruleView.styleDocument;
   this.toolbox = this.ruleView.inspector.toolbox;
-  // We stash this locally so that we can refer to it from |destroy|
-  // without accidentally reinstantiating the service during shutdown.
-  this.sourceMapURLService = this.toolbox.sourceMapURLService;
   this.rule = rule;
 
   this.isEditable = !rule.isSystem;
   // Flag that blocks updates of the selector and properties when it is
   // being edited
   this.isEditing = false;
 
   this._onNewProperty = this._onNewProperty.bind(this);
@@ -90,21 +87,33 @@ RuleEditor.prototype = {
     if (this.rule.sheet) {
       url = this.rule.sheet.href || this.rule.sheet.nodeHref;
     }
     if (url && !this.rule.isSystem && this.rule.domRule.type !== ELEMENT_STYLE) {
       // Only get the original source link if the rule isn't a system
       // rule and if it isn't an inline rule.
       let sourceLine = this.rule.ruleLine;
       let sourceColumn = this.rule.ruleColumn;
-      this.sourceMapURLService.unsubscribe(url, sourceLine, sourceColumn,
-                                           this._updateLocation);
+
+      if (this._sourceMapURLService) {
+        this._sourceMapURLService.unsubscribe(url, sourceLine, sourceColumn,
+          this._updateLocation);
+      }
     }
   },
 
+  get sourceMapURLService() {
+    if (!this._sourceMapURLService) {
+      // sourceMapURLService is a lazy getter in the toolbox.
+      this._sourceMapURLService = this.toolbox.sourceMapURLService;
+    }
+
+    return this._sourceMapURLService;
+  },
+
   get isSelectorEditable() {
     let trait = this.isEditable &&
       this.ruleView.inspector.target.client.traits.selectorEditable &&
       this.rule.domRule.type !== ELEMENT_STYLE &&
       this.rule.domRule.type !== CSSRule.KEYFRAME_RULE;
 
     // Do not allow editing anonymousselectors until we can
     // detect mutations on  pseudo elements in Bug 1034110.
@@ -169,20 +178,21 @@ RuleEditor.prototype = {
           // This is an inline style from an inherited rule. Need to resolve the unique
           // selector from the node which rule this is inherited from.
           selector = await this.rule.inherited.getUniqueSelector();
         } else {
           // This is an inline style from the current node.
           selector = this.ruleView.inspector.selectionCssSelector;
         }
 
+        let isHighlighted = this.ruleView._highlighters &&
+          this.ruleView.highlighters.selectorHighlighterShown === selector;
         let selectorHighlighter = createChild(header, "span", {
           class: "ruleview-selectorhighlighter" +
-                 (this.ruleView.highlighters.selectorHighlighterShown === selector ?
-                  " highlighted" : ""),
+                 (isHighlighted ? " highlighted" : ""),
           title: l10n("rule.selectorHighlighter.tooltip")
         });
         selectorHighlighter.addEventListener("click", () => {
           this.ruleView.toggleSelectorHighlighter(selectorHighlighter, selector);
         });
 
         this.uniqueSelector = selector;
         this.emit("selector-icon-created");
--- a/devtools/client/inspector/shared/highlighters-overlay.js
+++ b/devtools/client/inspector/shared/highlighters-overlay.js
@@ -967,17 +967,17 @@ class HighlightersOverlay {
 
     this.highlighters = null;
   }
 
   /**
    * Destroy this overlay instance, removing it from the view and destroying
    * all initialized highlighters.
    */
-  async destroy() {
+  destroy() {
     this.destroyHighlighters();
     this.destroyEditors();
 
     // Remove inspector events.
     this.inspector.off("markupmutation", this.onMarkupMutation);
     this.inspector.target.off("will-navigate", this.onWillNavigate);
 
     this._lastHovered = null;
--- a/devtools/client/inspector/test/browser_inspector_pane-toggle-01.js
+++ b/devtools/client/inspector/test/browser_inspector_pane-toggle-01.js
@@ -3,17 +3,17 @@
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests that the inspector panel has a 3 pane toggle button, and that
 // this button is visible both in BOTTOM and SIDE hosts.
 
 add_task(async function() {
-  info("Switch to 2 pane inspector to test that 3 pane toggle button behavior");
+  info("Switch to 2 pane inspector to test the 3 pane toggle button behavior");
   await pushPref("devtools.inspector.three-pane-enabled", false);
 
   info("Open the inspector in a bottom toolbox host");
   const { inspector, toolbox } = await openInspectorForURL("about:blank", "bottom");
 
   const button = inspector.panelDoc.querySelector(".sidebar-toggle");
   ok(button, "The toggle button exists in the DOM");
   ok(button.getAttribute("title"), "The title tooltip has initial state");
--- a/devtools/client/inspector/test/browser_inspector_pane-toggle-02.js
+++ b/devtools/client/inspector/test/browser_inspector_pane-toggle-02.js
@@ -3,17 +3,17 @@
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests that the 3 pane toggle button can toggle on and off the inspector's 3 pane mode,
 // and the 3 panes rendered are all of equal widths in the BOTTOM host.
 
 add_task(async function() {
-  info("Switch to 2 pane inspector to test that 3 pane toggle button behavior");
+  info("Switch to 2 pane inspector to test the 3 pane toggle button behavior");
   await pushPref("devtools.inspector.three-pane-enabled", false);
 
   const { inspector } = await openInspectorForURL("about:blank");
   const { panelDoc: doc } = inspector;
   const button = doc.querySelector(".sidebar-toggle");
   const ruleViewSidebar = inspector.sidebarSplitBox.startPanelContainer;
   const toolboxWidth = doc.getElementById("inspector-splitter-box").clientWidth;
 
--- a/devtools/client/inspector/test/browser_inspector_pane-toggle-03.js
+++ b/devtools/client/inspector/test/browser_inspector_pane-toggle-03.js
@@ -6,17 +6,17 @@
 
 // Tests that the 3 pane inspector toggle can render the middle and right panels of equal
 // sizes when the original sidebar can be doubled in width and be smaller than half the
 // toolbox's width in the BOTTOM host.
 
 const SIDEBAR_WIDTH = 200;
 
 add_task(async function() {
-  info("Switch to 2 pane inspector to test that 3 pane toggle button behavior");
+  info("Switch to 2 pane inspector to test the 3 pane toggle button behavior");
   await pushPref("devtools.inspector.three-pane-enabled", false);
 
   const { inspector } = await openInspectorForURL("about:blank");
   const { panelDoc: doc } = inspector;
   const button = doc.querySelector(".sidebar-toggle");
   const toolboxWidth = doc.getElementById("inspector-splitter-box").clientWidth;
 
   if (toolboxWidth < 600) {
--- a/devtools/client/inspector/test/browser_inspector_pane-toggle-04.js
+++ b/devtools/client/inspector/test/browser_inspector_pane-toggle-04.js
@@ -3,17 +3,17 @@
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests that the 3 pane inspector toggle button can render the bottom-left and
 // bottom-right panels of equal sizes in the SIDE host.
 
 add_task(async function() {
-  info("Switch to 2 pane inspector to test that 3 pane toggle button behavior");
+  info("Switch to 2 pane inspector to test the 3 pane toggle button behavior");
   await pushPref("devtools.inspector.three-pane-enabled", false);
 
   const { inspector, toolbox } = await openInspectorForURL("about:blank");
   const { panelDoc: doc } = inspector;
 
   info("Switch the host to side type");
   await toolbox.switchHost("side");
 
--- a/devtools/client/inspector/test/shared-head.js
+++ b/devtools/client/inspector/test/shared-head.js
@@ -74,43 +74,52 @@ var openInspectorSidebarTab = async func
  * Open the toolbox, with the inspector tool visible, and the rule-view
  * sidebar tab selected.
  *
  * @return a promise that resolves when the inspector is ready and the rule view
  * is visible and ready
  */
 function openRuleView() {
   return openInspector().then(data => {
+    let view = data.inspector.getPanel("ruleview").view;
+
     // Replace the view to use a custom debounce function that can be triggered manually
     // through an additional ".flush()" property.
-    data.inspector.getPanel("ruleview").view.debounce = manualDebounce();
+    view.debounce = manualDebounce();
+
+    // Adds the highlighters overlay in the rule view.
+    view.addHighlightersToView();
 
     return {
       toolbox: data.toolbox,
       inspector: data.inspector,
       testActor: data.testActor,
-      view: data.inspector.getPanel("ruleview").view
+      view,
     };
   });
 }
 
 /**
  * Open the toolbox, with the inspector tool visible, and the computed-view
  * sidebar tab selected.
  *
  * @return a promise that resolves when the inspector is ready and the computed
  * view is visible and ready
  */
 function openComputedView() {
   return openInspectorSidebarTab("computedview").then(data => {
+    let view = data.inspector.getPanel("computedview").computedView;
+    // Adds the highlighters overlay in the computed view.
+    view.addHighlightersToView();
+
     return {
       toolbox: data.toolbox,
       inspector: data.inspector,
       testActor: data.testActor,
-      view: data.inspector.getPanel("computedview").computedView
+      view,
     };
   });
 }
 
 /**
  * Open the toolbox, with the inspector tool visible, and the layout view
  * sidebar tab selected to display the box model view with properties.
  *
@@ -144,29 +153,33 @@ function openLayoutView() {
 /**
  * Select the rule view sidebar tab on an already opened inspector panel.
  *
  * @param {InspectorPanel} inspector
  *        The opened inspector panel
  * @return {CssRuleView} the rule view
  */
 function selectRuleView(inspector) {
-  return inspector.getPanel("ruleview").view;
+  let view = inspector.getPanel("ruleview").view;
+  view.addHighlightersToView();
+  return view;
 }
 
 /**
  * Select the computed view sidebar tab on an already opened inspector panel.
  *
  * @param {InspectorPanel} inspector
  *        The opened inspector panel
  * @return {CssComputedView} the computed view
  */
 function selectComputedView(inspector) {
   inspector.sidebar.select("computedview");
-  return inspector.getPanel("computedview").computedView;
+  let view = inspector.getPanel("computedview").computedView;
+  view.addHighlightersToView();
+  return view;
 }
 
 /**
  * Select the layout view sidebar tab on an already opened inspector panel.
  *
  * @param  {InspectorPanel} inspector
  * @return {BoxModel} the box model
  */
--- a/devtools/client/inspector/toolsidebar.js
+++ b/devtools/client/inspector/toolsidebar.js
@@ -80,16 +80,23 @@ ToolSidebar.prototype = {
       sidebarToggleButton: this._options.sidebarToggleButton,
       onSelect: this.handleSelectionChange.bind(this),
     });
 
     this._tabbar = this.ReactDOM.render(sidebar, this._tabbox);
   },
 
   /**
+   * Adds all the queued tabs.
+   */
+  addAllQueuedTabs: function() {
+    this._tabbar.addAllQueuedTabs();
+  },
+
+  /**
    * Register a side-panel tab.
    *
    * @param {String} tab uniq id
    * @param {String} title tab title
    * @param {React.Component} panel component. See `InspectorPanelTab` as an example.
    * @param {Boolean} selected true if the panel should be selected
    * @param {Number} index the position where the tab should be inserted
    */
@@ -138,16 +145,75 @@ ToolSidebar.prototype = {
       url: url,
       onMount: this.onSidePanelMounted.bind(this),
       onUnmount: this.onSidePanelUnmounted.bind(this),
     });
 
     this.addTab(id, title, panel, selected, index);
   },
 
+  /**
+   * Queues a side-panel tab to be added..
+   *
+   * @param {String} tab uniq id
+   * @param {String} title tab title
+   * @param {React.Component} panel component. See `InspectorPanelTab` as an example.
+   * @param {Boolean} selected true if the panel should be selected
+   * @param {Number} index the position where the tab should be inserted
+   */
+  queueTab: function(id, title, panel, selected, index) {
+    this._tabbar.queueTab(id, title, selected, panel, null, index);
+    this.emit("new-tab-registered", id);
+  },
+
+  /**
+   * Helper API for queuing side-panels that use existing DOM nodes
+   * (defined within inspector.xhtml) as the content.
+   *
+   * @param {String} tab uniq id
+   * @param {String} title tab title
+   * @param {Boolean} selected true if the panel should be selected
+   * @param {Number} index the position where the tab should be inserted
+   */
+  queueExistingTab: function(id, title, selected, index) {
+    let panel = this.InspectorTabPanel({
+      id: id,
+      idPrefix: this.TABPANEL_ID_PREFIX,
+      key: id,
+      title: title,
+    });
+
+    this.queueTab(id, title, panel, selected, index);
+  },
+
+  /**
+   * Helper API for queuing side-panels that use existing <iframe> nodes
+   * (defined within inspector.xhtml) as the content.
+   * The document must have a title, which will be used as the name of the tab.
+   *
+   * @param {String} tab uniq id
+   * @param {String} title tab title
+   * @param {String} url
+   * @param {Boolean} selected true if the panel should be selected
+   * @param {Number} index the position where the tab should be inserted
+   */
+  queueFrameTab: function(id, title, url, selected, index) {
+    let panel = this.InspectorTabPanel({
+      id: id,
+      idPrefix: this.TABPANEL_ID_PREFIX,
+      key: id,
+      title: title,
+      url: url,
+      onMount: this.onSidePanelMounted.bind(this),
+      onUnmount: this.onSidePanelUnmounted.bind(this),
+    });
+
+    this.queueTab(id, title, panel, selected, index);
+  },
+
   onSidePanelMounted: function(content, props) {
     let iframe = content.querySelector("iframe");
     if (!iframe || iframe.getAttribute("src")) {
       return;
     }
 
     let onIFrameLoaded = (event) => {
       iframe.removeEventListener("load", onIFrameLoaded, true);
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -4404,25 +4404,28 @@ class Tree extends Component {
         depth,
         renderItem: this.props.renderItem,
         focused: focused === item,
         expanded: this.props.isExpanded(item),
         isExpandable: this._nodeIsExpandable(item),
         onExpand: this._onExpand,
         onCollapse: this._onCollapse,
         onClick: e => {
+          // We can stop the propagation since click handler on the node can be
+          // created in `renderItem`.
+          e.stopPropagation();
+
           // Since the user just clicked the node, there's no need to check if
           // it should be scrolled into view.
           this._focus(item, { preventAutoScroll: true });
           if (this.props.isExpanded(item)) {
             this.props.onCollapse(item);
           } else {
             this.props.onExpand(item, e.altKey);
           }
-          e.stopPropagation();
         }
       });
     });
 
     const style = Object.assign({}, this.props.style || {}, {
       padding: 0,
       margin: 0
     });
@@ -6441,17 +6444,17 @@ class ObjectInspector extends Component 
 
     const parentElementProps = {
       className: classnames("node object-node", {
         focused,
         lessen: !expanded && (nodeIsDefaultProperties(item) || nodeIsPrototype(item) || dimTopLevelWindow === true && nodeIsWindow(item) && depth === 0),
         block: nodeIsBlock(item)
       }),
       onClick: e => {
-        if (e.metaKey && onCmdCtrlClick) {
+        if (onCmdCtrlClick && (isMacOS && e.metaKey || !isMacOS && e.ctrlKey)) {
           onCmdCtrlClick(item, {
             depth,
             event: e,
             focused,
             expanded
           });
           e.stopPropagation();
           return;
--- a/devtools/client/shared/components/tabs/TabBar.js
+++ b/devtools/client/shared/components/tabs/TabBar.js
@@ -58,18 +58,23 @@ class Tabbar extends Component {
     let tabs = this.createTabs(children);
     let activeTab = tabs.findIndex((tab, index) => tab.id === activeTabId);
 
     this.state = {
       activeTab: activeTab === -1 ? 0 : activeTab,
       tabs,
     };
 
+    // Array of queued tabs to add to the Tabbar.
+    this.queuedTabs = [];
+
     this.createTabs = this.createTabs.bind(this);
     this.addTab = this.addTab.bind(this);
+    this.addAllQueuedTabs = this.addAllQueuedTabs.bind(this);
+    this.queueTab = this.queueTab.bind(this);
     this.toggleTab = this.toggleTab.bind(this);
     this.removeTab = this.removeTab.bind(this);
     this.select = this.select.bind(this);
     this.getTabIndex = this.getTabIndex.bind(this);
     this.getTabId = this.getTabId.bind(this);
     this.getCurrentTabId = this.getCurrentTabId.bind(this);
     this.onTabChanged = this.onTabChanged.bind(this);
     this.onAllTabsMenuClick = this.onAllTabsMenuClick.bind(this);
@@ -109,30 +114,83 @@ class Tabbar extends Component {
 
     if (index >= 0) {
       tabs.splice(index, 0, {id, title, panel, url});
     } else {
       tabs.push({id, title, panel, url});
     }
 
     let newState = Object.assign({}, this.state, {
-      tabs: tabs,
+      tabs,
     });
 
     if (selected) {
       newState.activeTab = index >= 0 ? index : tabs.length - 1;
     }
 
     this.setState(newState, () => {
       if (this.props.onSelect && selected) {
         this.props.onSelect(id);
       }
     });
   }
 
+  addAllQueuedTabs() {
+    if (!this.queuedTabs.length) {
+      return;
+    }
+
+    let tabs = this.state.tabs.slice();
+    let activeId;
+    let activeTab;
+
+    for (let { id, index, panel, selected, title, url } of this.queuedTabs) {
+      if (index >= 0) {
+        tabs.splice(index, 0, {id, title, panel, url});
+      } else {
+        tabs.push({id, title, panel, url});
+      }
+
+      if (selected) {
+        activeId = id;
+        activeTab = index >= 0 ? index : tabs.length - 1;
+      }
+    }
+
+    let newState = Object.assign({}, this.state, {
+      activeTab,
+      tabs,
+    });
+
+    this.setState(newState, () => {
+      if (this.props.onSelect) {
+        this.props.onSelect(activeId);
+      }
+    });
+
+    this.queuedTabs = [];
+  }
+
+  /**
+   * Queues a tab to be added. This is more performant than calling addTab for every
+   * single tab to be added since we will limit the number of renders happening when
+   * a new state is set. Once all the tabs to be added have been queued, call
+   * addAllQueuedTabs() to populate the TabBar with all the queued tabs.
+   */
+  queueTab(id, title, selected = false, panel, url, index = -1) {
+    this.queuedTabs.push({
+      id,
+      index,
+      panel,
+      selected,
+      title,
+      url,
+    });
+  }
+
   toggleTab(tabId, isVisible) {
     let index = this.getTabIndex(tabId);
     if (index < 0) {
       return;
     }
 
     let tabs = this.state.tabs.slice();
     tabs[index] = Object.assign({}, tabs[index], {
--- a/devtools/client/shared/source-map/worker.js
+++ b/devtools/client/shared/source-map/worker.js
@@ -2141,31 +2141,51 @@ async function getAllGeneratedLocations(
 
   return positions.map(({ line, column }) => ({
     sourceId: generatedSourceId,
     line,
     column
   }));
 }
 
-async function getOriginalLocation(location) {
+async function getOriginalLocation(location, { search } = {}) {
   if (!isGeneratedId(location.sourceId)) {
     return location;
   }
 
   const map = await getSourceMap(location.sourceId);
   if (!map) {
     return location;
   }
 
-  const { source: sourceUrl, line, column } = map.originalPositionFor({
+  // First check for an exact match
+  let match = map.originalPositionFor({
     line: location.line,
     column: location.column == null ? 0 : location.column
   });
 
+  // If there is not an exact match, look for a match with a bias at the
+  // current location and then on subsequent lines
+  if (search) {
+    let line = location.line;
+    let column = location.column == null ? 0 : location.column;
+
+    while (match.source === null) {
+      match = map.originalPositionFor({
+        line,
+        column,
+        bias: SourceMapConsumer[search]
+      });
+
+      line += search == "LEAST_UPPER_BOUND" ? 1 : -1;
+      column = search == "LEAST_UPPER_BOUND" ? 0 : Infinity;
+    }
+  }
+
+  const { source: sourceUrl, line, column } = match;
   if (sourceUrl == null) {
     // No url means the location didn't map.
     return location;
   }
 
   return {
     sourceId: generatedToOriginalId(location.sourceId, sourceUrl),
     sourceUrl,
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -445,17 +445,17 @@ nsINode::GetComposedDocInternal() const
              "Should only be caled on nodes in the shadow tree.");
 
   ShadowRoot* containingShadow = AsContent()->GetContainingShadow();
   return containingShadow && containingShadow->IsComposedDocParticipant() ?
     OwnerDoc() : nullptr;
 }
 
 DocumentOrShadowRoot*
-nsINode::GetUncomposedDocOrConnectedShadowRoot()
+nsINode::GetUncomposedDocOrConnectedShadowRoot() const
 {
   if (IsInUncomposedDoc()) {
     return OwnerDoc();
   }
 
   if (IsInComposedDoc() && HasFlag(NODE_IS_IN_SHADOW_TREE)) {
     return AsContent()->GetContainingShadow();
   }
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -687,17 +687,17 @@ public:
   {
     return IsInUncomposedDoc() || (IsInShadowTree() && GetComposedDocInternal());
   }
 
   /**
    * Returns OwnerDoc() if the node is in uncomposed document and ShadowRoot if
    * the node is in Shadow DOM and is in composed document.
    */
-  mozilla::dom::DocumentOrShadowRoot* GetUncomposedDocOrConnectedShadowRoot();
+  mozilla::dom::DocumentOrShadowRoot* GetUncomposedDocOrConnectedShadowRoot() const;
 
   /**
    * The values returned by this function are the ones defined for
    * nsIDOMNode.nodeType
    */
   uint16_t NodeType() const
   {
     return mNodeInfo->NodeType();
--- a/dom/console/tests/test_timer.html
+++ b/dom/console/tests/test_timer.html
@@ -65,33 +65,33 @@ async function runTest() {
   ok(true, "Console.time received!");
 
   // Timer check:
   cl = listener.waitFor(obj => {
     return ("timer" in obj) &&
            ("name" in obj.timer) &&
            obj.timer.name == 'test' &&
            ("duration" in obj.timer) &&
-           obj.timer.duration > 0 &&
+           obj.timer.duration >= 0 &&
            obj.arguments[1] == 1 &&
            obj.arguments[2] == 2 &&
            obj.arguments[3] == 3 &&
            obj.arguments[4] == 4;
   });
   console.timeLog('test', 1, 2, 3, 4);
   await cl;
   ok(true, "Console.timeLog received!");
 
   // Time deleted:
   cl = listener.waitFor(obj => {
     return ("timer" in obj) &&
            ("name" in obj.timer) &&
            obj.timer.name == 'test' &&
            ("duration" in obj.timer) &&
-           obj.timer.duration > 0;
+           obj.timer.duration >= 0;
   });
   console.timeEnd('test');
   await cl;
   ok(true, "Console.timeEnd received!");
 
   // Here an error:
   cl = listener.waitFor(obj => {
     return ("timer" in obj) &&
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -6,16 +6,17 @@
 
 #include "mozilla/dom/HTMLInputElement.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/Date.h"
 #include "mozilla/dom/Directory.h"
+#include "mozilla/dom/DocumentOrShadowRoot.h"
 #include "mozilla/dom/DOMPrefs.h"
 #include "mozilla/dom/HTMLFormSubmission.h"
 #include "mozilla/dom/FileSystemUtils.h"
 #include "mozilla/dom/GetFilesHelper.h"
 #include "mozilla/dom/WheelEventBinding.h"
 #include "nsAttrValueInlines.h"
 #include "nsCRTGlue.h"
 #include "nsQueryObject.h"
@@ -1712,23 +1713,22 @@ nsGenericHTMLElement*
 HTMLInputElement::GetList() const
 {
   nsAutoString dataListId;
   GetAttr(kNameSpaceID_None, nsGkAtoms::list_, dataListId);
   if (dataListId.IsEmpty()) {
     return nullptr;
   }
 
-  //XXXsmaug How should this all work in case input element is in Shadow DOM.
-  nsIDocument* doc = GetUncomposedDoc();
-  if (!doc) {
+  DocumentOrShadowRoot* docOrShadow = GetUncomposedDocOrConnectedShadowRoot();
+  if (!docOrShadow) {
     return nullptr;
   }
 
-  Element* element = doc->GetElementById(dataListId);
+  Element* element = docOrShadow->GetElementById(dataListId);
   if (!element || !element->IsHTMLElement(nsGkAtoms::datalist)) {
     return nullptr;
   }
 
   return static_cast<nsGenericHTMLElement*>(element);
 }
 
 void
--- a/dom/webauthn/AuthenticatorAssertionResponse.cpp
+++ b/dom/webauthn/AuthenticatorAssertionResponse.cpp
@@ -94,20 +94,26 @@ AuthenticatorAssertionResponse::SetSigna
   }
   return NS_OK;
 }
 
 void
 AuthenticatorAssertionResponse::GetUserHandle(JSContext* aCx,
                                               JS::MutableHandle<JSObject*> aRetVal)
 {
-  if (!mUserHandleCachedObj) {
-    mUserHandleCachedObj = mUserHandle.ToArrayBuffer(aCx);
+  // Per https://w3c.github.io/webauthn/#ref-for-dom-authenticatorassertionresponse-userhandle%E2%91%A0
+  // this should return null if the handle is unset.
+  if (mUserHandle.IsEmpty()) {
+    aRetVal.set(nullptr);
+  } else {
+    if (!mUserHandleCachedObj) {
+      mUserHandleCachedObj = mUserHandle.ToArrayBuffer(aCx);
+    }
+    aRetVal.set(mUserHandleCachedObj);
   }
-  aRetVal.set(mUserHandleCachedObj);
 }
 
 nsresult
 AuthenticatorAssertionResponse::SetUserHandle(CryptoBuffer& aBuffer)
 {
   if (NS_WARN_IF(!mUserHandle.Assign(aBuffer))) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
--- a/dom/webauthn/tests/test_webauthn_loopback.html
+++ b/dom/webauthn/tests/test_webauthn_loopback.html
@@ -111,19 +111,17 @@ function() {
 
     ok(aAssertion.rawId.byteLength > 0, "Key ID exists");
     is(aAssertion.id, bytesToBase64UrlSafe(new Uint8Array(aAssertion.rawId)), "Encoded Key ID and Raw Key ID match");
 
     ok(aAssertion.response.authenticatorData === aAssertion.response.authenticatorData, "AuthenticatorAssertionResponse.AuthenticatorData is SameObject");
     ok(aAssertion.response.authenticatorData instanceof ArrayBuffer, "AuthenticatorAssertionResponse.AuthenticatorData is an ArrayBuffer");
     ok(aAssertion.response.signature === aAssertion.response.signature, "AuthenticatorAssertionResponse.Signature is SameObject");
     ok(aAssertion.response.signature instanceof ArrayBuffer, "AuthenticatorAssertionResponse.Signature is an ArrayBuffer");
-    ok(aAssertion.response.userHandle === aAssertion.response.userHandle, "AuthenticatorAssertionResponse.UserHandle is SameObject");
-    ok(aAssertion.response.userHandle instanceof ArrayBuffer, "AuthenticatorAssertionResponse.UserHandle is an ArrayBuffer");
-    is(aAssertion.response.userHandle.byteLength, 0, "AuthenticatorAssertionResponse.UserHandle is emtpy");
+    ok(aAssertion.response.userHandle === null, "AuthenticatorAssertionResponse.UserHandle is null for u2f authenticators");
 
     ok(aAssertion.response.authenticatorData.byteLength > 0, "Authenticator data exists");
     let clientData = JSON.parse(buffer2string(aAssertion.response.clientDataJSON));
     is(clientData.challenge, bytesToBase64UrlSafe(gAssertionChallenge), "Challenge is correct");
     is(clientData.origin, window.location.origin, "Origin is correct");
     is(clientData.hashAlgorithm, "SHA-256", "Hash algorithm is correct");
     is(clientData.type, "webauthn.get", "Type is correct");
 
--- a/dom/webidl/WebAuthentication.webidl
+++ b/dom/webidl/WebAuthentication.webidl
@@ -30,17 +30,17 @@ interface AuthenticatorResponse {
 interface AuthenticatorAttestationResponse : AuthenticatorResponse {
     [SameObject] readonly attribute ArrayBuffer attestationObject;
 };
 
 [SecureContext, Pref="security.webauth.webauthn"]
 interface AuthenticatorAssertionResponse : AuthenticatorResponse {
     [SameObject] readonly attribute ArrayBuffer      authenticatorData;
     [SameObject] readonly attribute ArrayBuffer      signature;
-    [SameObject] readonly attribute ArrayBuffer      userHandle;
+    [SameObject] readonly attribute ArrayBuffer?     userHandle;
 };
 
 dictionary PublicKeyCredentialParameters {
     required PublicKeyCredentialType  type;
     required COSEAlgorithmIdentifier  alg;
 };
 
 dictionary PublicKeyCredentialCreationOptions {
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ClientLayerManager.h"
 #include "GeckoProfiler.h"              // for AUTO_PROFILER_LABEL
+#include "gfxEnv.h"                     // for gfxEnv
 #include "gfxPrefs.h"                   // for gfxPrefs::LayersTile...
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/Hal.h"
 #include "mozilla/dom/ScreenOrientation.h"  // for ScreenOrientation
 #include "mozilla/dom/TabChild.h"       // for TabChild
 #include "mozilla/dom/TabGroup.h"       // for TabGroup
 #include "mozilla/hal_sandbox/PHal.h"   // for ScreenConfiguration
 #include "mozilla/layers/CompositableClient.h"
@@ -219,18 +220,25 @@ ClientLayerManager::CreateReadbackLayer(
 {
   RefPtr<ReadbackLayer> layer = new ClientReadbackLayer(this);
   return layer.forget();
 }
 
 bool
 ClientLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
 {
-  // Wait for any previous async paints to complete before starting to paint again.
-  FlushAsyncPaints();
+#ifdef MOZ_DUMP_PAINTING
+  // When we are dump painting, we expect to be able to read the contents of
+  // compositable clients from previous paints inside this layer transaction
+  // before we flush async paints in EndTransactionInternal.
+  // So to work around this flush async paints now.
+  if (gfxEnv::DumpPaint()) {
+    FlushAsyncPaints();
+  }
+#endif
 
   MOZ_ASSERT(mForwarder, "ClientLayerManager::BeginTransaction without forwarder");
   if (!mForwarder->IPCOpen()) {
     gfxCriticalNote << "ClientLayerManager::BeginTransaction with IPC channel down. GPU process may have died.";
     return false;
   }
 
   mInTransaction = true;
@@ -304,16 +312,21 @@ ClientLayerManager::BeginTransaction()
   return BeginTransactionWithTarget(nullptr);
 }
 
 bool
 ClientLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback,
                                            void* aCallbackData,
                                            EndTransactionFlags)
 {
+  // Wait for any previous async paints to complete before starting to paint again.
+  // Do this outside the profiler and telemetry block so this doesn't count as time
+  // spent rasterizing.
+  FlushAsyncPaints();
+
   PaintTelemetry::AutoRecord record(PaintTelemetry::Metric::Rasterization);
   AUTO_PROFILER_TRACING("Paint", "Rasterize");
 
   Maybe<TimeStamp> startTime;
   if (gfxPrefs::LayersDrawFPS()) {
     startTime = Some(TimeStamp::Now());
   }
 
@@ -406,22 +419,16 @@ ClientLayerManager::EndTransaction(DrawP
                                    void* aCallbackData,
                                    EndTransactionFlags aFlags)
 {
   if (!mForwarder->IPCOpen()) {
     mInTransaction = false;
     return;
   }
 
-  if (mTransactionIncomplete) {
-    // If the previous transaction was incomplete then we may have buffer operations
-    // running on the paint thread that haven't finished yet
-    FlushAsyncPaints();
-  }
-
   if (mWidget) {
     mWidget->PrepareWindowEffects();
   }
   EndTransactionInternal(aCallback, aCallbackData, aFlags);
   ForwardTransaction(!(aFlags & END_NO_REMOTE_COMPOSITE));
 
   if (mRepeatTransaction) {
     mRepeatTransaction = false;
@@ -442,22 +449,16 @@ bool
 ClientLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags)
 {
   mInTransaction = false;
 
   if (!mRoot || !mForwarder->IPCOpen()) {
     return false;
   }
 
-  if (mTransactionIncomplete) {
-    // If the previous transaction was incomplete then we may have buffer operations
-    // running on the paint thread that haven't finished yet
-    FlushAsyncPaints();
-  }
-
   if (!EndTransactionInternal(nullptr, nullptr, aFlags)) {
     // Return without calling ForwardTransaction. This leaves the
     // ShadowLayerForwarder transaction open; the following
     // EndTransaction will complete it.
     if (PaintThread::Get() && mQueuedAsyncPaints) {
       PaintThread::Get()->EndLayerTransaction(nullptr);
     }
     return false;
--- a/gfx/skia/skia/include/core/SkColorPriv.h
+++ b/gfx/skia/skia/include/core/SkColorPriv.h
@@ -50,27 +50,20 @@ static inline U8CPU SkUnitScalarClampToB
  *  For easier compatibility with Skia's GPU backend, we further restrict these
  *  to either (in memory-byte-order) RGBA or BGRA. Note that this "order" does
  *  not directly correspond to the same shift-order, since we have to take endianess
  *  into account.
  *
  *  Here we enforce this constraint.
  */
 
-#ifdef SK_CPU_BENDIAN
-    #define SK_RGBA_R32_SHIFT   24
-    #define SK_RGBA_G32_SHIFT   16
-    #define SK_RGBA_B32_SHIFT   8
-    #define SK_RGBA_A32_SHIFT   0
-#else
-    #define SK_RGBA_R32_SHIFT   0
-    #define SK_RGBA_G32_SHIFT   8
-    #define SK_RGBA_B32_SHIFT   16
-    #define SK_RGBA_A32_SHIFT   24
-#endif
+#define SK_RGBA_R32_SHIFT   0
+#define SK_RGBA_G32_SHIFT   8
+#define SK_RGBA_B32_SHIFT   16
+#define SK_RGBA_A32_SHIFT   24
 
 #define SkGetPackedA32(packed)      ((uint32_t)((packed) << (24 - SK_A32_SHIFT)) >> 24)
 #define SkGetPackedR32(packed)      ((uint32_t)((packed) << (24 - SK_R32_SHIFT)) >> 24)
 #define SkGetPackedG32(packed)      ((uint32_t)((packed) << (24 - SK_G32_SHIFT)) >> 24)
 #define SkGetPackedB32(packed)      ((uint32_t)((packed) << (24 - SK_B32_SHIFT)) >> 24)
 
 #define SkA32Assert(a)  SkASSERT((unsigned)(a) <= SK_A32_MASK)
 #define SkR32Assert(r)  SkASSERT((unsigned)(r) <= SK_R32_MASK)
--- a/gfx/skia/skia/include/core/SkImageInfo.h
+++ b/gfx/skia/skia/include/core/SkImageInfo.h
@@ -79,17 +79,17 @@ enum SkColorType {
 
     kLastEnum_SkColorType = kRGBA_F16_SkColorType,
 
 #if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
     kN32_SkColorType = kBGRA_8888_SkColorType,
 #elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
     kN32_SkColorType = kRGBA_8888_SkColorType,
 #else
-    #error "SK_*32_SHIFT values must correspond to BGRA or RGBA byte order"
+    kN32_SkColorType = kBGRA_8888_SkColorType,
 #endif
 };
 
 /**
  *  Returns the number of bytes-per-pixel for the specified colortype, or 0 if invalid.
  */
 SK_API int SkColorTypeBytesPerPixel(SkColorType ct);
 
--- a/gfx/skia/skia/include/gpu/GrTypes.h
+++ b/gfx/skia/skia/include/gpu/GrTypes.h
@@ -339,25 +339,22 @@ enum GrPixelConfig {
     kPrivateConfig4_GrPixelConfig,
     kPrivateConfig5_GrPixelConfig,
 
     kLast_GrPixelConfig = kPrivateConfig5_GrPixelConfig
 };
 static const int kGrPixelConfigCnt = kLast_GrPixelConfig + 1;
 
 // Aliases for pixel configs that match skia's byte order.
-#ifndef SK_CPU_LENDIAN
-    #error "Skia gpu currently assumes little endian"
-#endif
 #if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
     static const GrPixelConfig kSkia8888_GrPixelConfig = kBGRA_8888_GrPixelConfig;
 #elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
     static const GrPixelConfig kSkia8888_GrPixelConfig = kRGBA_8888_GrPixelConfig;
 #else
-    #error "SK_*32_SHIFT values must correspond to GL_BGRA or GL_RGBA format."
+    static const GrPixelConfig kSkia8888_GrPixelConfig = kBGRA_8888_GrPixelConfig;
 #endif
 
 /**
  * Optional bitfield flags that can be set on GrSurfaceDesc (below).
  */
 enum GrSurfaceFlags {
     kNone_GrSurfaceFlags = 0x0,
     /**
--- a/gfx/skia/skia/src/core/SkColorData.h
+++ b/gfx/skia/skia/src/core/SkColorData.h
@@ -27,27 +27,20 @@
  *  For easier compatibility with Skia's GPU backend, we further restrict these
  *  to either (in memory-byte-order) RGBA or BGRA. Note that this "order" does
  *  not directly correspond to the same shift-order, since we have to take endianess
  *  into account.
  *
  *  Here we enforce this constraint.
  */
 
-#ifdef SK_CPU_BENDIAN
-    #define SK_BGRA_B32_SHIFT   24
-    #define SK_BGRA_G32_SHIFT   16
-    #define SK_BGRA_R32_SHIFT   8
-    #define SK_BGRA_A32_SHIFT   0
-#else
-    #define SK_BGRA_B32_SHIFT   0
-    #define SK_BGRA_G32_SHIFT   8
-    #define SK_BGRA_R32_SHIFT   16
-    #define SK_BGRA_A32_SHIFT   24
-#endif
+#define SK_BGRA_B32_SHIFT   0
+#define SK_BGRA_G32_SHIFT   8
+#define SK_BGRA_R32_SHIFT   16
+#define SK_BGRA_A32_SHIFT   24
 
 #if defined(SK_PMCOLOR_IS_RGBA) && defined(SK_PMCOLOR_IS_BGRA)
     #error "can't define PMCOLOR to be RGBA and BGRA"
 #endif
 
 #define LOCAL_PMCOLOR_SHIFTS_EQUIVALENT_TO_RGBA  \
     (SK_A32_SHIFT == SK_RGBA_A32_SHIFT &&    \
      SK_R32_SHIFT == SK_RGBA_R32_SHIFT &&    \
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -3,16 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/arm/Assembler-arm.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MathAlgorithms.h"
+#include "mozilla/Sprintf.h"
 
 #include "jsutil.h"
 
 #include "gc/Marking.h"
 #include "jit/arm/disasm/Disasm-arm.h"
 #include "jit/arm/MacroAssembler-arm.h"
 #include "jit/ExecutableAllocator.h"
 #include "jit/JitRealm.h"
@@ -3309,17 +3310,17 @@ Assembler::spewBranch(Instruction* i, co
     DisasmBuffer buffer;
     disassembleInstruction(i, buffer);
 
     char labelBuf[128];
     labelBuf[0] = 0;
 
     bool haveTarget = target.valid;
     if (!haveTarget)
-        snprintf(labelBuf, sizeof(labelBuf), "  -> (link-time target)");
+        SprintfLiteral(labelBuf, "  -> (link-time target)");
 
     if (InstBranchImm::IsTHIS(*i)) {
         InstBranchImm* bimm = InstBranchImm::AsTHIS(*i);
         BOffImm destOff;
         bimm->extractImm(&destOff);
         if (destOff.isInvalid() || haveTarget) {
             // The target information in the instruction is likely garbage, so remove it.
             // The target label will in any case be printed if we have it.
@@ -3329,17 +3330,17 @@ Assembler::spewBranch(Instruction* i, co
             // and attach the label if we have it.
             int i;
             for ( i=8 ; i < buffer.length() && buffer[i] == ' ' ; i++ )
                 ;
             for ( ; i < buffer.length() && buffer[i] != ' ' ; i++ )
                 ;
             buffer[i] = 0;
             if (haveTarget) {
-                snprintf(labelBuf, sizeof(labelBuf), "  -> %d%s", target.doc,
+                SprintfLiteral(labelBuf, "  -> %d%s", target.doc,
                          !target.bound ? "f" : "");
                 haveTarget = false;
             }
         }
     }
     spew_.spew("%s%s", buffer.start(), labelBuf);
 
     if (haveTarget)
--- a/js/src/jit/arm64/CodeGenerator-arm64.h
+++ b/js/src/jit/arm64/CodeGenerator-arm64.h
@@ -116,17 +116,17 @@ typedef CodeGeneratorARM64 CodeGenerator
 
 // An out-of-line bailout thunk.
 class OutOfLineBailout : public OutOfLineCodeBase<CodeGeneratorARM64>
 {
   protected: // Silence Clang warning.
     LSnapshot* snapshot_;
 
   public:
-    OutOfLineBailout(LSnapshot* snapshot)
+    explicit OutOfLineBailout(LSnapshot* snapshot)
       : snapshot_(snapshot)
     { }
 
     void accept(CodeGeneratorARM64* codegen) override;
 
     LSnapshot* snapshot() const {
         return snapshot_;
     }
--- a/js/src/jit/arm64/MacroAssembler-arm64.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64.h
@@ -19,28 +19,28 @@ namespace js {
 namespace jit {
 
 // Import VIXL operands directly into the jit namespace for shared code.
 using vixl::Operand;
 using vixl::MemOperand;
 
 struct ImmShiftedTag : public ImmWord
 {
-    ImmShiftedTag(JSValueShiftedTag shtag)
+    explicit ImmShiftedTag(JSValueShiftedTag shtag)
       : ImmWord((uintptr_t)shtag)
     { }
 
-    ImmShiftedTag(JSValueType type)
+    explicit ImmShiftedTag(JSValueType type)
       : ImmWord(uintptr_t(JSValueShiftedTag(JSVAL_TYPE_TO_SHIFTED_TAG(type))))
     { }
 };
 
 struct ImmTag : public Imm32
 {
-    ImmTag(JSValueTag tag)
+    explicit ImmTag(JSValueTag tag)
       : Imm32(tag)
     { }
 };
 
 class ScratchTagScope;
 
 class MacroAssemblerCompat : public vixl::MacroAssembler
 {
--- a/js/src/jit/arm64/MoveEmitter-arm64.h
+++ b/js/src/jit/arm64/MoveEmitter-arm64.h
@@ -55,17 +55,17 @@ class MoveEmitterARM64
     void emitInt32Move(const MoveOperand& from, const MoveOperand& to);
     void emitGeneralMove(const MoveOperand& from, const MoveOperand& to);
 
     void emitMove(const MoveOp& move);
     void breakCycle(const MoveOperand& from, const MoveOperand& to, MoveOp::Type type);
     void completeCycle(const MoveOperand& from, const MoveOperand& to, MoveOp::Type type);
 
   public:
-    MoveEmitterARM64(MacroAssembler& masm)
+    explicit MoveEmitterARM64(MacroAssembler& masm)
       : inCycle_(false),
         masm(masm),
         pushedAtStart_(masm.framePushed()),
         pushedAtCycle_(-1)
     { }
 
     ~MoveEmitterARM64() {
         assertDone();
new file mode 100644
--- /dev/null
+++ b/layout/svg/crashtests/1443092-helper.svg
@@ -0,0 +1,6 @@
+<svg class='class2' xmlns='http://www.w3.org/2000/svg'>
+<clipPath clipPathUnits='userSpaceOnUse' id='id9' lighting-color='pink' class='class0 class1' >
+</clipPath>
+<a id='id0' clip-path='url(#id9)' >
+</a>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/svg/crashtests/1443092.html
@@ -0,0 +1,34 @@
+<script>
+function start() {
+	o7=document.createElement('div');
+	o7.innerHTML='<style>@keyframes key7{ from{ transform: rotate(-536870911deg)}}\n*{ animation-name: key7; animation-duration: 0.001s';
+	o17=document.createElement('div');
+	o17.innerHTML='<svg><style>@font-face{}\n*{ outline-style: dotted</style><style>@font-face{ font-family: font3; src: url(';
+	o18=o17.firstChild.getElementsByTagName('*');
+	o20=o18[0];
+	o23=o18[1];
+	o114=document.createElement('iframe');
+	o114.src='1443092-helper.svg';
+	o114.addEventListener('load', fun0,false);
+	document.body.appendChild(o114);
+}
+function fun0() {
+	o117=o114.contentDocument;
+	document.replaceChild(o117.documentElement,document.documentElement);
+	o124=document.createElement('iframe');
+	document.documentElement.appendChild(o124);
+	o145=document.createElement('div');
+	o145.innerHTML='<svg><set attributeName="text-decoration">';
+	document.documentElement.appendChild(o20);
+	document.documentElement.appendChild(o23);
+	document.documentElement.appendChild(o7);
+	o124.src='x';
+	document.documentElement.appendChild(o145);
+	o264=document.createElement('style');
+	o265=document.createTextNode('*{ float: left');
+	o264.appendChild(o265);
+	o23.appendChild(o264);
+	setTimeout("location.reload()",40);
+}
+</script>
+<body onload="start()"></body>
--- a/layout/svg/crashtests/crashtests.list
+++ b/layout/svg/crashtests/crashtests.list
@@ -199,15 +199,16 @@ load 1209525-1.svg
 load 1223281-1.svg
 load 1322537-1.html
 load 1322537-2.html
 load 1322852.html
 load 1348564.svg
 load 1402109.html
 load 1402124.html
 load 1402486.html
+load 1421807-1.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1421807-2.html
 load 1422226.html
+load 1443092.html
 load conditional-outer-svg-nondirty-reflow-assert.xhtml
 load extref-test-1.xhtml
 load blob-merging-and-retained-display-list.html
 load grouping-empty-bounds.html
-load 1421807-1.html
-pref(dom.webcomponents.shadowdom.enabled,true) load 1421807-2.html
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-c8ee333b84a0
+328d235fc7ee
--- a/security/nss/cmd/tstclnt/tstclnt.c
+++ b/security/nss/cmd/tstclnt/tstclnt.c
@@ -46,16 +46,17 @@
     if (verbose) \
     printf
 #define FPRINTF  \
     if (verbose) \
     fprintf
 
 #define MAX_WAIT_FOR_SERVER 600
 #define WAIT_INTERVAL 100
+#define ZERO_RTT_MAX (2 << 16)
 
 #define EXIT_CODE_HANDSHAKE_FAILED 254
 
 #define EXIT_CODE_SIDECHANNELTEST_GOOD 0
 #define EXIT_CODE_SIDECHANNELTEST_BADCERT 1
 #define EXIT_CODE_SIDECHANNELTEST_NODATA 2
 #define EXIT_CODE_SIDECHANNELTEST_REVOKED 3
 
@@ -94,16 +95,17 @@ int ssl3CipherSuites[] = {
 unsigned long __cmp_umuls;
 PRBool verbose;
 int dumpServerChain = 0;
 int renegotiationsToDo = 0;
 int renegotiationsDone = 0;
 PRBool initializedServerSessionCache = PR_FALSE;
 
 static char *progName;
+static const char *requestFile;
 
 secuPWData pwdata = { PW_NONE, 0 };
 
 SSLNamedGroup *enabledGroups = NULL;
 unsigned int enabledGroupsCount = 0;
 
 void
 printSecurityInfo(PRFileDesc *fd)
@@ -706,22 +708,28 @@ own_GetClientAuthData(void *arg,
     return NSS_GetClientAuthData(arg, socket, caNames, pRetCert, pRetKey);
 }
 
 #if defined(WIN32) || defined(OS2)
 void
 thread_main(void *arg)
 {
     PRFileDesc *ps = (PRFileDesc *)arg;
-    PRFileDesc *std_in = PR_GetSpecialFD(PR_StandardInput);
+    PRFileDesc *std_in;
     int wc, rc;
     char buf[256];
 
+    if (requestFile) {
+        std_in = PR_Open(requestFile, PR_RDONLY, 0);
+    } else {
+        std_in = PR_GetSpecialFD(PR_StandardInput);
+    }
+
 #ifdef WIN32
-    {
+    if (!requestFile) {
         /* Put stdin into O_BINARY mode
         ** or else incoming \r\n's will become \n's.
         */
         int smrv = _setmode(_fileno(stdin), _O_BINARY);
         if (smrv == -1) {
             fprintf(stderr,
                     "%s: Cannot change stdin to binary mode. Use -i option instead.\n",
                     progName);
@@ -732,16 +740,19 @@ thread_main(void *arg)
 
     do {
         rc = PR_Read(std_in, buf, sizeof buf);
         if (rc <= 0)
             break;
         wc = PR_Send(ps, buf, rc, 0, maxInterval);
     } while (wc == rc);
     PR_Close(ps);
+    if (requestFile) {
+        PR_Close(std_in);
+    }
 }
 
 #endif
 
 static void
 printHostNameAndAddr(const char *host, const PRNetAddr *addr)
 {
     PRUint16 port = PR_NetAddrInetPort(addr);
@@ -910,51 +921,59 @@ PRBool pingServerFirst = PR_FALSE;
 int pingTimeoutSeconds = -1;
 PRBool clientSpeaksFirst = PR_FALSE;
 PRBool skipProtoHeader = PR_FALSE;
 ServerCertAuth serverCertAuth;
 char *hs1SniHostName = NULL;
 char *hs2SniHostName = NULL;
 PRUint16 portno = 443;
 int override = 0;
-char *requestString = NULL;
-PRInt32 requestStringLen = 0;
-PRBool requestSent = PR_FALSE;
 PRBool enableZeroRtt = PR_FALSE;
+PRUint8 *zeroRttData;
+unsigned int zeroRttLen = 0;
 PRBool enableAltServerHello = PR_FALSE;
 PRBool useDTLS = PR_FALSE;
 PRBool actAsServer = PR_FALSE;
 PRBool stopAfterHandshake = PR_FALSE;
 PRBool requestToExit = PR_FALSE;
 char *versionString = NULL;
+PRBool handshakeComplete = PR_FALSE;
 
 static int
-writeBytesToServer(PRFileDesc *s, const char *buf, int nb)
+writeBytesToServer(PRFileDesc *s, const PRUint8 *buf, int nb)
 {
     SECStatus rv;
-    const char *bufp = buf;
+    const PRUint8 *bufp = buf;
     PRPollDesc pollDesc;
 
     pollDesc.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
     pollDesc.out_flags = 0;
     pollDesc.fd = s;
 
     FPRINTF(stderr, "%s: Writing %d bytes to server\n",
             progName, nb);
     do {
         PRInt32 cc = PR_Send(s, bufp, nb, 0, maxInterval);
         if (cc < 0) {
             PRErrorCode err = PR_GetError();
             if (err != PR_WOULD_BLOCK_ERROR) {
-                SECU_PrintError(progName,
-                                "write to SSL socket failed");
+                SECU_PrintError(progName, "write to SSL socket failed");
                 return 254;
             }
             cc = 0;
         }
+        FPRINTF(stderr, "%s: %d bytes written\n", progName, cc);
+        if (enableZeroRtt && !handshakeComplete) {
+            if (zeroRttLen + cc > ZERO_RTT_MAX) {
+                SECU_PrintError(progName, "too much early data to save");
+                return -1;
+            }
+            PORT_Memcpy(zeroRttData + zeroRttLen, bufp, cc);
+            zeroRttLen += cc;
+        }
         bufp += cc;
         nb -= cc;
         if (nb <= 0)
             break;
 
         rv = restartHandshakeAfterServerCertIfNeeded(s,
                                                      &serverCertAuth, override);
         if (rv != SECSuccess) {
@@ -964,18 +983,17 @@ writeBytesToServer(PRFileDesc *s, const 
 
         pollDesc.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
         pollDesc.out_flags = 0;
         FPRINTF(stderr,
                 "%s: about to call PR_Poll on writable socket !\n",
                 progName);
         cc = PR_Poll(&pollDesc, 1, PR_INTERVAL_NO_TIMEOUT);
         if (cc < 0) {
-            SECU_PrintError(progName,
-                            "PR_Poll failed");
+            SECU_PrintError(progName, "PR_Poll failed");
             return -1;
         }
         FPRINTF(stderr,
                 "%s: PR_Poll returned with writable socket !\n",
                 progName);
     } while (1);
 
     return 0;
@@ -988,37 +1006,38 @@ handshakeCallback(PRFileDesc *fd, void *
     if (secondHandshakeName) {
         SSL_SetURL(fd, secondHandshakeName);
     }
     printSecurityInfo(fd);
     if (renegotiationsDone < renegotiationsToDo) {
         SSL_ReHandshake(fd, (renegotiationsToDo < 2));
         ++renegotiationsDone;
     }
-    if (requestString && requestSent) {
+    if (zeroRttLen) {
         /* This data was sent in 0-RTT. */
         SSLChannelInfo info;
         SECStatus rv;
 
         rv = SSL_GetChannelInfo(fd, &info, sizeof(info));
         if (rv != SECSuccess)
             return;
 
         if (!info.earlyDataAccepted) {
-            FPRINTF(stderr, "Early data rejected. Re-sending\n");
-            writeBytesToServer(fd, requestString, requestStringLen);
+            FPRINTF(stderr, "Early data rejected. Re-sending %d bytes\n",
+                    zeroRttLen);
+            writeBytesToServer(fd, zeroRttData, zeroRttLen);
+            zeroRttLen = 0;
         }
     }
     if (stopAfterHandshake) {
         requestToExit = PR_TRUE;
     }
+    handshakeComplete = PR_TRUE;
 }
 
-#define REQUEST_WAITING (requestString && !requestSent)
-
 static SECStatus
 installServerCertificate(PRFileDesc *s, char *nick)
 {
     CERTCertificate *cert;
     SECKEYPrivateKey *privKey = NULL;
 
     if (!nick) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
@@ -1131,23 +1150,22 @@ connectToServer(PRFileDesc *s, PRPollDes
 static int
 run()
 {
     int headerSeparatorPtrnId = 0;
     int error = 0;
     SECStatus rv;
     PRStatus status;
     PRInt32 filesReady;
-    int npds;
     PRFileDesc *s = NULL;
     PRFileDesc *std_out;
     PRPollDesc pollset[2];
     PRBool wrStarted = PR_FALSE;
 
-    requestSent = PR_FALSE;
+    handshakeComplete = PR_FALSE;
 
     /* Create socket */
     if (useDTLS) {
         s = PR_OpenUDPSocket(addr.raw.family);
     } else {
         s = PR_OpenTCPSocket(addr.raw.family);
     }
 
@@ -1388,35 +1406,39 @@ run()
         rv = SSL_ResetHandshake(s, PR_TRUE /* server */);
         if (rv != SECSuccess) {
             return 1;
         }
     } else {
         /* Try to connect to the server */
         rv = connectToServer(s, pollset);
         if (rv != SECSuccess) {
-            ;
             error = 1;
             goto done;
         }
     }
 
     pollset[SSOCK_FD].fd = s;
     pollset[SSOCK_FD].in_flags = PR_POLL_EXCEPT;
     if (!actAsServer)
         pollset[SSOCK_FD].in_flags |= (clientSpeaksFirst ? 0 : PR_POLL_READ);
     else
         pollset[SSOCK_FD].in_flags |= PR_POLL_READ;
-    pollset[STDIN_FD].fd = PR_GetSpecialFD(PR_StandardInput);
-    if (!REQUEST_WAITING) {
-        pollset[STDIN_FD].in_flags = PR_POLL_READ;
-        npds = 2;
+    if (requestFile) {
+        pollset[STDIN_FD].fd = PR_Open(requestFile, PR_RDONLY, 0);
+        if (!pollset[STDIN_FD].fd) {
+            fprintf(stderr, "%s: unable to open input file: %s\n",
+                    progName, requestFile);
+            error = 1;
+            goto done;
+        }
     } else {
-        npds = 1;
+        pollset[STDIN_FD].fd = PR_GetSpecialFD(PR_StandardInput);
     }
+    pollset[STDIN_FD].in_flags = PR_POLL_READ;
     std_out = PR_GetSpecialFD(PR_StandardOutput);
 
 #if defined(WIN32) || defined(OS2)
     /* PR_Poll cannot be used with stdin on Windows or OS/2.  (sigh).
     ** But use of PR_Poll and non-blocking sockets is a major feature
     ** of this program.  So, we simulate a pollable stdin with a
     ** TCP socket pair and a  thread that reads stdin and writes to
     ** that socket pair.
@@ -1452,34 +1474,34 @@ run()
 
     /*
     ** Select on stdin and on the socket. Write data from stdin to
     ** socket, read data from socket and write to stdout.
     */
     requestToExit = PR_FALSE;
     FPRINTF(stderr, "%s: ready...\n", progName);
     while (!requestToExit &&
-           ((pollset[SSOCK_FD].in_flags | pollset[STDIN_FD].in_flags) ||
-            REQUEST_WAITING)) {
-        char buf[4000]; /* buffer for stdin */
-        int nb;         /* num bytes read from stdin. */
+           (pollset[SSOCK_FD].in_flags || pollset[STDIN_FD].in_flags)) {
+        PRUint8 buf[4000]; /* buffer for stdin */
+        int nb;            /* num bytes read from stdin. */
 
         rv = restartHandshakeAfterServerCertIfNeeded(s, &serverCertAuth,
                                                      override);
         if (rv != SECSuccess) {
             error = EXIT_CODE_HANDSHAKE_FAILED;
             SECU_PrintError(progName, "authentication of server cert failed");
             goto done;
         }
 
         pollset[SSOCK_FD].out_flags = 0;
         pollset[STDIN_FD].out_flags = 0;
 
         FPRINTF(stderr, "%s: about to call PR_Poll !\n", progName);
-        filesReady = PR_Poll(pollset, npds, PR_INTERVAL_NO_TIMEOUT);
+        filesReady = PR_Poll(pollset, PR_ARRAY_SIZE(pollset),
+                             PR_INTERVAL_NO_TIMEOUT);
         if (filesReady < 0) {
             SECU_PrintError(progName, "select failed");
             error = 1;
             goto done;
         }
         if (filesReady == 0) { /* shouldn't happen! */
             FPRINTF(stderr, "%s: PR_Poll returned zero!\n", progName);
             error = 1;
@@ -1491,125 +1513,87 @@ run()
                     "%s: PR_Poll returned 0x%02x for stdin out_flags.\n",
                     progName, pollset[STDIN_FD].out_flags);
         }
         if (pollset[SSOCK_FD].in_flags) {
             FPRINTF(stderr,
                     "%s: PR_Poll returned 0x%02x for socket out_flags.\n",
                     progName, pollset[SSOCK_FD].out_flags);
         }
-        if (REQUEST_WAITING) {
-            error = writeBytesToServer(s, requestString, requestStringLen);
-            if (error) {
-                goto done;
-            }
-            requestSent = PR_TRUE;
-            pollset[SSOCK_FD].in_flags = PR_POLL_READ;
-        }
         if (pollset[STDIN_FD].out_flags & PR_POLL_READ) {
             /* Read from stdin and write to socket */
             nb = PR_Read(pollset[STDIN_FD].fd, buf, sizeof(buf));
             FPRINTF(stderr, "%s: stdin read %d bytes\n", progName, nb);
             if (nb < 0) {
                 if (PR_GetError() != PR_WOULD_BLOCK_ERROR) {
                     SECU_PrintError(progName, "read from stdin failed");
                     error = 1;
                     break;
                 }
             } else if (nb == 0) {
                 /* EOF on stdin, stop polling stdin for read. */
                 pollset[STDIN_FD].in_flags = 0;
+                if (actAsServer)
+                    requestToExit = PR_TRUE;
             } else {
                 error = writeBytesToServer(s, buf, nb);
                 if (error) {
                     goto done;
                 }
                 pollset[SSOCK_FD].in_flags = PR_POLL_READ;
             }
         }
 
         if (pollset[SSOCK_FD].in_flags) {
             FPRINTF(stderr,
                     "%s: PR_Poll returned 0x%02x for socket out_flags.\n",
                     progName, pollset[SSOCK_FD].out_flags);
         }
-        if ((pollset[SSOCK_FD].out_flags & PR_POLL_READ) ||
-            (pollset[SSOCK_FD].out_flags & PR_POLL_ERR)
 #ifdef PR_POLL_HUP
-            || (pollset[SSOCK_FD].out_flags & PR_POLL_HUP)
+#define POLL_RECV_FLAGS (PR_POLL_READ | PR_POLL_ERR | PR_POLL_HUP)
+#else
+#define POLL_RECV_FLAGS (PR_POLL_READ | PR_POLL_ERR)
 #endif
-                ) {
+        if (pollset[SSOCK_FD].out_flags & POLL_RECV_FLAGS) {
             /* Read from socket and write to stdout */
             nb = PR_Recv(pollset[SSOCK_FD].fd, buf, sizeof buf, 0, maxInterval);
             FPRINTF(stderr, "%s: Read from server %d bytes\n", progName, nb);
             if (nb < 0) {
                 if (PR_GetError() != PR_WOULD_BLOCK_ERROR) {
                     SECU_PrintError(progName, "read from socket failed");
                     error = 1;
                     goto done;
                 }
             } else if (nb == 0) {
                 /* EOF from socket... stop polling socket for read */
                 pollset[SSOCK_FD].in_flags = 0;
             } else {
                 if (skipProtoHeader != PR_TRUE || wrStarted == PR_TRUE) {
                     PR_Write(std_out, buf, nb);
                 } else {
-                    separateReqHeader(std_out, buf, nb, &wrStarted,
+                    separateReqHeader(std_out, (char *)buf, nb, &wrStarted,
                                       &headerSeparatorPtrnId);
                 }
                 if (verbose)
                     fputs("\n\n", stderr);
             }
         }
         milliPause(50 * multiplier);
     }
 
 done:
     if (s) {
         PR_Close(s);
     }
-
+    if (requestFile) {
+        PR_Close(pollset[STDIN_FD].fd);
+    }
     return error;
 }
 
-PRInt32
-ReadFile(const char *filename, char **data)
-{
-    char *ret = NULL;
-    char buf[8192];
-    unsigned int len = 0;
-    PRStatus rv;
-
-    PRFileDesc *fd = PR_Open(filename, PR_RDONLY, 0);
-    if (!fd)
-        return -1;
-
-    for (;;) {
-        rv = PR_Read(fd, buf, sizeof(buf));
-        if (rv < 0) {
-            PR_Free(ret);
-            return rv;
-        }
-
-        if (!rv)
-            break;
-
-        ret = PR_Realloc(ret, len + rv);
-        if (!ret) {
-            return -1;
-        }
-        PORT_Memcpy(ret + len, buf, rv);
-        len += rv;
-    }
-
-    *data = ret;
-    return len;
-}
-
 int
 main(int argc, char **argv)
 {
     PLOptState *optstate;
     PLOptStatus optstatus;
     PRStatus status;
     PRStatus prStatus;
     int error = 0;
@@ -1662,21 +1646,17 @@ main(int argc, char **argv)
                 break;
             case '6':
                 allowIPv4 = PR_FALSE;
                 if (!allowIPv6)
                     Usage();
                 break;
 
             case 'A':
-                requestStringLen = ReadFile(optstate->value, &requestString);
-                if (requestStringLen < 0) {
-                    fprintf(stderr, "Couldn't read file %s\n", optstate->value);
-                    exit(1);
-                }
+                requestFile = PORT_Strdup(optstate->value);
                 break;
 
             case 'C':
                 ++dumpServerChain;
                 break;
 
             case 'D':
                 openDB = PR_FALSE;
@@ -1772,16 +1752,21 @@ main(int argc, char **argv)
                 break;
             case 'Y':
                 PrintCipherUsage();
                 exit(0);
                 break;
 
             case 'Z':
                 enableZeroRtt = PR_TRUE;
+                zeroRttData = PORT_ZAlloc(ZERO_RTT_MAX);
+                if (!zeroRttData) {
+                    fprintf(stderr, "Unable to allocate buffer for 0-RTT\n");
+                    exit(1);
+                }
                 break;
 
             case 'a':
                 if (!hs1SniHostName) {
                     hs1SniHostName = PORT_Strdup(optstate->value);
                 } else if (!hs2SniHostName) {
                     hs2SniHostName = PORT_Strdup(optstate->value);
                 } else {
@@ -2054,30 +2039,23 @@ main(int argc, char **argv)
         }
     }
 
 done:
     if (s) {
         PR_Close(s);
     }
 
-    if (hs1SniHostName) {
-        PORT_Free(hs1SniHostName);
-    }
-    if (hs2SniHostName) {
-        PORT_Free(hs2SniHostName);
-    }
-    if (nickname) {
-        PORT_Free(nickname);
-    }
-    if (pwdata.data) {
-        PORT_Free(pwdata.data);
-    }
+    PORT_Free((void *)requestFile);
+    PORT_Free(hs1SniHostName);
+    PORT_Free(hs2SniHostName);
+    PORT_Free(nickname);
+    PORT_Free(pwdata.data);
     PORT_Free(host);
-    PORT_Free(requestString);
+    PORT_Free(zeroRttData);
 
     if (enabledGroups) {
         PORT_Free(enabledGroups);
     }
     if (NSS_IsInitialized()) {
         SSL_ClearSessionCache();
         if (initializedServerSessionCache) {
             if (SSL_ShutdownServerSessionIDCache() != SECSuccess) {
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,8 +5,9 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
+
--- a/security/nss/lib/ssl/sslsecur.c
+++ b/security/nss/lib/ssl/sslsecur.c
@@ -917,31 +917,40 @@ ssl_SecureSend(sslSocket *ss, const unsi
 
     /* Check to see if we can write even though we're not finished.
      *
      * Case 1: False start
      * Case 2: TLS 1.3 0-RTT
      */
     if (!ss->firstHsDone) {
         PRBool allowEarlySend = PR_FALSE;
+        PRBool firstClientWrite = PR_FALSE;
 
         ssl_Get1stHandshakeLock(ss);
-        if (ss->opt.enableFalseStart ||
-            (ss->opt.enable0RttData && !ss->sec.isServer)) {
+        /* The client can sometimes send before the handshake is fully
+         * complete. In TLS 1.2: false start; in TLS 1.3: 0-RTT. */
+        if (!ss->sec.isServer &&
+            (ss->opt.enableFalseStart || ss->opt.enable0RttData)) {
             ssl_GetSSL3HandshakeLock(ss);
-            /* The client can sometimes send before the handshake is fully
-             * complete. In TLS 1.2: false start; in TLS 1.3: 0-RTT. */
             zeroRtt = ss->ssl3.hs.zeroRttState == ssl_0rtt_sent ||
                       ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted;
             allowEarlySend = ss->ssl3.hs.canFalseStart || zeroRtt;
+            firstClientWrite = ss->ssl3.hs.ws == idle_handshake;
             ssl_ReleaseSSL3HandshakeLock(ss);
         }
         if (!allowEarlySend && ss->handshake) {
             rv = ssl_Do1stHandshake(ss);
         }
+        if (firstClientWrite) {
+            /* Wait until after sending ClientHello and double-check 0-RTT. */
+            ssl_GetSSL3HandshakeLock(ss);
+            zeroRtt = ss->ssl3.hs.zeroRttState == ssl_0rtt_sent ||
+                      ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted;
+            ssl_ReleaseSSL3HandshakeLock(ss);
+        }
         ssl_Release1stHandshakeLock(ss);
     }
 
     if (rv < 0) {
         ss->writerThread = NULL;
         goto done;
     }
 
--- a/security/nss/lib/ssl/sslsock.c
+++ b/security/nss/lib/ssl/sslsock.c
@@ -3034,36 +3034,37 @@ ssl_Poll(PRFileDesc *fd, PRInt16 how_fla
                 ** not based on what the application requested.
                 */
                 new_flags &= ~PR_POLL_RW;
                 if (ss->handshaking == sslHandshakingAsClient) {
                     new_flags |= PR_POLL_WRITE;
                 } else { /* handshaking as server */
                     new_flags |= PR_POLL_READ;
                 }
-            } else
+            } else if (ss->lastWriteBlocked) {
                 /* First handshake is in progress */
-                if (ss->lastWriteBlocked) {
                 if (new_flags & PR_POLL_READ) {
                     /* The caller is waiting for data to be received,
                     ** but the initial handshake is blocked on write, or the
                     ** client's first handshake record has not been written.
                     ** The code should select on write, not read.
                     */
-                    new_flags ^= PR_POLL_READ;  /* don't select on read. */
+                    new_flags &= ~PR_POLL_READ; /* don't select on read. */
                     new_flags |= PR_POLL_WRITE; /* do    select on write. */
                 }
             } else if (new_flags & PR_POLL_WRITE) {
                 /* The caller is trying to write, but the handshake is
                 ** blocked waiting for data to read, and the first
                 ** handshake has been sent.  So do NOT to poll on write
-                ** unless we did false start.
+                ** unless we did false start or we are doing 0-RTT.
                 */
-                if (!ss->ssl3.hs.canFalseStart) {
-                    new_flags ^= PR_POLL_WRITE; /* don't select on write. */
+                if (!(ss->ssl3.hs.canFalseStart ||
+                      ss->ssl3.hs.zeroRttState == ssl_0rtt_sent ||
+                      ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted)) {
+                    new_flags &= ~PR_POLL_WRITE; /* don't select on write. */
                 }
                 new_flags |= PR_POLL_READ; /* do    select on read. */
             }
         }
     } else if ((new_flags & PR_POLL_READ) && (SSL_DataPending(fd) > 0)) {
         *p_out_flags = PR_POLL_READ; /* it's ready already. */
         return new_flags;
     } else if ((ss->lastWriteBlocked) && (how_flags & PR_POLL_READ) &&
@@ -3093,16 +3094,19 @@ ssl_Poll(PRFileDesc *fd, PRInt16 how_fla
              * calling PR_Poll that would return PR_POLL_EXCEPT, and send/recv
              * which won't actually report the I/O error while we are waiting
              * for the asynchronous callback to complete).
              */
             new_flags = 0;
         }
     }
 
+    SSL_TRC(20, ("%d: SSL[%d]: ssl_Poll flags %x -> %x",
+                 SSL_GETPID(), fd, how_flags, new_flags));
+
     if (new_flags && (fd->lower->methods->poll != NULL)) {
         PRInt16 lower_out_flags = 0;
         PRInt16 lower_new_flags;
         lower_new_flags = fd->lower->methods->poll(fd->lower, new_flags,
                                                    &lower_out_flags);
         if ((lower_new_flags & lower_out_flags) && (how_flags != new_flags)) {
             PRInt16 out_flags = lower_out_flags & ~PR_POLL_RW;
             if (lower_out_flags & PR_POLL_READ)
--- a/security/nss/lib/util/secasn1d.c
+++ b/security/nss/lib/util/secasn1d.c
@@ -2982,17 +2982,19 @@ SEC_ASN1DecoderFinish(SEC_ASN1DecoderCon
     } else {
         rv = SECSuccess;
     }
 
     /*
      * XXX anything else that needs to be finished?
      */
 
-    PORT_FreeArena(cx->our_pool, PR_TRUE);
+    if (cx) {
+        PORT_FreeArena(cx->our_pool, PR_TRUE);
+    }
 
     return rv;
 }
 
 SEC_ASN1DecoderContext *
 SEC_ASN1DecoderStart(PLArenaPool *their_pool, void *dest,
                      const SEC_ASN1Template *theTemplate)
 {
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -368883,16 +368883,22 @@
     ]
    ],
    "shadow-dom/historical.html": [
     [
      "/shadow-dom/historical.html",
      {}
     ]
    ],
+   "shadow-dom/input-element-list.html": [
+    [
+     "/shadow-dom/input-element-list.html",
+     {}
+    ]
+   ],
    "shadow-dom/leaktests/get-elements.html": [
     [
      "/shadow-dom/leaktests/get-elements.html",
      {}
     ]
    ],
    "shadow-dom/leaktests/html-collection.html": [
     [
@@ -606888,16 +606894,20 @@
   "shadow-dom/form-control-form-attribute.html": [
    "7726f8fe9056d3d5c9fb7b963c4bc6e777a8256a",
    "testharness"
   ],
   "shadow-dom/historical.html": [
    "1469992db34a25397dc3d5a5e1eb600e8afcf71b",
    "testharness"
   ],
+  "shadow-dom/input-element-list.html": [
+   "79b4a278f0e35646cfdffeebf8f0523e2772bc9b",
+   "testharness"
+  ],
   "shadow-dom/layout-slot-no-longer-assigned.html": [
    "224a688177941774e0bd3be74cb4aef20160d903",
    "reftest"
   ],
   "shadow-dom/layout-slot-no-longer-fallback.html": [
    "a9be2ec9174a8944a237462b51519a8f88b4987a",
    "reftest"
   ],
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/shadow-dom/input-element-list.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Input.list</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id="testcontent">
+  <input id="input" list="datalist">
+</div>
+<script>
+
+test(() => {
+  assert_equals(document.getElementById('input').list, null);
+  var dl = document.createElement("datalist");
+  dl.id = "datalist";
+  document.getElementById("testcontent").appendChild(dl);
+  assert_equals(document.getElementById('input').list, dl);
+}, "Input element's list attribute should point to the datalist element.");
+
+
+test(() => {
+  var host = document.createElement("div");
+  document.getElementById("testcontent").appendChild(host);
+  var sr = host.attachShadow({mode: "open"});
+  var input = document.createElement("input");
+  input.setAttribute("list", "datalist");
+  sr.appendChild(input);
+  assert_equals(input.list, null);
+
+  var dl = document.createElement("datalist");
+  dl.id = "datalist";
+  sr.appendChild(dl);
+  assert_equals(input.list, dl);
+
+  dl.remove();
+  assert_equals(input.list, null);
+}, "Input element's list attribute should point to the datalist element in Shadow DOM.");
+
+
+</script>
--- a/toolkit/components/telemetry/parse_scalars.py
+++ b/toolkit/components/telemetry/parse_scalars.py
@@ -87,16 +87,20 @@ class ScalarType:
         :param definition: the dictionary containing the scalar properties.
         :raises ParserError: if a scalar definition field is of the wrong type.
         :raises ParserError: if a required field is missing or unknown fields are present.
         """
 
         if not self._strict_type_checks:
             return
 
+        def validate_notification_email(notification_email):
+            # Perform simple email validation to make sure it doesn't contain spaces or commas.
+            return not any(c in notification_email for c in [',', ' '])
+
         # The required and optional fields in a scalar type definition.
         REQUIRED_FIELDS = {
             'bug_numbers': list,  # This contains ints. See LIST_FIELDS_CONTENT.
             'description': basestring,
             'expires': basestring,
             'kind': basestring,
             'notification_emails': list,  # This contains strings. See LIST_FIELDS_CONTENT.
             'record_in_processes': list,
@@ -137,16 +141,24 @@ class ScalarType:
         # Checks the type for all the fields.
         wrong_type_names = ['{} must be {}'.format(f, ALL_FIELDS[f].__name__)
                             for f in definition.keys()
                             if not isinstance(definition[f], ALL_FIELDS[f])]
         if len(wrong_type_names) > 0:
             ParserError(self._name + ' - ' + ', '.join(wrong_type_names) +
                         '.\nSee: {}#required-fields'.format(BASE_DOC_URL)).handle_later()
 
+        # Check that the email addresses doesn't contain spaces or commas
+        notification_emails = definition.get('notification_emails')
+        for notification_email in notification_emails:
+            print validate_notification_email(notification_email)
+            if not validate_notification_email(notification_email):
+                ParserError(self._name + ' - invalid email address: ' + notification_email +
+                            '.\nSee: {}'.format(BASE_DOC_URL)).handle_later()
+
         # Check that the lists are not empty and that data in the lists
         # have the correct types.
         list_fields = [f for f in definition if isinstance(definition[f], list)]
         for field in list_fields:
             # Check for empty lists.
             if len(definition[field]) == 0:
                 ParserError(("Field '{}' for probe '{}' must not be empty" +
                              ".\nSee: {}#required-fields)")
--- a/toolkit/components/telemetry/tests/python/python.ini
+++ b/toolkit/components/telemetry/tests/python/python.ini
@@ -1,4 +1,5 @@
 [test_gen_event_data_json.py]
 [test_gen_scalar_data_json.py]
 [test_histogramtools_non_strict.py]
 [test_histogramtools_strict.py]
+[test_parse_scalars.py]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/telemetry/tests/python/test_parse_scalars.py
@@ -0,0 +1,69 @@
+# This Source Code Form is subject to the terms of 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 yaml
+import mozunit
+import sys
+import unittest
+from os import path
+
+TELEMETRY_ROOT_PATH = path.abspath(path.join(path.dirname(__file__), path.pardir, path.pardir))
+sys.path.append(TELEMETRY_ROOT_PATH)
+from shared_telemetry_utils import ParserError
+import parse_scalars
+
+
+def load_scalar(scalar):
+    """Parse the passed Scalar and return a dictionary
+
+    :param scalar: Scalar as YAML string
+    :returns: Parsed Scalar dictionary
+    """
+    return yaml.load(scalar)
+
+
+class TestParser(unittest.TestCase):
+    def test_valid_email_address(self):
+        SAMPLE_SCALAR_VALID_ADDRESSES = """
+description: A nice one-line description.
+expires: never
+record_in_processes:
+  - 'main'
+kind: uint
+notification_emails:
+  - test01@mozilla.com
+  - test02@mozilla.com
+bug_numbers:
+  - 12345
+"""
+        scalar = load_scalar(SAMPLE_SCALAR_VALID_ADDRESSES)
+        sclr = parse_scalars.ScalarType("CATEGORY",
+                                        "PROVE",
+                                        scalar,
+                                        strict_type_checks=True)
+
+        self.assertEqual(sclr.notification_emails, ["test01@mozilla.com", "test02@mozilla.com"])
+
+    def test_invalid_email_address(self):
+        SAMPLE_SCALAR_INVALID_ADDRESSES = """
+description: A nice one-line description.
+expires: never
+  - 'main'
+kind: uint
+notification_emails:
+  - test01@mozilla.com, test02@mozilla.com
+bug_numbers:
+  - 12345
+"""
+        scalar = load_scalar(SAMPLE_SCALAR_INVALID_ADDRESSES)
+        parse_scalars.ScalarType("CATEGORY",
+                                 "PROVE",
+                                 scalar,
+                                 strict_type_checks=True)
+
+        self.assertRaises(SystemExit, ParserError.exit_func)
+
+
+if __name__ == '__main__':
+    mozunit.main()
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -192,17 +192,17 @@ NS_INTERFACE_MAP_BEGIN(nsThread)
     foundInterface = static_cast<nsIClassInfo*>(&sThreadClassInfo);
   } else
 NS_INTERFACE_MAP_END
 NS_IMPL_CI_INTERFACE_GETTER(nsThread, nsIThread, nsIThreadInternal,
                             nsIEventTarget, nsISupportsPriority)
 
 //-----------------------------------------------------------------------------
 
-class nsThreadStartupEvent : public Runnable
+class nsThreadStartupEvent final : public Runnable
 {
 public:
   nsThreadStartupEvent()
     : Runnable("nsThreadStartupEvent")
     , mMon("nsThreadStartupEvent.mMon")
     , mInitialized(false)
   {
   }
@@ -212,21 +212,19 @@ public:
   void Wait()
   {
     ReentrantMonitorAutoEnter mon(mMon);
     while (!mInitialized) {
       mon.Wait();
     }
   }
 
-  // This method needs to be public to support older compilers (xlC_r on AIX).
-  // It should be called directly as this class type is reference counted.
-  virtual ~nsThreadStartupEvent() {}
+private:
+  ~nsThreadStartupEvent() = default;
 
-private:
   NS_IMETHOD Run() override
   {
     ReentrantMonitorAutoEnter mon(mMon);
     mInitialized = true;
     mon.Notify();
     return NS_OK;
   }