tor-browser

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

SimpleURIUnknownSchemesRemoteObserver.sys.mjs (3979B)


      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 file,
      3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 const lazy = {};
      6 
      7 ChromeUtils.defineESModuleGetters(lazy, {
      8  RemoteSettings: "resource://services-settings/remote-settings.sys.mjs",
      9  RemoteSettingsClient:
     10    "resource://services-settings/RemoteSettingsClient.sys.mjs",
     11 });
     12 
     13 const SETTINGS_DEFAULTURI_BYPASS_LIST_KEY =
     14  "url-parser-default-unknown-schemes-interventions";
     15 
     16 export class SimpleURIUnknownSchemesRemoteObserver {
     17  #initialized = false;
     18  #bypassListSettings;
     19  classID = Components.ID("{86606ba1-de17-4df4-9013-e571ab94fd94}");
     20  QueryInterface = ChromeUtils.generateQI([
     21    "nsIObserver",
     22    "nsISimpleURIUnknownSchemesRemoteObserver",
     23  ]);
     24 
     25  observe(subject, topic) {
     26    // signal selected because RemoteSettingsClient is first getting initialised
     27    // by the AddonManager at addons-startup
     28    if (topic == "profile-after-change" && !this.#initialized) {
     29      this.#initialized = true;
     30      this.#init();
     31    }
     32  }
     33 
     34  /**
     35   * This method updates the io service with the local scheme list used to
     36   * bypass the defaultURI parser and use the simpleURI parser.
     37   * It also subscribes to Remote Settings changes to this list which are then
     38   * broadcast to processes interested in URL parsing.
     39   *
     40   * note that there doesn't appear to be a way to get a URI with a non-special
     41   * scheme into about:preferences so it should be safe to spin this up early
     42   */
     43  async #init() {
     44    if (!this.#bypassListSettings) {
     45      this.#bypassListSettings = lazy.RemoteSettings(
     46        SETTINGS_DEFAULTURI_BYPASS_LIST_KEY
     47      );
     48    }
     49 
     50    // Trigger a get from local remote settings and update the io service.
     51    const settingsList = await this.#getBypassList();
     52    let schemes = settingsList.map(r => r.scheme);
     53    if (schemes.length) {
     54      Services.io.setSimpleURIUnknownRemoteSchemes(schemes);
     55    }
     56 
     57    // Listen for future updates after we first get the values.
     58    this.#bypassListSettings.on("sync", this.#updateBypassList.bind(this));
     59  }
     60 
     61  async #updateBypassList() {
     62    const settingsList = await this.#getBypassList();
     63    let schemes = settingsList.map(r => r.scheme);
     64    if (schemes.length) {
     65      Services.io.setSimpleURIUnknownRemoteSchemes(schemes);
     66    }
     67  }
     68 
     69  async #getBypassList() {
     70    if (this._getSettingsPromise) {
     71      return this._getSettingsPromise;
     72    }
     73 
     74    const settings = await (this._getSettingsPromise =
     75      this.#getBypassListSettings());
     76    delete this._getSettingsPromise;
     77    return settings;
     78  }
     79 
     80  /**
     81   * Obtains the current bypass list from remote settings. This includes
     82   * verifying the signature of the bypass list within the database.
     83   *
     84   * If the signature in the database is invalid, the database will be wiped
     85   * and the stored dump will be used, until the settings next update.
     86   *
     87   * Note that this may cause a network check of the certificate, but that
     88   * should generally be quick.
     89   *
     90   * @param {boolean} [firstTime]
     91   *   Internal boolean to indicate if this is the first time check or not.
     92   * @returns {Array}
     93   *   An array of objects in the database, or an empty array if none
     94   *   could be obtained.
     95   */
     96  async #getBypassListSettings(firstTime = true) {
     97    let result = [];
     98    try {
     99      result = await this.#bypassListSettings.get({
    100        verifySignature: true,
    101      });
    102    } catch (ex) {
    103      if (
    104        ex instanceof lazy.RemoteSettingsClient.InvalidSignatureError &&
    105        firstTime
    106      ) {
    107        // The local database is invalid, try and reset it.
    108        await this.#bypassListSettings.db.clear();
    109        // Now call this again.
    110        return this.#getBypassListSettings(false);
    111      }
    112      // Don't throw an error just log it, just continue with no data, and hopefully
    113      // a sync will fix things later on.
    114      console.error(ex);
    115    }
    116    return result;
    117  }
    118 }