tor-browser

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

Scopes.js (11692B)


      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, { PureComponent } from "devtools/client/shared/vendor/react";
      6 import {
      7  div,
      8  button,
      9  span,
     10 } from "devtools/client/shared/vendor/react-dom-factories";
     11 import PropTypes from "devtools/client/shared/vendor/react-prop-types";
     12 import DebuggerImage from "../shared/DebuggerImage";
     13 import { showMenu } from "../../context-menu/menu";
     14 import { connect } from "devtools/client/shared/vendor/react-redux";
     15 import actions from "../../actions/index";
     16 
     17 import {
     18  getSelectedFrame,
     19  getSelectedSource,
     20  getGeneratedFrameScope,
     21  getOriginalFrameScope,
     22  getPauseReason,
     23  isMapScopesEnabled,
     24  getLastExpandedScopes,
     25  getIsCurrentThreadPaused,
     26 } from "../../selectors/index";
     27 import {
     28  getScopesItemsForSelectedFrame,
     29  getScopeItemPath,
     30 } from "../../utils/pause/scopes";
     31 import { clientCommands } from "../../client/firefox";
     32 
     33 import * as objectInspector from "resource://devtools/client/shared/components/object-inspector/index.js";
     34 const { ObjectInspector } = objectInspector;
     35 
     36 class Scopes extends PureComponent {
     37  constructor(props) {
     38    const { why, selectedFrame, originalFrameScopes, generatedFrameScopes } =
     39      props;
     40 
     41    super(props);
     42 
     43    this.state = {
     44      originalScopes: getScopesItemsForSelectedFrame(
     45        why,
     46        selectedFrame,
     47        originalFrameScopes
     48      ),
     49      generatedScopes: getScopesItemsForSelectedFrame(
     50        why,
     51        selectedFrame,
     52        generatedFrameScopes
     53      ),
     54    };
     55  }
     56 
     57  static get propTypes() {
     58    return {
     59      addWatchpoint: PropTypes.func.isRequired,
     60      expandedScopes: PropTypes.array.isRequired,
     61      generatedFrameScopes: PropTypes.object,
     62      highlightDomElement: PropTypes.func.isRequired,
     63      isLoading: PropTypes.bool.isRequired,
     64      isPaused: PropTypes.bool.isRequired,
     65      mapScopesEnabled: PropTypes.bool.isRequired,
     66      openElementInInspector: PropTypes.func.isRequired,
     67      openLink: PropTypes.func.isRequired,
     68      originalFrameScopes: PropTypes.object,
     69      removeWatchpoint: PropTypes.func.isRequired,
     70      setExpandedScope: PropTypes.func.isRequired,
     71      unHighlightDomElement: PropTypes.func.isRequired,
     72      why: PropTypes.object.isRequired,
     73      selectedFrame: PropTypes.object,
     74      toggleMapScopes: PropTypes.func.isRequired,
     75      selectedSource: PropTypes.object.isRequired,
     76    };
     77  }
     78 
     79  // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
     80  UNSAFE_componentWillReceiveProps(nextProps) {
     81    const {
     82      selectedFrame,
     83      originalFrameScopes,
     84      generatedFrameScopes,
     85      isPaused,
     86      selectedSource,
     87    } = this.props;
     88    const isPausedChanged = isPaused !== nextProps.isPaused;
     89    const selectedFrameChanged = selectedFrame !== nextProps.selectedFrame;
     90    const originalFrameScopesChanged =
     91      originalFrameScopes !== nextProps.originalFrameScopes;
     92    const generatedFrameScopesChanged =
     93      generatedFrameScopes !== nextProps.generatedFrameScopes;
     94    const selectedSourceChanged = selectedSource !== nextProps.selectedSource;
     95 
     96    if (
     97      isPausedChanged ||
     98      selectedFrameChanged ||
     99      originalFrameScopesChanged ||
    100      generatedFrameScopesChanged ||
    101      selectedSourceChanged
    102    ) {
    103      this.setState({
    104        originalScopes: getScopesItemsForSelectedFrame(
    105          nextProps.why,
    106          nextProps.selectedFrame,
    107          nextProps.originalFrameScopes
    108        ),
    109        generatedScopes: getScopesItemsForSelectedFrame(
    110          nextProps.why,
    111          nextProps.selectedFrame,
    112          nextProps.generatedFrameScopes
    113        ),
    114      });
    115    }
    116  }
    117 
    118  onContextMenu = (event, item) => {
    119    const { addWatchpoint, removeWatchpoint } = this.props;
    120 
    121    if (!item.parent || !item.contents.configurable) {
    122      return;
    123    }
    124 
    125    if (!item.contents || item.contents.watchpoint) {
    126      const removeWatchpointLabel = L10N.getStr("watchpoints.removeWatchpoint");
    127 
    128      const removeWatchpointItem = {
    129        id: "node-menu-remove-watchpoint",
    130        label: removeWatchpointLabel,
    131        disabled: false,
    132        click: () => removeWatchpoint(item),
    133      };
    134 
    135      const menuItems = [removeWatchpointItem];
    136      showMenu(event, menuItems);
    137      return;
    138    }
    139 
    140    const addSetWatchpointLabel = L10N.getStr("watchpoints.setWatchpoint");
    141    const addGetWatchpointLabel = L10N.getStr("watchpoints.getWatchpoint");
    142    const addGetOrSetWatchpointLabel = L10N.getStr(
    143      "watchpoints.getOrSetWatchpoint"
    144    );
    145    const watchpointsSubmenuLabel = L10N.getStr("watchpoints.submenu");
    146 
    147    const addSetWatchpointItem = {
    148      id: "node-menu-add-set-watchpoint",
    149      label: addSetWatchpointLabel,
    150      disabled: false,
    151      click: () => addWatchpoint(item, "set"),
    152    };
    153 
    154    const addGetWatchpointItem = {
    155      id: "node-menu-add-get-watchpoint",
    156      label: addGetWatchpointLabel,
    157      disabled: false,
    158      click: () => addWatchpoint(item, "get"),
    159    };
    160 
    161    const addGetOrSetWatchpointItem = {
    162      id: "node-menu-add-get-watchpoint",
    163      label: addGetOrSetWatchpointLabel,
    164      disabled: false,
    165      click: () => addWatchpoint(item, "getorset"),
    166    };
    167 
    168    const watchpointsSubmenuItem = {
    169      id: "node-menu-watchpoints",
    170      label: watchpointsSubmenuLabel,
    171      disabled: false,
    172      click: () => addWatchpoint(item, "set"),
    173      submenu: [
    174        addSetWatchpointItem,
    175        addGetWatchpointItem,
    176        addGetOrSetWatchpointItem,
    177      ],
    178    };
    179 
    180    const menuItems = [watchpointsSubmenuItem];
    181    showMenu(event, menuItems);
    182  };
    183 
    184  renderWatchpointButton = item => {
    185    const { removeWatchpoint } = this.props;
    186 
    187    if (
    188      !item ||
    189      !item.contents ||
    190      !item.contents.watchpoint ||
    191      typeof L10N === "undefined"
    192    ) {
    193      return null;
    194    }
    195 
    196    const { watchpoint } = item.contents;
    197    return button({
    198      className: `remove-watchpoint-${watchpoint}`,
    199      title: L10N.getStr("watchpoints.removeWatchpointTooltip"),
    200      onClick: e => {
    201        e.stopPropagation();
    202        removeWatchpoint(item);
    203      },
    204    });
    205  };
    206 
    207  renderScopesList() {
    208    const {
    209      isLoading,
    210      openLink,
    211      openElementInInspector,
    212      highlightDomElement,
    213      unHighlightDomElement,
    214      mapScopesEnabled,
    215      selectedFrame,
    216      setExpandedScope,
    217      expandedScopes,
    218      selectedSource,
    219    } = this.props;
    220 
    221    if (!selectedSource) {
    222      return div(
    223        { className: "pane scopes-list" },
    224        div({ className: "pane-info" }, L10N.getStr("scopes.notAvailable"))
    225      );
    226    }
    227 
    228    const { originalScopes, generatedScopes } = this.state;
    229    let scopes = null;
    230 
    231    if (
    232      selectedSource.isOriginal &&
    233      !selectedSource.isPrettyPrinted &&
    234      !selectedFrame.generatedLocation?.source.isWasm
    235    ) {
    236      if (!mapScopesEnabled) {
    237        return div(
    238          { className: "pane scopes-list" },
    239          div(
    240            {
    241              className: "pane-info no-original-scopes-info",
    242              "aria-role": "status",
    243            },
    244            span(
    245              { className: "info icon" },
    246              React.createElement(DebuggerImage, { name: "sourcemap" })
    247            ),
    248            L10N.getFormatStr(
    249              "scopes.noOriginalScopes",
    250              L10N.getStr("scopes.showOriginalScopes")
    251            )
    252          )
    253        );
    254      }
    255      if (isLoading) {
    256        return div(
    257          {
    258            className: "pane scopes-list",
    259          },
    260          div(
    261            { className: "pane-info" },
    262            span(
    263              { className: "info icon" },
    264              React.createElement(DebuggerImage, { name: "loader" })
    265            ),
    266            L10N.getStr("scopes.loadingOriginalScopes")
    267          )
    268        );
    269      }
    270      scopes = originalScopes;
    271    } else {
    272      if (isLoading) {
    273        return div(
    274          {
    275            className: "pane scopes-list",
    276          },
    277          div(
    278            { className: "pane-info" },
    279            span(
    280              { className: "info icon" },
    281              React.createElement(DebuggerImage, { name: "loader" })
    282            ),
    283            L10N.getStr("loadingText")
    284          )
    285        );
    286      }
    287      scopes = generatedScopes;
    288    }
    289 
    290    function getInitiallyExpanded(item) {
    291      return expandedScopes.some(path => path == getScopeItemPath(item));
    292    }
    293 
    294    if (scopes && !!scopes.length) {
    295      return div(
    296        {
    297          className: "pane scopes-list",
    298        },
    299        React.createElement(ObjectInspector, {
    300          roots: scopes,
    301          autoExpandAll: false,
    302          autoExpandDepth: 1,
    303          client: clientCommands,
    304          createElement: tagName => document.createElement(tagName),
    305          disableWrap: true,
    306          dimTopLevelWindow: true,
    307          frame: selectedFrame,
    308          mayUseCustomFormatter: true,
    309          openLink,
    310          onDOMNodeClick: grip => openElementInInspector(grip),
    311          onInspectIconClick: grip => openElementInInspector(grip),
    312          onDOMNodeMouseOver: grip => highlightDomElement(grip),
    313          onDOMNodeMouseOut: grip => unHighlightDomElement(grip),
    314          onContextMenu: this.onContextMenu,
    315          preventBlur: true,
    316          setExpanded: (path, expand) =>
    317            setExpandedScope(selectedFrame, path, expand),
    318          getInitiallyExpanded,
    319          renderItemActions: this.renderWatchpointButton,
    320          shouldRenderTooltip: true,
    321        })
    322      );
    323    }
    324 
    325    return div(
    326      {
    327        className: "pane scopes-list",
    328      },
    329      div(
    330        {
    331          className: "pane-info",
    332        },
    333        L10N.getStr("scopes.notAvailable")
    334      )
    335    );
    336  }
    337 
    338  render() {
    339    return div(
    340      {
    341        className: "scopes-content",
    342      },
    343      this.renderScopesList()
    344    );
    345  }
    346 }
    347 
    348 const mapStateToProps = state => {
    349  // This component doesn't need any prop when we are not paused
    350  const selectedFrame = getSelectedFrame(state);
    351  if (!selectedFrame) {
    352    return {};
    353  }
    354  const why = getPauseReason(state, selectedFrame.thread);
    355  const expandedScopes = getLastExpandedScopes(state, selectedFrame.thread);
    356  const isPaused = getIsCurrentThreadPaused(state);
    357  const selectedSource = getSelectedSource(state);
    358 
    359  let originalFrameScopes;
    360  let generatedFrameScopes;
    361  let isLoading;
    362  let mapScopesEnabled = false;
    363 
    364  if (
    365    selectedSource?.isOriginal &&
    366    !selectedSource?.isPrettyPrinted &&
    367    !selectedFrame.generatedLocation?.source.isWasm
    368  ) {
    369    const { scope, pending: originalPending } = getOriginalFrameScope(
    370      state,
    371      selectedFrame
    372    ) || {
    373      scope: null,
    374      pending: false,
    375    };
    376    originalFrameScopes = scope;
    377    isLoading = originalPending;
    378    mapScopesEnabled = isMapScopesEnabled(state);
    379  } else {
    380    const { scope, pending: generatedPending } = getGeneratedFrameScope(
    381      state,
    382      selectedFrame
    383    ) || {
    384      scope: null,
    385      pending: false,
    386    };
    387    generatedFrameScopes = scope;
    388    isLoading = generatedPending;
    389  }
    390 
    391  return {
    392    originalFrameScopes,
    393    generatedFrameScopes,
    394    mapScopesEnabled,
    395    selectedFrame,
    396    isLoading,
    397    why,
    398    expandedScopes,
    399    isPaused,
    400    selectedSource,
    401  };
    402 };
    403 
    404 export default connect(mapStateToProps, {
    405  openLink: actions.openLink,
    406  openElementInInspector: actions.openElementInInspectorCommand,
    407  highlightDomElement: actions.highlightDomElement,
    408  unHighlightDomElement: actions.unHighlightDomElement,
    409  setExpandedScope: actions.setExpandedScope,
    410  addWatchpoint: actions.addWatchpoint,
    411  removeWatchpoint: actions.removeWatchpoint,
    412  toggleMapScopes: actions.toggleMapScopes,
    413 })(Scopes);