tor-browser

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

api.js (5825B)


      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 EventEmitter = require("resource://devtools/shared/event-emitter.js");
      8 
      9 const {
     10  bindActionCreators,
     11 } = require("resource://devtools/client/shared/vendor/redux.js");
     12 const {
     13  Connector,
     14 } = require("resource://devtools/client/netmonitor/src/connector/index.js");
     15 const {
     16  configureStore,
     17 } = require("resource://devtools/client/netmonitor/src/create-store.js");
     18 const {
     19  EVENTS,
     20 } = require("resource://devtools/client/netmonitor/src/constants.js");
     21 const Actions = require("resource://devtools/client/netmonitor/src/actions/index.js");
     22 
     23 const {
     24  getDisplayedRequestById,
     25  getSortedRequests,
     26 } = require("resource://devtools/client/netmonitor/src/selectors/index.js");
     27 
     28 /**
     29 * API object for NetMonitor panel (like a facade). This object can be
     30 * consumed by other panels, WebExtension API, etc.
     31 *
     32 * This object doesn't depend on the panel UI and can be created
     33 * and used even if the Network panel UI doesn't exist.
     34 */
     35 class NetMonitorAPI extends EventEmitter {
     36  constructor() {
     37    super();
     38 
     39    // Connector to the backend.
     40    this.connector = new Connector();
     41 
     42    // List of listeners for `devtools.network.onRequestFinished` WebExt API
     43    this._requestFinishedListeners = new Set();
     44 
     45    // Bind event handlers
     46    this.onPayloadReady = this.onPayloadReady.bind(this);
     47  }
     48  async connect(toolbox) {
     49    // Bail out if already connected.
     50    if (this.toolbox) {
     51      return;
     52    }
     53 
     54    this.toolbox = toolbox;
     55 
     56    // Configure store/state object.
     57    this.store = configureStore(
     58      this.connector,
     59      this.toolbox.commands,
     60      this.toolbox.telemetry
     61    );
     62    this.actions = bindActionCreators(Actions, this.store.dispatch);
     63 
     64    // Register listener for new requests (utilized by WebExtension API).
     65    this.on(EVENTS.PAYLOAD_READY, this.onPayloadReady);
     66 
     67    // Initialize connection to the backend. Pass `this` as the owner,
     68    // so this object can receive all emitted events.
     69    const connection = {
     70      toolbox,
     71      owner: this,
     72    };
     73 
     74    await this.connector.connect(connection, this.actions, this.store.getState);
     75  }
     76 
     77  /**
     78   * Clean up (unmount from DOM, remove listeners, disconnect).
     79   */
     80  destroy() {
     81    this.off(EVENTS.PAYLOAD_READY, this.onPayloadReady);
     82 
     83    this.connector.disconnect();
     84 
     85    if (this.harExportConnector) {
     86      this.harExportConnector.disconnect();
     87    }
     88  }
     89 
     90  // HAR
     91 
     92  /**
     93   * Support for `devtools.network.getHAR` (get collected data as HAR)
     94   */
     95  async getHar() {
     96    const {
     97      HarExporter,
     98    } = require("resource://devtools/client/netmonitor/src/har/har-exporter.js");
     99    const state = this.store.getState();
    100 
    101    const options = {
    102      connector: this.connector,
    103      items: getSortedRequests(state),
    104    };
    105 
    106    return HarExporter.getHar(options);
    107  }
    108 
    109  /**
    110   * Support for `devtools.network.onRequestFinished`. A hook for
    111   * every finished HTTP request used by WebExtensions API.
    112   */
    113  async onPayloadReady(resource) {
    114    if (!this._requestFinishedListeners.size) {
    115      return;
    116    }
    117 
    118    const {
    119      HarExporter,
    120    } = require("resource://devtools/client/netmonitor/src/har/har-exporter.js");
    121 
    122    const connector = await this.getHarExportConnector();
    123    const request = getDisplayedRequestById(
    124      this.store.getState(),
    125      resource.actor
    126    );
    127    if (!request) {
    128      console.error("HAR: request not found " + resource.actor);
    129      return;
    130    }
    131 
    132    const options = {
    133      connector,
    134      includeResponseBodies: false,
    135      items: [request],
    136    };
    137 
    138    const har = await HarExporter.getHar(options);
    139 
    140    // There is page so remove the page reference.
    141    const harEntry = har.log.entries[0];
    142    delete harEntry.pageref;
    143 
    144    this._requestFinishedListeners.forEach(listener =>
    145      listener({
    146        harEntry,
    147        requestId: resource.actor,
    148      })
    149    );
    150  }
    151 
    152  /**
    153   * Support for `Request.getContent` WebExt API (lazy loading response body)
    154   */
    155  async fetchResponseContent(requestId) {
    156    return this.connector.requestData(requestId, "responseContent");
    157  }
    158 
    159  /**
    160   * Add listener for `onRequestFinished` events.
    161   *
    162   * @param {object} listener
    163   *        The listener to be called it's expected to be
    164   *        a function that takes ({harEntry, requestId})
    165   *        as first argument.
    166   */
    167  addRequestFinishedListener(listener) {
    168    this._requestFinishedListeners.add(listener);
    169  }
    170 
    171  removeRequestFinishedListener(listener) {
    172    this._requestFinishedListeners.delete(listener);
    173  }
    174 
    175  hasRequestFinishedListeners() {
    176    return this._requestFinishedListeners.size > 0;
    177  }
    178 
    179  /**
    180   * Separate connector for HAR export.
    181   */
    182  async getHarExportConnector() {
    183    if (this.harExportConnector) {
    184      // Wait for the connector to be ready to avoid exceptions if this method is called
    185      // twice during its initialization.
    186      await this.harExportConnectorReady;
    187      return this.harExportConnector;
    188    }
    189 
    190    const connection = {
    191      toolbox: this.toolbox,
    192    };
    193 
    194    this.harExportConnector = new Connector();
    195    this.harExportConnectorReady = this.harExportConnector.connect(connection);
    196 
    197    await this.harExportConnectorReady;
    198    return this.harExportConnector;
    199  }
    200 
    201  /**
    202   * Resends a given network request
    203   *
    204   * @param {string} requestId
    205   *        Id of the network request
    206   */
    207  resendRequest(requestId) {
    208    // Flush queued requests.
    209    this.store.dispatch(Actions.batchFlush());
    210    // Send custom request with same url, headers and body as the request
    211    // with the given requestId.
    212    this.store.dispatch(Actions.sendCustomRequest(requestId));
    213  }
    214 }
    215 
    216 exports.NetMonitorAPI = NetMonitorAPI;