tor-browser

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

TimingsPanel.js (8220B)


      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  connect,
      9 } = require("resource://devtools/client/shared/vendor/react-redux.js");
     10 const {
     11  Component,
     12 } = require("resource://devtools/client/shared/vendor/react.mjs");
     13 const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs");
     14 const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
     15 const {
     16  L10N,
     17 } = require("resource://devtools/client/netmonitor/src/utils/l10n.js");
     18 const {
     19  getNetMonitorTimingsURL,
     20 } = require("resource://devtools/client/netmonitor/src/utils/doc-utils.js");
     21 const {
     22  fetchNetworkUpdatePacket,
     23 } = require("resource://devtools/client/netmonitor/src/utils/request-utils.js");
     24 const {
     25  getFormattedTime,
     26 } = require("resource://devtools/client/netmonitor/src/utils/format-utils.js");
     27 const {
     28  TIMING_KEYS,
     29 } = require("resource://devtools/client/netmonitor/src/constants.js");
     30 
     31 // Components
     32 const MDNLink = require("resource://devtools/client/shared/components/MdnLink.js");
     33 
     34 const { div, span } = dom;
     35 
     36 /**
     37 * Timings panel component
     38 * Display timeline bars that shows the total wait time for various stages
     39 */
     40 class TimingsPanel extends Component {
     41  static get propTypes() {
     42    return {
     43      connector: PropTypes.object.isRequired,
     44      request: PropTypes.object.isRequired,
     45      firstRequestStartedMs: PropTypes.number,
     46    };
     47  }
     48 
     49  componentDidMount() {
     50    const { connector, request } = this.props;
     51    fetchNetworkUpdatePacket(connector.requestData, request, ["eventTimings"]);
     52  }
     53 
     54  // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
     55  UNSAFE_componentWillReceiveProps(nextProps) {
     56    const { connector, request } = nextProps;
     57    fetchNetworkUpdatePacket(connector.requestData, request, ["eventTimings"]);
     58  }
     59 
     60  renderServiceWorkerTimings() {
     61    const { serviceWorkerTimings } = this.props.request.eventTimings;
     62 
     63    if (!serviceWorkerTimings) {
     64      return null;
     65    }
     66 
     67    const totalTime = Object.values(serviceWorkerTimings).reduce(
     68      (acc, value) => acc + value,
     69      0
     70    );
     71 
     72    let offset = 0;
     73    let preValue = 0;
     74 
     75    return div(
     76      {},
     77      div(
     78        { className: "label-separator" },
     79        L10N.getStr("netmonitor.timings.serviceWorkerTiming")
     80      ),
     81      Object.entries(serviceWorkerTimings).map(([key, value]) => {
     82        if (preValue > 0) {
     83          offset += preValue / totalTime;
     84        }
     85        preValue = value;
     86        return div(
     87          {
     88            key,
     89            className:
     90              "tabpanel-summary-container timings-container service-worker",
     91          },
     92          span(
     93            { className: "tabpanel-summary-label timings-label" },
     94            L10N.getStr(`netmonitor.timings.${key}`)
     95          ),
     96          div(
     97            { className: "requests-list-timings-container" },
     98            span({
     99              className: `requests-list-timings-box serviceworker-timings-color-${key.replace(
    100                "ServiceWorker",
    101                ""
    102              )}`,
    103              style: {
    104                "--current-timing-offset": offset > 0 ? offset : 0,
    105                "--current-timing-width": value / totalTime,
    106              },
    107            }),
    108            span(
    109              { className: "requests-list-timings-total" },
    110              getFormattedTime(value)
    111            )
    112          )
    113        );
    114      })
    115    );
    116  }
    117 
    118  renderServerTimings() {
    119    const { serverTimings, totalTime } = this.props.request.eventTimings;
    120 
    121    if (!serverTimings?.length) {
    122      return null;
    123    }
    124 
    125    return div(
    126      {},
    127      div(
    128        { className: "label-separator" },
    129        L10N.getStr("netmonitor.timings.serverTiming")
    130      ),
    131      ...serverTimings.map(({ name, duration, description }, index) => {
    132        const color = name === "total" ? "total" : (index % 3) + 1;
    133 
    134        return div(
    135          {
    136            key: index,
    137            className: "tabpanel-summary-container timings-container server",
    138          },
    139          span(
    140            { className: "tabpanel-summary-label timings-label" },
    141            description || name
    142          ),
    143          div(
    144            { className: "requests-list-timings-container" },
    145            span({
    146              className: `requests-list-timings-box server-timings-color-${color}`,
    147              style: {
    148                "--current-timing-offset": (totalTime - duration) / totalTime,
    149                "--current-timing-width": duration / totalTime,
    150              },
    151            }),
    152            span(
    153              { className: "requests-list-timings-total" },
    154              getFormattedTime(duration)
    155            )
    156          )
    157        );
    158      })
    159    );
    160  }
    161 
    162  render() {
    163    const { eventTimings, totalTime, startedMs } = this.props.request;
    164    const { firstRequestStartedMs } = this.props;
    165 
    166    if (!eventTimings) {
    167      return div(
    168        {
    169          className:
    170            "tabpanel-summary-container timings-container empty-notice",
    171        },
    172        L10N.getStr("netmonitor.timings.noTimings")
    173      );
    174    }
    175 
    176    const { timings, offsets } = eventTimings;
    177    let queuedAt, startedAt, downloadedAt;
    178    const isFirstRequestStartedAvailable = firstRequestStartedMs !== null;
    179 
    180    if (isFirstRequestStartedAvailable) {
    181      queuedAt = startedMs - firstRequestStartedMs;
    182      startedAt = queuedAt + timings.blocked;
    183      downloadedAt = queuedAt + totalTime;
    184    }
    185 
    186    const timelines = TIMING_KEYS.map((type, idx) => {
    187      // Determine the relative offset for each timings box. For example, the
    188      // offset of third timings box will be 0 + blocked offset + dns offset
    189      // If offsets sent from the backend aren't available calculate it
    190      // from the timing info.
    191      const offset = offsets
    192        ? offsets[type]
    193        : TIMING_KEYS.slice(0, idx).reduce(
    194            (acc, cur) => acc + timings[cur] || 0,
    195            0
    196          );
    197 
    198      const offsetScale = offset / totalTime || 0;
    199      const timelineScale = timings[type] / totalTime || 0;
    200 
    201      return div(
    202        {
    203          key: type,
    204          id: `timings-summary-${type}`,
    205          className: "tabpanel-summary-container timings-container request",
    206        },
    207        span(
    208          { className: "tabpanel-summary-label timings-label" },
    209          L10N.getStr(`netmonitor.timings.${type}`)
    210        ),
    211        div(
    212          { className: "requests-list-timings-container" },
    213          span({
    214            className: `requests-list-timings-box ${type}`,
    215            style: {
    216              "--current-timing-offset": offsetScale,
    217              "--current-timing-width": timelineScale,
    218            },
    219          }),
    220          span(
    221            { className: "requests-list-timings-total" },
    222            getFormattedTime(timings[type])
    223          )
    224        )
    225      );
    226    });
    227 
    228    return div(
    229      { className: "panel-container" },
    230      isFirstRequestStartedAvailable &&
    231        div(
    232          { className: "timings-overview" },
    233          span(
    234            { className: "timings-overview-item" },
    235            L10N.getFormatStr(
    236              "netmonitor.timings.queuedAt",
    237              getFormattedTime(queuedAt)
    238            )
    239          ),
    240          span(
    241            { className: "timings-overview-item" },
    242            L10N.getFormatStr(
    243              "netmonitor.timings.startedAt",
    244              getFormattedTime(startedAt)
    245            )
    246          ),
    247          span(
    248            { className: "timings-overview-item" },
    249            L10N.getFormatStr(
    250              "netmonitor.timings.downloadedAt",
    251              getFormattedTime(downloadedAt)
    252            )
    253          )
    254        ),
    255      div(
    256        { className: "label-separator" },
    257        L10N.getStr("netmonitor.timings.requestTiming")
    258      ),
    259      timelines,
    260      this.renderServiceWorkerTimings(),
    261      this.renderServerTimings(),
    262      MDNLink({
    263        url: getNetMonitorTimingsURL(),
    264        title: L10N.getStr("netmonitor.timings.learnMore"),
    265      })
    266    );
    267  }
    268 }
    269 
    270 module.exports = connect(state => ({
    271  firstRequestStartedMs: state.requests ? state.requests.firstStartedMs : null,
    272 }))(TimingsPanel);