Bug 1496044 - Fix __proto__ handling in ObjectInspector. r=Oriol, a=RyanVM
authorNicolas Chevobbe <nchevobbe@mozilla.com>
Mon, 17 Dec 2018 13:46:50 +0000
changeset 509111 6da52487092a73ee4e2164c07abc2bb7f850319e
parent 509110 4195e37ecbe5026d45fff1660bd5b37fc3241c7b
child 509112 d57231c7ab800a5aed96eb1f05b2534bd39c44b8
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersOriol, RyanVM
bugs1496044
milestone65.0
Bug 1496044 - Fix __proto__ handling in ObjectInspector. r=Oriol, a=RyanVM This patch only remove the extends calls that were used to transpile object spread syntax. A test is added to ensure we don't regress this in the future. Differential Revision: https://phabricator.services.mozilla.com/D14233
devtools/client/shared/components/reps/reps.js
devtools/client/webconsole/test/mochitest/browser.ini
devtools/client/webconsole/test/mochitest/browser_webconsole_object_inspector__proto__.js
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -656,17 +656,17 @@ const ElementNode = __webpack_require__(
 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);
+const Grip = __webpack_require__(1783);
 
 // 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.
@@ -791,29 +791,31 @@ const { a, span } = dom;
 StringRep.propTypes = {
   useQuotes: PropTypes.bool,
   escapeWhitespace: PropTypes.bool,
   style: PropTypes.object,
   cropLimit: PropTypes.number.isRequired,
   member: PropTypes.object,
   object: PropTypes.object.isRequired,
   openLink: PropTypes.func,
-  className: PropTypes.string
+  className: PropTypes.string,
+  title: PropTypes.string
 };
 
 function StringRep(props) {
   const {
     className,
     style,
     cropLimit,
     object,
     useQuotes = true,
     escapeWhitespace = true,
     member,
-    openLink
+    openLink,
+    title
   } = props;
 
   let text = object;
 
   const isLong = isLongString(object);
   const isOpen = member && member.open;
   const shouldCrop = !isOpen && cropLimit && text.length > cropLimit;
 
@@ -832,17 +834,18 @@ function StringRep(props) {
   text = formatText({
     useQuotes,
     escapeWhitespace
   }, text);
 
   const config = getElementConfig({
     className,
     style,
-    actor: object.actor
+    actor: object.actor,
+    title
   });
 
   if (!isLong) {
     if (containsURL(text)) {
       return span(config, ...getLinkifiedElements(text, shouldCrop && cropLimit, openLink));
     }
 
     // Cropping of longString has been handled before formatting.
@@ -872,24 +875,28 @@ function maybeCropLongString(opts, text)
 
 function formatText(opts, text) {
   const { useQuotes, escapeWhitespace } = opts;
 
   return useQuotes ? escapeString(text, escapeWhitespace) : sanitizeString(text);
 }
 
 function getElementConfig(opts) {
-  const { className, style, actor } = opts;
+  const { className, style, actor, title } = opts;
 
   const config = {};
 
   if (actor) {
     config["data-link-actor-id"] = actor;
   }
 
+  if (title) {
+    config.title = title;
+  }
+
   const classNames = ["objectBox", "objectBox-string"];
   if (className) {
     classNames.push(className);
   }
   config.className = classNames.join(" ");
 
   if (style) {
     config.style = style;
@@ -1031,18 +1038,16 @@ module.exports = {
 /***/ }),
 
 /***/ 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__(1759);
 const PropTypes = __webpack_require__(1758);
 const { wrapRender } = __webpack_require__(1760);
@@ -1102,23 +1107,27 @@ function arrayIterator(props, array, max
   for (let i = 0; i < array.length && i < max; i++) {
     const config = {
       mode: MODE.TINY,
       delim: i == array.length - 1 ? "" : ", "
     };
     let item;
 
     try {
-      item = ItemRep(_extends({}, props, config, {
+      item = ItemRep({
+        ...props,
+        ...config,
         object: array[i]
-      }));
+      });
     } catch (exc) {
-      item = ItemRep(_extends({}, props, config, {
+      item = ItemRep({
+        ...props,
+        ...config,
         object: exc
-      }));
+      });
     }
     items.push(item);
   }
 
   if (array.length > max) {
     items.push(span({
       className: "more-ellipsis",
       title: "moreā€¦"
@@ -1136,20 +1145,21 @@ ItemRep.propTypes = {
   delim: PropTypes.string.isRequired,
   mode: ModePropType
 };
 
 function ItemRep(props) {
   const { Rep } = __webpack_require__(1767);
 
   const { object, delim, mode } = props;
-  return span({}, Rep(_extends({}, props, {
+  return span({}, Rep({
+    ...props,
     object: object,
     mode: mode
-  })), delim);
+  }), delim);
 }
 
 function getLength(object) {
   return object.length;
 }
 
 function supportsObject(object, noGrip = false) {
   return noGrip && (Array.isArray(object) || Object.prototype.toString.call(object) === "[object Arguments]");
@@ -1171,18 +1181,16 @@ module.exports = {
 /***/ }),
 
 /***/ 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__(1758);
 const { maybeEscapePropertyName, wrapRender } = __webpack_require__(1760);
 const { MODE } = __webpack_require__(1762);
@@ -1213,41 +1221,42 @@ 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__(1784);
+  const Grip = __webpack_require__(1783);
   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) {
       name = maybeEscapePropertyName(name);
     }
     key = span({ className: "nodeName" }, name);
   } else {
-    key = Rep(_extends({}, props, {
+    key = Rep({
+      ...props,
       className: "nodeName",
       object: name,
       mode: mode || MODE.TINY,
       defaultRep: Grip
-    }));
+    });
   }
 
   return [key, span({
     className: "objectEqual"
-  }, equal), Rep(_extends({}, props))];
+  }, equal), Rep({ ...props })];
 }
 
 // Exports from this module
 module.exports = wrapRender(PropRep);
 
 /***/ }),
 
 /***/ 1780:
@@ -1279,24 +1288,22 @@ module.exports = {
   parseURLEncodedText,
   parseURLParams,
   getGripPreviewItems,
   objectInspector
 };
 
 /***/ }),
 
-/***/ 1784:
+/***/ 1783:
 /***/ (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__(1758);
 
 // Dependencies
@@ -1414,17 +1421,17 @@ function propIterator(props, object, max
     return type == "boolean" || type == "number" || type == "string" && value.length != 0;
   });
 
   let properties = object.preview ? object.preview.ownProperties || {} : {};
 
   const propertiesLength = getPropertiesLength(object);
 
   if (object.preview && object.preview.safeGetterValues) {
-    properties = _extends({}, properties, object.preview.safeGetterValues);
+    properties = { ...properties, ...object.preview.safeGetterValues };
   }
 
   let indexes = getPropIndexes(properties, max, isInterestingProp);
   if (indexes.length < max && indexes.length < propertiesLength) {
     // There are not enough props yet.
     // Then add uninteresting props to display them.
     indexes = indexes.concat(getPropIndexes(properties, max - indexes.length, (t, value, name) => {
       return !isInterestingProp(t, value, name);
@@ -1439,25 +1446,26 @@ function propIterator(props, object, max
   const propsArray = getProps(props, properties, indexes, suppressQuotes);
 
   // Show symbols.
   if (object.preview && object.preview.ownSymbols) {
     const { ownSymbols } = object.preview;
     const length = max - indexes.length;
 
     const symbolsProps = ownSymbols.slice(0, length).map(symbolItem => {
-      return PropRep(_extends({}, props, {
+      return PropRep({
+        ...props,
         mode: MODE.TINY,
         name: symbolItem,
         object: symbolItem.descriptor.value,
         equal: ": ",
         defaultRep: Grip,
         title: null,
         suppressQuotes
-      }));
+      });
     });
 
     propsArray.push(...symbolsProps);
   }
 
   if (Object.keys(properties).length > max || propertiesLength > max ||
   // When the object has non-enumerable properties, we don't have them in the
   // packet, but we might want to show there's something in the object.
@@ -1489,25 +1497,26 @@ function getProps(componentProps, proper
     return a - b;
   });
 
   const propertiesKeys = Object.keys(properties);
   return indexes.map(i => {
     const name = propertiesKeys[i];
     const value = getPropValue(properties[name]);
 
-    return PropRep(_extends({}, componentProps, {
+    return PropRep({
+      ...componentProps,
       mode: MODE.TINY,
       name,
       object: value,
       equal: ": ",
       defaultRep: Grip,
       title: null,
       suppressQuotes
-    }));
+    });
   });
 }
 
 /**
  * Get the indexes of props in the object.
  *
  * @param {Object} properties Props object.
  * @param {Number} max The maximum length of indexes array.
@@ -1584,24 +1593,22 @@ const Grip = {
   maxLengthMap
 };
 
 // Exports from this module
 module.exports = Grip;
 
 /***/ }),
 
-/***/ 1785:
+/***/ 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/>. */
 
 const { maybeEscapePropertyName } = __webpack_require__(1760);
 const ArrayRep = __webpack_require__(1774);
 const GripArrayRep = __webpack_require__(1797);
 const GripMap = __webpack_require__(1799);
@@ -2074,17 +2081,17 @@ function makeNodesForProperties(objProps
     ownProperties = {},
     ownSymbols,
     prototype,
     safeGetterValues
   } = objProps;
 
   const parentValue = getValue(parent);
 
-  const allProperties = _extends({}, ownProperties, safeGetterValues);
+  const allProperties = { ...ownProperties, ...safeGetterValues };
 
   // Ignore properties that are neither non-concrete nor getters/setters.
   const propertiesNames = sortProperties(Object.keys(allProperties)).filter(name => {
     if (!allProperties[name]) {
       return false;
     }
 
     const properties = Object.getOwnPropertyNames(allProperties[name]);
@@ -2225,19 +2232,20 @@ function setNodeChildren(node, children)
   return node;
 }
 
 function getEvaluatedItem(item, evaluations) {
   if (!evaluations.has(item.path)) {
     return item;
   }
 
-  return _extends({}, item, {
+  return {
+    ...item,
     contents: evaluations.get(item.path)
-  });
+  };
 }
 
 function getChildrenWithEvaluations(options) {
   const { item, loadedProperties, cachedNodes, evaluations } = options;
 
   const children = getChildren({
     loadedProperties,
     cachedNodes,
@@ -2389,41 +2397,26 @@ function getNonPrototypeParentGripValue(
 
   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) {
-    return null;
-  }
-
-  return getValue(parentGripNode);
-}
-
 module.exports = {
   createNode,
   createGetterNode,
   createSetterNode,
   getActor,
   getChildren,
   getChildrenWithEvaluations,
   getClosestGripNode,
   getClosestNonBucketNode,
   getParent,
-  getParentGripValue,
   getNonPrototypeParentGripValue,
   getNumericalPropertiesCount,
   getValue,
   makeNodesForEntries,
   makeNodesForPromiseProperties,
   makeNodesForProperties,
   makeNumericalBuckets,
   nodeHasAccessors,
@@ -2459,40 +2452,38 @@ module.exports = {
   nodeSupportsNumericalBucketing,
   setNodeChildren,
   sortProperties,
   NODE_TYPES
 };
 
 /***/ }),
 
-/***/ 1786:
+/***/ 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; };
-
 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);
+  const cloneState = overrides => ({ ...state, ...overrides });
 
   if (type === "NODE_EXPAND") {
     return cloneState({
       expandedPaths: new Set(state.expandedPaths).add(data.node.path)
     });
   }
 
   if (type === "NODE_COLLAPSE") {
@@ -2570,31 +2561,29 @@ const selectors = {
 Object.defineProperty(module.exports, "__esModule", {
   value: true
 });
 module.exports = selectors;
 module.exports.default = reducer;
 
 /***/ }),
 
-/***/ 1787:
+/***/ 1786:
 /***/ (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 node = __webpack_require__(1784);
 const { nodeIsError, nodeIsPrimitive } = node;
 const selection = __webpack_require__(1859);
 
 const { MODE } = __webpack_require__(1762);
 const {
   REPS: { Rep, Grip }
 } = __webpack_require__(1767);
 
@@ -2605,21 +2594,22 @@ function shouldRenderRootsInReps(roots) 
   }
 
   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, {
+  return Rep({
+    ...props,
     object: node.getValue(item),
     mode: props.mode || MODE.TINY,
     defaultRep: Grip
-  }));
+  });
 }
 
 module.exports = {
   client,
   loadProperties,
   node,
   renderRep,
   selection,
@@ -3076,18 +3066,16 @@ module.exports = {
 /***/ }),
 
 /***/ 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);
@@ -3251,22 +3239,23 @@ function arrayIterator(props, grip, max)
 
     if (emptySlots > 0) {
       res.push(getEmptySlotsElement(emptySlots));
       foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
       emptySlots = 0;
     }
 
     if (res.length < max) {
-      res.push(Rep(_extends({}, props, {
+      res.push(Rep({
+        ...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));
@@ -3559,18 +3548,16 @@ module.exports = {
 /***/ }),
 
 /***/ 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);
@@ -3593,23 +3580,24 @@ GripMapEntry.propTypes = {
 
 function GripMapEntry(props) {
   const { object } = props;
 
   const { key, value } = object.preview;
 
   return span({
     className: "objectBox objectBox-map-entry"
-  }, PropRep(_extends({}, props, {
+  }, PropRep({
+    ...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;
 }
@@ -4467,18 +4455,16 @@ exports.default = Tree;
 /***/ }),
 
 /***/ 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,
@@ -4497,17 +4483,17 @@ const {
   nodeIsBucket,
   nodeIsDefaultProperties,
   nodeIsEntries,
   nodeIsMapEntry,
   nodeIsPrimitive,
   nodeIsProxy,
   nodeNeedsNumericalBuckets,
   nodeIsLongString
-} = __webpack_require__(1785);
+} = __webpack_require__(1784);
 
 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 = [];
@@ -4541,17 +4527,17 @@ function loadItemProperties(item, create
   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);
+      data.ownProperties = { ...data.ownProperties, ...response.ownProperties };
     }
 
     if (response.ownSymbols && response.ownSymbols.length > 0) {
       data.ownSymbols = response.ownSymbols;
     }
 
     if (response.prototype) {
       data.prototype = response.prototype;
@@ -4620,17 +4606,17 @@ module.exports = {
 /***/ }),
 
 /***/ 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
+const { getValue, nodeHasFullText } = __webpack_require__(1784); /* 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
     });
@@ -4871,18 +4857,16 @@ module.exports = {
 /***/ }),
 
 /***/ 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__(1758);
 const { wrapRender, ellipsisElement } = __webpack_require__(1760);
 const PropRep = __webpack_require__(1775);
@@ -4955,23 +4939,24 @@ function propIterator(props, object, max
   }
 
   const elements = [];
   const unimportantProperties = [];
   let propertiesNumber = 0;
   const propertiesNames = Object.keys(object);
 
   const pushPropRep = (name, value) => {
-    elements.push(PropRep(_extends({}, props, {
+    elements.push(PropRep({
+      ...props,
       key: name,
       mode: MODE.TINY,
       name,
       object: value,
       equal: ": "
-    })));
+    }));
     propertiesNumber++;
 
     if (propertiesNumber < propertiesNames.length) {
       elements.push(", ");
     }
   };
 
   try {
@@ -5169,18 +5154,16 @@ module.exports = {
 /***/ }),
 
 /***/ 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__(1759);
 const PropTypes = __webpack_require__(1758);
 const { wrapRender } = __webpack_require__(1760);
@@ -5198,21 +5181,22 @@ Accessor.propTypes = {
 
 function Accessor(props) {
   const { object, evaluation, onInvokeGetterButtonClick } = props;
 
   if (evaluation) {
     const { Rep, Grip } = __webpack_require__(1767);
     return span({
       className: "objectBox objectBox-accessor objectTitle"
-    }, Rep(_extends({}, props, {
+    }, Rep({
+      ...props,
       object: evaluation.getterValue,
       mode: props.mode || MODE.TINY,
       defaultRep: Grip
-    })));
+    }));
   }
 
   if (hasGetter(object) && onInvokeGetterButtonClick) {
     return dom.button({
       className: "invoke-getter",
       title: "Invoke getter",
       onClick: event => {
         onInvokeGetterButtonClick();
@@ -5408,17 +5392,17 @@ Attribute.propTypes = {
 
 function Attribute(props) {
   const { object } = props;
   const value = object.preview.value;
 
   return span({
     "data-link-actor-id": object.actor,
     className: "objectBox-Attr"
-  }, span({ className: "attrName" }, getTitle(object)), span({ className: "attrEqual" }, "="), StringRep({ className: "attrValue", object: value }));
+  }, span({ className: "attrName" }, getTitle(object)), span({ className: "attrEqual" }, "="), StringRep({ className: "attrValue", object: value, title: value }));
 }
 
 function getTitle(grip) {
   return grip.preview.nodeName;
 }
 
 // Registration
 function supportsObject(grip, noGrip = false) {
@@ -5622,52 +5606,53 @@ module.exports = {
 /***/ }),
 
 /***/ 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__(1758);
 
 // Reps
 const { isGrip, wrapRender } = __webpack_require__(1760);
 
 const { MODE } = __webpack_require__(1762);
-const { rep } = __webpack_require__(1784);
+const { rep } = __webpack_require__(1783);
 
 /**
  * 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])),
   onDOMNodeMouseOver: PropTypes.func,
   onDOMNodeMouseOut: PropTypes.func,
   onInspectIconClick: PropTypes.func
 };
 
 function Event(props) {
-  const gripProps = _extends({}, props, {
+  const gripProps = {
+    ...props,
     title: getTitle(props),
-    object: _extends({}, props.object, {
-      preview: _extends({}, props.object.preview, {
+    object: {
+      ...props.object,
+      preview: {
+        ...props.object.preview,
         ownProperties: {}
-      })
-    })
-  });
+      }
+    }
+  };
 
   if (gripProps.object.preview.target) {
     Object.assign(gripProps.object.preview.ownProperties, {
       target: gripProps.object.preview.target
     });
   }
   Object.assign(gripProps.object.preview.ownProperties, gripProps.object.preview.properties);
 
@@ -5728,18 +5713,16 @@ module.exports = {
 /***/ }),
 
 /***/ 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__(1758);
 // Dependencies
 const { getGripType, isGrip, wrapRender } = __webpack_require__(1760);
@@ -5798,23 +5781,24 @@ function getTitle(object) {
 function getProps(props, promiseState) {
   const keys = ["state"];
   if (Object.keys(promiseState).includes("value")) {
     keys.push("value");
   }
 
   return keys.reduce((res, key, i) => {
     const object = promiseState[key];
-    res = res.concat(PropRep(_extends({}, props, {
+    res = res.concat(PropRep({
+      ...props,
       mode: MODE.TINY,
       name: `<${key}>`,
       object,
       equal: ": ",
       suppressQuotes: true
-    })));
+    }));
 
     // Interleave commas between elements
     if (i !== keys.length - 1) {
       res.push(", ");
     }
 
     return res;
   }, []);
@@ -6035,23 +6019,25 @@ module.exports = {
  * 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 { isGrip, wrapRender } = __webpack_require__(1760);
-const { rep: StringRep } = __webpack_require__(1770);
+const { rep: StringRep, isLongString } = __webpack_require__(1770);
 const { MODE } = __webpack_require__(1762);
 const nodeConstants = __webpack_require__(1795);
 
 const dom = __webpack_require__(1759);
 const { span } = dom;
 
+const MAX_ATTRIBUTE_LENGTH = 50;
+
 /**
  * Renders DOM element node.
  */
 ElementNode.propTypes = {
   object: PropTypes.object.isRequired,
   inspectIconTitle: PropTypes.string,
   // @TODO Change this to Object.values when supported in Node's version of V8
   mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
@@ -6151,17 +6137,28 @@ function getElements(grip, mode) {
     attributeKeys.unshift("class");
   }
   if (attributeKeys.includes("id")) {
     attributeKeys.splice(attributeKeys.indexOf("id"), 1);
     attributeKeys.unshift("id");
   }
   const attributeElements = attributeKeys.reduce((arr, name, i, keys) => {
     const value = attributes[name];
-    const attribute = span({}, span({ className: "attrName" }, name), span({ className: "attrEqual" }, "="), StringRep({ className: "attrValue", object: value }));
+
+    let title = isLongString(value) ? value.initial : value;
+    if (title.length < MAX_ATTRIBUTE_LENGTH) {
+      title = null;
+    }
+
+    const attribute = span({}, span({ className: "attrName" }, name), span({ className: "attrEqual" }, "="), StringRep({
+      className: "attrValue",
+      object: value,
+      cropLimit: MAX_ATTRIBUTE_LENGTH,
+      title
+    }));
 
     return arr.concat([" ", attribute]);
   }, []);
 
   return [span({ className: "angleBracket" }, "<"), nodeNameElement, ...attributeElements, span({ className: "angleBracket" }, ">")];
 }
 
 // Registration
@@ -6170,17 +6167,18 @@ function supportsObject(object, noGrip =
     return false;
   }
   return object.preview && object.preview.nodeType === nodeConstants.ELEMENT_NODE;
 }
 
 // Exports from this module
 module.exports = {
   rep: wrapRender(ElementNode),
-  supportsObject
+  supportsObject,
+  MAX_ATTRIBUTE_LENGTH
 };
 
 /***/ }),
 
 /***/ 1850:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
@@ -6496,55 +6494,53 @@ module.exports = {
 "use strict";
 
 
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 const ObjectInspector = __webpack_require__(1855);
-const utils = __webpack_require__(1787);
-const reducer = __webpack_require__(1786);
+const utils = __webpack_require__(1786);
+const reducer = __webpack_require__(1785);
 
 module.exports = { ObjectInspector, utils, reducer };
 
 /***/ }),
 
 /***/ 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; };
-
 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__(1763);
 const actions = __webpack_require__(1856);
 
-const selectors = __webpack_require__(1786);
+const selectors = __webpack_require__(1785);
 
 const Tree = createFactory(_devtoolsComponents2.default.Tree);
 __webpack_require__(1857);
 
 const ObjectInspectorItem = createFactory(__webpack_require__(1858));
 
 const classnames = __webpack_require__(175);
 
-const Utils = __webpack_require__(1787);
+const Utils = __webpack_require__(1786);
 const { renderRep, shouldRenderRootsInReps } = Utils;
 const {
   getChildrenWithEvaluations,
   getActor,
   getParent,
   nodeIsPrimitive,
   nodeHasGetter,
   nodeHasSetter
@@ -6744,24 +6740,25 @@ class ObjectInspector extends Component 
       getParent,
       getChildren: this.getItemChildren,
       getKey: this.getNodeKey,
 
       onExpand: item => this.setExpanded(item, true),
       onCollapse: item => this.setExpanded(item, false),
       onFocus: focusable ? this.focusItem : null,
 
-      renderItem: (item, depth, focused, arrow, expanded) => ObjectInspectorItem(_extends({}, this.props, {
+      renderItem: (item, depth, focused, arrow, expanded) => ObjectInspectorItem({
+        ...this.props,
         item,
         depth,
         focused,
         arrow,
         expanded,
         setExpanded: this.setExpanded
-      }))
+      })
     });
   }
 }
 
 function mapStateToProps(state, props) {
   return {
     expandedPaths: selectors.getExpandedPaths(state),
     loadedProperties: selectors.getLoadedProperties(state),
@@ -6787,17 +6784,17 @@ module.exports = props => {
 
 "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);
+const { getLoadedProperties, getActors } = __webpack_require__(1785);
 
 /**
  * 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 } });
@@ -6911,18 +6908,16 @@ module.exports = {
 /***/ }),
 
 /***/ 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);
 
 var _devtoolsServices2 = _interopRequireDefault(_devtoolsServices);
 
 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
@@ -6932,17 +6927,17 @@ const { Component } = __webpack_require_
 const dom = __webpack_require__(1759);
 
 const { appinfo } = _devtoolsServices2.default;
 const isMacOS = appinfo.OS === "Darwin";
 
 const classnames = __webpack_require__(175);
 const { MODE } = __webpack_require__(1762);
 
-const Utils = __webpack_require__(1787);
+const Utils = __webpack_require__(1786);
 
 const {
   getValue,
   nodeHasAccessors,
   nodeHasProperties,
   nodeIsBlock,
   nodeIsDefaultProperties,
   nodeIsFunction,
@@ -7006,24 +7001,25 @@ class ObjectInspectorItem extends Compon
       return {
         label,
         value: dom.span({ className: "unavailable" }, "(unavailable)")
       };
     }
 
     if (nodeIsFunction(item) && !nodeIsGetter(item) && !nodeIsSetter(item) && (mode === MODE.TINY || !mode)) {
       return {
-        label: Utils.renderRep(item, _extends({}, this.props, {
+        label: Utils.renderRep(item, {
+          ...this.props,
           functionName: label
-        }))
+        })
       };
     }
 
     if (nodeHasProperties(item) || nodeHasAccessors(item) || nodeIsMapEntry(item) || nodeIsLongString(item) || isPrimitive) {
-      const repProps = _extends({}, this.props);
+      const repProps = { ...this.props };
       if (depth > 0) {
         repProps.mode = mode === MODE.LONG ? MODE.SHORT : MODE.TINY;
       }
       if (expanded) {
         repProps.mode = MODE.TINY;
       }
 
       if (nodeIsLongString(item)) {
--- a/devtools/client/webconsole/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/test/mochitest/browser.ini
@@ -338,16 +338,17 @@ skip-if = true  # Bug 1438979
 [browser_webconsole_network_messages_status_code.js]
 [browser_webconsole_network_requests_from_chrome.js]
 [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__proto__.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]
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_object_inspector__proto__.js
@@ -0,0 +1,36 @@
+/* -*- 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 displaying object with __proto__ in the console.
+const TEST_URI = "data:text/html;charset=utf8,<h1>test Object Inspector __proto__</h1>";
+
+add_task(async function() {
+  const hud = await openNewTabAndConsole(TEST_URI);
+
+  logAllStoreChanges(hud);
+
+  await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+    const obj = Object.create(null);
+    // eslint-disable-next-line no-proto
+    obj.__proto__ = [];
+    content.wrappedJSObject.console.log("oi-test", obj);
+  });
+
+  const node = await waitFor(() => findMessage(hud, "oi-test"));
+  const objectInspector = node.querySelector(".tree");
+  ok(objectInspector, "Object is printed in the console");
+
+  is(objectInspector.textContent.trim(), "Object { __proto__: [] }",
+    "Object is displayed as expected");
+
+  objectInspector.querySelector(".arrow").click();
+  await waitFor(() => node.querySelectorAll(".tree-node").length === 2);
+
+  const __proto__Node = node.querySelector(".tree-node:last-of-type");
+  ok(__proto__Node.textContent.includes("__proto__: Array []"),
+    "__proto__ node is displayed as expected");
+});