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

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

/**
 * @fileoverview
 * 'add-site-dialog' provides a dialog to add exceptions for a given Content
 * Settings category.
 */
import 'chrome://resources/cr_elements/cr_button/cr_button.js';
import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
import 'chrome://resources/cr_elements/cr_input/cr_input.js';
import '../settings_shared.css.js';

import type {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
import type {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
import type {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
import type {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
import {assert} from 'chrome://resources/js/assert.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import {loadTimeData} from '../i18n_setup.js';

import {getTemplate} from './add_site_dialog.html.js';
import {ContentSetting, ContentSettingsTypes, CookiesExceptionType, SITE_EXCEPTION_WILDCARD} from './constants.js';
import type {SiteSettingsMixinInterface} from './site_settings_mixin.js';
import {SiteSettingsMixin} from './site_settings_mixin.js';

export interface AddSiteDialogElement {
  $: {
    add: CrButtonElement,
    dialog: CrDialogElement,
    incognito: CrCheckboxElement,
    site: CrInputElement,
  };
}

const AddSiteDialogElementBase = SiteSettingsMixin(PolymerElement) as unknown as
    {new (): PolymerElement & SiteSettingsMixinInterface};

export class AddSiteDialogElement extends AddSiteDialogElementBase {
  static get is() {
    return 'add-site-dialog';
  }

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

  static get properties() {
    return {
      /**
       * Whether this is about an Allow, Block, SessionOnly, or other.
       */
      contentSetting: String,

      hasIncognito: {
        type: Boolean,
        observer: 'hasIncognitoChanged_',
      },

      /**
       * Controls what kind of patterns the created cookies exception will have
       * (based on the CookiesExceptionType):
       * - THIRD_PARTY: Exception that will have primary pattern as wildcard
       * (third-party cookie exceptions).
       * - SITE_DATA: Exception that will have secondary pattern as wildcard
       * (regular exceptions).
       * - COMBINED: Support both pattern types and have a checkbox to control
       * the mode.
       */
      cookiesExceptionType: String,

      /**
       * The site to add an exception for.
       */
      site_: String,

      /**
       * The error message to display when the pattern is invalid.
       */
      errorMessage_: String,
    };
  }

  contentSetting: ContentSetting;
  hasIncognito: boolean;
  private site_: string;
  private errorMessage_: string;
  cookiesExceptionType: CookiesExceptionType;

  override connectedCallback() {
    super.connectedCallback();

    assert(this.category);
    assert(this.contentSetting);
    assert(typeof this.hasIncognito !== 'undefined');

    this.$.dialog.showModal();
  }

  /**
   * Validates that the pattern entered is valid.
   */
  private validate_() {
    // If input is empty, disable the action button, but don't show the red
    // invalid message.
    if (this.$.site.value.trim() === '') {
      this.$.site.invalid = false;
      this.$.add.disabled = true;
      return;
    }

    this.browserProxy.isPatternValidForType(this.site_, this.category)
        .then(({isValid, reason}) => {
          this.$.site.invalid = !isValid;
          this.$.add.disabled = !isValid;
          this.errorMessage_ = reason || '';
        });
  }

  private onCancelClick_() {
    this.$.dialog.cancel();
  }

  /**
   * The tap handler for the Add [Site] button (adds the pattern and closes
   * the dialog).
   */
  private onSubmit_() {
    assert(!this.$.add.disabled);
    let primaryPattern = this.site_;
    let secondaryPattern = SITE_EXCEPTION_WILDCARD;

    if (this.cookiesExceptionType === CookiesExceptionType.THIRD_PARTY ||
        this.category === ContentSettingsTypes.TRACKING_PROTECTION) {
      primaryPattern = SITE_EXCEPTION_WILDCARD;
      secondaryPattern = this.site_;
    }

    this.browserProxy.setCategoryPermissionForPattern(
        primaryPattern, secondaryPattern, this.category, this.contentSetting,
        this.$.incognito.checked);

    this.$.dialog.close();
  }

  private showIncognitoSessionOnly_() {
    return this.hasIncognito && !loadTimeData.getBoolean('isGuest') &&
        this.contentSetting !== ContentSetting.SESSION_ONLY;
  }

  private hasIncognitoChanged_() {
    if (!this.hasIncognito) {
      this.$.incognito.checked = false;
    }
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'add-site-dialog': AddSiteDialogElement;
  }
}

customElements.define(AddSiteDialogElement.is, AddSiteDialogElement);