chromium/ash/webui/diagnostics_ui/resources/connectivity_card.ts

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

import './diagnostics_card.js';
import './diagnostics_network_icon.js';
import './diagnostics_shared.css.js';
import './ip_config_info_drawer.js';
import './network_info.js';
import './routine_section.js';

import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
import {assert} from 'chrome://resources/js/assert.js';
import {PolymerElementProperties} from 'chrome://resources/polymer/v3_0/polymer/interfaces.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import {getTemplate} from './connectivity_card.html.js';
import {filterNameServers, formatMacAddress, getNetworkCardTitle, getNetworkState, getNetworkType, getRoutineGroups} from './diagnostics_utils.js';
import {getNetworkHealthProvider} from './mojo_interface_provider.js';
import {Network, NetworkHealthProviderInterface, NetworkStateObserverReceiver} from './network_health_provider.mojom-webui.js';
import {RoutineGroup} from './routine_group.js';
import {TestSuiteStatus} from './routine_list_executor.js';
import {RoutineSectionElement} from './routine_section.js';

/**
 * @fileoverview
 * 'connectivity-card' runs network routines and displays network health data.
 */

const ConnectivityCardElementBase = I18nMixin(PolymerElement);

export class ConnectivityCardElement extends ConnectivityCardElementBase {
  static get is(): 'connectivity-card' {
    return 'connectivity-card' as const;
  }

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

  static get properties(): PolymerElementProperties {
    return {
      testSuiteStatus: {
        type: Number,
        value: TestSuiteStatus.NOT_RUNNING,
        notify: true,
      },

      routineGroups: {
        type: Array,
        value: () => [],
      },

      activeGuid: {
        type: String,
        value: '',
        observer: ConnectivityCardElement.prototype.activeGuidChanged,
      },

      isActive: {
        type: Boolean,
        observer: ConnectivityCardElement.prototype.isActiveChanged,
      },

      network: {
        type: Object,
      },

      networkType: {
        type: String,
        value: '',
      },

      networkState: {
        type: String,
        value: '',
      },

      macAddress: {
        type: String,
        value: '',
      },

    };
  }

  testSuiteStatus: TestSuiteStatus;
  activeGuid: string;
  isActive: boolean;
  network: Network;
  protected macAddress: string;
  private networkType: string;
  private networkState: string;
  private routineGroups: RoutineGroup[];
  private networkHealthProvider: NetworkHealthProviderInterface =
      getNetworkHealthProvider();
  private networkStateObserverReceiver: NetworkStateObserverReceiver|null =
      null;

  private getRoutineSectionElem(): RoutineSectionElement {
    const routineSection = this.shadowRoot!.querySelector('routine-section');
    assert(routineSection);
    return routineSection;
  }

  override disconnectedCallback(): void {
    super.disconnectedCallback();

    this.getRoutineSectionElem().stopTests();
  }

  getRoutineGroupsForTesting(): RoutineGroup[] {
    return this.routineGroups;
  }

  protected hasRoutines(): boolean {
    return this.routineGroups && this.routineGroups.length > 0;
  }

  private observeNetwork(): void {
    if (this.networkStateObserverReceiver) {
      this.networkStateObserverReceiver.$.close();
      this.networkStateObserverReceiver = null;
    }

    this.networkStateObserverReceiver = new NetworkStateObserverReceiver(this);

    this.networkHealthProvider.observeNetwork(
        this.networkStateObserverReceiver.$.bindNewPipeAndPassRemote(),
        this.activeGuid);
  }

  /**
   * Implements NetworkStateObserver.onNetworkStateChanged
   */
  onNetworkStateChanged(network: Network): void {
    this.networkType = getNetworkType(network.type);
    this.networkState = getNetworkState(network.state);
    this.macAddress = network.macAddress || '';

    if (this.testSuiteStatus === TestSuiteStatus.NOT_RUNNING) {
      this.routineGroups = getRoutineGroups(network.type);
      this.getRoutineSectionElem().runTests();
    }

    // Remove '0.0.0.0' (if present) from list of name servers.
    filterNameServers(network);
    this.set('network', network);
  }

  protected getEstimateRuntimeInMinutes(): 1 {
    // Connectivity routines will always last <= 1 minute.
    return 1;
  }

  protected getNetworkCardTitle(): string {
    return getNetworkCardTitle(this.networkType, this.networkState);
  }

  protected activeGuidChanged(activeGuid: string): void {
    if (this.testSuiteStatus === TestSuiteStatus.COMPLETED) {
      this.testSuiteStatus = TestSuiteStatus.NOT_RUNNING;
    }

    if (!activeGuid) {
      return;
    }
    this.getRoutineSectionElem().stopTests();
    this.observeNetwork();
  }

  protected isActiveChanged(active: boolean): void {
    if (!active) {
      return;
    }

    if (this.routineGroups.length > 0) {
      this.getRoutineSectionElem().runTests();
    }
  }

  protected getMacAddress(): string {
    if (!this.macAddress) {
      return '';
    }
    return formatMacAddress(this.macAddress);
  }

  getRoutineSectionElemForTesting(): RoutineSectionElement {
    return this.getRoutineSectionElem();
  }
}

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

customElements.define(ConnectivityCardElement.is, ConnectivityCardElement);