tor-browser

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

WhyPaused.js (7379B)


      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 {
      6  LocalizationProvider,
      7  Localized,
      8 } = require("resource://devtools/client/shared/vendor/fluent-react.js");
      9 
     10 import React, { PureComponent } from "devtools/client/shared/vendor/react";
     11 import { div, span } from "devtools/client/shared/vendor/react-dom-factories";
     12 import PropTypes from "devtools/client/shared/vendor/react-prop-types";
     13 import { connect } from "devtools/client/shared/vendor/react-redux";
     14 import DebuggerImage from "../shared/DebuggerImage";
     15 import actions from "../../actions/index";
     16 
     17 const Reps = ChromeUtils.importESModule(
     18  "resource://devtools/client/shared/components/reps/index.mjs"
     19 );
     20 const {
     21  REPS: { Rep },
     22  MODE,
     23 } = Reps;
     24 
     25 import { getPauseReason } from "../../utils/pause/index";
     26 import {
     27  getCurrentThread,
     28  getPauseCommand,
     29  getPaneCollapse,
     30  getPauseReason as getWhy,
     31  getVisibleSelectedFrame,
     32 } from "../../selectors/index";
     33 
     34 const classnames = require("resource://devtools/client/shared/classnames.js");
     35 
     36 class WhyPaused extends PureComponent {
     37  constructor(props) {
     38    super(props);
     39    this.state = { hideWhyPaused: true };
     40  }
     41 
     42  static get propTypes() {
     43    return {
     44      delay: PropTypes.number.isRequired,
     45      endPanelCollapsed: PropTypes.bool.isRequired,
     46      highlightDomElement: PropTypes.func.isRequired,
     47      openElementInInspector: PropTypes.func.isRequired,
     48      unHighlightDomElement: PropTypes.func.isRequired,
     49      why: PropTypes.object,
     50    };
     51  }
     52 
     53  componentDidUpdate() {
     54    const { delay } = this.props;
     55 
     56    if (delay) {
     57      setTimeout(() => {
     58        this.setState({ hideWhyPaused: true });
     59      }, delay);
     60    } else {
     61      this.setState({ hideWhyPaused: false });
     62    }
     63  }
     64 
     65  renderExceptionSummary(exception) {
     66    if (typeof exception === "string") {
     67      return exception;
     68    }
     69 
     70    const { preview } = exception;
     71    if (!preview || !preview.name || !preview.message) {
     72      return null;
     73    }
     74 
     75    return `${preview.name}: ${preview.message}`;
     76  }
     77 
     78  renderMessage(why) {
     79    const { type, exception, message } = why;
     80 
     81    if (type == "exception" && exception) {
     82      // Our types for 'Why' are too general because 'type' can be 'string'.
     83      // $FlowFixMe - We should have a proper discriminating union of reasons.
     84      const summary = this.renderExceptionSummary(exception);
     85      return div(
     86        {
     87          className: "message error",
     88        },
     89        summary
     90      );
     91    }
     92 
     93    if (type === "mutationBreakpoint" && why.nodeGrip) {
     94      const { nodeGrip, ancestorGrip, action } = why;
     95      const {
     96        openElementInInspector,
     97        highlightDomElement,
     98        unHighlightDomElement,
     99      } = this.props;
    100 
    101      const targetRep = Rep({
    102        object: nodeGrip,
    103        mode: MODE.TINY,
    104        onDOMNodeClick: () => openElementInInspector(nodeGrip),
    105        onInspectIconClick: () => openElementInInspector(nodeGrip),
    106        onDOMNodeMouseOver: () => highlightDomElement(nodeGrip),
    107        onDOMNodeMouseOut: () => unHighlightDomElement(),
    108      });
    109 
    110      const ancestorRep = ancestorGrip
    111        ? Rep({
    112            object: ancestorGrip,
    113            mode: MODE.TINY,
    114            onDOMNodeClick: () => openElementInInspector(ancestorGrip),
    115            onInspectIconClick: () => openElementInInspector(ancestorGrip),
    116            onDOMNodeMouseOver: () => highlightDomElement(ancestorGrip),
    117            onDOMNodeMouseOut: () => unHighlightDomElement(),
    118          })
    119        : null;
    120      return div(
    121        null,
    122        div(
    123          {
    124            className: "message",
    125          },
    126          why.message
    127        ),
    128        div(
    129          {
    130            className: "mutationNode",
    131          },
    132          ancestorRep,
    133          ancestorGrip
    134            ? span(
    135                {
    136                  className: "why-paused-ancestor",
    137                },
    138                React.createElement(Localized, {
    139                  id:
    140                    action === "remove"
    141                      ? "whypaused-mutation-breakpoint-removed"
    142                      : "whypaused-mutation-breakpoint-added",
    143                }),
    144                targetRep
    145              )
    146            : targetRep
    147        )
    148      );
    149    }
    150 
    151    if (typeof message == "string") {
    152      return div(
    153        {
    154          className: "message",
    155        },
    156        message
    157      );
    158    }
    159 
    160    return null;
    161  }
    162 
    163  renderLocation() {
    164    const { visibleSelectedFrame } = this.props;
    165    if (!visibleSelectedFrame || !visibleSelectedFrame.location?.source) {
    166      return null;
    167    }
    168    const { location, displayName } = visibleSelectedFrame;
    169    let pauseLocation = "";
    170    if (visibleSelectedFrame.displayName) {
    171      pauseLocation += `${displayName} - `;
    172    }
    173    pauseLocation += `${location.source.displayURL?.filename}:${location.line}:${location.column}`;
    174    return div({ className: "location" }, pauseLocation);
    175  }
    176 
    177  render() {
    178    const { endPanelCollapsed, why } = this.props;
    179    const { fluentBundles } = this.context;
    180    const reason = getPauseReason(why);
    181 
    182    let content = "";
    183    if (!why || !reason) {
    184      if (this.state.hideWhyPaused) {
    185        content = null;
    186      }
    187    } else {
    188      content = div(
    189        null,
    190        div(
    191          {
    192            className: "info icon",
    193          },
    194          React.createElement(DebuggerImage, {
    195            name: "info",
    196          })
    197        ),
    198        div(
    199          {
    200            className: "pause reason",
    201          },
    202          div(
    203            {},
    204            React.createElement(Localized, {
    205              id: reason,
    206            })
    207          ),
    208          this.renderLocation(),
    209          this.renderMessage(why)
    210        )
    211      );
    212    }
    213 
    214    return (
    215      // We're rendering the LocalizationProvider component from here and not in an upper
    216      // component because it does set a new context, overriding the context that we set
    217      // in the first place in <App>, which breaks some components.
    218      // This should be fixed in Bug 1743155.
    219      React.createElement(
    220        LocalizationProvider,
    221        {
    222          bundles: fluentBundles || [],
    223        },
    224        // Always render the component so the live region works as expected
    225        div(
    226          {
    227            className: classnames("pane why-paused", {
    228              hidden: content == null || endPanelCollapsed,
    229            }),
    230            "aria-live": "polite",
    231          },
    232          content
    233        )
    234      )
    235    );
    236  }
    237 }
    238 
    239 WhyPaused.contextTypes = { fluentBundles: PropTypes.array };
    240 
    241 // Checks if user is in debugging mode and adds a delay preventing
    242 // excessive vertical 'jumpiness'
    243 function getDelay(state, thread) {
    244  const inPauseCommand = !!getPauseCommand(state, thread);
    245 
    246  if (!inPauseCommand) {
    247    return 100;
    248  }
    249 
    250  return 0;
    251 }
    252 
    253 const mapStateToProps = state => {
    254  const thread = getCurrentThread(state);
    255 
    256  return {
    257    delay: getDelay(state, thread),
    258    endPanelCollapsed: getPaneCollapse(state, "end"),
    259    why: getWhy(state, thread),
    260    visibleSelectedFrame: getVisibleSelectedFrame(state),
    261  };
    262 };
    263 
    264 export default connect(mapStateToProps, {
    265  openElementInInspector: actions.openElementInInspectorCommand,
    266  highlightDomElement: actions.highlightDomElement,
    267  unHighlightDomElement: actions.unHighlightDomElement,
    268 })(WhyPaused);