tor-browser

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

CookiesPanel.js (6350B)


      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 PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs");
     12 const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
     13 const {
     14  L10N,
     15 } = require("resource://devtools/client/netmonitor/src/utils/l10n.js");
     16 const {
     17  fetchNetworkUpdatePacket,
     18 } = require("resource://devtools/client/netmonitor/src/utils/request-utils.js");
     19 const {
     20  sortObjectKeys,
     21 } = require("resource://devtools/client/netmonitor/src/utils/sort-utils.js");
     22 const {
     23  FILTER_SEARCH_DELAY,
     24 } = require("resource://devtools/client/netmonitor/src/constants.js");
     25 
     26 // Component
     27 const PropertiesView = createFactory(
     28  require("resource://devtools/client/netmonitor/src/components/request-details/PropertiesView.js")
     29 );
     30 const SearchBox = createFactory(
     31  require("resource://devtools/client/shared/components/SearchBox.js")
     32 );
     33 const Accordion = createFactory(
     34  require("resource://devtools/client/shared/components/Accordion.js")
     35 );
     36 
     37 loader.lazyGetter(this, "TreeRow", function () {
     38  return createFactory(
     39    ChromeUtils.importESModule(
     40      "resource://devtools/client/shared/components/tree/TreeRow.mjs",
     41      { global: "current" }
     42    ).default
     43  );
     44 });
     45 
     46 const { div } = dom;
     47 
     48 const COOKIES_EMPTY_TEXT = L10N.getStr("cookiesEmptyText");
     49 const COOKIES_FILTER_TEXT = L10N.getStr("cookiesFilterText");
     50 const REQUEST_COOKIES = L10N.getStr("requestCookies");
     51 const RESPONSE_COOKIES = L10N.getStr("responseCookies");
     52 
     53 /*
     54 * Cookies panel component
     55 * This tab lists full details of any cookies sent with the request or response
     56 */
     57 class CookiesPanel extends Component {
     58  static get propTypes() {
     59    return {
     60      connector: PropTypes.object.isRequired,
     61      openLink: PropTypes.func,
     62      request: PropTypes.object.isRequired,
     63      targetSearchResult: PropTypes.object,
     64    };
     65  }
     66 
     67  constructor(props) {
     68    super(props);
     69    this.state = {
     70      filterText: "",
     71    };
     72  }
     73 
     74  componentDidMount() {
     75    const { connector, request } = this.props;
     76    fetchNetworkUpdatePacket(connector.requestData, request, [
     77      "requestCookies",
     78      "responseCookies",
     79    ]);
     80  }
     81 
     82  // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
     83  UNSAFE_componentWillReceiveProps(nextProps) {
     84    const { connector, request } = nextProps;
     85    fetchNetworkUpdatePacket(connector.requestData, request, [
     86      "requestCookies",
     87      "responseCookies",
     88    ]);
     89  }
     90 
     91  /**
     92   * Mapping array to dict for TreeView usage.
     93   * Since TreeView only support Object(dict) format.
     94   *
     95   * @param {object[]} arr - key-value pair array like cookies or params
     96   * @returns {object}
     97   */
     98  getProperties(arr, title) {
     99    const cookies = arr.reduce((map, obj) => {
    100      // Generally cookies object contains only name and value properties and can
    101      // be rendered as name: value pair.
    102      // When there are more properties in cookies object such as extra or path,
    103      // We will pass the object to display these extra information
    104      if (Object.keys(obj).length > 2) {
    105        map[obj.name] = Object.assign({}, obj);
    106        delete map[obj.name].name;
    107      } else {
    108        map[obj.name] = obj.value;
    109      }
    110      return map;
    111    }, Object.create(null));
    112 
    113    // To have different roots for Request and Response cookies
    114    return { [title]: cookies };
    115  }
    116 
    117  /**
    118   * Custom rendering method passed to PropertiesView. It's
    119   * responsible to filter out level 0 node in the tree
    120   *
    121   * @param {object} props
    122   */
    123  renderRow(props) {
    124    const { level } = props.member;
    125 
    126    if (level === 0) {
    127      return null;
    128    }
    129 
    130    return TreeRow(props);
    131  }
    132 
    133  /**
    134   * Get the selected cookies path
    135   *
    136   * @param {object} searchResult
    137   * @returns {string}
    138   */
    139  getTargetCookiePath(searchResult) {
    140    if (!searchResult) {
    141      return null;
    142    }
    143 
    144    switch (searchResult.type) {
    145      case "requestCookies": {
    146        return `/${REQUEST_COOKIES}/${searchResult.label}`;
    147      }
    148      case "responseCookies":
    149        return `/${RESPONSE_COOKIES}/${searchResult.label}`;
    150    }
    151 
    152    return null;
    153  }
    154 
    155  render() {
    156    let {
    157      request: {
    158        requestCookies = { cookies: [] },
    159        responseCookies = { cookies: [] },
    160      },
    161      targetSearchResult,
    162    } = this.props;
    163 
    164    const { filterText } = this.state;
    165 
    166    requestCookies = requestCookies.cookies || requestCookies;
    167    responseCookies = responseCookies.cookies || responseCookies;
    168 
    169    if (!requestCookies.length && !responseCookies.length) {
    170      return div({ className: "empty-notice" }, COOKIES_EMPTY_TEXT);
    171    }
    172 
    173    const items = [];
    174 
    175    if (responseCookies.length) {
    176      items.push({
    177        component: PropertiesView,
    178        componentProps: {
    179          object: sortObjectKeys(
    180            this.getProperties(responseCookies, RESPONSE_COOKIES)
    181          ),
    182          filterText,
    183          targetSearchResult,
    184          defaultSelectFirstNode: false,
    185          selectPath: this.getTargetCookiePath,
    186          renderRow: this.renderRow,
    187        },
    188        header: RESPONSE_COOKIES,
    189        id: "responseCookies",
    190        opened: true,
    191      });
    192    }
    193 
    194    if (requestCookies.length) {
    195      items.push({
    196        component: PropertiesView,
    197        componentProps: {
    198          object: sortObjectKeys(
    199            this.getProperties(requestCookies, REQUEST_COOKIES)
    200          ),
    201          filterText,
    202          targetSearchResult,
    203          defaultSelectFirstNode: false,
    204          selectPath: this.getTargetCookiePath,
    205          renderRow: this.renderRow,
    206        },
    207        header: REQUEST_COOKIES,
    208        id: "requestCookies",
    209        opened: true,
    210      });
    211    }
    212 
    213    return div(
    214      { className: "panel-container cookies-panel-container" },
    215      div(
    216        { className: "devtools-toolbar devtools-input-toolbar" },
    217        SearchBox({
    218          delay: FILTER_SEARCH_DELAY,
    219          type: "filter",
    220          onChange: text => this.setState({ filterText: text }),
    221          placeholder: COOKIES_FILTER_TEXT,
    222        })
    223      ),
    224      Accordion({ items })
    225    );
    226  }
    227 }
    228 
    229 module.exports = CookiesPanel;