123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- /* 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 { assert } = require("devtools/shared/DevToolsUtils");
- const { appinfo } = require("Services");
- const { DOM: dom, createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react");
- const { connect } = require("devtools/client/shared/vendor/react-redux");
- const { censusDisplays, labelDisplays, treeMapDisplays, diffingState, viewState } = require("./constants");
- const { toggleRecordingAllocationStacks } = require("./actions/allocations");
- const { setCensusDisplayAndRefresh } = require("./actions/census-display");
- const { setLabelDisplayAndRefresh } = require("./actions/label-display");
- const { setTreeMapDisplayAndRefresh } = require("./actions/tree-map-display");
- const {
- getCustomCensusDisplays,
- getCustomLabelDisplays,
- getCustomTreeMapDisplays,
- } = require("devtools/client/memory/utils");
- const {
- selectSnapshotForDiffingAndRefresh,
- toggleDiffing,
- expandDiffingCensusNode,
- collapseDiffingCensusNode,
- focusDiffingCensusNode,
- } = require("./actions/diffing");
- const { setFilterStringAndRefresh } = require("./actions/filter");
- const { pickFileAndExportSnapshot, pickFileAndImportSnapshotAndCensus } = require("./actions/io");
- const {
- selectSnapshotAndRefresh,
- takeSnapshotAndCensus,
- clearSnapshots,
- deleteSnapshot,
- fetchImmediatelyDominated,
- expandCensusNode,
- collapseCensusNode,
- focusCensusNode,
- expandDominatorTreeNode,
- collapseDominatorTreeNode,
- focusDominatorTreeNode,
- fetchIndividuals,
- focusIndividual,
- } = require("./actions/snapshot");
- const { changeViewAndRefresh, popViewAndRefresh } = require("./actions/view");
- const { resizeShortestPaths } = require("./actions/sizes");
- const Toolbar = createFactory(require("./components/toolbar"));
- const List = createFactory(require("./components/list"));
- const SnapshotListItem = createFactory(require("./components/snapshot-list-item"));
- const Heap = createFactory(require("./components/heap"));
- const { app: appModel } = require("./models");
- const MemoryApp = createClass({
- displayName: "MemoryApp",
- propTypes: appModel,
- getDefaultProps() {
- return {};
- },
- componentDidMount() {
- // Attach the keydown listener directly to the window. When an element that
- // has the focus (such as a tree node) is removed from the DOM, the focus
- // falls back to the body.
- window.addEventListener("keydown", this.onKeyDown);
- },
- componentWillUnmount() {
- window.removeEventListener("keydown", this.onKeyDown);
- },
- childContextTypes: {
- front: PropTypes.any,
- heapWorker: PropTypes.any,
- toolbox: PropTypes.any,
- },
- getChildContext() {
- return {
- front: this.props.front,
- heapWorker: this.props.heapWorker,
- toolbox: this.props.toolbox,
- };
- },
- onKeyDown(e) {
- let { snapshots, dispatch, heapWorker } = this.props;
- const selectedSnapshot = snapshots.find(s => s.selected);
- const selectedIndex = snapshots.indexOf(selectedSnapshot);
- let isOSX = appinfo.OS == "Darwin";
- let isAccelKey = (isOSX && e.metaKey) || (!isOSX && e.ctrlKey);
- // On ACCEL+UP, select previous snapshot.
- if (isAccelKey && e.key === "ArrowUp") {
- let previousIndex = Math.max(0, selectedIndex - 1);
- let previousSnapshotId = snapshots[previousIndex].id;
- dispatch(selectSnapshotAndRefresh(heapWorker, previousSnapshotId));
- }
- // On ACCEL+DOWN, select next snapshot.
- if (isAccelKey && e.key === "ArrowDown") {
- let nextIndex = Math.min(snapshots.length - 1, selectedIndex + 1);
- let nextSnapshotId = snapshots[nextIndex].id;
- dispatch(selectSnapshotAndRefresh(heapWorker, nextSnapshotId));
- }
- },
- _getCensusDisplays() {
- const customDisplays = getCustomCensusDisplays();
- const custom = Object.keys(customDisplays).reduce((arr, key) => {
- arr.push(customDisplays[key]);
- return arr;
- }, []);
- return [
- censusDisplays.coarseType,
- censusDisplays.allocationStack,
- censusDisplays.invertedAllocationStack,
- ].concat(custom);
- },
- _getLabelDisplays() {
- const customDisplays = getCustomLabelDisplays();
- const custom = Object.keys(customDisplays).reduce((arr, key) => {
- arr.push(customDisplays[key]);
- return arr;
- }, []);
- return [
- labelDisplays.coarseType,
- labelDisplays.allocationStack,
- ].concat(custom);
- },
- _getTreeMapDisplays() {
- const customDisplays = getCustomTreeMapDisplays();
- const custom = Object.keys(customDisplays).reduce((arr, key) => {
- arr.push(customDisplays[key]);
- return arr;
- }, []);
- return [
- treeMapDisplays.coarseType
- ].concat(custom);
- },
- render() {
- let {
- dispatch,
- snapshots,
- front,
- heapWorker,
- allocations,
- toolbox,
- filter,
- diffing,
- view,
- sizes,
- censusDisplay,
- labelDisplay,
- individuals,
- } = this.props;
- const selectedSnapshot = snapshots.find(s => s.selected);
- const onClickSnapshotListItem = diffing && diffing.state === diffingState.SELECTING
- ? snapshot => dispatch(selectSnapshotForDiffingAndRefresh(heapWorker, snapshot))
- : snapshot => dispatch(selectSnapshotAndRefresh(heapWorker, snapshot.id));
- return (
- dom.div(
- {
- id: "memory-tool"
- },
- Toolbar({
- snapshots,
- censusDisplays: this._getCensusDisplays(),
- censusDisplay,
- onCensusDisplayChange: newDisplay =>
- dispatch(setCensusDisplayAndRefresh(heapWorker, newDisplay)),
- onImportClick: () => dispatch(pickFileAndImportSnapshotAndCensus(heapWorker)),
- onClearSnapshotsClick: () => dispatch(clearSnapshots(heapWorker)),
- onTakeSnapshotClick: () => dispatch(takeSnapshotAndCensus(front, heapWorker)),
- onToggleRecordAllocationStacks: () =>
- dispatch(toggleRecordingAllocationStacks(front)),
- allocations,
- filterString: filter,
- setFilterString: filterString =>
- dispatch(setFilterStringAndRefresh(filterString, heapWorker)),
- diffing,
- onToggleDiffing: () => dispatch(toggleDiffing()),
- view,
- labelDisplays: this._getLabelDisplays(),
- labelDisplay,
- onLabelDisplayChange: newDisplay =>
- dispatch(setLabelDisplayAndRefresh(heapWorker, newDisplay)),
- treeMapDisplays: this._getTreeMapDisplays(),
- onTreeMapDisplayChange: newDisplay =>
- dispatch(setTreeMapDisplayAndRefresh(heapWorker, newDisplay)),
- onViewChange: v => dispatch(changeViewAndRefresh(v, heapWorker)),
- }),
- dom.div(
- {
- id: "memory-tool-container"
- },
- List({
- itemComponent: SnapshotListItem,
- items: snapshots,
- onSave: snapshot => dispatch(pickFileAndExportSnapshot(snapshot)),
- onDelete: snapshot => dispatch(deleteSnapshot(heapWorker, snapshot)),
- onClick: onClickSnapshotListItem,
- diffing,
- }),
- Heap({
- snapshot: selectedSnapshot,
- diffing,
- onViewSourceInDebugger: frame => toolbox.viewSourceInDebugger(frame.source, frame.line),
- onSnapshotClick: () =>
- dispatch(takeSnapshotAndCensus(front, heapWorker)),
- onLoadMoreSiblings: lazyChildren =>
- dispatch(fetchImmediatelyDominated(heapWorker,
- selectedSnapshot.id,
- lazyChildren)),
- onPopView: () => dispatch(popViewAndRefresh(heapWorker)),
- individuals,
- onViewIndividuals: node => {
- const snapshotId = diffing
- ? diffing.secondSnapshotId
- : selectedSnapshot.id;
- dispatch(fetchIndividuals(heapWorker,
- snapshotId,
- censusDisplay.breakdown,
- node.reportLeafIndex));
- },
- onFocusIndividual: node => {
- assert(view.state === viewState.INDIVIDUALS,
- "Should be in the individuals view");
- dispatch(focusIndividual(node));
- },
- onCensusExpand: (census, node) => {
- if (diffing) {
- assert(diffing.census === census,
- "Should only expand active census");
- dispatch(expandDiffingCensusNode(node));
- } else {
- assert(selectedSnapshot && selectedSnapshot.census === census,
- "If not diffing, should be expanding on selected snapshot's census");
- dispatch(expandCensusNode(selectedSnapshot.id, node));
- }
- },
- onCensusCollapse: (census, node) => {
- if (diffing) {
- assert(diffing.census === census,
- "Should only collapse active census");
- dispatch(collapseDiffingCensusNode(node));
- } else {
- assert(selectedSnapshot && selectedSnapshot.census === census,
- "If not diffing, should be collapsing on selected snapshot's census");
- dispatch(collapseCensusNode(selectedSnapshot.id, node));
- }
- },
- onCensusFocus: (census, node) => {
- if (diffing) {
- assert(diffing.census === census,
- "Should only focus nodes in active census");
- dispatch(focusDiffingCensusNode(node));
- } else {
- assert(selectedSnapshot && selectedSnapshot.census === census,
- "If not diffing, should be focusing on nodes in selected snapshot's census");
- dispatch(focusCensusNode(selectedSnapshot.id, node));
- }
- },
- onDominatorTreeExpand: node => {
- assert(view.state === viewState.DOMINATOR_TREE,
- "If expanding dominator tree nodes, should be in dominator tree view");
- assert(selectedSnapshot, "...and we should have a selected snapshot");
- assert(selectedSnapshot.dominatorTree,
- "...and that snapshot should have a dominator tree");
- dispatch(expandDominatorTreeNode(selectedSnapshot.id, node));
- },
- onDominatorTreeCollapse: node => {
- assert(view.state === viewState.DOMINATOR_TREE,
- "If collapsing dominator tree nodes, should be in dominator tree view");
- assert(selectedSnapshot, "...and we should have a selected snapshot");
- assert(selectedSnapshot.dominatorTree,
- "...and that snapshot should have a dominator tree");
- dispatch(collapseDominatorTreeNode(selectedSnapshot.id, node));
- },
- onDominatorTreeFocus: node => {
- assert(view.state === viewState.DOMINATOR_TREE,
- "If focusing dominator tree nodes, should be in dominator tree view");
- assert(selectedSnapshot, "...and we should have a selected snapshot");
- assert(selectedSnapshot.dominatorTree,
- "...and that snapshot should have a dominator tree");
- dispatch(focusDominatorTreeNode(selectedSnapshot.id, node));
- },
- onShortestPathsResize: newSize => {
- dispatch(resizeShortestPaths(newSize));
- },
- sizes,
- view,
- })
- )
- )
- );
- },
- });
- /**
- * Passed into react-redux's `connect` method that is called on store change
- * and passed to components.
- */
- function mapStateToProps(state) {
- return state;
- }
- module.exports = connect(mapStateToProps)(MemoryApp);
|