/* 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 { assert, isSavedFrame, } = require("resource://devtools/shared/DevToolsUtils.js"); 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 { L10N, formatNumber, formatPercent, } = require("resource://devtools/client/memory/utils.js"); const Frame = createFactory( require("resource://devtools/client/shared/components/Frame.js") ); const { TREE_ROW_HEIGHT, } = require("resource://devtools/client/memory/constants.js"); class SeparatorClass extends Component { render() { return dom.span({ className: "separator" }, "›"); } } const Separator = createFactory(SeparatorClass); class DominatorTreeItem extends Component { static get propTypes() { return { item: PropTypes.object.isRequired, depth: PropTypes.number.isRequired, arrow: PropTypes.object, expanded: PropTypes.bool.isRequired, focused: PropTypes.bool.isRequired, getPercentSize: PropTypes.func.isRequired, onViewSourceInDebugger: PropTypes.func.isRequired, }; } shouldComponentUpdate(nextProps) { return ( this.props.item != nextProps.item || this.props.depth != nextProps.depth || this.props.expanded != nextProps.expanded || this.props.focused != nextProps.focused ); } render() { const { item, depth, arrow, focused, getPercentSize, onViewSourceInDebugger, } = this.props; const retainedSize = formatNumber(item.retainedSize); const percentRetainedSize = formatPercent( getPercentSize(item.retainedSize) ); const shallowSize = formatNumber(item.shallowSize); const percentShallowSize = formatPercent(getPercentSize(item.shallowSize)); // Build up our label UI as an array of each label piece, which is either a // string or a frame, and separators in between them. assert(!!item.label.length, "Our label should not be empty"); const label = Array(item.label.length * 2 - 1); label.fill(undefined); for (let i = 0, length = item.label.length; i < length; i++) { const piece = item.label[i]; const key = `${item.nodeId}-label-${i}`; // `i` is the index of the label piece we are rendering, `label[i*2]` is // where the rendered label piece belngs, and `label[i*2+1]` (if it isn't // out of bounds) is where the separator belongs. if (isSavedFrame(piece)) { label[i * 2] = Frame({ key, onClick: onViewSourceInDebugger, frame: piece, showFunctionName: true, }); } else if (piece === "noStack") { label[i * 2] = dom.span( { key, className: "not-available" }, L10N.getStr("tree-item.nostack") ); } else if (piece === "noFilename") { label[i * 2] = dom.span( { key, className: "not-available" }, L10N.getStr("tree-item.nofilename") ); } else if (piece === "JS::ubi::RootList") { // Don't use the usual labeling machinery for root lists: replace it // with the "GC Roots" string. label.splice(0, label.length); label.push(L10N.getStr("tree-item.rootlist")); break; } else { label[i * 2] = piece; } // If this is not the last piece of the label, add a separator. if (i < length - 1) { label[i * 2 + 1] = Separator({ key: `${item.nodeId}-separator-${i}` }); } } return dom.div( { className: `heap-tree-item ${focused ? "focused" : ""} node-${ item.nodeId }`, }, dom.span( { className: "heap-tree-item-field heap-tree-item-bytes", }, dom.span( { className: "heap-tree-number", }, retainedSize ), dom.span({ className: "heap-tree-percent" }, percentRetainedSize) ), dom.span( { className: "heap-tree-item-field heap-tree-item-bytes", }, dom.span( { className: "heap-tree-number", }, shallowSize ), dom.span({ className: "heap-tree-percent" }, percentShallowSize) ), dom.span( { className: "heap-tree-item-field heap-tree-item-name", style: { marginInlineStart: depth * TREE_ROW_HEIGHT }, }, arrow, label, dom.span( { className: "heap-tree-item-address" }, `@ 0x${item.nodeId.toString(16)}` ) ) ); } } module.exports = DominatorTreeItem;