tor-browser

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

CommandBar.js (12392B)


      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
      3 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
      4 
      5 import React, { Component } from "devtools/client/shared/vendor/react";
      6 import { div, button } from "devtools/client/shared/vendor/react-dom-factories";
      7 import PropTypes from "devtools/client/shared/vendor/react-prop-types";
      8 
      9 import { connect } from "devtools/client/shared/vendor/react-redux";
     10 import { features, prefs } from "../../utils/prefs";
     11 import {
     12  getIsWaitingOnBreak,
     13  getSkipPausing,
     14  getCurrentThread,
     15  isTopFrameSelected,
     16  getIsCurrentThreadPaused,
     17 } from "../../selectors/index";
     18 import actions from "../../actions/index";
     19 import { debugBtn } from "../shared/Button/CommandBarButton";
     20 import DebuggerImage from "../shared/DebuggerImage";
     21 
     22 const {
     23  stringifyFromElectronKey,
     24 } = require("resource://devtools/client/shared/key-shortcuts.js");
     25 const classnames = require("resource://devtools/client/shared/classnames.js");
     26 const MenuButton = require("resource://devtools/client/shared/components/menu/MenuButton.js");
     27 const MenuItem = require("resource://devtools/client/shared/components/menu/MenuItem.js");
     28 const MenuList = require("resource://devtools/client/shared/components/menu/MenuList.js");
     29 
     30 const isMacOS = Services.appinfo.OS === "Darwin";
     31 
     32 // NOTE: the "resume" command will call either the resume or breakOnNext action
     33 // depending on whether or not the debugger is paused or running
     34 const COMMANDS = ["resume", "stepOver", "stepIn", "stepOut"];
     35 
     36 const KEYS = {
     37  WINNT: {
     38    resume: "F8",
     39    stepOver: "F10",
     40    stepIn: "F11",
     41    stepOut: "Shift+F11",
     42    trace: "Ctrl+Shift+5",
     43  },
     44  Darwin: {
     45    resume: "Cmd+\\",
     46    stepOver: "Cmd+'",
     47    stepIn: "Cmd+;",
     48    stepOut: "Cmd+Shift+:",
     49    stepOutDisplay: "Cmd+Shift+;",
     50    trace: "Ctrl+Shift+5",
     51  },
     52  Linux: {
     53    resume: "F8",
     54    stepOver: "F10",
     55    stepIn: "F11",
     56    stepOut: "Shift+F11",
     57    trace: "Ctrl+Shift+5",
     58  },
     59 };
     60 
     61 function getKey(action) {
     62  return getKeyForOS(Services.appinfo.OS, action);
     63 }
     64 
     65 function getKeyForOS(os, action) {
     66  const osActions = KEYS[os] || KEYS.Linux;
     67  return osActions[action];
     68 }
     69 
     70 function formatKey(action) {
     71  const key = getKey(`${action}Display`) || getKey(action);
     72 
     73  // On MacOS, we bind both Windows and MacOS/Darwin key shortcuts
     74  // Display them both, but only when they are different
     75  if (isMacOS) {
     76    const winKey =
     77      getKeyForOS("WINNT", `${action}Display`) || getKeyForOS("WINNT", action);
     78    if (key != winKey) {
     79      return stringifyFromElectronKey([key, winKey].join(" "));
     80    }
     81  }
     82  return stringifyFromElectronKey(key);
     83 }
     84 
     85 class CommandBar extends Component {
     86  constructor() {
     87    super();
     88 
     89    this.state = {};
     90  }
     91  static get propTypes() {
     92    return {
     93      breakOnNext: PropTypes.func.isRequired,
     94      horizontal: PropTypes.bool.isRequired,
     95      isPaused: PropTypes.bool.isRequired,
     96      isWaitingOnBreak: PropTypes.bool.isRequired,
     97      javascriptEnabled: PropTypes.bool.isRequired,
     98      resume: PropTypes.func.isRequired,
     99      skipPausing: PropTypes.bool.isRequired,
    100      stepIn: PropTypes.func.isRequired,
    101      stepOut: PropTypes.func.isRequired,
    102      stepOver: PropTypes.func.isRequired,
    103      toggleEditorWrapping: PropTypes.func.isRequired,
    104      toggleInlinePreview: PropTypes.func.isRequired,
    105      toggleJavaScriptEnabled: PropTypes.func.isRequired,
    106      toggleSkipPausing: PropTypes.any.isRequired,
    107      toggleSourceMapsEnabled: PropTypes.func.isRequired,
    108      topFrameSelected: PropTypes.bool.isRequired,
    109      setHideOrShowIgnoredSources: PropTypes.func.isRequired,
    110      toggleSourceMapIgnoreList: PropTypes.func.isRequired,
    111      togglePausedOverlay: PropTypes.func.isRequired,
    112    };
    113  }
    114 
    115  componentWillUnmount() {
    116    const { shortcuts } = this.context;
    117 
    118    COMMANDS.forEach(action => shortcuts.off(getKey(action)));
    119 
    120    if (isMacOS) {
    121      COMMANDS.forEach(action => shortcuts.off(getKeyForOS("WINNT", action)));
    122    }
    123  }
    124 
    125  componentDidMount() {
    126    const { shortcuts } = this.context;
    127 
    128    COMMANDS.forEach(action =>
    129      shortcuts.on(getKey(action), e => this.handleEvent(e, action))
    130    );
    131 
    132    if (isMacOS) {
    133      // The Mac supports both the Windows Function keys
    134      // as well as the Mac non-Function keys
    135      COMMANDS.forEach(action =>
    136        shortcuts.on(getKeyForOS("WINNT", action), e =>
    137          this.handleEvent(e, action)
    138        )
    139      );
    140    }
    141  }
    142 
    143  handleEvent(e, action) {
    144    e.preventDefault();
    145    e.stopPropagation();
    146    if (action === "resume") {
    147      this.props.isPaused ? this.props.resume() : this.props.breakOnNext();
    148    } else {
    149      this.props[action]();
    150    }
    151  }
    152 
    153  renderStepButtons() {
    154    const { isPaused, topFrameSelected } = this.props;
    155    const className = isPaused ? "active" : "disabled";
    156    const isDisabled = !isPaused;
    157 
    158    return [
    159      this.renderPauseButton(),
    160      debugBtn(
    161        () => this.props.stepOver(),
    162        "stepOver",
    163        className,
    164        L10N.getFormatStr("stepOverTooltip", formatKey("stepOver")),
    165        isDisabled
    166      ),
    167      debugBtn(
    168        () => this.props.stepIn(),
    169        "stepIn",
    170        className,
    171        L10N.getFormatStr("stepInTooltip", formatKey("stepIn")),
    172        isDisabled || !topFrameSelected
    173      ),
    174      debugBtn(
    175        () => this.props.stepOut(),
    176        "stepOut",
    177        className,
    178        L10N.getFormatStr("stepOutTooltip", formatKey("stepOut")),
    179        isDisabled
    180      ),
    181    ];
    182  }
    183 
    184  resume() {
    185    this.props.resume();
    186  }
    187 
    188  renderPauseButton() {
    189    const { breakOnNext, isWaitingOnBreak } = this.props;
    190 
    191    if (this.props.isPaused) {
    192      return debugBtn(
    193        () => this.resume(),
    194        "resume",
    195        "active",
    196        L10N.getFormatStr("resumeButtonTooltip", formatKey("resume"))
    197      );
    198    }
    199 
    200    if (isWaitingOnBreak) {
    201      return debugBtn(
    202        null,
    203        "pause",
    204        "disabled",
    205        L10N.getStr("pausePendingButtonTooltip"),
    206        true
    207      );
    208    }
    209 
    210    return debugBtn(
    211      () => breakOnNext(),
    212      "pause",
    213      "active",
    214      L10N.getFormatStr("pauseButtonTooltip", formatKey("resume"))
    215    );
    216  }
    217 
    218  renderSkipPausingButton() {
    219    const { skipPausing, toggleSkipPausing } = this.props;
    220    return button(
    221      {
    222        className: classnames(
    223          "command-bar-button",
    224          "command-bar-skip-pausing",
    225          {
    226            active: skipPausing,
    227          }
    228        ),
    229        title: skipPausing
    230          ? L10N.getStr("undoSkipPausingTooltip.label")
    231          : L10N.getStr("skipPausingTooltip.label"),
    232        onClick: toggleSkipPausing,
    233      },
    234      React.createElement(DebuggerImage, {
    235        name: skipPausing ? "enable-pausing" : "disable-pausing",
    236      })
    237    );
    238  }
    239 
    240  renderSettingsButton() {
    241    const { toolboxDoc } = this.context;
    242    return React.createElement(
    243      MenuButton,
    244      {
    245        menuId: "debugger-settings-menu-button",
    246        toolboxDoc,
    247        className:
    248          "devtools-button command-bar-button debugger-settings-menu-button",
    249        title: L10N.getStr("settings.button.label"),
    250      },
    251      () => this.renderSettingsMenuItems()
    252    );
    253  }
    254 
    255  renderSettingsMenuItems() {
    256    return React.createElement(
    257      MenuList,
    258      {
    259        id: "debugger-settings-menu-list",
    260      },
    261      React.createElement(MenuItem, {
    262        key: "debugger-settings-menu-item-disable-javascript",
    263        className: "menu-item debugger-settings-menu-item-disable-javascript",
    264        checked: !this.props.javascriptEnabled,
    265        label: L10N.getStr("settings.disableJavaScript.label"),
    266        tooltip: L10N.getStr("settings.disableJavaScript.tooltip"),
    267        onClick: () => {
    268          this.props.toggleJavaScriptEnabled(!this.props.javascriptEnabled);
    269        },
    270      }),
    271      React.createElement(MenuItem, {
    272        key: "debugger-settings-menu-item-disable-inline-previews",
    273        checked: features.inlinePreview,
    274        label: L10N.getStr("inlinePreview.toggle.label"),
    275        tooltip: L10N.getStr("inlinePreview.toggle.tooltip"),
    276        onClick: () => this.props.toggleInlinePreview(!features.inlinePreview),
    277      }),
    278      React.createElement(MenuItem, {
    279        key: "debugger-settings-menu-item-disable-wrap-lines",
    280        checked: prefs.editorWrapping,
    281        label: L10N.getStr("editorWrapping.toggle.label"),
    282        tooltip: L10N.getStr("editorWrapping.toggle.tooltip"),
    283        onClick: () => this.props.toggleEditorWrapping(!prefs.editorWrapping),
    284      }),
    285      React.createElement(MenuItem, {
    286        key: "debugger-settings-menu-item-disable-sourcemaps",
    287        checked: prefs.clientSourceMapsEnabled,
    288        label: L10N.getStr("settings.toggleSourceMaps.label"),
    289        tooltip: L10N.getStr("settings.toggleSourceMaps.tooltip"),
    290        onClick: () =>
    291          this.props.toggleSourceMapsEnabled(!prefs.clientSourceMapsEnabled),
    292      }),
    293      React.createElement(MenuItem, {
    294        key: "debugger-settings-menu-item-hide-ignored-sources",
    295        className: "menu-item debugger-settings-menu-item-hide-ignored-sources",
    296        checked: prefs.hideIgnoredSources,
    297        label: L10N.getStr("settings.hideIgnoredSources.label"),
    298        tooltip: L10N.getStr("settings.hideIgnoredSources.tooltip"),
    299        onClick: () =>
    300          this.props.setHideOrShowIgnoredSources(!prefs.hideIgnoredSources),
    301      }),
    302      React.createElement(MenuItem, {
    303        key: "debugger-settings-menu-item-enable-sourcemap-ignore-list",
    304        className:
    305          "menu-item debugger-settings-menu-item-enable-sourcemap-ignore-list",
    306        checked: prefs.sourceMapIgnoreListEnabled,
    307        label: L10N.getStr("settings.enableSourceMapIgnoreList.label"),
    308        tooltip: L10N.getStr("settings.enableSourceMapIgnoreList.tooltip"),
    309        onClick: () =>
    310          this.props.toggleSourceMapIgnoreList(
    311            !prefs.sourceMapIgnoreListEnabled
    312          ),
    313      }),
    314      React.createElement(MenuItem, {
    315        key: "debugger-settings-menu-item-toggle-pause-overlay",
    316        className: "menu-item debugger-settings-menu-item-toggle-pause-overlay",
    317        checked: prefs.pausedOverlayEnabled,
    318        label: L10N.getStr("settings.showPausedOverlay.label"),
    319        tooltip: L10N.getStr("settings.showPausedOverlay.tooltip"),
    320        onClick: () =>
    321          this.props.togglePausedOverlay(!prefs.pausedOverlayEnabled),
    322      }),
    323      React.createElement(MenuItem, {
    324        key: "debugger-settings-menu-item-toggle-auto-pretty-print",
    325        className:
    326          "menu-item debugger-settings-menu-item-toggle-auto-pretty-print",
    327        checked: prefs.autoPrettyPrint,
    328        label: L10N.getStr("settings.autoPrettyPrint.label"),
    329        tooltip: L10N.getStr("settings.autoPrettyPrint.tooltip"),
    330        onClick: () => {
    331          prefs.autoPrettyPrint = !prefs.autoPrettyPrint;
    332        },
    333      })
    334    );
    335  }
    336 
    337  render() {
    338    return div(
    339      {
    340        className: classnames("command-bar", {
    341          vertical: !this.props.horizontal,
    342        }),
    343      },
    344      this.renderStepButtons(),
    345      div({
    346        className: "filler",
    347      }),
    348      this.renderSkipPausingButton(),
    349      div({
    350        className: "devtools-separator",
    351      }),
    352      this.renderSettingsButton()
    353    );
    354  }
    355 }
    356 
    357 CommandBar.contextTypes = {
    358  shortcuts: PropTypes.object,
    359  toolboxDoc: PropTypes.object,
    360 };
    361 
    362 const mapStateToProps = state => ({
    363  isWaitingOnBreak: getIsWaitingOnBreak(state, getCurrentThread(state)),
    364  skipPausing: getSkipPausing(state),
    365  topFrameSelected: isTopFrameSelected(state, getCurrentThread(state)),
    366  javascriptEnabled: state.ui.javascriptEnabled,
    367  isPaused: getIsCurrentThreadPaused(state),
    368 });
    369 
    370 export default connect(mapStateToProps, {
    371  resume: actions.resume,
    372  stepIn: actions.stepIn,
    373  stepOut: actions.stepOut,
    374  stepOver: actions.stepOver,
    375  breakOnNext: actions.breakOnNext,
    376  pauseOnExceptions: actions.pauseOnExceptions,
    377  toggleSkipPausing: actions.toggleSkipPausing,
    378  toggleInlinePreview: actions.toggleInlinePreview,
    379  toggleEditorWrapping: actions.toggleEditorWrapping,
    380  toggleSourceMapsEnabled: actions.toggleSourceMapsEnabled,
    381  toggleJavaScriptEnabled: actions.toggleJavaScriptEnabled,
    382  setHideOrShowIgnoredSources: actions.setHideOrShowIgnoredSources,
    383  toggleSourceMapIgnoreList: actions.toggleSourceMapIgnoreList,
    384  togglePausedOverlay: actions.togglePausedOverlay,
    385 })(CommandBar);