chromium/chrome/browser/resources/ash/settings/internet_page/network_proxy_section.ts

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

/**
 * @fileoverview Polymer element hosting <network-proxy> in the internet
 * detail page. This element is responsible for setting 'Allow proxies for
 * shared networks'.
 */

import 'chrome://resources/ash/common/network/cr_policy_network_indicator_mojo.js';
import 'chrome://resources/ash/common/network/network_proxy.js';
import 'chrome://resources/ash/common/cr_elements/cr_button/cr_button.js';
import 'chrome://resources/ash/common/cr_elements/cr_dialog/cr_dialog.js';
import 'chrome://resources/ash/common/cr_elements/cr_hidden_style.css.js';
import 'chrome://resources/ash/common/cr_elements/cr_toggle/cr_toggle.js';
import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
import '../controls/extension_controlled_indicator.js';
import '../settings_vars.css.js';
import './internet_shared.css.js';
import '../controls/settings_toggle_button.js';
import '../common/lacros_extension_controlled_indicator.js';

import {PrefsMixin, PrefsMixinInterface} from '/shared/settings/prefs/prefs_mixin.js';
import {CrDialogElement} from 'chrome://resources/ash/common/cr_elements/cr_dialog/cr_dialog.js';
import {CrToggleElement} from 'chrome://resources/ash/common/cr_elements/cr_toggle/cr_toggle.js';
import {I18nMixin, I18nMixinInterface} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
import {CrPolicyNetworkBehaviorMojo, CrPolicyNetworkBehaviorMojoInterface} from 'chrome://resources/ash/common/network/cr_policy_network_behavior_mojo.js';
import {NetworkProxyElement} from 'chrome://resources/ash/common/network/network_proxy.js';
import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js';
import {ManagedProperties, ManagedString} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
import {OncSource} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
import {mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import {RouteObserverMixin, RouteObserverMixinInterface} from '../common/route_observer_mixin.js';
import {Constructor} from '../common/types.js';
import {SettingsToggleButtonElement} from '../controls/settings_toggle_button.js';
import {Route, routes} from '../router.js';

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

interface ExtensionInfo {
  name: string|undefined;
  id: string|undefined;
  canBeDisabled: boolean|undefined;
}

export interface NetworkProxySectionElement {
  $: {
    allowShared: SettingsToggleButtonElement,
    confirmAllowSharedDialog: CrDialogElement,
  };
}

const NetworkProxySectionElementBase =
    mixinBehaviors(
        [
          CrPolicyNetworkBehaviorMojo,
        ],
        PrefsMixin(RouteObserverMixin(I18nMixin(PolymerElement)))) as
    Constructor<PolymerElement&I18nMixinInterface&RouteObserverMixinInterface&
                PrefsMixinInterface&CrPolicyNetworkBehaviorMojoInterface>;

export class NetworkProxySectionElement extends NetworkProxySectionElementBase {
  static get is() {
    return 'network-proxy-section' as const;
  }

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

  static get properties() {
    return {
      disabled: {
        type: Boolean,
        value: false,
      },

      managedProperties: Object,

      /**
       * Reflects prefs.settings.use_shared_proxies for data binding.
       */
      useSharedProxies_: Boolean,

      /**
       * Indicates if the proxy if set by an extension in the Lacros primary
       * profile.
       */
      isProxySetByLacrosExtension_: Boolean,

      /**
       * Information about the extension in the Ash or Lacros browser which
       * controlling the proxy. Can be null is the proxy is not controlled by an
       * extension.
       */
      extensionInfo_: Object,
    };
  }

  static get observers() {
    return [
      'useSharedProxiesChanged_(prefs.settings.use_shared_proxies.value)',
      'extensionProxyChanged_(prefs.ash.lacros_proxy_controlling_extension, ' +
          'managedProperties.proxySettings)',
    ];
  }

  disabled: boolean;
  managedProperties: ManagedProperties|undefined;
  private extensionInfo_: ExtensionInfo|undefined;
  private isProxySetByLacrosExtension_: boolean;
  private useSharedProxies_: boolean;

  /**
   * Returns the allow shared CrToggleElement.
   */
  getAllowSharedToggle(): CrToggleElement|null {
    return this.shadowRoot!.querySelector<CrToggleElement>('#allowShared');
  }

  override currentRouteChanged(newRoute: Route): void {
    if (newRoute === routes.NETWORK_DETAIL) {
      this.shadowRoot!.querySelector<NetworkProxyElement>(
                          'network-proxy')!.reset();
    }
  }

  private useSharedProxiesChanged_(): void {
    const pref = this.getPref('settings.use_shared_proxies');
    this.useSharedProxies_ = !!pref && !!pref.value;
  }

  private extensionProxyChanged_(): void {
    if (this.proxySetByAshExtension_()) {
      return;
    }

    const property = this.getProxySettingsTypeProperty_();
    if (!property || !this.isExtensionControlled(property)) {
      this.isProxySetByLacrosExtension_ = false;
      return;
    }

    const pref = this.getPref('ash.lacros_proxy_controlling_extension');
    this.isProxySetByLacrosExtension_ = !!pref.value &&
        !!pref.value['extension_id_key'] && !!pref.value['extension_name_key'];
    if (this.isProxySetByLacrosExtension_) {
      this.extensionInfo_ = {
        id: pref.value['extension_id_key'],
        name: pref.value['extension_name_key'],
        canBeDisabled: pref.value['can_be_disabled_key'],
      };
    }
  }

  private proxySetByAshExtension_(): boolean {
    const property = this.getProxySettingsTypeProperty_();
    if (!property || !this.isExtensionControlled(property) ||
        !this.prefs.proxy.controlledByName) {
      return false;
    }
    this.extensionInfo_ = {
      id: this.prefs.proxy.extensionId,
      name: this.prefs.proxy.controlledByName,
      canBeDisabled: this.prefs.proxy.extensionCanBeDisabled,
    };
    return true;
  }

  /**
   * Return true if the proxy is controlled by an extension in the Ash Browser
   * or in the Lacros Browser.
   */
  private isProxySetByExtension_(): boolean {
    return this.proxySetByAshExtension_() || this.isProxySetByLacrosExtension_;
  }

  private isShared_(): boolean {
    return this.managedProperties!.source === OncSource.kDevice ||
        this.managedProperties!.source === OncSource.kDevicePolicy;
  }

  private getProxySettingsTypeProperty_(): ManagedString|undefined {
    if (!this.managedProperties) {
      return undefined;
    }
    const proxySettings = this.managedProperties.proxySettings;
    return proxySettings ? proxySettings.type : undefined;
  }

  private getAllowSharedDialogTitle_(allowShared: boolean): string {
    if (allowShared) {
      return this.i18n('networkProxyAllowSharedDisableWarningTitle');
    }
    return this.i18n('networkProxyAllowSharedEnableWarningTitle');
  }

  private shouldShowNetworkPolicyIndicator_(): boolean {
    const property = this.getProxySettingsTypeProperty_();
    return !!property && !this.isProxySetByExtension_() &&
        this.isNetworkPolicyEnforced(property);
  }

  private shouldShowAllowShared_(_property: OncMojo.ManagedProperty): boolean {
    if (!this.isShared_()) {
      return false;
    }
    // We currently do not accurately determine the source if the policy
    // controlling the proxy setting, so always show the 'allow shared'
    // toggle for shared networks. http://crbug.com/662529.
    return true;
  }

  /**
   * Handles the change event for the shared proxy checkbox. Shows a
   * confirmation dialog.
   */
  private onAllowSharedProxiesChange_(): void {
    this.$.confirmAllowSharedDialog.showModal();
  }

  /**
   * Handles the shared proxy confirmation dialog 'Confirm' button.
   */
  private onAllowSharedDialogConfirm_(): void {
    this.$.allowShared.sendPrefChange();
    this.$.confirmAllowSharedDialog.close();
  }

  /**
   * Handles the shared proxy confirmation dialog 'Cancel' button or a cancel
   * event.
   */
  private onAllowSharedDialogCancel_(): void {
    this.$.allowShared.resetToPrefValue();
    this.$.confirmAllowSharedDialog.close();
  }

  private onAllowSharedDialogClose_(): void {
    this.$.allowShared.focus();
  }
}

declare global {
  interface HTMLElementTagNameMap {
    [NetworkProxySectionElement.is]: NetworkProxySectionElement;
  }
}

customElements.define(
    NetworkProxySectionElement.is, NetworkProxySectionElement);