--- 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-dom-factories"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react"), require("devtools/client/shared/vendor/react-redux"), require("devtools/client/shared/vendor/redux"));
+ module.exports = factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react"), require("devtools/client/shared/vendor/react-redux"), require("devtools/client/shared/vendor/redux"));
else if(typeof define === 'function' && define.amd)
- define(["devtools/client/shared/vendor/react-dom-factories", "devtools/client/shared/vendor/lodash", "devtools/client/shared/vendor/react-prop-types", "devtools/client/shared/vendor/react", "devtools/client/shared/vendor/react-redux", "devtools/client/shared/vendor/redux"], factory);
+ define(["devtools/client/shared/vendor/react-dom-factories", "devtools/client/shared/vendor/react-prop-types", "devtools/client/shared/vendor/react", "devtools/client/shared/vendor/react-redux", "devtools/client/shared/vendor/redux"], factory);
else {
- var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react"), require("devtools/client/shared/vendor/react-redux"), require("devtools/client/shared/vendor/redux")) : factory(root["devtools/client/shared/vendor/react-dom-factories"], root["devtools/client/shared/vendor/lodash"], root["devtools/client/shared/vendor/react-prop-types"], root["devtools/client/shared/vendor/react"], root["devtools/client/shared/vendor/react-redux"], root["devtools/client/shared/vendor/redux"]);
+ var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react"), require("devtools/client/shared/vendor/react-redux"), require("devtools/client/shared/vendor/redux")) : factory(root["devtools/client/shared/vendor/react-dom-factories"], root["devtools/client/shared/vendor/react-prop-types"], root["devtools/client/shared/vendor/react"], root["devtools/client/shared/vendor/react-redux"], root["devtools/client/shared/vendor/redux"]);
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
}
-})(typeof self !== 'undefined' ? self : this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_54__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_10__, __WEBPACK_EXTERNAL_MODULE_18__, __WEBPACK_EXTERNAL_MODULE_19__) {
+})(typeof self !== 'undefined' ? self : this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_9__, __WEBPACK_EXTERNAL_MODULE_18__, __WEBPACK_EXTERNAL_MODULE_19__) {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
@@ -80,17 +80,17 @@ return /******/ (function(modules) { //
"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 validProtocols = /(http|https|ftp|data|resource|chrome):/i;
const tokenSplitRegex = /(\s|\'|\"|\\)+/;
const ELLIPSIS = "\u2026";
const dom = __webpack_require__(1);
const { span } = dom;
/**
* Returns true if the given object is a grip (see RDP protocol)
*/
@@ -427,22 +427,22 @@ function getGripType(object, noGrip) {
* Determines whether a grip is a string containing a URL.
*
* @param string grip
* The grip, which may contain a URL.
* @return boolean
* Whether the grip is a string containing a URL.
*/
function containsURL(grip) {
- if (typeof grip !== "string") {
+ // An URL can't be shorter than 5 char (e.g. "ftp:").
+ if (typeof grip !== "string" || grip.length < 5) {
return false;
}
- let tokens = grip.split(tokenSplitRegex);
- return tokens.some(isURL);
+ return validProtocols.test(grip);
}
/**
* Determines whether a string token is a valid URL.
*
* @param string token
* The token.
* @return boolean
@@ -562,17 +562,17 @@ const NaNRep = __webpack_require__(31);
const Accessor = __webpack_require__(32);
// DOM types (grips)
const Attribute = __webpack_require__(33);
const DateTime = __webpack_require__(34);
const Document = __webpack_require__(35);
const DocumentType = __webpack_require__(36);
const Event = __webpack_require__(37);
-const Func = __webpack_require__(12);
+const Func = __webpack_require__(11);
const PromiseRep = __webpack_require__(38);
const RegExp = __webpack_require__(39);
const StyleSheet = __webpack_require__(40);
const CommentNode = __webpack_require__(41);
const ElementNode = __webpack_require__(42);
const TextNode = __webpack_require__(43);
const ErrorRep = __webpack_require__(13);
const Window = __webpack_require__(44);
@@ -584,17 +584,17 @@ const GripMapEntry = __webpack_require__
const Grip = __webpack_require__(8);
// List of all registered template.
// XXX there should be a way for extensions to register a new
// or modify an existing rep.
let reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, ElementNode, TextNode, Attribute, Func, PromiseRep, ArrayRep, Document, DocumentType, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, GripMapEntry, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep, Accessor];
/**
- * Generic rep that is using for rendering native JS types or an object.
+ * Generic rep that is used for rendering native JS types or an object.
* The right template used for rendering is picked automatically according
* to the current value type. The value must be passed is as 'object'
* property.
*/
const Rep = function (props) {
let {
object,
defaultRep
@@ -608,17 +608,17 @@ const Rep = function (props) {
/**
* Return a rep object that is responsible for rendering given
* object.
*
* @param object {Object} Object to be rendered in the UI. This
* can be generic JS object as well as a grip (handle to a remote
* debuggee object).
*
- * @param defaultObject {React.Component} The default template
+ * @param defaultRep {React.Component} The default template
* that should be used to render given object if none is found.
*
* @param noGrip {Boolean} If true, will only check reps not made for remote objects.
*/
function getRep(object, defaultRep = Obj, noGrip = false) {
for (let i = 0; i < reps.length; i++) {
let rep = reps[i];
try {
@@ -708,17 +708,17 @@ const { a, span } = dom;
/**
* Renders a string. String value is enclosed within quotes.
*/
StringRep.propTypes = {
useQuotes: PropTypes.bool,
escapeWhitespace: PropTypes.bool,
style: PropTypes.object,
cropLimit: PropTypes.number.isRequired,
- member: PropTypes.string,
+ member: PropTypes.object,
object: PropTypes.object.isRequired,
openLink: PropTypes.func,
className: PropTypes.string
};
function StringRep(props) {
let {
className,
@@ -729,23 +729,29 @@ function StringRep(props) {
escapeWhitespace = true,
member,
openLink
} = props;
let text = object;
const isLong = isLongString(object);
- const shouldCrop = (!member || !member.open) && cropLimit && text.length > cropLimit;
+ const isOpen = member && member.open;
+ const shouldCrop = !isOpen && cropLimit && text.length > cropLimit;
if (isLong) {
text = maybeCropLongString({
shouldCrop,
cropLimit
}, text);
+
+ const { fullText } = object;
+ if (isOpen && fullText) {
+ text = fullText;
+ }
}
text = formatText({
useQuotes,
escapeWhitespace
}, text);
const config = getElementConfig({
@@ -772,22 +778,21 @@ function StringRep(props) {
function maybeCropLongString(opts, text) {
const {
shouldCrop,
cropLimit
} = opts;
const {
- fullText,
initial,
length
} = text;
- text = shouldCrop ? initial.substring(0, cropLimit) : fullText || initial;
+ text = shouldCrop ? initial.substring(0, cropLimit) : initial;
if (text.length < length) {
text += ELLIPSIS;
}
return text;
}
@@ -1502,70 +1507,36 @@ let Grip = {
maxLengthMap
};
// Exports from this module
module.exports = Grip;
/***/ }),
/* 9 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-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
-};
+/***/ (function(module, exports) {
+
+module.exports = __WEBPACK_EXTERNAL_MODULE_9__;
/***/ }),
/* 10 */
-/***/ (function(module, exports) {
-
-module.exports = __WEBPACK_EXTERNAL_MODULE_10__;
-
-/***/ }),
-/* 11 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const client = __webpack_require__(20);
const loadProperties = __webpack_require__(21);
const node = __webpack_require__(22);
const { nodeIsError, nodeIsPrimitive } = node;
-const selection = __webpack_require__(55);
+const selection = __webpack_require__(54);
const { MODE } = __webpack_require__(3);
const {
REPS: {
Rep,
Grip
}
} = __webpack_require__(4);
@@ -1595,17 +1566,17 @@ module.exports = {
loadProperties,
node,
renderRep,
selection,
shouldRenderRootsInReps
};
/***/ }),
-/* 12 */
+/* 11 */
/***/ (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/. */
@@ -1767,16 +1738,50 @@ module.exports = {
rep: wrapRender(FunctionRep),
supportsObject,
cleanFunctionName,
// exported for testing purpose.
getFunctionName
};
/***/ }),
+/* 12 */
+/***/ (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
+};
+
+/***/ }),
/* 13 */
/***/ (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
@@ -1785,17 +1790,17 @@ module.exports = {
// ReactJS
const PropTypes = __webpack_require__(2);
// Utils
const {
getGripType,
isGrip,
wrapRender
} = __webpack_require__(0);
-const { cleanFunctionName } = __webpack_require__(12);
+const { cleanFunctionName } = __webpack_require__(11);
const { isLongString } = __webpack_require__(5);
const { MODE } = __webpack_require__(3);
const dom = __webpack_require__(1);
const { span } = dom;
const IGNORED_SOURCE_URLS = ["debugger eval code"];
/**
@@ -2489,17 +2494,17 @@ function GripMapEntry(props) {
suppressQuotes: false
}));
}
function supportsObject(grip, noGrip = false) {
if (noGrip === true) {
return false;
}
- return grip && grip.type === "mapEntry" && grip.preview;
+ return grip && (grip.type === "mapEntry" || grip.type === "storageEntry") && grip.preview;
}
function createGripMapEntry(key, value) {
return {
type: "mapEntry",
preview: {
key,
value
@@ -2582,28 +2587,51 @@ async function enumSymbols(objectClient,
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, object) {
+ const { initial, length } = object;
+
+ 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
+ getPrototype,
+ getFullText
};
/***/ }),
/* 21 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
@@ -2612,36 +2640,38 @@ 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/. */
const {
enumEntries,
enumIndexedProperties,
enumNonIndexedProperties,
getPrototype,
- enumSymbols
+ enumSymbols,
+ getFullText
} = __webpack_require__(20);
const {
getClosestGripNode,
getClosestNonBucketNode,
getValue,
nodeHasAccessors,
nodeHasAllEntriesInPreview,
nodeHasProperties,
nodeIsBucket,
nodeIsDefaultProperties,
nodeIsEntries,
nodeIsMapEntry,
nodeIsPrimitive,
nodeIsProxy,
- nodeNeedsNumericalBuckets
+ nodeNeedsNumericalBuckets,
+ nodeIsLongString
} = __webpack_require__(22);
-function loadItemProperties(item, createObjectClient, loadedProperties) {
+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] : [];
let promises = [];
let objectClient;
const getObjectClient = () => objectClient || createObjectClient(value);
@@ -2661,16 +2691,20 @@ function loadItemProperties(item, create
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), value));
+ }
+
return Promise.all(promises).then(mergeResponses);
}
function mergeResponses(responses) {
const data = {};
for (const response of responses) {
if (response.hasOwnProperty("ownProperties")) {
@@ -2679,16 +2713,20 @@ function mergeResponses(responses) {
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);
@@ -2712,53 +2750,58 @@ function shouldLoadItemEntries(item, loa
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);
+ 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) && !nodeIsProxy(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
+ shouldLoadItemSymbols,
+ shouldLoadItemFullText
};
/***/ }),
/* 22 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-const { get, has } = __webpack_require__(54);
const { maybeEscapePropertyName } = __webpack_require__(0);
const ArrayRep = __webpack_require__(6);
const GripArrayRep = __webpack_require__(14);
const GripMap = __webpack_require__(16);
const GripMapEntryRep = __webpack_require__(17);
const ErrorRep = __webpack_require__(13);
+const { isLongString } = __webpack_require__(5);
const MAX_NUMERICAL_PROPERTIES = 100;
const NODE_TYPES = {
BUCKET: Symbol("[n…m]"),
DEFAULT_PROPERTIES: Symbol("<default properties>"),
ENTRIES: Symbol("<entries>"),
GET: Symbol("<get>"),
@@ -2781,22 +2824,22 @@ if (typeof window === "object") {
WINDOW_PROPERTIES = Object.getOwnPropertyNames(window);
}
function getType(item) {
return item.type;
}
function getValue(item) {
- if (has(item, "contents.value")) {
- return get(item, "contents.value");
- }
-
- if (has(item, "contents.getterValue")) {
- return get(item, "contents.getterValue", undefined);
+ if (item && item.contents && item.contents.hasOwnProperty("value")) {
+ return item.contents.value;
+ }
+
+ if (item && item.contents && item.contents.hasOwnProperty("getterValue")) {
+ return item.contents.getterValue;
}
if (nodeHasAccessors(item)) {
return item.contents;
}
return undefined;
}
@@ -2863,17 +2906,17 @@ function nodeIsMissingArguments(item) {
return !nodeHasChildren(item) && value && value.missingArguments;
}
function nodeHasProperties(item) {
return !nodeHasChildren(item) && nodeIsObject(item);
}
function nodeIsPrimitive(item) {
- return !nodeHasChildren(item) && !nodeHasProperties(item) && !nodeIsEntries(item) && !nodeIsMapEntry(item) && !nodeHasAccessors(item) && !nodeIsBucket(item);
+ return !nodeHasChildren(item) && !nodeHasProperties(item) && !nodeIsEntries(item) && !nodeIsMapEntry(item) && !nodeHasAccessors(item) && !nodeIsBucket(item) && !nodeIsLongString(item);
}
function nodeIsDefaultProperties(item) {
return getType(item) === NODE_TYPES.DEFAULT_PROPERTIES;
}
function isDefaultWindowProperty(name) {
return WINDOW_PROPERTIES.includes(name);
@@ -2921,33 +2964,42 @@ function nodeIsSetter(item) {
function nodeIsBlock(item) {
return getType(item) === NODE_TYPES.BLOCK;
}
function nodeIsError(item) {
return ErrorRep.supportsObject(getValue(item));
}
+function nodeIsLongString(item) {
+ return isLongString(getValue(item));
+}
+
+function nodeHasFullText(item) {
+ const value = getValue(item);
+ return nodeIsLongString(item) && value.hasOwnProperty("fullText");
+}
+
function nodeHasAccessors(item) {
return !!getNodeGetter(item) || !!getNodeSetter(item);
}
function nodeSupportsNumericalBucketing(item) {
// We exclude elements with entries since it's the <entries> node
// itself that can have buckets.
return nodeIsArrayLike(item) && !nodeHasEntries(item) || nodeIsEntries(item) || nodeIsBucket(item);
}
function nodeHasEntries(item) {
const value = getValue(item);
if (!value) {
return false;
}
- return value.class === "Map" || value.class === "Set" || value.class === "WeakMap" || value.class === "WeakSet";
+ return value.class === "Map" || value.class === "Set" || value.class === "WeakMap" || value.class === "WeakSet" || value.class === "Storage";
}
function nodeHasAllEntriesInPreview(item) {
const { preview } = getValue(item) || {};
if (!preview) {
return false;
}
@@ -3081,21 +3133,21 @@ function makeNodesForMapEntry(item) {
parent: item,
name: "<value>",
contents: { value },
type: NODE_TYPES.MAP_ENTRY_VALUE
})];
}
function getNodeGetter(item) {
- return get(item, "contents.get", undefined);
+ return item && item.contents ? item.contents.get : undefined;
}
function getNodeSetter(item) {
- return get(item, "contents.set", undefined);
+ return item && item.contents ? item.contents.set : undefined;
}
function makeNodesForAccessors(item) {
const accessors = [];
const getter = getNodeGetter(item);
if (getter && getter.type !== "undefined") {
accessors.push(createNode({
@@ -3255,16 +3307,28 @@ function makeNodesForProperties(objProps
// Add the prototype if it exists and is not null
if (prototype && prototype.type !== "null") {
nodes.push(makeNodeForPrototype(objProps, parent));
}
return nodes;
}
+function setNodeFullText(loadedProps, node) {
+ if (nodeHasFullText(node)) {
+ return node;
+ }
+
+ if (nodeIsLongString(node)) {
+ node.contents.value.fullText = loadedProps.fullText;
+ }
+
+ return node;
+}
+
function makeNodeForPrototype(objProps, parent) {
const {
prototype
} = objProps || {};
// Add the prototype if it exists and is not null
if (prototype && prototype.type !== "null") {
return createNode({
@@ -3360,19 +3424,23 @@ function getChildren(options) {
if (nodeIsMapEntry(item)) {
return addToCache(makeNodesForMapEntry(item));
}
if (nodeIsProxy(item)) {
return addToCache(makeNodesForProxyProperties(item));
}
+ if (nodeIsLongString(item) && hasLoadedProps) {
+ // Set longString object's fullText to fetched one.
+ return addToCache(setNodeFullText(loadedProps, item));
+ }
+
if (nodeNeedsNumericalBuckets(item) && hasLoadedProps) {
// Even if we have numerical buckets, we should have loaded non indexed properties,
- // like length for example.
const bucketNodes = makeNumericalBuckets(item);
return addToCache(bucketNodes.concat(makeNodesForProperties(loadedProps, item)));
}
if (!nodeIsEntries(item) && !nodeIsBucket(item) && !nodeHasProperties(item)) {
return [];
}
@@ -3458,16 +3526,18 @@ module.exports = {
nodeHasChildren,
nodeHasEntries,
nodeHasProperties,
nodeIsBlock,
nodeIsBucket,
nodeIsDefaultProperties,
nodeIsEntries,
nodeIsError,
+ nodeIsLongString,
+ nodeHasFullText,
nodeIsFunction,
nodeIsGetter,
nodeIsMapEntry,
nodeIsMissingArguments,
nodeIsObject,
nodeIsOptimizedOut,
nodeIsPrimitive,
nodeIsPromise,
@@ -3494,17 +3564,17 @@ module.exports = {
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const { MODE } = __webpack_require__(3);
const { REPS, getRep } = __webpack_require__(4);
const ObjectInspector = __webpack_require__(47);
-const ObjectInspectorUtils = __webpack_require__(11);
+const ObjectInspectorUtils = __webpack_require__(10);
const {
parseURLEncodedText,
parseURLParams,
maybeEscapePropertyName,
getGripPreviewItems
} = __webpack_require__(0);
@@ -4652,17 +4722,17 @@ module.exports = {
const PropTypes = __webpack_require__(2);
const {
isGrip,
cropString,
cropMultipleLines,
wrapRender
} = __webpack_require__(0);
const { MODE } = __webpack_require__(3);
-const nodeConstants = __webpack_require__(9);
+const nodeConstants = __webpack_require__(12);
const dom = __webpack_require__(1);
const { span } = dom;
/**
* Renders DOM comment node.
*/
CommentNode.propTypes = {
object: PropTypes.object.isRequired,
@@ -4719,92 +4789,118 @@ const PropTypes = __webpack_require__(2)
// Utils
const {
isGrip,
wrapRender
} = __webpack_require__(0);
const { rep: StringRep } = __webpack_require__(5);
const { MODE } = __webpack_require__(3);
-const nodeConstants = __webpack_require__(9);
+const nodeConstants = __webpack_require__(12);
const dom = __webpack_require__(1);
const { span } = dom;
/**
* Renders DOM element node.
*/
ElementNode.propTypes = {
object: PropTypes.object.isRequired,
+ inspectIconTitle: PropTypes.string,
// @TODO Change this to Object.values once it's supported in Node's version of V8
mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+ onDOMNodeClick: PropTypes.func,
onDOMNodeMouseOver: PropTypes.func,
onDOMNodeMouseOut: PropTypes.func,
onInspectIconClick: PropTypes.func
};
function ElementNode(props) {
let {
object,
+ inspectIconTitle,
mode,
+ onDOMNodeClick,
onDOMNodeMouseOver,
onDOMNodeMouseOut,
onInspectIconClick
} = props;
let elements = getElements(object, mode);
let isInTree = object.preview && object.preview.isConnected === true;
let baseConfig = {
"data-link-actor-id": object.actor,
className: "objectBox objectBox-node"
};
let inspectIcon;
if (isInTree) {
+ if (onDOMNodeClick) {
+ Object.assign(baseConfig, {
+ onClick: _ => onDOMNodeClick(object)
+ });
+ }
+
if (onDOMNodeMouseOver) {
Object.assign(baseConfig, {
onMouseOver: _ => onDOMNodeMouseOver(object)
});
}
if (onDOMNodeMouseOut) {
Object.assign(baseConfig, {
onMouseOut: onDOMNodeMouseOut
});
}
if (onInspectIconClick) {
inspectIcon = dom.button({
className: "open-inspector",
// TODO: Localize this with "openNodeInInspector" when Bug 1317038 lands
- title: "Click to select the node in the inspector",
- onClick: e => onInspectIconClick(object, e)
+ title: inspectIconTitle || "Click to select the node in the inspector",
+ onClick: e => {
+ if (onDOMNodeClick) {
+ e.stopPropagation();
+ }
+
+ onInspectIconClick(object, e);
+ }
});
}
}
return span(baseConfig, ...elements, inspectIcon);
}
function getElements(grip, mode) {
- let { attributes, nodeName } = grip.preview;
+ let {
+ attributes,
+ nodeName,
+ isAfterPseudoElement,
+ isBeforePseudoElement
+ } = grip.preview;
const nodeNameElement = span({
className: "tag-name"
}, nodeName);
+ if (isAfterPseudoElement || isBeforePseudoElement) {
+ return [span({ className: "attrName" }, `::${isAfterPseudoElement ? "after" : "before"}`)];
+ }
+
if (mode === MODE.TINY) {
let elements = [nodeNameElement];
if (attributes.id) {
elements.push(span({ className: "attrName" }, `#${attributes.id}`));
}
if (attributes.class) {
elements.push(span({ className: "attrName" }, attributes.class.trim().split(/\s+/).map(cls => `.${cls}`).join("")));
}
return elements;
}
+
let attributeKeys = Object.keys(attributes);
if (attributeKeys.includes("class")) {
attributeKeys.splice(attributeKeys.indexOf("class"), 1);
attributeKeys.unshift("class");
}
if (attributeKeys.includes("id")) {
attributeKeys.splice(attributeKeys.indexOf("id"), 1);
attributeKeys.unshift("id");
@@ -5159,21 +5255,21 @@ 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 { createElement, createFactory, PureComponent } = __webpack_require__(10);
+const { createElement, createFactory, PureComponent } = __webpack_require__(9);
const { Provider } = __webpack_require__(18);
const ObjectInspector = createFactory(__webpack_require__(48));
-const createStore = __webpack_require__(57);
-const Utils = __webpack_require__(11);
+const createStore = __webpack_require__(56);
+const Utils = __webpack_require__(10);
const {
renderRep,
shouldRenderRootsInReps
} = Utils;
class OI extends PureComponent {
constructor(props) {
@@ -5213,30 +5309,30 @@ function _interopRequireDefault(obj) { r
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const {
Component,
createFactory
-} = __webpack_require__(10);
+} = __webpack_require__(9);
const dom = __webpack_require__(1);
const { connect } = __webpack_require__(18);
const { bindActionCreators } = __webpack_require__(19);
const Tree = createFactory(_devtoolsComponents2.default.Tree);
__webpack_require__(52);
const classnames = __webpack_require__(53);
const {
MODE
} = __webpack_require__(3);
-const Utils = __webpack_require__(11);
+const Utils = __webpack_require__(10);
const {
getChildren,
getClosestGripNode,
getParent,
getValue,
nodeHasAccessors,
nodeHasProperties,
@@ -5248,17 +5344,19 @@ const {
nodeIsMissingArguments,
nodeIsOptimizedOut,
nodeIsPrimitive,
nodeIsPrototype,
nodeIsSetter,
nodeIsUninitializedBinding,
nodeIsUnmappedBinding,
nodeIsUnscopedBinding,
- nodeIsWindow
+ nodeIsWindow,
+ nodeIsLongString,
+ nodeHasFullText
} = Utils.node;
// This implements a component that renders an interactive inspector
// for looking at JavaScript objects. It expects descriptions of
// objects from the protocol, and will dynamically fetch children
// properties as objects are expanded.
//
// If you want to inspect a single object, pass the name and the
@@ -5295,32 +5393,54 @@ class ObjectInspector extends Component
self.setExpanded = this.setExpanded.bind(this);
self.focusItem = this.focusItem.bind(this);
self.getRoots = this.getRoots.bind(this);
}
shouldComponentUpdate(nextProps) {
const {
expandedPaths,
+ focusedItem,
loadedProperties,
roots
} = this.props;
if (roots !== nextProps.roots) {
- // Since the roots changed, we assume the properties did as well. Thus we can clear
- // the cachedNodes to avoid bugs and memory leaks.
+ // Since the roots changed, we assume the properties did as well, so we need to
+ // cleanup the component internal state.
+
+ // We can clear the cachedNodes to avoid bugs and memory leaks.
this.cachedNodes.clear();
+ // The rootsChanged action will be handled in a middleware to release the actors
+ // of the old roots, as well as cleanup the state properties (expandedPaths,
+ // loadedProperties, …).
+ this.props.rootsChanged(nextProps);
+ // We don't render right away since the state is going to be changed by the
+ // rootsChanged action. The `state.forceUpdate` flag will be set to `true` so we
+ // can execute a new render cycle with the cleaned state.
+ return false;
+ }
+
+ if (nextProps.forceUpdate === true) {
return true;
}
// We should update if:
// - there are new loaded properties
// - OR the expanded paths number changed, and all of them have properties loaded
// - OR the expanded paths number did not changed, but old and new sets differ
- return loadedProperties.size !== nextProps.loadedProperties.size || expandedPaths.size !== nextProps.expandedPaths.size && [...nextProps.expandedPaths].every(path => nextProps.loadedProperties.has(path)) || expandedPaths.size === nextProps.expandedPaths.size && [...nextProps.expandedPaths].some(key => !expandedPaths.has(key));
+ // - OR the focused node changed.
+ return loadedProperties.size !== nextProps.loadedProperties.size || expandedPaths.size !== nextProps.expandedPaths.size && [...nextProps.expandedPaths].every(path => nextProps.loadedProperties.has(path)) || expandedPaths.size === nextProps.expandedPaths.size && [...nextProps.expandedPaths].some(key => !expandedPaths.has(key)) || focusedItem !== nextProps.focusedItem;
+ }
+
+ componentDidUpdate(prevProps) {
+ if (this.props.forceUpdate) {
+ // If the component was updated, we can then reset the forceUpdate flag.
+ this.props.forceUpdated();
+ }
}
componentWillUnmount() {
const { releaseActor } = this.props;
if (typeof releaseActor !== "function") {
return;
}
@@ -5353,49 +5473,55 @@ class ObjectInspector extends Component
setExpanded(item, expand) {
if (nodeIsPrimitive(item)) {
return;
}
const {
createObjectClient,
+ createLongStringClient,
loadedProperties,
nodeExpand,
nodeCollapse,
roots
} = this.props;
if (expand === true) {
const gripItem = getClosestGripNode(item);
const value = getValue(gripItem);
const isRoot = value && roots.some(root => {
const rootValue = getValue(root);
return rootValue && rootValue.actor === value.actor;
});
const actor = isRoot || !value ? null : value.actor;
- nodeExpand(item, actor, loadedProperties, createObjectClient);
+ nodeExpand(item, actor, loadedProperties, createObjectClient, createLongStringClient);
} else {
nodeCollapse(item);
}
}
focusItem(item) {
const {
+ focusable = true,
focusedItem,
+ nodeFocus,
onFocus
} = this.props;
- if (focusedItem !== item && onFocus) {
- onFocus(item);
+ if (focusable && focusedItem !== item) {
+ nodeFocus(item);
+ if (focusedItem !== item && onFocus) {
+ onFocus(item);
+ }
}
}
getTreeItemLabelAndValue(item, depth, expanded) {
- let label = item.name;
+ const label = item.name;
const isPrimitive = nodeIsPrimitive(item);
if (nodeIsOptimizedOut(item)) {
return {
label,
value: dom.span({ className: "unavailable" }, "(optimized away)")
};
}
@@ -5442,28 +5568,34 @@ class ObjectInspector extends Component
return {
label: Utils.renderRep(item, {
...this.props,
functionName: label
})
};
}
- if (nodeHasProperties(item) || nodeHasAccessors(item) || nodeIsMapEntry(item) || isPrimitive) {
- let repsProp = { ...this.props };
+ if (nodeHasProperties(item) || nodeHasAccessors(item) || nodeIsMapEntry(item) || nodeIsLongString(item) || isPrimitive) {
+ let repProps = { ...this.props };
if (depth > 0) {
- repsProp.mode = this.props.mode === MODE.LONG ? MODE.SHORT : MODE.TINY;
+ repProps.mode = this.props.mode === MODE.LONG ? MODE.SHORT : MODE.TINY;
}
if (expanded) {
- repsProp.mode = MODE.TINY;
+ repProps.mode = MODE.TINY;
+ }
+
+ if (nodeIsLongString(item)) {
+ repProps.member = {
+ open: nodeHasFullText(item) && expanded
+ };
}
return {
label,
- value: Utils.renderRep(item, repsProp)
+ value: Utils.renderRep(item, repProps)
};
}
return {
label
};
}
@@ -5493,35 +5625,48 @@ class ObjectInspector extends Component
setExpanded: this.setExpanded
});
} : undefined
}, label);
}
getTreeTopElementProps(item, depth, focused, expanded) {
const {
+ onCmdCtrlClick,
onDoubleClick,
dimTopLevelWindow
} = this.props;
let parentElementProps = {
className: classnames("node object-node", {
focused,
lessen: !expanded && (nodeIsDefaultProperties(item) || nodeIsPrototype(item) || dimTopLevelWindow === true && nodeIsWindow(item) && depth === 0),
block: nodeIsBlock(item)
}),
onClick: e => {
- e.stopPropagation();
-
- // If the user selected text, bail out.
- if (Utils.selection.documentHasSelection()) {
+ if (e.metaKey && onCmdCtrlClick) {
+ onCmdCtrlClick(item, {
+ depth,
+ event: e,
+ focused,
+ expanded
+ });
+ e.stopPropagation();
return;
}
- this.setExpanded(item, !expanded);
+ // If this click happened because the user selected some text, bail out.
+ // Note that if the user selected some text before and then clicks here,
+ // the previously selected text will be first unselected, unless the user
+ // clicked on the arrow itself. Indeed because the arrow is an image, clicking on
+ // it does not remove any existing text selection. So we need to also check if
+ // teh arrow was clicked.
+ if (Utils.selection.documentHasSelection() && !(e.target && e.target.matches && e.target.matches(".arrow"))) {
+ e.stopPropagation();
+ }
}
};
if (onDoubleClick) {
parentElementProps.onDoubleClick = e => {
e.stopPropagation();
onDoubleClick(item, {
depth,
@@ -5541,62 +5686,63 @@ class ObjectInspector extends Component
return dom.div(this.getTreeTopElementProps(item, depth, focused, expanded), arrow, labelElement, delimiter, value);
}
render() {
const {
autoExpandAll = true,
autoExpandDepth = 1,
- disabledFocus,
+ focusable = true,
disableWrap = false,
expandedPaths,
focusedItem,
inline
} = this.props;
return Tree({
className: classnames({
inline,
nowrap: disableWrap,
"object-inspector": true
}),
autoExpandAll,
autoExpandDepth,
- disabledFocus,
isExpanded: item => expandedPaths && expandedPaths.has(item.path),
isExpandable: item => nodeIsPrimitive(item) === false,
focused: focusedItem,
getRoots: this.getRoots,
getParent,
getChildren: this.getItemChildren,
getKey: this.getNodeKey,
onExpand: item => this.setExpanded(item, true),
onCollapse: item => this.setExpanded(item, false),
- onFocus: this.focusItem,
+ onFocus: focusable ? this.focusItem : null,
renderItem: this.renderTreeItem
});
}
}
function mapStateToProps(state, props) {
return {
actors: state.actors,
expandedPaths: state.expandedPaths,
- focusedItem: state.focusedItem,
- loadedProperties: state.loadedProperties
+ // If the root changes, we want to pass a possibly new focusedItem property
+ focusedItem: state.roots !== props.roots ? props.focusedItem : state.focusedItem,
+ loadedProperties: state.loadedProperties,
+ forceUpdate: state.forceUpdate
};
}
function mapDispatchToProps(dispatch) {
- return bindActionCreators(__webpack_require__(56), dispatch);
+ return bindActionCreators(__webpack_require__(55), dispatch);
}
module.exports = connect(mapStateToProps, mapDispatchToProps)(ObjectInspector);
/***/ }),
/* 49 */
/***/ (function(module, exports, __webpack_require__) {
@@ -5621,17 +5767,17 @@ module.exports = {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
-var _react = __webpack_require__(10);
+var _react = __webpack_require__(9);
var _react2 = _interopRequireDefault(_react);
var _reactDomFactories = __webpack_require__(1);
var _reactDomFactories2 = _interopRequireDefault(_reactDomFactories);
var _propTypes = __webpack_require__(2);
@@ -5955,16 +6101,19 @@ class Tree extends Component {
// onExpand(item: Item)
// onCollapse(item: Item)
//
// Example:
//
// onExpand: item => dispatchExpandActionToRedux(item)
onExpand: _propTypes2.default.func,
onCollapse: _propTypes2.default.func,
+ // Optional event handler called with the current focused node when the Enter key
+ // is pressed. Can be useful to allow further keyboard actions within the tree node.
+ onActivate: _propTypes2.default.func,
isExpandable: _propTypes2.default.func,
// Additional classes to add to the root element.
className: _propTypes2.default.string,
// style object to be applied to the root element.
style: _propTypes2.default.object
};
}
@@ -5982,42 +6131,49 @@ class Tree extends Component {
seen: new Set()
};
this._onExpand = oncePerAnimationFrame(this._onExpand).bind(this);
this._onCollapse = oncePerAnimationFrame(this._onCollapse).bind(this);
this._focusPrevNode = oncePerAnimationFrame(this._focusPrevNode).bind(this);
this._focusNextNode = oncePerAnimationFrame(this._focusNextNode).bind(this);
this._focusParentNode = oncePerAnimationFrame(this._focusParentNode).bind(this);
+ this._focusFirstNode = oncePerAnimationFrame(this._focusFirstNode).bind(this);
+ this._focusLastNode = oncePerAnimationFrame(this._focusLastNode).bind(this);
this._autoExpand = this._autoExpand.bind(this);
this._preventArrowKeyScrolling = this._preventArrowKeyScrolling.bind(this);
this._dfs = this._dfs.bind(this);
this._dfsFromRoots = this._dfsFromRoots.bind(this);
this._focus = this._focus.bind(this);
this._scrollNodeIntoView = this._scrollNodeIntoView.bind(this);
this._onBlur = this._onBlur.bind(this);
this._onKeyDown = this._onKeyDown.bind(this);
this._nodeIsExpandable = this._nodeIsExpandable.bind(this);
+ this._activateNode = oncePerAnimationFrame(this._activateNode).bind(this);
}
componentDidMount() {
this._autoExpand();
if (this.props.focused) {
this._scrollNodeIntoView(this.props.focused);
+ // Always keep the focus on the tree itself.
+ this.treeRef.focus();
}
}
componentWillReceiveProps(nextProps) {
this._autoExpand();
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.focused !== this.props.focused) {
this._scrollNodeIntoView(this.props.focused);
+ // Always keep the focus on the tree itself.
+ this.treeRef.focus();
}
}
_autoExpand() {
if (!this.props.autoExpandDepth) {
return;
}
@@ -6147,18 +6303,21 @@ class Tree extends Component {
* The item to be focused, or undefined to focus no item.
*
* @param {Object|undefined} options
* An options object which can contain:
* - dir: "up" or "down" to indicate if we should scroll the element to the
* top or the bottom of the scrollable container when the element is
* off canvas.
*/
- _focus(item, options) {
- this._scrollNodeIntoView(item, options);
+ _focus(item, options = {}) {
+ const { preventAutoScroll } = options;
+ if (item && !preventAutoScroll) {
+ this._scrollNodeIntoView(item, options);
+ }
if (this.props.onFocus) {
this.props.onFocus(item);
}
}
/**
* Sets the passed in item to be the focused item.
*
@@ -6170,33 +6329,36 @@ class Tree extends Component {
* - dir: "up" or "down" to indicate if we should scroll the element to the
* top or the bottom of the scrollable container when the element is
* off canvas.
*/
_scrollNodeIntoView(item, options = {}) {
if (item !== undefined) {
const treeElement = this.treeRef;
const element = document.getElementById(this.props.getKey(item));
+
if (element) {
const { top, bottom } = element.getBoundingClientRect();
const closestScrolledParent = node => {
if (node == null) {
return null;
}
if (node.scrollHeight > node.clientHeight) {
return node;
}
return closestScrolledParent(node.parentNode);
};
const scrolledParent = closestScrolledParent(treeElement);
- const isVisible = !scrolledParent || top >= 0 && bottom <= scrolledParent.clientHeight;
+ const scrolledParentRect = scrolledParent ? scrolledParent.getBoundingClientRect() : null;
+ const isVisible = !scrolledParent || top >= scrolledParentRect.top && bottom <= scrolledParentRect.bottom;
if (!isVisible) {
- let scrollToTop = !options.alignTo && top < 0 || options.alignTo === "top";
+ const { alignTo } = options;
+ let scrollToTop = alignTo ? alignTo === "top" : !scrolledParentRect || top < scrolledParentRect.top;
element.scrollIntoView(scrollToTop);
}
}
}
}
/**
* Sets the state to have no focused item.
@@ -6240,16 +6402,28 @@ class Tree extends Component {
return;
case "ArrowRight":
if (this._nodeIsExpandable(this.props.focused) && !this.props.isExpanded(this.props.focused)) {
this._onExpand(this.props.focused);
} else {
this._focusNextNode();
}
+ return;
+
+ case "Home":
+ this._focusFirstNode();
+ return;
+
+ case "End":
+ this._focusLastNode();
+ return;
+
+ case "Enter":
+ this._activateNode();
}
}
/**
* Sets the previous node relative to the currently focused item, to focused.
*/
_focusPrevNode() {
// Start a depth first search and keep going until we reach the currently
@@ -6316,16 +6490,33 @@ class Tree extends Component {
if (traversal[parentIndex].item === parent) {
break;
}
}
this._focus(parent, { alignTo: "top" });
}
+ _focusFirstNode() {
+ const traversal = this._dfsFromRoots();
+ this._focus(traversal[0].item, { alignTo: "top" });
+ }
+
+ _focusLastNode() {
+ const traversal = this._dfsFromRoots();
+ const lastIndex = traversal.length - 1;
+ this._focus(traversal[lastIndex].item, { alignTo: "bottom" });
+ }
+
+ _activateNode() {
+ if (this.props.onActivate) {
+ this.props.onActivate(this.props.focused);
+ }
+ }
+
_nodeIsExpandable(item) {
return this.props.isExpandable ? this.props.isExpandable(item) : !!this.props.getChildren(item).length;
}
render() {
const traversal = this._dfsFromRoots();
const {
focused
@@ -6342,17 +6533,19 @@ class Tree extends Component {
depth,
renderItem: this.props.renderItem,
focused: focused === item,
expanded: this.props.isExpanded(item),
isExpandable: this._nodeIsExpandable(item),
onExpand: this._onExpand,
onCollapse: this._onCollapse,
onClick: e => {
- this._focus(item);
+ // Since the user just clicked the node, there's no need to check if it should
+ // be scrolled into view.
+ this._focus(item, { preventAutoScroll: true });
if (this.props.isExpanded(item)) {
this.props.onCollapse(item);
} else {
this.props.onExpand(item, e.altKey);
}
}
});
});
@@ -6464,84 +6657,64 @@ var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBP
} else {
window.classNames = classNames;
}
}());
/***/ }),
/* 54 */
-/***/ (function(module, exports) {
-
-module.exports = __WEBPACK_EXTERNAL_MODULE_54__;
-
-/***/ }),
-/* 55 */
/***/ (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 { ELEMENT_NODE } = __webpack_require__(9);
-
function documentHasSelection() {
const selection = getSelection();
if (!selection) {
return false;
}
- const {
- anchorNode,
- focusNode
- } = selection;
-
- // When clicking the arrow, which is an inline svg element, the selection do have a type
- // of "Range". We need to have an explicit case when the anchor and the focus node are
- // the same and they have an arrow ancestor.
- if (focusNode && focusNode === anchorNode && focusNode.nodeType == ELEMENT_NODE && focusNode.closest(".arrow")) {
- return false;
- }
-
return selection.type === "Range";
}
module.exports = {
documentHasSelection
};
/***/ }),
-/* 56 */
+/* 55 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const {
loadItemProperties
} = __webpack_require__(21); /* 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/. */
/**
* 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, loadedProperties, createObjectClient) {
+function nodeExpand(node, actor, loadedProperties, createObjectClient, createLongStringClient) {
return async ({ dispatch }) => {
dispatch({
type: "NODE_EXPAND",
data: { node }
});
if (!loadedProperties.has(node.path)) {
- dispatch(nodeLoadProperties(node, actor, loadedProperties, createObjectClient));
+ dispatch(nodeLoadProperties(node, actor, loadedProperties, createObjectClient, createLongStringClient));
}
};
}
function nodeCollapse(node) {
return {
type: "NODE_COLLAPSE",
data: { node }
@@ -6553,79 +6726,130 @@ function nodeFocus(node) {
type: "NODE_FOCUS",
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(item, actor, loadedProperties, createObjectClient) {
+function nodeLoadProperties(item, actor, loadedProperties, createObjectClient, createLongStringClient) {
return async ({ dispatch }) => {
try {
- const properties = await loadItemProperties(item, createObjectClient, loadedProperties);
+ const properties = await loadItemProperties(item, createObjectClient, createLongStringClient, loadedProperties);
dispatch(nodePropertiesLoaded(item, actor, properties));
} catch (e) {
console.error(e);
}
};
}
function nodePropertiesLoaded(node, actor, properties) {
return {
type: "NODE_PROPERTIES_LOADED",
data: { node, actor, properties }
};
}
+/*
+ * 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 {
+ type: "ROOTS_CHANGED",
+ data: props
+ };
+}
+
+/*
+ * This action will reset the `forceUpdate` flag in the state.
+ */
+function forceUpdated() {
+ return {
+ type: "FORCE_UPDATED"
+ };
+}
+
module.exports = {
+ forceUpdated,
nodeExpand,
nodeCollapse,
nodeFocus,
nodeLoadProperties,
- nodePropertiesLoaded
+ nodePropertiesLoaded,
+ rootsChanged
};
/***/ }),
-/* 57 */
+/* 56 */
/***/ (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 { applyMiddleware, createStore } = __webpack_require__(19);
-const { thunk } = __webpack_require__(58);
-const { waitUntilService } = __webpack_require__(59);
-const reducer = __webpack_require__(60);
+const { applyMiddleware, createStore, compose } = __webpack_require__(19);
+const { thunk } = __webpack_require__(57);
+const { waitUntilService } = __webpack_require__(58);
+const reducer = __webpack_require__(59);
function createInitialState(overrides) {
return {
actors: new Set(),
expandedPaths: new Set(),
focusedItem: null,
loadedProperties: new Map(),
+ forceUpdated: false,
...overrides
};
}
+function enableStateReinitializer(props) {
+ return next => (innerReducer, initialState, enhancer) => {
+ function reinitializerEnhancer(state, action) {
+ if (action.type !== "ROOTS_CHANGED") {
+ return innerReducer(state, action);
+ }
+
+ if (props.releaseActor && initialState.actors) {
+ initialState.actors.forEach(props.releaseActor);
+ }
+
+ return {
+ ...action.data,
+ actors: new Set(),
+ expandedPaths: new Set(),
+ loadedProperties: new Map(),
+ // Indicates to the component that we do want to render on the next render cycle.
+ forceUpdate: true
+ };
+ }
+ return next(reinitializerEnhancer, initialState, enhancer);
+ };
+}
+
module.exports = props => {
const middlewares = [thunk];
+
if (props.injectWaitService) {
middlewares.push(waitUntilService);
}
- return createStore(reducer, createInitialState(props), applyMiddleware(...middlewares));
+ return createStore(reducer, createInitialState(props), compose(applyMiddleware(...middlewares), enableStateReinitializer(props)));
};
/***/ }),
-/* 58 */
+/* 57 */
/***/ (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/. */
@@ -6639,17 +6863,17 @@ module.exports = props => {
function thunk({ dispatch, getState }) {
return next => action => {
return typeof action === "function" ? action({ dispatch, getState }) : next(action);
};
}
exports.thunk = thunk;
/***/ }),
-/* 59 */
+/* 58 */
/***/ (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/. */
@@ -6713,17 +6937,17 @@ function waitUntilService({ dispatch, ge
}
module.exports = {
WAIT_UNTIL_TYPE,
waitUntilService
};
/***/ }),
-/* 60 */
+/* 59 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
function reducer(state = {}, action) {
const {
type,
@@ -6747,21 +6971,31 @@ function reducer(state = {}, action) {
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 === "NODE_FOCUS") {
+ if (state.focusedItem === data.node) {
+ return state;
+ }
+
return cloneState({
focusedItem: data.node
});
}
+ if (type === "FORCE_UPDATED") {
+ return cloneState({
+ forceUpdate: false
+ });
+ }
+
return state;
} /* 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 = reducer;