tor-browser

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

RequestListColumnWaterfall.js (5395B)


      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 
      7 const {
      8  Component,
      9 } = require("resource://devtools/client/shared/vendor/react.mjs");
     10 const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
     11 const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs");
     12 
     13 const {
     14  L10N,
     15 } = require("resource://devtools/client/netmonitor/src/utils/l10n.js");
     16 const {
     17  fetchNetworkUpdatePacket,
     18  propertiesEqual,
     19 } = require("resource://devtools/client/netmonitor/src/utils/request-utils.js");
     20 
     21 // List of properties of the timing info we want to create boxes for
     22 const {
     23  TIMING_KEYS,
     24 } = require("resource://devtools/client/netmonitor/src/constants.js");
     25 
     26 const { div } = dom;
     27 
     28 const UPDATED_WATERFALL_ITEM_PROPS = ["eventTimings", "totalTime"];
     29 const UPDATED_WATERFALL_PROPS = [
     30  "item",
     31  "firstRequestStartedMs",
     32  "waterfallScale",
     33  "isVisible",
     34 ];
     35 
     36 module.exports = class RequestListColumnWaterfall extends Component {
     37  static get propTypes() {
     38    return {
     39      connector: PropTypes.object.isRequired,
     40      firstRequestStartedMs: PropTypes.number.isRequired,
     41      item: PropTypes.object.isRequired,
     42      onWaterfallMouseDown: PropTypes.func.isRequired,
     43      waterfallScale: PropTypes.number,
     44      isVisible: PropTypes.bool.isRequired,
     45    };
     46  }
     47 
     48  constructor() {
     49    super();
     50    this.handleMouseOver = this.handleMouseOver.bind(this);
     51  }
     52 
     53  componentDidMount() {
     54    const { connector, item } = this.props;
     55    fetchNetworkUpdatePacket(connector.requestData, item, ["eventTimings"]);
     56  }
     57 
     58  // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
     59  UNSAFE_componentWillReceiveProps(nextProps) {
     60    if (nextProps.isVisible && typeof nextProps.item.totalTime === "number") {
     61      const { connector, item } = nextProps;
     62      fetchNetworkUpdatePacket(connector.requestData, item, ["eventTimings"]);
     63    }
     64  }
     65 
     66  shouldComponentUpdate(nextProps) {
     67    return (
     68      nextProps.isVisible &&
     69      (!propertiesEqual(UPDATED_WATERFALL_PROPS, this.props, nextProps) ||
     70        !propertiesEqual(
     71          UPDATED_WATERFALL_ITEM_PROPS,
     72          this.props.item,
     73          nextProps.item
     74        ))
     75    );
     76  }
     77 
     78  handleMouseOver({ target }) {
     79    if (!target.title) {
     80      target.title = this.timingTooltip();
     81    }
     82  }
     83 
     84  timingTooltip() {
     85    const { eventTimings, totalTime } = this.props.item;
     86    const tooltip = [];
     87 
     88    if (eventTimings) {
     89      for (const key of TIMING_KEYS) {
     90        const width = eventTimings.timings[key];
     91 
     92        if (width > 0) {
     93          tooltip.push(
     94            L10N.getFormatStr("netmonitor.waterfall.tooltip." + key, width)
     95          );
     96        }
     97      }
     98    }
     99 
    100    if (typeof totalTime === "number") {
    101      tooltip.push(
    102        L10N.getFormatStr("netmonitor.waterfall.tooltip.total", totalTime)
    103      );
    104    }
    105 
    106    const lf = new Intl.ListFormat(undefined, { type: "unit" });
    107    return lf.format(tooltip);
    108  }
    109 
    110  timingBoxes() {
    111    const {
    112      waterfallScale,
    113      item: { eventTimings, totalTime },
    114    } = this.props;
    115    const boxes = [];
    116 
    117    // Physical pixel as minimum size
    118    const minPixel = 1 / window.devicePixelRatio;
    119 
    120    if (typeof totalTime === "number") {
    121      if (eventTimings) {
    122        // Add a set of boxes representing timing information.
    123        for (const key of TIMING_KEYS) {
    124          if (eventTimings.timings[key] > 0) {
    125            boxes.push(
    126              div({
    127                key,
    128                className: `requests-list-timings-box ${key}`,
    129                style: {
    130                  width: Math.max(
    131                    eventTimings.timings[key] * waterfallScale,
    132                    minPixel
    133                  ),
    134                },
    135              })
    136            );
    137          }
    138        }
    139      }
    140      // Minimal box to at least show start and total time
    141      if (!boxes.length) {
    142        boxes.push(
    143          div({
    144            className: "requests-list-timings-box filler",
    145            key: "filler",
    146            style: { width: Math.max(totalTime * waterfallScale, minPixel) },
    147          })
    148        );
    149      }
    150 
    151      const title = L10N.getFormatStr("networkMenu.totalMS2", totalTime);
    152      boxes.push(
    153        div(
    154          {
    155            key: "total",
    156            className: "requests-list-timings-total",
    157            title,
    158          },
    159          title
    160        )
    161      );
    162    } else {
    163      // Pending requests are marked for start time
    164      boxes.push(
    165        div({
    166          className: "requests-list-timings-box filler",
    167          key: "pending",
    168          style: { width: minPixel },
    169        })
    170      );
    171    }
    172 
    173    return boxes;
    174  }
    175 
    176  render() {
    177    const {
    178      firstRequestStartedMs,
    179      item: { startedMs },
    180      waterfallScale,
    181      onWaterfallMouseDown,
    182    } = this.props;
    183 
    184    return dom.td(
    185      {
    186        className: "requests-list-column requests-list-waterfall",
    187        onMouseOver: this.handleMouseOver,
    188      },
    189      div(
    190        {
    191          className: "requests-list-timings",
    192          style: {
    193            paddingInlineStart: `${
    194              (startedMs - firstRequestStartedMs) * waterfallScale
    195            }px`,
    196          },
    197          onMouseDown: onWaterfallMouseDown,
    198        },
    199        this.timingBoxes()
    200      )
    201    );
    202  }
    203 };