tor-browser

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

head.js (6265B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 ChromeUtils.defineESModuleGetters(this, {
      7  NetworkHelper:
      8    "resource://devtools/shared/network-observer/NetworkHelper.sys.mjs",
      9  NetworkObserver:
     10    "resource://devtools/shared/network-observer/NetworkObserver.sys.mjs",
     11  NetworkUtils:
     12    "resource://devtools/shared/network-observer/NetworkUtils.sys.mjs",
     13 });
     14 
     15 const TEST_DIR = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
     16 const CHROME_URL_ROOT = TEST_DIR + "/";
     17 const URL_ROOT = CHROME_URL_ROOT.replace(
     18  "chrome://mochitests/content/",
     19  "https://example.com/"
     20 );
     21 
     22 /**
     23 * Load the provided url in an existing browser.
     24 * Returns a promise which will resolve when the page is loaded.
     25 *
     26 * @param {Browser} browser
     27 *     The browser element where the URL should be loaded.
     28 * @param {string} url
     29 *     The URL to load in the new tab
     30 */
     31 async function loadURL(browser, url) {
     32  const loaded = BrowserTestUtils.browserLoaded(browser);
     33  BrowserTestUtils.startLoadingURIString(browser, url);
     34  return loaded;
     35 }
     36 
     37 /**
     38 * Create a new foreground tab loading the provided url.
     39 * Returns a promise which will resolve when the page is loaded.
     40 *
     41 * @param {string} url
     42 *     The URL to load in the new tab
     43 */
     44 async function addTab(url) {
     45  const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
     46  registerCleanupFunction(() => {
     47    gBrowser.removeTab(tab);
     48  });
     49  return tab;
     50 }
     51 
     52 /**
     53 * Base network event owner class implementing all mandatory callbacks and
     54 * keeping track of which callbacks have been called.
     55 */
     56 class NetworkEventOwner {
     57  hasCacheDetails = false;
     58  hasEventTimings = false;
     59  hasRawHeaders = false;
     60  hasResponseCache = false;
     61  hasResponseContent = false;
     62  hasResponseContentComplete = false;
     63  hasResponseStart = false;
     64  hasSecurityInfo = false;
     65  hasServerTimings = false;
     66 
     67  addCacheDetails() {
     68    this.hasCacheDetails = true;
     69  }
     70  addEventTimings() {
     71    this.hasEventTimings = true;
     72  }
     73  addRawHeaders() {
     74    this.hasRawHeaders = true;
     75  }
     76  addResponseCache() {
     77    this.hasResponseCache = true;
     78  }
     79  addResponseContent() {
     80    this.hasResponseContent = true;
     81  }
     82  addResponseContentComplete() {
     83    this.hasResponseContentComplete = true;
     84  }
     85  addResponseStart() {
     86    this.hasResponseStart = true;
     87  }
     88  addSecurityInfo() {
     89    this.hasSecurityInfo = true;
     90  }
     91  addServerTimings() {
     92    this.hasServerTimings = true;
     93  }
     94  addServiceWorkerTimings() {
     95    this.hasServiceWorkerTimings = true;
     96  }
     97 }
     98 
     99 /**
    100 * Create a simple network event owner, with mock implementations of all
    101 * the expected APIs for a NetworkEventOwner.
    102 */
    103 function createNetworkEventOwner() {
    104  return new NetworkEventOwner();
    105 }
    106 
    107 /**
    108 * Wait for network events matching the provided URL, until the count reaches
    109 * the provided expected count.
    110 *
    111 * @param {string|null} expectedUrl
    112 *     The URL which should be monitored by the NetworkObserver.If set to null watch for
    113 *      all requests
    114 * @param {number} expectedRequestsCount
    115 *     How many different events (requests) are expected.
    116 * @returns {Promise}
    117 *     A promise which will resolve with an array of network event owners, when
    118 *     the expected event count is reached.
    119 */
    120 async function waitForNetworkEvents(expectedUrl = null, expectedRequestsCount) {
    121  const events = [];
    122  const networkObserver = new NetworkObserver({
    123    ignoreChannelFunction: channel =>
    124      expectedUrl ? channel.URI.spec !== expectedUrl : false,
    125    onNetworkEvent: () => {
    126      info("waitForNetworkEvents received a new event");
    127      const owner = createNetworkEventOwner();
    128      events.push(owner);
    129      return owner;
    130    },
    131  });
    132  registerCleanupFunction(() => networkObserver.destroy());
    133 
    134  info("Wait until the events count reaches " + expectedRequestsCount);
    135  await BrowserTestUtils.waitForCondition(
    136    () => events.length >= expectedRequestsCount
    137  );
    138  return events;
    139 }
    140 
    141 /**
    142 * NetworkEventOwner class which can be used to assert the response content of
    143 * a network event.
    144 */
    145 class ResponseContentOwner extends NetworkEventOwner {
    146  addResponseContent(response) {
    147    super.addResponseContent();
    148    this.compressionEncodings = response.compressionEncodings;
    149    this.contentCharset = response.contentCharset;
    150    this.decodedBodySize = response.decodedBodySize;
    151    this.encodedBodySize = response.encodedBodySize;
    152    this.encodedData = response.encodedData;
    153    this.encoding = response.encoding;
    154    this.isContentEncoded = response.isContentEncoded;
    155    this.text = response.text;
    156  }
    157 
    158  addResponseContentComplete({ truncated }) {
    159    super.addResponseContentComplete();
    160    this.truncated = truncated;
    161  }
    162 
    163  /**
    164   * Simple helper to decode the content of a response from a network event.
    165   */
    166  async getDecodedContent() {
    167    if (!this.isContentEncoded) {
    168      // If the content is not encoded we can directly return the text property.
    169      return this.text;
    170    }
    171 
    172    // Otherwise call the dedicated NetworkUtils decodeResponseChunks helper.
    173    return NetworkUtils.decodeResponseChunks(this.encodedData, {
    174      charset: this.contentCharset,
    175      compressionEncodings: this.compressionEncodings,
    176      encodedBodySize: this.encodedBodySize,
    177      encoding: this.encoding,
    178    });
    179  }
    180 }
    181 
    182 /**
    183 * Helper to compress a string using gzip.
    184 */
    185 function gzipCompressString(string) {
    186  return new Promise(resolve => {
    187    const observer = {
    188      onStreamComplete(loader, context, status, length, result) {
    189        resolve(String.fromCharCode.apply(this, result));
    190      },
    191    };
    192 
    193    const scs = Cc["@mozilla.org/streamConverters;1"].getService(
    194      Ci.nsIStreamConverterService
    195    );
    196    const listener = Cc["@mozilla.org/network/stream-loader;1"].createInstance(
    197      Ci.nsIStreamLoader
    198    );
    199    listener.init(observer);
    200    const converter = scs.asyncConvertData(
    201      "uncompressed",
    202      "gzip",
    203      listener,
    204      null
    205    );
    206    const stringStream = Cc[
    207      "@mozilla.org/io/string-input-stream;1"
    208    ].createInstance(Ci.nsIStringInputStream);
    209    stringStream.setByteStringData(string);
    210    converter.onStartRequest(null, null);
    211    converter.onDataAvailable(null, stringStream, 0, string.length);
    212    converter.onStopRequest(null, null, null);
    213  });
    214 }