chromium/chrome/browser/resources/settings/a11y_page/captions_subpage.ts

// Copyright 2019 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-captions' is a component for showing captions
 * settings on chrome://settings/captions.
 */

import '//resources/cr_elements/cr_shared_style.css.js';
import '../controls/settings_slider.js';
import '../settings_shared.css.js';
import './live_caption_section.js';

import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import type {FontsData} from '/shared/settings/appearance_page/fonts_browser_proxy.js';
import {FontsBrowserProxyImpl} from '/shared/settings/appearance_page/fonts_browser_proxy.js';
import {PrefsMixin} from '/shared/settings/prefs/prefs_mixin.js';

import type {DropdownMenuOptionList} from '../controls/settings_dropdown_menu.js';
import type {SettingsToggleButtonElement} from '../controls/settings_toggle_button.js';
import {loadTimeData} from '../i18n_setup.js';
import type {LanguageHelper, LanguagesModel} from '../languages_page/languages_types.js';

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

const SettingsCaptionsElementBase = PrefsMixin(PolymerElement);

export class SettingsCaptionsElement extends SettingsCaptionsElementBase {
  static get is() {
    return 'settings-captions';
  }

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

  static get properties() {
    return {
      prefs: {
        type: Object,
        notify: true,
      },


      /**
       * Read-only reference to the languages model provided by the
       * 'settings-languages' instance.
       */
      languages: {
        type: Object,
        notify: true,
      },

      languageHelper: Object,

      /**
       * List of options for the background opacity drop-down menu.
       */
      backgroundOpacityOptions_: {
        readOnly: true,
        type: Array,
        value() {
          return [
            {
              value: 100,  // Default
              name: loadTimeData.getString('captionsOpacityOpaque'),
            },
            {
              value: 50,
              name: loadTimeData.getString('captionsOpacitySemiTransparent'),
            },
            {
              value: 0,
              name: loadTimeData.getString('captionsOpacityTransparent'),
            },
          ];
        },
      },

      /**
       * List of options for the color drop-down menu.
       */
      colorOptions_: {
        readOnly: true,
        type: Array,
        value() {
          return [
            {value: '', name: loadTimeData.getString('captionsDefaultSetting')},
            {
              value: '0,0,0',
              name: loadTimeData.getString('captionsColorBlack'),
            },
            {
              value: '255,255,255',
              name: loadTimeData.getString('captionsColorWhite'),
            },
            {
              value: '255,0,0',
              name: loadTimeData.getString('captionsColorRed'),
            },
            {
              value: '0,255,0',
              name: loadTimeData.getString('captionsColorGreen'),
            },
            {
              value: '0,0,255',
              name: loadTimeData.getString('captionsColorBlue'),
            },
            {
              value: '255,255,0',
              name: loadTimeData.getString('captionsColorYellow'),
            },
            {
              value: '0,255,255',
              name: loadTimeData.getString('captionsColorCyan'),
            },
            {
              value: '255,0,255',
              name: loadTimeData.getString('captionsColorMagenta'),
            },
          ];
        },
      },

      /**
       * List of fonts populated by the fonts browser proxy.
       */
      textFontOptions_: Object,

      /**
       * List of options for the text opacity drop-down menu.
       */
      textOpacityOptions_: {
        readOnly: true,
        type: Array,
        value() {
          return [
            {
              value: 100,  // Default
              name: loadTimeData.getString('captionsOpacityOpaque'),
            },
            {
              value: 50,
              name: loadTimeData.getString('captionsOpacitySemiTransparent'),
            },
            {
              value: 10,
              name: loadTimeData.getString('captionsOpacityTransparent'),
            },
          ];
        },
      },

      /**
       * List of options for the text shadow drop-down menu.
       *
       * Other clients are relying on these values to determine text shadow type
       * from preference. Please update the following files if any of these
       * values are changed:
       * https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/resources/ash/settings/os_a11y_page/captions_subpage.ts;l=142-170;drc=0918c7f73782a9575396f0c6b80a722b5a3d255a
       * https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ash/arc/intent_helper/arc_settings_service.cc;l=86-94;drc=a782e6ac5124014b8473c9e7e445d799624b532c
       */
      textShadowOptions_: {
        readOnly: true,
        type: Array,
        value() {
          return [
            {value: '', name: loadTimeData.getString('captionsTextShadowNone')},
            {
              value: '-2px -2px 4px rgba(0, 0, 0, 0.5)',
              name: loadTimeData.getString('captionsTextShadowRaised'),
            },
            {
              value: '2px 2px 4px rgba(0, 0, 0, 0.5)',
              name: loadTimeData.getString('captionsTextShadowDepressed'),
            },
            {
              value: '-1px 0px 0px black, ' +
                  '0px -1px 0px black, 1px 0px 0px black, 0px  1px 0px black',
              name: loadTimeData.getString('captionsTextShadowUniform'),
            },
            {
              value: '0px 0px 2px rgba(0, 0, 0, 0.5), 2px 2px 2px black',
              name: loadTimeData.getString('captionsTextShadowDropShadow'),
            },
          ];
        },
      },

      /**
       * List of options for the text size drop-down menu.
       */
      textSizeOptions_: {
        readOnly: true,
        type: Array,
        value() {
          return [
            {value: '25%', name: loadTimeData.getString('verySmall')},
            {value: '50%', name: loadTimeData.getString('small')},
            {
              value: '',
              name: loadTimeData.getString('medium'),
            },  // Default = 100%
            {value: '150%', name: loadTimeData.getString('large')},
            {value: '200%', name: loadTimeData.getString('veryLarge')},
          ];
        },
      },

      enableLiveCaption_: {
        type: Boolean,
        value: function() {
          return loadTimeData.getBoolean('enableLiveCaption');
        },
      },
    };
  }

  languages: LanguagesModel;
  languageHelper: LanguageHelper;
  private readonly backgroundOpacityOptions_: DropdownMenuOptionList;
  private readonly colorOptions_: DropdownMenuOptionList;
  private textFontOptions_: DropdownMenuOptionList;
  private readonly textOpacityOptions_: DropdownMenuOptionList;
  private readonly textShadowOptions_: DropdownMenuOptionList;
  private readonly textSizeOptions_: DropdownMenuOptionList;
  private enableLiveCaption_: boolean;

  override ready() {
    super.ready();
    FontsBrowserProxyImpl.getInstance().fetchFontsData().then(
        (response: FontsData) => this.setFontsData_(response));
  }

  /**
   * @return the Live Caption toggle element.
   */
  getLiveCaptionToggle(): SettingsToggleButtonElement|null {
    const liveCaptionSection =
        this.shadowRoot!.querySelector('settings-live-caption');
    return liveCaptionSection ? liveCaptionSection.getLiveCaptionToggle() :
                                null;
  }

  /**
   * @param response A list of fonts.
   */
  private setFontsData_(response: FontsData) {
    const fontMenuOptions =
        [{value: '', name: loadTimeData.getString('captionsDefaultSetting')}];
    for (const fontData of response.fontList) {
      fontMenuOptions.push({value: fontData[0], name: fontData[1]});
    }
    this.textFontOptions_ = fontMenuOptions;
  }

  /**
   * @return the font family as a CSS property value.
   */
  private getFontFamily_(): string {
    const fontFamily =
        this.getPref<string>('accessibility.captions.text_font').value;

    // Return the preference value or the default font family for
    // video::-webkit-media-text-track-container defined in mediaControls.css.
    return fontFamily || 'sans-serif';
  }

  /**
   * @return the background color as a RGBA string.
   */
  private computeBackgroundColor_(): string {
    const backgroundColor = this.formatRgaString_(
        'accessibility.captions.background_color',
        'accessibility.captions.background_opacity');

    // Return the preference value or the default background color for
    // video::cue defined in mediaControls.css.
    return backgroundColor || 'rgba(0, 0, 0, 0.8)';
  }

  /**
   * @return the text color as a RGBA string.
   */
  private computeTextColor_(): string {
    const textColor = this.formatRgaString_(
        'accessibility.captions.text_color',
        'accessibility.captions.text_opacity');

    // Return the preference value or the default text color for
    // video::-webkit-media-text-track-container defined in mediaControls.css.
    return textColor || 'rgba(255, 255, 255, 1)';
  }

  /**
   * Formats the color as an RGBA string.
   * @param colorPreference The name of the preference containing the RGB values
   *     as a comma-separated string.
   * @param opacityPreference The name of the preference containing the opacity
   *     value as a percentage.
   * @return The formatted RGBA string.
   */
  private formatRgaString_(colorPreference: string, opacityPreference: string):
      string {
    const color = this.getPref(colorPreference).value;

    if (!color) {
      return '';
    }

    return 'rgba(' + color + ',' +
        this.getPref<number>(opacityPreference).value / 100.0 + ')';
  }

  /**
   * @param size The font size of the captions text as a percentage.
   * @return The padding around the captions text as a percentage.
   */
  private computePadding_(size: string): string {
    if (size === '') {
      return '1%';
    }

    return `${+ size.slice(0, -1) / 100}%`;
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'settings-captions': SettingsCaptionsElement;
  }
}

customElements.define(SettingsCaptionsElement.is, SettingsCaptionsElement);