chromium/chrome/browser/resources/settings/search_page/search_page.ts

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview
 * 'settings-search-page' is the settings page containing search settings.
 */
import '/shared/settings/controls/cr_policy_pref_indicator.js';
import 'chrome://resources/cr_elements/cr_shared_style.css.js';
import 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
import 'chrome://resources/cr_elements/md_select.css.js';
import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
import '/shared/settings/controls/extension_controlled_indicator.js';
import '../settings_page/settings_animated_pages.js';
import '../settings_page/settings_subpage.js';
import '../settings_shared.css.js';
import '../settings_vars.css.js';
import '../site_favicon.js';

import type {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
import {assert} from 'chrome://resources/js/assert.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import {BaseMixin} from '../base_mixin.js';
import {loadTimeData} from '../i18n_setup.js';
import {routes} from '../route.js';
import {Router} from '../router.js';
import type {SearchEngine, SearchEnginesBrowserProxy, SearchEnginesInfo} from '../search_engines_page/search_engines_browser_proxy.js';
import {ChoiceMadeLocation, SearchEnginesBrowserProxyImpl} from '../search_engines_page/search_engines_browser_proxy.js';

import {getTemplate} from './search_page.html.js';

const SettingsSearchPageElementBase =
    BaseMixin(WebUiListenerMixin(I18nMixin(PolymerElement)));

export class SettingsSearchPageElement extends SettingsSearchPageElementBase {
  static get is() {
    return 'settings-search-page';
  }

  static get template() {
    return getTemplate();
  }

  static get properties() {
    return {
      prefs: Object,

      /**
       * List of search engines available.
       */
      searchEngines_: Array,

      // Whether the `SearchEngineChoiceTrigger` feature is enabled.
      searchEngineChoiceSettingsUi_: {
        type: Boolean,
        value() {
          return loadTimeData.getBoolean('searchEngineChoiceSettingsUi');
        },
      },

      // Whether we need to set the icon size to large because they are loaded
      // in the binary or smaller because we get them from the favicon service.
      isEeaChoiceCountry_: {
        type: Boolean,
        value() {
          return loadTimeData.getBoolean('isEeaChoiceCountry');
        },
      },

      // The selected default search engine.
      defaultSearchEngine_: {
        type: Object,
        computed: 'computeDefaultSearchEngine_(searchEngines_)',
      },

      /** Filter applied to search engines. */
      searchEnginesFilter_: String,

      focusConfig_: Object,

      // Boolean to check whether we need to show the dialog or not.
      showSearchEngineListDialog_: Boolean,

      // The label of the confirmation toast that is displayed when the user
      // chooses a default search engine.
      confirmationToastLabel_: String,
    };
  }

  prefs: Object;
  private searchEngines_: SearchEngine[];
  private searchEnginesFilter_: string;
  private searchEngineChoiceSettingsUi_: boolean;
  private showSearchEngineListDialog_: boolean;
  private defaultSearchEngine_: SearchEngine|null;
  private focusConfig_: Map<string, string>|null;
  private browserProxy_: SearchEnginesBrowserProxy =
      SearchEnginesBrowserProxyImpl.getInstance();
  private useLargeSearchEngineIcons_: boolean;
  private confirmationToastLabel_: string;

  override ready() {
    super.ready();

    // Omnibox search engine
    const updateSearchEngines = (searchEngines: SearchEnginesInfo) => {
      this.searchEngines_ = searchEngines.defaults;
    };
    this.browserProxy_.getSearchEnginesList().then(updateSearchEngines);
    this.addWebUiListener('search-engines-changed', updateSearchEngines);

    this.focusConfig_ = new Map();
    if (routes.SEARCH_ENGINES) {
      this.focusConfig_.set(
          routes.SEARCH_ENGINES.path, '#enginesSubpageTrigger');
    }
  }

  override connectedCallback() {
    super.connectedCallback();
    this.setFaviconSize_();
  }

  private onChange_() {
    assert(!this.searchEngineChoiceSettingsUi_);
    const select = this.shadowRoot!.querySelector('select');
    assert(select);
    const searchEngine = this.searchEngines_[select.selectedIndex];
    this.browserProxy_.setDefaultSearchEngine(
        searchEngine.modelIndex, ChoiceMadeLocation.SEARCH_SETTINGS);
  }

  private onDisableExtension_() {
    this.dispatchEvent(new CustomEvent('refresh-pref', {
      bubbles: true,
      composed: true,
      detail: 'default_search_provider.enabled',
    }));
  }

  private onManageSearchEnginesClick_() {
    Router.getInstance().navigateTo(routes.SEARCH_ENGINES);
  }

  private isDefaultSearchControlledByPolicy_(
      pref: chrome.settingsPrivate.PrefObject): boolean {
    return pref.controlledBy ===
        chrome.settingsPrivate.ControlledBy.USER_POLICY;
  }

  private isDefaultSearchEngineEnforced_(
      pref: chrome.settingsPrivate.PrefObject): boolean {
    return pref.enforcement === chrome.settingsPrivate.Enforcement.ENFORCED;
  }

  private computeDefaultSearchEngine_() {
    if (!this.searchEngines_.length || !this.searchEngineChoiceSettingsUi_) {
      return null;
    }

    return this.searchEngines_.find(engine => engine.default)!;
  }

  private onOpenDialogButtonClick_() {
    assert(this.searchEngineChoiceSettingsUi_);
    this.showSearchEngineListDialog_ = true;
    chrome.metricsPrivate.recordUserAction('ChooseDefaultSearchEngine');
  }

  private onDefaultSearchEngineChangedInDialog_(e: CustomEvent) {
    this.confirmationToastLabel_ = this.i18n(
        'searchEnginesConfirmationToastLabel', e.detail.searchEngine.name);
    this.shadowRoot!.querySelector<CrToastElement>(
                        '#confirmationToast')!.show();
  }

  private onSearchEngineListDialogClose_() {
    assert(this.searchEngineChoiceSettingsUi_);
    this.showSearchEngineListDialog_ = false;
  }

  private setFaviconSize_() {
    this.style.setProperty(
        '--favicon-size', this.useLargeSearchEngineIcons_ ? '24px' : '16px');
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'settings-search-page': SettingsSearchPageElement;
  }
}

customElements.define(SettingsSearchPageElement.is, SettingsSearchPageElement);