diff --git a/devtools/client/memory/components/dominator-tree.js b/devtools/client/memory/components/dominator-tree.js index b96ca32cb362..7de371028be9 100644 --- a/devtools/client/memory/components/dominator-tree.js +++ b/devtools/client/memory/components/dominator-tree.js @@ -209,6 +209,9 @@ const DominatorTree = module.exports = createClass({ getKey: node => node instanceof DominatorTreeLazyChildren ? node.key() : node.nodeId, itemHeight: TREE_ROW_HEIGHT, + // We can't cache traversals because incremental fetching of children + // means the traversal might not be valid. + reuseCachedTraversal: _ => false, }); } }); diff --git a/devtools/client/shared/components/tree.js b/devtools/client/shared/components/tree.js index 58f9a565d335..d1afcec936ed 100644 --- a/devtools/client/shared/components/tree.js +++ b/devtools/client/shared/components/tree.js @@ -166,6 +166,10 @@ const Tree = module.exports = createClass({ onFocus: PropTypes.func, // The depth to which we should automatically expand new items. autoExpandDepth: PropTypes.number, + // A predicate that returns true if the last DFS traversal that was cached + // can be reused, false otherwise. The predicate function is passed the + // cached traversal as an array of nodes. + reuseCachedTraversal: PropTypes.func, // Optional event handlers for when items are expanded or collapsed. onExpand: PropTypes.func, onCollapse: PropTypes.func, @@ -174,6 +178,7 @@ const Tree = module.exports = createClass({ getDefaultProps() { return { autoExpandDepth: AUTO_EXPAND_DEPTH, + reuseCachedTraversal: null, }; }, @@ -182,6 +187,7 @@ const Tree = module.exports = createClass({ scroll: 0, height: window.innerHeight, seen: new Set(), + cachedTraversal: undefined, }; }, @@ -323,12 +329,23 @@ const Tree = module.exports = createClass({ * Perform a pre-order depth-first search over the whole forest. */ _dfsFromRoots(maxDepth = Infinity) { - const traversal = []; + const cached = this.state.cachedTraversal; + if (cached + && maxDepth === Infinity + && this.props.reuseCachedTraversal + && this.props.reuseCachedTraversal(cached)) { + return cached; + } + const traversal = []; for (let root of this.props.getRoots()) { this._dfs(root, maxDepth, traversal); } + if (this.props.reuseCachedTraversal) { + this.state.cachedTraversal = traversal; + } + return traversal; }, @@ -348,6 +365,10 @@ const Tree = module.exports = createClass({ } } } + + this.setState({ + cachedTraversal: null, + }); }), /** @@ -359,6 +380,10 @@ const Tree = module.exports = createClass({ if (this.props.onCollapse) { this.props.onCollapse(item); } + + this.setState({ + cachedTraversal: null, + }); }), /**