tor-browser

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

SourcePreview.js (4954B)


      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 "use strict";
      6 const {
      7  Component,
      8 } = require("resource://devtools/client/shared/vendor/react.mjs");
      9 const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs");
     10 const {
     11  connect,
     12 } = require("resource://devtools/client/shared/vendor/react-redux.js");
     13 const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
     14 const Editor = require("resource://devtools/client/shared/sourceeditor/editor.js");
     15 const {
     16  setTargetSearchResult,
     17 } = require("resource://devtools/client/netmonitor/src/actions/search.js");
     18 const { div } = dom;
     19 /**
     20 * CodeMirror editor as a React component
     21 */
     22 class SourcePreview extends Component {
     23  static get propTypes() {
     24    return {
     25      // Source editor syntax highlight mimeType, which is a mime type defined in CodeMirror
     26      mimeType: PropTypes.string,
     27      // Source editor content
     28      text: PropTypes.string,
     29      // Search result text to select
     30      targetSearchResult: PropTypes.object,
     31      // Reset target search result that has been used for navigation in this panel.
     32      // This is done to avoid second navigation the next time.
     33      resetTargetSearchResult: PropTypes.func,
     34      url: PropTypes.string,
     35    };
     36  }
     37 
     38  componentDidMount() {
     39    this.loadEditor();
     40    this.updateEditor();
     41  }
     42 
     43  shouldComponentUpdate(nextProps) {
     44    return (
     45      nextProps.mimeType !== this.props.mimeType ||
     46      nextProps.text !== this.props.text ||
     47      nextProps.targetSearchResult !== this.props.targetSearchResult
     48    );
     49  }
     50 
     51  componentDidUpdate(prevProps) {
     52    const { targetSearchResult, text } = this.props;
     53    if (prevProps.text !== text) {
     54      // When updating from editor to editor
     55      this.updateEditor();
     56    } else if (prevProps.targetSearchResult !== targetSearchResult) {
     57      this.findSearchResult();
     58    }
     59  }
     60 
     61  componentWillUnmount() {
     62    this.unloadEditor();
     63  }
     64 
     65  getSourceEditorModeForMimetype(mimeType) {
     66    const lang = mimeType.split("/")[1];
     67    return Editor.modes[lang];
     68  }
     69 
     70  loadEditor() {
     71    this.editor = new Editor({
     72      cm6: true,
     73      lineNumbers: true,
     74      lineWrapping: false,
     75      disableSearchAddon: false,
     76      useSearchAddonPanel: true,
     77      mode: null, // Disable auto syntax detection, but then we set the mode later
     78      readOnly: true,
     79      theme: "mozilla",
     80      value: "",
     81    });
     82 
     83    this.editor.appendToLocalElement(this.refs.editorElement);
     84    // Used for netmonitor tests
     85    window.codeMirrorSourceEditorTestInstance = this.editor;
     86  }
     87 
     88  async updateEditor() {
     89    const { mimeType, text, url } = this.props;
     90    if (this?.editor?.hasCodeMirror) {
     91      const mode = this.getSourceEditorModeForMimetype(mimeType);
     92      await this.editor.setMode(mode);
     93      await this.editor.setText(text, { documentId: url });
     94      // When navigating from the netmonitor search, find and highlight the
     95      // the current search result.
     96      await this.findSearchResult();
     97    }
     98  }
     99 
    100  unloadEditor() {
    101    if (this.editor) {
    102      this.editor.destroy();
    103      this.editor = null;
    104    }
    105  }
    106 
    107  async findSearchResult() {
    108    const { targetSearchResult, resetTargetSearchResult } = this.props;
    109    if (targetSearchResult?.line) {
    110      const { line } = targetSearchResult;
    111      // scroll the editor to center the line
    112      // with the target search result
    113      if (this.editor) {
    114        await this.editor.setCursorAt(line, 0);
    115 
    116        // Highlight line
    117        this.editor.setLineContentMarker({
    118          id: this.editor.markerTypes.HIGHLIGHT_LINE_MARKER,
    119          lineClassName: "highlight-line",
    120          lines: [{ line }],
    121        });
    122        this.clearHighlightLineAfterDuration();
    123      }
    124    }
    125 
    126    resetTargetSearchResult();
    127  }
    128 
    129  clearHighlightLineAfterDuration() {
    130    const editorContainer = document.querySelector(".editor-row-container");
    131 
    132    if (editorContainer === null) {
    133      return;
    134    }
    135 
    136    const duration = parseInt(
    137      getComputedStyle(editorContainer).getPropertyValue(
    138        "--highlight-line-duration"
    139      ),
    140      10
    141    );
    142 
    143    const highlightTimeout = setTimeout(() => {
    144      if (!this.editor) {
    145        return;
    146      }
    147      clearTimeout(highlightTimeout);
    148      this.editor.removeLineContentMarker("highlight-line-marker");
    149    }, duration);
    150  }
    151 
    152  render() {
    153    return div(
    154      { className: "editor-row-container" },
    155      div({
    156        ref: "editorElement",
    157        className: "source-editor-mount devtools-monospace",
    158      })
    159    );
    160  }
    161 }
    162 
    163 module.exports = connect(
    164  state => {
    165    if (!state.search) {
    166      return null;
    167    }
    168    return {
    169      targetSearchResult: state.search.targetSearchResult,
    170    };
    171  },
    172  dispatch => ({
    173    resetTargetSearchResult: () => dispatch(setTargetSearchResult(null)),
    174  })
    175 )(SourcePreview);