tor-browser

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

OpenSearchManager.sys.mjs (7012B)


      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 const lazy = {};
      6 
      7 ChromeUtils.defineESModuleGetters(lazy, {
      8  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
      9 });
     10 
     11 /**
     12 * Manages the set of available opensearch engines per browser.
     13 */
     14 class _OpenSearchManager {
     15  /**
     16   * @typedef {object} OpenSearchData
     17   * @property {string} uri
     18   *   The uri of the opensearch XML.
     19   * @property {string} title
     20   *   The name of the engine.
     21   * @property {string} icon
     22   *   Data URI containing the engine's icon.
     23   */
     24 
     25  /**
     26   * @type {WeakMap<MozBrowser, OpenSearchData[]>}
     27   */
     28  #offeredEngines = new WeakMap();
     29 
     30  /**
     31   * @type {WeakMap<MozBrowser, OpenSearchData[]>}
     32   */
     33  #hiddenEngines = new WeakMap();
     34 
     35  constructor() {
     36    Services.obs.addObserver(this, "browser-search-engine-modified");
     37  }
     38 
     39  /**
     40   * Observer for browser-search-engine-modified.
     41   *
     42   * @param {nsISearchEngine} engine
     43   *   The modified engine.
     44   * @param {string} _topic
     45   *   Always browser-search-engine-modified.
     46   * @param {string} data
     47   *   The type of modification.
     48   */
     49  observe(engine, _topic, data) {
     50    // There are two kinds of search engine objects: nsISearchEngine objects
     51    // and plain OpenSearchData objects. `engine` in this observer is the
     52    // former and the arrays in #offeredEngines and #hiddenEngines contain the
     53    // latter. They are related by their names.
     54    switch (data) {
     55      case "engine-added":
     56        // An engine was added to the search service.  If a page is offering the
     57        // engine, then the engine needs to be removed from the corresponding
     58        // browser's offered engines.
     59        this.#removeMaybeOfferedEngine(engine.name);
     60        break;
     61      case "engine-removed":
     62        // An engine was removed from the search service.  If a page is offering
     63        // the engine, then the engine needs to be added back to the corresponding
     64        // browser's offered engines.
     65        this.#addMaybeOfferedEngine(engine.name);
     66        break;
     67    }
     68  }
     69 
     70  /**
     71   * Adds an open search engine to the list of available engines for a browser.
     72   * If an engine with that name is already installed, adds it to the list
     73   * of hidden engines instead.
     74   *
     75   * @param {MozBrowser} browser
     76   *   The browser offering the engine.
     77   * @param {{title: string, href: string}} engine
     78   *   The title of the engine and the url to the opensearch XML.
     79   */
     80  addEngine(browser, engine) {
     81    if (!Services.search.hasSuccessfullyInitialized) {
     82      // We haven't finished initializing search yet. This means we can't
     83      // call getEngineByName here. Since this is only on start-up and unlikely
     84      // to happen in the normal case, we'll just return early rather than
     85      // trying to handle it asynchronously.
     86      return;
     87    }
     88    // Check to see whether we've already added an engine with this title
     89    if (this.#offeredEngines.get(browser)?.some(e => e.title == engine.title)) {
     90      return;
     91    }
     92 
     93    // If this engine (identified by title) is already in the list, add it
     94    // to the list of hidden engines rather than to the main list.
     95    let shouldBeHidden = !!Services.search.getEngineByName(engine.title);
     96 
     97    let engines =
     98      (shouldBeHidden
     99        ? this.#hiddenEngines.get(browser)
    100        : this.#offeredEngines.get(browser)) || [];
    101 
    102    engines.push({
    103      uri: engine.href,
    104      title: engine.title,
    105      get icon() {
    106        return browser.mIconURL;
    107      },
    108    });
    109 
    110    if (shouldBeHidden) {
    111      this.#hiddenEngines.set(browser, engines);
    112    } else {
    113      let win = browser.ownerGlobal;
    114      this.#offeredEngines.set(browser, engines);
    115      if (browser == win.gBrowser.selectedBrowser) {
    116        this.updateOpenSearchBadge(win);
    117      }
    118    }
    119  }
    120 
    121  /**
    122   * Updates the browser UI to show whether or not additional engines are
    123   * available when a page is loaded or the user switches tabs to a page that
    124   * has open search engines.
    125   *
    126   * @param {WindowProxy} win
    127   *   The window whose UI should be updated.
    128   */
    129  updateOpenSearchBadge(win) {
    130    let engines = this.#offeredEngines.get(win.gBrowser.selectedBrowser);
    131    for (let urlbar of win.document.querySelectorAll("moz-urlbar")) {
    132      if (!urlbar.controller) {
    133        // This means it is not initialized and happens
    134        // if the new searchbar is disabled.
    135        continue;
    136      }
    137      urlbar.addSearchEngineHelper.setEnginesFromBrowser(
    138        win.gBrowser.selectedBrowser,
    139        engines || []
    140      );
    141    }
    142 
    143    let searchBar = win.document.getElementById("searchbar");
    144    if (!searchBar) {
    145      return;
    146    }
    147 
    148    if (engines && engines.length) {
    149      searchBar.setAttribute("addengines", "true");
    150    } else {
    151      searchBar.removeAttribute("addengines");
    152    }
    153  }
    154 
    155  #addMaybeOfferedEngine(engineName) {
    156    for (let win of lazy.BrowserWindowTracker.orderedWindows) {
    157      for (let browser of win.gBrowser.browsers) {
    158        let hiddenEngines = this.#hiddenEngines.get(browser) || [];
    159        let offeredEngines = this.#offeredEngines.get(browser) || [];
    160 
    161        for (let i = 0; i < hiddenEngines.length; i++) {
    162          if (hiddenEngines[i].title == engineName) {
    163            offeredEngines.push(hiddenEngines[i]);
    164            if (offeredEngines.length == 1) {
    165              this.#offeredEngines.set(browser, offeredEngines);
    166            }
    167 
    168            hiddenEngines.splice(i, 1);
    169            if (browser == win.gBrowser.selectedBrowser) {
    170              this.updateOpenSearchBadge(win);
    171            }
    172            break;
    173          }
    174        }
    175      }
    176    }
    177  }
    178 
    179  #removeMaybeOfferedEngine(engineName) {
    180    for (let win of lazy.BrowserWindowTracker.orderedWindows) {
    181      for (let browser of win.gBrowser.browsers) {
    182        let hiddenEngines = this.#hiddenEngines.get(browser) || [];
    183        let offeredEngines = this.#offeredEngines.get(browser) || [];
    184 
    185        for (let i = 0; i < offeredEngines.length; i++) {
    186          if (offeredEngines[i].title == engineName) {
    187            hiddenEngines.push(offeredEngines[i]);
    188            if (hiddenEngines.length == 1) {
    189              this.#hiddenEngines.set(browser, hiddenEngines);
    190            }
    191 
    192            offeredEngines.splice(i, 1);
    193            if (browser == win.gBrowser.selectedBrowser) {
    194              this.updateOpenSearchBadge(win);
    195            }
    196            break;
    197          }
    198        }
    199      }
    200    }
    201  }
    202 
    203  /**
    204   * Get the open search engines offered by a certain browser.
    205   *
    206   * @param {MozBrowser} browser
    207   *   The browser for which to get the engines.
    208   * @returns {OpenSearchData[]}
    209   *   The open search engines.
    210   */
    211  getEngines(browser) {
    212    return this.#offeredEngines.get(browser) || [];
    213  }
    214 
    215  clearEngines(browser) {
    216    this.#offeredEngines.delete(browser);
    217    this.#hiddenEngines.delete(browser);
    218  }
    219 }
    220 
    221 export const OpenSearchManager = new _OpenSearchManager();