174 lines
4.9 KiB
JavaScript
174 lines
4.9 KiB
JavaScript
/* 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.mjs");
|
||
const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
|
||
const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs");
|
||
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;
|