mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-10-22 02:25:05 +00:00
204 lines
6.6 KiB
JavaScript
204 lines
6.6 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/. */
|
|
|
|
const { DOM: dom, createClass, PropTypes, createFactory } = require("devtools/client/shared/vendor/react");
|
|
const { assert, safeErrorString } = require("devtools/shared/DevToolsUtils");
|
|
const Tree = createFactory(require("devtools/client/shared/components/tree"));
|
|
const TreeItem = createFactory(require("./tree-item"));
|
|
const { getStatusTextFull, L10N } = require("../utils");
|
|
const { snapshotState: states, diffingState } = require("../constants");
|
|
const { snapshot: snapshotModel, diffingModel } = require("../models");
|
|
// If HEAP_TREE_ROW_HEIGHT changes, be sure to change `var(--heap-tree-row-height)`
|
|
// in `devtools/client/themes/memory.css`
|
|
const HEAP_TREE_ROW_HEIGHT = 14;
|
|
|
|
/**
|
|
* Creates a hash map mapping node IDs to its parent node.
|
|
*
|
|
* @param {CensusTreeNode} node
|
|
* @param {Object<number, CensusTreeNode>} aggregator
|
|
*
|
|
* @return {Object<number, CensusTreeNode>}
|
|
*/
|
|
function createParentMap (node, aggregator=Object.create(null)) {
|
|
for (let child of (node.children || [])) {
|
|
aggregator[child.id] = node;
|
|
createParentMap(child, aggregator);
|
|
}
|
|
|
|
return aggregator;
|
|
}
|
|
|
|
/**
|
|
* Creates properties to be passed into the Tree component.
|
|
*
|
|
* @param {censusModel} census
|
|
* @param {Object} toolbox
|
|
* @param {Object} diffing
|
|
* @return {Object}
|
|
*/
|
|
function createTreeProperties(census, toolbox, diffing) {
|
|
const report = census.report;
|
|
let map = createParentMap(report);
|
|
const { totalBytes, totalCount } = report;
|
|
|
|
const getPercentBytes = totalBytes === 0
|
|
? _ => 0
|
|
: bytes => (bytes / totalBytes) * 100;
|
|
|
|
const getPercentCount = totalCount === 0
|
|
? _ => 0
|
|
: count => (count / totalCount) * 100;
|
|
|
|
return {
|
|
autoExpandDepth: 0,
|
|
getParent: node => {
|
|
const parent = map[node.id];
|
|
return parent === report ? null : parent;
|
|
},
|
|
getChildren: node => node.children || [],
|
|
renderItem: (item, depth, focused, arrow, expanded) =>
|
|
new TreeItem({
|
|
toolbox,
|
|
item,
|
|
depth,
|
|
focused,
|
|
arrow,
|
|
expanded,
|
|
getPercentBytes,
|
|
getPercentCount,
|
|
showSign: !!diffing,
|
|
}),
|
|
getRoots: () => report.children || [],
|
|
getKey: node => node.id,
|
|
itemHeight: HEAP_TREE_ROW_HEIGHT,
|
|
// Because we never add or remove children when viewing the same census, we
|
|
// can always reuse a cached traversal if one is available.
|
|
reuseCachedTraversal: _ => true,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Main view for the memory tool -- contains several panels for different states;
|
|
* an initial state of only a button to take a snapshot, loading states, and the
|
|
* heap view tree.
|
|
*/
|
|
|
|
const Heap = module.exports = createClass({
|
|
displayName: "heap-view",
|
|
|
|
propTypes: {
|
|
onSnapshotClick: PropTypes.func.isRequired,
|
|
snapshot: snapshotModel,
|
|
toolbox: PropTypes.object.isRequired,
|
|
diffing: diffingModel,
|
|
},
|
|
|
|
render() {
|
|
let { snapshot, diffing, onSnapshotClick, toolbox } = this.props;
|
|
|
|
let census;
|
|
let state;
|
|
let statusText;
|
|
let error;
|
|
if (diffing) {
|
|
census = diffing.census;
|
|
state = diffing.state;
|
|
|
|
if (diffing.state === diffingState.SELECTING) {
|
|
statusText = L10N.getStr(diffing.firstSnapshotId === null
|
|
? "diffing.prompt.selectBaseline"
|
|
: "diffing.prompt.selectComparison");
|
|
} else {
|
|
statusText = getStatusTextFull(diffing.state);
|
|
}
|
|
|
|
if (diffing.error) {
|
|
error = diffing.error;
|
|
}
|
|
} else {
|
|
census = snapshot ? snapshot.census : null;
|
|
state = snapshot ? snapshot.state : "initial";
|
|
statusText = snapshot ? getStatusTextFull(snapshot.state) : "";
|
|
if (snapshot && snapshot.error) {
|
|
error = snapshot.error;
|
|
}
|
|
}
|
|
assert(census !== undefined, "census should have been set");
|
|
assert(state !== undefined, "state should have been set");
|
|
assert(statusText !== undefined, "statusText should have been set");
|
|
|
|
let content;
|
|
switch (state) {
|
|
case "initial":
|
|
content = [dom.button({
|
|
className: "devtools-toolbarbutton take-snapshot",
|
|
onClick: onSnapshotClick,
|
|
// Want to use the [standalone] tag to leverage our styles,
|
|
// but React hates that evidently
|
|
"data-standalone": true,
|
|
"data-text-only": true,
|
|
}, L10N.getStr("take-snapshot"))];
|
|
break;
|
|
|
|
case diffingState.ERROR:
|
|
case states.ERROR:
|
|
content = [
|
|
dom.span({ className: "snapshot-status error" }, statusText),
|
|
dom.pre({}, safeErrorString(error))
|
|
];
|
|
break;
|
|
|
|
case diffingState.SELECTING:
|
|
case diffingState.TAKING_DIFF:
|
|
case states.IMPORTING:
|
|
case states.SAVING:
|
|
case states.SAVED:
|
|
case states.READING:
|
|
case states.READ:
|
|
case states.SAVING_CENSUS:
|
|
const throbber = state === diffingState.SELECTING
|
|
? ""
|
|
: "devtools-throbber";
|
|
content = [dom.span({ className: `snapshot-status ${throbber}` },
|
|
statusText)];
|
|
break;
|
|
|
|
case diffingState.TOOK_DIFF:
|
|
case states.SAVED_CENSUS:
|
|
content = [];
|
|
|
|
if (census.breakdown.by === "allocationStack"
|
|
&& census.report.children.length === 1
|
|
&& census.report.children[0].name === "noStack") {
|
|
content.push(dom.div({ className: "error no-allocation-stacks" },
|
|
L10N.getStr("heapview.noAllocationStacks")));
|
|
}
|
|
|
|
content.push(
|
|
dom.div({ className: "header" },
|
|
dom.span({ className: "heap-tree-item-bytes" }, L10N.getStr("heapview.field.bytes")),
|
|
dom.span({ className: "heap-tree-item-count" }, L10N.getStr("heapview.field.count")),
|
|
dom.span({ className: "heap-tree-item-total-bytes" }, L10N.getStr("heapview.field.totalbytes")),
|
|
dom.span({ className: "heap-tree-item-total-count" }, L10N.getStr("heapview.field.totalcount")),
|
|
dom.span({ className: "heap-tree-item-name" }, L10N.getStr("heapview.field.name"))
|
|
),
|
|
Tree(createTreeProperties(census, toolbox, diffing))
|
|
);
|
|
break;
|
|
|
|
default:
|
|
assert(false, "Unexpected state: ${state}");
|
|
}
|
|
assert(!!content, "Should have set content in the above switch block");
|
|
|
|
let pane = dom.div({ className: "heap-view-panel", "data-state": state },
|
|
...content);
|
|
|
|
return (
|
|
dom.div({ id: "heap-view", "data-state": state }, pane)
|
|
);
|
|
}
|
|
});
|