tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

app.js (14122B)


      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 { assert } = require("resource://devtools/shared/DevToolsUtils.js");
      8 const {
      9  Component,
     10  createFactory,
     11 } = require("resource://devtools/client/shared/vendor/react.mjs");
     12 const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
     13 const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs");
     14 const {
     15  connect,
     16 } = require("resource://devtools/client/shared/vendor/react-redux.js");
     17 const {
     18  censusDisplays,
     19  labelDisplays,
     20  treeMapDisplays,
     21  diffingState,
     22  viewState,
     23 } = require("resource://devtools/client/memory/constants.js");
     24 const {
     25  toggleRecordingAllocationStacks,
     26 } = require("resource://devtools/client/memory/actions/allocations.js");
     27 const {
     28  setCensusDisplayAndRefresh,
     29 } = require("resource://devtools/client/memory/actions/census-display.js");
     30 const {
     31  setLabelDisplayAndRefresh,
     32 } = require("resource://devtools/client/memory/actions/label-display.js");
     33 const {
     34  setTreeMapDisplayAndRefresh,
     35 } = require("resource://devtools/client/memory/actions/tree-map-display.js");
     36 
     37 const {
     38  getCustomCensusDisplays,
     39  getCustomLabelDisplays,
     40  getCustomTreeMapDisplays,
     41 } = require("resource://devtools/client/memory/utils.js");
     42 const {
     43  selectSnapshotForDiffingAndRefresh,
     44  toggleDiffing,
     45  expandDiffingCensusNode,
     46  collapseDiffingCensusNode,
     47  focusDiffingCensusNode,
     48 } = require("resource://devtools/client/memory/actions/diffing.js");
     49 const {
     50  setFilterStringAndRefresh,
     51 } = require("resource://devtools/client/memory/actions/filter.js");
     52 const {
     53  pickFileAndExportSnapshot,
     54  pickFileAndImportSnapshotAndCensus,
     55 } = require("resource://devtools/client/memory/actions/io.js");
     56 const {
     57  selectSnapshotAndRefresh,
     58  takeSnapshotAndCensus,
     59  clearSnapshots,
     60  deleteSnapshot,
     61  fetchImmediatelyDominated,
     62  expandCensusNode,
     63  collapseCensusNode,
     64  focusCensusNode,
     65  expandDominatorTreeNode,
     66  collapseDominatorTreeNode,
     67  focusDominatorTreeNode,
     68  fetchIndividuals,
     69  focusIndividual,
     70 } = require("resource://devtools/client/memory/actions/snapshot.js");
     71 const {
     72  changeViewAndRefresh,
     73  popViewAndRefresh,
     74 } = require("resource://devtools/client/memory/actions/view.js");
     75 const {
     76  resizeShortestPaths,
     77 } = require("resource://devtools/client/memory/actions/sizes.js");
     78 const Toolbar = createFactory(
     79  require("resource://devtools/client/memory/components/Toolbar.js")
     80 );
     81 const List = createFactory(
     82  require("resource://devtools/client/memory/components/List.js")
     83 );
     84 const SnapshotListItem = createFactory(
     85  require("resource://devtools/client/memory/components/SnapshotListItem.js")
     86 );
     87 const Heap = createFactory(
     88  require("resource://devtools/client/memory/components/Heap.js")
     89 );
     90 const {
     91  app: appModel,
     92 } = require("resource://devtools/client/memory/models.js");
     93 
     94 class MemoryApp extends Component {
     95  static get propTypes() {
     96    return {
     97      allocations: appModel.allocations,
     98      censusDisplay: appModel.censusDisplay,
     99      commands: appModel.commands,
    100      diffing: appModel.diffing,
    101      dispatch: PropTypes.func,
    102      filter: appModel.filter,
    103      front: appModel.front,
    104      heapWorker: appModel.heapWorker,
    105      individuals: appModel.individuals,
    106      labelDisplay: appModel.labelDisplay,
    107      sizes: PropTypes.object,
    108      snapshots: appModel.snapshots,
    109      toolbox: PropTypes.object,
    110      view: appModel.view,
    111    };
    112  }
    113 
    114  static get childContextTypes() {
    115    return {
    116      front: PropTypes.any,
    117      heapWorker: PropTypes.any,
    118      toolbox: PropTypes.any,
    119    };
    120  }
    121 
    122  static get defaultProps() {
    123    return {};
    124  }
    125 
    126  constructor(props) {
    127    super(props);
    128    this.onKeyDown = this.onKeyDown.bind(this);
    129    this._getCensusDisplays = this._getCensusDisplays.bind(this);
    130    this._getLabelDisplays = this._getLabelDisplays.bind(this);
    131    this._getTreeMapDisplays = this._getTreeMapDisplays.bind(this);
    132  }
    133 
    134  getChildContext() {
    135    return {
    136      front: this.props.front,
    137      heapWorker: this.props.heapWorker,
    138      toolbox: this.props.toolbox,
    139    };
    140  }
    141 
    142  componentDidMount() {
    143    // Attach the keydown listener directly to the window. When an element that
    144    // has the focus (such as a tree node) is removed from the DOM, the focus
    145    // falls back to the body.
    146    window.addEventListener("keydown", this.onKeyDown);
    147  }
    148 
    149  componentWillUnmount() {
    150    window.removeEventListener("keydown", this.onKeyDown);
    151  }
    152 
    153  onKeyDown(e) {
    154    const { snapshots, dispatch, heapWorker } = this.props;
    155    const selectedSnapshot = snapshots.find(s => s.selected);
    156    const selectedIndex = snapshots.indexOf(selectedSnapshot);
    157 
    158    const isOSX = Services.appinfo.OS == "Darwin";
    159    const isAccelKey = (isOSX && e.metaKey) || (!isOSX && e.ctrlKey);
    160 
    161    // On ACCEL+UP, select previous snapshot.
    162    if (isAccelKey && e.key === "ArrowUp") {
    163      const previousIndex = Math.max(0, selectedIndex - 1);
    164      const previousSnapshotId = snapshots[previousIndex].id;
    165      dispatch(selectSnapshotAndRefresh(heapWorker, previousSnapshotId));
    166    }
    167 
    168    // On ACCEL+DOWN, select next snapshot.
    169    if (isAccelKey && e.key === "ArrowDown") {
    170      const nextIndex = Math.min(snapshots.length - 1, selectedIndex + 1);
    171      const nextSnapshotId = snapshots[nextIndex].id;
    172      dispatch(selectSnapshotAndRefresh(heapWorker, nextSnapshotId));
    173    }
    174  }
    175 
    176  _getCensusDisplays() {
    177    const customDisplays = getCustomCensusDisplays();
    178    const custom = Object.keys(customDisplays).reduce((arr, key) => {
    179      arr.push(customDisplays[key]);
    180      return arr;
    181    }, []);
    182 
    183    return [
    184      censusDisplays.coarseType,
    185      censusDisplays.allocationStack,
    186      censusDisplays.invertedAllocationStack,
    187    ].concat(custom);
    188  }
    189 
    190  _getLabelDisplays() {
    191    const customDisplays = getCustomLabelDisplays();
    192    const custom = Object.keys(customDisplays).reduce((arr, key) => {
    193      arr.push(customDisplays[key]);
    194      return arr;
    195    }, []);
    196 
    197    return [labelDisplays.coarseType, labelDisplays.allocationStack].concat(
    198      custom
    199    );
    200  }
    201 
    202  _getTreeMapDisplays() {
    203    const customDisplays = getCustomTreeMapDisplays();
    204    const custom = Object.keys(customDisplays).reduce((arr, key) => {
    205      arr.push(customDisplays[key]);
    206      return arr;
    207    }, []);
    208 
    209    return [treeMapDisplays.coarseType].concat(custom);
    210  }
    211 
    212  render() {
    213    const {
    214      commands,
    215      dispatch,
    216      snapshots,
    217      front,
    218      heapWorker,
    219      allocations,
    220      toolbox,
    221      filter,
    222      diffing,
    223      view,
    224      sizes,
    225      censusDisplay,
    226      labelDisplay,
    227      individuals,
    228    } = this.props;
    229 
    230    const selectedSnapshot = snapshots.find(s => s.selected);
    231 
    232    const onClickSnapshotListItem =
    233      diffing && diffing.state === diffingState.SELECTING
    234        ? snapshot =>
    235            dispatch(selectSnapshotForDiffingAndRefresh(heapWorker, snapshot))
    236        : snapshot =>
    237            dispatch(selectSnapshotAndRefresh(heapWorker, snapshot.id));
    238 
    239    return dom.div(
    240      {
    241        id: "memory-tool",
    242      },
    243 
    244      Toolbar({
    245        snapshots,
    246        censusDisplays: this._getCensusDisplays(),
    247        censusDisplay,
    248        onCensusDisplayChange: newDisplay =>
    249          dispatch(setCensusDisplayAndRefresh(heapWorker, newDisplay)),
    250        onImportClick: () =>
    251          dispatch(pickFileAndImportSnapshotAndCensus(heapWorker)),
    252        onClearSnapshotsClick: () => dispatch(clearSnapshots(heapWorker)),
    253        onTakeSnapshotClick: () =>
    254          dispatch(takeSnapshotAndCensus(front, heapWorker)),
    255        onToggleRecordAllocationStacks: () =>
    256          dispatch(toggleRecordingAllocationStacks(commands)),
    257        allocations,
    258        filterString: filter,
    259        setFilterString: filterString =>
    260          dispatch(setFilterStringAndRefresh(filterString, heapWorker)),
    261        diffing,
    262        onToggleDiffing: () => dispatch(toggleDiffing()),
    263        view,
    264        labelDisplays: this._getLabelDisplays(),
    265        labelDisplay,
    266        onLabelDisplayChange: newDisplay =>
    267          dispatch(setLabelDisplayAndRefresh(heapWorker, newDisplay)),
    268        treeMapDisplays: this._getTreeMapDisplays(),
    269        onTreeMapDisplayChange: newDisplay =>
    270          dispatch(setTreeMapDisplayAndRefresh(heapWorker, newDisplay)),
    271        onViewChange: v => dispatch(changeViewAndRefresh(v, heapWorker)),
    272      }),
    273 
    274      dom.div(
    275        {
    276          id: "memory-tool-container",
    277        },
    278 
    279        List({
    280          itemComponent: SnapshotListItem,
    281          items: snapshots,
    282          onSave: snapshot => dispatch(pickFileAndExportSnapshot(snapshot)),
    283          onDelete: snapshot => dispatch(deleteSnapshot(heapWorker, snapshot)),
    284          onClick: onClickSnapshotListItem,
    285          diffing,
    286        }),
    287 
    288        Heap({
    289          snapshot: selectedSnapshot,
    290          diffing,
    291          onViewSourceInDebugger: ({ url, line, column }) => {
    292            toolbox.viewSourceInDebugger(url, line, column);
    293          },
    294          onSnapshotClick: () =>
    295            dispatch(takeSnapshotAndCensus(front, heapWorker)),
    296          onLoadMoreSiblings: lazyChildren =>
    297            dispatch(
    298              fetchImmediatelyDominated(
    299                heapWorker,
    300                selectedSnapshot.id,
    301                lazyChildren
    302              )
    303            ),
    304          onPopView: () => dispatch(popViewAndRefresh(heapWorker)),
    305          individuals,
    306          onViewIndividuals: node => {
    307            const snapshotId = diffing
    308              ? diffing.secondSnapshotId
    309              : selectedSnapshot.id;
    310            dispatch(
    311              fetchIndividuals(
    312                heapWorker,
    313                snapshotId,
    314                censusDisplay.breakdown,
    315                node.reportLeafIndex
    316              )
    317            );
    318          },
    319          onFocusIndividual: node => {
    320            assert(
    321              view.state === viewState.INDIVIDUALS,
    322              "Should be in the individuals view"
    323            );
    324            dispatch(focusIndividual(node));
    325          },
    326          onCensusExpand: (census, node) => {
    327            if (diffing) {
    328              assert(
    329                diffing.census === census,
    330                "Should only expand active census"
    331              );
    332              dispatch(expandDiffingCensusNode(node));
    333            } else {
    334              assert(
    335                selectedSnapshot && selectedSnapshot.census === census,
    336                "If not diffing, " +
    337                  "should be expanding on selected snapshot's census"
    338              );
    339              dispatch(expandCensusNode(selectedSnapshot.id, node));
    340            }
    341          },
    342          onCensusCollapse: (census, node) => {
    343            if (diffing) {
    344              assert(
    345                diffing.census === census,
    346                "Should only collapse active census"
    347              );
    348              dispatch(collapseDiffingCensusNode(node));
    349            } else {
    350              assert(
    351                selectedSnapshot && selectedSnapshot.census === census,
    352                "If not diffing, " +
    353                  "should be collapsing on selected snapshot's census"
    354              );
    355              dispatch(collapseCensusNode(selectedSnapshot.id, node));
    356            }
    357          },
    358          onCensusFocus: (census, node) => {
    359            if (diffing) {
    360              assert(
    361                diffing.census === census,
    362                "Should only focus nodes in active census"
    363              );
    364              dispatch(focusDiffingCensusNode(node));
    365            } else {
    366              assert(
    367                selectedSnapshot && selectedSnapshot.census === census,
    368                "If not diffing, " +
    369                  "should be focusing on nodes in selected snapshot's census"
    370              );
    371              dispatch(focusCensusNode(selectedSnapshot.id, node));
    372            }
    373          },
    374          onDominatorTreeExpand: node => {
    375            assert(
    376              view.state === viewState.DOMINATOR_TREE,
    377              "If expanding dominator tree nodes, " +
    378                "should be in dominator tree view"
    379            );
    380            assert(
    381              selectedSnapshot,
    382              "...and we should have a selected snapshot"
    383            );
    384            assert(
    385              selectedSnapshot.dominatorTree,
    386              "...and that snapshot should have a dominator tree"
    387            );
    388            dispatch(expandDominatorTreeNode(selectedSnapshot.id, node));
    389          },
    390          onDominatorTreeCollapse: node => {
    391            assert(
    392              view.state === viewState.DOMINATOR_TREE,
    393              "If collapsing dominator tree nodes, " +
    394                "should be in dominator tree view"
    395            );
    396            assert(
    397              selectedSnapshot,
    398              "...and we should have a selected snapshot"
    399            );
    400            assert(
    401              selectedSnapshot.dominatorTree,
    402              "...and that snapshot should have a dominator tree"
    403            );
    404            dispatch(collapseDominatorTreeNode(selectedSnapshot.id, node));
    405          },
    406          onDominatorTreeFocus: node => {
    407            assert(
    408              view.state === viewState.DOMINATOR_TREE,
    409              "If focusing dominator tree nodes, " +
    410                "should be in dominator tree view"
    411            );
    412            assert(
    413              selectedSnapshot,
    414              "...and we should have a selected snapshot"
    415            );
    416            assert(
    417              selectedSnapshot.dominatorTree,
    418              "...and that snapshot should have a dominator tree"
    419            );
    420            dispatch(focusDominatorTreeNode(selectedSnapshot.id, node));
    421          },
    422          onShortestPathsResize: newSize => {
    423            dispatch(resizeShortestPaths(newSize));
    424          },
    425          sizes,
    426          view,
    427        })
    428      )
    429    );
    430  }
    431 }
    432 
    433 /**
    434 * Passed into react-redux's `connect` method that is called on store change
    435 * and passed to components.
    436 */
    437 function mapStateToProps(state) {
    438  return state;
    439 }
    440 
    441 module.exports = connect(mapStateToProps)(MemoryApp);