summaryrefslogtreecommitdiffstats
path: root/devtools/client/memory/components/DominatorTree.js
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
commit0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch)
treea31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /devtools/client/memory/components/DominatorTree.js
parentInitial commit. (diff)
downloadfirefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz
firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/client/memory/components/DominatorTree.js')
-rw-r--r--devtools/client/memory/components/DominatorTree.js250
1 files changed, 250 insertions, 0 deletions
diff --git a/devtools/client/memory/components/DominatorTree.js b/devtools/client/memory/components/DominatorTree.js
new file mode 100644
index 0000000000..9c767cece7
--- /dev/null
+++ b/devtools/client/memory/components/DominatorTree.js
@@ -0,0 +1,250 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ Component,
+ createFactory,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const { assert } = require("resource://devtools/shared/DevToolsUtils.js");
+const {
+ createParentMap,
+} = require("resource://devtools/shared/heapsnapshot/CensusUtils.js");
+const Tree = createFactory(
+ require("resource://devtools/client/shared/components/VirtualizedTree.js")
+);
+const DominatorTreeItem = createFactory(
+ require("resource://devtools/client/memory/components/DominatorTreeItem.js")
+);
+const { L10N } = require("resource://devtools/client/memory/utils.js");
+const {
+ TREE_ROW_HEIGHT,
+ dominatorTreeState,
+} = require("resource://devtools/client/memory/constants.js");
+const {
+ dominatorTreeModel,
+} = require("resource://devtools/client/memory/models.js");
+const DominatorTreeLazyChildren = require("resource://devtools/client/memory/dominator-tree-lazy-children.js");
+
+const DOMINATOR_TREE_AUTO_EXPAND_DEPTH = 3;
+
+/**
+ * A throbber that represents a subtree in the dominator tree that is actively
+ * being incrementally loaded and fetched from the `HeapAnalysesWorker`.
+ */
+class DominatorTreeSubtreeFetchingClass extends Component {
+ static get propTypes() {
+ return {
+ depth: PropTypes.number.isRequired,
+ focused: PropTypes.bool.isRequired,
+ };
+ }
+
+ shouldComponentUpdate(nextProps, nextState) {
+ return (
+ this.props.depth !== nextProps.depth ||
+ this.props.focused !== nextProps.focused
+ );
+ }
+
+ render() {
+ const { depth, focused } = this.props;
+
+ return dom.div(
+ {
+ className: `heap-tree-item subtree-fetching ${
+ focused ? "focused" : ""
+ }`,
+ },
+ dom.span({ className: "heap-tree-item-field heap-tree-item-bytes" }),
+ dom.span({ className: "heap-tree-item-field heap-tree-item-bytes" }),
+ dom.span({
+ className: "heap-tree-item-field heap-tree-item-name devtools-throbber",
+ style: { marginInlineStart: depth * TREE_ROW_HEIGHT },
+ })
+ );
+ }
+}
+
+/**
+ * A link to fetch and load more siblings in the dominator tree, when there are
+ * already many loaded above.
+ */
+class DominatorTreeSiblingLinkClass extends Component {
+ static get propTypes() {
+ return {
+ depth: PropTypes.number.isRequired,
+ focused: PropTypes.bool.isRequired,
+ item: PropTypes.instanceOf(DominatorTreeLazyChildren).isRequired,
+ onLoadMoreSiblings: PropTypes.func.isRequired,
+ };
+ }
+
+ shouldComponentUpdate(nextProps, nextState) {
+ return (
+ this.props.depth !== nextProps.depth ||
+ this.props.focused !== nextProps.focused
+ );
+ }
+
+ render() {
+ const { depth, focused, item, onLoadMoreSiblings } = this.props;
+
+ return dom.div(
+ {
+ className: `heap-tree-item more-children ${focused ? "focused" : ""}`,
+ },
+ dom.span({ className: "heap-tree-item-field heap-tree-item-bytes" }),
+ dom.span({ className: "heap-tree-item-field heap-tree-item-bytes" }),
+ dom.span(
+ {
+ className: "heap-tree-item-field heap-tree-item-name",
+ style: { marginInlineStart: depth * TREE_ROW_HEIGHT },
+ },
+ dom.a(
+ {
+ className: "load-more-link",
+ onClick: () => onLoadMoreSiblings(item),
+ },
+ L10N.getStr("tree-item.load-more")
+ )
+ )
+ );
+ }
+}
+
+class DominatorTree extends Component {
+ static get propTypes() {
+ return {
+ dominatorTree: dominatorTreeModel.isRequired,
+ onLoadMoreSiblings: PropTypes.func.isRequired,
+ onViewSourceInDebugger: PropTypes.func.isRequired,
+ onExpand: PropTypes.func.isRequired,
+ onCollapse: PropTypes.func.isRequired,
+ onFocus: PropTypes.func.isRequired,
+ };
+ }
+
+ shouldComponentUpdate(nextProps, nextState) {
+ // Safe to use referential equality here because all of our mutations on
+ // dominator tree models use immutableUpdate in a persistent manner. The
+ // exception to the rule are mutations of the expanded set, however we take
+ // care that the dominatorTree model itself is still re-allocated when
+ // mutations to the expanded set occur. Because of the re-allocations, we
+ // can continue using referential equality here.
+ return this.props.dominatorTree !== nextProps.dominatorTree;
+ }
+
+ render() {
+ const { dominatorTree, onViewSourceInDebugger, onLoadMoreSiblings } =
+ this.props;
+
+ const parentMap = createParentMap(dominatorTree.root, node => node.nodeId);
+
+ return Tree({
+ key: "dominator-tree-tree",
+ autoExpandDepth: DOMINATOR_TREE_AUTO_EXPAND_DEPTH,
+ preventNavigationOnArrowRight: false,
+ focused: dominatorTree.focused,
+ getParent: node =>
+ node instanceof DominatorTreeLazyChildren
+ ? parentMap[node.parentNodeId()]
+ : parentMap[node.nodeId],
+ getChildren: node => {
+ const children = node.children ? node.children.slice() : [];
+ if (node.moreChildrenAvailable) {
+ children.push(
+ new DominatorTreeLazyChildren(node.nodeId, children.length)
+ );
+ }
+ return children;
+ },
+ isExpanded: node => {
+ return node instanceof DominatorTreeLazyChildren
+ ? false
+ : dominatorTree.expanded.has(node.nodeId);
+ },
+ onExpand: item => {
+ if (item instanceof DominatorTreeLazyChildren) {
+ return;
+ }
+
+ if (
+ item.moreChildrenAvailable &&
+ (!item.children || !item.children.length)
+ ) {
+ const startIndex = item.children ? item.children.length : 0;
+ onLoadMoreSiblings(
+ new DominatorTreeLazyChildren(item.nodeId, startIndex)
+ );
+ }
+
+ this.props.onExpand(item);
+ },
+ onCollapse: item => {
+ if (item instanceof DominatorTreeLazyChildren) {
+ return;
+ }
+
+ this.props.onCollapse(item);
+ },
+ onFocus: item => {
+ if (item instanceof DominatorTreeLazyChildren) {
+ return;
+ }
+
+ this.props.onFocus(item);
+ },
+ renderItem: (item, depth, focused, arrow, expanded) => {
+ if (item instanceof DominatorTreeLazyChildren) {
+ if (item.isFirstChild()) {
+ assert(
+ dominatorTree.state === dominatorTreeState.INCREMENTAL_FETCHING,
+ "If we are displaying a throbber for loading a subtree, " +
+ "then we should be INCREMENTAL_FETCHING those children right now"
+ );
+ return DominatorTreeSubtreeFetching({
+ key: item.key(),
+ depth,
+ focused,
+ });
+ }
+
+ return DominatorTreeSiblingLink({
+ key: item.key(),
+ item,
+ depth,
+ focused,
+ onLoadMoreSiblings,
+ });
+ }
+
+ return DominatorTreeItem({
+ item,
+ depth,
+ focused,
+ arrow,
+ expanded,
+ getPercentSize: size =>
+ (size / dominatorTree.root.retainedSize) * 100,
+ onViewSourceInDebugger,
+ });
+ },
+ getRoots: () => [dominatorTree.root],
+ getKey: node =>
+ node instanceof DominatorTreeLazyChildren ? node.key() : node.nodeId,
+ itemHeight: TREE_ROW_HEIGHT,
+ });
+ }
+}
+
+const DominatorTreeSubtreeFetching = createFactory(
+ DominatorTreeSubtreeFetchingClass
+);
+const DominatorTreeSiblingLink = createFactory(DominatorTreeSiblingLinkClass);
+
+module.exports = DominatorTree;