chromium/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_app.ts

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

import 'chrome://resources/cr_elements/cr_button/cr_button.js';
import 'chrome://resources/cr_elements/cr_expand_button/cr_expand_button.js';
import 'chrome://resources/cr_elements/cr_collapse/cr_collapse.js';
import 'chrome://resources/cr_elements/cr_shared_style.css.js';
import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
import './strings.m.js';

import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {afterNextRender, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import {getTemplate} from './privacy_sandbox_dialog_app.html.js';
import {PrivacySandboxDialogBrowserProxy, PrivacySandboxPromptAction} from './privacy_sandbox_dialog_browser_proxy.js';

export interface PrivacySandboxDialogAppElement {
  $: {
    contentArea: HTMLElement,
    expandSection: HTMLElement,
  };
}

export class PrivacySandboxDialogAppElement extends PolymerElement {
  static get is() {
    return 'privacy-sandbox-dialog-app';
  }

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

  static get properties() {
    return {
      expanded_: {
        type: Boolean,
        observer: 'onLearnMoreExpandedChanged_',
      },

      isConsent_: {
        type: Boolean,
        value: () => {
          return loadTimeData.getBoolean('isConsent');
        },
      },

      canScrollClass_: String,
      fitIntoDialogClass_: String,
    };
  }

  private expanded_: boolean;
  private isConsent_: boolean;
  private canScrollClass_: string;
  private fitIntoDialogClass_: string;
  private didStartWithScrollbar_: boolean;

  override connectedCallback() {
    super.connectedCallback();

    afterNextRender(this, async () => {
      const proxy = PrivacySandboxDialogBrowserProxy.getInstance();
      // Prefer using |document.body.offsetHeight| instead of
      // |document.body.scrollHeight| as it returns the correct height of the
      // page even when the page zoom in Chrome is different than 100%.
      await proxy.resizeDialog(document.body.offsetHeight);

      // After the content was rendered at size it requires, toggle a class
      // to fit the content into dialog bounds.
      this.fitIntoDialogClass_ = 'fit-into-size';

      // After the layout is adjusted to fit into the dialog, save if the
      // dialog is scrollable and add a divider if needed.
      this.didStartWithScrollbar_ =
          this.$.contentArea.offsetHeight < this.$.contentArea.scrollHeight;
      this.canScrollClass_ = this.didStartWithScrollbar_ ? 'can-scroll' : '';

      proxy.showDialog();
    });

    window.addEventListener('keydown', event => {
      // Only notice dialog can be dismissed by pressing "Esc".
      if (event.key === 'Escape' && !this.isConsent_) {
        this.promptActionOccurred(PrivacySandboxPromptAction.NOTICE_DISMISS);
      }
    });
  }

  private onNoticeOpenSettings_() {
    this.promptActionOccurred(PrivacySandboxPromptAction.NOTICE_OPEN_SETTINGS);
  }

  private onNoticeAcknowledge_() {
    this.promptActionOccurred(PrivacySandboxPromptAction.NOTICE_ACKNOWLEDGE);
  }

  private onConsentAccepted_() {
    this.promptActionOccurred(PrivacySandboxPromptAction.CONSENT_ACCEPTED);
  }

  private onConsentDeclined_() {
    this.promptActionOccurred(PrivacySandboxPromptAction.CONSENT_DECLINED);
  }

  private onLearnMoreExpandedChanged_(newVal: boolean, oldVal: boolean) {
    if (!oldVal && newVal) {
      this.promptActionOccurred(
          PrivacySandboxPromptAction.CONSENT_MORE_INFO_OPENED);
    }
    if (oldVal && !newVal) {
      this.promptActionOccurred(
          PrivacySandboxPromptAction.CONSENT_MORE_INFO_CLOSED);
    }
    // Show divider if the dialog was scrollable from the beginning or became
    // scrollable because the section is expanded. Otherwise, hide the
    // scrollbar to avoid animating it out. Without it, when the section is
    // collapsing, the scrollbar thumb would grow until it fills the track and
    // disappears.
    this.canScrollClass_ =
        this.didStartWithScrollbar_ || newVal ? 'can-scroll' : 'hide-scrollbar';

    // Wait for collapse section transition to complete 70%.
    const collapseElement = this.$.expandSection.querySelector('cr-collapse');
    if (collapseElement) {
      const computedStyle = window.getComputedStyle(collapseElement);
      const duration = parseFloat(computedStyle.getPropertyValue(
          '--iron-collapse-transition-duration'));
      setTimeout(() => {
        // ...and scroll the content area up to make the section content
        // visible.
        const rect = this.$.expandSection.getBoundingClientRect();
        this.$.contentArea.scrollTo({top: rect.top, behavior: 'smooth'});
      }, duration * 0.7);
    }
  }

  private promptActionOccurred(action: PrivacySandboxPromptAction) {
    PrivacySandboxDialogBrowserProxy.getInstance().promptActionOccurred(action);
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'privacy-sandbox-dialog-app': PrivacySandboxDialogAppElement;
  }
}

customElements.define(
    PrivacySandboxDialogAppElement.is, PrivacySandboxDialogAppElement);