tor-browser

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

Toolbar.js (22141B)


      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  connect,
     15 } = require("resource://devtools/client/shared/vendor/react-redux.js");
     16 const Actions = require("resource://devtools/client/netmonitor/src/actions/index.js");
     17 const {
     18  FILTER_SEARCH_DELAY,
     19  FILTER_TAGS,
     20  PANELS,
     21 } = require("resource://devtools/client/netmonitor/src/constants.js");
     22 const {
     23  getDisplayedRequests,
     24  getRecordingState,
     25  getTypeFilteredRequests,
     26  getSelectedRequest,
     27 } = require("resource://devtools/client/netmonitor/src/selectors/index.js");
     28 const {
     29  autocompleteProvider,
     30 } = require("resource://devtools/client/netmonitor/src/utils/filter-autocomplete-provider.js");
     31 const {
     32  L10N,
     33 } = require("resource://devtools/client/netmonitor/src/utils/l10n.js");
     34 const {
     35  fetchNetworkUpdatePacket,
     36 } = require("resource://devtools/client/netmonitor/src/utils/request-utils.js");
     37 
     38 loader.lazyRequireGetter(
     39  this,
     40  "KeyShortcuts",
     41  "resource://devtools/client/shared/key-shortcuts.js"
     42 );
     43 
     44 // MDN
     45 const {
     46  getFilterBoxURL,
     47 } = require("resource://devtools/client/netmonitor/src/utils/doc-utils.js");
     48 const LEARN_MORE_URL = getFilterBoxURL();
     49 
     50 // Components
     51 const NetworkThrottlingMenu = createFactory(
     52  require("resource://devtools/client/shared/components/throttling/NetworkThrottlingMenu.js")
     53 );
     54 const SearchBox = createFactory(
     55  require("resource://devtools/client/shared/components/SearchBox.js")
     56 );
     57 
     58 const { button, div, input, label, span, hr } = dom;
     59 
     60 // Localization
     61 const FILTER_KEY_SHORTCUT = L10N.getStr(
     62  "netmonitor.toolbar.filterFreetext.key"
     63 );
     64 const SEARCH_KEY_SHORTCUT = L10N.getStr("netmonitor.toolbar.search.key");
     65 const SEARCH_PLACE_HOLDER = L10N.getStr(
     66  "netmonitor.toolbar.filterFreetext.label"
     67 );
     68 const COPY_KEY_SHORTCUT = L10N.getStr("netmonitor.toolbar.copy.key");
     69 const TOOLBAR_CLEAR = L10N.getStr("netmonitor.toolbar.clear");
     70 const TOOLBAR_TOGGLE_RECORDING = L10N.getStr(
     71  "netmonitor.toolbar.toggleRecording"
     72 );
     73 const TOOLBAR_HTTP_CUSTOM_REQUEST = L10N.getStr(
     74  "netmonitor.toolbar.HTTPCustomRequest"
     75 );
     76 const TOOLBAR_SEARCH = L10N.getStr("netmonitor.toolbar.search");
     77 const TOOLBAR_BLOCKING = L10N.getStr("netmonitor.toolbar.requestBlocking");
     78 const LEARN_MORE_TITLE = L10N.getStr(
     79  "netmonitor.toolbar.filterFreetext.learnMore"
     80 );
     81 
     82 // Preferences
     83 const DEVTOOLS_DISABLE_CACHE_PREF = "devtools.cache.disabled";
     84 const DEVTOOLS_ENABLE_PERSISTENT_LOG_PREF = "devtools.netmonitor.persistlog";
     85 const TOOLBAR_FILTER_LABELS = FILTER_TAGS.concat("all").reduce(
     86  (o, tag) =>
     87    Object.assign(o, {
     88      [tag]: L10N.getStr(`netmonitor.toolbar.filter.${tag}`),
     89    }),
     90  {}
     91 );
     92 const DISABLE_CACHE_TOOLTIP = L10N.getStr(
     93  "netmonitor.toolbar.disableCache.tooltip"
     94 );
     95 const DISABLE_CACHE_LABEL = L10N.getStr(
     96  "netmonitor.toolbar.disableCache.label"
     97 );
     98 
     99 const MenuButton = createFactory(
    100  require("resource://devtools/client/shared/components/menu/MenuButton.js")
    101 );
    102 
    103 loader.lazyGetter(this, "MenuItem", function () {
    104  return createFactory(
    105    require("resource://devtools/client/shared/components/menu/MenuItem.js")
    106  );
    107 });
    108 
    109 loader.lazyGetter(this, "MenuList", function () {
    110  return createFactory(
    111    require("resource://devtools/client/shared/components/menu/MenuList.js")
    112  );
    113 });
    114 
    115 // Menu
    116 loader.lazyRequireGetter(
    117  this,
    118  "HarMenuUtils",
    119  "resource://devtools/client/netmonitor/src/har/har-menu-utils.js",
    120  true
    121 );
    122 loader.lazyRequireGetter(
    123  this,
    124  "copyString",
    125  "resource://devtools/shared/platform/clipboard.js",
    126  true
    127 );
    128 
    129 // Throttling
    130 const Types = require("resource://devtools/client/shared/components/throttling/types.js");
    131 const {
    132  changeNetworkThrottling,
    133 } = require("resource://devtools/client/shared/components/throttling/actions.js");
    134 
    135 /**
    136 * Network monitor toolbar component.
    137 *
    138 * Toolbar contains a set of useful tools to control network requests
    139 * as well as set of filters for filtering the content.
    140 */
    141 class Toolbar extends Component {
    142  static get propTypes() {
    143    return {
    144      actions: PropTypes.object.isRequired,
    145      connector: PropTypes.object.isRequired,
    146      toggleRecording: PropTypes.func.isRequired,
    147      recording: PropTypes.bool.isRequired,
    148      clearRequests: PropTypes.func.isRequired,
    149      // List of currently displayed requests (i.e. filtered & sorted).
    150      displayedRequests: PropTypes.array.isRequired,
    151      requestFilterTypes: PropTypes.object.isRequired,
    152      setRequestFilterText: PropTypes.func.isRequired,
    153      enablePersistentLogs: PropTypes.func.isRequired,
    154      togglePersistentLogs: PropTypes.func.isRequired,
    155      persistentLogsEnabled: PropTypes.bool.isRequired,
    156      disableBrowserCache: PropTypes.func.isRequired,
    157      toggleBrowserCache: PropTypes.func.isRequired,
    158      browserCacheDisabled: PropTypes.bool.isRequired,
    159      toggleRequestFilterType: PropTypes.func.isRequired,
    160      filteredRequests: PropTypes.array.isRequired,
    161      // Set to true if there is enough horizontal space
    162      // and the toolbar needs just one row.
    163      singleRow: PropTypes.bool.isRequired,
    164      // Callback for opening split console.
    165      openSplitConsole: PropTypes.func,
    166      networkThrottling: PropTypes.shape(Types.networkThrottling).isRequired,
    167      // Executed when throttling changes (through toolbar button).
    168      onChangeNetworkThrottling: PropTypes.func.isRequired,
    169      toggleSearchPanel: PropTypes.func.isRequired,
    170      toggleHTTPCustomRequestPanel: PropTypes.func.isRequired,
    171      networkActionBarOpen: PropTypes.bool,
    172      toggleRequestBlockingPanel: PropTypes.func.isRequired,
    173      networkActionBarSelectedPanel: PropTypes.string.isRequired,
    174      hasBlockedRequests: PropTypes.bool.isRequired,
    175      selectedRequest: PropTypes.object,
    176      toolboxDoc: PropTypes.object.isRequired,
    177    };
    178  }
    179 
    180  constructor(props) {
    181    super(props);
    182 
    183    this.autocompleteProvider = this.autocompleteProvider.bind(this);
    184    this.onSearchBoxFocusKeyboardShortcut =
    185      this.onSearchBoxFocusKeyboardShortcut.bind(this);
    186    this.onSearchBoxFocus = this.onSearchBoxFocus.bind(this);
    187    this.toggleRequestFilterType = this.toggleRequestFilterType.bind(this);
    188    this.updatePersistentLogsEnabled =
    189      this.updatePersistentLogsEnabled.bind(this);
    190    this.updateBrowserCacheDisabled =
    191      this.updateBrowserCacheDisabled.bind(this);
    192  }
    193 
    194  componentDidMount() {
    195    Services.prefs.addObserver(
    196      DEVTOOLS_ENABLE_PERSISTENT_LOG_PREF,
    197      this.updatePersistentLogsEnabled
    198    );
    199    Services.prefs.addObserver(
    200      DEVTOOLS_DISABLE_CACHE_PREF,
    201      this.updateBrowserCacheDisabled
    202    );
    203 
    204    this.shortcuts = new KeyShortcuts({
    205      window,
    206    });
    207 
    208    this.shortcuts.on(SEARCH_KEY_SHORTCUT, event => {
    209      event.preventDefault();
    210      this.props.toggleSearchPanel();
    211    });
    212 
    213    // Keyboard shortcut to copy the selected request URL
    214    this.shortcuts.on(COPY_KEY_SHORTCUT, e => {
    215      if (!this.props.selectedRequest?.url) {
    216        return;
    217      }
    218 
    219      const selection = window.getSelection();
    220      if (
    221        // We don't want to copy selected URL in clipboard if the user selected some text…
    222        (!selection.isCollapsed && selection.toString()) ||
    223        // …or if the keyboard shortcut happened in some inputs (which includes
    224        // CodeMirror 5 underlying textarea)
    225        e.target.matches("input, textarea")
    226      ) {
    227        return;
    228      }
    229 
    230      copyString(this.props.selectedRequest.url);
    231    });
    232  }
    233 
    234  shouldComponentUpdate(nextProps) {
    235    return (
    236      this.props.persistentLogsEnabled !== nextProps.persistentLogsEnabled ||
    237      this.props.browserCacheDisabled !== nextProps.browserCacheDisabled ||
    238      this.props.recording !== nextProps.recording ||
    239      this.props.networkActionBarOpen !== nextProps.networkActionBarOpen ||
    240      this.props.singleRow !== nextProps.singleRow ||
    241      !Object.is(this.props.requestFilterTypes, nextProps.requestFilterTypes) ||
    242      this.props.networkThrottling !== nextProps.networkThrottling ||
    243      // Filtered requests are useful only when searchbox is focused
    244      !!(this.refs.searchbox && this.refs.searchbox.focused) ||
    245      this.props.networkActionBarSelectedPanel !==
    246        nextProps.networkActionBarSelectedPanel ||
    247      this.props.hasBlockedRequests !== nextProps.hasBlockedRequests
    248    );
    249  }
    250 
    251  componentWillUnmount() {
    252    Services.prefs.removeObserver(
    253      DEVTOOLS_ENABLE_PERSISTENT_LOG_PREF,
    254      this.updatePersistentLogsEnabled
    255    );
    256    Services.prefs.removeObserver(
    257      DEVTOOLS_DISABLE_CACHE_PREF,
    258      this.updateBrowserCacheDisabled
    259    );
    260 
    261    if (this.shortcuts) {
    262      this.shortcuts.destroy();
    263    }
    264  }
    265 
    266  toggleRequestFilterType(evt) {
    267    if (evt.type === "keydown" && (evt.key !== "" || evt.key !== "Enter")) {
    268      return;
    269    }
    270    this.props.toggleRequestFilterType(evt.target.dataset.key);
    271  }
    272 
    273  updatePersistentLogsEnabled() {
    274    // Make sure the UI is updated when the pref changes.
    275    // It might happen when the user changed it through about:config or
    276    // through another Toolbox instance (opened in another browser tab).
    277    // In such case, skip telemetry recordings.
    278    this.props.enablePersistentLogs(
    279      Services.prefs.getBoolPref(DEVTOOLS_ENABLE_PERSISTENT_LOG_PREF),
    280      true
    281    );
    282  }
    283 
    284  updateBrowserCacheDisabled() {
    285    this.props.disableBrowserCache(
    286      Services.prefs.getBoolPref(DEVTOOLS_DISABLE_CACHE_PREF)
    287    );
    288  }
    289 
    290  autocompleteProvider(filter) {
    291    return autocompleteProvider(filter, this.props.filteredRequests);
    292  }
    293 
    294  onSearchBoxFocusKeyboardShortcut(event) {
    295    // Don't take focus when the keyboard shortcut is triggered in a CodeMirror instance,
    296    // so the CodeMirror search UI is displayed.
    297    return !!event.target.closest(".cm-editor");
    298  }
    299 
    300  onSearchBoxFocus() {
    301    const { connector, filteredRequests } = this.props;
    302 
    303    // Fetch responseCookies & responseHeaders for building autocomplete list
    304    filteredRequests.forEach(request => {
    305      fetchNetworkUpdatePacket(connector.requestData, request, [
    306        "responseCookies",
    307        "responseHeaders",
    308      ]);
    309    });
    310  }
    311 
    312  /**
    313   * Render a separator.
    314   */
    315  renderSeparator() {
    316    return span({ className: "devtools-separator" });
    317  }
    318 
    319  /**
    320   * Render a clear button.
    321   */
    322  renderClearButton(clearRequests) {
    323    return button({
    324      className:
    325        "devtools-button devtools-clear-icon requests-list-clear-button",
    326      title: TOOLBAR_CLEAR,
    327      onClick: clearRequests,
    328    });
    329  }
    330 
    331  /**
    332   * Render a ToggleRecording button.
    333   */
    334  renderToggleRecordingButton(recording, toggleRecording) {
    335    return button({
    336      className: "devtools-button requests-list-pause-button",
    337      title: TOOLBAR_TOGGLE_RECORDING,
    338      "aria-pressed": !recording,
    339      onClick: toggleRecording,
    340    });
    341  }
    342 
    343  /**
    344   * Render a blocking button.
    345   */
    346  renderBlockingButton() {
    347    const {
    348      networkActionBarOpen,
    349      toggleRequestBlockingPanel,
    350      networkActionBarSelectedPanel,
    351      hasBlockedRequests,
    352    } = this.props;
    353 
    354    // The blocking feature is available behind a pref.
    355    if (
    356      !Services.prefs.getBoolPref(
    357        "devtools.netmonitor.features.requestBlocking"
    358      )
    359    ) {
    360      return null;
    361    }
    362 
    363    const className = ["devtools-button", "requests-list-blocking-button"];
    364    if (hasBlockedRequests) {
    365      className.push("requests-list-blocking-button-enabled");
    366    }
    367 
    368    return button({
    369      className: className.join(" "),
    370      title: TOOLBAR_BLOCKING,
    371      "aria-pressed":
    372        networkActionBarOpen &&
    373        networkActionBarSelectedPanel === PANELS.BLOCKING,
    374      onClick: toggleRequestBlockingPanel,
    375    });
    376  }
    377 
    378  /**
    379   * Render a search button.
    380   */
    381  renderSearchButton(toggleSearchPanel) {
    382    const { networkActionBarOpen, networkActionBarSelectedPanel } = this.props;
    383 
    384    return button({
    385      className: "devtools-button devtools-search-icon",
    386      title: TOOLBAR_SEARCH,
    387      "aria-pressed":
    388        networkActionBarOpen && networkActionBarSelectedPanel === PANELS.SEARCH,
    389      onClick: toggleSearchPanel,
    390    });
    391  }
    392 
    393  /**
    394   * Render a new HTTP Custom Request button.
    395   */
    396  renderHTTPCustomRequestButton() {
    397    const {
    398      networkActionBarOpen,
    399      networkActionBarSelectedPanel,
    400      toggleHTTPCustomRequestPanel,
    401    } = this.props;
    402 
    403    // The new HTTP Custom Request feature is available behind a pref.
    404    if (
    405      !Services.prefs.getBoolPref(
    406        "devtools.netmonitor.features.newEditAndResend"
    407      )
    408    ) {
    409      return null;
    410    }
    411 
    412    return button({
    413      className: "devtools-button devtools-http-custom-request-icon",
    414      title: TOOLBAR_HTTP_CUSTOM_REQUEST,
    415      "aria-pressed":
    416        networkActionBarOpen &&
    417        networkActionBarSelectedPanel === PANELS.HTTP_CUSTOM_REQUEST,
    418      onClick: toggleHTTPCustomRequestPanel,
    419    });
    420  }
    421 
    422  /**
    423   * Render filter buttons.
    424   */
    425  renderFilterButtons(requestFilterTypes) {
    426    // Render list of filter-buttons.
    427    const buttons = Object.entries(requestFilterTypes).map(([type, checked]) =>
    428      button(
    429        {
    430          className: `devtools-togglebutton requests-list-filter-${type}-button`,
    431          key: type,
    432          onClick: this.toggleRequestFilterType,
    433          onKeyDown: this.toggleRequestFilterType,
    434          "aria-pressed": checked,
    435          "data-key": type,
    436        },
    437        TOOLBAR_FILTER_LABELS[type]
    438      )
    439    );
    440    return div({ className: "requests-list-filter-buttons" }, buttons);
    441  }
    442 
    443  /**
    444   * Render a Cache checkbox.
    445   */
    446  renderCacheCheckbox(browserCacheDisabled, toggleBrowserCache) {
    447    return label(
    448      {
    449        className: "devtools-checkbox-label devtools-cache-checkbox",
    450        title: DISABLE_CACHE_TOOLTIP,
    451      },
    452      input({
    453        id: "devtools-cache-checkbox",
    454        className: "devtools-checkbox",
    455        type: "checkbox",
    456        checked: browserCacheDisabled,
    457        onChange: toggleBrowserCache,
    458      }),
    459      DISABLE_CACHE_LABEL
    460    );
    461  }
    462 
    463  /**
    464   * Render network throttling menu button.
    465   */
    466  renderThrottlingMenu() {
    467    const { networkThrottling, onChangeNetworkThrottling, toolboxDoc } =
    468      this.props;
    469 
    470    return NetworkThrottlingMenu({
    471      networkThrottling,
    472      onChangeNetworkThrottling,
    473      toolboxDoc,
    474    });
    475  }
    476 
    477  /**
    478   * Render filter Searchbox.
    479   */
    480  renderFilterBox(setRequestFilterText) {
    481    return SearchBox({
    482      delay: FILTER_SEARCH_DELAY,
    483      keyShortcut: FILTER_KEY_SHORTCUT,
    484      placeholder: SEARCH_PLACE_HOLDER,
    485      type: "filter",
    486      ref: "searchbox",
    487      initialValue: Services.prefs.getCharPref(
    488        "devtools.netmonitor.requestfilter"
    489      ),
    490      onChange: setRequestFilterText,
    491      onFocusKeyboardShortcut: this.onSearchBoxFocusKeyboardShortcut,
    492      onFocus: this.onSearchBoxFocus,
    493      autocompleteProvider: this.autocompleteProvider,
    494      learnMoreUrl: LEARN_MORE_URL,
    495      learnMoreTitle: LEARN_MORE_TITLE,
    496    });
    497  }
    498 
    499  renderSettingsMenuButton() {
    500    const { toolboxDoc } = this.props;
    501    return MenuButton(
    502      {
    503        menuId: "netmonitor-settings-menu-button",
    504        toolboxDoc,
    505        className: "devtools-button netmonitor-settings-menu-button",
    506        title: L10N.getStr("netmonitor.settings.menuTooltip"),
    507      },
    508      // We pass the children in a function so we don't require the MenuItem and MenuList
    509      // components until we need to display them (i.e. when the button is clicked).
    510      () => this.renderSettingsMenuItems()
    511    );
    512  }
    513 
    514  renderSettingsMenuItems() {
    515    const {
    516      actions,
    517      connector,
    518      displayedRequests,
    519      openSplitConsole,
    520      persistentLogsEnabled,
    521      togglePersistentLogs,
    522    } = this.props;
    523 
    524    const menuItems = [
    525      MenuItem({
    526        key: "netmonitor-settings-persist-item",
    527        className: "menu-item netmonitor-settings-persist-item",
    528        type: "checkbox",
    529        checked: persistentLogsEnabled,
    530        label: L10N.getStr("netmonitor.toolbar.enablePersistentLogs.label"),
    531        tooltip: L10N.getStr("netmonitor.toolbar.enablePersistentLogs.tooltip"),
    532        onClick: () => togglePersistentLogs(),
    533      }),
    534      hr({ key: "netmonitor-settings-har-divider" }),
    535      MenuItem({
    536        key: "request-list-context-import-har",
    537        className: "menu-item netmonitor-settings-import-har-item",
    538        label: L10N.getStr("netmonitor.har.importHarDialogTitle"),
    539        tooltip: L10N.getStr("netmonitor.settings.importHarTooltip"),
    540        accesskey: L10N.getStr("netmonitor.context.importHar.accesskey"),
    541        onClick: () => HarMenuUtils.openHarFile(actions, openSplitConsole),
    542      }),
    543      MenuItem({
    544        key: "request-list-context-save-all-as-har",
    545        className: "menu-item netmonitor-settings-save-har-item",
    546        label: L10N.getStr("netmonitor.context.saveAllAsHar"),
    547        accesskey: L10N.getStr("netmonitor.context.saveAllAsHar.accesskey"),
    548        tooltip: L10N.getStr("netmonitor.settings.saveHarTooltip"),
    549        disabled: !displayedRequests.length,
    550        onClick: () => HarMenuUtils.saveAllAsHar(displayedRequests, connector),
    551      }),
    552      MenuItem({
    553        key: "request-list-context-copy-all-as-har",
    554        className: "menu-item netmonitor-settings-copy-har-item",
    555        label: L10N.getStr("netmonitor.context.copyAllAsHar"),
    556        accesskey: L10N.getStr("netmonitor.context.copyAllAsHar.accesskey"),
    557        tooltip: L10N.getStr("netmonitor.settings.copyHarTooltip"),
    558        disabled: !displayedRequests.length,
    559        onClick: () => HarMenuUtils.copyAllAsHar(displayedRequests, connector),
    560      }),
    561    ];
    562 
    563    return MenuList({ id: "netmonitor-settings-menu-list" }, menuItems);
    564  }
    565 
    566  render() {
    567    const {
    568      toggleRecording,
    569      clearRequests,
    570      requestFilterTypes,
    571      setRequestFilterText,
    572      toggleBrowserCache,
    573      browserCacheDisabled,
    574      recording,
    575      singleRow,
    576      toggleSearchPanel,
    577    } = this.props;
    578 
    579    // Render the entire toolbar.
    580    // dock at bottom or dock at side has different layout
    581    return singleRow
    582      ? span(
    583          { id: "netmonitor-toolbar-container" },
    584          span(
    585            { className: "devtools-toolbar devtools-input-toolbar" },
    586            this.renderClearButton(clearRequests),
    587            this.renderSeparator(),
    588            this.renderFilterBox(setRequestFilterText),
    589            this.renderSeparator(),
    590            this.renderToggleRecordingButton(recording, toggleRecording),
    591            this.renderHTTPCustomRequestButton(),
    592            this.renderSearchButton(toggleSearchPanel),
    593            this.renderBlockingButton(toggleSearchPanel),
    594            this.renderSeparator(),
    595            this.renderFilterButtons(requestFilterTypes),
    596            this.renderSeparator(),
    597            this.renderCacheCheckbox(browserCacheDisabled, toggleBrowserCache),
    598            this.renderSeparator(),
    599            this.renderThrottlingMenu(),
    600            this.renderSeparator(),
    601            this.renderSettingsMenuButton()
    602          )
    603        )
    604      : span(
    605          { id: "netmonitor-toolbar-container" },
    606          span(
    607            { className: "devtools-toolbar devtools-input-toolbar" },
    608            this.renderClearButton(clearRequests),
    609            this.renderSeparator(),
    610            this.renderFilterBox(setRequestFilterText),
    611            this.renderSeparator(),
    612            this.renderToggleRecordingButton(recording, toggleRecording),
    613            this.renderHTTPCustomRequestButton(),
    614            this.renderSearchButton(toggleSearchPanel),
    615            this.renderBlockingButton(toggleSearchPanel),
    616            this.renderSeparator(),
    617            this.renderCacheCheckbox(browserCacheDisabled, toggleBrowserCache),
    618            this.renderSeparator(),
    619            this.renderThrottlingMenu(),
    620            this.renderSeparator(),
    621            this.renderSettingsMenuButton()
    622          ),
    623          span(
    624            { className: "devtools-toolbar devtools-input-toolbar" },
    625            this.renderFilterButtons(requestFilterTypes)
    626          )
    627        );
    628  }
    629 }
    630 
    631 module.exports = connect(
    632  state => ({
    633    browserCacheDisabled: state.ui.browserCacheDisabled,
    634    displayedRequests: getDisplayedRequests(state),
    635    hasBlockedRequests:
    636      state.requestBlocking.blockingEnabled &&
    637      state.requestBlocking.blockedUrls.some(({ enabled }) => enabled),
    638    filteredRequests: getTypeFilteredRequests(state),
    639    persistentLogsEnabled: state.ui.persistentLogsEnabled,
    640    recording: getRecordingState(state),
    641    requestFilterTypes: state.filters.requestFilterTypes,
    642    networkThrottling: state.networkThrottling,
    643    networkActionBarOpen: state.ui.networkActionOpen,
    644    networkActionBarSelectedPanel: state.ui.selectedActionBarTabId || "",
    645    selectedRequest: getSelectedRequest(state),
    646  }),
    647  dispatch => ({
    648    clearRequests: () =>
    649      dispatch(Actions.clearRequests({ isExplicitClear: true })),
    650    disableBrowserCache: disabled =>
    651      dispatch(Actions.disableBrowserCache(disabled)),
    652    enablePersistentLogs: (enabled, skipTelemetry) =>
    653      dispatch(Actions.enablePersistentLogs(enabled, skipTelemetry)),
    654    setRequestFilterText: text => dispatch(Actions.setRequestFilterText(text)),
    655    toggleBrowserCache: () => dispatch(Actions.toggleBrowserCache()),
    656    toggleRecording: () => dispatch(Actions.toggleRecording()),
    657    togglePersistentLogs: () => dispatch(Actions.togglePersistentLogs()),
    658    toggleRequestFilterType: type =>
    659      dispatch(Actions.toggleRequestFilterType(type)),
    660    onChangeNetworkThrottling: (enabled, profile) =>
    661      dispatch(changeNetworkThrottling(enabled, profile)),
    662    toggleHTTPCustomRequestPanel: () =>
    663      dispatch(Actions.toggleHTTPCustomRequestPanel()),
    664    toggleSearchPanel: () => dispatch(Actions.toggleSearchPanel()),
    665    toggleRequestBlockingPanel: () =>
    666      dispatch(Actions.toggleRequestBlockingPanel()),
    667  })
    668 )(Toolbar);