tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

DominatorTree.js (7498B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 "use strict";
      6 
      7 const {
      8  Component,
      9  createFactory,
     10 } = require("resource://devtools/client/shared/vendor/react.mjs");
     11 const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
     12 const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs");
     13 const { assert } = require("resource://devtools/shared/DevToolsUtils.js");
     14 const {
     15  createParentMap,
     16 } = require("resource://devtools/shared/heapsnapshot/CensusUtils.js");
     17 const Tree = createFactory(
     18  require("resource://devtools/client/shared/components/VirtualizedTree.js")
     19 );
     20 const DominatorTreeItem = createFactory(
     21  require("resource://devtools/client/memory/components/DominatorTreeItem.js")
     22 );
     23 const { L10N } = require("resource://devtools/client/memory/utils.js");
     24 const {
     25  TREE_ROW_HEIGHT,
     26  dominatorTreeState,
     27 } = require("resource://devtools/client/memory/constants.js");
     28 const {
     29  dominatorTreeModel,
     30 } = require("resource://devtools/client/memory/models.js");
     31 const DominatorTreeLazyChildren = require("resource://devtools/client/memory/dominator-tree-lazy-children.js");
     32 
     33 const DOMINATOR_TREE_AUTO_EXPAND_DEPTH = 3;
     34 
     35 /**
     36 * A throbber that represents a subtree in the dominator tree that is actively
     37 * being incrementally loaded and fetched from the `HeapAnalysesWorker`.
     38 */
     39 class DominatorTreeSubtreeFetchingClass extends Component {
     40  static get propTypes() {
     41    return {
     42      depth: PropTypes.number.isRequired,
     43    };
     44  }
     45 
     46  shouldComponentUpdate(nextProps) {
     47    return this.props.depth !== nextProps.depth;
     48  }
     49 
     50  render() {
     51    const { depth } = this.props;
     52 
     53    return dom.div(
     54      {
     55        className: "heap-tree-item subtree-fetching",
     56      },
     57      dom.span({ className: "heap-tree-item-field heap-tree-item-bytes" }),
     58      dom.span({ className: "heap-tree-item-field heap-tree-item-bytes" }),
     59      dom.span({
     60        className: "heap-tree-item-field heap-tree-item-name devtools-throbber",
     61        style: { marginInlineStart: depth * TREE_ROW_HEIGHT },
     62      })
     63    );
     64  }
     65 }
     66 
     67 /**
     68 * A link to fetch and load more siblings in the dominator tree, when there are
     69 * already many loaded above.
     70 */
     71 class DominatorTreeSiblingLinkClass extends Component {
     72  static get propTypes() {
     73    return {
     74      depth: PropTypes.number.isRequired,
     75      item: PropTypes.instanceOf(DominatorTreeLazyChildren).isRequired,
     76      onLoadMoreSiblings: PropTypes.func.isRequired,
     77    };
     78  }
     79 
     80  shouldComponentUpdate(nextProps) {
     81    return this.props.depth !== nextProps.depth;
     82  }
     83 
     84  render() {
     85    const { depth, item, onLoadMoreSiblings } = this.props;
     86 
     87    return dom.div(
     88      {
     89        className: `heap-tree-item more-children`,
     90      },
     91      dom.span({ className: "heap-tree-item-field heap-tree-item-bytes" }),
     92      dom.span({ className: "heap-tree-item-field heap-tree-item-bytes" }),
     93      dom.span(
     94        {
     95          className: "heap-tree-item-field heap-tree-item-name",
     96          style: { marginInlineStart: depth * TREE_ROW_HEIGHT },
     97        },
     98        dom.a(
     99          {
    100            className: "load-more-link",
    101            onClick: () => onLoadMoreSiblings(item),
    102          },
    103          L10N.getStr("tree-item.load-more")
    104        )
    105      )
    106    );
    107  }
    108 }
    109 
    110 class DominatorTree extends Component {
    111  static get propTypes() {
    112    return {
    113      dominatorTree: dominatorTreeModel.isRequired,
    114      onLoadMoreSiblings: PropTypes.func.isRequired,
    115      onViewSourceInDebugger: PropTypes.func.isRequired,
    116      onExpand: PropTypes.func.isRequired,
    117      onCollapse: PropTypes.func.isRequired,
    118      onFocus: PropTypes.func.isRequired,
    119    };
    120  }
    121 
    122  shouldComponentUpdate(nextProps) {
    123    // Safe to use referential equality here because all of our mutations on
    124    // dominator tree models use immutableUpdate in a persistent manner. The
    125    // exception to the rule are mutations of the expanded set, however we take
    126    // care that the dominatorTree model itself is still re-allocated when
    127    // mutations to the expanded set occur. Because of the re-allocations, we
    128    // can continue using referential equality here.
    129    return this.props.dominatorTree !== nextProps.dominatorTree;
    130  }
    131 
    132  render() {
    133    const { dominatorTree, onViewSourceInDebugger, onLoadMoreSiblings } =
    134      this.props;
    135 
    136    const parentMap = createParentMap(dominatorTree.root, node => node.nodeId);
    137 
    138    return Tree({
    139      key: "dominator-tree-tree",
    140      autoExpandDepth: DOMINATOR_TREE_AUTO_EXPAND_DEPTH,
    141      preventNavigationOnArrowRight: false,
    142      focused: dominatorTree.focused,
    143      getParent: node =>
    144        node instanceof DominatorTreeLazyChildren
    145          ? parentMap[node.parentNodeId()]
    146          : parentMap[node.nodeId],
    147      getChildren: node => {
    148        const children = node.children ? node.children.slice() : [];
    149        if (node.moreChildrenAvailable) {
    150          children.push(
    151            new DominatorTreeLazyChildren(node.nodeId, children.length)
    152          );
    153        }
    154        return children;
    155      },
    156      isExpanded: node => {
    157        return node instanceof DominatorTreeLazyChildren
    158          ? false
    159          : dominatorTree.expanded.has(node.nodeId);
    160      },
    161      onExpand: item => {
    162        if (item instanceof DominatorTreeLazyChildren) {
    163          return;
    164        }
    165 
    166        if (
    167          item.moreChildrenAvailable &&
    168          (!item.children || !item.children.length)
    169        ) {
    170          const startIndex = item.children ? item.children.length : 0;
    171          onLoadMoreSiblings(
    172            new DominatorTreeLazyChildren(item.nodeId, startIndex)
    173          );
    174        }
    175 
    176        this.props.onExpand(item);
    177      },
    178      onCollapse: item => {
    179        if (item instanceof DominatorTreeLazyChildren) {
    180          return;
    181        }
    182 
    183        this.props.onCollapse(item);
    184      },
    185      onFocus: item => {
    186        if (item instanceof DominatorTreeLazyChildren) {
    187          return;
    188        }
    189 
    190        this.props.onFocus(item);
    191      },
    192      renderItem: (item, depth, focused, arrow, expanded) => {
    193        if (item instanceof DominatorTreeLazyChildren) {
    194          if (item.isFirstChild()) {
    195            assert(
    196              dominatorTree.state === dominatorTreeState.INCREMENTAL_FETCHING,
    197              "If we are displaying a throbber for loading a subtree, " +
    198                "then we should be INCREMENTAL_FETCHING those children right now"
    199            );
    200            return DominatorTreeSubtreeFetching({
    201              key: item.key(),
    202              depth,
    203              focused,
    204            });
    205          }
    206 
    207          return DominatorTreeSiblingLink({
    208            key: item.key(),
    209            item,
    210            depth,
    211            onLoadMoreSiblings,
    212          });
    213        }
    214 
    215        return DominatorTreeItem({
    216          item,
    217          depth,
    218          arrow,
    219          expanded,
    220          getPercentSize: size =>
    221            (size / dominatorTree.root.retainedSize) * 100,
    222          onViewSourceInDebugger,
    223        });
    224      },
    225      getRoots: () => [dominatorTree.root],
    226      getKey: node =>
    227        node instanceof DominatorTreeLazyChildren ? node.key() : node.nodeId,
    228      itemHeight: TREE_ROW_HEIGHT,
    229    });
    230  }
    231 }
    232 
    233 const DominatorTreeSubtreeFetching = createFactory(
    234  DominatorTreeSubtreeFetchingClass
    235 );
    236 const DominatorTreeSiblingLink = createFactory(DominatorTreeSiblingLinkClass);
    237 
    238 module.exports = DominatorTree;