chromium/chrome/browser/resources/ash/settings/os_printing_page/cups_add_printer_manufacturer_model_dialog.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.

/**
 * @fileoverview
 * 'add-printer-manufacturer-model-dialog' is a dialog in which the user can
 *   manually select the manufacture and model of the new printer.
 */
import 'chrome://resources/ash/common/cr_elements/localized_link/localized_link.js';
import 'chrome://resources/ash/common/cr_elements/cr_button/cr_button.js';
import 'chrome://resources/ash/common/cr_elements/cr_input/cr_input.js';
import './cups_add_printer_dialog.js';
import './cups_printer_dialog_error.js';
import './cups_printer_shared.css.js';
import './cups_printers_browser_proxy.js';

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

import {getTemplate} from './cups_add_printer_manufacturer_model_dialog.html.js';
import {getBaseName, getErrorText, isPPDInfoValid} from './cups_printer_dialog_util.js';
import {CupsPrinterInfo, CupsPrintersBrowserProxy, CupsPrintersBrowserProxyImpl, ManufacturersInfo, ModelsInfo, PrinterSetupResult} from './cups_printers_browser_proxy.js';

export class AddPrinterManufacturerModelDialogElement extends PolymerElement {
  static get is() {
    return 'add-printer-manufacturer-model-dialog';
  }

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

  static get properties() {
    return {
      activePrinter: {
        type: Object,
        notify: true,
      },

      manufacturerList: Array,

      modelList: Array,

      /**
       * Whether the user selected PPD file is valid.
       */
      invalidPPD_: {
        type: Boolean,
        value: false,
      },

      /**
       * The base name of a newly selected PPD file.
       */
      newUserPPD_: String,

      /**
       * The URL to a printer's EULA.
       */
      eulaUrl_: {
        type: String,
        value: '',
      },

      addPrinterInProgress_: {
        type: Boolean,
        value: false,
      },

      /**
       * The error text to be displayed on the dialog.
       */
      errorText_: {
        type: String,
        value: '',
      },

      /**
       * Indicates whether the value in the Manufacturer dropdown is a valid
       * printer manufacturer.
       */
      isManufacturerInvalid_: Boolean,

      /**
       * Indicates whether the value in the Model dropdown is a valid printer
       * model.
       */
      isModelInvalid_: Boolean,
    };
  }

  static get observers() {
    return [
      'selectedManufacturerChanged_(activePrinter.ppdManufacturer)',
      'selectedModelChanged_(activePrinter.ppdModel)',

    ];
  }

  activePrinter: CupsPrinterInfo;
  manufacturerList: string[];
  modelList: string[];

  private addPrinterInProgress_: boolean;
  private browserProxy_: CupsPrintersBrowserProxy;
  private errorText_: string;
  private eulaUrl_: string;
  private invalidPPD_: boolean;
  private newUserPPD_: string;

  constructor() {
    super();

    this.browserProxy_ = CupsPrintersBrowserProxyImpl.getInstance();
  }

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

    this.browserProxy_.getCupsPrinterManufacturersList().then(
        this.manufacturerListChanged_.bind(this));
  }

  private close(): void {
    this.shadowRoot!.querySelector('add-printer-dialog')!.close();
  }

  private onPrinterAddedSucceeded_(result: PrinterSetupResult): void {
    this.recordAddPrinterResult(/*success=*/ true);
    const showCupsPrinterToastEvent =
        new CustomEvent('show-cups-printer-toast', {
          bubbles: true,
          composed: true,
          detail:
              {resultCode: result, printerName: this.activePrinter.printerName},
        });
    this.dispatchEvent(showCupsPrinterToastEvent);
    this.close();
  }

  /**
   * Handler for addCupsPrinter failure.
   */
  private onPrinterAddedFailed_(result: PrinterSetupResult): void {
    this.recordAddPrinterResult(/*success=*/ false);
    this.addPrinterInProgress_ = false;
    this.errorText_ = getErrorText(result);
  }

  /**
   * If the printer is a nearby printer, return make + model with the subtext.
   * Otherwise, return printer name.
   */
  private getManufacturerAndModelSubtext_(): string {
    if (this.activePrinter.printerMakeAndModel) {
      return loadTimeData.getStringF(
          'manufacturerAndModelAdditionalInformation',
          this.activePrinter.printerMakeAndModel);
    }
    return loadTimeData.getStringF(
        'manufacturerAndModelAdditionalInformation',
        this.activePrinter.printerName);
  }

  private selectedManufacturerChanged_(manufacturer: string): void {
    // Reset model if manufacturer is changed.
    this.set('activePrinter.ppdModel', '');
    this.modelList = [];
    if (manufacturer && manufacturer.length !== 0) {
      this.browserProxy_.getCupsPrinterModelsList(manufacturer)
          .then(this.modelListChanged_.bind(this));
    }
  }

  /**
   * Attempts to get the EULA Url if the selected printer has one.
   */
  private selectedModelChanged_(): void {
    this.errorText_ = '';
    if (!this.activePrinter.ppdManufacturer || !this.activePrinter.ppdModel) {
      // Do not check for an EULA unless both |ppdManufacturer| and |ppdModel|
      // are set. Set |eulaUrl_| to be empty in this case.
      this.onGetEulaUrlCompleted_('' /* eulaUrl */);
      return;
    }

    this.browserProxy_
        .getEulaUrl(
            this.activePrinter.ppdManufacturer, this.activePrinter.ppdModel)
        .then(this.onGetEulaUrlCompleted_.bind(this));
  }

  private onGetEulaUrlCompleted_(eulaUrl: string): void {
    this.eulaUrl_ = eulaUrl;
  }

  private manufacturerListChanged_(manufacturersInfo: ManufacturersInfo): void {
    if (!manufacturersInfo.success) {
      return;
    }
    this.manufacturerList = manufacturersInfo.manufacturers;
    if (this.activePrinter.ppdManufacturer.length !== 0) {
      this.browserProxy_
          .getCupsPrinterModelsList(this.activePrinter.ppdManufacturer)
          .then(this.modelListChanged_.bind(this));
    }
  }

  private modelListChanged_(modelsInfo: ModelsInfo): void {
    if (modelsInfo.success) {
      this.modelList = modelsInfo.models;
    }
  }

  private onBrowseFile_(): void {
    this.browserProxy_.getCupsPrinterPpdPath().then(
        this.printerPpdPathChanged_.bind(this));
  }


  /**
   * @param path The full path to the selected PPD file
   */
  private printerPpdPathChanged_(path: string): void {
    this.set('activePrinter.printerPPDPath', path);
    this.invalidPPD_ = !path;
    this.newUserPPD_ = getBaseName(path);
  }

  private onCancelClick_(): void {
    this.close();
    this.browserProxy_.cancelPrinterSetUp(this.activePrinter);
  }

  private addPrinter_(): void {
    this.addPrinterInProgress_ = true;
    this.browserProxy_.addCupsPrinter(this.activePrinter)
        .then(
            this.onPrinterAddedSucceeded_.bind(this),
            this.onPrinterAddedFailed_.bind(this));
  }

  /**
   * @return Whether we have enough information to set up the printer
   */
  private canAddPrinter_(
      ppdManufacturer: string, ppdModel: string, printerPPDPath: string,
      addPrinterInProgress: boolean, isManufacturerInvalid: boolean,
      isModelInvalid: boolean): boolean {
    return !addPrinterInProgress &&
        isPPDInfoValid(ppdManufacturer, ppdModel, printerPPDPath) &&
        !isManufacturerInvalid && !isModelInvalid;
  }

  private recordAddPrinterResult(success: boolean): void {
    chrome.metricsPrivate.recordBoolean(
        'Printing.CUPS.AddPrinterManuallyResult', success);
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'add-printer-manufacturer-model-dialog':
        AddPrinterManufacturerModelDialogElement;
  }
}

customElements.define(
    AddPrinterManufacturerModelDialogElement.is,
    AddPrinterManufacturerModelDialogElement);