browser/components/search/content/contentSearchHandoffUI.js
author Lando <lando@lando.test>
Fri, 02 May 2025 09:42:48 +0000
changeset 785412 5b5bd7e730096ef3867efe107dc97fb4a38a489a
parent 781699 ab971ce38388882ad4ff30b33b6e008bc95cbad5
permissions -rw-r--r--
Merge autoland to mozilla-central

/* 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/. */

"use strict";

/**
 * Handles handing off searches from an in-page search input field to the
 * browser's main URL bar. Communicates with the parent via the ContentSearch
 * actor, using custom events to talk to the child actor.
 */
class ContentSearchHandoffUIController {
  constructor() {
    this._isPrivateEngine = false;
    this._engineIcon = null;

    window.addEventListener("ContentSearchService", this);
    this._sendMsg("GetEngine");
    this._sendMsg("GetHandoffSearchModePrefs");
  }

  handleEvent(event) {
    let methodName = "_onMsg" + event.detail.type;
    if (methodName in this) {
      this[methodName](event.detail.data);
    }
  }

  get defaultEngine() {
    return this._defaultEngine;
  }

  static privateBrowsingRegex = /^about:privatebrowsing([#?]|$)/i;
  get _isAboutPrivateBrowsing() {
    return ContentSearchHandoffUIController.privateBrowsingRegex.test(
      document.location.href
    );
  }

  _onMsgEngine({ isPrivateEngine, engine }) {
    this._isPrivateEngine = isPrivateEngine;
    this._updateEngine(engine);
  }

  _onMsgCurrentEngine(engine) {
    if (!this._isPrivateEngine) {
      this._updateEngine(engine);
    }
  }

  _onMsgCurrentPrivateEngine(engine) {
    if (this._isPrivateEngine) {
      this._updateEngine(engine);
    }
  }

  _onMsgHandoffSearchModePrefs(pref) {
    this._shouldHandOffToSearchMode = pref;
    this._updatel10nIds();
  }

  _updateEngine(engine) {
    this._defaultEngine = engine;
    if (this._engineIcon) {
      URL.revokeObjectURL(this._engineIcon);
    }

    // We only show the engines icon for app provided engines, otherwise show
    // a default. xref https://bugzilla.mozilla.org/show_bug.cgi?id=1449338#c19
    if (!engine.isAppProvided) {
      this._engineIcon = "chrome://global/skin/icons/search-glass.svg";
    } else if (engine.iconData) {
      this._engineIcon = this._getFaviconURIFromIconData(engine.iconData);
    } else {
      this._engineIcon = "chrome://global/skin/icons/defaultFavicon.svg";
    }

    document.body.style.setProperty(
      "--newtab-search-icon",
      "url(" + this._engineIcon + ")"
    );
    this._updatel10nIds();
  }

  _updatel10nIds() {
    let engine = this._defaultEngine;
    let fakeButton = document.querySelector(".search-handoff-button");
    let fakeInput = document.querySelector(".fake-textbox");
    if (!fakeButton || !fakeInput) {
      return;
    }
    if (!engine || this._shouldHandOffToSearchMode) {
      document.l10n.setAttributes(
        fakeButton,
        this._isAboutPrivateBrowsing
          ? "about-private-browsing-search-btn"
          : "newtab-search-box-input"
      );
      document.l10n.setAttributes(
        fakeInput,
        this._isAboutPrivateBrowsing
          ? "about-private-browsing-search-placeholder"
          : "newtab-search-box-text"
      );
    } else if (!engine.isAppProvided) {
      document.l10n.setAttributes(
        fakeButton,
        this._isAboutPrivateBrowsing
          ? "about-private-browsing-handoff-no-engine"
          : "newtab-search-box-handoff-input-no-engine"
      );
      document.l10n.setAttributes(
        fakeInput,
        this._isAboutPrivateBrowsing
          ? "about-private-browsing-handoff-text-no-engine"
          : "newtab-search-box-handoff-text-no-engine"
      );
    } else {
      document.l10n.setAttributes(
        fakeButton,
        this._isAboutPrivateBrowsing
          ? "about-private-browsing-handoff"
          : "newtab-search-box-handoff-input",
        {
          engine: engine.name,
        }
      );
      document.l10n.setAttributes(
        fakeInput,
        this._isAboutPrivateBrowsing
          ? "about-private-browsing-handoff-text"
          : "newtab-search-box-handoff-text",
        {
          engine: engine.name,
        }
      );
    }
  }

  /**
   * If the favicon is an iconData object, convert it into a Blob URI.
   * Otherwise just return the plain URI.
   *
   * @param {string|iconData} data
   *   The icon's URL or an iconData object containing the icon data.
   * @returns {string}
   *   A blob URL or the plain icon URI.
   */
  _getFaviconURIFromIconData(data) {
    if (typeof data == "string") {
      return data;
    }

    // If typeof(data) != "string", the iconData object is returned.
    let blob = new Blob([data.icon], { type: data.mimeType });
    return URL.createObjectURL(blob);
  }

  _sendMsg(type, data = null) {
    dispatchEvent(
      new CustomEvent("ContentSearchClient", {
        detail: {
          type,
          data,
        },
      })
    );
  }
}

window.ContentSearchHandoffUIController = ContentSearchHandoffUIController;