chromium/chrome/browser/resources/settings/site_settings/storage_access_site_list_entry.ts

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

/**
 * @fileoverview
 * 'storage-access-site-list-entry' is an element representing a group of
 * storage access permissions with the same origin and type of permission (e.g.
 * allowed, blocked).
 */
import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
import 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
import 'chrome://resources/cr_elements/cr_shared_style.css.js';
import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
import '../settings_shared.css.js';
import '../site_favicon.js';

import {FocusRowMixin} from 'chrome://resources/cr_elements/focus_row_mixin.js';
import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
import type {DomIf} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import {ContentSettingsTypes} from './constants.js';
import {SiteSettingsMixin} from './site_settings_mixin.js';
import type {StorageAccessEmbeddingException, StorageAccessSiteException} from './site_settings_prefs_browser_proxy.js';
import {getTemplate} from './storage_access_site_list_entry.html.js';
import type {StorageAccessStaticSiteListEntry} from './storage_access_static_site_list_entry.js';

const StorageAccessSiteListEntryElementBase =
    FocusRowMixin(SiteSettingsMixin(I18nMixin(PolymerElement)));

export class StorageAccessSiteListEntryElement extends
    StorageAccessSiteListEntryElementBase {
  static get is() {
    return 'storage-access-site-list-entry';
  }

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

  static get properties() {
    return {
      /**
       * A group of storage access site exceptions with the same `origin` and
       * `setting`.
       */
      model: {
        type: Object,
        observer: 'onModelChanged_',
      },

      /**
       * Signals if the expand button is opened or closed.
       */
      expanded_: {
        type: Boolean,
        observer: 'onExpandedChanged_',
        notify: true,
        value: false,
      },
    };
  }

  model: StorageAccessSiteException;

  private description_: string;
  private expandAriaLabel_: string;
  private expanded_: boolean;

  /**
   * Triggered when the top row reset button is clicked.
   * Resets all the permissions in `model.exceptions` i.e. all
   * permissions with the same origin.
   */
  private onResetAllButtonClick_() {
    for (const exception of this.model.exceptions) {
      this.browserProxy.resetCategoryPermissionForPattern(
          this.model.origin, exception.embeddingOrigin,
          ContentSettingsTypes.STORAGE_ACCESS, exception.incognito);
    }
  }

  /**
   * A handler for the model change.
   */
  private onModelChanged_() {
    this.description_ = this.computeDescription_();
    this.expandAriaLabel_ = this.computeExpandButtonAriaLabel_();
  }

  /**
   * A handler for clicking on the top-row. This will scroll to the
   * element if needed.
   */
  private onExpandedChanged_() {
    if (!this.shouldBeCollapsible_()) {
      return;
    }

    this.description_ = this.computeDescription_();
    this.expandAriaLabel_ = this.computeExpandButtonAriaLabel_();

    if (!this.expanded_) {
      return;
    }

    // Renders the nested rows if they haven't been opened before, so we can
    // scroll to make them visible if necessary.
    this.shadowRoot!.querySelector<DomIf>('#originList')!.render();

    this.scrollIntoViewIfNeeded();
  }

  private getResetAllButtonAriaLabel_() {
    return this.i18n('storageAccessResetAll', this.model.displayName);
  }

  private getResetButtonAriaLabel_(item: StorageAccessEmbeddingException) {
    return this.i18n(
        'storageAccessResetSite', this.model.displayName,
        item.embeddingDisplayName);
  }

  private computeExpandButtonAriaLabel_() {
    return this.expanded_ ? this.i18n('storageAccessCloseExpand') :
                            this.i18n('storageAccessOpenExpand');
  }

  /**
   * @returns the correct description according to the widget's state.
   */
  private computeDescription_(): string {
    if (!this.model || !this.model.openDescription ||
        !this.model.closeDescription) {
      return '';
    }

    return this.expanded_ ? this.model.openDescription :
                            this.model.closeDescription;
  }

  private shouldBeStatic_(): boolean {
    if (!this.model) {
      return false;
    }

    return this.model.exceptions.length === 0;
  }

  private shouldBeCollapsible_(): boolean {
    if (!this.model) {
      return false;
    }

    return this.model.exceptions.length !== 0;
  }

  private getStaticSiteEntryForModel_(): StorageAccessStaticSiteListEntry {
    return {
      faviconOrigin: this.model.origin,
      displayName: this.model.displayName,
      description: this.model.description,
      resetAriaLabel: this.getResetAllButtonAriaLabel_(),
      origin: this.model.origin,
      embeddingOrigin: '',
      incognito: this.model.incognito || false,
    };
  }

  private getStaticSiteEntryForException_(
      item: StorageAccessEmbeddingException): StorageAccessStaticSiteListEntry {
    return {
      faviconOrigin: item.embeddingOrigin,
      displayName: item.embeddingDisplayName,
      description: item.description,
      resetAriaLabel: this.getResetButtonAriaLabel_(item),
      origin: this.model.origin,
      embeddingOrigin: item.embeddingOrigin,
      incognito: item.incognito,
    };
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'storage-access-site-list-entry': StorageAccessSiteListEntryElement;
  }
}

customElements.define(
    StorageAccessSiteListEntryElement.is, StorageAccessSiteListEntryElement);