chromium/chrome/browser/resources/settings/safety_check_page/safety_check_child.ts

// Copyright 2020 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-safety-check-element' bundles functionality safety check elements
 * have in common. It is used by all safety check elements: parent, updates,
 * passwords, etc.
 */
import 'chrome://resources/cr_elements/cr_actionable_row_style.css.js';
import 'chrome://resources/cr_elements/cr_button/cr_button.js';
import 'chrome://resources/cr_elements/cr_shared_style.css.js';
import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
import '../settings_shared.css.js';

import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
import {assertNotReached} from 'chrome://resources/js/assert.js';
import {sanitizeInnerHtml} from 'chrome://resources/js/parse_html_subset.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';

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

/**
 * UI states a safety check child can be in. Defines the basic UI of the child.
 */
export enum SafetyCheckIconStatus {
  RUNNING = 0,
  SAFE = 1,
  INFO = 2,
  WARNING = 3,
  NOTIFICATION_PERMISSIONS = 4,
  UNUSED_SITE_PERMISSIONS = 5,
  EXTENSIONS_REVIEW = 6,
}

const SettingsSafetyCheckChildElementBase = I18nMixin(PolymerElement);

export class SettingsSafetyCheckChildElement extends
    SettingsSafetyCheckChildElementBase {
  static get is() {
    return 'settings-safety-check-child';
  }

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

  static get properties() {
    return {
      /**
       * Status of the left hand icon.
       */
      iconStatus: {
        type: Number,
        value: SafetyCheckIconStatus.RUNNING,
      },

      // Primary label of the child.
      label: String,

      // Secondary label of the child.
      subLabel: String,

      // Text of the right hand button. |null| removes it from the DOM.
      buttonLabel: String,

      // Aria label of the right hand button.
      buttonAriaLabel: String,

      // Classes of the right hand button.
      buttonClass: String,

      // Icon for the right hand button.
      buttonIcon: String,

      // Should the entire row be clickable.
      rowClickable: {
        type: Boolean,
        value: false,
        reflectToAttribute: true,
        observer: 'onRowClickableChanged_',
      },

      // Is the row directed to external link.
      external: {
        type: Boolean,
        value: false,
      },

      rowClickableIcon_: {
        type: String,
        computed: 'computeRowClickableIcon_(external)',
      },

      // Right hand managed icon. |null| removes it from the DOM.
      managedIcon: String,
    };
  }

  iconStatus: SafetyCheckIconStatus;
  label: string;
  subLabel: string;
  buttonLabel: string;
  buttonAriaLabel: string;
  buttonIcon: string;
  buttonClass: string;
  rowClickable: boolean;
  external: boolean;
  private rowClickableIcon_: string;
  managedIcon: string;

  /** @return The left hand icon for an icon status. */
  private getStatusIcon_(): string|null {
    switch (this.iconStatus) {
      case SafetyCheckIconStatus.RUNNING:
        return null;
      case SafetyCheckIconStatus.SAFE:
        return 'cr:check';
      case SafetyCheckIconStatus.INFO:
        return 'cr:info';
      case SafetyCheckIconStatus.WARNING:
        return 'cr:warning';
      case SafetyCheckIconStatus.NOTIFICATION_PERMISSIONS:
        return 'settings:notifications-none';
      case SafetyCheckIconStatus.UNUSED_SITE_PERMISSIONS:
        return 'cr:info-outline';
      case SafetyCheckIconStatus.EXTENSIONS_REVIEW:
        return 'cr:extension';
      default:
        assertNotReached();
    }
  }

  /** @return The left hand icon src for an icon status. */
  private getStatusIconSrc_(): string|null {
    if (this.iconStatus === SafetyCheckIconStatus.RUNNING) {
      return 'chrome://resources/images/throbber_small.svg';
    }
    return null;
  }

  /** @return The left hand icon class for an icon status. */
  private getStatusIconClass_(): string {
    switch (this.iconStatus) {
      case SafetyCheckIconStatus.RUNNING:
      case SafetyCheckIconStatus.SAFE:
        return 'icon-blue';
      case SafetyCheckIconStatus.WARNING:
        return 'icon-red';
      default:
        return '';
    }
  }

  /** @return The left hand icon aria label for an icon status. */
  private getStatusIconAriaLabel_(): string|undefined {
    switch (this.iconStatus) {
      case SafetyCheckIconStatus.RUNNING:
        return this.i18n('safetyCheckIconRunningAriaLabel');
      case SafetyCheckIconStatus.SAFE:
        return this.i18n('safetyCheckIconSafeAriaLabel');
      case SafetyCheckIconStatus.INFO:
        return this.i18n('safetyCheckIconInfoAriaLabel');
      case SafetyCheckIconStatus.WARNING:
        return this.i18n('safetyCheckIconWarningAriaLabel');
      case SafetyCheckIconStatus.NOTIFICATION_PERMISSIONS:
      case SafetyCheckIconStatus.UNUSED_SITE_PERMISSIONS:
      case SafetyCheckIconStatus.EXTENSIONS_REVIEW:
        return undefined;
      default:
        assertNotReached();
    }
  }

  /** @return Whether right-hand side button should be shown. */
  private showButton_(): boolean {
    return !!this.buttonLabel;
  }

  private onButtonClick_() {
    this.dispatchEvent(
        new CustomEvent('button-click', {bubbles: true, composed: true}));
  }

  /** @return Whether the right-hand side managed icon should be shown. */
  private showManagedIcon_(): boolean {
    return !!this.managedIcon;
  }

  /** @return Whether the right-hand side button icon should be shown. */
  private showButtonIcon_(): boolean {
    return !!this.buttonIcon;
  }

  /** @return The icon to show when the row is clickable. */
  private computeRowClickableIcon_(): string {
    return this.external ? 'cr:open-in-new' : 'cr:arrow-right';
  }

  /** @return The subpage role description if the arrow right icon is used. */
  private getRoleDescription_(): string {
    return this.rowClickableIcon_ === 'cr:arrow-right' ?
        this.i18n('subpageArrowRoleDescription') :
        '';
  }

  private onRowClickableChanged_() {
    // For cr-actionable-row-style.
    this.toggleAttribute('effectively-disabled_', !this.rowClickable);
  }

  private sanitizeInnerHtml_(rawString: string): TrustedHTML {
    return sanitizeInnerHtml(rawString);
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'settings-safety-check-child': SettingsSafetyCheckChildElement;
  }
}

customElements.define(
    SettingsSafetyCheckChildElement.is, SettingsSafetyCheckChildElement);