Bug 1513505 - Fix invoke getter on prototype's property; r=jdescottes a=RyanVM
authorNicolas Chevobbe <nchevobbe@mozilla.com>
Wed, 12 Dec 2018 14:50:16 +0000
changeset 506228 6f8e2bdfd8b61347c2ccd7a29841ef277d055d04
parent 506227 0d66c5be649b285f6881428dc3029d5281c93018
child 506229 9eff0feefcf92ed0cd1232c484686108965f4474
push id10336
push usercsabou@mozilla.com
push dateFri, 14 Dec 2018 21:17:48 +0000
treeherdermozilla-beta@6f8e2bdfd8b6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdescottes, RyanVM
bugs1513505
milestone65.0
Bug 1513505 - Fix invoke getter on prototype's property; r=jdescottes a=RyanVM This lands the fix done in the debugger Reps for ObjectInspector (https://github.com/devtools-html/debugger.html/pull/7484\), and add a test to ensure we don't regress this. We take this as an opportunity to put some object inspector helpers in head.js so we don't repeat ourselves too much. Differential Revision: https://phabricator.services.mozilla.com/D14240
devtools/client/shared/components/reps/reps.js
devtools/client/webconsole/test/mochitest/browser.ini
devtools/client/webconsole/test/mochitest/browser_webconsole_object_inspector_getters.js
devtools/client/webconsole/test/mochitest/browser_webconsole_object_inspector_getters_prototype.js
devtools/client/webconsole/test/mochitest/head.js
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -1,18 +1,18 @@
 (function webpackUniversalModuleDefinition(root, factory) {
 	if(typeof exports === 'object' && typeof module === 'object')
-		module.exports = factory(require("devtools/client/shared/vendor/react"), require("Services"), require("devtools/client/shared/vendor/react-redux"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react-dom-factories"));
+		module.exports = factory(require("devtools/client/shared/vendor/react"), require("Services"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/react-redux"));
 	else if(typeof define === 'function' && define.amd)
-		define(["devtools/client/shared/vendor/react", "Services", "devtools/client/shared/vendor/react-redux", "devtools/client/shared/vendor/react-prop-types", "devtools/client/shared/vendor/react-dom-factories"], factory);
+		define(["devtools/client/shared/vendor/react", "Services", "devtools/client/shared/vendor/react-prop-types", "devtools/client/shared/vendor/react-dom-factories", "devtools/client/shared/vendor/react-redux"], factory);
 	else {
-		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react"), require("Services"), require("devtools/client/shared/vendor/react-redux"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react-dom-factories")) : factory(root["devtools/client/shared/vendor/react"], root["Services"], root["devtools/client/shared/vendor/react-redux"], root["devtools/client/shared/vendor/react-prop-types"], root["devtools/client/shared/vendor/react-dom-factories"]);
+		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react"), require("Services"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/react-redux")) : factory(root["devtools/client/shared/vendor/react"], root["Services"], root["devtools/client/shared/vendor/react-prop-types"], root["devtools/client/shared/vendor/react-dom-factories"], root["devtools/client/shared/vendor/react-redux"]);
 		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
 	}
-})(typeof self !== 'undefined' ? self : this, function(__WEBPACK_EXTERNAL_MODULE_0__, __WEBPACK_EXTERNAL_MODULE_22__, __WEBPACK_EXTERNAL_MODULE_3592__, __WEBPACK_EXTERNAL_MODULE_3642__, __WEBPACK_EXTERNAL_MODULE_3643__) {
+})(typeof self !== 'undefined' ? self : this, function(__WEBPACK_EXTERNAL_MODULE_0__, __WEBPACK_EXTERNAL_MODULE_22__, __WEBPACK_EXTERNAL_MODULE_1758__, __WEBPACK_EXTERNAL_MODULE_1759__, __WEBPACK_EXTERNAL_MODULE_1763__) {
 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 = 3730);
+/******/ 	return __webpack_require__(__webpack_require__.s = 2104);
 /******/ })
 /************************************************************************/
 /******/ ({
 
 /***/ 0:
 /***/ (function(module, exports) {
 
 module.exports = __WEBPACK_EXTERNAL_MODULE_0__;
@@ -137,59 +137,45 @@ var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBP
 	} else {
 		window.classNames = classNames;
 	}
 }());
 
 
 /***/ }),
 
-/***/ 22:
+/***/ 1758:
 /***/ (function(module, exports) {
 
-module.exports = __WEBPACK_EXTERNAL_MODULE_22__;
-
-/***/ }),
-
-/***/ 3592:
-/***/ (function(module, exports) {
-
-module.exports = __WEBPACK_EXTERNAL_MODULE_3592__;
+module.exports = __WEBPACK_EXTERNAL_MODULE_1758__;
 
 /***/ }),
 
-/***/ 3642:
+/***/ 1759:
 /***/ (function(module, exports) {
 
-module.exports = __WEBPACK_EXTERNAL_MODULE_3642__;
+module.exports = __WEBPACK_EXTERNAL_MODULE_1759__;
 
 /***/ }),
 
-/***/ 3643:
-/***/ (function(module, exports) {
-
-module.exports = __WEBPACK_EXTERNAL_MODULE_3643__;
-
-/***/ }),
-
-/***/ 3644:
+/***/ 1760:
 /***/ (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 validProtocols = /(http|https|ftp|data|resource|chrome):/i;
 const tokenSplitRegex = /(\s|\'|\"|\\)+/;
 const ELLIPSIS = "\u2026";
-const dom = __webpack_require__(3643);
+const dom = __webpack_require__(1759);
 const { span } = dom;
 
 /**
  * Returns true if the given object is a grip (see RDP protocol)
  */
 function isGrip(object) {
   return object && object.actor;
 }
@@ -598,17 +584,17 @@ module.exports = {
   getGripType,
   tokenSplitRegex,
   ellipsisElement,
   ELLIPSIS
 };
 
 /***/ }),
 
-/***/ 3645:
+/***/ 1762:
 /***/ (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/>. */
@@ -618,62 +604,69 @@ module.exports = {
     TINY: Symbol("TINY"),
     SHORT: Symbol("SHORT"),
     LONG: Symbol("LONG")
   }
 };
 
 /***/ }),
 
-/***/ 3647:
+/***/ 1763:
+/***/ (function(module, exports) {
+
+module.exports = __WEBPACK_EXTERNAL_MODULE_1763__;
+
+/***/ }),
+
+/***/ 1767:
 /***/ (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/>. */
 
-__webpack_require__(3672);
+__webpack_require__(1830);
 
 // Load all existing rep templates
-const Undefined = __webpack_require__(3673);
-const Null = __webpack_require__(3674);
-const StringRep = __webpack_require__(3648);
-const Number = __webpack_require__(3675);
-const ArrayRep = __webpack_require__(3649);
-const Obj = __webpack_require__(3676);
-const SymbolRep = __webpack_require__(3677);
-const InfinityRep = __webpack_require__(3678);
-const NaNRep = __webpack_require__(3679);
-const Accessor = __webpack_require__(3680);
+const Undefined = __webpack_require__(1831);
+const Null = __webpack_require__(1832);
+const StringRep = __webpack_require__(1770);
+const Number = __webpack_require__(1833);
+const ArrayRep = __webpack_require__(1774);
+const Obj = __webpack_require__(1834);
+const SymbolRep = __webpack_require__(1835);
+const InfinityRep = __webpack_require__(1836);
+const NaNRep = __webpack_require__(1837);
+const Accessor = __webpack_require__(1838);
 
 // DOM types (grips)
-const Accessible = __webpack_require__(3787);
-const Attribute = __webpack_require__(3681);
-const DateTime = __webpack_require__(3682);
-const Document = __webpack_require__(3683);
-const DocumentType = __webpack_require__(3684);
-const Event = __webpack_require__(3685);
-const Func = __webpack_require__(3658);
-const PromiseRep = __webpack_require__(3686);
-const RegExp = __webpack_require__(3687);
-const StyleSheet = __webpack_require__(3688);
-const CommentNode = __webpack_require__(3689);
-const ElementNode = __webpack_require__(3690);
-const TextNode = __webpack_require__(3691);
-const ErrorRep = __webpack_require__(3660);
-const Window = __webpack_require__(3692);
-const ObjectWithText = __webpack_require__(3693);
-const ObjectWithURL = __webpack_require__(3694);
-const GripArray = __webpack_require__(3661);
-const GripMap = __webpack_require__(3663);
-const GripMapEntry = __webpack_require__(3664);
-const Grip = __webpack_require__(3656);
+const Accessible = __webpack_require__(1839);
+const Attribute = __webpack_require__(1840);
+const DateTime = __webpack_require__(1841);
+const Document = __webpack_require__(1842);
+const DocumentType = __webpack_require__(1843);
+const Event = __webpack_require__(1844);
+const Func = __webpack_require__(1794);
+const PromiseRep = __webpack_require__(1845);
+const RegExp = __webpack_require__(1846);
+const StyleSheet = __webpack_require__(1847);
+const CommentNode = __webpack_require__(1848);
+const ElementNode = __webpack_require__(1849);
+const TextNode = __webpack_require__(1850);
+const ErrorRep = __webpack_require__(1796);
+const Window = __webpack_require__(1851);
+const ObjectWithText = __webpack_require__(1852);
+const ObjectWithURL = __webpack_require__(1853);
+const GripArray = __webpack_require__(1797);
+const GripMap = __webpack_require__(1799);
+const GripMapEntry = __webpack_require__(1800);
+const Grip = __webpack_require__(1784);
 
 // List of all registered template.
 // XXX there should be a way for extensions to register a new
 // or modify an existing rep.
 const reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, Accessible, ElementNode, TextNode, Attribute, Func, PromiseRep, ArrayRep, Document, DocumentType, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, GripMapEntry, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep, Accessor, Obj];
 
 /**
  * Generic rep that is used for rendering native JS types or an object.
@@ -758,43 +751,43 @@ module.exports = {
     Window
   },
   // Exporting for tests
   getRep
 };
 
 /***/ }),
 
-/***/ 3648:
+/***/ 1770:
 /***/ (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__(3642);
+const PropTypes = __webpack_require__(1758);
 
 const {
   containsURL,
   isURL,
   escapeString,
   getGripType,
   rawCropString,
   sanitizeString,
   wrapRender,
   isGrip,
   tokenSplitRegex,
   ELLIPSIS
-} = __webpack_require__(3644);
-
-const dom = __webpack_require__(3643);
+} = __webpack_require__(1760);
+
+const dom = __webpack_require__(1759);
 const { a, span } = dom;
 
 /**
  * Renders a string. String value is enclosed within quotes.
  */
 StringRep.propTypes = {
   useQuotes: PropTypes.bool,
   escapeWhitespace: PropTypes.bool,
@@ -1032,33 +1025,33 @@ function supportsObject(object, noGrip =
 module.exports = {
   rep: wrapRender(StringRep),
   supportsObject,
   isLongString
 };
 
 /***/ }),
 
-/***/ 3649:
+/***/ 1774:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 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; };
 
 /* 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__(3643);
-const PropTypes = __webpack_require__(3642);
-const { wrapRender } = __webpack_require__(3644);
-const { MODE } = __webpack_require__(3645);
+const dom = __webpack_require__(1759);
+const PropTypes = __webpack_require__(1758);
+const { wrapRender } = __webpack_require__(1760);
+const { MODE } = __webpack_require__(1762);
 const { span } = dom;
 
 const ModePropType = PropTypes.oneOf(
 // @TODO Change this to Object.values when 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
@@ -1140,17 +1133,17 @@ function arrayIterator(props, array, max
  */
 ItemRep.propTypes = {
   object: PropTypes.any.isRequired,
   delim: PropTypes.string.isRequired,
   mode: ModePropType
 };
 
 function ItemRep(props) {
-  const { Rep } = __webpack_require__(3647);
+  const { Rep } = __webpack_require__(1767);
 
   const { object, delim, mode } = props;
   return span({}, Rep(_extends({}, props, {
     object: object,
     mode: mode
   })), delim);
 }
 
@@ -1172,34 +1165,34 @@ module.exports = {
   supportsObject,
   maxLengthMap,
   getLength,
   ModePropType
 };
 
 /***/ }),
 
-/***/ 3650:
+/***/ 1775:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 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; };
 
 /* 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__(3642);
-const { maybeEscapePropertyName, wrapRender } = __webpack_require__(3644);
-const { MODE } = __webpack_require__(3645);
-
-const { span } = __webpack_require__(3643);
+const PropTypes = __webpack_require__(1758);
+const { maybeEscapePropertyName, wrapRender } = __webpack_require__(1760);
+const { MODE } = __webpack_require__(1762);
+
+const { span } = __webpack_require__(1759);
 
 /**
  * Property for Obj (local JS objects), Grip (remote JS objects)
  * and GripMap (remote JS maps and weakmaps) reps.
  * It's used to render object properties.
  */
 PropRep.propTypes = {
   // Property name.
@@ -1220,18 +1213,18 @@ 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__(3656);
-  const { Rep } = __webpack_require__(3647);
+  const Grip = __webpack_require__(1784);
+  const { Rep } = __webpack_require__(1767);
 
   let { name, mode, equal, suppressQuotes } = props;
 
   let key;
   // The key can be a simple string, for plain objects,
   // or another object for maps and weakmaps.
   if (typeof name === "string") {
     if (!suppressQuotes) {
@@ -1252,71 +1245,71 @@ function PropRep(props) {
   }, equal), Rep(_extends({}, props))];
 }
 
 // Exports from this module
 module.exports = wrapRender(PropRep);
 
 /***/ }),
 
-/***/ 3655:
+/***/ 1780:
 /***/ (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__(3645);
-const { REPS, getRep } = __webpack_require__(3647);
-const objectInspector = __webpack_require__(3695);
+const { MODE } = __webpack_require__(1762);
+const { REPS, getRep } = __webpack_require__(1767);
+const objectInspector = __webpack_require__(1854);
 
 const {
   parseURLEncodedText,
   parseURLParams,
   maybeEscapePropertyName,
   getGripPreviewItems
-} = __webpack_require__(3644);
+} = __webpack_require__(1760);
 
 module.exports = {
   REPS,
   getRep,
   MODE,
   maybeEscapePropertyName,
   parseURLEncodedText,
   parseURLParams,
   getGripPreviewItems,
   objectInspector
 };
 
 /***/ }),
 
-/***/ 3656:
+/***/ 1784:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 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; };
 
 /* 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 PropTypes = __webpack_require__(3642);
+const PropTypes = __webpack_require__(1758);
 
 // Dependencies
-const { interleave, isGrip, wrapRender } = __webpack_require__(3644);
-const PropRep = __webpack_require__(3650);
-const { MODE } = __webpack_require__(3645);
-
-const dom = __webpack_require__(3643);
+const { interleave, isGrip, wrapRender } = __webpack_require__(1760);
+const PropRep = __webpack_require__(1775);
+const { MODE } = __webpack_require__(1762);
+
+const dom = __webpack_require__(1759);
 const { span } = dom;
 
 /**
  * Renders generic grip. Grip is client representation
  * of remote JS object and is used as an input object
  * for this rep component.
  */
 GripRep.propTypes = {
@@ -1402,17 +1395,17 @@ function safePropIterator(props, object,
   } catch (err) {
     console.error(err);
   }
   return [];
 }
 
 function propIterator(props, object, max) {
   if (object.preview && Object.keys(object.preview).includes("wrappedValue")) {
-    const { Rep } = __webpack_require__(3647);
+    const { Rep } = __webpack_require__(1767);
 
     return [Rep({
       object: object.preview.wrappedValue,
       mode: props.mode || MODE.TINY,
       defaultRep: Grip
     })];
   }
 
@@ -1591,1337 +1584,35 @@ const Grip = {
   maxLengthMap
 };
 
 // Exports from this module
 module.exports = Grip;
 
 /***/ }),
 
-/***/ 3657:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-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; };
-
-/* 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__(3665);
-const loadProperties = __webpack_require__(3666);
-const node = __webpack_require__(3667);
-const { nodeIsError, nodeIsPrimitive } = node;
-const selection = __webpack_require__(3698);
-
-const { MODE } = __webpack_require__(3645);
-const {
-  REPS: { Rep, Grip }
-} = __webpack_require__(3647);
-
-
-function shouldRenderRootsInReps(roots) {
-  if (roots.length > 1) {
-    return false;
-  }
-
-  const root = roots[0];
-  const name = root && root.name;
-  return (name === null || typeof name === "undefined") && (nodeIsPrimitive(root) || nodeIsError(root));
-}
-
-function renderRep(item, props) {
-  return Rep(_extends({}, props, {
-    object: node.getValue(item),
-    mode: props.mode || MODE.TINY,
-    defaultRep: Grip
-  }));
-}
-
-module.exports = {
-  client,
-  loadProperties,
-  node,
-  renderRep,
-  selection,
-  shouldRenderRootsInReps
-};
-
-/***/ }),
-
-/***/ 3658:
-/***/ (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/>. */
-
-// ReactJS
-const PropTypes = __webpack_require__(3642);
-
-// Reps
-const { getGripType, isGrip, cropString, wrapRender } = __webpack_require__(3644);
-const { MODE } = __webpack_require__(3645);
-
-const dom = __webpack_require__(3643);
-const { span } = dom;
-
-const IGNORED_SOURCE_URLS = ["debugger eval code"];
-
-/**
- * This component represents a template for Function objects.
- */
-FunctionRep.propTypes = {
-  object: PropTypes.object.isRequired,
-  parameterNames: PropTypes.array,
-  onViewSourceInDebugger: PropTypes.func
-};
-
-function FunctionRep(props) {
-  const { object: grip, onViewSourceInDebugger, recordTelemetryEvent } = props;
-
-  let jumpToDefinitionButton;
-  if (onViewSourceInDebugger && grip.location && grip.location.url && !IGNORED_SOURCE_URLS.includes(grip.location.url)) {
-    jumpToDefinitionButton = dom.button({
-      className: "jump-definition",
-      draggable: false,
-      title: "Jump to definition",
-      onClick: e => {
-        // Stop the event propagation so we don't trigger ObjectInspector
-        // expand/collapse.
-        e.stopPropagation();
-        if (recordTelemetryEvent) {
-          recordTelemetryEvent("jump_to_definition");
-        }
-        onViewSourceInDebugger(grip.location);
-      }
-    });
-  }
-
-  return span({
-    "data-link-actor-id": grip.actor,
-    className: "objectBox objectBox-function",
-    // Set dir="ltr" to prevent function parentheses from
-    // appearing in the wrong direction
-    dir: "ltr"
-  }, getTitle(grip, props), getFunctionName(grip, props), "(", ...renderParams(props), ")", jumpToDefinitionButton);
-}
-
-function getTitle(grip, props) {
-  const { mode } = props;
-
-  if (mode === MODE.TINY && !grip.isGenerator && !grip.isAsync) {
-    return null;
-  }
-
-  let title = mode === MODE.TINY ? "" : "function ";
-
-  if (grip.isGenerator) {
-    title = mode === MODE.TINY ? "* " : "function* ";
-  }
-
-  if (grip.isAsync) {
-    title = `${"async" + " "}${title}`;
-  }
-
-  return span({
-    className: "objectTitle"
-  }, title);
-}
-
-/**
- * Returns a ReactElement representing the function name.
- *
- * @param {Object} grip : Function grip
- * @param {Object} props: Function rep props
- */
-function getFunctionName(grip, props = {}) {
-  let { functionName } = props;
-  let name;
-
-  if (functionName) {
-    const end = functionName.length - 1;
-    functionName = functionName.startsWith('"') && functionName.endsWith('"') ? functionName.substring(1, end) : functionName;
-  }
-
-  if (grip.displayName != undefined && functionName != undefined && grip.displayName != functionName) {
-    name = `${functionName}:${grip.displayName}`;
-  } else {
-    name = cleanFunctionName(grip.userDisplayName || grip.displayName || grip.name || props.functionName || "");
-  }
-
-  return cropString(name, 100);
-}
-
-const objectProperty = /([\w\d]+)$/;
-const arrayProperty = /\[(.*?)\]$/;
-const functionProperty = /([\w\d]+)[\/\.<]*?$/;
-const annonymousProperty = /([\w\d]+)\(\^\)$/;
-
-/**
- * Decodes an anonymous naming scheme that
- * spider monkey implements based on "Naming Anonymous JavaScript Functions"
- * http://johnjbarton.github.io/nonymous/index.html
- *
- * @param {String} name : Function name to clean up
- * @returns String
- */
-function cleanFunctionName(name) {
-  for (const reg of [objectProperty, arrayProperty, functionProperty, annonymousProperty]) {
-    const match = reg.exec(name);
-    if (match) {
-      return match[1];
-    }
-  }
-
-  return name;
-}
-
-function renderParams(props) {
-  const { parameterNames = [] } = props;
-
-  return parameterNames.filter(param => param).reduce((res, param, index, arr) => {
-    res.push(span({ className: "param" }, param));
-    if (index < arr.length - 1) {
-      res.push(span({ className: "delimiter" }, ", "));
-    }
-    return res;
-  }, []);
-}
-
-// Registration
-function supportsObject(grip, noGrip = false) {
-  const type = getGripType(grip, noGrip);
-  if (noGrip === true || !isGrip(grip)) {
-    return type == "function";
-  }
-
-  return type == "Function";
-}
-
-// Exports from this module
-
-module.exports = {
-  rep: wrapRender(FunctionRep),
-  supportsObject,
-  cleanFunctionName,
-  // exported for testing purpose.
-  getFunctionName
-};
-
-/***/ }),
-
-/***/ 3659:
-/***/ (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/>. */
-
-module.exports = {
-  ELEMENT_NODE: 1,
-  ATTRIBUTE_NODE: 2,
-  TEXT_NODE: 3,
-  CDATA_SECTION_NODE: 4,
-  ENTITY_REFERENCE_NODE: 5,
-  ENTITY_NODE: 6,
-  PROCESSING_INSTRUCTION_NODE: 7,
-  COMMENT_NODE: 8,
-  DOCUMENT_NODE: 9,
-  DOCUMENT_TYPE_NODE: 10,
-  DOCUMENT_FRAGMENT_NODE: 11,
-  NOTATION_NODE: 12,
-
-  // DocumentPosition
-  DOCUMENT_POSITION_DISCONNECTED: 0x01,
-  DOCUMENT_POSITION_PRECEDING: 0x02,
-  DOCUMENT_POSITION_FOLLOWING: 0x04,
-  DOCUMENT_POSITION_CONTAINS: 0x08,
-  DOCUMENT_POSITION_CONTAINED_BY: 0x10,
-  DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 0x20
-};
-
-/***/ }),
-
-/***/ 3660:
-/***/ (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/>. */
-
-// ReactJS
-const PropTypes = __webpack_require__(3642);
-// Utils
-const { getGripType, isGrip, wrapRender } = __webpack_require__(3644);
-const { cleanFunctionName } = __webpack_require__(3658);
-const { isLongString } = __webpack_require__(3648);
-const { MODE } = __webpack_require__(3645);
-
-const dom = __webpack_require__(3643);
-const { span } = dom;
-const IGNORED_SOURCE_URLS = ["debugger eval code"];
-
-/**
- * Renders Error objects.
- */
-ErrorRep.propTypes = {
-  object: PropTypes.object.isRequired,
-  // @TODO Change this to Object.values when supported in Node's version of V8
-  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
-  // An optional function that will be used to render the Error stacktrace.
-  renderStacktrace: PropTypes.func
-};
-
-function ErrorRep(props) {
-  const object = props.object;
-  const preview = object.preview;
-
-  let name;
-  if (preview && preview.name && preview.kind) {
-    switch (preview.kind) {
-      case "Error":
-        name = preview.name;
-        break;
-      case "DOMException":
-        name = preview.kind;
-        break;
-      default:
-        throw new Error("Unknown preview kind for the Error rep.");
-    }
-  } else {
-    name = "Error";
-  }
-
-  const content = [];
-
-  if (props.mode === MODE.TINY) {
-    content.push(name);
-  } else {
-    content.push(`${name}: "${preview.message}"`);
-  }
-
-  if (preview.stack && props.mode !== MODE.TINY) {
-    const stacktrace = props.renderStacktrace ? props.renderStacktrace(parseStackString(preview.stack)) : getStacktraceElements(props, preview);
-    content.push("\n", stacktrace);
-  }
-
-  return span({
-    "data-link-actor-id": object.actor,
-    className: "objectBox-stackTrace"
-  }, content);
-}
-
-/**
- * Returns a React element reprensenting the Error stacktrace, i.e.
- * transform error.stack from:
- *
- * semicolon@debugger eval code:1:109
- * jkl@debugger eval code:1:63
- * asdf@debugger eval code:1:28
- * @debugger eval code:1:227
- *
- * Into a column layout:
- *
- * semicolon  (<anonymous>:8:10)
- * jkl        (<anonymous>:5:10)
- * asdf       (<anonymous>:2:10)
- *            (<anonymous>:11:1)
- */
-function getStacktraceElements(props, preview) {
-  const stack = [];
-  if (!preview.stack) {
-    return stack;
-  }
-
-  parseStackString(preview.stack).forEach((frame, index, frames) => {
-    let onLocationClick;
-    const {
-      filename,
-      lineNumber,
-      columnNumber,
-      functionName,
-      location
-    } = frame;
-
-    if (props.onViewSourceInDebugger && !IGNORED_SOURCE_URLS.includes(filename)) {
-      onLocationClick = e => {
-        // Don't trigger ObjectInspector expand/collapse.
-        e.stopPropagation();
-        props.onViewSourceInDebugger({
-          url: filename,
-          line: lineNumber,
-          column: columnNumber
-        });
-      };
-    }
-
-    stack.push("\t", span({
-      key: `fn${index}`,
-      className: "objectBox-stackTrace-fn"
-    }, cleanFunctionName(functionName)), " ", span({
-      key: `location${index}`,
-      className: "objectBox-stackTrace-location",
-      onClick: onLocationClick,
-      title: onLocationClick ? `View source in debugger → ${location}` : undefined
-    }, location), "\n");
-  });
-
-  return span({
-    key: "stack",
-    className: "objectBox-stackTrace-grid"
-  }, stack);
-}
-
-/**
- * Parse a string that should represent a stack trace and returns an array of
- * the frames. The shape of the frames are extremely important as they can then
- * be processed here or in the toolbox by other components.
- * @param {String} stack
- * @returns {Array} Array of frames, which are object with the following shape:
- *                  - {String} filename
- *                  - {String} functionName
- *                  - {String} location
- *                  - {Number} columnNumber
- *                  - {Number} lineNumber
- */
-function parseStackString(stack) {
-  const res = [];
-  if (!stack) {
-    return res;
-  }
-
-  const isStacktraceALongString = isLongString(stack);
-  const stackString = isStacktraceALongString ? stack.initial : stack;
-
-  stackString.split("\n").forEach((frame, index, frames) => {
-    if (!frame) {
-      // Skip any blank lines
-      return;
-    }
-
-    // If the stacktrace is a longString, don't include the last frame in the
-    // array, since it is certainly incomplete.
-    // Can be removed when https://bugzilla.mozilla.org/show_bug.cgi?id=1448833
-    // is fixed.
-    if (isStacktraceALongString && index === frames.length - 1) {
-      return;
-    }
-
-    let functionName;
-    let location;
-
-    // Given the input: "functionName@scriptLocation:2:100"
-    // Result: [
-    //   "functionName@scriptLocation:2:100",
-    //   "functionName",
-    //   "scriptLocation:2:100"
-    // ]
-    const result = frame.match(/^(.*)@(.*)$/);
-    if (result && result.length === 3) {
-      functionName = result[1];
-
-      // If the resource was loaded by base-loader.js, the location looks like:
-      // resource://devtools/shared/base-loader.js -> resource://path/to/file.js .
-      // What's needed is only the last part after " -> ".
-      location = result[2].split(" -> ").pop();
-    }
-
-    if (!functionName) {
-      functionName = "<anonymous>";
-    }
-
-    // Given the input: "scriptLocation:2:100"
-    // Result:
-    // ["scriptLocation:2:100", "scriptLocation", "2", "100"]
-    const locationParts = location.match(/^(.*):(\d+):(\d+)$/);
-
-    if (location && locationParts) {
-      const [, filename, line, column] = locationParts;
-      res.push({
-        filename,
-        functionName,
-        location,
-        columnNumber: Number(column),
-        lineNumber: Number(line)
-      });
-    }
-  });
-
-  return res;
-}
-
-// Registration
-function supportsObject(object, noGrip = false) {
-  if (noGrip === true || !isGrip(object)) {
-    return false;
-  }
-  return object.preview && getGripType(object, noGrip) === "Error" || object.class === "DOMException";
-}
-
-// Exports from this module
-module.exports = {
-  rep: wrapRender(ErrorRep),
-  supportsObject
-};
-
-/***/ }),
-
-/***/ 3661:
+/***/ 1785:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 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; };
 
 /* 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__(3642);
-
-const { lengthBubble } = __webpack_require__(3662);
-const {
-  interleave,
-  getGripType,
-  isGrip,
-  wrapRender,
-  ellipsisElement
-} = __webpack_require__(3644);
-const { MODE } = __webpack_require__(3645);
-
-const dom = __webpack_require__(3643);
-const { span } = dom;
-const { ModePropType } = __webpack_require__(3649);
-const DEFAULT_TITLE = "Array";
-
-/**
- * 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 when supported in Node's version of V8
-  mode: ModePropType,
-  provider: PropTypes.object,
-  onDOMNodeMouseOver: PropTypes.func,
-  onDOMNodeMouseOut: PropTypes.func,
-  onInspectIconClick: PropTypes.func
-};
-
-function GripArray(props) {
-  const { object, mode = MODE.SHORT } = props;
-
-  let brackets;
-  const 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) {
-    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);
-    }
-
-    brackets = needSpace(false);
-    return span(config, title, span({
-      className: "arrayLeftBracket"
-    }, brackets.left), isEmpty ? null : ellipsisElement, span({
-      className: "arrayRightBracket"
-    }, brackets.right));
-  }
-
-  const max = maxLengthMap.get(mode);
-  const items = arrayIterator(props, object, max);
-  brackets = needSpace(items.length > 0);
-
-  return span({
-    "data-link-actor-id": object.actor,
-    className: "objectBox objectBox-array"
-  }, title, span({
-    className: "arrayLeftBracket"
-  }, brackets.left), ...interleave(items, ", "), span({
-    className: "arrayRightBracket"
-  }, brackets.right), span({
-    className: "arrayProperties",
-    role: "group"
-  }));
-}
-
-function getLength(grip) {
-  if (!grip.preview) {
-    return 0;
-  }
-
-  return grip.preview.length || grip.preview.childNodesLength || 0;
-}
-
-function getTitle(props, object) {
-  const objectLength = getLength(object);
-  const isEmpty = objectLength === 0;
-
-  let title = props.title || object.class || DEFAULT_TITLE;
-
-  const length = lengthBubble({
-    object,
-    mode: props.mode,
-    maxLengthMap,
-    getLength
-  });
-
-  if (props.mode === MODE.TINY) {
-    if (isEmpty) {
-      if (object.class === DEFAULT_TITLE) {
-        return null;
-      }
-
-      return span({ className: "objectTitle" }, `${title} `);
-    }
-
-    let trailingSpace;
-    if (object.class === DEFAULT_TITLE) {
-      title = null;
-      trailingSpace = " ";
-    }
-
-    return span({ className: "objectTitle" }, title, length, trailingSpace);
-  }
-
-  return span({ className: "objectTitle" }, title, length, " ");
-}
-
-function getPreviewItems(grip) {
-  if (!grip.preview) {
-    return null;
-  }
-
-  return grip.preview.items || grip.preview.childNodes || [];
-}
-
-function arrayIterator(props, grip, max) {
-  const { Rep } = __webpack_require__(3647);
-
-  let items = [];
-  const gripLength = getLength(grip);
-
-  if (!gripLength) {
-    return items;
-  }
-
-  const previewItems = getPreviewItems(grip);
-  const provider = props.provider;
-
-  let emptySlots = 0;
-  let foldedEmptySlots = 0;
-  items = previewItems.reduce((res, itemGrip) => {
-    if (res.length >= max) {
-      return res;
-    }
-
-    let object;
-    try {
-      if (!provider && itemGrip === null) {
-        emptySlots++;
-        return res;
-      }
-
-      object = provider ? provider.getValue(itemGrip) : itemGrip;
-    } catch (exc) {
-      object = exc;
-    }
-
-    if (emptySlots > 0) {
-      res.push(getEmptySlotsElement(emptySlots));
-      foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
-      emptySlots = 0;
-    }
-
-    if (res.length < max) {
-      res.push(Rep(_extends({}, props, {
-        object,
-        mode: MODE.TINY,
-        // Do not propagate title to array items reps
-        title: undefined
-      })));
-    }
-
-    return res;
-  }, []);
-
-  // 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(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" : ""}>`;
-}
-
-function supportsObject(grip, noGrip = false) {
-  if (noGrip === true || !isGrip(grip)) {
-    return false;
-  }
-
-  return grip.preview && (grip.preview.kind == "ArrayLike" || getGripType(grip, noGrip) === "DocumentFragment");
-}
-
-const maxLengthMap = new Map();
-maxLengthMap.set(MODE.SHORT, 3);
-maxLengthMap.set(MODE.LONG, 10);
-
-// Exports from this module
-module.exports = {
-  rep: wrapRender(GripArray),
-  supportsObject,
-  maxLengthMap,
-  getLength
-};
-
-/***/ }),
-
-/***/ 3662:
-/***/ (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 PropTypes = __webpack_require__(3642);
-
-const { wrapRender } = __webpack_require__(3644);
-const { MODE } = __webpack_require__(3645);
-const { ModePropType } = __webpack_require__(3649);
-
-const dom = __webpack_require__(3643);
-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)
-};
-
-/***/ }),
-
-/***/ 3663:
-/***/ (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__(3662);
-const PropTypes = __webpack_require__(3642);
-const {
-  interleave,
-  isGrip,
-  wrapRender,
-  ellipsisElement
-} = __webpack_require__(3644);
-const PropRep = __webpack_require__(3650);
-const { MODE } = __webpack_require__(3645);
-const { ModePropType } = __webpack_require__(3649);
-
-const { span } = __webpack_require__(3643);
-
-/**
- * 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 when supported in Node's version of V8
-  mode: ModePropType,
-  isInterestingEntry: PropTypes.func,
-  onDOMNodeMouseOver: PropTypes.func,
-  onDOMNodeMouseOut: PropTypes.func,
-  onInspectIconClick: PropTypes.func,
-  title: PropTypes.string
-};
-
-function GripMap(props) {
-  const { mode, object } = props;
-
-  const config = {
-    "data-link-actor-id": object.actor,
-    className: "objectBox objectBox-object"
-  };
-
-  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"
-  }, " { "), ...interleave(propsArray, ", "), span({
-    className: "objectRightBrace"
-  }, " }"));
-}
-
-function getTitle(props, object) {
-  const title = props.title || (object && object.class ? object.class : "Map");
-  return span({
-    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);
-  }
-  return [];
-}
-
-function entriesIterator(props, object, max) {
-  // Entry filter. Show only interesting entries to the user.
-  const isInterestingEntry = props.isInterestingEntry || ((type, value) => {
-    return type == "boolean" || type == "number" || type == "string" && value.length != 0;
-  });
-
-  const mapEntries = object.preview && object.preview.entries ? object.preview.entries : [];
-
-  let indexes = getEntriesIndexes(mapEntries, max, isInterestingEntry);
-  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);
-    }));
-  }
-
-  const entries = getEntries(props, mapEntries, indexes);
-  if (entries.length < getLength(object)) {
-    // There are some undisplayed entries. Then display "…".
-    entries.push(ellipsisElement);
-  }
-
-  return entries;
-}
-
-/**
- * Get entries ordered by index.
- *
- * @param {Object} props Component props.
- * @param {Array} entries Entries array.
- * @param {Array} indexes Indexes of entries.
- * @return {Array} Array of PropRep.
- */
-function getEntries(props, entries, indexes) {
-  const { onDOMNodeMouseOver, onDOMNodeMouseOut, onInspectIconClick } = props;
-
-  // Make indexes ordered by ascending.
-  indexes.sort(function (a, b) {
-    return a - b;
-  });
-
-  return indexes.map((index, i) => {
-    const [key, entryValue] = entries[index];
-    const value = entryValue.value !== undefined ? entryValue.value : entryValue;
-
-    return PropRep({
-      name: key,
-      equal: " \u2192 ",
-      object: value,
-      mode: MODE.TINY,
-      onDOMNodeMouseOver,
-      onDOMNodeMouseOut,
-      onInspectIconClick
-    });
-  });
-}
-
-/**
- * Get the indexes of entries in the map.
- *
- * @param {Array} entries Entries array.
- * @param {Number} max The maximum length of indexes array.
- * @param {Function} filter Filter the entry you want.
- * @return {Array} Indexes of filtered entries in the map.
- */
-function getEntriesIndexes(entries, max, filter) {
-  return entries.reduce((indexes, [key, entry], i) => {
-    if (indexes.length < max) {
-      const value = entry && entry.value !== undefined ? entry.value : entry;
-      // Type is specified in grip's "class" field and for primitive
-      // values use typeof.
-      const type = (value && value.class ? value.class : typeof value).toLowerCase();
-
-      if (filter(type, value, key)) {
-        indexes.push(i);
-      }
-    }
-
-    return indexes;
-  }, []);
-}
-
-function getLength(grip) {
-  return grip.preview.size || 0;
-}
-
-function supportsObject(grip, noGrip = false) {
-  if (noGrip === true || !isGrip(grip)) {
-    return false;
-  }
-  return grip.preview && grip.preview.kind == "MapLike";
-}
-
-const maxLengthMap = new Map();
-maxLengthMap.set(MODE.SHORT, 3);
-maxLengthMap.set(MODE.LONG, 10);
-
-// Exports from this module
-module.exports = {
-  rep: wrapRender(GripMap),
-  supportsObject,
-  maxLengthMap,
-  getLength
-};
-
-/***/ }),
-
-/***/ 3664:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-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; };
-
-/* 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__(3642);
-// Shortcuts
-const dom = __webpack_require__(3643);
-const { span } = dom;
-const { wrapRender } = __webpack_require__(3644);
-const PropRep = __webpack_require__(3650);
-const { MODE } = __webpack_require__(3645);
-/**
- * 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 when supported in Node's version of V8
-  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
-  onDOMNodeMouseOver: PropTypes.func,
-  onDOMNodeMouseOut: PropTypes.func,
-  onInspectIconClick: PropTypes.func
-};
-
-function GripMapEntry(props) {
-  const { object } = props;
-
-  const { key, value } = object.preview;
-
-  return span({
-    className: "objectBox objectBox-map-entry"
-  }, PropRep(_extends({}, props, {
-    name: key,
-    object: value,
-    equal: " \u2192 ",
-    title: null,
-    suppressQuotes: false
-  })));
-}
-
-function supportsObject(grip, noGrip = false) {
-  if (noGrip === true) {
-    return false;
-  }
-  return grip && (grip.type === "mapEntry" || grip.type === "storageEntry") && grip.preview;
-}
-
-function createGripMapEntry(key, value) {
-  return {
-    type: "mapEntry",
-    preview: {
-      key,
-      value
-    }
-  };
-}
-
-// Exports from this module
-module.exports = {
-  rep: wrapRender(GripMapEntry),
-  createGripMapEntry,
-  supportsObject
-};
-
-/***/ }),
-
-/***/ 3665:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-const { getValue, nodeHasFullText } = __webpack_require__(3667); /* 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/>. */
-
-async function enumIndexedProperties(objectClient, start, end) {
-  try {
-    const { iterator } = await objectClient.enumProperties({
-      ignoreNonIndexedProperties: true
-    });
-    const response = await iteratorSlice(iterator, start, end);
-    return response;
-  } catch (e) {
-    console.error("Error in enumIndexedProperties", e);
-    return {};
-  }
-}
-
-async function enumNonIndexedProperties(objectClient, start, end) {
-  try {
-    const { iterator } = await objectClient.enumProperties({
-      ignoreIndexedProperties: true
-    });
-    const response = await iteratorSlice(iterator, start, end);
-    return response;
-  } catch (e) {
-    console.error("Error in enumNonIndexedProperties", e);
-    return {};
-  }
-}
-
-async function enumEntries(objectClient, start, end) {
-  try {
-    const { iterator } = await objectClient.enumEntries();
-    const response = await iteratorSlice(iterator, start, end);
-    return response;
-  } catch (e) {
-    console.error("Error in enumEntries", e);
-    return {};
-  }
-}
-
-async function enumSymbols(objectClient, start, end) {
-  try {
-    const { iterator } = await objectClient.enumSymbols();
-    const response = await iteratorSlice(iterator, start, end);
-    return response;
-  } catch (e) {
-    console.error("Error in enumSymbols", e);
-    return {};
-  }
-}
-
-async function getPrototype(objectClient) {
-  if (typeof objectClient.getPrototype !== "function") {
-    console.error("objectClient.getPrototype is not a function");
-    return Promise.resolve({});
-  }
-  return objectClient.getPrototype();
-}
-
-async function getFullText(longStringClient, item) {
-  const { initial, fullText, length } = getValue(item);
-
-  // Return fullText property if it exists so that it can be added to the
-  // loadedProperties map.
-  if (nodeHasFullText(item)) {
-    return Promise.resolve({ fullText });
-  }
-
-  return new Promise((resolve, reject) => {
-    longStringClient.substring(initial.length, length, response => {
-      if (response.error) {
-        console.error("LongStringClient.substring", `${response.error}: ${response.message}`);
-        reject({});
-        return;
-      }
-
-      resolve({
-        fullText: initial + response.substring
-      });
-    });
-  });
-}
-
-function iteratorSlice(iterator, start, end) {
-  start = start || 0;
-  const count = end ? end - start + 1 : iterator.count;
-
-  if (count === 0) {
-    return Promise.resolve({});
-  }
-  return iterator.slice(start, count);
-}
-
-module.exports = {
-  enumEntries,
-  enumIndexedProperties,
-  enumNonIndexedProperties,
-  enumSymbols,
-  getPrototype,
-  getFullText
-};
-
-/***/ }),
-
-/***/ 3666:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-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; };
-
-/* 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 {
-  enumEntries,
-  enumIndexedProperties,
-  enumNonIndexedProperties,
-  getPrototype,
-  enumSymbols,
-  getFullText
-} = __webpack_require__(3665);
-
-const {
-  getClosestGripNode,
-  getClosestNonBucketNode,
-  getValue,
-  nodeHasAccessors,
-  nodeHasAllEntriesInPreview,
-  nodeHasProperties,
-  nodeIsBucket,
-  nodeIsDefaultProperties,
-  nodeIsEntries,
-  nodeIsMapEntry,
-  nodeIsPrimitive,
-  nodeIsProxy,
-  nodeNeedsNumericalBuckets,
-  nodeIsLongString
-} = __webpack_require__(3667);
-
-function loadItemProperties(item, createObjectClient, createLongStringClient, loadedProperties) {
-  const gripItem = getClosestGripNode(item);
-  const value = getValue(gripItem);
-
-  const [start, end] = item.meta ? [item.meta.startIndex, item.meta.endIndex] : [];
-
-  const promises = [];
-  let objectClient;
-  const getObjectClient = () => objectClient || createObjectClient(value);
-
-  if (shouldLoadItemIndexedProperties(item, loadedProperties)) {
-    promises.push(enumIndexedProperties(getObjectClient(), start, end));
-  }
-
-  if (shouldLoadItemNonIndexedProperties(item, loadedProperties)) {
-    promises.push(enumNonIndexedProperties(getObjectClient(), start, end));
-  }
-
-  if (shouldLoadItemEntries(item, loadedProperties)) {
-    promises.push(enumEntries(getObjectClient(), start, end));
-  }
-
-  if (shouldLoadItemPrototype(item, loadedProperties)) {
-    promises.push(getPrototype(getObjectClient()));
-  }
-
-  if (shouldLoadItemSymbols(item, loadedProperties)) {
-    promises.push(enumSymbols(getObjectClient(), start, end));
-  }
-
-  if (shouldLoadItemFullText(item, loadedProperties)) {
-    promises.push(getFullText(createLongStringClient(value), item));
-  }
-
-  return Promise.all(promises).then(mergeResponses);
-}
-
-function mergeResponses(responses) {
-  const data = {};
-
-  for (const response of responses) {
-    if (response.hasOwnProperty("ownProperties")) {
-      data.ownProperties = _extends({}, data.ownProperties, response.ownProperties);
-    }
-
-    if (response.ownSymbols && response.ownSymbols.length > 0) {
-      data.ownSymbols = response.ownSymbols;
-    }
-
-    if (response.prototype) {
-      data.prototype = response.prototype;
-    }
-
-    if (response.fullText) {
-      data.fullText = response.fullText;
-    }
-  }
-
-  return data;
-}
-
-function shouldLoadItemIndexedProperties(item, loadedProperties = new Map()) {
-  const gripItem = getClosestGripNode(item);
-  const value = getValue(gripItem);
-
-  return value && nodeHasProperties(gripItem) && !loadedProperties.has(item.path) && !nodeIsProxy(item) && !nodeNeedsNumericalBuckets(item) && !nodeIsEntries(getClosestNonBucketNode(item)) &&
-  // The data is loaded when expanding the window node.
-  !nodeIsDefaultProperties(item);
-}
-
-function shouldLoadItemNonIndexedProperties(item, loadedProperties = new Map()) {
-  const gripItem = getClosestGripNode(item);
-  const value = getValue(gripItem);
-
-  return value && nodeHasProperties(gripItem) && !loadedProperties.has(item.path) && !nodeIsProxy(item) && !nodeIsEntries(getClosestNonBucketNode(item)) && !nodeIsBucket(item) &&
-  // The data is loaded when expanding the window node.
-  !nodeIsDefaultProperties(item);
-}
-
-function shouldLoadItemEntries(item, loadedProperties = new Map()) {
-  const gripItem = getClosestGripNode(item);
-  const value = getValue(gripItem);
-
-  return value && nodeIsEntries(getClosestNonBucketNode(item)) && !nodeHasAllEntriesInPreview(gripItem) && !loadedProperties.has(item.path) && !nodeNeedsNumericalBuckets(item);
-}
-
-function shouldLoadItemPrototype(item, loadedProperties = new Map()) {
-  const value = getValue(item);
-
-  return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item) && !nodeIsLongString(item);
-}
-
-function shouldLoadItemSymbols(item, loadedProperties = new Map()) {
-  const value = getValue(item);
-
-  return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item) && !nodeIsLongString(item) && !nodeIsProxy(item);
-}
-
-function shouldLoadItemFullText(item, loadedProperties = new Map()) {
-  return !loadedProperties.has(item.path) && nodeIsLongString(item);
-}
-
-module.exports = {
-  loadItemProperties,
-  mergeResponses,
-  shouldLoadItemEntries,
-  shouldLoadItemIndexedProperties,
-  shouldLoadItemNonIndexedProperties,
-  shouldLoadItemPrototype,
-  shouldLoadItemSymbols,
-  shouldLoadItemFullText
-};
-
-/***/ }),
-
-/***/ 3667:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-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; };
-
-/* 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 { maybeEscapePropertyName } = __webpack_require__(3644);
-const ArrayRep = __webpack_require__(3649);
-const GripArrayRep = __webpack_require__(3661);
-const GripMap = __webpack_require__(3663);
-const GripMapEntryRep = __webpack_require__(3664);
-const ErrorRep = __webpack_require__(3660);
-const { isLongString } = __webpack_require__(3648);
+const { maybeEscapePropertyName } = __webpack_require__(1760);
+const ArrayRep = __webpack_require__(1774);
+const GripArrayRep = __webpack_require__(1797);
+const GripMap = __webpack_require__(1799);
+const GripMapEntryRep = __webpack_require__(1800);
+const ErrorRep = __webpack_require__(1796);
+const { isLongString } = __webpack_require__(1770);
 
 const MAX_NUMERICAL_PROPERTIES = 100;
 
 const NODE_TYPES = {
   BUCKET: Symbol("[n…m]"),
   DEFAULT_PROPERTIES: Symbol("<default properties>"),
   ENTRIES: Symbol("<entries>"),
   GET: Symbol("<get>"),
@@ -3680,16 +2371,34 @@ function getClosestNonBucketNode(item) {
   const parent = getParent(item);
   if (!parent) {
     return null;
   }
 
   return getClosestNonBucketNode(parent);
 }
 
+function getNonPrototypeParentGripValue(item) {
+  const parentNode = getParent(item);
+  if (!parentNode) {
+    return null;
+  }
+
+  const parentGripNode = getClosestGripNode(parentNode);
+  if (!parentGripNode) {
+    return null;
+  }
+
+  if (getType(parentGripNode) === NODE_TYPES.PROTOTYPE) {
+    return getNonPrototypeParentGripValue(parentGripNode);
+  }
+
+  return getValue(parentGripNode);
+}
+
 function getParentGripValue(item) {
   const parentNode = getParent(item);
   if (!parentNode) {
     return null;
   }
 
   const parentGripNode = getClosestGripNode(parentNode);
   if (!parentGripNode) {
@@ -3705,16 +2414,17 @@ module.exports = {
   createSetterNode,
   getActor,
   getChildren,
   getChildrenWithEvaluations,
   getClosestGripNode,
   getClosestNonBucketNode,
   getParent,
   getParentGripValue,
+  getNonPrototypeParentGripValue,
   getNumericalPropertiesCount,
   getValue,
   makeNodesForEntries,
   makeNodesForPromiseProperties,
   makeNodesForProperties,
   makeNumericalBuckets,
   nodeHasAccessors,
   nodeHasAllEntriesInPreview,
@@ -3749,65 +2459,1208 @@ module.exports = {
   nodeSupportsNumericalBucketing,
   setNodeChildren,
   sortProperties,
   NODE_TYPES
 };
 
 /***/ }),
 
-/***/ 3669:
+/***/ 1786:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
-var _tree = __webpack_require__(3670);
+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; };
+
+function initialState() {
+  return {
+    expandedPaths: new Set(),
+    loadedProperties: new Map(),
+    evaluations: new Map(),
+    actors: new Set()
+  };
+} /* This Source Code Form is subject to the terms of the Mozilla Public
+   * License, v. 2.0. If a copy of the MPL was not distributed with this
+   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
+
+
+function reducer(state = initialState(), action = {}) {
+  const { type, data } = action;
+
+  const cloneState = overrides => _extends({}, state, overrides);
+
+  if (type === "NODE_EXPAND") {
+    return cloneState({
+      expandedPaths: new Set(state.expandedPaths).add(data.node.path)
+    });
+  }
+
+  if (type === "NODE_COLLAPSE") {
+    const expandedPaths = new Set(state.expandedPaths);
+    expandedPaths.delete(data.node.path);
+    return cloneState({ expandedPaths });
+  }
+
+  if (type === "NODE_PROPERTIES_LOADED") {
+    return cloneState({
+      actors: data.actor ? new Set(state.actors || []).add(data.actor) : state.actors,
+      loadedProperties: new Map(state.loadedProperties).set(data.node.path, action.data.properties)
+    });
+  }
+
+  if (type === "ROOTS_CHANGED") {
+    return cloneState();
+  }
+
+  if (type === "GETTER_INVOKED") {
+    return cloneState({
+      actors: data.actor ? new Set(state.actors || []).add(data.result.from) : state.actors,
+      evaluations: new Map(state.evaluations).set(data.node.path, {
+        getterValue: data.result && data.result.value && (data.result.value.return || data.result.value.throw)
+      })
+    });
+  }
+
+  return state;
+}
+
+function getObjectInspectorState(state) {
+  return state.objectInspector;
+}
+
+function getExpandedPaths(state) {
+  return getObjectInspectorState(state).expandedPaths;
+}
+
+function getExpandedPathKeys(state) {
+  return [...getExpandedPaths(state).keys()];
+}
+
+function getActors(state) {
+  return getObjectInspectorState(state).actors;
+}
+
+function getLoadedProperties(state) {
+  return getObjectInspectorState(state).loadedProperties;
+}
+
+function getLoadedPropertyKeys(state) {
+  return [...getLoadedProperties(state).keys()];
+}
+
+function getEvaluations(state) {
+  return getObjectInspectorState(state).evaluations;
+}
+
+const selectors = {
+  getActors,
+  getEvaluations,
+  getExpandedPathKeys,
+  getExpandedPaths,
+  getLoadedProperties,
+  getLoadedPropertyKeys
+};
+
+Object.defineProperty(module.exports, "__esModule", {
+  value: true
+});
+module.exports = selectors;
+module.exports.default = reducer;
+
+/***/ }),
+
+/***/ 1787:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+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; };
+
+/* 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__(1804);
+const loadProperties = __webpack_require__(1803);
+const node = __webpack_require__(1785);
+const { nodeIsError, nodeIsPrimitive } = node;
+const selection = __webpack_require__(1859);
+
+const { MODE } = __webpack_require__(1762);
+const {
+  REPS: { Rep, Grip }
+} = __webpack_require__(1767);
+
+
+function shouldRenderRootsInReps(roots) {
+  if (roots.length > 1) {
+    return false;
+  }
+
+  const root = roots[0];
+  const name = root && root.name;
+  return (name === null || typeof name === "undefined") && (nodeIsPrimitive(root) || nodeIsError(root));
+}
+
+function renderRep(item, props) {
+  return Rep(_extends({}, props, {
+    object: node.getValue(item),
+    mode: props.mode || MODE.TINY,
+    defaultRep: Grip
+  }));
+}
+
+module.exports = {
+  client,
+  loadProperties,
+  node,
+  renderRep,
+  selection,
+  shouldRenderRootsInReps
+};
+
+/***/ }),
+
+/***/ 1791:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+var _tree = __webpack_require__(1801);
 
 var _tree2 = _interopRequireDefault(_tree);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 module.exports = {
   Tree: _tree2.default
 }; /* 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/. */
 
 /***/ }),
 
-/***/ 3670:
+/***/ 1794:
+/***/ (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/>. */
+
+// ReactJS
+const PropTypes = __webpack_require__(1758);
+
+// Reps
+const { getGripType, isGrip, cropString, wrapRender } = __webpack_require__(1760);
+const { MODE } = __webpack_require__(1762);
+
+const dom = __webpack_require__(1759);
+const { span } = dom;
+
+const IGNORED_SOURCE_URLS = ["debugger eval code"];
+
+/**
+ * This component represents a template for Function objects.
+ */
+FunctionRep.propTypes = {
+  object: PropTypes.object.isRequired,
+  parameterNames: PropTypes.array,
+  onViewSourceInDebugger: PropTypes.func
+};
+
+function FunctionRep(props) {
+  const { object: grip, onViewSourceInDebugger, recordTelemetryEvent } = props;
+
+  let jumpToDefinitionButton;
+  if (onViewSourceInDebugger && grip.location && grip.location.url && !IGNORED_SOURCE_URLS.includes(grip.location.url)) {
+    jumpToDefinitionButton = dom.button({
+      className: "jump-definition",
+      draggable: false,
+      title: "Jump to definition",
+      onClick: e => {
+        // Stop the event propagation so we don't trigger ObjectInspector
+        // expand/collapse.
+        e.stopPropagation();
+        if (recordTelemetryEvent) {
+          recordTelemetryEvent("jump_to_definition");
+        }
+        onViewSourceInDebugger(grip.location);
+      }
+    });
+  }
+
+  return span({
+    "data-link-actor-id": grip.actor,
+    className: "objectBox objectBox-function",
+    // Set dir="ltr" to prevent function parentheses from
+    // appearing in the wrong direction
+    dir: "ltr"
+  }, getTitle(grip, props), getFunctionName(grip, props), "(", ...renderParams(props), ")", jumpToDefinitionButton);
+}
+
+function getTitle(grip, props) {
+  const { mode } = props;
+
+  if (mode === MODE.TINY && !grip.isGenerator && !grip.isAsync) {
+    return null;
+  }
+
+  let title = mode === MODE.TINY ? "" : "function ";
+
+  if (grip.isGenerator) {
+    title = mode === MODE.TINY ? "* " : "function* ";
+  }
+
+  if (grip.isAsync) {
+    title = `${"async" + " "}${title}`;
+  }
+
+  return span({
+    className: "objectTitle"
+  }, title);
+}
+
+/**
+ * Returns a ReactElement representing the function name.
+ *
+ * @param {Object} grip : Function grip
+ * @param {Object} props: Function rep props
+ */
+function getFunctionName(grip, props = {}) {
+  let { functionName } = props;
+  let name;
+
+  if (functionName) {
+    const end = functionName.length - 1;
+    functionName = functionName.startsWith('"') && functionName.endsWith('"') ? functionName.substring(1, end) : functionName;
+  }
+
+  if (grip.displayName != undefined && functionName != undefined && grip.displayName != functionName) {
+    name = `${functionName}:${grip.displayName}`;
+  } else {
+    name = cleanFunctionName(grip.userDisplayName || grip.displayName || grip.name || props.functionName || "");
+  }
+
+  return cropString(name, 100);
+}
+
+const objectProperty = /([\w\d\$]+)$/;
+const arrayProperty = /\[(.*?)\]$/;
+const functionProperty = /([\w\d]+)[\/\.<]*?$/;
+const annonymousProperty = /([\w\d]+)\(\^\)$/;
+
+/**
+ * Decodes an anonymous naming scheme that
+ * spider monkey implements based on "Naming Anonymous JavaScript Functions"
+ * http://johnjbarton.github.io/nonymous/index.html
+ *
+ * @param {String} name : Function name to clean up
+ * @returns String
+ */
+function cleanFunctionName(name) {
+  for (const reg of [objectProperty, arrayProperty, functionProperty, annonymousProperty]) {
+    const match = reg.exec(name);
+    if (match) {
+      return match[1];
+    }
+  }
+
+  return name;
+}
+
+function renderParams(props) {
+  const { parameterNames = [] } = props;
+
+  return parameterNames.filter(param => param).reduce((res, param, index, arr) => {
+    res.push(span({ className: "param" }, param));
+    if (index < arr.length - 1) {
+      res.push(span({ className: "delimiter" }, ", "));
+    }
+    return res;
+  }, []);
+}
+
+// Registration
+function supportsObject(grip, noGrip = false) {
+  const type = getGripType(grip, noGrip);
+  if (noGrip === true || !isGrip(grip)) {
+    return type == "function";
+  }
+
+  return type == "Function";
+}
+
+// Exports from this module
+
+module.exports = {
+  rep: wrapRender(FunctionRep),
+  supportsObject,
+  cleanFunctionName,
+  // exported for testing purpose.
+  getFunctionName
+};
+
+/***/ }),
+
+/***/ 1795:
+/***/ (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/>. */
+
+module.exports = {
+  ELEMENT_NODE: 1,
+  ATTRIBUTE_NODE: 2,
+  TEXT_NODE: 3,
+  CDATA_SECTION_NODE: 4,
+  ENTITY_REFERENCE_NODE: 5,
+  ENTITY_NODE: 6,
+  PROCESSING_INSTRUCTION_NODE: 7,
+  COMMENT_NODE: 8,
+  DOCUMENT_NODE: 9,
+  DOCUMENT_TYPE_NODE: 10,
+  DOCUMENT_FRAGMENT_NODE: 11,
+  NOTATION_NODE: 12,
+
+  // DocumentPosition
+  DOCUMENT_POSITION_DISCONNECTED: 0x01,
+  DOCUMENT_POSITION_PRECEDING: 0x02,
+  DOCUMENT_POSITION_FOLLOWING: 0x04,
+  DOCUMENT_POSITION_CONTAINS: 0x08,
+  DOCUMENT_POSITION_CONTAINED_BY: 0x10,
+  DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 0x20
+};
+
+/***/ }),
+
+/***/ 1796:
+/***/ (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/>. */
+
+// ReactJS
+const PropTypes = __webpack_require__(1758);
+// Utils
+const { getGripType, isGrip, wrapRender } = __webpack_require__(1760);
+const { cleanFunctionName } = __webpack_require__(1794);
+const { isLongString } = __webpack_require__(1770);
+const { MODE } = __webpack_require__(1762);
+
+const dom = __webpack_require__(1759);
+const { span } = dom;
+const IGNORED_SOURCE_URLS = ["debugger eval code"];
+
+/**
+ * Renders Error objects.
+ */
+ErrorRep.propTypes = {
+  object: PropTypes.object.isRequired,
+  // @TODO Change this to Object.values when supported in Node's version of V8
+  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+  // An optional function that will be used to render the Error stacktrace.
+  renderStacktrace: PropTypes.func
+};
+
+function ErrorRep(props) {
+  const object = props.object;
+  const preview = object.preview;
+
+  let name;
+  if (preview && preview.name && preview.kind) {
+    switch (preview.kind) {
+      case "Error":
+        name = preview.name;
+        break;
+      case "DOMException":
+        name = preview.kind;
+        break;
+      default:
+        throw new Error("Unknown preview kind for the Error rep.");
+    }
+  } else {
+    name = "Error";
+  }
+
+  const content = [];
+
+  if (props.mode === MODE.TINY) {
+    content.push(name);
+  } else {
+    content.push(`${name}: "${preview.message}"`);
+  }
+
+  if (preview.stack && props.mode !== MODE.TINY) {
+    const stacktrace = props.renderStacktrace ? props.renderStacktrace(parseStackString(preview.stack)) : getStacktraceElements(props, preview);
+    content.push("\n", stacktrace);
+  }
+
+  return span({
+    "data-link-actor-id": object.actor,
+    className: "objectBox-stackTrace"
+  }, content);
+}
+
+/**
+ * Returns a React element reprensenting the Error stacktrace, i.e.
+ * transform error.stack from:
+ *
+ * semicolon@debugger eval code:1:109
+ * jkl@debugger eval code:1:63
+ * asdf@debugger eval code:1:28
+ * @debugger eval code:1:227
+ *
+ * Into a column layout:
+ *
+ * semicolon  (<anonymous>:8:10)
+ * jkl        (<anonymous>:5:10)
+ * asdf       (<anonymous>:2:10)
+ *            (<anonymous>:11:1)
+ */
+function getStacktraceElements(props, preview) {
+  const stack = [];
+  if (!preview.stack) {
+    return stack;
+  }
+
+  parseStackString(preview.stack).forEach((frame, index, frames) => {
+    let onLocationClick;
+    const {
+      filename,
+      lineNumber,
+      columnNumber,
+      functionName,
+      location
+    } = frame;
+
+    if (props.onViewSourceInDebugger && !IGNORED_SOURCE_URLS.includes(filename)) {
+      onLocationClick = e => {
+        // Don't trigger ObjectInspector expand/collapse.
+        e.stopPropagation();
+        props.onViewSourceInDebugger({
+          url: filename,
+          line: lineNumber,
+          column: columnNumber
+        });
+      };
+    }
+
+    stack.push("\t", span({
+      key: `fn${index}`,
+      className: "objectBox-stackTrace-fn"
+    }, cleanFunctionName(functionName)), " ", span({
+      key: `location${index}`,
+      className: "objectBox-stackTrace-location",
+      onClick: onLocationClick,
+      title: onLocationClick ? `View source in debugger → ${location}` : undefined
+    }, location), "\n");
+  });
+
+  return span({
+    key: "stack",
+    className: "objectBox-stackTrace-grid"
+  }, stack);
+}
+
+/**
+ * Parse a string that should represent a stack trace and returns an array of
+ * the frames. The shape of the frames are extremely important as they can then
+ * be processed here or in the toolbox by other components.
+ * @param {String} stack
+ * @returns {Array} Array of frames, which are object with the following shape:
+ *                  - {String} filename
+ *                  - {String} functionName
+ *                  - {String} location
+ *                  - {Number} columnNumber
+ *                  - {Number} lineNumber
+ */
+function parseStackString(stack) {
+  const res = [];
+  if (!stack) {
+    return res;
+  }
+
+  const isStacktraceALongString = isLongString(stack);
+  const stackString = isStacktraceALongString ? stack.initial : stack;
+
+  stackString.split("\n").forEach((frame, index, frames) => {
+    if (!frame) {
+      // Skip any blank lines
+      return;
+    }
+
+    // If the stacktrace is a longString, don't include the last frame in the
+    // array, since it is certainly incomplete.
+    // Can be removed when https://bugzilla.mozilla.org/show_bug.cgi?id=1448833
+    // is fixed.
+    if (isStacktraceALongString && index === frames.length - 1) {
+      return;
+    }
+
+    let functionName;
+    let location;
+
+    // Given the input: "functionName@scriptLocation:2:100"
+    // Result: [
+    //   "functionName@scriptLocation:2:100",
+    //   "functionName",
+    //   "scriptLocation:2:100"
+    // ]
+    const result = frame.match(/^(.*)@(.*)$/);
+    if (result && result.length === 3) {
+      functionName = result[1];
+
+      // If the resource was loaded by base-loader.js, the location looks like:
+      // resource://devtools/shared/base-loader.js -> resource://path/to/file.js .
+      // What's needed is only the last part after " -> ".
+      location = result[2].split(" -> ").pop();
+    }
+
+    if (!functionName) {
+      functionName = "<anonymous>";
+    }
+
+    // Given the input: "scriptLocation:2:100"
+    // Result:
+    // ["scriptLocation:2:100", "scriptLocation", "2", "100"]
+    const locationParts = location.match(/^(.*):(\d+):(\d+)$/);
+
+    if (location && locationParts) {
+      const [, filename, line, column] = locationParts;
+      res.push({
+        filename,
+        functionName,
+        location,
+        columnNumber: Number(column),
+        lineNumber: Number(line)
+      });
+    }
+  });
+
+  return res;
+}
+
+// Registration
+function supportsObject(object, noGrip = false) {
+  if (noGrip === true || !isGrip(object)) {
+    return false;
+  }
+  return object.preview && getGripType(object, noGrip) === "Error" || object.class === "DOMException";
+}
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(ErrorRep),
+  supportsObject
+};
+
+/***/ }),
+
+/***/ 1797:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+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; };
+
+/* 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__(1758);
+
+const { lengthBubble } = __webpack_require__(1798);
+const {
+  interleave,
+  getGripType,
+  isGrip,
+  wrapRender,
+  ellipsisElement
+} = __webpack_require__(1760);
+const { MODE } = __webpack_require__(1762);
+
+const dom = __webpack_require__(1759);
+const { span } = dom;
+const { ModePropType } = __webpack_require__(1774);
+const DEFAULT_TITLE = "Array";
+
+/**
+ * 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 when supported in Node's version of V8
+  mode: ModePropType,
+  provider: PropTypes.object,
+  onDOMNodeMouseOver: PropTypes.func,
+  onDOMNodeMouseOut: PropTypes.func,
+  onInspectIconClick: PropTypes.func
+};
+
+function GripArray(props) {
+  const { object, mode = MODE.SHORT } = props;
+
+  let brackets;
+  const 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) {
+    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);
+    }
+
+    brackets = needSpace(false);
+    return span(config, title, span({
+      className: "arrayLeftBracket"
+    }, brackets.left), isEmpty ? null : ellipsisElement, span({
+      className: "arrayRightBracket"
+    }, brackets.right));
+  }
+
+  const max = maxLengthMap.get(mode);
+  const items = arrayIterator(props, object, max);
+  brackets = needSpace(items.length > 0);
+
+  return span({
+    "data-link-actor-id": object.actor,
+    className: "objectBox objectBox-array"
+  }, title, span({
+    className: "arrayLeftBracket"
+  }, brackets.left), ...interleave(items, ", "), span({
+    className: "arrayRightBracket"
+  }, brackets.right), span({
+    className: "arrayProperties",
+    role: "group"
+  }));
+}
+
+function getLength(grip) {
+  if (!grip.preview) {
+    return 0;
+  }
+
+  return grip.preview.length || grip.preview.childNodesLength || 0;
+}
+
+function getTitle(props, object) {
+  const objectLength = getLength(object);
+  const isEmpty = objectLength === 0;
+
+  let title = props.title || object.class || DEFAULT_TITLE;
+
+  const length = lengthBubble({
+    object,
+    mode: props.mode,
+    maxLengthMap,
+    getLength
+  });
+
+  if (props.mode === MODE.TINY) {
+    if (isEmpty) {
+      if (object.class === DEFAULT_TITLE) {
+        return null;
+      }
+
+      return span({ className: "objectTitle" }, `${title} `);
+    }
+
+    let trailingSpace;
+    if (object.class === DEFAULT_TITLE) {
+      title = null;
+      trailingSpace = " ";
+    }
+
+    return span({ className: "objectTitle" }, title, length, trailingSpace);
+  }
+
+  return span({ className: "objectTitle" }, title, length, " ");
+}
+
+function getPreviewItems(grip) {
+  if (!grip.preview) {
+    return null;
+  }
+
+  return grip.preview.items || grip.preview.childNodes || [];
+}
+
+function arrayIterator(props, grip, max) {
+  const { Rep } = __webpack_require__(1767);
+
+  let items = [];
+  const gripLength = getLength(grip);
+
+  if (!gripLength) {
+    return items;
+  }
+
+  const previewItems = getPreviewItems(grip);
+  const provider = props.provider;
+
+  let emptySlots = 0;
+  let foldedEmptySlots = 0;
+  items = previewItems.reduce((res, itemGrip) => {
+    if (res.length >= max) {
+      return res;
+    }
+
+    let object;
+    try {
+      if (!provider && itemGrip === null) {
+        emptySlots++;
+        return res;
+      }
+
+      object = provider ? provider.getValue(itemGrip) : itemGrip;
+    } catch (exc) {
+      object = exc;
+    }
+
+    if (emptySlots > 0) {
+      res.push(getEmptySlotsElement(emptySlots));
+      foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
+      emptySlots = 0;
+    }
+
+    if (res.length < max) {
+      res.push(Rep(_extends({}, props, {
+        object,
+        mode: MODE.TINY,
+        // Do not propagate title to array items reps
+        title: undefined
+      })));
+    }
+
+    return res;
+  }, []);
+
+  // 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(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" : ""}>`;
+}
+
+function supportsObject(grip, noGrip = false) {
+  if (noGrip === true || !isGrip(grip)) {
+    return false;
+  }
+
+  return grip.preview && (grip.preview.kind == "ArrayLike" || getGripType(grip, noGrip) === "DocumentFragment");
+}
+
+const maxLengthMap = new Map();
+maxLengthMap.set(MODE.SHORT, 3);
+maxLengthMap.set(MODE.LONG, 10);
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(GripArray),
+  supportsObject,
+  maxLengthMap,
+  getLength
+};
+
+/***/ }),
+
+/***/ 1798:
+/***/ (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 PropTypes = __webpack_require__(1758);
+
+const { wrapRender } = __webpack_require__(1760);
+const { MODE } = __webpack_require__(1762);
+const { ModePropType } = __webpack_require__(1774);
+
+const dom = __webpack_require__(1759);
+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)
+};
+
+/***/ }),
+
+/***/ 1799:
+/***/ (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__(1798);
+const PropTypes = __webpack_require__(1758);
+const {
+  interleave,
+  isGrip,
+  wrapRender,
+  ellipsisElement
+} = __webpack_require__(1760);
+const PropRep = __webpack_require__(1775);
+const { MODE } = __webpack_require__(1762);
+const { ModePropType } = __webpack_require__(1774);
+
+const { span } = __webpack_require__(1759);
+
+/**
+ * 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 when supported in Node's version of V8
+  mode: ModePropType,
+  isInterestingEntry: PropTypes.func,
+  onDOMNodeMouseOver: PropTypes.func,
+  onDOMNodeMouseOut: PropTypes.func,
+  onInspectIconClick: PropTypes.func,
+  title: PropTypes.string
+};
+
+function GripMap(props) {
+  const { mode, object } = props;
+
+  const config = {
+    "data-link-actor-id": object.actor,
+    className: "objectBox objectBox-object"
+  };
+
+  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"
+  }, " { "), ...interleave(propsArray, ", "), span({
+    className: "objectRightBrace"
+  }, " }"));
+}
+
+function getTitle(props, object) {
+  const title = props.title || (object && object.class ? object.class : "Map");
+  return span({
+    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);
+  }
+  return [];
+}
+
+function entriesIterator(props, object, max) {
+  // Entry filter. Show only interesting entries to the user.
+  const isInterestingEntry = props.isInterestingEntry || ((type, value) => {
+    return type == "boolean" || type == "number" || type == "string" && value.length != 0;
+  });
+
+  const mapEntries = object.preview && object.preview.entries ? object.preview.entries : [];
+
+  let indexes = getEntriesIndexes(mapEntries, max, isInterestingEntry);
+  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);
+    }));
+  }
+
+  const entries = getEntries(props, mapEntries, indexes);
+  if (entries.length < getLength(object)) {
+    // There are some undisplayed entries. Then display "…".
+    entries.push(ellipsisElement);
+  }
+
+  return entries;
+}
+
+/**
+ * Get entries ordered by index.
+ *
+ * @param {Object} props Component props.
+ * @param {Array} entries Entries array.
+ * @param {Array} indexes Indexes of entries.
+ * @return {Array} Array of PropRep.
+ */
+function getEntries(props, entries, indexes) {
+  const { onDOMNodeMouseOver, onDOMNodeMouseOut, onInspectIconClick } = props;
+
+  // Make indexes ordered by ascending.
+  indexes.sort(function (a, b) {
+    return a - b;
+  });
+
+  return indexes.map((index, i) => {
+    const [key, entryValue] = entries[index];
+    const value = entryValue.value !== undefined ? entryValue.value : entryValue;
+
+    return PropRep({
+      name: key,
+      equal: " \u2192 ",
+      object: value,
+      mode: MODE.TINY,
+      onDOMNodeMouseOver,
+      onDOMNodeMouseOut,
+      onInspectIconClick
+    });
+  });
+}
+
+/**
+ * Get the indexes of entries in the map.
+ *
+ * @param {Array} entries Entries array.
+ * @param {Number} max The maximum length of indexes array.
+ * @param {Function} filter Filter the entry you want.
+ * @return {Array} Indexes of filtered entries in the map.
+ */
+function getEntriesIndexes(entries, max, filter) {
+  return entries.reduce((indexes, [key, entry], i) => {
+    if (indexes.length < max) {
+      const value = entry && entry.value !== undefined ? entry.value : entry;
+      // Type is specified in grip's "class" field and for primitive
+      // values use typeof.
+      const type = (value && value.class ? value.class : typeof value).toLowerCase();
+
+      if (filter(type, value, key)) {
+        indexes.push(i);
+      }
+    }
+
+    return indexes;
+  }, []);
+}
+
+function getLength(grip) {
+  return grip.preview.size || 0;
+}
+
+function supportsObject(grip, noGrip = false) {
+  if (noGrip === true || !isGrip(grip)) {
+    return false;
+  }
+  return grip.preview && grip.preview.kind == "MapLike";
+}
+
+const maxLengthMap = new Map();
+maxLengthMap.set(MODE.SHORT, 3);
+maxLengthMap.set(MODE.LONG, 10);
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(GripMap),
+  supportsObject,
+  maxLengthMap,
+  getLength
+};
+
+/***/ }),
+
+/***/ 1800:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+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; };
+
+/* 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__(1758);
+// Shortcuts
+const dom = __webpack_require__(1759);
+const { span } = dom;
+const { wrapRender } = __webpack_require__(1760);
+const PropRep = __webpack_require__(1775);
+const { MODE } = __webpack_require__(1762);
+/**
+ * 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 when supported in Node's version of V8
+  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+  onDOMNodeMouseOver: PropTypes.func,
+  onDOMNodeMouseOut: PropTypes.func,
+  onInspectIconClick: PropTypes.func
+};
+
+function GripMapEntry(props) {
+  const { object } = props;
+
+  const { key, value } = object.preview;
+
+  return span({
+    className: "objectBox objectBox-map-entry"
+  }, PropRep(_extends({}, props, {
+    name: key,
+    object: value,
+    equal: " \u2192 ",
+    title: null,
+    suppressQuotes: false
+  })));
+}
+
+function supportsObject(grip, noGrip = false) {
+  if (noGrip === true) {
+    return false;
+  }
+  return grip && (grip.type === "mapEntry" || grip.type === "storageEntry") && grip.preview;
+}
+
+function createGripMapEntry(key, value) {
+  return {
+    type: "mapEntry",
+    preview: {
+      key,
+      value
+    }
+  };
+}
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(GripMapEntry),
+  createGripMapEntry,
+  supportsObject
+};
+
+/***/ }),
+
+/***/ 1801:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 
 var _react = __webpack_require__(0);
 
 var _react2 = _interopRequireDefault(_react);
 
-var _reactDomFactories = __webpack_require__(3643);
+var _reactDomFactories = __webpack_require__(1759);
 
 var _reactDomFactories2 = _interopRequireDefault(_reactDomFactories);
 
-var _propTypes = __webpack_require__(3642);
+var _propTypes = __webpack_require__(1758);
 
 var _propTypes2 = _interopRequireDefault(_propTypes);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 const { Component, createFactory } = _react2.default; /* 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__(3671);
+__webpack_require__(1802);
 
 // depth
 const AUTO_EXPAND_DEPTH = 0;
 
 /**
  * An arrow that displays whether its node is expanded (▼) or collapsed
  * (▶). When its node has no children, it is hidden.
  */
@@ -4595,44 +4448,308 @@ class Tree extends Component {
     }, nodes);
   }
 }
 
 exports.default = Tree;
 
 /***/ }),
 
-/***/ 3671:
+/***/ 1802:
 /***/ (function(module, exports) {
 
 // removed by extract-text-webpack-plugin
 
 /***/ }),
 
-/***/ 3672:
+/***/ 1803:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+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; };
+
+/* 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 {
+  enumEntries,
+  enumIndexedProperties,
+  enumNonIndexedProperties,
+  getPrototype,
+  enumSymbols,
+  getFullText
+} = __webpack_require__(1804);
+
+const {
+  getClosestGripNode,
+  getClosestNonBucketNode,
+  getValue,
+  nodeHasAccessors,
+  nodeHasAllEntriesInPreview,
+  nodeHasProperties,
+  nodeIsBucket,
+  nodeIsDefaultProperties,
+  nodeIsEntries,
+  nodeIsMapEntry,
+  nodeIsPrimitive,
+  nodeIsProxy,
+  nodeNeedsNumericalBuckets,
+  nodeIsLongString
+} = __webpack_require__(1785);
+
+function loadItemProperties(item, createObjectClient, createLongStringClient, loadedProperties) {
+  const gripItem = getClosestGripNode(item);
+  const value = getValue(gripItem);
+
+  const [start, end] = item.meta ? [item.meta.startIndex, item.meta.endIndex] : [];
+
+  const promises = [];
+  let objectClient;
+  const getObjectClient = () => objectClient || createObjectClient(value);
+
+  if (shouldLoadItemIndexedProperties(item, loadedProperties)) {
+    promises.push(enumIndexedProperties(getObjectClient(), start, end));
+  }
+
+  if (shouldLoadItemNonIndexedProperties(item, loadedProperties)) {
+    promises.push(enumNonIndexedProperties(getObjectClient(), start, end));
+  }
+
+  if (shouldLoadItemEntries(item, loadedProperties)) {
+    promises.push(enumEntries(getObjectClient(), start, end));
+  }
+
+  if (shouldLoadItemPrototype(item, loadedProperties)) {
+    promises.push(getPrototype(getObjectClient()));
+  }
+
+  if (shouldLoadItemSymbols(item, loadedProperties)) {
+    promises.push(enumSymbols(getObjectClient(), start, end));
+  }
+
+  if (shouldLoadItemFullText(item, loadedProperties)) {
+    promises.push(getFullText(createLongStringClient(value), item));
+  }
+
+  return Promise.all(promises).then(mergeResponses);
+}
+
+function mergeResponses(responses) {
+  const data = {};
+
+  for (const response of responses) {
+    if (response.hasOwnProperty("ownProperties")) {
+      data.ownProperties = _extends({}, data.ownProperties, response.ownProperties);
+    }
+
+    if (response.ownSymbols && response.ownSymbols.length > 0) {
+      data.ownSymbols = response.ownSymbols;
+    }
+
+    if (response.prototype) {
+      data.prototype = response.prototype;
+    }
+
+    if (response.fullText) {
+      data.fullText = response.fullText;
+    }
+  }
+
+  return data;
+}
+
+function shouldLoadItemIndexedProperties(item, loadedProperties = new Map()) {
+  const gripItem = getClosestGripNode(item);
+  const value = getValue(gripItem);
+
+  return value && nodeHasProperties(gripItem) && !loadedProperties.has(item.path) && !nodeIsProxy(item) && !nodeNeedsNumericalBuckets(item) && !nodeIsEntries(getClosestNonBucketNode(item)) &&
+  // The data is loaded when expanding the window node.
+  !nodeIsDefaultProperties(item);
+}
+
+function shouldLoadItemNonIndexedProperties(item, loadedProperties = new Map()) {
+  const gripItem = getClosestGripNode(item);
+  const value = getValue(gripItem);
+
+  return value && nodeHasProperties(gripItem) && !loadedProperties.has(item.path) && !nodeIsProxy(item) && !nodeIsEntries(getClosestNonBucketNode(item)) && !nodeIsBucket(item) &&
+  // The data is loaded when expanding the window node.
+  !nodeIsDefaultProperties(item);
+}
+
+function shouldLoadItemEntries(item, loadedProperties = new Map()) {
+  const gripItem = getClosestGripNode(item);
+  const value = getValue(gripItem);
+
+  return value && nodeIsEntries(getClosestNonBucketNode(item)) && !nodeHasAllEntriesInPreview(gripItem) && !loadedProperties.has(item.path) && !nodeNeedsNumericalBuckets(item);
+}
+
+function shouldLoadItemPrototype(item, loadedProperties = new Map()) {
+  const value = getValue(item);
+
+  return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item) && !nodeIsLongString(item);
+}
+
+function shouldLoadItemSymbols(item, loadedProperties = new Map()) {
+  const value = getValue(item);
+
+  return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item) && !nodeIsLongString(item) && !nodeIsProxy(item);
+}
+
+function shouldLoadItemFullText(item, loadedProperties = new Map()) {
+  return !loadedProperties.has(item.path) && nodeIsLongString(item);
+}
+
+module.exports = {
+  loadItemProperties,
+  mergeResponses,
+  shouldLoadItemEntries,
+  shouldLoadItemIndexedProperties,
+  shouldLoadItemNonIndexedProperties,
+  shouldLoadItemPrototype,
+  shouldLoadItemSymbols,
+  shouldLoadItemFullText
+};
+
+/***/ }),
+
+/***/ 1804:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const { getValue, nodeHasFullText } = __webpack_require__(1785); /* 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/>. */
+
+async function enumIndexedProperties(objectClient, start, end) {
+  try {
+    const { iterator } = await objectClient.enumProperties({
+      ignoreNonIndexedProperties: true
+    });
+    const response = await iteratorSlice(iterator, start, end);
+    return response;
+  } catch (e) {
+    console.error("Error in enumIndexedProperties", e);
+    return {};
+  }
+}
+
+async function enumNonIndexedProperties(objectClient, start, end) {
+  try {
+    const { iterator } = await objectClient.enumProperties({
+      ignoreIndexedProperties: true
+    });
+    const response = await iteratorSlice(iterator, start, end);
+    return response;
+  } catch (e) {
+    console.error("Error in enumNonIndexedProperties", e);
+    return {};
+  }
+}
+
+async function enumEntries(objectClient, start, end) {
+  try {
+    const { iterator } = await objectClient.enumEntries();
+    const response = await iteratorSlice(iterator, start, end);
+    return response;
+  } catch (e) {
+    console.error("Error in enumEntries", e);
+    return {};
+  }
+}
+
+async function enumSymbols(objectClient, start, end) {
+  try {
+    const { iterator } = await objectClient.enumSymbols();
+    const response = await iteratorSlice(iterator, start, end);
+    return response;
+  } catch (e) {
+    console.error("Error in enumSymbols", e);
+    return {};
+  }
+}
+
+async function getPrototype(objectClient) {
+  if (typeof objectClient.getPrototype !== "function") {
+    console.error("objectClient.getPrototype is not a function");
+    return Promise.resolve({});
+  }
+  return objectClient.getPrototype();
+}
+
+async function getFullText(longStringClient, item) {
+  const { initial, fullText, length } = getValue(item);
+
+  // Return fullText property if it exists so that it can be added to the
+  // loadedProperties map.
+  if (nodeHasFullText(item)) {
+    return Promise.resolve({ fullText });
+  }
+
+  return new Promise((resolve, reject) => {
+    longStringClient.substring(initial.length, length, response => {
+      if (response.error) {
+        console.error("LongStringClient.substring", `${response.error}: ${response.message}`);
+        reject({});
+        return;
+      }
+
+      resolve({
+        fullText: initial + response.substring
+      });
+    });
+  });
+}
+
+function iteratorSlice(iterator, start, end) {
+  start = start || 0;
+  const count = end ? end - start + 1 : iterator.count;
+
+  if (count === 0) {
+    return Promise.resolve({});
+  }
+  return iterator.slice(start, count);
+}
+
+module.exports = {
+  enumEntries,
+  enumIndexedProperties,
+  enumNonIndexedProperties,
+  enumSymbols,
+  getPrototype,
+  getFullText
+};
+
+/***/ }),
+
+/***/ 1830:
 /***/ (function(module, exports) {
 
 // removed by extract-text-webpack-plugin
 
 /***/ }),
 
-/***/ 3673:
+/***/ 1831:
 /***/ (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 { getGripType, wrapRender } = __webpack_require__(3644);
-
-const dom = __webpack_require__(3643);
+const { getGripType, wrapRender } = __webpack_require__(1760);
+
+const dom = __webpack_require__(1759);
 const { span } = dom;
 
 /**
  * Renders undefined value
  */
 const Undefined = function () {
   return span({ className: "objectBox objectBox-undefined" }, "undefined");
 };
@@ -4649,29 +4766,29 @@ function supportsObject(object, noGrip =
 
 module.exports = {
   rep: wrapRender(Undefined),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 3674:
+/***/ 1832:
 /***/ (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 { wrapRender } = __webpack_require__(3644);
-const dom = __webpack_require__(3643);
+const { wrapRender } = __webpack_require__(1760);
+const dom = __webpack_require__(1759);
 const { span } = dom;
 
 /**
  * Renders null value
  */
 function Null(props) {
   return span({ className: "objectBox objectBox-null" }, "null");
 }
@@ -4692,32 +4809,32 @@ function supportsObject(object, noGrip =
 
 module.exports = {
   rep: wrapRender(Null),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 3675:
+/***/ 1833:
 /***/ (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__(3642);
-
-const { getGripType, wrapRender } = __webpack_require__(3644);
-
-const dom = __webpack_require__(3643);
+const PropTypes = __webpack_require__(1758);
+
+const { getGripType, wrapRender } = __webpack_require__(1760);
+
+const dom = __webpack_require__(1759);
 const { span } = dom;
 
 /**
  * Renders a number
  */
 Number.propTypes = {
   object: PropTypes.oneOfType([PropTypes.object, PropTypes.number, PropTypes.bool]).isRequired
 };
@@ -4742,35 +4859,35 @@ function supportsObject(object, noGrip =
 
 module.exports = {
   rep: wrapRender(Number),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 3676:
+/***/ 1834:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 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; };
 
 /* 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__(3642);
-const { wrapRender, ellipsisElement } = __webpack_require__(3644);
-const PropRep = __webpack_require__(3650);
-const { MODE } = __webpack_require__(3645);
-
-const dom = __webpack_require__(3643);
+const PropTypes = __webpack_require__(1758);
+const { wrapRender, ellipsisElement } = __webpack_require__(1760);
+const PropRep = __webpack_require__(1775);
+const { MODE } = __webpack_require__(1762);
+
+const dom = __webpack_require__(1759);
 const { span } = dom;
 
 const DEFAULT_TITLE = "Object";
 
 /**
  * Renders an object. An object is represented by a list of its
  * properties enclosed in curly brackets.
  */
@@ -4914,32 +5031,32 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(ObjectRep),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 3677:
+/***/ 1835:
 /***/ (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__(3642);
-
-const { getGripType, wrapRender } = __webpack_require__(3644);
-
-const dom = __webpack_require__(3643);
+const PropTypes = __webpack_require__(1758);
+
+const { getGripType, wrapRender } = __webpack_require__(1760);
+
+const dom = __webpack_require__(1759);
 const { span } = dom;
 
 /**
  * Renders a symbol.
  */
 SymbolRep.propTypes = {
   object: PropTypes.object.isRequired
 };
@@ -4961,32 +5078,32 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(SymbolRep),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 3678:
+/***/ 1836:
 /***/ (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__(3642);
-
-const { getGripType, wrapRender } = __webpack_require__(3644);
-
-const dom = __webpack_require__(3643);
+const PropTypes = __webpack_require__(1758);
+
+const { getGripType, wrapRender } = __webpack_require__(1760);
+
+const dom = __webpack_require__(1759);
 const { span } = dom;
 
 /**
  * Renders a Infinity object
  */
 InfinityRep.propTypes = {
   object: PropTypes.object.isRequired
 };
@@ -5005,30 +5122,30 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(InfinityRep),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 3679:
+/***/ 1837:
 /***/ (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 { getGripType, wrapRender } = __webpack_require__(3644);
-
-const dom = __webpack_require__(3643);
+const { getGripType, wrapRender } = __webpack_require__(1760);
+
+const dom = __webpack_require__(1759);
 const { span } = dom;
 
 /**
  * Renders a NaN object
  */
 function NaNRep(props) {
   return span({ className: "objectBox objectBox-nan" }, "NaN");
 }
@@ -5040,49 +5157,49 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(NaNRep),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 3680:
+/***/ 1838:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 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; };
 
 /* 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__(3643);
-const PropTypes = __webpack_require__(3642);
-const { wrapRender } = __webpack_require__(3644);
-const { MODE } = __webpack_require__(3645);
+const dom = __webpack_require__(1759);
+const PropTypes = __webpack_require__(1758);
+const { wrapRender } = __webpack_require__(1760);
+const { MODE } = __webpack_require__(1762);
 const { span } = dom;
 
 /**
  * Renders an object. An object is represented by a list of its
  * properties enclosed in curly brackets.
  */
 Accessor.propTypes = {
   object: PropTypes.object.isRequired,
   mode: PropTypes.oneOf(Object.values(MODE))
 };
 
 function Accessor(props) {
   const { object, evaluation, onInvokeGetterButtonClick } = props;
 
   if (evaluation) {
-    const { Rep, Grip } = __webpack_require__(3647);
+    const { Rep, Grip } = __webpack_require__(1767);
     return span({
       className: "objectBox objectBox-accessor objectTitle"
     }, Rep(_extends({}, props, {
       object: evaluation.getterValue,
       mode: props.mode || MODE.TINY,
       defaultRep: Grip
     })));
   }
@@ -5129,34 +5246,157 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(Accessor),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 3681:
+/***/ 1839:
 /***/ (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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(3642);
-const dom = __webpack_require__(3643);
+const PropTypes = __webpack_require__(1758);
+const { button, span } = __webpack_require__(1759);
+
+// Utils
+const { isGrip, wrapRender } = __webpack_require__(1760);
+const { rep: StringRep } = __webpack_require__(1770);
+
+/**
+ * Renders Accessible object.
+ */
+Accessible.propTypes = {
+  object: PropTypes.object.isRequired,
+  inspectIconTitle: PropTypes.string,
+  nameMaxLength: PropTypes.number,
+  onAccessibleClick: PropTypes.func,
+  onAccessibleMouseOver: PropTypes.func,
+  onAccessibleMouseOut: PropTypes.func,
+  onInspectIconClick: PropTypes.func,
+  roleFirst: PropTypes.bool,
+  separatorText: PropTypes.string
+};
+
+function Accessible(props) {
+  const {
+    object,
+    inspectIconTitle,
+    nameMaxLength,
+    onAccessibleClick,
+    onAccessibleMouseOver,
+    onAccessibleMouseOut,
+    onInspectIconClick,
+    roleFirst,
+    separatorText
+  } = props;
+  const elements = getElements(object, nameMaxLength, roleFirst, separatorText);
+  const isInTree = object.preview && object.preview.isConnected === true;
+  const baseConfig = {
+    "data-link-actor-id": object.actor,
+    className: "objectBox objectBox-accessible"
+  };
+
+  let inspectIcon;
+  if (isInTree) {
+    if (onAccessibleClick) {
+      Object.assign(baseConfig, {
+        onClick: _ => onAccessibleClick(object),
+        className: `${baseConfig.className} clickable`
+      });
+    }
+
+    if (onAccessibleMouseOver) {
+      Object.assign(baseConfig, {
+        onMouseOver: _ => onAccessibleMouseOver(object)
+      });
+    }
+
+    if (onAccessibleMouseOut) {
+      Object.assign(baseConfig, {
+        onMouseOut: onAccessibleMouseOut
+      });
+    }
+
+    if (onInspectIconClick) {
+      inspectIcon = button({
+        className: "open-accessibility-inspector",
+        title: inspectIconTitle,
+        onClick: e => {
+          if (onAccessibleClick) {
+            e.stopPropagation();
+          }
+
+          onInspectIconClick(object, e);
+        }
+      });
+    }
+  }
+
+  return span(baseConfig, ...elements, inspectIcon);
+}
+
+function getElements(grip, nameMaxLength, roleFirst = false, separatorText = ": ") {
+  const { name, role } = grip.preview;
+  const elements = [];
+  if (name) {
+    elements.push(StringRep({
+      className: "accessible-name",
+      object: name,
+      cropLimit: nameMaxLength
+    }), span({ className: "separator" }, separatorText));
+  }
+
+  elements.push(span({ className: "accessible-role" }, role));
+  return roleFirst ? elements.reverse() : elements;
+}
+
+// Registration
+function supportsObject(object, noGrip = false) {
+  if (noGrip === true || !isGrip(object)) {
+    return false;
+  }
+
+  return object.preview && object.typeName && object.typeName === "accessible";
+}
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(Accessible),
+  supportsObject
+};
+
+/***/ }),
+
+/***/ 1840:
+/***/ (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/>. */
+
+// ReactJS
+const PropTypes = __webpack_require__(1758);
+const dom = __webpack_require__(1759);
 const { span } = dom;
 
 // Reps
-const { getGripType, isGrip, wrapRender } = __webpack_require__(3644);
-const { rep: StringRep } = __webpack_require__(3648);
+const { getGripType, isGrip, wrapRender } = __webpack_require__(1760);
+const { rep: StringRep } = __webpack_require__(1770);
 
 /**
  * Renders DOM attribute
  */
 Attribute.propTypes = {
   object: PropTypes.object.isRequired
 };
 
@@ -5185,33 +5425,33 @@ function supportsObject(grip, noGrip = f
 
 module.exports = {
   rep: wrapRender(Attribute),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 3682:
+/***/ 1841:
 /***/ (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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(3642);
+const PropTypes = __webpack_require__(1758);
 
 // Reps
-const { getGripType, isGrip, wrapRender } = __webpack_require__(3644);
-
-const dom = __webpack_require__(3643);
+const { getGripType, isGrip, wrapRender } = __webpack_require__(1760);
+
+const dom = __webpack_require__(1759);
 const { span } = dom;
 
 /**
  * Used to render JS built-in Date() object.
  */
 DateTime.propTypes = {
   object: PropTypes.object.isRequired
 };
@@ -5249,38 +5489,38 @@ function supportsObject(grip, noGrip = f
 // Exports from this module
 module.exports = {
   rep: wrapRender(DateTime),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 3683:
+/***/ 1842:
 /***/ (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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(3642);
+const PropTypes = __webpack_require__(1758);
 
 // Reps
 const {
   getGripType,
   isGrip,
   getURLDisplayString,
   wrapRender
-} = __webpack_require__(3644);
-
-const dom = __webpack_require__(3643);
+} = __webpack_require__(1760);
+
+const dom = __webpack_require__(1759);
 const { span } = dom;
 
 /**
  * Renders DOM document object.
  */
 Document.propTypes = {
   object: PropTypes.object.isRequired
 };
@@ -5318,32 +5558,32 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(Document),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 3684:
+/***/ 1843:
 /***/ (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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(3642);
+const PropTypes = __webpack_require__(1758);
 
 // Reps
-const { getGripType, isGrip, wrapRender } = __webpack_require__(3644);
-const dom = __webpack_require__(3643);
+const { getGripType, isGrip, wrapRender } = __webpack_require__(1760);
+const dom = __webpack_require__(1759);
 const { span } = dom;
 
 /**
  * Renders DOM documentType object.
  */
 DocumentType.propTypes = {
   object: PropTypes.object.isRequired
 };
@@ -5370,36 +5610,36 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(DocumentType),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 3685:
+/***/ 1844:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 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; };
 
 /* 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 PropTypes = __webpack_require__(3642);
+const PropTypes = __webpack_require__(1758);
 
 // Reps
-const { isGrip, wrapRender } = __webpack_require__(3644);
-
-const { MODE } = __webpack_require__(3645);
-const { rep } = __webpack_require__(3656);
+const { isGrip, wrapRender } = __webpack_require__(1760);
+
+const { MODE } = __webpack_require__(1762);
+const { rep } = __webpack_require__(1784);
 
 /**
  * Renders DOM event objects.
  */
 Event.propTypes = {
   object: PropTypes.object.isRequired,
   // @TODO Change this to Object.values when supported in Node's version of V8
   mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
@@ -5476,37 +5716,37 @@ function supportsObject(grip, noGrip = f
 // Exports from this module
 module.exports = {
   rep: wrapRender(Event),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 3686:
+/***/ 1845:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 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; };
 
 /* 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 PropTypes = __webpack_require__(3642);
+const PropTypes = __webpack_require__(1758);
 // Dependencies
-const { getGripType, isGrip, wrapRender } = __webpack_require__(3644);
-
-const PropRep = __webpack_require__(3650);
-const { MODE } = __webpack_require__(3645);
-
-const dom = __webpack_require__(3643);
+const { getGripType, isGrip, wrapRender } = __webpack_require__(1760);
+
+const PropRep = __webpack_require__(1775);
+const { MODE } = __webpack_require__(1762);
+
+const dom = __webpack_require__(1759);
 const { span } = dom;
 
 /**
  * Renders a DOM Promise object.
  */
 PromiseRep.propTypes = {
   object: PropTypes.object.isRequired,
   // @TODO Change this to Object.values when supported in Node's version of V8
@@ -5521,17 +5761,17 @@ function PromiseRep(props) {
   const { promiseState } = object;
 
   const config = {
     "data-link-actor-id": object.actor,
     className: "objectBox objectBox-object"
   };
 
   if (props.mode === MODE.TINY) {
-    const { Rep } = __webpack_require__(3647);
+    const { Rep } = __webpack_require__(1767);
 
     return span(config, getTitle(object), span({
       className: "objectLeftBrace"
     }, " { "), Rep({ object: promiseState.state }), span({
       className: "objectRightBrace"
     }, " }"));
   }
 
@@ -5585,33 +5825,33 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(PromiseRep),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 3687:
+/***/ 1846:
 /***/ (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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(3642);
+const PropTypes = __webpack_require__(1758);
 
 // Reps
-const { getGripType, isGrip, wrapRender } = __webpack_require__(3644);
-
-const dom = __webpack_require__(3643);
+const { getGripType, isGrip, wrapRender } = __webpack_require__(1760);
+
+const dom = __webpack_require__(1759);
 const { span } = dom;
 
 /**
  * Renders a grip object with regular expression.
  */
 RegExp.propTypes = {
   object: PropTypes.object.isRequired
 };
@@ -5641,38 +5881,38 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(RegExp),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 3688:
+/***/ 1847:
 /***/ (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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(3642);
+const PropTypes = __webpack_require__(1758);
 
 // Reps
 const {
   getGripType,
   isGrip,
   getURLDisplayString,
   wrapRender
-} = __webpack_require__(3644);
-
-const dom = __webpack_require__(3643);
+} = __webpack_require__(1760);
+
+const dom = __webpack_require__(1759);
 const { span } = dom;
 
 /**
  * Renders a grip representing CSSStyleSheet
  */
 StyleSheet.propTypes = {
   object: PropTypes.object.isRequired
 };
@@ -5710,37 +5950,37 @@ function supportsObject(object, noGrip =
 
 module.exports = {
   rep: wrapRender(StyleSheet),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 3689:
+/***/ 1848:
 /***/ (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__(3642);
+const PropTypes = __webpack_require__(1758);
 const {
   isGrip,
   cropString,
   cropMultipleLines,
   wrapRender
-} = __webpack_require__(3644);
-const { MODE } = __webpack_require__(3645);
-const nodeConstants = __webpack_require__(3659);
-const dom = __webpack_require__(3643);
+} = __webpack_require__(1760);
+const { MODE } = __webpack_require__(1762);
+const nodeConstants = __webpack_require__(1795);
+const dom = __webpack_require__(1759);
 const { span } = dom;
 
 /**
  * Renders DOM comment node.
  */
 CommentNode.propTypes = {
   object: PropTypes.object.isRequired,
   // @TODO Change this to Object.values when supported in Node's version of V8
@@ -5774,36 +6014,36 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(CommentNode),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 3690:
+/***/ 1849:
 /***/ (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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(3642);
+const PropTypes = __webpack_require__(1758);
 
 // Utils
-const { isGrip, wrapRender } = __webpack_require__(3644);
-const { rep: StringRep } = __webpack_require__(3648);
-const { MODE } = __webpack_require__(3645);
-const nodeConstants = __webpack_require__(3659);
-
-const dom = __webpack_require__(3643);
+const { isGrip, wrapRender } = __webpack_require__(1760);
+const { rep: StringRep } = __webpack_require__(1770);
+const { MODE } = __webpack_require__(1762);
+const nodeConstants = __webpack_require__(1795);
+
+const dom = __webpack_require__(1759);
 const { span } = dom;
 
 /**
  * Renders DOM element node.
  */
 ElementNode.propTypes = {
   object: PropTypes.object.isRequired,
   inspectIconTitle: PropTypes.string,
@@ -5929,34 +6169,34 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(ElementNode),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 3691:
+/***/ 1850:
 /***/ (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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(3642);
+const PropTypes = __webpack_require__(1758);
 
 // Reps
-const { isGrip, cropString, wrapRender } = __webpack_require__(3644);
-const { MODE } = __webpack_require__(3645);
-
-const dom = __webpack_require__(3643);
+const { isGrip, cropString, wrapRender } = __webpack_require__(1760);
+const { MODE } = __webpack_require__(1762);
+
+const dom = __webpack_require__(1759);
 const { span } = dom;
 
 /**
  * Renders DOM #text node.
  */
 TextNode.propTypes = {
   object: PropTypes.object.isRequired,
   // @TODO Change this to Object.values when supported in Node's version of V8
@@ -6034,40 +6274,40 @@ function supportsObject(grip, noGrip = f
 // Exports from this module
 module.exports = {
   rep: wrapRender(TextNode),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 3692:
+/***/ 1851:
 /***/ (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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(3642);
+const PropTypes = __webpack_require__(1758);
 
 // Reps
 const {
   getGripType,
   isGrip,
   getURLDisplayString,
   wrapRender
-} = __webpack_require__(3644);
-
-const { MODE } = __webpack_require__(3645);
-
-const dom = __webpack_require__(3643);
+} = __webpack_require__(1760);
+
+const { MODE } = __webpack_require__(1762);
+
+const dom = __webpack_require__(1759);
 const { span } = dom;
 
 /**
  * Renders a grip representing a window.
  */
 WindowRep.propTypes = {
   // @TODO Change this to Object.values when supported in Node's version of V8
   mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
@@ -6113,35 +6353,35 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(WindowRep),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 3693:
+/***/ 1852:
 /***/ (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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(3642);
+const PropTypes = __webpack_require__(1758);
 
 // Reps
-const { isGrip, wrapRender } = __webpack_require__(3644);
-
-const String = __webpack_require__(3648).rep;
-
-const dom = __webpack_require__(3643);
+const { isGrip, wrapRender } = __webpack_require__(1760);
+
+const String = __webpack_require__(1770).rep;
+
+const dom = __webpack_require__(1759);
 const { span } = dom;
 
 /**
  * Renders a grip object with textual data.
  */
 ObjectWithText.propTypes = {
   object: PropTypes.object.isRequired
 };
@@ -6176,33 +6416,33 @@ function supportsObject(grip, noGrip = f
 // Exports from this module
 module.exports = {
   rep: wrapRender(ObjectWithText),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 3694:
+/***/ 1853:
 /***/ (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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(3642);
+const PropTypes = __webpack_require__(1758);
 
 // Reps
-const { isGrip, getURLDisplayString, wrapRender } = __webpack_require__(3644);
-
-const dom = __webpack_require__(3643);
+const { isGrip, getURLDisplayString, wrapRender } = __webpack_require__(1760);
+
+const dom = __webpack_require__(1759);
 const { span } = dom;
 
 /**
  * Renders a grip object with URL data.
  */
 ObjectWithURL.propTypes = {
   object: PropTypes.object.isRequired
 };
@@ -6239,448 +6479,66 @@ function supportsObject(grip, noGrip = f
 // Exports from this module
 module.exports = {
   rep: wrapRender(ObjectWithURL),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 3695:
-/***/ (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 ObjectInspector = __webpack_require__(3829);
-const utils = __webpack_require__(3657);
-const reducer = __webpack_require__(3703);
-
-module.exports = { ObjectInspector, utils, reducer };
-
-/***/ }),
-
-/***/ 3698:
+/***/ 1854:
 /***/ (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/>. */
 
-function documentHasSelection() {
-  const selection = getSelection();
-  if (!selection) {
-    return false;
-  }
-
-  return selection.type === "Range";
-}
-
-module.exports = {
-  documentHasSelection
-};
+const ObjectInspector = __webpack_require__(1855);
+const utils = __webpack_require__(1787);
+const reducer = __webpack_require__(1786);
+
+module.exports = { ObjectInspector, utils, reducer };
 
 /***/ }),
 
-/***/ 3699:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-const { loadItemProperties } = __webpack_require__(3666); /* 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 { getLoadedProperties, getActors } = __webpack_require__(3703);
-
-/**
- * This action is responsible for expanding a given node, which also means that
- * it will call the action responsible to fetch properties.
- */
-function nodeExpand(node, actor) {
-  return async ({ dispatch, getState }) => {
-    dispatch({ type: "NODE_EXPAND", data: { node } });
-    dispatch(nodeLoadProperties(node, actor));
-  };
-}
-
-function nodeCollapse(node) {
-  return {
-    type: "NODE_COLLAPSE",
-    data: { node }
-  };
-}
-
-/*
- * This action checks if we need to fetch properties, entries, prototype and
- * symbols for a given node. If we do, it will call the appropriate ObjectClient
- * functions.
- */
-function nodeLoadProperties(node, actor) {
-  return async ({ dispatch, client, getState }) => {
-    const state = getState();
-    const loadedProperties = getLoadedProperties(state);
-    if (loadedProperties.has(node.path)) {
-      return;
-    }
-
-    try {
-      const properties = await loadItemProperties(node, client.createObjectClient, client.createLongStringClient, loadedProperties);
-
-      dispatch(nodePropertiesLoaded(node, actor, properties));
-    } catch (e) {
-      console.error(e);
-    }
-  };
-}
-
-function nodePropertiesLoaded(node, actor, properties) {
-  return {
-    type: "NODE_PROPERTIES_LOADED",
-    data: { node, actor, properties }
-  };
-}
-
-function closeObjectInspector() {
-  return async ({ getState, client }) => {
-    releaseActors(getState(), client);
-  };
-}
-
-/*
- * This action is dispatched when the `roots` prop, provided by a consumer of
- * the ObjectInspector (inspector, console, …), is modified. It will clean the
- * internal state properties (expandedPaths, loadedProperties, …) and release
- * the actors consumed with the previous roots.
- * It takes a props argument which reflects what is passed by the upper-level
- * consumer.
- */
-function rootsChanged(props) {
-  return async ({ dispatch, client, getState }) => {
-    releaseActors(getState(), client);
-    dispatch({
-      type: "ROOTS_CHANGED",
-      data: props
-    });
-  };
-}
-
-function releaseActors(state, client) {
-  const actors = getActors(state);
-  for (const actor of actors) {
-    client.releaseActor(actor);
-  }
-}
-
-function invokeGetter(node, grip, getterName) {
-  return async ({ dispatch, client, getState }) => {
-    try {
-      const objectClient = client.createObjectClient(grip);
-      const result = await objectClient.getPropertyValue(getterName);
-      dispatch({
-        type: "GETTER_INVOKED",
-        data: {
-          node,
-          result
-        }
-      });
-    } catch (e) {
-      console.error(e);
-    }
-  };
-}
-
-module.exports = {
-  closeObjectInspector,
-  invokeGetter,
-  nodeExpand,
-  nodeCollapse,
-  nodeLoadProperties,
-  nodePropertiesLoaded,
-  rootsChanged
-};
-
-/***/ }),
-
-/***/ 3703:
+/***/ 1855:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 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; };
 
-function initialState() {
-  return {
-    expandedPaths: new Set(),
-    loadedProperties: new Map(),
-    evaluations: new Map(),
-    actors: new Set()
-  };
-} /* This Source Code Form is subject to the terms of the Mozilla Public
-   * License, v. 2.0. If a copy of the MPL was not distributed with this
-   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
-
-
-function reducer(state = initialState(), action = {}) {
-  const { type, data } = action;
-
-  const cloneState = overrides => _extends({}, state, overrides);
-
-  if (type === "NODE_EXPAND") {
-    return cloneState({
-      expandedPaths: new Set(state.expandedPaths).add(data.node.path)
-    });
-  }
-
-  if (type === "NODE_COLLAPSE") {
-    const expandedPaths = new Set(state.expandedPaths);
-    expandedPaths.delete(data.node.path);
-    return cloneState({ expandedPaths });
-  }
-
-  if (type === "NODE_PROPERTIES_LOADED") {
-    return cloneState({
-      actors: data.actor ? new Set(state.actors || []).add(data.actor) : state.actors,
-      loadedProperties: new Map(state.loadedProperties).set(data.node.path, action.data.properties)
-    });
-  }
-
-  if (type === "ROOTS_CHANGED") {
-    return cloneState();
-  }
-
-  if (type === "GETTER_INVOKED") {
-    return cloneState({
-      actors: data.actor ? new Set(state.actors || []).add(data.result.from) : state.actors,
-      evaluations: new Map(state.evaluations).set(data.node.path, {
-        getterValue: data.result && data.result.value && (data.result.value.return || data.result.value.throw)
-      })
-    });
-  }
-
-  return state;
-}
-
-function getObjectInspectorState(state) {
-  return state.objectInspector;
-}
-
-function getExpandedPaths(state) {
-  return getObjectInspectorState(state).expandedPaths;
-}
-
-function getExpandedPathKeys(state) {
-  return [...getExpandedPaths(state).keys()];
-}
-
-function getActors(state) {
-  return getObjectInspectorState(state).actors;
-}
-
-function getLoadedProperties(state) {
-  return getObjectInspectorState(state).loadedProperties;
-}
-
-function getLoadedPropertyKeys(state) {
-  return [...getLoadedProperties(state).keys()];
-}
-
-function getEvaluations(state) {
-  return getObjectInspectorState(state).evaluations;
-}
-
-const selectors = {
-  getActors,
-  getEvaluations,
-  getExpandedPathKeys,
-  getExpandedPaths,
-  getLoadedProperties,
-  getLoadedPropertyKeys
-};
-
-Object.defineProperty(module.exports, "__esModule", {
-  value: true
-});
-module.exports = selectors;
-module.exports.default = reducer;
-
-/***/ }),
-
-/***/ 3730:
-/***/ (function(module, exports, __webpack_require__) {
-
-module.exports = __webpack_require__(3655);
-
-
-/***/ }),
-
-/***/ 3787:
-/***/ (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/>. */
-
-// ReactJS
-const PropTypes = __webpack_require__(3642);
-const { button, span } = __webpack_require__(3643);
-
-// Utils
-const { isGrip, wrapRender } = __webpack_require__(3644);
-const { rep: StringRep } = __webpack_require__(3648);
-
-/**
- * Renders Accessible object.
- */
-Accessible.propTypes = {
-  object: PropTypes.object.isRequired,
-  inspectIconTitle: PropTypes.string,
-  nameMaxLength: PropTypes.number,
-  onAccessibleClick: PropTypes.func,
-  onAccessibleMouseOver: PropTypes.func,
-  onAccessibleMouseOut: PropTypes.func,
-  onInspectIconClick: PropTypes.func,
-  roleFirst: PropTypes.bool,
-  separatorText: PropTypes.string
-};
-
-function Accessible(props) {
-  const {
-    object,
-    inspectIconTitle,
-    nameMaxLength,
-    onAccessibleClick,
-    onAccessibleMouseOver,
-    onAccessibleMouseOut,
-    onInspectIconClick,
-    roleFirst,
-    separatorText
-  } = props;
-  const elements = getElements(object, nameMaxLength, roleFirst, separatorText);
-  const isInTree = object.preview && object.preview.isConnected === true;
-  const baseConfig = {
-    "data-link-actor-id": object.actor,
-    className: "objectBox objectBox-accessible"
-  };
-
-  let inspectIcon;
-  if (isInTree) {
-    if (onAccessibleClick) {
-      Object.assign(baseConfig, {
-        onClick: _ => onAccessibleClick(object),
-        className: `${baseConfig.className} clickable`
-      });
-    }
-
-    if (onAccessibleMouseOver) {
-      Object.assign(baseConfig, {
-        onMouseOver: _ => onAccessibleMouseOver(object)
-      });
-    }
-
-    if (onAccessibleMouseOut) {
-      Object.assign(baseConfig, {
-        onMouseOut: onAccessibleMouseOut
-      });
-    }
-
-    if (onInspectIconClick) {
-      inspectIcon = button({
-        className: "open-accessibility-inspector",
-        title: inspectIconTitle,
-        onClick: e => {
-          if (onAccessibleClick) {
-            e.stopPropagation();
-          }
-
-          onInspectIconClick(object, e);
-        }
-      });
-    }
-  }
-
-  return span(baseConfig, ...elements, inspectIcon);
-}
-
-function getElements(grip, nameMaxLength, roleFirst = false, separatorText = ": ") {
-  const { name, role } = grip.preview;
-  const elements = [];
-  if (name) {
-    elements.push(StringRep({
-      className: "accessible-name",
-      object: name,
-      cropLimit: nameMaxLength
-    }), span({ className: "separator" }, separatorText));
-  }
-
-  elements.push(span({ className: "accessible-role" }, role));
-  return roleFirst ? elements.reverse() : elements;
-}
-
-// Registration
-function supportsObject(object, noGrip = false) {
-  if (noGrip === true || !isGrip(object)) {
-    return false;
-  }
-
-  return object.preview && object.typeName && object.typeName === "accessible";
-}
-
-// Exports from this module
-module.exports = {
-  rep: wrapRender(Accessible),
-  supportsObject
-};
-
-/***/ }),
-
-/***/ 3829:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-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 _devtoolsComponents = __webpack_require__(3669);
+var _devtoolsComponents = __webpack_require__(1791);
 
 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, createElement } = __webpack_require__(0);
-const { connect } = __webpack_require__(3592);
-const actions = __webpack_require__(3699);
-
-const selectors = __webpack_require__(3703);
+const { connect } = __webpack_require__(1763);
+const actions = __webpack_require__(1856);
+
+const selectors = __webpack_require__(1786);
 
 const Tree = createFactory(_devtoolsComponents2.default.Tree);
-__webpack_require__(3830);
-
-const ObjectInspectorItem = createFactory(__webpack_require__(3831));
+__webpack_require__(1857);
+
+const ObjectInspectorItem = createFactory(__webpack_require__(1858));
 
 const classnames = __webpack_require__(175);
 
-const Utils = __webpack_require__(3657);
+const Utils = __webpack_require__(1787);
 const { renderRep, shouldRenderRootsInReps } = Utils;
 const {
   getChildrenWithEvaluations,
   getActor,
   getParent,
   nodeIsPrimitive,
   nodeHasGetter,
   nodeHasSetter
@@ -6913,24 +6771,145 @@ module.exports = props => {
     return renderRep(roots[0], props);
   }
 
   return createElement(OI, props);
 };
 
 /***/ }),
 
-/***/ 3830:
+/***/ 1856:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const { loadItemProperties } = __webpack_require__(1803); /* 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 { getLoadedProperties, getActors } = __webpack_require__(1786);
+
+/**
+ * This action is responsible for expanding a given node, which also means that
+ * it will call the action responsible to fetch properties.
+ */
+function nodeExpand(node, actor) {
+  return async ({ dispatch, getState }) => {
+    dispatch({ type: "NODE_EXPAND", data: { node } });
+    dispatch(nodeLoadProperties(node, actor));
+  };
+}
+
+function nodeCollapse(node) {
+  return {
+    type: "NODE_COLLAPSE",
+    data: { node }
+  };
+}
+
+/*
+ * This action checks if we need to fetch properties, entries, prototype and
+ * symbols for a given node. If we do, it will call the appropriate ObjectClient
+ * functions.
+ */
+function nodeLoadProperties(node, actor) {
+  return async ({ dispatch, client, getState }) => {
+    const state = getState();
+    const loadedProperties = getLoadedProperties(state);
+    if (loadedProperties.has(node.path)) {
+      return;
+    }
+
+    try {
+      const properties = await loadItemProperties(node, client.createObjectClient, client.createLongStringClient, loadedProperties);
+
+      dispatch(nodePropertiesLoaded(node, actor, properties));
+    } catch (e) {
+      console.error(e);
+    }
+  };
+}
+
+function nodePropertiesLoaded(node, actor, properties) {
+  return {
+    type: "NODE_PROPERTIES_LOADED",
+    data: { node, actor, properties }
+  };
+}
+
+function closeObjectInspector() {
+  return async ({ getState, client }) => {
+    releaseActors(getState(), client);
+  };
+}
+
+/*
+ * This action is dispatched when the `roots` prop, provided by a consumer of
+ * the ObjectInspector (inspector, console, …), is modified. It will clean the
+ * internal state properties (expandedPaths, loadedProperties, …) and release
+ * the actors consumed with the previous roots.
+ * It takes a props argument which reflects what is passed by the upper-level
+ * consumer.
+ */
+function rootsChanged(props) {
+  return async ({ dispatch, client, getState }) => {
+    releaseActors(getState(), client);
+    dispatch({
+      type: "ROOTS_CHANGED",
+      data: props
+    });
+  };
+}
+
+function releaseActors(state, client) {
+  const actors = getActors(state);
+  for (const actor of actors) {
+    client.releaseActor(actor);
+  }
+}
+
+function invokeGetter(node, grip, getterName) {
+  return async ({ dispatch, client, getState }) => {
+    try {
+      const objectClient = client.createObjectClient(grip);
+      const result = await objectClient.getPropertyValue(getterName);
+      dispatch({
+        type: "GETTER_INVOKED",
+        data: {
+          node,
+          result
+        }
+      });
+    } catch (e) {
+      console.error(e);
+    }
+  };
+}
+
+module.exports = {
+  closeObjectInspector,
+  invokeGetter,
+  nodeExpand,
+  nodeCollapse,
+  nodeLoadProperties,
+  nodePropertiesLoaded,
+  rootsChanged
+};
+
+/***/ }),
+
+/***/ 1857:
 /***/ (function(module, exports) {
 
 // removed by extract-text-webpack-plugin
 
 /***/ }),
 
-/***/ 3831:
+/***/ 1858:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 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 _devtoolsServices = __webpack_require__(22);
@@ -6939,25 +6918,25 @@ var _devtoolsServices2 = _interopRequire
 
 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 } = __webpack_require__(0);
-const dom = __webpack_require__(3643);
+const dom = __webpack_require__(1759);
 
 const { appinfo } = _devtoolsServices2.default;
 const isMacOS = appinfo.OS === "Darwin";
 
 const classnames = __webpack_require__(175);
-const { MODE } = __webpack_require__(3645);
-
-const Utils = __webpack_require__(3657);
+const { MODE } = __webpack_require__(1762);
+
+const Utils = __webpack_require__(1787);
 
 const {
   getValue,
   nodeHasAccessors,
   nodeHasProperties,
   nodeIsBlock,
   nodeIsDefaultProperties,
   nodeIsFunction,
@@ -6970,17 +6949,17 @@ const {
   nodeIsSetter,
   nodeIsUninitializedBinding,
   nodeIsUnmappedBinding,
   nodeIsUnscopedBinding,
   nodeIsWindow,
   nodeIsLongString,
   nodeHasFullText,
   nodeHasGetter,
-  getParentGripValue
+  getNonPrototypeParentGripValue
 } = Utils.node;
 
 class ObjectInspectorItem extends Component {
   // eslint-disable-next-line complexity
   getLabelAndValue() {
     const { item, depth, expanded, mode } = this.props;
 
     const label = item.name;
@@ -7043,17 +7022,17 @@ class ObjectInspectorItem extends Compon
 
       if (nodeIsLongString(item)) {
         repProps.member = {
           open: nodeHasFullText(item) && expanded
         };
       }
 
       if (nodeHasGetter(item)) {
-        const parentGrip = getParentGripValue(item);
+        const parentGrip = getNonPrototypeParentGripValue(item);
         if (parentGrip) {
           Object.assign(repProps, {
             onInvokeGetterButtonClick: () => this.props.invokeGetter(item, parentGrip, item.name)
           });
         }
       }
 
       return {
@@ -7156,12 +7135,52 @@ class ObjectInspectorItem extends Compon
     const delimiter = value && labelElement ? dom.span({ className: "object-delimiter" }, ": ") : null;
 
     return dom.div(this.getTreeItemProps(), arrow, labelElement, delimiter, value);
   }
 }
 
 module.exports = ObjectInspectorItem;
 
+/***/ }),
+
+/***/ 1859:
+/***/ (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/>. */
+
+function documentHasSelection() {
+  const selection = getSelection();
+  if (!selection) {
+    return false;
+  }
+
+  return selection.type === "Range";
+}
+
+module.exports = {
+  documentHasSelection
+};
+
+/***/ }),
+
+/***/ 2104:
+/***/ (function(module, exports, __webpack_require__) {
+
+module.exports = __webpack_require__(1780);
+
+
+/***/ }),
+
+/***/ 22:
+/***/ (function(module, exports) {
+
+module.exports = __WEBPACK_EXTERNAL_MODULE_22__;
+
 /***/ })
 
 /******/ });
 });
\ No newline at end of file
--- a/devtools/client/webconsole/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/test/mochitest/browser.ini
@@ -340,16 +340,17 @@ skip-if = true  # Bug 1438979
 [browser_webconsole_network_reset_filter.js]
 [browser_webconsole_nodes_highlight.js]
 [browser_webconsole_nodes_select.js]
 [browser_webconsole_object_ctrl_click.js]
 [browser_webconsole_object_in_sidebar_keyboard_nav.js]
 [browser_webconsole_object_inspector.js]
 [browser_webconsole_object_inspector_entries.js]
 [browser_webconsole_object_inspector_getters.js]
+[browser_webconsole_object_inspector_getters_prototype.js]
 [browser_webconsole_object_inspector_key_sorting.js]
 [browser_webconsole_object_inspector_local_session_storage.js]
 [browser_webconsole_object_inspector_selected_text.js]
 [browser_webconsole_object_inspector_scroll.js]
 [browser_webconsole_object_inspector_while_debugging_and_inspecting.js]
 [browser_webconsole_observer_notifications.js]
 [browser_webconsole_optimized_out_vars.js]
 [browser_webconsole_output_copy.js]
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_object_inspector_getters.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_object_inspector_getters.js
@@ -55,288 +55,248 @@ add_task(async function() {
         },
       }))
     );
   });
 
   const node = await waitFor(() => findMessage(hud, "oi-test"));
   const oi = node.querySelector(".tree");
 
-  expandNode(oi);
-  await waitFor(() => getOiNodes(oi).length > 1);
+  expandObjectInspectorNode(oi);
+  await waitFor(() => getObjectInspectorNodes(oi).length > 1);
 
   await testStringGetter(oi);
   await testNumberGetter(oi);
   await testUndefinedGetter(oi);
   await testNullGetter(oi);
   await testObjectGetter(oi);
   await testArrayGetter(oi);
   await testMapGetter(oi);
   await testProxyGetter(oi);
   await testThrowingGetter(oi);
   await testLongStringGetter(oi, LONGSTRING);
 });
 
 async function testStringGetter(oi) {
-  let node = getOiNode(oi, "myStringGetter");
-  is(nodeIsExpandable(node), false, "The node can't be expanded");
-  const invokeButton = getInvokeGetterButton(node);
+  let node = findObjectInspectorNode(oi, "myStringGetter");
+  is(isObjectInspectorNodeExpandable(node), false, "The node can't be expanded");
+  const invokeButton = getObjectInspectorInvokeGetterButton(node);
   ok(invokeButton, "There is an invoke button as expected");
 
   invokeButton.click();
-  await waitFor(() => !getInvokeGetterButton(getOiNode(oi, "myStringGetter")));
+  await waitFor(() =>
+    !getObjectInspectorInvokeGetterButton(findObjectInspectorNode(oi, "myStringGetter")));
 
-  node = getOiNode(oi, "myStringGetter");
+  node = findObjectInspectorNode(oi, "myStringGetter");
   ok(node.textContent.includes(`myStringGetter: "hello"`),
     "String getter now has the expected text content");
-  is(nodeIsExpandable(node), false, "The node can't be expanded");
+  is(isObjectInspectorNodeExpandable(node), false, "The node can't be expanded");
 }
 
 async function testNumberGetter(oi) {
-  let node = getOiNode(oi, "myNumberGetter");
-  is(nodeIsExpandable(node), false, "The node can't be expanded");
-  const invokeButton = getInvokeGetterButton(node);
+  let node = findObjectInspectorNode(oi, "myNumberGetter");
+  is(isObjectInspectorNodeExpandable(node), false, "The node can't be expanded");
+  const invokeButton = getObjectInspectorInvokeGetterButton(node);
   ok(invokeButton, "There is an invoke button as expected");
 
   invokeButton.click();
-  await waitFor(() => !getInvokeGetterButton(getOiNode(oi, "myNumberGetter")));
+  await waitFor(() =>
+    !getObjectInspectorInvokeGetterButton(findObjectInspectorNode(oi, "myNumberGetter")));
 
-  node = getOiNode(oi, "myNumberGetter");
+  node = findObjectInspectorNode(oi, "myNumberGetter");
   ok(node.textContent.includes(`myNumberGetter: 123`),
     "Number getter now has the expected text content");
-  is(nodeIsExpandable(node), false, "The node can't be expanded");
+  is(isObjectInspectorNodeExpandable(node), false, "The node can't be expanded");
 }
 
 async function testUndefinedGetter(oi) {
-  let node = getOiNode(oi, "myUndefinedGetter");
-  is(nodeIsExpandable(node), false, "The node can't be expanded");
-  const invokeButton = getInvokeGetterButton(node);
+  let node = findObjectInspectorNode(oi, "myUndefinedGetter");
+  is(isObjectInspectorNodeExpandable(node), false, "The node can't be expanded");
+  const invokeButton = getObjectInspectorInvokeGetterButton(node);
   ok(invokeButton, "There is an invoke button as expected");
 
   invokeButton.click();
-  await waitFor(() => !getInvokeGetterButton(getOiNode(oi, "myUndefinedGetter")));
+  await waitFor(() => !getObjectInspectorInvokeGetterButton(
+    findObjectInspectorNode(oi, "myUndefinedGetter")));
 
-  node = getOiNode(oi, "myUndefinedGetter");
+  node = findObjectInspectorNode(oi, "myUndefinedGetter");
   ok(node.textContent.includes(`myUndefinedGetter: undefined`),
     "undefined getter now has the expected text content");
-  is(nodeIsExpandable(node), false, "The node can't be expanded");
+  is(isObjectInspectorNodeExpandable(node), false, "The node can't be expanded");
 }
 
 async function testNullGetter(oi) {
-  let node = getOiNode(oi, "myNullGetter");
-  is(nodeIsExpandable(node), false, "The node can't be expanded");
-  const invokeButton = getInvokeGetterButton(node);
+  let node = findObjectInspectorNode(oi, "myNullGetter");
+  is(isObjectInspectorNodeExpandable(node), false, "The node can't be expanded");
+  const invokeButton = getObjectInspectorInvokeGetterButton(node);
   ok(invokeButton, "There is an invoke button as expected");
 
   invokeButton.click();
-  await waitFor(() => !getInvokeGetterButton(getOiNode(oi, "myNullGetter")));
+  await waitFor(() =>
+    !getObjectInspectorInvokeGetterButton(findObjectInspectorNode(oi, "myNullGetter")));
 
-  node = getOiNode(oi, "myNullGetter");
+  node = findObjectInspectorNode(oi, "myNullGetter");
   ok(node.textContent.includes(`myNullGetter: null`),
     "null getter now has the expected text content");
-  is(nodeIsExpandable(node), false, "The node can't be expanded");
+  is(isObjectInspectorNodeExpandable(node), false, "The node can't be expanded");
 }
 
 async function testObjectGetter(oi) {
-  let node = getOiNode(oi, "myObjectGetter");
-  is(nodeIsExpandable(node), false, "The node can't be expanded");
-  const invokeButton = getInvokeGetterButton(node);
+  let node = findObjectInspectorNode(oi, "myObjectGetter");
+  is(isObjectInspectorNodeExpandable(node), false, "The node can't be expanded");
+  const invokeButton = getObjectInspectorInvokeGetterButton(node);
   ok(invokeButton, "There is an invoke button as expected");
 
   invokeButton.click();
-  await waitFor(() => !getInvokeGetterButton(getOiNode(oi, "myObjectGetter")));
+  await waitFor(() =>
+    !getObjectInspectorInvokeGetterButton(findObjectInspectorNode(oi, "myObjectGetter")));
 
-  node = getOiNode(oi, "myObjectGetter");
+  node = findObjectInspectorNode(oi, "myObjectGetter");
   ok(node.textContent.includes(`myObjectGetter: Object { foo: "bar" }`),
     "object getter now has the expected text content");
-  is(nodeIsExpandable(node), true, "The node can be expanded");
+  is(isObjectInspectorNodeExpandable(node), true, "The node can be expanded");
 
-  expandNode(node);
-  await waitFor(() => getChildrenNodes(node).length > 0);
+  expandObjectInspectorNode(node);
+  await waitFor(() => getObjectInspectorChildrenNodes(node).length > 0);
   checkChildren(node, [`foo: "bar"`, `<prototype>`]);
 }
 
 async function testArrayGetter(oi) {
-  let node = getOiNode(oi, "myArrayGetter");
-  is(nodeIsExpandable(node), false, "The node can't be expanded");
-  const invokeButton = getInvokeGetterButton(node);
+  let node = findObjectInspectorNode(oi, "myArrayGetter");
+  is(isObjectInspectorNodeExpandable(node), false, "The node can't be expanded");
+  const invokeButton = getObjectInspectorInvokeGetterButton(node);
   ok(invokeButton, "There is an invoke button as expected");
 
   invokeButton.click();
-  await waitFor(() => !getInvokeGetterButton(getOiNode(oi, "myArrayGetter")));
+  await waitFor(() =>
+    !getObjectInspectorInvokeGetterButton(findObjectInspectorNode(oi, "myArrayGetter")));
 
-  node = getOiNode(oi, "myArrayGetter");
+  node = findObjectInspectorNode(oi, "myArrayGetter");
   ok(node.textContent.includes(
     `myArrayGetter: Array(1000) [ 0, 1, 2, ${ELLIPSIS} ]`),
     "Array getter now has the expected text content - ");
-  is(nodeIsExpandable(node), true, "The node can be expanded");
+  is(isObjectInspectorNodeExpandable(node), true, "The node can be expanded");
 
-  expandNode(node);
-  await waitFor(() => getChildrenNodes(node).length > 0);
-  const children = getChildrenNodes(node);
+  expandObjectInspectorNode(node);
+  await waitFor(() => getObjectInspectorChildrenNodes(node).length > 0);
+  const children = getObjectInspectorChildrenNodes(node);
 
   const firstBucket = children[0];
   ok(firstBucket.textContent.includes(`[0${ELLIPSIS}99]`), "Array has buckets");
 
-  is(nodeIsExpandable(firstBucket), true, "The bucket can be expanded");
-  expandNode(firstBucket);
-  await waitFor(() => getChildrenNodes(firstBucket).length > 0);
+  is(isObjectInspectorNodeExpandable(firstBucket), true, "The bucket can be expanded");
+  expandObjectInspectorNode(firstBucket);
+  await waitFor(() => getObjectInspectorChildrenNodes(firstBucket).length > 0);
   checkChildren(firstBucket, Array.from({length: 100}, (_, i) => `${i}: ${i}`));
 }
 
 async function testMapGetter(oi) {
-  let node = getOiNode(oi, "myMapGetter");
-  is(nodeIsExpandable(node), false, "The node can't be expanded");
-  const invokeButton = getInvokeGetterButton(node);
+  let node = findObjectInspectorNode(oi, "myMapGetter");
+  is(isObjectInspectorNodeExpandable(node), false, "The node can't be expanded");
+  const invokeButton = getObjectInspectorInvokeGetterButton(node);
   ok(invokeButton, "There is an invoke button as expected");
 
   invokeButton.click();
-  await waitFor(() => !getInvokeGetterButton(getOiNode(oi, "myMapGetter")));
+  await waitFor(() =>
+    !getObjectInspectorInvokeGetterButton(findObjectInspectorNode(oi, "myMapGetter")));
 
-  node = getOiNode(oi, "myMapGetter");
+  node = findObjectInspectorNode(oi, "myMapGetter");
   ok(node.textContent.includes(`myMapGetter: Map`),
     "map getter now has the expected text content");
-  is(nodeIsExpandable(node), true, "The node can be expanded");
+  is(isObjectInspectorNodeExpandable(node), true, "The node can be expanded");
 
-  expandNode(node);
-  await waitFor(() => getChildrenNodes(node).length > 0);
+  expandObjectInspectorNode(node);
+  await waitFor(() => getObjectInspectorChildrenNodes(node).length > 0);
   checkChildren(node, [`size`, `<entries>`, `<prototype>`]);
 
-  const entriesNode = getOiNode(oi, "<entries>");
-  expandNode(entriesNode);
-  await waitFor(() => getChildrenNodes(entriesNode).length > 0);
+  const entriesNode = findObjectInspectorNode(oi, "<entries>");
+  expandObjectInspectorNode(entriesNode);
+  await waitFor(() => getObjectInspectorChildrenNodes(entriesNode).length > 0);
   // "→" character. Need to be instantiated this way for the test to pass.
   const arrow = String.fromCodePoint(8594);
   checkChildren(entriesNode, [`foo ${arrow} Object { ${ELLIPSIS} }`]);
 
-  const entryNode = getChildrenNodes(entriesNode)[0];
-  expandNode(entryNode);
-  await waitFor(() => getChildrenNodes(entryNode).length > 0);
+  const entryNode = getObjectInspectorChildrenNodes(entriesNode)[0];
+  expandObjectInspectorNode(entryNode);
+  await waitFor(() => getObjectInspectorChildrenNodes(entryNode).length > 0);
   checkChildren(entryNode, [`<key>: "foo"`, `<value>: Object { ${ELLIPSIS} }`]);
 }
 
 async function testProxyGetter(oi) {
-  let node = getOiNode(oi, "myProxyGetter");
-  is(nodeIsExpandable(node), false, "The node can't be expanded");
-  const invokeButton = getInvokeGetterButton(node);
+  let node = findObjectInspectorNode(oi, "myProxyGetter");
+  is(isObjectInspectorNodeExpandable(node), false, "The node can't be expanded");
+  const invokeButton = getObjectInspectorInvokeGetterButton(node);
   ok(invokeButton, "There is an invoke button as expected");
 
   invokeButton.click();
-  await waitFor(() => !getInvokeGetterButton(getOiNode(oi, "myProxyGetter")));
+  await waitFor(() =>
+    !getObjectInspectorInvokeGetterButton(findObjectInspectorNode(oi, "myProxyGetter")));
 
-  node = getOiNode(oi, "myProxyGetter");
+  node = findObjectInspectorNode(oi, "myProxyGetter");
   ok(node.textContent.includes(`myProxyGetter: Proxy`),
     "proxy getter now has the expected text content");
-  is(nodeIsExpandable(node), true, "The node can be expanded");
+  is(isObjectInspectorNodeExpandable(node), true, "The node can be expanded");
 
-  expandNode(node);
-  await waitFor(() => getChildrenNodes(node).length > 0);
+  expandObjectInspectorNode(node);
+  await waitFor(() => getObjectInspectorChildrenNodes(node).length > 0);
   checkChildren(node, [`<target>`, `<handler>`]);
 
-  const targetNode = getOiNode(oi, "<target>");
-  expandNode(targetNode);
-  await waitFor(() => getChildrenNodes(targetNode).length > 0);
+  const targetNode = findObjectInspectorNode(oi, "<target>");
+  expandObjectInspectorNode(targetNode);
+  await waitFor(() => getObjectInspectorChildrenNodes(targetNode).length > 0);
   checkChildren(targetNode, [`a: 1`, `<prototype>`]);
 
-  const handlerNode = getOiNode(oi, "<handler>");
-  expandNode(handlerNode);
-  await waitFor(() => getChildrenNodes(handlerNode).length > 0);
+  const handlerNode = findObjectInspectorNode(oi, "<handler>");
+  expandObjectInspectorNode(handlerNode);
+  await waitFor(() => getObjectInspectorChildrenNodes(handlerNode).length > 0);
   checkChildren(handlerNode, [`get:`, `<prototype>`]);
 }
 
 async function testThrowingGetter(oi) {
-  let node = getOiNode(oi, "myThrowingGetter");
-  is(nodeIsExpandable(node), false, "The node can't be expanded");
-  const invokeButton = getInvokeGetterButton(node);
+  let node = findObjectInspectorNode(oi, "myThrowingGetter");
+  is(isObjectInspectorNodeExpandable(node), false, "The node can't be expanded");
+  const invokeButton = getObjectInspectorInvokeGetterButton(node);
   ok(invokeButton, "There is an invoke button as expected");
 
   invokeButton.click();
-  await waitFor(() => !getInvokeGetterButton(getOiNode(oi, "myThrowingGetter")));
+  await waitFor(() => !getObjectInspectorInvokeGetterButton(
+    findObjectInspectorNode(oi, "myThrowingGetter")));
 
-  node = getOiNode(oi, "myThrowingGetter");
+  node = findObjectInspectorNode(oi, "myThrowingGetter");
   ok(node.textContent.includes(`myThrowingGetter: Error: "myError"`),
     "throwing getter does show the error");
-  is(nodeIsExpandable(node), true, "The node can be expanded");
+  is(isObjectInspectorNodeExpandable(node), true, "The node can be expanded");
 
-  expandNode(node);
-  await waitFor(() => getChildrenNodes(node).length > 0);
+  expandObjectInspectorNode(node);
+  await waitFor(() => getObjectInspectorChildrenNodes(node).length > 0);
   checkChildren(node,
     [`columnNumber`, `fileName`, `lineNumber`, `message`, `stack`, `<prototype>`]);
 }
 
 async function testLongStringGetter(oi, longString) {
-  const getLongStringNode = () => getOiNode(oi, "myLongStringGetter");
+  const getLongStringNode = () => findObjectInspectorNode(oi, "myLongStringGetter");
   const node = getLongStringNode();
-  is(nodeIsExpandable(node), false, "The node can't be expanded");
-  const invokeButton = getInvokeGetterButton(node);
+  is(isObjectInspectorNodeExpandable(node), false, "The node can't be expanded");
+  const invokeButton = getObjectInspectorInvokeGetterButton(node);
   ok(invokeButton, "There is an invoke button as expected");
 
   invokeButton.click();
   await waitFor(() =>
     getLongStringNode().textContent.includes(`myLongStringGetter: "ab ab`));
   ok(true, "longstring getter shows the initial text");
-  is(nodeIsExpandable(getLongStringNode()), true, "The node can be expanded");
+  is(isObjectInspectorNodeExpandable(getLongStringNode()), true,
+    "The node can be expanded");
 
-  expandNode(getLongStringNode());
+  expandObjectInspectorNode(getLongStringNode());
   await waitFor(() =>
     getLongStringNode().textContent.includes(`myLongStringGetter: "${longString}"`));
   ok(true, "the longstring was expanded");
 }
 
 function checkChildren(node, expectedChildren) {
-  const children = getChildrenNodes(node);
+  const children = getObjectInspectorChildrenNodes(node);
   is(children.length, expectedChildren.length,
     "There is the expected number of children");
   children.forEach((child, index) => {
     ok(child.textContent.includes(expectedChildren[index]),
       `Expected "${expectedChildren[index]}" child`);
   });
 }
-
-function nodeIsExpandable(node) {
-  return !!getNodeArrow(node);
-}
-
-function expandNode(node) {
-  const arrow = getNodeArrow(node);
-  if (!arrow) {
-    ok(false, "Node can't be expanded");
-    return;
-  }
-  arrow.click();
-}
-
-function getNodeArrow(node) {
-  return node.querySelector(".arrow");
-}
-
-function getOiNodes(oi) {
-  return oi.querySelectorAll(".tree-node");
-}
-
-function getChildrenNodes(node) {
-  const getLevel = n => parseInt(n.getAttribute("aria-level"), 10);
-  const level = getLevel(node);
-  const childLevel = level + 1;
-  const children = [];
-
-  let currentNode = node;
-  while (currentNode.nextSibling && getLevel(currentNode.nextSibling) === childLevel) {
-    currentNode = currentNode.nextSibling;
-    children.push(currentNode);
-  }
-
-  return children;
-}
-
-function getInvokeGetterButton(node) {
-  return node.querySelector(".invoke-getter");
-}
-
-function getOiNode(oi, nodeLabel) {
-  return [...oi.querySelectorAll(".tree-node")].find(node => {
-    const label = node.querySelector(".object-label");
-    if (!label) {
-      return false;
-    }
-    return label.textContent === nodeLabel;
-  });
-}
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_object_inspector_getters_prototype.js
@@ -0,0 +1,109 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Check evaluating getters on prototype nodes in the console.
+const TEST_URI = "data:text/html;charset=utf8,<h1>Object Inspector on Getters</h1>";
+
+add_task(async function() {
+  const hud = await openNewTabAndConsole(TEST_URI);
+
+  await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+    class A {
+      constructor() {
+        this.myValue = "foo";
+      }
+      get value() {
+        return `A-value:${this.myValue}`;
+      }
+    }
+
+    class B extends A {
+      get value() {
+        return `B-value:${this.myValue}`;
+      }
+    }
+
+    class C extends A {
+      constructor() {
+        super();
+        this.myValue = "bar";
+      }
+    }
+
+    const a = new A();
+    const b = new B();
+    const c = new C();
+
+    const d = new C();
+    d.myValue = "d";
+
+    content.wrappedJSObject.console.log("oi-test", a, b, c, d);
+  });
+
+  const node = await waitFor(() => findMessage(hud, "oi-test"));
+  const [a, b, c, d] = node.querySelectorAll(".tree");
+
+  await testObject(a, {
+    myValue: `myValue: "foo"`,
+    value: `value: "A-value:foo"`,
+  });
+
+  await testObject(b, {
+    myValue: `myValue: "foo"`,
+    value: `value: "B-value:foo"`,
+  });
+
+  await testObject(c, {
+    myValue: `myValue: "bar"`,
+    value: `value: "A-value:bar"`,
+  });
+
+  await testObject(d, {
+    myValue: `myValue: "d"`,
+    value: `value: "A-value:d"`,
+  });
+});
+
+async function testObject(oi, {myValue, value}) {
+  expandObjectInspectorNode(oi.querySelector(".tree-node"));
+  const prototypeNode = await waitFor(() => findObjectInspectorNode(oi, "<prototype>"));
+  let valueGetterNode = await getValueNode(prototypeNode);
+
+  ok(findObjectInspectorNode(oi, "myValue").textContent.includes(myValue),
+    `myValue has expected "${myValue}" content`);
+
+  getObjectInspectorInvokeGetterButton(valueGetterNode).click();
+
+  await waitFor(() =>
+    !getObjectInspectorInvokeGetterButton(findObjectInspectorNode(oi, "value")));
+  valueGetterNode = findObjectInspectorNode(oi, "value");
+  ok(valueGetterNode.textContent.includes(value),
+    `Getter now has the expected "${value}" content`);
+}
+
+async function getValueNode(prototypeNode) {
+  expandObjectInspectorNode(prototypeNode);
+
+  await waitFor(() => getObjectInspectorChildrenNodes(prototypeNode).length > 0);
+
+  const children = getObjectInspectorChildrenNodes(prototypeNode);
+  const valueNode = children.find(
+    child => child.querySelector(".object-label").textContent === "value");
+
+  if (valueNode) {
+    return valueNode;
+  }
+
+  const childPrototypeNode = children.find(child =>
+    child.querySelector(".object-label").textContent === "<prototype>");
+  if (!childPrototypeNode) {
+    return null;
+  }
+
+  return getValueNode(childPrototypeNode);
+}
+
--- a/devtools/client/webconsole/test/mochitest/head.js
+++ b/devtools/client/webconsole/test/mochitest/head.js
@@ -976,8 +976,102 @@ async function selectNodeWithPicker(tool
     selector,
     center: true,
     options: {},
   });
 
   await onPickerStopped;
   await onInspectorUpdated;
 }
+
+/**
+ * Clicks on the arrow of a single object inspector node if it exists.
+ *
+ * @param {HTMLElement} node: Object inspector node (.tree-node)
+ */
+function expandObjectInspectorNode(node) {
+  const arrow = getObjectInspectorNodeArrow(node);
+  if (!arrow) {
+    ok(false, "Node can't be expanded");
+    return;
+  }
+  arrow.click();
+}
+
+/**
+ * Retrieve the arrow of a single object inspector node.
+ *
+ * @param {HTMLElement} node: Object inspector node (.tree-node)
+ * @return {HTMLElement|null} the arrow element
+ */
+function getObjectInspectorNodeArrow(node) {
+  return node.querySelector(".arrow");
+}
+
+/**
+ * Check if a single object inspector node is expandable.
+ *
+ * @param {HTMLElement} node: Object inspector node (.tree-node)
+ * @return {Boolean} true if the node can be expanded
+ */
+function isObjectInspectorNodeExpandable(node) {
+  return !!getObjectInspectorNodeArrow(node);
+}
+
+/**
+ * Retrieve the nodes for a given object inspector element.
+ *
+ * @param {HTMLElement} oi: Object inspector element
+ * @return {NodeList} the object inspector nodes
+ */
+function getObjectInspectorNodes(oi) {
+  return oi.querySelectorAll(".tree-node");
+}
+
+/**
+ * Retrieve the "children" nodes for a given object inspector node.
+ *
+ * @param {HTMLElement} node: Object inspector node (.tree-node)
+ * @return {Array<HTMLElement>} the direct children (i.e. the ones that are one level
+ *                              deeper than the passed node)
+ */
+function getObjectInspectorChildrenNodes(node) {
+  const getLevel = n => parseInt(n.getAttribute("aria-level"), 10);
+  const level = getLevel(node);
+  const childLevel = level + 1;
+  const children = [];
+
+  let currentNode = node;
+  while (currentNode.nextSibling && getLevel(currentNode.nextSibling) === childLevel) {
+    currentNode = currentNode.nextSibling;
+    children.push(currentNode);
+  }
+
+  return children;
+}
+
+/**
+ * Retrieve the invoke getter button for a given object inspector node.
+ *
+ * @param {HTMLElement} node: Object inspector node (.tree-node)
+ * @return {HTMLElement|null} the invoke button element
+ */
+function getObjectInspectorInvokeGetterButton(node) {
+  return node.querySelector(".invoke-getter");
+}
+
+/**
+ * Retrieve the first node that match the passed node label, for a given object inspector
+ * element.
+ *
+ * @param {HTMLElement} oi: Object inspector element
+ * @param {String} nodeLabel: label of the searched node
+ * @return {HTMLElement|null} the Object inspector node with the matching label
+ */
+function findObjectInspectorNode(oi, nodeLabel) {
+  return [...oi.querySelectorAll(".tree-node")].find(node => {
+    const label = node.querySelector(".object-label");
+    if (!label) {
+      return false;
+    }
+    return label.textContent === nodeLabel;
+  });
+}