diff --git a/devtools/client/jsonview/components/json-panel.js b/devtools/client/jsonview/components/json-panel.js index bf992566a8a6..096d15484bb6 100644 --- a/devtools/client/jsonview/components/json-panel.js +++ b/devtools/client/jsonview/components/json-panel.js @@ -8,7 +8,8 @@ define(function (require, exports, module) { const { DOM: dom, createFactory, createClass, PropTypes } = require("devtools/client/shared/vendor/react"); - const TreeView = createFactory(require("devtools/client/shared/components/tree/tree-view")); + const TreeViewClass = require("devtools/client/shared/components/tree/tree-view"); + const TreeView = createFactory(TreeViewClass); const { REPS, MODE } = require("devtools/client/shared/components/reps/reps"); const { createFactories } = require("devtools/client/shared/react-utils"); @@ -65,28 +66,6 @@ define(function (require, exports, module) { return json.toLowerCase().indexOf(this.props.searchFilter.toLowerCase()) >= 0; }, - getExpandedNodes: function (object, path = "", level = 0) { - if (typeof object != "object") { - return null; - } - - if (level > AUTO_EXPAND_MAX_LEVEL) { - return null; - } - - let expandedNodes = new Set(); - for (let prop in object) { - let nodePath = path + "/" + prop; - expandedNodes.add(nodePath); - - let nodes = this.getExpandedNodes(object[prop], nodePath, level + 1); - if (nodes) { - expandedNodes = new Set([...expandedNodes, ...nodes]); - } - } - return expandedNodes; - }, - renderValue: props => { let member = props.member; @@ -112,7 +91,10 @@ define(function (require, exports, module) { // Expand the document by default if its size isn't bigger than 100KB. let expandedNodes = new Set(); if (this.props.jsonTextLength <= AUTO_EXPAND_MAX_SIZE) { - expandedNodes = this.getExpandedNodes(this.props.data); + expandedNodes = TreeViewClass.getExpandedNodes( + this.props.data, + {maxLevel: AUTO_EXPAND_MAX_LEVEL} + ); } // Render tree component. diff --git a/devtools/client/jsonview/test/browser.ini b/devtools/client/jsonview/test/browser.ini index a7f4c6c8c51a..852f280bc7db 100644 --- a/devtools/client/jsonview/test/browser.ini +++ b/devtools/client/jsonview/test/browser.ini @@ -35,6 +35,7 @@ skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 [browser_jsonview_save_json.js] support-files = !/toolkit/content/tests/browser/common/mockTransfer.js +[browser_jsonview_slash.js] [browser_jsonview_utf8.js] [browser_jsonview_valid_json.js] [browser_json_refresh.js] diff --git a/devtools/client/jsonview/test/browser_jsonview_slash.js b/devtools/client/jsonview/test/browser_jsonview_slash.js new file mode 100644 index 000000000000..9a242b824ee5 --- /dev/null +++ b/devtools/client/jsonview/test/browser_jsonview_slash.js @@ -0,0 +1,16 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(function* () { + info("Test JSON with NUL started."); + + const TEST_JSON_URL = "data:application/json,{\"a/b\":[1,2],\"a\":{\"b\":[3,4]}}"; + yield addJsonViewTab(TEST_JSON_URL); + + let countBefore = yield getElementCount(".jsonPanelBox .treeTable .treeRow"); + ok(countBefore == 7, "There must be seven rows"); +}); diff --git a/devtools/client/netmonitor/src/components/properties-view.js b/devtools/client/netmonitor/src/components/properties-view.js index 04b6aee2d400..1d52ae8b3a21 100644 --- a/devtools/client/netmonitor/src/components/properties-view.js +++ b/devtools/client/netmonitor/src/components/properties-view.js @@ -20,7 +20,8 @@ const { FILTER_SEARCH_DELAY } = require("../constants"); // Components const SearchBox = createFactory(require("devtools/client/shared/components/search-box")); -const TreeView = createFactory(require("devtools/client/shared/components/tree/tree-view")); +const TreeViewClass = require("devtools/client/shared/components/tree/tree-view"); +const TreeView = createFactory(TreeViewClass); const TreeRow = createFactory(require("devtools/client/shared/components/tree/tree-row")); const SourceEditor = createFactory(require("./source-editor")); @@ -137,38 +138,6 @@ const PropertiesView = createClass({ }); }, - getExpandedNodes: function (object, path = "", level = 0) { - if (typeof object != "object") { - return null; - } - - if (level > AUTO_EXPAND_MAX_LEVEL) { - return null; - } - - let expandedNodes = new Set(); - for (let prop in object) { - if (expandedNodes.size > AUTO_EXPAND_MAX_NODES) { - // If we reached the limit of expandable nodes, bail out to avoid performance - // issues. - break; - } - - let nodePath = path + "/" + prop; - expandedNodes.add(nodePath); - - let nodes = this.getExpandedNodes(object[prop], nodePath, level + 1); - if (nodes) { - let newSize = expandedNodes.size + nodes.size; - if (newSize < AUTO_EXPAND_MAX_NODES) { - // Avoid having a subtree half expanded. - expandedNodes = new Set([...expandedNodes, ...nodes]); - } - } - } - return expandedNodes; - }, - render() { const { decorator, @@ -205,7 +174,10 @@ const PropertiesView = createClass({ enableInput, expandableStrings, useQuotes: false, - expandedNodes: this.getExpandedNodes(object), + expandedNodes: TreeViewClass.getExpandedNodes( + object, + {maxLevel: AUTO_EXPAND_MAX_LEVEL, maxNodes: AUTO_EXPAND_MAX_NODES} + ), onFilter: (props) => this.onFilter(props, sectionNames), renderRow: renderRow || this.renderRowWithEditor, renderValue: renderValue || this.renderValueWithRep, diff --git a/devtools/client/netmonitor/src/components/security-panel.js b/devtools/client/netmonitor/src/components/security-panel.js index 7606448ceae2..faceb47996eb 100644 --- a/devtools/client/netmonitor/src/components/security-panel.js +++ b/devtools/client/netmonitor/src/components/security-panel.js @@ -13,6 +13,7 @@ const { L10N } = require("../utils/l10n"); const { getUrlHost } = require("../utils/request-utils"); // Components +const TreeViewClass = require("devtools/client/shared/components/tree/tree-view"); const PropertiesView = createFactory(require("./properties-view")); const { div, input, span } = DOM; @@ -95,7 +96,7 @@ function SecurityPanel({ request }) { object, renderValue: (props) => renderValue(props, securityInfo.weaknessReasons), enableFilter: false, - expandedNodes: getExpandedNodes(object), + expandedNodes: TreeViewClass.getExpandedNodes(object), }) ); } @@ -139,22 +140,4 @@ function renderValue(props, weaknessReasons = []) { ); } -function getExpandedNodes(object, path = "", level = 0) { - if (typeof object !== "object") { - return null; - } - - let expandedNodes = new Set(); - for (let prop in object) { - let nodePath = path + "/" + prop; - expandedNodes.add(nodePath); - - let nodes = getExpandedNodes(object[prop], nodePath, level + 1); - if (nodes) { - expandedNodes = new Set([...expandedNodes, ...nodes]); - } - } - return expandedNodes; -} - module.exports = SecurityPanel; diff --git a/devtools/client/shared/components/tree/tree-view.js b/devtools/client/shared/components/tree/tree-view.js index d23c9c7c0755..9d30f92ef8b0 100644 --- a/devtools/client/shared/components/tree/tree-view.js +++ b/devtools/client/shared/components/tree/tree-view.js @@ -277,7 +277,7 @@ define(function (require, exports, module) { return children.map(child => { let key = provider.getKey(child); - let nodePath = path + "/" + key; + let nodePath = TreeView.subPath(path, key); let type = provider.getType(child); let hasChildren = provider.hasChildren(child); @@ -419,6 +419,55 @@ define(function (require, exports, module) { } }); + TreeView.subPath = function (path, subKey) { + return path + "/" + subKey.replace(/[\\/]/g, "\\$&"); + }; + + /** + * Creates a set with the paths of the nodes that should be expanded by default + * according to the passed options. + * @param {Object} The root node of the tree. + * @param {Object} [optional] An object with the following optional parameters: + * - maxLevel: nodes nested deeper than this level won't be expanded. + * - maxNodes: maximum number of nodes that can be expanded. The traversal is + breadth-first, so expanding nodes nearer to the root will be preferred. + Sibling nodes will either be all expanded or none expanded. + * } + */ + TreeView.getExpandedNodes = function (rootObj, + { maxLevel = Infinity, maxNodes = Infinity } = {} + ) { + let expandedNodes = new Set(); + let queue = [{ + object: rootObj, + level: 1, + path: "" + }]; + while (queue.length) { + let {object, level, path} = queue.shift(); + if (Object(object) !== object) { + continue; + } + let keys = Object.keys(object); + if (expandedNodes.size + keys.length > maxNodes) { + // Avoid having children half expanded. + break; + } + for (let key of keys) { + let nodePath = TreeView.subPath(path, key); + expandedNodes.add(nodePath); + if (level < maxLevel) { + queue.push({ + object: object[key], + level: level + 1, + path: nodePath + }); + } + } + } + return expandedNodes; + }; + // Helpers /**