tor-browser

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

index.js (14412B)


      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 const SplitBox = require("resource://devtools/client/shared/components/splitter/SplitBox.js");
      6 
      7 import React, { Component } from "devtools/client/shared/vendor/react";
      8 import {
      9  div,
     10  input,
     11  label,
     12  button,
     13  a,
     14 } from "devtools/client/shared/vendor/react-dom-factories";
     15 import PropTypes from "devtools/client/shared/vendor/react-prop-types";
     16 import { connect } from "devtools/client/shared/vendor/react-redux";
     17 
     18 import actions from "../../actions/index";
     19 import {
     20  getTopFrame,
     21  getExpressions,
     22  isMapScopesEnabled,
     23  getSelectedFrame,
     24  getSelectedSource,
     25  getThreads,
     26  getCurrentThread,
     27  getPauseReason,
     28  getShouldBreakpointsPaneOpenOnPause,
     29  getSkipPausing,
     30  shouldLogEventBreakpoints,
     31 } from "../../selectors/index";
     32 
     33 import DebuggerImage from "../shared/DebuggerImage";
     34 import { prefs } from "../../utils/prefs";
     35 
     36 import Breakpoints from "./Breakpoints/index";
     37 import Expressions from "./Expressions";
     38 import Frames from "./Frames/index";
     39 import Threads from "./Threads";
     40 import Accordion from "../shared/Accordion";
     41 import CommandBar from "./CommandBar";
     42 import XHRBreakpoints from "./XHRBreakpoints";
     43 import EventListeners from "../shared/EventListeners";
     44 import DOMMutationBreakpoints from "./DOMMutationBreakpoints";
     45 import WhyPaused from "./WhyPaused";
     46 
     47 import Scopes from "./Scopes";
     48 
     49 const classnames = require("resource://devtools/client/shared/classnames.js");
     50 
     51 function debugBtn(onClick, type, className, tooltip) {
     52  return button(
     53    {
     54      onClick,
     55      className: `${type} ${className}`,
     56      key: type,
     57      title: tooltip,
     58    },
     59    React.createElement(DebuggerImage, {
     60      name: type,
     61      title: tooltip,
     62      "aria-label": tooltip,
     63    })
     64  );
     65 }
     66 
     67 const mdnLink =
     68  "https://firefox-source-docs.mozilla.org/devtools-user/debugger/using_the_debugger_map_scopes_feature/";
     69 
     70 class SecondaryPanes extends Component {
     71  constructor(props) {
     72    super(props);
     73 
     74    this.state = {
     75      showExpressionsInput: false,
     76      showXHRInput: false,
     77      expandedFrameGroups: {},
     78    };
     79  }
     80 
     81  static get propTypes() {
     82    return {
     83      evaluateExpressionsForCurrentContext: PropTypes.func.isRequired,
     84      expressions: PropTypes.array.isRequired,
     85      hasFrames: PropTypes.bool.isRequired,
     86      horizontal: PropTypes.bool.isRequired,
     87      logEventBreakpoints: PropTypes.bool.isRequired,
     88      mapScopesEnabled: PropTypes.bool.isRequired,
     89      pauseReason: PropTypes.string.isRequired,
     90      shouldBreakpointsPaneOpenOnPause: PropTypes.bool.isRequired,
     91      thread: PropTypes.string,
     92      skipPausing: PropTypes.bool.isRequired,
     93      showScopesButtons: PropTypes.bool.isRequired,
     94      toggleEventLogging: PropTypes.func.isRequired,
     95      resetBreakpointsPaneState: PropTypes.func.isRequired,
     96      toggleMapScopes: PropTypes.func.isRequired,
     97      showThreads: PropTypes.bool.isRequired,
     98      removeAllBreakpoints: PropTypes.func.isRequired,
     99      removeAllXHRBreakpoints: PropTypes.func.isRequired,
    100    };
    101  }
    102 
    103  onExpressionAdded = () => {
    104    this.setState({ showExpressionsInput: false });
    105  };
    106 
    107  onXHRAdded = () => {
    108    this.setState({ showXHRInput: false });
    109  };
    110 
    111  onExpandFrameGroup = expandedFrameGroups => {
    112    this.setState({
    113      expandedFrameGroups: { ...expandedFrameGroups },
    114    });
    115  };
    116 
    117  watchExpressionHeaderButtons() {
    118    const { expressions } = this.props;
    119    const buttons = [];
    120 
    121    if (expressions.length) {
    122      buttons.push(
    123        debugBtn(
    124          () => {
    125            this.props.evaluateExpressionsForCurrentContext();
    126          },
    127          "refresh",
    128          "active",
    129          L10N.getStr("watchExpressions.refreshButton")
    130        )
    131      );
    132    }
    133    buttons.push(
    134      debugBtn(
    135        () => {
    136          if (!prefs.expressionsVisible) {
    137            this.onWatchExpressionPaneToggle(true);
    138          }
    139          this.setState({ showExpressionsInput: true });
    140        },
    141        "plus",
    142        "active",
    143        L10N.getStr("expressions.placeholder2")
    144      )
    145    );
    146    return buttons;
    147  }
    148 
    149  xhrBreakpointsHeaderButtons() {
    150    return [
    151      debugBtn(
    152        () => {
    153          if (!prefs.xhrBreakpointsVisible) {
    154            this.onXHRPaneToggle(true);
    155          }
    156          this.setState({ showXHRInput: true });
    157        },
    158        "plus",
    159        "active",
    160        L10N.getStr("xhrBreakpoints.label")
    161      ),
    162 
    163      debugBtn(
    164        () => {
    165          this.props.removeAllXHRBreakpoints();
    166        },
    167        "removeAll",
    168        "active",
    169        L10N.getStr("xhrBreakpoints.removeAll.tooltip")
    170      ),
    171    ];
    172  }
    173 
    174  breakpointsHeaderButtons() {
    175    return [
    176      debugBtn(
    177        () => {
    178          this.props.removeAllBreakpoints();
    179        },
    180        "removeAll",
    181        "active",
    182        L10N.getStr("breakpointMenuItem.deleteAll")
    183      ),
    184    ];
    185  }
    186 
    187  getScopeItem() {
    188    return {
    189      header: L10N.getStr("scopes.header"),
    190      className: "scopes-pane",
    191      id: "scopes-pane",
    192      component: React.createElement(Scopes, null),
    193      opened: prefs.scopesVisible,
    194      buttons: this.getScopesButtons(),
    195      onToggle: opened => {
    196        prefs.scopesVisible = opened;
    197      },
    198    };
    199  }
    200 
    201  getScopesButtons() {
    202    if (!this.props.showScopesButtons) {
    203      return null;
    204    }
    205    const { mapScopesEnabled } = this.props;
    206 
    207    return [
    208      div(
    209        {
    210          key: "scopes-buttons",
    211        },
    212        label(
    213          {
    214            className: "map-scopes-header",
    215            title: L10N.getStr("scopes.showOriginalScopesTooltip"),
    216            onClick: e => e.stopPropagation(),
    217          },
    218          input({
    219            type: "checkbox",
    220            checked: mapScopesEnabled ? "checked" : "",
    221            onChange: () => this.props.toggleMapScopes(),
    222          }),
    223          L10N.getStr("scopes.showOriginalScopes")
    224        ),
    225        a(
    226          {
    227            className: "mdn",
    228            target: "_blank",
    229            href: mdnLink,
    230            onClick: e => e.stopPropagation(),
    231            title: L10N.getStr("scopes.showOriginalScopesHelpTooltip"),
    232          },
    233          React.createElement(DebuggerImage, {
    234            name: "shortcuts",
    235          })
    236        )
    237      ),
    238    ];
    239  }
    240 
    241  getEventButtons() {
    242    const { logEventBreakpoints } = this.props;
    243    return [
    244      div(
    245        {
    246          key: "events-buttons",
    247        },
    248        label(
    249          {
    250            className: "events-header",
    251            title: L10N.getStr("eventlisteners.log.label"),
    252          },
    253          input({
    254            type: "checkbox",
    255            checked: logEventBreakpoints ? "checked" : "",
    256            onChange: () => this.props.toggleEventLogging(),
    257          }),
    258          L10N.getStr("eventlisteners.log")
    259        )
    260      ),
    261    ];
    262  }
    263 
    264  onWatchExpressionPaneToggle(opened) {
    265    prefs.expressionsVisible = opened;
    266  }
    267 
    268  getWatchItem() {
    269    return {
    270      header: L10N.getStr("watchExpressions.header"),
    271      id: "watch-expressions-pane",
    272      className: "watch-expressions-pane",
    273      buttons: this.watchExpressionHeaderButtons(),
    274      component: React.createElement(Expressions, {
    275        showInput: this.state.showExpressionsInput,
    276        onExpressionAdded: this.onExpressionAdded,
    277      }),
    278      opened: prefs.expressionsVisible,
    279      onToggle: this.onWatchExpressionPaneToggle,
    280    };
    281  }
    282 
    283  onXHRPaneToggle(opened) {
    284    prefs.xhrBreakpointsVisible = opened;
    285  }
    286 
    287  getXHRItem() {
    288    const { pauseReason } = this.props;
    289 
    290    return {
    291      header: L10N.getStr("xhrBreakpoints.header"),
    292      id: "xhr-breakpoints-pane",
    293      className: "xhr-breakpoints-pane",
    294      buttons: this.xhrBreakpointsHeaderButtons(),
    295      component: React.createElement(XHRBreakpoints, {
    296        showInput: this.state.showXHRInput,
    297        onXHRAdded: this.onXHRAdded,
    298      }),
    299      opened: prefs.xhrBreakpointsVisible || pauseReason === "XHR",
    300      onToggle: this.onXHRPaneToggle,
    301    };
    302  }
    303 
    304  getCallStackItem() {
    305    return {
    306      header: L10N.getStr("callStack.header"),
    307      id: "call-stack-pane",
    308      className: "call-stack-pane",
    309      component: React.createElement(Frames, {
    310        panel: "debugger",
    311        // These props enable storing and using the current expanded state
    312        // of the frame groups. This is we always handle displaying selected frames
    313        // in groups correctly.
    314        onExpandFrameGroup: this.onExpandFrameGroup,
    315        expandedFrameGroups: this.state.expandedFrameGroups,
    316      }),
    317      opened: prefs.callStackVisible,
    318      onToggle: opened => {
    319        prefs.callStackVisible = opened;
    320      },
    321    };
    322  }
    323 
    324  getThreadsItem() {
    325    return {
    326      header: L10N.getStr("threadsHeader"),
    327      id: "threads-pane",
    328      className: "threads-pane",
    329      component: React.createElement(Threads, null),
    330      opened: prefs.threadsVisible,
    331      onToggle: opened => {
    332        prefs.threadsVisible = opened;
    333      },
    334    };
    335  }
    336 
    337  getBreakpointsItem() {
    338    const { pauseReason, shouldBreakpointsPaneOpenOnPause, thread } =
    339      this.props;
    340 
    341    return {
    342      header: L10N.getStr("breakpoints.header"),
    343      id: "breakpoints-pane",
    344      className: "breakpoints-pane",
    345      buttons: this.breakpointsHeaderButtons(),
    346      component: React.createElement(Breakpoints),
    347      opened:
    348        prefs.breakpointsVisible ||
    349        (pauseReason === "breakpoint" && shouldBreakpointsPaneOpenOnPause),
    350      onToggle: opened => {
    351        prefs.breakpointsVisible = opened;
    352        //  one-shot flag used to force open the Breakpoints Pane only
    353        //  when hitting a breakpoint, but not when selecting frames etc...
    354        if (shouldBreakpointsPaneOpenOnPause) {
    355          this.props.resetBreakpointsPaneState(thread);
    356        }
    357      },
    358    };
    359  }
    360 
    361  getEventListenersItem() {
    362    const { pauseReason } = this.props;
    363 
    364    return {
    365      header: L10N.getStr("eventListenersHeader1"),
    366      id: "event-listeners-pane",
    367      className: "event-listeners-pane",
    368      buttons: this.getEventButtons(),
    369      component: React.createElement(EventListeners, {
    370        panelKey: "breakpoint",
    371      }),
    372      opened: prefs.eventListenersVisible || pauseReason === "eventBreakpoint",
    373      onToggle: opened => {
    374        prefs.eventListenersVisible = opened;
    375      },
    376    };
    377  }
    378 
    379  getDOMMutationsItem() {
    380    const { pauseReason } = this.props;
    381 
    382    return {
    383      header: L10N.getStr("domMutationHeader"),
    384      id: "dom-mutations-pane",
    385      className: "dom-mutations-pane",
    386      buttons: [],
    387      component: React.createElement(DOMMutationBreakpoints, null),
    388      opened:
    389        prefs.domMutationBreakpointsVisible ||
    390        pauseReason === "mutationBreakpoint",
    391      onToggle: opened => {
    392        prefs.domMutationBreakpointsVisible = opened;
    393      },
    394    };
    395  }
    396 
    397  getStartItems() {
    398    const items = [];
    399    const { horizontal, hasFrames } = this.props;
    400 
    401    if (horizontal) {
    402      if (this.props.showThreads) {
    403        items.push(this.getThreadsItem());
    404      }
    405 
    406      items.push(this.getWatchItem());
    407    }
    408 
    409    items.push(this.getBreakpointsItem());
    410 
    411    if (hasFrames) {
    412      items.push(this.getCallStackItem());
    413      if (horizontal) {
    414        items.push(this.getScopeItem());
    415      }
    416    }
    417 
    418    items.push(this.getXHRItem());
    419 
    420    items.push(this.getEventListenersItem());
    421 
    422    items.push(this.getDOMMutationsItem());
    423 
    424    return items;
    425  }
    426 
    427  getEndItems() {
    428    if (this.props.horizontal) {
    429      return [];
    430    }
    431 
    432    const items = [];
    433    if (this.props.showThreads) {
    434      items.push(this.getThreadsItem());
    435    }
    436 
    437    items.push(this.getWatchItem());
    438 
    439    if (this.props.hasFrames) {
    440      items.push(this.getScopeItem());
    441    }
    442 
    443    return items;
    444  }
    445 
    446  getItems() {
    447    return [...this.getStartItems(), ...this.getEndItems()];
    448  }
    449 
    450  renderHorizontalLayout() {
    451    return div(
    452      null,
    453      React.createElement(WhyPaused),
    454      React.createElement(Accordion, {
    455        items: this.getItems(),
    456      })
    457    );
    458  }
    459 
    460  renderVerticalLayout() {
    461    return React.createElement(SplitBox, {
    462      initialSize: "300px",
    463      minSize: 10,
    464      maxSize: "50%",
    465      splitterSize: 1,
    466      startPanel: div(
    467        {
    468          style: {
    469            width: "inherit",
    470          },
    471        },
    472        React.createElement(WhyPaused),
    473        React.createElement(Accordion, {
    474          items: this.getStartItems(),
    475        })
    476      ),
    477      endPanel: React.createElement(Accordion, {
    478        items: this.getEndItems(),
    479      }),
    480    });
    481  }
    482 
    483  render() {
    484    const { skipPausing } = this.props;
    485    return div(
    486      {
    487        className: "secondary-panes-wrapper",
    488      },
    489      React.createElement(CommandBar, {
    490        horizontal: this.props.horizontal,
    491      }),
    492      React.createElement(
    493        "div",
    494        {
    495          className: classnames(
    496            "secondary-panes",
    497            skipPausing && "skip-pausing"
    498          ),
    499        },
    500        this.props.horizontal
    501          ? this.renderHorizontalLayout()
    502          : this.renderVerticalLayout()
    503      )
    504    );
    505  }
    506 }
    507 
    508 const mapStateToProps = state => {
    509  const thread = getCurrentThread(state);
    510  const selectedFrame = getSelectedFrame(state);
    511  const selectedSource = getSelectedSource(state);
    512  const pauseReason = getPauseReason(state, thread);
    513  const shouldBreakpointsPaneOpenOnPause = getShouldBreakpointsPaneOpenOnPause(
    514    state,
    515    thread
    516  );
    517 
    518  return {
    519    expressions: getExpressions(state),
    520    hasFrames: !!getTopFrame(state, thread),
    521    mapScopesEnabled: isMapScopesEnabled(state),
    522    showThreads: !!getThreads(state).length,
    523    skipPausing: getSkipPausing(state),
    524    logEventBreakpoints: shouldLogEventBreakpoints(state),
    525    showScopesButtons:
    526      selectedFrame &&
    527      selectedSource &&
    528      selectedSource.isOriginal &&
    529      !selectedSource.isPrettyPrinted,
    530    pauseReason: pauseReason?.type ?? "",
    531    shouldBreakpointsPaneOpenOnPause,
    532    thread,
    533  };
    534 };
    535 
    536 export default connect(mapStateToProps, {
    537  evaluateExpressionsForCurrentContext:
    538    actions.evaluateExpressionsForCurrentContext,
    539  toggleMapScopes: actions.toggleMapScopes,
    540  breakOnNext: actions.breakOnNext,
    541  toggleEventLogging: actions.toggleEventLogging,
    542  removeAllBreakpoints: actions.removeAllBreakpoints,
    543  removeAllXHRBreakpoints: actions.removeAllXHRBreakpoints,
    544  resetBreakpointsPaneState: actions.resetBreakpointsPaneState,
    545 })(SecondaryPanes);