DominatorTreeItem.js (5006B)
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 assert, 9 isSavedFrame, 10 } = require("resource://devtools/shared/DevToolsUtils.js"); 11 const { 12 Component, 13 createFactory, 14 } = require("resource://devtools/client/shared/vendor/react.mjs"); 15 const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js"); 16 const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs"); 17 const { 18 L10N, 19 formatNumber, 20 formatPercent, 21 } = require("resource://devtools/client/memory/utils.js"); 22 const Frame = createFactory( 23 require("resource://devtools/client/shared/components/Frame.js") 24 ); 25 const { 26 TREE_ROW_HEIGHT, 27 } = require("resource://devtools/client/memory/constants.js"); 28 29 class SeparatorClass extends Component { 30 render() { 31 return dom.span({ className: "separator" }, "›"); 32 } 33 } 34 35 const Separator = createFactory(SeparatorClass); 36 37 class DominatorTreeItem extends Component { 38 static get propTypes() { 39 return { 40 item: PropTypes.object.isRequired, 41 depth: PropTypes.number.isRequired, 42 arrow: PropTypes.object, 43 expanded: PropTypes.bool.isRequired, 44 focused: PropTypes.bool.isRequired, 45 getPercentSize: PropTypes.func.isRequired, 46 onViewSourceInDebugger: PropTypes.func.isRequired, 47 }; 48 } 49 50 shouldComponentUpdate(nextProps) { 51 return ( 52 this.props.item != nextProps.item || 53 this.props.depth != nextProps.depth || 54 this.props.expanded != nextProps.expanded || 55 this.props.focused != nextProps.focused 56 ); 57 } 58 59 render() { 60 const { 61 item, 62 depth, 63 arrow, 64 focused, 65 getPercentSize, 66 onViewSourceInDebugger, 67 } = this.props; 68 69 const retainedSize = formatNumber(item.retainedSize); 70 const percentRetainedSize = formatPercent( 71 getPercentSize(item.retainedSize) 72 ); 73 74 const shallowSize = formatNumber(item.shallowSize); 75 const percentShallowSize = formatPercent(getPercentSize(item.shallowSize)); 76 77 // Build up our label UI as an array of each label piece, which is either a 78 // string or a frame, and separators in between them. 79 80 assert(!!item.label.length, "Our label should not be empty"); 81 const label = Array(item.label.length * 2 - 1); 82 label.fill(undefined); 83 84 for (let i = 0, length = item.label.length; i < length; i++) { 85 const piece = item.label[i]; 86 const key = `${item.nodeId}-label-${i}`; 87 88 // `i` is the index of the label piece we are rendering, `label[i*2]` is 89 // where the rendered label piece belngs, and `label[i*2+1]` (if it isn't 90 // out of bounds) is where the separator belongs. 91 92 if (isSavedFrame(piece)) { 93 label[i * 2] = Frame({ 94 key, 95 onClick: onViewSourceInDebugger, 96 frame: piece, 97 showFunctionName: true, 98 }); 99 } else if (piece === "noStack") { 100 label[i * 2] = dom.span( 101 { key, className: "not-available" }, 102 L10N.getStr("tree-item.nostack") 103 ); 104 } else if (piece === "noFilename") { 105 label[i * 2] = dom.span( 106 { key, className: "not-available" }, 107 L10N.getStr("tree-item.nofilename") 108 ); 109 } else if (piece === "JS::ubi::RootList") { 110 // Don't use the usual labeling machinery for root lists: replace it 111 // with the "GC Roots" string. 112 label.splice(0, label.length); 113 label.push(L10N.getStr("tree-item.rootlist")); 114 break; 115 } else { 116 label[i * 2] = piece; 117 } 118 119 // If this is not the last piece of the label, add a separator. 120 if (i < length - 1) { 121 label[i * 2 + 1] = Separator({ key: `${item.nodeId}-separator-${i}` }); 122 } 123 } 124 125 return dom.div( 126 { 127 className: `heap-tree-item ${focused ? "focused" : ""} node-${ 128 item.nodeId 129 }`, 130 }, 131 132 dom.span( 133 { 134 className: "heap-tree-item-field heap-tree-item-bytes", 135 }, 136 dom.span( 137 { 138 className: "heap-tree-number", 139 }, 140 retainedSize 141 ), 142 dom.span({ className: "heap-tree-percent" }, percentRetainedSize) 143 ), 144 145 dom.span( 146 { 147 className: "heap-tree-item-field heap-tree-item-bytes", 148 }, 149 dom.span( 150 { 151 className: "heap-tree-number", 152 }, 153 shallowSize 154 ), 155 dom.span({ className: "heap-tree-percent" }, percentShallowSize) 156 ), 157 158 dom.span( 159 { 160 className: "heap-tree-item-field heap-tree-item-name", 161 style: { marginInlineStart: depth * TREE_ROW_HEIGHT }, 162 }, 163 arrow, 164 label, 165 dom.span( 166 { className: "heap-tree-item-address" }, 167 `@ 0x${item.nodeId.toString(16)}` 168 ) 169 ) 170 ); 171 } 172 } 173 174 module.exports = DominatorTreeItem;