Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/toolkit/components/search/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 8 kB image not shown  

Quelle  AddonSearchEngine.sys.mjs   Sprache: unbekannt

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* eslint no-shadow: error, mozilla/no-aArgs: error */

import { SearchEngine } from "resource://gre/modules/SearchEngine.sys.mjs";

const lazy = {};

ChromeUtils.defineESModuleGetters(lazy, {
  AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
  ExtensionParent: "resource://gre/modules/ExtensionParent.sys.mjs",
  SearchUtils: "resource://gre/modules/SearchUtils.sys.mjs",
});

ChromeUtils.defineLazyGetter(lazy, "logConsole", () => {
  return console.createInstance({
    prefix: "AddonSearchEngine",
    maxLogLevel: lazy.SearchUtils.loggingEnabled ? "Debug" : "Warn",
  });
});

/**
 * AddonSearchEngine represents a search engine defined by an add-on.
 */
export class AddonSearchEngine extends SearchEngine {
  // The extension ID if added by an extension.
  _extensionID = null;

  /**
   * Creates a AddonSearchEngine.
   *
   * @param {object} options
   *   The options object
   * @param {object} [options.details]
   *   An object that simulates the manifest object from a WebExtension.
   * @param {object} [options.json]
   *   An object that represents the saved JSON settings for the engine.
   */
  constructor({ details, json } = {}) {
    let extensionId =
      details?.extensionID ?? json.extensionID ?? json._extensionID;
    let id = extensionId + lazy.SearchUtils.DEFAULT_TAG;

    super({
      loadPath: "[addon]" + extensionId,
      id,
    });

    this._extensionID = extensionId;

    if (json) {
      this._initWithJSON(json);
    }
  }

  _initWithJSON(json) {
    super._initWithJSON(json);
    this._extensionID = json.extensionID || json._extensionID || null;
  }

  /**
   * Call to initalise an instance with extension details. Does not need to be
   * called if json has been passed to the constructor.
   *
   * @param {object} options
   *   The options object.
   * @param {Extension} [options.extension]
   *   The extension object representing the add-on.
   * @param {object} [options.settings]
   *   The saved settings for the user.
   */
  async init({ extension, settings } = {}) {
    let { baseURI, manifest } =
      await this.#getExtensionDetailsForLocale(extension);

    this.#initFromManifest(baseURI, manifest);
    this._loadSettings(settings);
  }

  /**
   * Manages updates to this engine.
   *
   * @param {object} options
   *   The options object.
   * @param {object} [options.extension]
   *   The extension associated with this search engine, if known.
   */
  async update({ extension } = {}) {
    let { baseURI, manifest } =
      await this.#getExtensionDetailsForLocale(extension);

    let originalName = this.name;
    let name = manifest.chrome_settings_overrides.search_provider.name.trim();
    if (originalName != name && Services.search.getEngineByName(name)) {
      throw new Error("Can't upgrade to the same name as an existing engine");
    }

    this.#updateFromManifest(baseURI, manifest);
  }

  /**
   * Creates a JavaScript object that represents this engine.
   *
   * @returns {object}
   *   An object suitable for serialization as JSON.
   */
  toJSON() {
    let json = super.toJSON();
    json._extensionID = this._extensionID;
    return json;
  }

  /**
   * Checks to see if this engine's settings are in sync with what the add-on
   * manager has, and reports the results to telemetry.
   */
  async checkAndReportIfSettingsValid() {
    let addon = await lazy.AddonManager.getAddonByID(this._extensionID);

    if (!addon) {
      lazy.logConsole.debug(
        `Add-on ${this._extensionID} for search engine ${this.name} is not installed!`
      );
      Glean.browserSearchinit.engineInvalidWebextension[this._extensionID].set(
        1
      );
    } else if (!addon.isActive) {
      lazy.logConsole.debug(
        `Add-on ${this._extensionID} for search engine ${this.name} is not active!`
      );
      Glean.browserSearchinit.engineInvalidWebextension[this._extensionID].set(
        2
      );
    } else {
      let policy = await AddonSearchEngine.getWebExtensionPolicy(
        this._extensionID
      );
      let providerSettings =
        policy.extension.manifest?.chrome_settings_overrides?.search_provider;

      if (!providerSettings) {
        lazy.logConsole.debug(
          `Add-on ${this._extensionID} for search engine ${this.name} no longer has an engine defined`
        );
        Glean.browserSearchinit.engineInvalidWebextension[
          this._extensionID
        ].set(4);
      } else if (this.name != providerSettings.name) {
        lazy.logConsole.debug(
          `Add-on ${this._extensionID} for search engine ${this.name} has a different name!`
        );
        Glean.browserSearchinit.engineInvalidWebextension[
          this._extensionID
        ].set(5);
      } else if (!this.checkSearchUrlMatchesManifest(providerSettings)) {
        lazy.logConsole.debug(
          `Add-on ${this._extensionID} for search engine ${this.name} has out-of-date manifest!`
        );
        Glean.browserSearchinit.engineInvalidWebextension[
          this._extensionID
        ].set(6);
      }
    }
  }

  /**
   * Initializes the engine based on the manifest and other values.
   *
   * @param {nsIURI} extensionBaseURI
   *   The Base URI of the WebExtension.
   * @param {object} manifest
   *   An object representing the WebExtensions' manifest.
   */
  #initFromManifest(extensionBaseURI, manifest) {
    let searchProvider = manifest.chrome_settings_overrides.search_provider;

    // Set the main icon URL for the engine.
    let iconURL = searchProvider.favicon_url;

    if (!iconURL) {
      iconURL =
        manifest.icons &&
        extensionBaseURI.resolve(
          lazy.ExtensionParent.IconDetails.getPreferredIcon(manifest.icons).icon
        );
    }

    // Record other icons that the WebExtension has.
    if (manifest.icons) {
      for (let [size, icon] of Object.entries(manifest.icons)) {
        this._addIconToMap(
          extensionBaseURI.resolve(icon),
          parseInt(size),
          false
        );
      }
    }

    this._initWithDetails({
      ...searchProvider,
      iconURL,
      description: manifest.description,
    });
  }

  /**
   * Update this engine based on new manifest, used during
   * webextension upgrades.
   *
   * @param {nsIURI} extensionBaseURI
   *   The Base URI of the WebExtension.
   * @param {object} manifest
   *   An object representing the WebExtensions' manifest.
   */
  #updateFromManifest(extensionBaseURI, manifest) {
    this._urls = [];
    this._iconMapObj = null;
    this.#initFromManifest(extensionBaseURI, manifest);
    lazy.SearchUtils.notifyAction(this, lazy.SearchUtils.MODIFIED_TYPE.CHANGED);
  }

  /**
   * Get the localized manifest from the WebExtension for the given locale or
   * manifest default locale.
   *
   * @param {object} [extension]
   *   The extension to get the manifest from.
   * @returns {Promise<object>}
   *   The loaded manifest.
   */
  async #getExtensionDetailsForLocale(extension) {
    // If we haven't been passed an extension object, then go and find it.
    if (!extension) {
      extension = (
        await AddonSearchEngine.getWebExtensionPolicy(this._extensionID)
      ).extension;
    }

    let manifest = extension.manifest;

    // For user installed add-ons, we have to simulate the add-on manager
    // code for loading the correct locale.
    // We do this, as in the case of a live language switch, the add-on manager
    // may not have yet reloaded the extension, and there's no way for us to
    // listen for that reload to complete.
    // See also https://bugzilla.mozilla.org/show_bug.cgi?id=1781768#c3 for
    // more background.
    let localeToLoad = Services.locale.negotiateLanguages(
      Services.locale.appLocalesAsBCP47,
      [...extension.localeData.locales.keys()],
      manifest.default_locale
    )[0];

    if (localeToLoad) {
      manifest = await extension.getLocalizedManifest(localeToLoad);
    }
    return { baseURI: extension.baseURI, manifest };
  }

  /**
   * Gets the WebExtensionPolicy for an add-on.
   *
   * @param {string} id
   *   The WebExtension id.
   * @returns {Promise<WebExtensionPolicy>}
   */
  static async getWebExtensionPolicy(id) {
    let policy = WebExtensionPolicy.getByID(id);
    if (!policy) {
      let idPrefix = id.split("@")[0];
      let path = `resource://search-extensions/${idPrefix}/`;
      await lazy.AddonManager.installBuiltinAddon(path);
      policy = WebExtensionPolicy.getByID(id);
    }
    // On startup the extension may have not finished parsing the
    // manifest, wait for that here.
    await policy.readyPromise;
    return policy;
  }
}

[ Dauer der Verarbeitung: 0.24 Sekunden  (vorverarbeitet)  ]