tor-browser

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

RequestListItem.js (14019B)


      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  createFactory,
     10 } = require("resource://devtools/client/shared/vendor/react.mjs");
     11 const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
     12 const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs");
     13 const {
     14  fetchNetworkUpdatePacket,
     15  propertiesEqual,
     16 } = require("resource://devtools/client/netmonitor/src/utils/request-utils.js");
     17 const {
     18  PANELS,
     19  RESPONSE_HEADERS,
     20 } = require("resource://devtools/client/netmonitor/src/constants.js");
     21 
     22 // Components
     23 /* global
     24  RequestListColumnInitiator,
     25  RequestListColumnContentSize,
     26  RequestListColumnCookies,
     27  RequestListColumnDomain,
     28  RequestListColumnFile,
     29  RequestListColumnPath,
     30  RequestListColumnMethod,
     31  RequestListColumnProtocol,
     32  RequestListColumnRemoteIP,
     33  RequestListColumnResponseHeader,
     34  RequestListColumnScheme,
     35  RequestListColumnSetCookies,
     36  RequestListColumnStatus,
     37  RequestListColumnTime,
     38  RequestListColumnTransferredSize,
     39  RequestListColumnType,
     40  RequestListColumnUrl,
     41  RequestListColumnWaterfall,
     42  RequestListColumnPriority
     43 */
     44 loader.lazyGetter(this, "RequestListColumnInitiator", function () {
     45  return createFactory(
     46    require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnInitiator.js")
     47  );
     48 });
     49 loader.lazyGetter(this, "RequestListColumnContentSize", function () {
     50  return createFactory(
     51    require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnContentSize.js")
     52  );
     53 });
     54 loader.lazyGetter(this, "RequestListColumnCookies", function () {
     55  return createFactory(
     56    require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnCookies.js")
     57  );
     58 });
     59 loader.lazyGetter(this, "RequestListColumnDomain", function () {
     60  return createFactory(
     61    require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnDomain.js")
     62  );
     63 });
     64 loader.lazyGetter(this, "RequestListColumnFile", function () {
     65  return createFactory(
     66    require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnFile.js")
     67  );
     68 });
     69 loader.lazyGetter(this, "RequestListColumnPath", function () {
     70  return createFactory(
     71    require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnPath.js")
     72  );
     73 });
     74 loader.lazyGetter(this, "RequestListColumnUrl", function () {
     75  return createFactory(
     76    require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnUrl.js")
     77  );
     78 });
     79 loader.lazyGetter(this, "RequestListColumnMethod", function () {
     80  return createFactory(
     81    require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnMethod.js")
     82  );
     83 });
     84 loader.lazyGetter(this, "RequestListColumnOverride", function () {
     85  return createFactory(
     86    require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnOverride.js")
     87  );
     88 });
     89 loader.lazyGetter(this, "RequestListColumnProtocol", function () {
     90  return createFactory(
     91    require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnProtocol.js")
     92  );
     93 });
     94 loader.lazyGetter(this, "RequestListColumnRemoteIP", function () {
     95  return createFactory(
     96    require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnRemoteIP.js")
     97  );
     98 });
     99 loader.lazyGetter(this, "RequestListColumnResponseHeader", function () {
    100  return createFactory(
    101    require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnResponseHeader.js")
    102  );
    103 });
    104 loader.lazyGetter(this, "RequestListColumnTime", function () {
    105  return createFactory(
    106    require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnTime.js")
    107  );
    108 });
    109 loader.lazyGetter(this, "RequestListColumnScheme", function () {
    110  return createFactory(
    111    require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnScheme.js")
    112  );
    113 });
    114 loader.lazyGetter(this, "RequestListColumnSetCookies", function () {
    115  return createFactory(
    116    require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnSetCookies.js")
    117  );
    118 });
    119 loader.lazyGetter(this, "RequestListColumnStatus", function () {
    120  return createFactory(
    121    require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnStatus.js")
    122  );
    123 });
    124 loader.lazyGetter(this, "RequestListColumnTransferredSize", function () {
    125  return createFactory(
    126    require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnTransferredSize.js")
    127  );
    128 });
    129 loader.lazyGetter(this, "RequestListColumnType", function () {
    130  return createFactory(
    131    require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnType.js")
    132  );
    133 });
    134 loader.lazyGetter(this, "RequestListColumnWaterfall", function () {
    135  return createFactory(
    136    require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnWaterfall.js")
    137  );
    138 });
    139 loader.lazyGetter(this, "RequestListColumnPriority", function () {
    140  return createFactory(
    141    require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnPriority.js")
    142  );
    143 });
    144 
    145 /**
    146 * Used by shouldComponentUpdate: compare two items, and compare only properties
    147 * relevant for rendering the RequestListItem. Other properties (like request and
    148 * response headers, cookies, bodies) are ignored. These are very useful for the
    149 * network details, but not here.
    150 */
    151 const UPDATED_REQ_ITEM_PROPS = [
    152  "mimeType",
    153  "eventTimings",
    154  "earlyHintsStatus",
    155  "securityState",
    156  "status",
    157  "statusText",
    158  "fromCache",
    159  "isRacing",
    160  "fromServiceWorker",
    161  "method",
    162  "url",
    163  "remoteAddress",
    164  "cause",
    165  "contentSize",
    166  "transferredSize",
    167  "startedMs",
    168  "totalTime",
    169  "requestCookies",
    170  "requestHeaders",
    171  "responseCookies",
    172  "responseHeaders",
    173  "waitingTime",
    174  "isEventStream",
    175  "priority",
    176  "blockedReason",
    177  "extension",
    178 ];
    179 
    180 const UPDATED_REQ_PROPS = [
    181  "firstRequestStartedMs",
    182  "index",
    183  "networkDetailsOpen",
    184  "isSelected",
    185  "isVisible",
    186  "requestFilterTypes",
    187  "waterfallScale",
    188 ];
    189 
    190 /**
    191 * Used by render: renders the given ColumnComponent if the flag for this column
    192 * is set in the columns prop. The list of props are used to determine which of
    193 * RequestListItem's need to be passed to the ColumnComponent. Any objects contained
    194 * in that list are passed as props verbatim.
    195 */
    196 const COLUMN_COMPONENTS = [
    197  { column: "override", ColumnComponent: RequestListColumnOverride },
    198  { column: "status", ColumnComponent: RequestListColumnStatus },
    199  { column: "method", ColumnComponent: RequestListColumnMethod },
    200  {
    201    column: "domain",
    202    ColumnComponent: RequestListColumnDomain,
    203    props: ["onSecurityIconMouseDown"],
    204  },
    205  {
    206    column: "file",
    207    ColumnComponent: RequestListColumnFile,
    208    props: ["onWaterfallMouseDown", "slowLimit"],
    209  },
    210  {
    211    column: "path",
    212    ColumnComponent: RequestListColumnPath,
    213    props: ["onWaterfallMouseDown"],
    214  },
    215  {
    216    column: "url",
    217    ColumnComponent: RequestListColumnUrl,
    218    props: ["onSecurityIconMouseDown"],
    219  },
    220  { column: "protocol", ColumnComponent: RequestListColumnProtocol },
    221  { column: "scheme", ColumnComponent: RequestListColumnScheme },
    222  { column: "remoteip", ColumnComponent: RequestListColumnRemoteIP },
    223  {
    224    column: "initiator",
    225    ColumnComponent: RequestListColumnInitiator,
    226    props: ["onInitiatorBadgeMouseDown"],
    227  },
    228  { column: "type", ColumnComponent: RequestListColumnType },
    229  {
    230    column: "cookies",
    231    ColumnComponent: RequestListColumnCookies,
    232    props: ["connector"],
    233  },
    234  {
    235    column: "setCookies",
    236    ColumnComponent: RequestListColumnSetCookies,
    237    props: ["connector"],
    238  },
    239  { column: "transferred", ColumnComponent: RequestListColumnTransferredSize },
    240  { column: "contentSize", ColumnComponent: RequestListColumnContentSize },
    241  { column: "priority", ColumnComponent: RequestListColumnPriority },
    242  {
    243    column: "startTime",
    244    ColumnComponent: RequestListColumnTime,
    245    props: ["connector", "firstRequestStartedMs", { type: "start" }],
    246  },
    247  {
    248    column: "endTime",
    249    ColumnComponent: RequestListColumnTime,
    250    props: ["connector", "firstRequestStartedMs", { type: "end" }],
    251  },
    252  {
    253    column: "responseTime",
    254    ColumnComponent: RequestListColumnTime,
    255    props: ["connector", "firstRequestStartedMs", { type: "response" }],
    256  },
    257  {
    258    column: "duration",
    259    ColumnComponent: RequestListColumnTime,
    260    props: ["connector", "firstRequestStartedMs", { type: "duration" }],
    261  },
    262  {
    263    column: "latency",
    264    ColumnComponent: RequestListColumnTime,
    265    props: ["connector", "firstRequestStartedMs", { type: "latency" }],
    266  },
    267 ];
    268 
    269 /**
    270 * Render one row in the request list.
    271 */
    272 class RequestListItem extends Component {
    273  static get propTypes() {
    274    return {
    275      blocked: PropTypes.bool,
    276      columns: PropTypes.object.isRequired,
    277      connector: PropTypes.object.isRequired,
    278      firstRequestStartedMs: PropTypes.number.isRequired,
    279      fromCache: PropTypes.bool,
    280      item: PropTypes.object.isRequired,
    281      index: PropTypes.number.isRequired,
    282      intersectionObserver: PropTypes.object,
    283      isSelected: PropTypes.bool.isRequired,
    284      isVisible: PropTypes.bool.isRequired,
    285      networkActionOpen: PropTypes.bool,
    286      networkDetailsOpen: PropTypes.bool,
    287      onContextMenu: PropTypes.func.isRequired,
    288      onDoubleClick: PropTypes.func.isRequired,
    289      onDragStart: PropTypes.func.isRequired,
    290      onFocusedNodeChange: PropTypes.func,
    291      onInitiatorBadgeMouseDown: PropTypes.func.isRequired,
    292      onMouseDown: PropTypes.func.isRequired,
    293      onSecurityIconMouseDown: PropTypes.func.isRequired,
    294      onWaterfallMouseDown: PropTypes.func.isRequired,
    295      overriddenUrl: PropTypes.string,
    296      requestFilterTypes: PropTypes.object.isRequired,
    297      selectedActionBarTabId: PropTypes.string,
    298      waterfallScale: PropTypes.number,
    299    };
    300  }
    301 
    302  componentDidMount() {
    303    if (this.props.isSelected) {
    304      this.refs.listItem.focus();
    305    }
    306    if (this.props.intersectionObserver) {
    307      this.props.intersectionObserver.observe(this.refs.listItem);
    308    }
    309 
    310    const { connector, item, requestFilterTypes } = this.props;
    311    // Filtering XHR & WS require to lazily fetch requestHeaders & responseHeaders
    312    if (requestFilterTypes.xhr || requestFilterTypes.ws) {
    313      fetchNetworkUpdatePacket(connector.requestData, item, [
    314        "requestHeaders",
    315        "responseHeaders",
    316      ]);
    317    }
    318  }
    319 
    320  // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
    321  UNSAFE_componentWillReceiveProps(nextProps) {
    322    const { connector, item, requestFilterTypes } = nextProps;
    323    // Filtering XHR & WS require to lazily fetch requestHeaders & responseHeaders
    324    if (requestFilterTypes.xhr || requestFilterTypes.ws) {
    325      fetchNetworkUpdatePacket(connector.requestData, item, [
    326        "requestHeaders",
    327        "responseHeaders",
    328      ]);
    329    }
    330  }
    331 
    332  shouldComponentUpdate(nextProps) {
    333    return (
    334      !propertiesEqual(
    335        UPDATED_REQ_ITEM_PROPS,
    336        this.props.item,
    337        nextProps.item
    338      ) ||
    339      !propertiesEqual(UPDATED_REQ_PROPS, this.props, nextProps) ||
    340      this.props.columns !== nextProps.columns ||
    341      nextProps.overriddenUrl !== this.props.overriddenUrl
    342    );
    343  }
    344 
    345  componentDidUpdate(prevProps) {
    346    if (!prevProps.isSelected && this.props.isSelected) {
    347      this.refs.listItem.focus();
    348      if (this.props.onFocusedNodeChange) {
    349        this.props.onFocusedNodeChange();
    350      }
    351    }
    352  }
    353 
    354  componentWillUnmount() {
    355    if (this.props.intersectionObserver) {
    356      this.props.intersectionObserver.unobserve(this.refs.listItem);
    357    }
    358  }
    359 
    360  render() {
    361    const {
    362      blocked,
    363      connector,
    364      columns,
    365      item,
    366      index,
    367      isSelected,
    368      isVisible,
    369      firstRequestStartedMs,
    370      fromCache,
    371      networkActionOpen,
    372      onDoubleClick,
    373      onDragStart,
    374      onContextMenu,
    375      onMouseDown,
    376      onWaterfallMouseDown,
    377      selectedActionBarTabId,
    378      waterfallScale,
    379    } = this.props;
    380 
    381    const classList = ["request-list-item", index % 2 ? "odd" : "even"];
    382    isSelected && classList.push("selected");
    383    fromCache && classList.push("fromCache");
    384    blocked && classList.push("blocked");
    385 
    386    return dom.tr(
    387      {
    388        ref: "listItem",
    389        className: classList.join(" "),
    390        "data-id": item.id,
    391        draggable:
    392          !blocked &&
    393          networkActionOpen &&
    394          selectedActionBarTabId === PANELS.BLOCKING,
    395        tabIndex: 0,
    396        onContextMenu,
    397        onMouseDown,
    398        onDoubleClick,
    399        onDragStart,
    400      },
    401      ...COLUMN_COMPONENTS.filter(({ column }) => columns[column]).map(
    402        ({ column, ColumnComponent, props: columnProps }) => {
    403          return ColumnComponent({
    404            key: column,
    405            item,
    406            ...(columnProps || []).reduce((acc, keyOrObject) => {
    407              if (typeof keyOrObject == "string") {
    408                acc[keyOrObject] = this.props[keyOrObject];
    409              } else {
    410                Object.assign(acc, keyOrObject);
    411              }
    412              return acc;
    413            }, {}),
    414          });
    415        }
    416      ),
    417      ...RESPONSE_HEADERS.filter(header => columns[header]).map(header =>
    418        RequestListColumnResponseHeader({
    419          connector,
    420          item,
    421          header,
    422        })
    423      ),
    424      // The last column is Waterfall (aka Timeline)
    425      columns.waterfall &&
    426        RequestListColumnWaterfall({
    427          connector,
    428          firstRequestStartedMs,
    429          item,
    430          onWaterfallMouseDown,
    431          isVisible,
    432          waterfallScale,
    433        })
    434    );
    435  }
    436 }
    437 
    438 module.exports = RequestListItem;