tor-browser

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

DebugLine.js (5642B)


      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 { PureComponent } from "devtools/client/shared/vendor/react";
      6 import PropTypes from "devtools/client/shared/vendor/react-prop-types";
      7 import { toEditorPosition } from "../../utils/editor/index";
      8 import { isException } from "../../utils/pause/index";
      9 import { connect } from "devtools/client/shared/vendor/react-redux";
     10 import {
     11  getVisibleSelectedFrame,
     12  getPauseReason,
     13  getSourceTextContentForLocation,
     14  getCurrentThread,
     15  getViewport,
     16  getSelectedTraceLocation,
     17 } from "../../selectors/index";
     18 
     19 export class DebugLine extends PureComponent {
     20  debugExpression;
     21 
     22  static get propTypes() {
     23    return {
     24      editor: PropTypes.object,
     25      selectedSource: PropTypes.object,
     26      location: PropTypes.object,
     27      why: PropTypes.object,
     28      sourceTextContent: PropTypes.object,
     29    };
     30  }
     31 
     32  componentDidMount() {
     33    this.setDebugLine();
     34  }
     35 
     36  componentWillUnmount() {
     37    this.clearDebugLine(this.props);
     38  }
     39 
     40  componentDidUpdate(prevProps) {
     41    this.clearDebugLine(prevProps);
     42    this.setDebugLine();
     43  }
     44 
     45  setDebugLine() {
     46    const { why, location, editor, selectedSource } = this.props;
     47    if (!location) {
     48      return;
     49    }
     50 
     51    if (!selectedSource || location.source.id !== selectedSource.id) {
     52      return;
     53    }
     54 
     55    const { lineClass, markTextClass } = this.getTextClasses(why);
     56    const editorLocation = toEditorPosition(location);
     57 
     58    // Show the paused "caret", to highlight on which particular line **and column** we are paused.
     59    //
     60    // Using only a `positionClassName` wouldn't only be applied to the immediate
     61    // token after the position and force to use ::before to show the paused location.
     62    // Using ::before prevents using :hover to be able to hide the icon on mouse hovering.
     63    //
     64    // So we have to use `createPositionElementNode`, similarly to column breakpoints
     65    // to have a new dedicated DOM element for the paused location.
     66    editor.setPositionContentMarker({
     67      id: editor.markerTypes.PAUSED_LOCATION_MARKER,
     68 
     69      // Ensure displaying the marker after all the other markers and especially the column breakpoint markers
     70      displayLast: true,
     71 
     72      positions: [editorLocation],
     73      createPositionElementNode(_line, _column, isFirstNonSpaceColumn) {
     74        const pausedLocation = document.createElement("span");
     75        pausedLocation.className = `paused-location${isFirstNonSpaceColumn ? " first-column" : ""}`;
     76 
     77        const triangle = document.createElement("span");
     78        triangle.className = `triangle`;
     79        pausedLocation.appendChild(triangle);
     80 
     81        return pausedLocation;
     82      },
     83    });
     84 
     85    editor.setLineContentMarker({
     86      id: editor.markerTypes.DEBUG_LINE_MARKER,
     87      lineClassName: lineClass,
     88      lines: [{ line: editorLocation.line }],
     89    });
     90    editor.setPositionContentMarker({
     91      id: editor.markerTypes.DEBUG_POSITION_MARKER,
     92      positionClassName: markTextClass,
     93      positions: [editorLocation],
     94    });
     95  }
     96 
     97  clearDebugLine(otherProps = {}) {
     98    const { location, editor, selectedSource } = this.props;
     99    // Remove the debug line marker when no longer paused, or the selected source
    100    // is no longer the source where the pause occured.
    101    if (
    102      !location ||
    103      location.source.id !== selectedSource.id ||
    104      otherProps?.location !== location ||
    105      otherProps?.selectedSource?.id !== selectedSource.id
    106    ) {
    107      editor.removeLineContentMarker(editor.markerTypes.DEBUG_LINE_MARKER);
    108      editor.removePositionContentMarker(
    109        editor.markerTypes.DEBUG_POSITION_MARKER
    110      );
    111      editor.removePositionContentMarker(
    112        editor.markerTypes.PAUSED_LOCATION_MARKER
    113      );
    114    }
    115  }
    116 
    117  getTextClasses(why) {
    118    if (why && isException(why)) {
    119      return {
    120        markTextClass: "debug-expression-error",
    121        lineClass: "new-debug-line-error",
    122      };
    123    }
    124 
    125    // We no longer highlight the next token via debug-expression
    126    // and only highlight the line via paused-line.
    127    return {
    128      markTextClass: null,
    129      lineClass: why == "tracer" ? "traced-line" : "paused-line",
    130    };
    131  }
    132 
    133  render() {
    134    return null;
    135  }
    136 }
    137 
    138 function isDocumentReady(location, sourceTextContent) {
    139  return location && sourceTextContent;
    140 }
    141 
    142 const mapStateToProps = state => {
    143  // If we aren't paused, fallback on showing the JS tracer
    144  // currently selected trace location.
    145  // If any trace is selected in the JS Tracer, this takes the lead over
    146  // any paused location. (the same choice is made when showing inline previews)
    147  let why;
    148  let location = getSelectedTraceLocation(state);
    149  if (location) {
    150    why = "tracer";
    151  } else {
    152    // Avoid unecessary intermediate updates when there is no location
    153    // or the source text content isn't yet fully loaded
    154    const frame = getVisibleSelectedFrame(state);
    155    location = frame?.location;
    156 
    157    // We are not tracing, nor pausing
    158    if (!location) {
    159      return {};
    160    }
    161 
    162    why = getPauseReason(state, getCurrentThread(state));
    163  }
    164 
    165  // if we have a valid viewport.
    166  // This is a way to know if the actual source is displayed
    167  // and we are no longer on the "loading..." message
    168  if (!getViewport(state)) {
    169    return {};
    170  }
    171 
    172  const sourceTextContent = getSourceTextContentForLocation(state, location);
    173  if (!isDocumentReady(location, sourceTextContent)) {
    174    return {};
    175  }
    176 
    177  return {
    178    location,
    179    why,
    180    sourceTextContent,
    181  };
    182 };
    183 
    184 export default connect(mapStateToProps)(DebugLine);