Bug 1436670 - Devtools Reps: update bundle to v0.20.0; r=nchevobbe.
authorNicolas Chevobbe <nchevobbe@mozilla.com>
Mon, 19 Feb 2018 11:05:06 +0100
changeset 404409 5b8dd2380f3eda126be82974414577a53ca64a27
parent 404408 43390eee81420fcd1c462b3e775e8926ae407ca8
child 404410 445276274143b5e2d4dcf8f0afc13df28530e378
push id100000
push userncsoregi@mozilla.com
push dateMon, 19 Feb 2018 16:46:49 +0000
treeherdermozilla-inbound@176fb0d46b64 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnchevobbe
bugs1436670
milestone60.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1436670 - Devtools Reps: update bundle to v0.20.0; r=nchevobbe. MozReview-Commit-ID: 75lSnYTx0Ql
devtools/client/shared/components/reps/reps.css
devtools/client/shared/components/reps/reps.js
--- a/devtools/client/shared/components/reps/reps.css
+++ b/devtools/client/shared/components/reps/reps.css
@@ -61,16 +61,22 @@
 .objectBox-textNode,
 .objectBox-string,
 .objectBox-symbol {
   color: var(--string-color);
 }
 
 .objectBox-string a, .objectBox-string a:visited {
   color: currentColor;
+  text-decoration: none;
+  font-style: italic;
+}
+
+.objectBox-string a:hover {
+  text-decoration: underline;
 }
 
 .objectBox-function,
 .objectBox-stackTrace,
 .objectBox-profile {
   color: var(--object-color);
 }
 
@@ -144,26 +150,33 @@
   color: var(--node-color);
 }
 
 .angleBracket {
   color: var(--theme-body-color);
 }
 
 /******************************************************************************/
+/* Length bubble for arraylikes and maplikes */
+
+.objectLengthBubble {
+  color: var(--null-color);
+}
+
+/******************************************************************************/
 
 .objectLeftBrace,
 .objectRightBrace,
 .arrayLeftBracket,
 .arrayRightBracket {
   color: var(--object-color);
 }
 
 /******************************************************************************/
-/* Cycle reference*/
+/* Cycle reference */
 
 .objectBox-Reference {
   font-weight: bold;
   color: var(--reference-color);
 }
 
 [class*="objectBox"] > .objectTitle {
   color: var(--object-color);
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -2,17 +2,17 @@
 	if(typeof exports === 'object' && typeof module === 'object')
 		module.exports = factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react"));
 	else if(typeof define === 'function' && define.amd)
 		define(["devtools/client/shared/vendor/react-dom-factories", "devtools/client/shared/vendor/lodash", "devtools/client/shared/vendor/react-prop-types", "devtools/client/shared/vendor/react"], factory);
 	else {
 		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react")) : factory(root["devtools/client/shared/vendor/react-dom-factories"], root["devtools/client/shared/vendor/lodash"], root["devtools/client/shared/vendor/react-prop-types"], root["devtools/client/shared/vendor/react"]);
 		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
 	}
-})(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_56__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_7__) {
+})(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_56__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_8__) {
 return /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
 /******/
 /******/ 	// The require function
 /******/ 	function __webpack_require__(moduleId) {
 /******/
 /******/ 		// Check if module is in cache
@@ -65,17 +65,17 @@ return /******/ (function(modules) { // 
 /******/
 /******/ 	// Object.prototype.hasOwnProperty.call
 /******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
 /******/
 /******/ 	// __webpack_public_path__
 /******/ 	__webpack_require__.p = "/assets/build";
 /******/
 /******/ 	// Load entry module and return exports
-/******/ 	return __webpack_require__(__webpack_require__.s = 19);
+/******/ 	return __webpack_require__(__webpack_require__.s = 20);
 /******/ })
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
@@ -455,16 +455,22 @@ function isURL(token) {
     }
     new URL(token);
     return true;
   } catch (e) {
     return false;
   }
 }
 
+const ellipsisElement = span({
+  key: "more",
+  className: "more-ellipsis",
+  title: `more${ELLIPSIS}`
+}, ELLIPSIS);
+
 module.exports = {
   isGrip,
   isURL,
   cropString,
   containsURL,
   rawCropString,
   sanitizeString,
   escapeString,
@@ -473,16 +479,17 @@ module.exports = {
   parseURLParams,
   parseURLEncodedText,
   getFileName,
   getURLDisplayString,
   maybeEscapePropertyName,
   getGripPreviewItems,
   getGripType,
   tokenSplitRegex,
+  ellipsisElement,
   ELLIPSIS
 };
 
 /***/ }),
 /* 1 */
 /***/ (function(module, exports) {
 
 module.exports = __WEBPACK_EXTERNAL_MODULE_1__;
@@ -518,25 +525,24 @@ module.exports = {
 
 "use strict";
 
 
 /* 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/. */
 
-__webpack_require__(20);
+__webpack_require__(21);
 
 // Load all existing rep templates
-const Undefined = __webpack_require__(21);
-const Null = __webpack_require__(22);
-const StringRep = __webpack_require__(6);
-const LongStringRep = __webpack_require__(23);
+const Undefined = __webpack_require__(22);
+const Null = __webpack_require__(23);
+const StringRep = __webpack_require__(7);
 const Number = __webpack_require__(24);
-const ArrayRep = __webpack_require__(10);
+const ArrayRep = __webpack_require__(5);
 const Obj = __webpack_require__(25);
 const SymbolRep = __webpack_require__(26);
 const InfinityRep = __webpack_require__(27);
 const NaNRep = __webpack_require__(28);
 const Accessor = __webpack_require__(29);
 
 // DOM types (grips)
 const Attribute = __webpack_require__(30);
@@ -550,24 +556,24 @@ const StyleSheet = __webpack_require__(4
 const CommentNode = __webpack_require__(41);
 const ElementNode = __webpack_require__(42);
 const TextNode = __webpack_require__(43);
 const ErrorRep = __webpack_require__(44);
 const Window = __webpack_require__(45);
 const ObjectWithText = __webpack_require__(46);
 const ObjectWithURL = __webpack_require__(47);
 const GripArray = __webpack_require__(13);
-const GripMap = __webpack_require__(14);
-const GripMapEntry = __webpack_require__(15);
-const Grip = __webpack_require__(8);
+const GripMap = __webpack_require__(15);
+const GripMapEntry = __webpack_require__(16);
+const Grip = __webpack_require__(9);
 
 // List of all registered template.
 // XXX there should be a way for extensions to register a new
 // or modify an existing rep.
-let reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, ElementNode, TextNode, Attribute, LongStringRep, Func, PromiseRep, ArrayRep, Document, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, GripMapEntry, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep, Accessor];
+let reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, ElementNode, TextNode, Attribute, Func, PromiseRep, ArrayRep, Document, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, GripMapEntry, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep, Accessor];
 
 /**
  * Generic rep that is using for rendering native JS types or an object.
  * The right template used for rendering is picked automatically according
  * to the current value type. The value must be passed is as 'object'
  * property.
  */
 const Rep = function (props) {
@@ -625,17 +631,16 @@ module.exports = {
     ErrorRep,
     Event,
     Func,
     Grip,
     GripArray,
     GripMap,
     GripMapEntry,
     InfinityRep,
-    LongStringRep,
     NaNRep,
     Null,
     Number,
     Obj,
     ObjectWithText,
     ObjectWithURL,
     PromiseRep,
     RegExp,
@@ -658,16 +663,163 @@ module.exports = {
 "use strict";
 
 
 /* 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/. */
 
 // Dependencies
+const dom = __webpack_require__(1);
+const PropTypes = __webpack_require__(2);
+const {
+  wrapRender
+} = __webpack_require__(0);
+const { MODE } = __webpack_require__(3);
+const { span } = dom;
+
+const ModePropType = PropTypes.oneOf(
+// @TODO Change this to Object.values once it's supported in Node's version of V8
+Object.keys(MODE).map(key => MODE[key]));
+
+/**
+ * Renders an array. The array is enclosed by left and right bracket
+ * and the max number of rendered items depends on the current mode.
+ */
+ArrayRep.propTypes = {
+  mode: ModePropType,
+  object: PropTypes.array.isRequired
+};
+
+function ArrayRep(props) {
+  let {
+    object,
+    mode = MODE.SHORT
+  } = props;
+
+  let items;
+  let brackets;
+  let needSpace = function (space) {
+    return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
+  };
+
+  if (mode === MODE.TINY) {
+    let isEmpty = object.length === 0;
+    if (isEmpty) {
+      items = [];
+    } else {
+      items = [span({
+        className: "more-ellipsis",
+        title: "more…"
+      }, "…")];
+    }
+    brackets = needSpace(false);
+  } else {
+    items = arrayIterator(props, object, maxLengthMap.get(mode));
+    brackets = needSpace(items.length > 0);
+  }
+
+  return span({
+    className: "objectBox objectBox-array" }, span({
+    className: "arrayLeftBracket"
+  }, brackets.left), ...items, span({
+    className: "arrayRightBracket"
+  }, brackets.right), span({
+    className: "arrayProperties",
+    role: "group" }));
+}
+
+function arrayIterator(props, array, max) {
+  let items = [];
+
+  for (let i = 0; i < array.length && i < max; i++) {
+    let config = {
+      mode: MODE.TINY,
+      delim: i == array.length - 1 ? "" : ", "
+    };
+    let item;
+
+    try {
+      item = ItemRep(Object.assign({}, props, config, {
+        object: array[i]
+      }));
+    } catch (exc) {
+      item = ItemRep(Object.assign({}, props, config, {
+        object: exc
+      }));
+    }
+    items.push(item);
+  }
+
+  if (array.length > max) {
+    items.push(span({
+      className: "more-ellipsis",
+      title: "more…"
+    }, "…"));
+  }
+
+  return items;
+}
+
+/**
+ * Renders array item. Individual values are separated by a comma.
+ */
+ItemRep.propTypes = {
+  object: PropTypes.any.isRequired,
+  delim: PropTypes.string.isRequired,
+  mode: ModePropType
+};
+
+function ItemRep(props) {
+  const { Rep } = __webpack_require__(4);
+
+  let {
+    object,
+    delim,
+    mode
+  } = props;
+  return span({}, Rep(Object.assign({}, props, {
+    object: object,
+    mode: mode
+  })), delim);
+}
+
+function getLength(object) {
+  return object.length;
+}
+
+function supportsObject(object) {
+  return Array.isArray(object) || Object.prototype.toString.call(object) === "[object Arguments]";
+}
+
+const maxLengthMap = new Map();
+maxLengthMap.set(MODE.SHORT, 3);
+maxLengthMap.set(MODE.LONG, 10);
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(ArrayRep),
+  supportsObject,
+  maxLengthMap,
+  getLength,
+  ModePropType
+};
+
+/***/ }),
+/* 6 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+/* 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/. */
+
+// Dependencies
 const PropTypes = __webpack_require__(2);
 const {
   maybeEscapePropertyName,
   wrapRender
 } = __webpack_require__(0);
 const { MODE } = __webpack_require__(3);
 
 const dom = __webpack_require__(1);
@@ -697,17 +849,17 @@ PropRep.propTypes = {
 /**
  * Function that given a name, a delimiter and an object returns an array
  * of React elements representing an object property (e.g. `name: value`)
  *
  * @param {Object} props
  * @return {Array} Array of React elements.
  */
 function PropRep(props) {
-  const Grip = __webpack_require__(8);
+  const Grip = __webpack_require__(9);
   const { Rep } = __webpack_require__(4);
 
   let {
     name,
     mode,
     equal,
     suppressQuotes
   } = props;
@@ -733,17 +885,17 @@ function PropRep(props) {
     "className": "objectEqual"
   }, equal), Rep(Object.assign({}, props))];
 }
 
 // Exports from this module
 module.exports = wrapRender(PropRep);
 
 /***/ }),
-/* 6 */
+/* 7 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
@@ -754,75 +906,154 @@ const PropTypes = __webpack_require__(2)
 const {
   containsURL,
   isURL,
   escapeString,
   getGripType,
   rawCropString,
   sanitizeString,
   wrapRender,
+  isGrip,
   tokenSplitRegex,
   ELLIPSIS
 } = __webpack_require__(0);
 
 const dom = __webpack_require__(1);
 const { a, span } = dom;
 
 /**
  * Renders a string. String value is enclosed within quotes.
  */
 StringRep.propTypes = {
   useQuotes: PropTypes.bool,
   escapeWhitespace: PropTypes.bool,
   style: PropTypes.object,
-  object: PropTypes.string.isRequired,
-  member: PropTypes.any,
-  cropLimit: PropTypes.number,
+  cropLimit: PropTypes.number.isRequired,
+  member: PropTypes.string,
+  object: PropTypes.object.isRequired,
   openLink: PropTypes.func,
   className: PropTypes.string,
   omitLinkHref: PropTypes.bool
 };
 
 function StringRep(props) {
   let {
     className,
+    style,
     cropLimit,
-    object: text,
-    member,
-    style,
+    object,
     useQuotes = true,
     escapeWhitespace = true,
+    member,
     openLink,
     omitLinkHref = true
   } = props;
 
+  let text = object;
+
+  const isLong = isLongString(object);
+  const shouldCrop = (!member || !member.open) && cropLimit && text.length > cropLimit;
+
+  if (isLong) {
+    text = maybeCropLongString({
+      shouldCrop,
+      cropLimit
+    }, text);
+  }
+
+  text = formatText({
+    useQuotes,
+    escapeWhitespace
+  }, text);
+
+  const config = getElementConfig({
+    className,
+    style,
+    actor: object.actor
+  });
+
+  if (!isLong) {
+    if (containsURL(text)) {
+      return span(config, ...getLinkifiedElements(text, shouldCrop && cropLimit, omitLinkHref, openLink));
+    }
+
+    // Cropping of longString has been handled before formatting.
+    text = maybeCropString({
+      isLong,
+      shouldCrop,
+      cropLimit
+    }, text);
+  }
+
+  return span(config, text);
+}
+
+function maybeCropLongString(opts, text) {
+  const {
+    shouldCrop,
+    cropLimit
+  } = opts;
+
+  const {
+    fullText,
+    initial,
+    length
+  } = text;
+
+  text = shouldCrop ? initial.substring(0, cropLimit) : fullText || initial;
+
+  if (text.length < length) {
+    text += ELLIPSIS;
+  }
+
+  return text;
+}
+
+function formatText(opts, text) {
+  let {
+    useQuotes,
+    escapeWhitespace
+  } = opts;
+
+  return useQuotes ? escapeString(text, escapeWhitespace) : sanitizeString(text);
+}
+
+function getElementConfig(opts) {
+  const {
+    className,
+    style,
+    actor
+  } = opts;
+
+  const config = {};
+
+  if (actor) {
+    config["data-link-actor-id"] = actor;
+  }
+
   const classNames = ["objectBox", "objectBox-string"];
   if (className) {
     classNames.push(className);
   }
-  let config = { className: classNames.join(" ") };
+  config.className = classNames.join(" ");
+
   if (style) {
     config.style = style;
   }
 
-  if (useQuotes) {
-    text = escapeString(text, escapeWhitespace);
-  } else {
-    text = sanitizeString(text);
-  }
-
-  const shouldCrop = (!member || !member.open) && cropLimit && text.length > cropLimit;
-  if (!containsURL(text)) {
-    if (shouldCrop) {
-      text = rawCropString(text, cropLimit);
-    }
-    return span(config, text);
-  }
-
-  return span(config, ...getLinkifiedElements(text, shouldCrop && cropLimit, omitLinkHref, openLink));
+  return config;
+}
+
+function maybeCropString(opts, text) {
+  const {
+    shouldCrop,
+    cropLimit
+  } = opts;
+
+  return shouldCrop ? rawCropString(text, cropLimit) : text;
 }
 
 /**
  * Get an array of the elements representing the string, cropped if needed,
  * with actual links.
  *
  * @param {String} text: The actual string to linkify.
  * @param {Integer | null} cropLimit
@@ -921,35 +1152,43 @@ function getCroppedString(text, offset =
     // The string should be cropped at the beginning.
     const cutIndex = endCropIndex - start;
     return text.substring(cutIndex);
   }
 
   return text;
 }
 
+function isLongString(object) {
+  return object && object.type === "longString";
+}
+
 function supportsObject(object, noGrip = false) {
+  if (noGrip === false && isGrip(object)) {
+    return isLongString(object);
+  }
+
   return getGripType(object, noGrip) == "string";
 }
 
 // Exports from this module
 
 module.exports = {
   rep: wrapRender(StringRep),
   supportsObject
 };
 
 /***/ }),
-/* 7 */
+/* 8 */
 /***/ (function(module, exports) {
 
-module.exports = __WEBPACK_EXTERNAL_MODULE_7__;
+module.exports = __WEBPACK_EXTERNAL_MODULE_8__;
 
 /***/ }),
-/* 8 */
+/* 9 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
@@ -957,17 +1196,17 @@ module.exports = __WEBPACK_EXTERNAL_MODU
 // ReactJS
 const PropTypes = __webpack_require__(2);
 
 // Dependencies
 const {
   isGrip,
   wrapRender
 } = __webpack_require__(0);
-const PropRep = __webpack_require__(5);
+const PropRep = __webpack_require__(6);
 const { MODE } = __webpack_require__(3);
 
 const dom = __webpack_require__(1);
 const { span } = dom;
 
 /**
  * Renders generic grip. Grip is client representation
  * of remote JS object and is used as an input object
@@ -1239,16 +1478,20 @@ function getPropValue(property) {
 }
 
 // Registration
 function supportsObject(object, noGrip = false) {
   if (noGrip === true || !isGrip(object)) {
     return false;
   }
 
+  if (object.class === "DeadObject") {
+    return true;
+  }
+
   return object.preview ? typeof object.preview.ownProperties !== "undefined" : typeof object.ownPropertyLength !== "undefined";
 }
 
 const maxLengthMap = new Map();
 maxLengthMap.set(MODE.SHORT, 3);
 maxLengthMap.set(MODE.LONG, 10);
 
 // Grip is used in propIterator and has to be defined here.
@@ -1257,33 +1500,33 @@ let Grip = {
   supportsObject,
   maxLengthMap
 };
 
 // Exports from this module
 module.exports = Grip;
 
 /***/ }),
-/* 9 */
+/* 10 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 var _svgInlineReact = __webpack_require__(11);
 
 var _svgInlineReact2 = _interopRequireDefault(_svgInlineReact);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 /* 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 React = __webpack_require__(7);
+const React = __webpack_require__(8);
 const PropTypes = __webpack_require__(2);
 
 
 const svg = {
   "open-inspector": __webpack_require__(36),
   "jump-definition": __webpack_require__(37)
 };
 
@@ -1304,187 +1547,41 @@ function Svg(name, props) {
   }
   props = Object.assign({}, props, { className, src: svg[name] });
   return React.createElement(_svgInlineReact2.default, props);
 }
 
 module.exports = Svg;
 
 /***/ }),
-/* 10 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-/* 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/. */
-
-// Dependencies
-const dom = __webpack_require__(1);
-const PropTypes = __webpack_require__(2);
-const {
-  wrapRender
-} = __webpack_require__(0);
-const { MODE } = __webpack_require__(3);
-const { span } = dom;
-
-const ModePropType = PropTypes.oneOf(
-// @TODO Change this to Object.values once it's supported in Node's version of V8
-Object.keys(MODE).map(key => MODE[key]));
-
-/**
- * Renders an array. The array is enclosed by left and right bracket
- * and the max number of rendered items depends on the current mode.
- */
-ArrayRep.propTypes = {
-  mode: ModePropType,
-  object: PropTypes.array.isRequired
-};
-
-function ArrayRep(props) {
-  let {
-    object,
-    mode = MODE.SHORT
-  } = props;
-
-  let items;
-  let brackets;
-  let needSpace = function (space) {
-    return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
-  };
-
-  if (mode === MODE.TINY) {
-    let isEmpty = object.length === 0;
-    if (isEmpty) {
-      items = [];
-    } else {
-      items = [span({
-        className: "more-ellipsis",
-        title: "more…"
-      }, "…")];
-    }
-    brackets = needSpace(false);
-  } else {
-    items = arrayIterator(props, object, maxLengthMap.get(mode));
-    brackets = needSpace(items.length > 0);
-  }
-
-  return span({
-    className: "objectBox objectBox-array" }, span({
-    className: "arrayLeftBracket"
-  }, brackets.left), ...items, span({
-    className: "arrayRightBracket"
-  }, brackets.right), span({
-    className: "arrayProperties",
-    role: "group" }));
-}
-
-function arrayIterator(props, array, max) {
-  let items = [];
-
-  for (let i = 0; i < array.length && i < max; i++) {
-    let config = {
-      mode: MODE.TINY,
-      delim: i == array.length - 1 ? "" : ", "
-    };
-    let item;
-
-    try {
-      item = ItemRep(Object.assign({}, props, config, {
-        object: array[i]
-      }));
-    } catch (exc) {
-      item = ItemRep(Object.assign({}, props, config, {
-        object: exc
-      }));
-    }
-    items.push(item);
-  }
-
-  if (array.length > max) {
-    items.push(span({
-      className: "more-ellipsis",
-      title: "more…"
-    }, "…"));
-  }
-
-  return items;
-}
-
-/**
- * Renders array item. Individual values are separated by a comma.
- */
-ItemRep.propTypes = {
-  object: PropTypes.any.isRequired,
-  delim: PropTypes.string.isRequired,
-  mode: ModePropType
-};
-
-function ItemRep(props) {
-  const { Rep } = __webpack_require__(4);
-
-  let {
-    object,
-    delim,
-    mode
-  } = props;
-  return span({}, Rep(Object.assign({}, props, {
-    object: object,
-    mode: mode
-  })), delim);
-}
-
-function getLength(object) {
-  return object.length;
-}
-
-function supportsObject(object) {
-  return Array.isArray(object) || Object.prototype.toString.call(object) === "[object Arguments]";
-}
-
-const maxLengthMap = new Map();
-maxLengthMap.set(MODE.SHORT, 3);
-maxLengthMap.set(MODE.LONG, 10);
-
-// Exports from this module
-module.exports = {
-  rep: wrapRender(ArrayRep),
-  supportsObject,
-  maxLengthMap,
-  getLength
-};
-
-/***/ }),
 /* 11 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
     value: true
 });
 
 var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
 
 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
 
-var _react = __webpack_require__(7);
+var _react = __webpack_require__(8);
 
 var _react2 = _interopRequireDefault(_react);
 
 var _propTypes = __webpack_require__(2);
 
 var _util = __webpack_require__(35);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
-function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.includes(i)) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
 
 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
 
 function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
 
 function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
 
 var process = process || { env: {} };
@@ -1594,34 +1691,38 @@ module.exports = {
 
 
 /* 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/. */
 
 // Dependencies
 const PropTypes = __webpack_require__(2);
+
+const { lengthBubble } = __webpack_require__(14);
 const {
   getGripType,
   isGrip,
-  wrapRender
+  wrapRender,
+  ellipsisElement
 } = __webpack_require__(0);
 const { MODE } = __webpack_require__(3);
 
 const dom = __webpack_require__(1);
 const { span } = dom;
+const { ModePropType } = __webpack_require__(5);
 
 /**
  * Renders an array. The array is enclosed by left and right bracket
  * and the max number of rendered items depends on the current mode.
  */
 GripArray.propTypes = {
   object: PropTypes.object.isRequired,
   // @TODO Change this to Object.values once it's supported in Node's version of V8
-  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+  mode: ModePropType,
   provider: PropTypes.object,
   onDOMNodeMouseOver: PropTypes.func,
   onDOMNodeMouseOut: PropTypes.func,
   onInspectIconClick: PropTypes.func
 };
 
 function GripArray(props) {
   let {
@@ -1630,46 +1731,43 @@ function GripArray(props) {
   } = props;
 
   let items;
   let brackets;
   let needSpace = function (space) {
     return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
   };
 
+  const config = {
+    "data-link-actor-id": object.actor,
+    className: "objectBox objectBox-array"
+  };
+
+  const title = getTitle(props, object);
+
   if (mode === MODE.TINY) {
-    let objectLength = getLength(object);
-    let isEmpty = objectLength === 0;
-    let ellipsis;
-    if (!isEmpty) {
-      ellipsis = span({
-        className: "more-ellipsis",
-        title: "more…"
-      }, "…");
+    const isEmpty = getLength(object) === 0;
+
+    // Omit bracketed ellipsis for non-empty non-Array arraylikes (f.e: Sets).
+    if (!isEmpty && object.class !== "Array") {
+      return span(config, title);
     }
 
-    let title;
-    if (object.class != "Array") {
-      title = object.class + " ";
-    }
     brackets = needSpace(false);
-    return span({
-      "data-link-actor-id": object.actor,
-      className: "objectBox objectBox-array" }, title, span({
+    return span(config, title, span({
       className: "arrayLeftBracket"
-    }, brackets.left), ellipsis, span({
+    }, brackets.left), isEmpty ? null : ellipsisElement, span({
       className: "arrayRightBracket"
     }, brackets.right));
   }
+
   let max = maxLengthMap.get(mode);
   items = arrayIterator(props, object, max);
   brackets = needSpace(items.length > 0);
 
-  let title = getTitle(props, object);
-
   return span({
     "data-link-actor-id": object.actor,
     className: "objectBox objectBox-array" }, title, span({
     className: "arrayLeftBracket"
   }, brackets.left), ...interleaveCommas(items), span({
     className: "arrayRightBracket"
   }, brackets.right), span({
     className: "arrayProperties",
@@ -1689,24 +1787,46 @@ function getLength(grip) {
   if (!grip.preview) {
     return 0;
   }
 
   return grip.preview.length || grip.preview.childNodesLength || 0;
 }
 
 function getTitle(props, object) {
-  if (props.mode === MODE.TINY) {
-    return "";
-  }
+  let objectLength = getLength(object);
+  let isEmpty = objectLength === 0;
 
   let title = props.title || object.class || "Array";
+
+  const length = lengthBubble({
+    object,
+    mode: props.mode,
+    maxLengthMap,
+    getLength
+  });
+
+  if (props.mode === MODE.TINY) {
+    if (isEmpty) {
+      return object.class === "Array" ? "" : span({
+        className: "objectTitle" }, title, " ");
+    }
+
+    let trailingSpace;
+    if (object.class === "Array") {
+      title = "";
+      trailingSpace = " ";
+    }
+
+    return span({
+      className: "objectTitle" }, title, length, trailingSpace);
+  }
+
   return span({
-    className: "objectTitle"
-  }, title + " ");
+    className: "objectTitle" }, title, length, " ");
 }
 
 function getPreviewItems(grip) {
   if (!grip.preview) {
     return null;
   }
 
   return grip.preview.items || grip.preview.childNodes || [];
@@ -1765,20 +1885,17 @@ function arrayIterator(props, grip, max)
   // Handle trailing empty slots if there are some.
   if (items.length < max && emptySlots > 0) {
     items.push(getEmptySlotsElement(emptySlots));
     foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
   }
 
   const itemsShown = items.length + foldedEmptySlots;
   if (gripLength > itemsShown) {
-    items.push(span({
-      className: "more-ellipsis",
-      title: "more…"
-    }, "…"));
+    items.push(ellipsisElement);
   }
 
   return items;
 }
 
 function getEmptySlotsElement(number) {
   // TODO: Use l10N - See https://github.com/devtools-html/reps/issues/141
   return `<${number} empty slot${number > 1 ? "s" : ""}>`;
@@ -1806,40 +1923,94 @@ module.exports = {
 
 /***/ }),
 /* 14 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
+const PropTypes = __webpack_require__(2);
+
+const { wrapRender } = __webpack_require__(0);
+const { MODE } = __webpack_require__(3);
+const { ModePropType } = __webpack_require__(5);
+
+const dom = __webpack_require__(1);
+const { span } = dom;
+
+GripLengthBubble.propTypes = {
+  object: PropTypes.object.isRequired,
+  maxLengthMap: PropTypes.instanceOf(Map).isRequired,
+  getLength: PropTypes.func.isRequired,
+  mode: ModePropType,
+  visibilityThreshold: PropTypes.number
+};
+
+function GripLengthBubble(props) {
+  const {
+    object,
+    mode = MODE.SHORT,
+    visibilityThreshold = 2,
+    maxLengthMap,
+    getLength,
+    showZeroLength = false
+  } = props;
+
+  const length = getLength(object);
+  const isEmpty = length === 0;
+  const isObvious = [MODE.SHORT, MODE.LONG].includes(mode) && length > 0 && length <= maxLengthMap.get(mode) && length <= visibilityThreshold;
+  if (isEmpty && !showZeroLength || isObvious) {
+    return "";
+  }
+
+  return span({
+    className: "objectLengthBubble"
+  }, `(${length})`);
+}
+
+module.exports = {
+  lengthBubble: wrapRender(GripLengthBubble)
+};
+
+/***/ }),
+/* 15 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
 /* 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/. */
 
 // Dependencies
+
+const { lengthBubble } = __webpack_require__(14);
 const PropTypes = __webpack_require__(2);
 const {
   isGrip,
-  wrapRender
+  wrapRender,
+  ellipsisElement
 } = __webpack_require__(0);
-const PropRep = __webpack_require__(5);
+const PropRep = __webpack_require__(6);
 const { MODE } = __webpack_require__(3);
+const { ModePropType } = __webpack_require__(5);
 
 const dom = __webpack_require__(1);
 const { span } = dom;
 
 /**
  * Renders an map. A map is represented by a list of its
  * entries enclosed in curly brackets.
  */
 GripMap.propTypes = {
   object: PropTypes.object,
   // @TODO Change this to Object.values once it's supported in Node's version of V8
-  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+  mode: ModePropType,
   isInterestingEntry: PropTypes.func,
   onDOMNodeMouseOver: PropTypes.func,
   onDOMNodeMouseOut: PropTypes.func,
   onInspectIconClick: PropTypes.func,
   title: PropTypes.string
 };
 
 function GripMap(props) {
@@ -1848,34 +2019,42 @@ function GripMap(props) {
     object
   } = props;
 
   const config = {
     "data-link-actor-id": object.actor,
     className: "objectBox objectBox-object"
   };
 
-  if (mode === MODE.TINY) {
-    return span(config, getTitle(props, object));
-  }
-
-  let propsArray = safeEntriesIterator(props, object, maxLengthMap.get(mode));
-
-  return span(config, getTitle(props, object), span({
+  const title = getTitle(props, object);
+  const isEmpty = getLength(object) === 0;
+
+  if (isEmpty || mode === MODE.TINY) {
+    return span(config, title);
+  }
+
+  const propsArray = safeEntriesIterator(props, object, maxLengthMap.get(mode));
+
+  return span(config, title, span({
     className: "objectLeftBrace"
   }, " { "), ...propsArray, span({
     className: "objectRightBrace"
   }, " }"));
 }
 
 function getTitle(props, object) {
-  let title = props.title || (object && object.class ? object.class : "Map");
+  const title = props.title || (object && object.class ? object.class : "Map");
   return span({
-    className: "objectTitle"
-  }, title);
+    className: "objectTitle" }, title, lengthBubble({
+    object,
+    mode: props.mode,
+    maxLengthMap,
+    getLength,
+    showZeroLength: true
+  }));
 }
 
 function safeEntriesIterator(props, object, max) {
   max = typeof max === "undefined" ? 3 : max;
   try {
     return entriesIterator(props, object, max);
   } catch (err) {
     console.error(err);
@@ -1897,21 +2076,17 @@ function entriesIterator(props, object, 
     indexes = indexes.concat(getEntriesIndexes(mapEntries, max - indexes.length, (t, value, name) => {
       return !isInterestingEntry(t, value, name);
     }));
   }
 
   let entries = getEntries(props, mapEntries, indexes);
   if (entries.length < getLength(object)) {
     // There are some undisplayed entries. Then display "…".
-    entries.push(span({
-      key: "more",
-      className: "more-ellipsis",
-      title: "more…"
-    }, "…"));
+    entries.push(ellipsisElement);
   }
 
   return unfoldEntries(entries);
 }
 
 function unfoldEntries(items) {
   return items.reduce((res, item, index) => {
     if (Array.isArray(item)) {
@@ -2008,17 +2183,17 @@ maxLengthMap.set(MODE.LONG, 10);
 module.exports = {
   rep: wrapRender(GripMap),
   supportsObject,
   maxLengthMap,
   getLength
 };
 
 /***/ }),
-/* 15 */
+/* 16 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
@@ -2026,17 +2201,17 @@ module.exports = {
 // Dependencies
 const PropTypes = __webpack_require__(2);
 // Shortcuts
 const dom = __webpack_require__(1);
 const { span } = dom;
 const {
   wrapRender
 } = __webpack_require__(0);
-const PropRep = __webpack_require__(5);
+const PropRep = __webpack_require__(6);
 const { MODE } = __webpack_require__(3);
 /**
  * Renders an map entry. A map entry is represented by its key, a column and its value.
  */
 GripMapEntry.propTypes = {
   object: PropTypes.object,
   // @TODO Change this to Object.values once it's supported in Node's version of V8
   mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
@@ -2086,38 +2261,38 @@ function createGripMapEntry(key, value) 
 // Exports from this module
 module.exports = {
   rep: wrapRender(GripMapEntry),
   createGripMapEntry,
   supportsObject
 };
 
 /***/ }),
-/* 16 */
+/* 17 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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 client = __webpack_require__(17);
+const client = __webpack_require__(18);
 const loadProperties = __webpack_require__(55);
-const node = __webpack_require__(18);
+const node = __webpack_require__(19);
 
 module.exports = {
   client,
   loadProperties,
   node
 };
 
 /***/ }),
-/* 17 */
+/* 18 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 async function enumIndexedProperties(objectClient, start, end) {
   try {
     const { iterator } = await objectClient.enumProperties({ ignoreNonIndexedProperties: true });
@@ -2182,32 +2357,32 @@ module.exports = {
   enumEntries,
   enumIndexedProperties,
   enumNonIndexedProperties,
   enumSymbols,
   getPrototype
 };
 
 /***/ }),
-/* 18 */
+/* 19 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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 { get, has } = __webpack_require__(56);
 const { maybeEscapePropertyName } = __webpack_require__(0);
-const ArrayRep = __webpack_require__(10);
+const ArrayRep = __webpack_require__(5);
 const GripArrayRep = __webpack_require__(13);
-const GripMap = __webpack_require__(14);
-const GripMapEntryRep = __webpack_require__(15);
+const GripMap = __webpack_require__(15);
+const GripMapEntryRep = __webpack_require__(16);
 
 const MAX_NUMERICAL_PROPERTIES = 100;
 
 const NODE_TYPES = {
   BUCKET: Symbol("[n…n]"),
   DEFAULT_PROPERTIES: Symbol("[default properties]"),
   ENTRIES: Symbol("<entries>"),
   GET: Symbol("<get>"),
@@ -2282,16 +2457,37 @@ function nodeIsFunction(item) {
   return value && value.class === "Function";
 }
 
 function nodeIsOptimizedOut(item) {
   const value = getValue(item);
   return !nodeHasChildren(item) && value && value.optimizedOut;
 }
 
+function nodeIsUninitializedBinding(item) {
+  const value = getValue(item);
+  return value && value.uninitialized;
+}
+
+// Used to check if an item represents a binding that exists in a sourcemap's
+// original file content, but does not match up with a binding found in the
+// generated code.
+function nodeIsUnmappedBinding(item) {
+  const value = getValue(item);
+  return value && value.unmapped;
+}
+
+// Used to check if an item represents a binding that exists in the debugger's
+// parser result, but does not match up with a binding returned by the
+// debugger server.
+function nodeIsUnscopedBinding(item) {
+  const value = getValue(item);
+  return value && value.unscoped;
+}
+
 function nodeIsMissingArguments(item) {
   const value = getValue(item);
   return !nodeHasChildren(item) && value && value.missingArguments;
 }
 
 function nodeHasProperties(item) {
   return !nodeHasChildren(item) && nodeIsObject(item);
 }
@@ -2806,41 +3002,44 @@ module.exports = {
   nodeIsMissingArguments,
   nodeIsObject,
   nodeIsOptimizedOut,
   nodeIsPrimitive,
   nodeIsPromise,
   nodeIsPrototype,
   nodeIsProxy,
   nodeIsSetter,
+  nodeIsUninitializedBinding,
+  nodeIsUnmappedBinding,
+  nodeIsUnscopedBinding,
   nodeIsWindow,
   nodeNeedsNumericalBuckets,
   nodeSupportsNumericalBucketing,
   setNodeChildren,
   sortProperties,
   NODE_TYPES,
   // Export for testing purpose.
   SAFE_PATH_PREFIX
 };
 
 /***/ }),
-/* 19 */
+/* 20 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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 { MODE } = __webpack_require__(3);
 const { REPS, getRep } = __webpack_require__(4);
 const ObjectInspector = __webpack_require__(48);
-const ObjectInspectorUtils = __webpack_require__(16);
+const ObjectInspectorUtils = __webpack_require__(17);
 
 const {
   parseURLEncodedText,
   parseURLParams,
   maybeEscapePropertyName,
   getGripPreviewItems
 } = __webpack_require__(0);
 
@@ -2852,23 +3051,23 @@ module.exports = {
   parseURLEncodedText,
   parseURLParams,
   getGripPreviewItems,
   ObjectInspector,
   ObjectInspectorUtils
 };
 
 /***/ }),
-/* 20 */
+/* 21 */
 /***/ (function(module, exports) {
 
 // removed by extract-text-webpack-plugin
 
 /***/ }),
-/* 21 */
+/* 22 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
@@ -2900,17 +3099,17 @@ function supportsObject(object, noGrip =
 // Exports from this module
 
 module.exports = {
   rep: wrapRender(Undefined),
   supportsObject
 };
 
 /***/ }),
-/* 22 */
+/* 23 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
@@ -2942,93 +3141,16 @@ function supportsObject(object, noGrip =
 // Exports from this module
 
 module.exports = {
   rep: wrapRender(Null),
   supportsObject
 };
 
 /***/ }),
-/* 23 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-/* 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/. */
-
-// Dependencies
-const PropTypes = __webpack_require__(2);
-const {
-  escapeString,
-  sanitizeString,
-  isGrip,
-  wrapRender
-} = __webpack_require__(0);
-
-const dom = __webpack_require__(1);
-const { span } = dom;
-
-/**
- * Renders a long string grip.
- */
-LongStringRep.propTypes = {
-  useQuotes: PropTypes.bool,
-  escapeWhitespace: PropTypes.bool,
-  style: PropTypes.object,
-  cropLimit: PropTypes.number.isRequired,
-  member: PropTypes.string,
-  object: PropTypes.object.isRequired
-};
-
-function LongStringRep(props) {
-  let {
-    cropLimit,
-    member,
-    object,
-    style,
-    useQuotes = true,
-    escapeWhitespace = true
-  } = props;
-  let { fullText, initial, length } = object;
-
-  let config = {
-    "data-link-actor-id": object.actor,
-    className: "objectBox objectBox-string"
-  };
-
-  if (style) {
-    config.style = style;
-  }
-
-  let string = member && member.open ? fullText || initial : initial.substring(0, cropLimit);
-
-  if (string.length < length) {
-    string += "\u2026";
-  }
-  let formattedString = useQuotes ? escapeString(string, escapeWhitespace) : sanitizeString(string);
-  return span(config, formattedString);
-}
-
-function supportsObject(object, noGrip = false) {
-  if (noGrip === true || !isGrip(object)) {
-    return false;
-  }
-  return object.type === "longString";
-}
-
-// Exports from this module
-module.exports = {
-  rep: wrapRender(LongStringRep),
-  supportsObject
-};
-
-/***/ }),
 /* 24 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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
@@ -3084,19 +3206,20 @@ module.exports = {
 
 /* 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/. */
 
 // Dependencies
 const PropTypes = __webpack_require__(2);
 const {
-  wrapRender
+  wrapRender,
+  ellipsisElement
 } = __webpack_require__(0);
-const PropRep = __webpack_require__(5);
+const PropRep = __webpack_require__(6);
 const { MODE } = __webpack_require__(3);
 
 const dom = __webpack_require__(1);
 const { span } = dom;
 
 const DEFAULT_TITLE = "Object";
 
 /**
@@ -3116,21 +3239,17 @@ function ObjectRep(props) {
 
   if (props.mode === MODE.TINY) {
     const tinyModeItems = [];
     if (getTitle(props, object) !== DEFAULT_TITLE) {
       tinyModeItems.push(getTitleElement(props, object));
     } else {
       tinyModeItems.push(span({
         className: "objectLeftBrace"
-      }, "{"), propsArray.length > 0 ? span({
-        key: "more",
-        className: "more-ellipsis",
-        title: "more…"
-      }, "…") : null, span({
+      }, "{"), propsArray.length > 0 ? ellipsisElement : null, span({
         className: "objectRightBrace"
       }, "}"));
     }
 
     return span({ className: "objectBox objectBox-object" }, ...tinyModeItems);
   }
 
   return span({ className: "objectBox objectBox-object" }, getTitleElement(props, object), span({
@@ -3140,17 +3259,17 @@ function ObjectRep(props) {
   }, " }"));
 }
 
 function getTitleElement(props, object) {
   return span({ className: "objectTitle" }, getTitle(props, object));
 }
 
 function getTitle(props, object) {
-  return props.title || object.class || DEFAULT_TITLE;
+  return props.title || DEFAULT_TITLE;
 }
 
 function safePropIterator(props, object, max) {
   max = typeof max === "undefined" ? 3 : max;
   try {
     return propIterator(props, object, max);
   } catch (err) {
     console.error(err);
@@ -3223,21 +3342,17 @@ function propIterator(props, object, max
         continue;
       }
 
       pushPropRep(name, value);
     }
   }
 
   if (propertiesNumber < propertiesNames.length) {
-    elements.push(span({
-      key: "more",
-      className: "more-ellipsis",
-      title: ", more…"
-    }, "…"));
+    elements.push(ellipsisElement);
   }
 
   return elements;
 }
 
 function isInterestingProp(value) {
   const type = typeof value;
   return type == "boolean" || type == "number" || type == "string" && value;
@@ -3477,17 +3592,17 @@ const dom = __webpack_require__(1);
 const { span } = dom;
 
 // Reps
 const {
   getGripType,
   isGrip,
   wrapRender
 } = __webpack_require__(0);
-const { rep: StringRep } = __webpack_require__(6);
+const { rep: StringRep } = __webpack_require__(7);
 
 /**
  * Renders DOM attribute
  */
 Attribute.propTypes = {
   object: PropTypes.object.isRequired
 };
 
@@ -3641,17 +3756,18 @@ function getTitle(grip) {
 }
 
 // Registration
 function supportsObject(object, noGrip = false) {
   if (noGrip === true || !isGrip(object)) {
     return false;
   }
 
-  return object.preview && getGripType(object, noGrip) == "HTMLDocument";
+  const type = getGripType(object, noGrip);
+  return object.preview && (type === "HTMLDocument" || type === "XULDocument");
 }
 
 // Exports from this module
 module.exports = {
   rep: wrapRender(Document),
   supportsObject
 };
 
@@ -3671,17 +3787,17 @@ const PropTypes = __webpack_require__(2)
 
 // Reps
 const {
   isGrip,
   wrapRender
 } = __webpack_require__(0);
 
 const { MODE } = __webpack_require__(3);
-const { rep } = __webpack_require__(8);
+const { rep } = __webpack_require__(9);
 
 /**
  * Renders DOM event objects.
  */
 Event.propTypes = {
   object: PropTypes.object.isRequired,
   // @TODO Change this to Object.values once it's supported in Node's version of V8
   mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
@@ -3779,17 +3895,17 @@ const PropTypes = __webpack_require__(2)
 // Reps
 const {
   getGripType,
   isGrip,
   cropString,
   wrapRender
 } = __webpack_require__(0);
 const { MODE } = __webpack_require__(3);
-const Svg = __webpack_require__(9);
+const Svg = __webpack_require__(10);
 
 const dom = __webpack_require__(1);
 const { span } = dom;
 
 const IGNORED_SOURCE_URLS = ["debugger eval code"];
 
 /**
  * This component represents a template for Function objects.
@@ -4005,17 +4121,17 @@ module.exports = "<!-- This Source Code 
 const PropTypes = __webpack_require__(2);
 // Dependencies
 const {
   getGripType,
   isGrip,
   wrapRender
 } = __webpack_require__(0);
 
-const PropRep = __webpack_require__(5);
+const PropRep = __webpack_require__(6);
 const { MODE } = __webpack_require__(3);
 
 const dom = __webpack_require__(1);
 const { span } = dom;
 
 /**
  * Renders a DOM Promise object.
  */
@@ -4307,20 +4423,20 @@ module.exports = {
 // ReactJS
 const PropTypes = __webpack_require__(2);
 
 // Utils
 const {
   isGrip,
   wrapRender
 } = __webpack_require__(0);
-const { rep: StringRep } = __webpack_require__(6);
+const { rep: StringRep } = __webpack_require__(7);
 const { MODE } = __webpack_require__(3);
 const nodeConstants = __webpack_require__(12);
-const Svg = __webpack_require__(9);
+const Svg = __webpack_require__(10);
 
 const dom = __webpack_require__(1);
 const { span } = dom;
 
 /**
  * Renders DOM element node.
  */
 ElementNode.propTypes = {
@@ -4441,17 +4557,17 @@ const PropTypes = __webpack_require__(2)
 
 // Reps
 const {
   isGrip,
   cropString,
   wrapRender
 } = __webpack_require__(0);
 const { MODE } = __webpack_require__(3);
-const Svg = __webpack_require__(9);
+const Svg = __webpack_require__(10);
 
 const dom = __webpack_require__(1);
 const { span } = dom;
 
 /**
  * Renders DOM #text node.
  */
 TextNode.propTypes = {
@@ -4711,17 +4827,17 @@ module.exports = {
 const PropTypes = __webpack_require__(2);
 
 // Reps
 const {
   isGrip,
   wrapRender
 } = __webpack_require__(0);
 
-const String = __webpack_require__(6).rep;
+const String = __webpack_require__(7).rep;
 
 const dom = __webpack_require__(1);
 const { span } = dom;
 
 /**
  * Renders a grip object with textual data.
  */
 ObjectWithText.propTypes = {
@@ -4839,17 +4955,17 @@ var _devtoolsComponents = __webpack_requ
 var _devtoolsComponents2 = _interopRequireDefault(_devtoolsComponents);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 /* 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 { Component, createFactory } = __webpack_require__(7);
+const { Component, createFactory } = __webpack_require__(8);
 const PropTypes = __webpack_require__(2);
 const dom = __webpack_require__(1);
 
 const Tree = createFactory(_devtoolsComponents2.default.Tree);
 __webpack_require__(53);
 
 const classnames = __webpack_require__(54);
 
@@ -4858,17 +4974,17 @@ const {
     Rep,
     Grip
   }
 } = __webpack_require__(4);
 const {
   MODE
 } = __webpack_require__(3);
 
-const Utils = __webpack_require__(16);
+const Utils = __webpack_require__(17);
 
 const {
   getChildren,
   getClosestGripNode,
   getParent,
   getValue,
   nodeHasAccessors,
   nodeHasProperties,
@@ -4876,16 +4992,19 @@ const {
   nodeIsFunction,
   nodeIsGetter,
   nodeIsMapEntry,
   nodeIsMissingArguments,
   nodeIsOptimizedOut,
   nodeIsPrimitive,
   nodeIsPrototype,
   nodeIsSetter,
+  nodeIsUninitializedBinding,
+  nodeIsUnmappedBinding,
+  nodeIsUnscopedBinding,
   nodeIsWindow
 } = Utils.node;
 
 const {
   loadItemProperties
 } = Utils.loadProperties;
 
 // This implements a component that renders an interactive inspector
@@ -5068,17 +5187,23 @@ class ObjectInspector extends Component 
     let objectValue;
     let label = item.name;
     let itemValue = getValue(item);
 
     const isPrimitive = nodeIsPrimitive(item);
 
     const unavailable = isPrimitive && itemValue && itemValue.hasOwnProperty && itemValue.hasOwnProperty("unavailable");
 
-    if (nodeIsOptimizedOut(item)) {
+    if (nodeIsUninitializedBinding(item)) {
+      objectValue = dom.span({ className: "unavailable" }, "(uninitialized)");
+    } else if (nodeIsUnmappedBinding(item)) {
+      objectValue = dom.span({ className: "unavailable" }, "(unmapped)");
+    } else if (nodeIsUnscopedBinding(item)) {
+      objectValue = dom.span({ className: "unavailable" }, "(unscoped)");
+    } else if (nodeIsOptimizedOut(item)) {
       objectValue = dom.span({ className: "unavailable" }, "(optimized away)");
     } else if (nodeIsMissingArguments(item) || unavailable) {
       objectValue = dom.span({ className: "unavailable" }, "(unavailable)");
     } else if (nodeIsFunction(item) && !nodeIsGetter(item) && !nodeIsSetter(item) && (this.props.mode === MODE.TINY || !this.props.mode)) {
       objectValue = undefined;
       label = this.renderGrip(item, Object.assign({}, this.props, {
         functionName: label
       }));
@@ -5242,17 +5367,17 @@ module.exports = {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 
-var _react = __webpack_require__(7);
+var _react = __webpack_require__(8);
 
 var _react2 = _interopRequireDefault(_react);
 
 var _reactDomFactories = __webpack_require__(1);
 
 var _reactDomFactories2 = _interopRequireDefault(_reactDomFactories);
 
 var _propTypes = __webpack_require__(2);
@@ -6115,33 +6240,33 @@ var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBP
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const {
   enumEntries,
   enumIndexedProperties,
   enumNonIndexedProperties,
   getPrototype,
   enumSymbols
-} = __webpack_require__(17);
+} = __webpack_require__(18);
 
 const {
   getClosestGripNode,
   getClosestNonBucketNode,
   getValue,
   nodeHasAccessors,
   nodeHasAllEntriesInPreview,
   nodeHasProperties,
   nodeIsBucket,
   nodeIsDefaultProperties,
   nodeIsEntries,
   nodeIsMapEntry,
   nodeIsPrimitive,
   nodeIsProxy,
   nodeNeedsNumericalBuckets
-} = __webpack_require__(18);
+} = __webpack_require__(19);
 
 function loadItemProperties(item, createObjectClient, loadedProperties) {
   const [start, end] = item.meta ? [item.meta.startIndex, item.meta.endIndex] : [];
 
   let objectClient;
   const getObjectClient = () => {
     if (objectClient) {
       return objectClient;