Bug 1380790 - devtools reps: update bundle to v0.10.0; r=bgrins
authorNicolas Chevobbe <nchevobbe@mozilla.com>
Tue, 25 Jul 2017 17:52:38 +0200
changeset 419832 93bdd4766ea3d7be4c53357b3ff4de770b2a6145
parent 419831 a6bd93dfdbd87ccb23f0f03a08c0b529daf19b95
child 419833 ee7761772725d1c985112aebe792b3fd032a1d0a
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbgrins
bugs1380790
milestone56.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 1380790 - devtools reps: update bundle to v0.10.0; r=bgrins This also fixes a few tests that were failing due to changes in how we now render arrays and objects in tiny mode. MozReview-Commit-ID: 9JRjDQQ46FA
devtools/client/jsonview/test/browser_jsonview_empty_object.js
devtools/client/netmonitor/test/browser_net_json-long.js
devtools/client/shared/components/reps/reps.css
devtools/client/shared/components/reps/reps.js
devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_context_menu_store_as_global.js
--- a/devtools/client/jsonview/test/browser_jsonview_empty_object.js
+++ b/devtools/client/jsonview/test/browser_jsonview_empty_object.js
@@ -40,9 +40,9 @@ function testNestedObject(objExpr, summa
       ".jsonPanelBox .treeTable .objectCell");
     is(textAfter, summary, objExpr + " still has a visible summary");
   };
 }
 
 add_task(testRootObject("null"));
 add_task(testNestedObject("null"));
 add_task(testNestedObject("[]"));
-add_task(testNestedObject("{}", "Object"));
+add_task(testNestedObject("{}"));
--- a/devtools/client/netmonitor/test/browser_net_json-long.js
+++ b/devtools/client/netmonitor/test/browser_net_json-long.js
@@ -85,17 +85,17 @@ add_task(function* () {
 
     let labels = tabpanel
       .querySelectorAll("tr:not(.tree-section) .treeLabelCell .treeLabel");
     let values = tabpanel
       .querySelectorAll("tr:not(.tree-section) .treeValueCell .objectBox");
 
     is(labels[0].textContent, "0",
       "The first json property name was incorrect.");
-    is(values[0].textContent, "Object",
+    is(values[0].textContent, "{\u2026}",
       "The first json property value was incorrect.");
 
     is(labels[1].textContent, "1",
       "The second json property name was incorrect.");
-    is(values[1].textContent, "Object",
+    is(values[1].textContent, "{\u2026}",
       "The second json property value was incorrect.");
   }
 });
--- a/devtools/client/shared/components/reps/reps.css
+++ b/devtools/client/shared/components/reps/reps.css
@@ -186,16 +186,22 @@
   vertical-align: middle;
 }
 
 .objectBox-node:hover .open-inspector svg,
 .objectBox-textNode:hover .open-inspector svg,
 .open-inspector svg:hover {
   fill: rgb(65, 175, 230);
 }
+
+/******************************************************************************/
+/* "more…" ellipsis */
+.more-ellipsis {
+  color: var(--theme-comment);
+}
 /* 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/. */
 
 .tree {
   overflow: auto;
   display: inline-block;
 }
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -109,20 +109,21 @@ return /******/ (function(modules) { // 
 
 	// Load all existing rep templates
 	const Undefined = __webpack_require__(9);
 	const Null = __webpack_require__(10);
 	const StringRep = __webpack_require__(11);
 	const LongStringRep = __webpack_require__(12);
 	const Number = __webpack_require__(13);
 	const ArrayRep = __webpack_require__(14);
-	const Obj = __webpack_require__(16);
-	const SymbolRep = __webpack_require__(19);
-	const InfinityRep = __webpack_require__(20);
-	const NaNRep = __webpack_require__(21);
+	const Obj = __webpack_require__(15);
+	const SymbolRep = __webpack_require__(18);
+	const InfinityRep = __webpack_require__(19);
+	const NaNRep = __webpack_require__(20);
+	const Accessor = __webpack_require__(21);
 
 	// DOM types (grips)
 	const Attribute = __webpack_require__(22);
 	const DateTime = __webpack_require__(23);
 	const Document = __webpack_require__(24);
 	const Event = __webpack_require__(25);
 	const Func = __webpack_require__(26);
 	const PromiseRep = __webpack_require__(27);
@@ -132,22 +133,22 @@ return /******/ (function(modules) { // 
 	const ElementNode = __webpack_require__(32);
 	const TextNode = __webpack_require__(37);
 	const ErrorRep = __webpack_require__(38);
 	const Window = __webpack_require__(39);
 	const ObjectWithText = __webpack_require__(40);
 	const ObjectWithURL = __webpack_require__(41);
 	const GripArray = __webpack_require__(42);
 	const GripMap = __webpack_require__(43);
-	const Grip = __webpack_require__(18);
+	const Grip = __webpack_require__(17);
 
 	// 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, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep];
+	let reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, ElementNode, TextNode, Attribute, LongStringRep, Func, PromiseRep, ArrayRep, Document, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, 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) {
@@ -201,16 +202,17 @@ return /******/ (function(modules) { // 
 	  }
 
 	  return defaultRep.rep;
 	}
 
 	module.exports = {
 	  Rep,
 	  REPS: {
+	    Accessor,
 	    ArrayRep,
 	    Attribute,
 	    CommentNode,
 	    DateTime,
 	    Document,
 	    ElementNode,
 	    ErrorRep,
 	    Event,
@@ -880,17 +882,16 @@ return /******/ (function(modules) { // 
 	 * 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 React = __webpack_require__(8);
 	const {
 	  wrapRender
 	} = __webpack_require__(7);
-	const Caption = __webpack_require__(15);
 	const { MODE } = __webpack_require__(1);
 
 	const ModePropType = React.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]));
 
 	// Shortcuts
 	const DOM = React.DOM;
@@ -913,17 +914,24 @@ return /******/ (function(modules) { // 
 	  let items;
 	  let brackets;
 	  let needSpace = function (space) {
 	    return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
 	  };
 
 	  if (mode === MODE.TINY) {
 	    let isEmpty = object.length === 0;
-	    items = [DOM.span({ className: "length" }, isEmpty ? "" : object.length)];
+	    if (isEmpty) {
+	      items = [];
+	    } else {
+	      items = [DOM.span({
+	        className: "more-ellipsis",
+	        title: "more…"
+	      }, "…")];
+	    }
 	    brackets = needSpace(false);
 	  } else {
 	    items = arrayIterator(props, object, maxLengthMap.get(mode));
 	    brackets = needSpace(items.length > 0);
 	  }
 
 	  return DOM.span({
 	    className: "objectBox objectBox-array" }, DOM.span({
@@ -956,19 +964,20 @@ return /******/ (function(modules) { // 
 	        object: exc,
 	        mode: MODE.TINY,
 	        delim: delim
 	      }));
 	    }
 	  }
 
 	  if (array.length > max) {
-	    items.push(Caption({
-	      object: DOM.span({}, "more…")
-	    }));
+	    items.push(DOM.span({
+	      className: "more-ellipsis",
+	      title: "more…"
+	    }, "…"));
 	  }
 
 	  return items;
 	}
 
 	/**
 	 * Renders array item. Individual values are separated by a comma.
 	 */
@@ -1009,82 +1018,73 @@ return /******/ (function(modules) { // 
 /***/ function(module, exports, __webpack_require__) {
 
 	/* 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 React = __webpack_require__(8);
-	const DOM = React.DOM;
-
-	const { wrapRender } = __webpack_require__(7);
-
-	/**
-	 * Renders a caption. This template is used by other components
-	 * that needs to distinguish between a simple text/value and a label.
-	 */
-	Caption.propTypes = {
-	  object: React.PropTypes.oneOfType([React.PropTypes.number, React.PropTypes.string]).isRequired
-	};
-
-	function Caption(props) {
-	  return DOM.span({ "className": "caption" }, props.object);
-	}
-
-	// Exports from this module
-	module.exports = wrapRender(Caption);
-
-/***/ },
-/* 16 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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 React = __webpack_require__(8);
 	const {
 	  wrapRender
 	} = __webpack_require__(7);
-	const Caption = __webpack_require__(15);
-	const PropRep = __webpack_require__(17);
+	const PropRep = __webpack_require__(16);
 	const { MODE } = __webpack_require__(1);
 	// Shortcuts
 	const { span } = React.DOM;
+
+	const DEFAULT_TITLE = "Object";
+
 	/**
 	 * Renders an object. An object is represented by a list of its
 	 * properties enclosed in curly brackets.
 	 */
 	ObjectRep.propTypes = {
 	  object: React.PropTypes.object.isRequired,
 	  // @TODO Change this to Object.values once it's supported in Node's version of V8
 	  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
 	  title: React.PropTypes.string
 	};
 
 	function ObjectRep(props) {
 	  let object = props.object;
 	  let propsArray = safePropIterator(props, object);
 
-	  if (props.mode === MODE.TINY || !propsArray.length) {
-	    return span({ className: "objectBox objectBox-object" }, getTitle(props, object));
-	  }
-
-	  return span({ className: "objectBox objectBox-object" }, getTitle(props, object), span({
+	  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({
+	        className: "objectRightBrace"
+	      }, "}"));
+	    }
+
+	    return span({ className: "objectBox objectBox-object" }, ...tinyModeItems);
+	  }
+
+	  return span({ className: "objectBox objectBox-object" }, getTitleElement(props, object), span({
 	    className: "objectLeftBrace"
 	  }, " { "), ...propsArray, span({
 	    className: "objectRightBrace"
 	  }, " }"));
 	}
 
+	function getTitleElement(props, object) {
+	  return span({ className: "objectTitle" }, getTitle(props, object));
+	}
+
 	function getTitle(props, object) {
-	  let title = props.title || object.class || "Object";
-	  return span({ className: "objectTitle" }, title);
+	  return props.title || object.class || DEFAULT_TITLE;
 	}
 
 	function safePropIterator(props, object, max) {
 	  max = typeof max === "undefined" ? 3 : max;
 	  try {
 	    return propIterator(props, object, max);
 	  } catch (err) {
 	    console.error(err);
@@ -1111,19 +1111,20 @@ return /******/ (function(modules) { // 
 	    // There are not enough props yet (or at least, not enough props to
 	    // be able to know whether we should print "more…" or not).
 	    // Let's display also empty members and functions.
 	    interestingObject = Object.assign({}, interestingObject, getFilteredObject(object, max - Object.keys(interestingObject).length, (type, value) => !isInterestingProp(type, value)));
 	  }
 
 	  let propsArray = getPropsArray(interestingObject);
 	  if (Object.keys(object).length > max) {
-	    propsArray.push(Caption({
-	      object: span({}, "more…")
-	    }));
+	    propsArray.push(span({
+	      className: "more-ellipsis",
+	      title: "more…"
+	    }, "…"));
 	  }
 
 	  return unfoldProps(propsArray);
 	}
 
 	function unfoldProps(items) {
 	  return items.reduce((res, item, index) => {
 	    if (Array.isArray(item)) {
@@ -1205,17 +1206,17 @@ return /******/ (function(modules) { // 
 
 	// Exports from this module
 	module.exports = {
 	  rep: wrapRender(ObjectRep),
 	  supportsObject
 	};
 
 /***/ },
-/* 17 */
+/* 16 */
 /***/ function(module, exports, __webpack_require__) {
 
 	/* 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 React = __webpack_require__(8);
@@ -1251,17 +1252,17 @@ return /******/ (function(modules) { // 
 	/**
 	 * 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__(18);
+	  const Grip = __webpack_require__(17);
 	  const { Rep } = __webpack_require__(2);
 
 	  let {
 	    name,
 	    mode,
 	    equal,
 	    suppressQuotes
 	  } = props;
@@ -1286,32 +1287,31 @@ return /******/ (function(modules) { // 
 	    "className": "objectEqual"
 	  }, equal), Rep(Object.assign({}, props))];
 	}
 
 	// Exports from this module
 	module.exports = wrapRender(PropRep);
 
 /***/ },
-/* 18 */
+/* 17 */
 /***/ function(module, exports, __webpack_require__) {
 
 	/* 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/. */
 
 	// ReactJS
 	const React = __webpack_require__(8);
 	// Dependencies
 	const {
 	  isGrip,
 	  wrapRender
 	} = __webpack_require__(7);
-	const Caption = __webpack_require__(15);
-	const PropRep = __webpack_require__(17);
+	const PropRep = __webpack_require__(16);
 	const { MODE } = __webpack_require__(1);
 	// Shortcuts
 	const { span } = React.DOM;
 
 	/**
 	 * Renders generic grip. Grip is client representation
 	 * of remote JS object and is used as an input object
 	 * for this rep component.
@@ -1323,45 +1323,71 @@ return /******/ (function(modules) { // 
 	  isInterestingProp: React.PropTypes.func,
 	  title: React.PropTypes.string,
 	  onDOMNodeMouseOver: React.PropTypes.func,
 	  onDOMNodeMouseOut: React.PropTypes.func,
 	  onInspectIconClick: React.PropTypes.func,
 	  noGrip: React.PropTypes.bool
 	};
 
+	const DEFAULT_TITLE = "Object";
+
 	function GripRep(props) {
 	  let {
 	    mode = MODE.SHORT,
 	    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 propertiesLength = object.preview && object.preview.ownPropertiesLength ? object.preview.ownPropertiesLength : object.ownPropertyLength;
+
+	    if (object.preview && object.preview.safeGetterValues) {
+	      propertiesLength += Object.keys(object.preview.safeGetterValues).length;
+	    }
+
+	    const tinyModeItems = [];
+	    if (getTitle(props, object) !== DEFAULT_TITLE) {
+	      tinyModeItems.push(getTitleElement(props, object));
+	    } else {
+	      tinyModeItems.push(span({
+	        className: "objectLeftBrace"
+	      }, "{"), propertiesLength > 0 ? span({
+	        key: "more",
+	        className: "more-ellipsis",
+	        title: "more…"
+	      }, "…") : null, span({
+	        className: "objectRightBrace"
+	      }, "}"));
+	    }
+
+	    return span(config, ...tinyModeItems);
 	  }
 
 	  let propsArray = safePropIterator(props, object, maxLengthMap.get(mode));
 
-	  return span(config, getTitle(props, object), span({
+	  return span(config, getTitleElement(props, object), span({
 	    className: "objectLeftBrace"
 	  }, " { "), ...propsArray, span({
 	    className: "objectRightBrace"
 	  }, " }"));
 	}
 
-	function getTitle(props, object) {
-	  let title = props.title || object.class || "Object";
+	function getTitleElement(props, object) {
 	  return span({
 	    className: "objectTitle"
-	  }, title);
+	  }, getTitle(props, object));
+	}
+
+	function getTitle(props, object) {
+	  return props.title || object.class || DEFAULT_TITLE;
 	}
 
 	function safePropIterator(props, object, max) {
 	  max = typeof max === "undefined" ? maxLengthMap.get(MODE.SHORT) : max;
 	  try {
 	    return propIterator(props, object, max);
 	  } catch (err) {
 	    console.error(err);
@@ -1402,21 +1428,26 @@ return /******/ (function(modules) { // 
 	  }
 
 	  // The server synthesizes some property names for a Proxy, like
 	  // <target> and <handler>; we don't want to quote these because,
 	  // as synthetic properties, they appear more natural when
 	  // unquoted.
 	  const suppressQuotes = object.class === "Proxy";
 	  let propsArray = getProps(props, properties, indexes, suppressQuotes);
-	  if (Object.keys(properties).length > max || propertiesLength > max) {
+	  if (Object.keys(properties).length > max || propertiesLength > max
+	  // When the object has non-enumerable properties, we don't have them in the packet,
+	  // but we might want to show there's something in the object.
+	  || propertiesLength > propsArray.length) {
 	    // There are some undisplayed props. Then display "more...".
-	    propsArray.push(Caption({
-	      object: span({}, "more…")
-	    }));
+	    propsArray.push(span({
+	      key: "more",
+	      className: "more-ellipsis",
+	      title: "more…"
+	    }, "…"));
 	  }
 
 	  return unfoldProps(propsArray);
 	}
 
 	function unfoldProps(items) {
 	  return items.reduce((res, item, index) => {
 	    if (Array.isArray(item)) {
@@ -1538,17 +1569,17 @@ return /******/ (function(modules) { // 
 	  supportsObject,
 	  maxLengthMap
 	};
 
 	// Exports from this module
 	module.exports = Grip;
 
 /***/ },
-/* 19 */
+/* 18 */
 /***/ function(module, exports, __webpack_require__) {
 
 	/* 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 React = __webpack_require__(8);
@@ -1578,17 +1609,17 @@ return /******/ (function(modules) { // 
 
 	// Exports from this module
 	module.exports = {
 	  rep: wrapRender(SymbolRep),
 	  supportsObject
 	};
 
 /***/ },
-/* 20 */
+/* 19 */
 /***/ function(module, exports, __webpack_require__) {
 
 	/* 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 React = __webpack_require__(8);
@@ -1619,17 +1650,17 @@ return /******/ (function(modules) { // 
 
 	// Exports from this module
 	module.exports = {
 	  rep: wrapRender(InfinityRep),
 	  supportsObject
 	};
 
 /***/ },
-/* 21 */
+/* 20 */
 /***/ function(module, exports, __webpack_require__) {
 
 	/* 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 React = __webpack_require__(8);
@@ -1652,16 +1683,84 @@ return /******/ (function(modules) { // 
 
 	// Exports from this module
 	module.exports = {
 	  rep: wrapRender(NaNRep),
 	  supportsObject
 	};
 
 /***/ },
+/* 21 */
+/***/ function(module, exports, __webpack_require__) {
+
+	/* 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 React = __webpack_require__(8);
+	const {
+	  wrapRender
+	} = __webpack_require__(7);
+	const { MODE } = __webpack_require__(1);
+	// Shortcuts
+	const {
+	  span
+	} = React.DOM;
+	/**
+	 * Renders an object. An object is represented by a list of its
+	 * properties enclosed in curly brackets.
+	 */
+	Accessor.propTypes = {
+	  object: React.PropTypes.object.isRequired,
+	  mode: React.PropTypes.oneOf(Object.values(MODE))
+	};
+
+	function Accessor(props) {
+	  const {
+	    object
+	  } = props;
+
+	  const accessors = [];
+	  if (hasGetter(object)) {
+	    accessors.push("Getter");
+	  }
+	  if (hasSetter(object)) {
+	    accessors.push("Setter");
+	  }
+	  const title = accessors.join(" & ");
+
+	  return span({ className: "objectBox objectBox-accessor" }, span({
+	    className: "objectTitle"
+	  }, title));
+	}
+
+	function hasGetter(object) {
+	  return object && object.get && object.get.type !== "undefined";
+	}
+
+	function hasSetter(object) {
+	  return object && object.set && object.set.type !== "undefined";
+	}
+
+	function supportsObject(object, type, noGrip = false) {
+	  if (noGrip !== true && (hasGetter(object) || hasSetter(object))) {
+	    return true;
+	  }
+
+	  return false;
+	}
+
+	// Exports from this module
+	module.exports = {
+	  rep: wrapRender(Accessor),
+	  supportsObject
+	};
+
+/***/ },
 /* 22 */
 /***/ function(module, exports, __webpack_require__) {
 
 	/* 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/. */
 
 	// ReactJS
@@ -1853,17 +1952,17 @@ return /******/ (function(modules) { // 
 
 	// Reps
 	const {
 	  isGrip,
 	  wrapRender
 	} = __webpack_require__(7);
 
 	const { MODE } = __webpack_require__(1);
-	const { rep } = __webpack_require__(18);
+	const { rep } = __webpack_require__(17);
 
 	/**
 	 * Renders DOM event objects.
 	 */
 	Event.propTypes = {
 	  object: React.PropTypes.object.isRequired,
 	  // @TODO Change this to Object.values once it's supported in Node's version of V8
 	  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
@@ -2045,17 +2144,17 @@ return /******/ (function(modules) { // 
 	// ReactJS
 	const React = __webpack_require__(8);
 	// Dependencies
 	const {
 	  isGrip,
 	  wrapRender
 	} = __webpack_require__(7);
 
-	const PropRep = __webpack_require__(17);
+	const PropRep = __webpack_require__(16);
 	const { MODE } = __webpack_require__(1);
 	// Shortcuts
 	const { span } = React.DOM;
 
 	/**
 	 * Renders a DOM Promise object.
 	 */
 	PromiseRep.propTypes = {
@@ -3054,17 +3153,16 @@ return /******/ (function(modules) { // 
 	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 	// Dependencies
 	const React = __webpack_require__(8);
 	const {
 	  isGrip,
 	  wrapRender
 	} = __webpack_require__(7);
-	const Caption = __webpack_require__(15);
 	const { MODE } = __webpack_require__(1);
 
 	// Shortcuts
 	const { span } = React.DOM;
 
 	/**
 	 * Renders an array. The array is enclosed by left and right bracket
 	 * and the max number of rendered items depends on the current mode.
@@ -3089,19 +3187,24 @@ return /******/ (function(modules) { // 
 	  let brackets;
 	  let needSpace = function (space) {
 	    return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
 	  };
 
 	  if (mode === MODE.TINY) {
 	    let objectLength = getLength(object);
 	    let isEmpty = objectLength === 0;
-	    items = [span({
-	      className: "length"
-	    }, isEmpty ? "" : objectLength)];
+	    if (isEmpty) {
+	      items = [];
+	    } else {
+	      items = [span({
+	        className: "more-ellipsis",
+	        title: "more…"
+	      }, "…")];
+	    }
 	    brackets = needSpace(false);
 	  } else {
 	    let max = maxLengthMap.get(mode);
 	    items = arrayIterator(props, object, max);
 	    brackets = needSpace(items.length > 0);
 	  }
 
 	  let title = getTitle(props, object);
@@ -3145,34 +3248,30 @@ return /******/ (function(modules) { // 
 	  }, title + " ");
 	}
 
 	function getPreviewItems(grip) {
 	  if (!grip.preview) {
 	    return null;
 	  }
 
-	  return grip.preview.items || grip.preview.childNodes || null;
+	  return grip.preview.items || grip.preview.childNodes || [];
 	}
 
 	function arrayIterator(props, grip, max) {
 	  let { Rep } = __webpack_require__(2);
 
 	  let items = [];
 	  const gripLength = getLength(grip);
 
 	  if (!gripLength) {
 	    return items;
 	  }
 
 	  const previewItems = getPreviewItems(grip);
-	  if (!previewItems) {
-	    return items;
-	  }
-
 	  let provider = props.provider;
 
 	  let emptySlots = 0;
 	  let foldedEmptySlots = 0;
 	  items = previewItems.reduce((res, itemGrip) => {
 	    if (res.length >= max) {
 	      return res;
 	    }
@@ -3210,19 +3309,20 @@ return /******/ (function(modules) { // 
 	  // 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(Caption({
-	      object: span({}, "more…")
-	    }));
+	    items.push(span({
+	      className: "more-ellipsis",
+	      title: "more…"
+	    }, "…"));
 	  }
 
 	  return items;
 	}
 
 	function getEmptySlotsElement(number) {
 	  // TODO: Use l10N - See https://github.com/devtools-html/reps/issues/141
 	  return `<${number} empty slot${number > 1 ? "s" : ""}>`;
@@ -3256,18 +3356,17 @@ return /******/ (function(modules) { // 
 	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 	// Dependencies
 	const React = __webpack_require__(8);
 	const {
 	  isGrip,
 	  wrapRender
 	} = __webpack_require__(7);
-	const Caption = __webpack_require__(15);
-	const PropRep = __webpack_require__(17);
+	const PropRep = __webpack_require__(16);
 	const { MODE } = __webpack_require__(1);
 	// Shortcuts
 	const { span } = React.DOM;
 
 	/**
 	 * Renders an map. A map is represented by a list of its
 	 * entries enclosed in curly brackets.
 	 */
@@ -3335,22 +3434,23 @@ return /******/ (function(modules) { // 
 	  if (indexes.length < max && indexes.length < mapEntries.length) {
 	    // There are not enough entries yet, so we add uninteresting entries.
 	    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 < mapEntries.length) {
-	    // There are some undisplayed entries. Then display "more…".
-	    entries.push(Caption({
+	  if (entries.length < object.preview.size) {
+	    // There are some undisplayed entries. Then display "…".
+	    entries.push(span({
 	      key: "more",
-	      object: span({}, "more…")
-	    }));
+	      className: "more-ellipsis",
+	      title: "more…"
+	    }, "…"));
 	  }
 
 	  return unfoldEntries(entries);
 	}
 
 	function unfoldEntries(items) {
 	  return items.reduce((res, item, index) => {
 	    if (Array.isArray(item)) {
@@ -3472,16 +3572,17 @@ return /******/ (function(modules) { // 
 	const {
 	  MODE
 	} = __webpack_require__(1);
 
 	const {
 	  getChildren,
 	  getValue,
 	  isDefault,
+	  nodeHasAccessors,
 	  nodeHasProperties,
 	  nodeIsMissingArguments,
 	  nodeIsOptimizedOut,
 	  nodeIsPrimitive
 	} = __webpack_require__(50);
 
 	// This implements a component that renders an interactive inspector
 	// for looking at JavaScript objects. It expects descriptions of
@@ -3594,62 +3695,62 @@ return /******/ (function(modules) { // 
 	    }
 	  }
 
 	  renderTreeItem(item, depth, focused, arrow, expanded) {
 	    let objectValue;
 	    let label = item.name;
 	    let itemValue = getValue(item);
 
-	    const unavailable = nodeIsPrimitive(item) && itemValue && itemValue.hasOwnProperty && itemValue.hasOwnProperty("unavailable");
+	    const isPrimitive = nodeIsPrimitive(item);
+
+	    const unavailable = isPrimitive && itemValue && itemValue.hasOwnProperty && itemValue.hasOwnProperty("unavailable");
 
 	    if (nodeIsOptimizedOut(item)) {
 	      objectValue = dom.span({ className: "unavailable" }, "(optimized away)");
 	    } else if (nodeIsMissingArguments(item) || unavailable) {
 	      objectValue = dom.span({ className: "unavailable" }, "(unavailable)");
-	    } else if (nodeHasProperties(item) || nodeIsPrimitive(item)) {
-	      let mode;
-	      if (depth === 0) {
-	        mode = this.props.mode;
-	      } else {
-	        mode = this.props.mode === MODE.LONG ? MODE.SHORT : MODE.TINY;
+	    } else if (nodeHasProperties(item) || nodeHasAccessors(item) || isPrimitive) {
+	      let repsProp = Object.assign({}, this.props);
+	      if (depth > 0) {
+	        repsProp.mode = this.props.mode === MODE.LONG ? MODE.SHORT : MODE.TINY;
 	      }
 
-	      objectValue = this.renderGrip(item, this.props, mode);
+	      objectValue = this.renderGrip(item, repsProp);
 	    }
 
 	    const SINGLE_INDENT_WIDTH = 15;
-	    const indentWidth = (depth + (nodeIsPrimitive(item) ? 1 : 0)) * SINGLE_INDENT_WIDTH;
+	    const indentWidth = (depth + (isPrimitive ? 1 : 0)) * SINGLE_INDENT_WIDTH;
 
 	    const {
 	      onDoubleClick,
 	      onLabelClick
 	    } = this.props;
 
 	    return dom.div({
 	      className: classnames("node object-node", {
 	        focused,
 	        "default-property": this.isDefaultProperty(item)
 	      }),
 	      style: {
 	        marginLeft: indentWidth
 	      },
-	      onClick: e => {
+	      onClick: isPrimitive === false ? e => {
 	        e.stopPropagation();
 	        this.setExpanded(item, !expanded);
-	      },
+	      } : null,
 	      onDoubleClick: onDoubleClick ? e => {
 	        e.stopPropagation();
 	        onDoubleClick(item, {
 	          depth,
 	          focused,
 	          expanded
 	        });
 	      } : null
-	    }, nodeIsPrimitive(item) === false ? Svg("arrow", {
+	    }, isPrimitive === false ? Svg("arrow", {
 	      className: classnames({
 	        expanded: expanded
 	      })
 	    }) : null, label ? dom.span({
 	      className: "object-label",
 	      onClick: onLabelClick ? event => {
 	        event.stopPropagation();
 	        onLabelClick(item, {
@@ -3657,21 +3758,21 @@ return /******/ (function(modules) { // 
 	          focused,
 	          expanded,
 	          setExpanded: this.setExpanded
 	        });
 	      } : null
 	    }, label) : null, label && objectValue ? dom.span({ className: "object-delimiter" }, " : ") : null, objectValue);
 	  }
 
-	  renderGrip(item, props, mode = MODE.TINY) {
+	  renderGrip(item, props) {
 	    const object = getValue(item);
 	    return Rep(Object.assign({}, props, {
 	      object,
-	      mode
+	      mode: props.mode || MODE.TINY
 	    }));
 	  }
 
 	  render() {
 	    const {
 	      autoExpandDepth = 1,
 	      autoExpandAll = true,
 	      disabledFocus,
@@ -3681,17 +3782,17 @@ return /******/ (function(modules) { // 
 
 	    const {
 	      expandedKeys,
 	      focusedItem
 	    } = this.state;
 
 	    let roots = this.getRoots();
 	    if (roots.length === 1 && nodeIsPrimitive(roots[0])) {
-	      return this.renderGrip(roots[0], this.props, this.props.mode);
+	      return this.renderGrip(roots[0], this.props);
 	    }
 
 	    return Tree({
 	      className: disableWrap ? "nowrap" : "",
 	      autoExpandAll,
 	      autoExpandDepth,
 	      disabledFocus,
 	      itemHeight,
@@ -4351,26 +4452,39 @@ return /******/ (function(modules) { // 
 /* 50 */
 /***/ function(module, exports, __webpack_require__) {
 
 	/* 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 = __webpack_require__(51);
+	const has = __webpack_require__(103);
 	const { maybeEscapePropertyName } = __webpack_require__(7);
 
 	let WINDOW_PROPERTIES = {};
 
 	if (typeof window === "object") {
 	  WINDOW_PROPERTIES = Object.getOwnPropertyNames(window);
 	}
 
 	function getValue(item) {
-	  return get(item, "contents.value", undefined);
+	  if (has(item, "contents.value")) {
+	    return get(item, "contents.value");
+	  }
+
+	  if (has(item, "contents.getterValue")) {
+	    return get(item, "contents.getterValue", undefined);
+	  }
+
+	  if (nodeHasAccessors(item)) {
+	    return item.contents;
+	  }
+
+	  return undefined;
 	}
 
 	function isBucket(item) {
 	  return item.path && item.path.match(/bucket(\d+)$/);
 	}
 
 	function nodeHasChildren(item) {
 	  return Array.isArray(item.contents) || isBucket(item);
@@ -4400,17 +4514,17 @@ return /******/ (function(modules) { // 
 	  return !nodeHasChildren(item) && value && value.missingArguments;
 	}
 
 	function nodeHasProperties(item) {
 	  return !nodeHasChildren(item) && nodeIsObject(item);
 	}
 
 	function nodeIsPrimitive(item) {
-	  return !nodeHasChildren(item) && !nodeHasProperties(item);
+	  return !nodeHasChildren(item) && !nodeHasProperties(item) && !nodeHasAccessors(item);
 	}
 
 	function isPromise(item) {
 	  const value = getValue(item);
 	  return value.class == "Promise";
 	}
 
 	function getPromiseProperties(item) {
@@ -4428,16 +4542,44 @@ return /******/ (function(modules) { // 
 
 	  if (value) {
 	    properties.push(createNode("<value>", `${item.path}/value`, { value: value }));
 	  }
 
 	  return properties;
 	}
 
+	function getNodeGetter(item) {
+	  return get(item, "contents.get", undefined);
+	}
+
+	function getNodeSetter(item) {
+	  return get(item, "contents.set", undefined);
+	}
+
+	function nodeHasAccessors(item) {
+	  return !!getNodeGetter(item) || !!getNodeSetter(item);
+	}
+
+	function getAccessors(item) {
+	  const accessors = [];
+
+	  const getter = getNodeGetter(item);
+	  if (getter && getter.type !== "undefined") {
+	    accessors.push(createNode("<get>", `${item.path}/get`, { value: getter }));
+	  }
+
+	  const setter = getNodeSetter(item);
+	  if (setter && setter.type !== "undefined") {
+	    accessors.push(createNode("<set>", `${item.path}/set`, { value: setter }));
+	  }
+
+	  return accessors;
+	}
+
 	function isDefault(item, roots) {
 	  if (roots && roots.length === 1) {
 	    const value = getValue(roots[0]);
 	    return value.class === "Window";
 	  }
 	  return WINDOW_PROPERTIES.includes(item.name);
 	}
 
@@ -4486,34 +4628,41 @@ return /******/ (function(modules) { // 
 	  return nodes;
 	}
 
 	function makeNodesForOwnProps(properties, parentPath, ownProperties) {
 	  return properties.map(name => createNode(maybeEscapePropertyName(name), `${parentPath}/${name}`, ownProperties[name]));
 	}
 
 	/*
-	 * Ignore non-concrete values like getters and setters
-	 * for now by making sure we have a value.
+	 * Ignore properties that are neither non-concrete nor getters/setters.
 	*/
 	function makeNodesForProperties(objProps, parent, { bucketSize = 100 } = {}) {
-	  const { ownProperties, prototype, ownSymbols } = objProps;
+	  const {
+	    ownProperties,
+	    ownSymbols,
+	    prototype,
+	    safeGetterValues
+	  } = objProps;
+
 	  const parentPath = parent.path;
 	  const parentValue = getValue(parent);
-	  const properties = sortProperties(Object.keys(ownProperties)).filter(name => ownProperties[name].hasOwnProperty("value"));
+
+	  let allProperties = Object.assign({}, ownProperties, safeGetterValues);
+	  const properties = sortProperties(Object.keys(allProperties)).filter(name => allProperties[name].hasOwnProperty("value") || allProperties[name].hasOwnProperty("getterValue") || allProperties[name].hasOwnProperty("get") || allProperties[name].hasOwnProperty("set"));
 
 	  const numProperties = properties.length;
 
 	  let nodes = [];
 	  if (nodeIsArray(prototype) && numProperties > bucketSize) {
-	    nodes = makeNumericalBuckets(properties, bucketSize, parentPath, ownProperties);
+	    nodes = makeNumericalBuckets(properties, bucketSize, parentPath, allProperties);
 	  } else if (parentValue.class == "Window") {
-	    nodes = makeDefaultPropsBucket(properties, parentPath, ownProperties);
+	    nodes = makeDefaultPropsBucket(properties, parentPath, allProperties);
 	  } else {
-	    nodes = makeNodesForOwnProps(properties, parentPath, ownProperties);
+	    nodes = makeNodesForOwnProps(properties, parentPath, allProperties);
 	  }
 
 	  for (let index in ownSymbols) {
 	    nodes.push(createNode(ownSymbols[index].name, `${parentPath}/##symbol-${index}`, ownSymbols[index].descriptor));
 	  }
 
 	  if (isPromise(parent)) {
 	    nodes.push(...getPromiseProperties(parent));
@@ -4535,65 +4684,72 @@ return /******/ (function(modules) { // 
 	  // tree. This helps debugging & optimizes React's rendering of large
 	  // lists. The path will be separated by property name,
 	  // i.e. `{ foo: { bar: { baz: 5 }}}` will have a path of `foo/bar/baz`
 	  // for the inner object.
 	  return { name, path, contents };
 	}
 
 	function getChildren({ getObjectProperties, actors, item }) {
-	  const obj = item.contents;
-
 	  // Nodes can either have children already, or be an object with
 	  // properties that we need to go and fetch.
+	  if (nodeHasAccessors(item)) {
+	    return getAccessors(item);
+	  }
+
 	  if (nodeHasChildren(item)) {
 	    return item.contents;
 	  }
 
 	  if (!nodeHasProperties(item)) {
 	    return [];
 	  }
 
-	  const actor = obj.value.actor;
-
 	  // Because we are dynamically creating the tree as the user
 	  // expands it (not precalculated tree structure), we cache child
 	  // arrays. This not only helps performance, but is necessary
 	  // because the expanded state depends on instances of nodes
 	  // being the same across renders. If we didn't do this, each
 	  // node would be a new instance every render.
 	  const key = item.path;
 	  if (actors && actors[key]) {
 	    return actors[key];
 	  }
 
 	  if (isBucket(item)) {
 	    return item.contents.children;
 	  }
 
+	  const actor = get(getValue(item), "actor", undefined);
 	  const loadedProps = getObjectProperties(actor);
-	  const { ownProperties, prototype } = loadedProps || {};
-
-	  if (!ownProperties && !prototype) {
+	  const {
+	    ownProperties,
+	    ownSymbols,
+	    safeGetterValues,
+	    prototype
+	  } = loadedProps || {};
+
+	  if (!ownProperties && !ownSymbols && !safeGetterValues && !prototype) {
 	    return [];
 	  }
 
 	  let children = makeNodesForProperties(loadedProps, item);
 	  actors[key] = children;
 	  return children;
 	}
 
 	module.exports = {
 	  createNode,
 	  getChildren,
 	  getPromiseProperties,
 	  getValue,
 	  isDefault,
 	  isPromise,
 	  makeNodesForProperties,
+	  nodeHasAccessors,
 	  nodeHasChildren,
 	  nodeHasProperties,
 	  nodeIsFunction,
 	  nodeIsMissingArguments,
 	  nodeIsObject,
 	  nodeIsOptimizedOut,
 	  nodeIsPrimitive,
 	  sortProperties
@@ -6154,12 +6310,258 @@ return /******/ (function(modules) { // 
 	  }
 	  var result = (value + '');
 	  return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
 	}
 
 	module.exports = toKey;
 
 
+/***/ },
+/* 103 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var baseHas = __webpack_require__(104),
+	    hasPath = __webpack_require__(105);
+
+	/**
+	 * Checks if `path` is a direct property of `object`.
+	 *
+	 * @static
+	 * @since 0.1.0
+	 * @memberOf _
+	 * @category Object
+	 * @param {Object} object The object to query.
+	 * @param {Array|string} path The path to check.
+	 * @returns {boolean} Returns `true` if `path` exists, else `false`.
+	 * @example
+	 *
+	 * var object = { 'a': { 'b': 2 } };
+	 * var other = _.create({ 'a': _.create({ 'b': 2 }) });
+	 *
+	 * _.has(object, 'a');
+	 * // => true
+	 *
+	 * _.has(object, 'a.b');
+	 * // => true
+	 *
+	 * _.has(object, ['a', 'b']);
+	 * // => true
+	 *
+	 * _.has(other, 'a');
+	 * // => false
+	 */
+	function has(object, path) {
+	  return object != null && hasPath(object, path, baseHas);
+	}
+
+	module.exports = has;
+
+
+/***/ },
+/* 104 */
+/***/ function(module, exports) {
+
+	/** Used for built-in method references. */
+	var objectProto = Object.prototype;
+
+	/** Used to check objects for own properties. */
+	var hasOwnProperty = objectProto.hasOwnProperty;
+
+	/**
+	 * The base implementation of `_.has` without support for deep paths.
+	 *
+	 * @private
+	 * @param {Object} [object] The object to query.
+	 * @param {Array|string} key The key to check.
+	 * @returns {boolean} Returns `true` if `key` exists, else `false`.
+	 */
+	function baseHas(object, key) {
+	  return object != null && hasOwnProperty.call(object, key);
+	}
+
+	module.exports = baseHas;
+
+
+/***/ },
+/* 105 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var castPath = __webpack_require__(53),
+	    isArguments = __webpack_require__(106),
+	    isArray = __webpack_require__(54),
+	    isIndex = __webpack_require__(108),
+	    isLength = __webpack_require__(109),
+	    toKey = __webpack_require__(102);
+
+	/**
+	 * Checks if `path` exists on `object`.
+	 *
+	 * @private
+	 * @param {Object} object The object to query.
+	 * @param {Array|string} path The path to check.
+	 * @param {Function} hasFunc The function to check properties.
+	 * @returns {boolean} Returns `true` if `path` exists, else `false`.
+	 */
+	function hasPath(object, path, hasFunc) {
+	  path = castPath(path, object);
+
+	  var index = -1,
+	      length = path.length,
+	      result = false;
+
+	  while (++index < length) {
+	    var key = toKey(path[index]);
+	    if (!(result = object != null && hasFunc(object, key))) {
+	      break;
+	    }
+	    object = object[key];
+	  }
+	  if (result || ++index != length) {
+	    return result;
+	  }
+	  length = object == null ? 0 : object.length;
+	  return !!length && isLength(length) && isIndex(key, length) &&
+	    (isArray(object) || isArguments(object));
+	}
+
+	module.exports = hasPath;
+
+
+/***/ },
+/* 106 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var baseIsArguments = __webpack_require__(107),
+	    isObjectLike = __webpack_require__(63);
+
+	/** Used for built-in method references. */
+	var objectProto = Object.prototype;
+
+	/** Used to check objects for own properties. */
+	var hasOwnProperty = objectProto.hasOwnProperty;
+
+	/** Built-in value references. */
+	var propertyIsEnumerable = objectProto.propertyIsEnumerable;
+
+	/**
+	 * Checks if `value` is likely an `arguments` object.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 0.1.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is an `arguments` object,
+	 *  else `false`.
+	 * @example
+	 *
+	 * _.isArguments(function() { return arguments; }());
+	 * // => true
+	 *
+	 * _.isArguments([1, 2, 3]);
+	 * // => false
+	 */
+	var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) {
+	  return isObjectLike(value) && hasOwnProperty.call(value, 'callee') &&
+	    !propertyIsEnumerable.call(value, 'callee');
+	};
+
+	module.exports = isArguments;
+
+
+/***/ },
+/* 107 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var baseGetTag = __webpack_require__(57),
+	    isObjectLike = __webpack_require__(63);
+
+	/** `Object#toString` result references. */
+	var argsTag = '[object Arguments]';
+
+	/**
+	 * The base implementation of `_.isArguments`.
+	 *
+	 * @private
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is an `arguments` object,
+	 */
+	function baseIsArguments(value) {
+	  return isObjectLike(value) && baseGetTag(value) == argsTag;
+	}
+
+	module.exports = baseIsArguments;
+
+
+/***/ },
+/* 108 */
+/***/ function(module, exports) {
+
+	/** Used as references for various `Number` constants. */
+	var MAX_SAFE_INTEGER = 9007199254740991;
+
+	/** Used to detect unsigned integer values. */
+	var reIsUint = /^(?:0|[1-9]\d*)$/;
+
+	/**
+	 * Checks if `value` is a valid array-like index.
+	 *
+	 * @private
+	 * @param {*} value The value to check.
+	 * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
+	 * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
+	 */
+	function isIndex(value, length) {
+	  length = length == null ? MAX_SAFE_INTEGER : length;
+	  return !!length &&
+	    (typeof value == 'number' || reIsUint.test(value)) &&
+	    (value > -1 && value % 1 == 0 && value < length);
+	}
+
+	module.exports = isIndex;
+
+
+/***/ },
+/* 109 */
+/***/ function(module, exports) {
+
+	/** Used as references for various `Number` constants. */
+	var MAX_SAFE_INTEGER = 9007199254740991;
+
+	/**
+	 * Checks if `value` is a valid array-like length.
+	 *
+	 * **Note:** This method is loosely based on
+	 * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.0.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
+	 * @example
+	 *
+	 * _.isLength(3);
+	 * // => true
+	 *
+	 * _.isLength(Number.MIN_VALUE);
+	 * // => false
+	 *
+	 * _.isLength(Infinity);
+	 * // => false
+	 *
+	 * _.isLength('3');
+	 * // => false
+	 */
+	function isLength(value) {
+	  return typeof value == 'number' &&
+	    value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
+	}
+
+	module.exports = isLength;
+
+
 /***/ }
 /******/ ])
 });
 ;
\ No newline at end of file
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_context_menu_store_as_global.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_context_menu_store_as_global.js
@@ -54,17 +54,17 @@ add_task(function* () {
      "Correct variable assigned into console");
 
   info("Check store as global variable is enabled for top object in nested messages");
   yield storeAsVariable(hud, topObjInMsg);
 
   is(hud.jsterm.getInputValue(), "temp1", "Input was set");
 
   executedResult = yield hud.jsterm.execute();
-  ok(executedResult.textContent.includes("[ \"foo\", Object, 2 ]"),
+  ok(executedResult.textContent.includes(`[ "foo", {\u2026}, 2 ]`),
      "Correct variable assigned into console " + executedResult.textContent);
 
   info("Check store as global variable is enabled for nested object in nested messages");
   yield storeAsVariable(hud, nestedObjInMsg);
 
   is(hud.jsterm.getInputValue(), "temp2", "Input was set");
 
   executedResult = yield hud.jsterm.execute();