tor-browser

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

index.js (4652B)


      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 PropTypes from "devtools/client/shared/vendor/react-prop-types";
      6 import React, { PureComponent } from "devtools/client/shared/vendor/react";
      7 import { connect } from "devtools/client/shared/vendor/react-redux";
      8 
      9 import Popup from "./Popup";
     10 
     11 import {
     12  getIsCurrentThreadPaused,
     13  getSelectedTraceIndex,
     14 } from "../../../selectors/index";
     15 import actions from "../../../actions/index";
     16 
     17 const EXCEPTION_MARKER = "mark-text-exception";
     18 
     19 const LOADING_CLASS = "preview-loading-token";
     20 
     21 class Preview extends PureComponent {
     22  target = null;
     23  constructor(props) {
     24    super(props);
     25    this.state = { selecting: false, loading: null };
     26  }
     27 
     28  static get propTypes() {
     29    return {
     30      editor: PropTypes.object.isRequired,
     31      editorRef: PropTypes.object.isRequired,
     32      isPaused: PropTypes.bool.isRequired,
     33      hasSelectedTrace: PropTypes.bool.isRequired,
     34      getExceptionPreview: PropTypes.func.isRequired,
     35      getPreview: PropTypes.func,
     36    };
     37  }
     38 
     39  componentDidMount() {
     40    this.props.editor.on("tokenenter", this.onTokenEnter);
     41    this.props.editor.addEditorDOMEventListeners({
     42      mouseup: this.onMouseUp,
     43      mousedown: this.onMouseDown,
     44      scroll: this.onScroll,
     45    });
     46  }
     47 
     48  componentWillUnmount() {
     49    this.props.editor.off("tokenenter", this.onTokenEnter);
     50    this.props.editor.removeEditorDOMEventListeners({
     51      mouseup: this.onMouseUp,
     52      mousedown: this.onMouseDown,
     53      scroll: this.onScroll,
     54    });
     55  }
     56 
     57  componentDidUpdate(_prevProps, prevState) {
     58    // Ensure that only one token is highlighted as "loading"
     59    const previous = prevState.loading;
     60    if (previous) {
     61      previous.classList.remove(LOADING_CLASS);
     62    }
     63    const { loading } = this.state;
     64    if (loading) {
     65      loading.classList.add(LOADING_CLASS);
     66    }
     67  }
     68 
     69  // Note that these events are emitted by utils/editor/tokens.js
     70  onTokenEnter = async ({ target, tokenPos }) => {
     71    // Use a temporary object to uniquely identify the asynchronous processing of this user event
     72    // and bail out if we started hovering another token.
     73    const tokenId = {};
     74    this.currentTokenId = tokenId;
     75 
     76    // Immediately highlight the hovered token as "loading"
     77    this.setState({ loading: target });
     78 
     79    const {
     80      editor,
     81      getPausedPreview,
     82      getTracerPreview,
     83      getExceptionPreview,
     84      isPaused,
     85      hasSelectedTrace,
     86    } = this.props;
     87    const isTargetException = target.closest(`.${EXCEPTION_MARKER}`);
     88 
     89    let preview;
     90    try {
     91      if (isTargetException) {
     92        preview = await getExceptionPreview(target, tokenPos, editor);
     93      }
     94 
     95      if (!preview && (hasSelectedTrace || isPaused) && !this.state.selecting) {
     96        if (hasSelectedTrace) {
     97          preview = await getTracerPreview(target, tokenPos, editor);
     98        }
     99        if (!preview && isPaused) {
    100          preview = await getPausedPreview(target, tokenPos, editor);
    101        }
    102      }
    103    } catch (e) {
    104      // Ignore any exception and dismiss the popup (as preview will be null)
    105    }
    106 
    107    // Prevent modifying state and showing this preview if we started hovering another token
    108    if (this.currentTokenId !== tokenId) {
    109      return;
    110    }
    111 
    112    this.setState({ loading: null, preview });
    113  };
    114 
    115  onMouseUp = () => {
    116    if (this.props.isPaused || this.props.hasSelectedTrace) {
    117      this.setState({ selecting: false });
    118    }
    119  };
    120 
    121  onMouseDown = () => {
    122    if (this.props.isPaused || this.props.hasSelectedTrace) {
    123      this.setState({ selecting: true });
    124    }
    125  };
    126 
    127  onScroll = () => {
    128    if (this.props.isPaused || this.props.hasSelectedTrace) {
    129      this.clearPreview();
    130    }
    131  };
    132 
    133  clearPreview = () => {
    134    this.setState({ loading: null, preview: null });
    135  };
    136 
    137  render() {
    138    if (this.state.selecting) {
    139      return null;
    140    }
    141 
    142    const { preview } = this.state;
    143    if (!preview) {
    144      return null;
    145    }
    146 
    147    return React.createElement(Popup, {
    148      preview,
    149      editor: this.props.editor,
    150      editorRef: this.props.editorRef,
    151      clearPreview: this.clearPreview,
    152    });
    153  }
    154 }
    155 
    156 const mapStateToProps = state => {
    157  return {
    158    isPaused: getIsCurrentThreadPaused(state),
    159    hasSelectedTrace: getSelectedTraceIndex(state) != null,
    160  };
    161 };
    162 
    163 export default connect(mapStateToProps, {
    164  addExpression: actions.addExpression,
    165  getPausedPreview: actions.getPausedPreview,
    166  getTracerPreview: actions.getTracerPreview,
    167  getExceptionPreview: actions.getExceptionPreview,
    168 })(Preview);