tor-browser

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

EssentialDomainsRemoteSettings.sys.mjs (3878B)


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