/* 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/. */ const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { PanelMultiView: "resource:///modules/PanelMultiView.sys.mjs", UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs", UrlbarSearchUtils: "resource:///modules/UrlbarSearchUtils.sys.mjs", UrlbarUtils: "resource:///modules/UrlbarUtils.sys.mjs", }); ChromeUtils.defineLazyGetter(lazy, "SearchModeSwitcherL10n", () => { return new Localization(["preview/enUS-searchFeatures.ftl"]); }); /** * Implements the SearchModeSwitcher in the urlbar. */ export class SearchModeSwitcher { #engineListNeedsRebuild = true; #input = null; constructor(input) { this.#input = input; this.QueryInterface = ChromeUtils.generateQI([ "nsIObserver", "nsISupportsWeakReference", ]); Services.obs.addObserver(this, "browser-search-engine-modified", true); let toolbarbutton = input.document.querySelector( "#urlbar-searchmode-switcher" ); toolbarbutton.addEventListener("mousedown", this); toolbarbutton.addEventListener("keypress", this); let closebutton = input.document.querySelector( "#searchmode-switcher-close" ); closebutton.addEventListener("mousedown", this); closebutton.addEventListener("keypress", this); input.window.addEventListener( "MozAfterPaint", () => this.#updateSearchIcon(), { once: true } ); } /** * Open the SearchSwitcher popup. * * @param {Event} event * The event that triggered the opening of the popup. */ async openPanel(event) { let anchor = event.target; event.stopPropagation(); if (this.#input.document.documentElement.hasAttribute("customizing")) { return; } if (this.#engineListNeedsRebuild) { await this.#rebuildSearchModeList(this.#input.window); this.#engineListNeedsRebuild = false; } if (anchor.getAttribute("open") != "true") { this.#input.view.hideTemporarily(); this.#getPopup().addEventListener( "popuphidden", () => { anchor.removeAttribute("open"); this.#input.view.restoreVisibility(); }, { once: true } ); anchor.setAttribute("open", true); lazy.PanelMultiView.openPopup(this.#getPopup(), anchor, { position: "bottomleft topleft", triggerEvent: event, }).catch(console.error); } } /** * Exit the engine specific searchMode. * * @param {Event} event * The event that triggered the searchMode exit. */ exitSearchMode(event) { event.stopPropagation(); event.preventDefault(); this.#input.searchMode = null; } /** * Called when the value of the searchMode attribute on UrlbarInput is changed. */ onSearchModeChanged() { this.#updateSearchIcon(); } handleEvent(event) { let action = event.currentTarget.dataset.action ?? event.type; switch (action) { case "openpopup": { this.openPanel(event); break; } case "exitsearchmode": { this.exitSearchMode(event); break; } } } observe(_subject, topic, _data) { switch (topic) { case "browser-search-engine-modified": { this.#engineListNeedsRebuild = true; break; } } } #popup = null; #getPopup() { if (!this.#popup) { this.#popup = this.#input.document.getElementById( "searchmode-switcher-popup" ); } return this.#popup; } async #updateSearchIcon() { try { await lazy.UrlbarSearchUtils.init(); } catch { // We should still work if the SearchService is not working. } let button = this.#input.document.getElementById( "searchmode-switcher-icon" ); let iconURL = await this.#getIconForSearchMode(this.#input.searchMode); if (iconURL) { button.style.listStyleImage = `url(${iconURL})`; } let label = this.#input.document.getElementById( "searchmode-switcher-title" ); if (!this.#input.searchMode) { label.replaceChildren(); } else if (this.#input.searchMode?.engineName) { label.textContent = this.#input.searchMode.engineName; } else if (this.#input.searchMode?.source) { // Certainly the wrong way to do this, the same string is used // as a