910e62b5创建于 1月15日历史提交
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/printing/local_printer_impl.h"

#include <vector>

#include "ash/constants/ash_features.h"
#include "base/logging.h"
#include "chrome/browser/ash/printing/cups_printers_manager.h"
#include "chrome/browser/ash/printing/cups_printers_manager_factory.h"
#include "chrome/browser/ash/printing/oauth2/authorization_zones_manager.h"
#include "chrome/browser/ash/printing/oauth2/authorization_zones_manager_factory.h"
#include "chrome/browser/ash/printing/printer_authenticator.h"
#include "chrome/browser/ash/printing/printer_setup_util.h"
#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
#include "components/account_id/account_id.h"

namespace ash {

namespace {

LocalPrinter* g_instance = nullptr;

std::vector<chromeos::Printer> GetLocalPrinters(const AccountId& accountId) {
  static constexpr chromeos::PrinterClass printer_classes_to_fetch[] = {
      chromeos::PrinterClass::kSaved, chromeos::PrinterClass::kEnterprise,
      chromeos::PrinterClass::kAutomatic, chromeos::PrinterClass::kDiscovered};
  ash::CupsPrintersManager* printers_manager =
      ash::CupsPrintersManagerFactory::GetForBrowserContext(
          ash::BrowserContextHelper::Get()->GetBrowserContextByAccountId(
              accountId));
  std::vector<chromeos::Printer> printers;
  for (chromeos::PrinterClass pc : printer_classes_to_fetch) {
    for (const chromeos::Printer& p : printers_manager->GetPrinters(pc)) {
      VLOG(1) << "Found printer " << p.display_name() << " with device name "
              << p.id();
      printers.push_back(p);
    }
  }

  return printers;
}

// Mark if a not yet installed printer is autoconf then continue with setup.
void OnPrinterQueriedForAutoConf(ash::CupsPrintersManager* printers_manager,
                                 LocalPrinter::GetCapabilityCallback callback,
                                 chromeos::Printer printer,
                                 bool is_printer_autoconf,
                                 const chromeos::IppPrinterInfo& info) {
  if (!is_printer_autoconf) {
    std::move(callback).Run(std::nullopt);
    return;
  }

  printer.mutable_ppd_reference()->autoconf = true;
  printer.set_ipp_printer_info(info);
  printing::SetUpPrinter(printers_manager, printer, std::move(callback));
}

// Query the printer for setup metrics then continue with setup.
void OnPrinterQueriedForAutoConfMetricsOnly(
    ash::CupsPrintersManager* printers_manager,
    LocalPrinter::GetCapabilityCallback callback,
    chromeos::Printer printer,
    bool is_printer_autoconf,
    const chromeos::IppPrinterInfo& info) {
  printer.set_ipp_printer_info(info);
  printing::SetUpPrinter(printers_manager, printer, std::move(callback));
}

// This function is called when user's rights to access the printer were
// verified. The user can use the printer <=> `status` == StatusCode::kOK.
// Other values of `status` mean that the access was denied or an error
// occurred. The function is supposed to set-up the printer <=> the access was
// granted. The first parameter is used only for keep the pointer alive until
// this callback is executed.
void OnPrinterAuthenticated(
    std::unique_ptr<ash::printing::PrinterAuthenticator> /* authenticator */,
    ash::CupsPrintersManager* printers_manager,
    const chromeos::Printer& printer,
    LocalPrinter::GetCapabilityCallback callback,
    ash::printing::oauth2::StatusCode status,
    std::string /* access_token */) {
  if (status != ash::printing::oauth2::StatusCode::kOK) {
    // An error occurred.
    std::move(callback).Run(std::nullopt);
    return;
  }

  // In order for an IPP printer to be valid for set up, it needs to either be
  // previously installed, be autoconf compatible, or have a valid PPD
  // reference. If necessary, the printer is queried to determine its autoconf
  // compatibility.
  if (!printers_manager->IsPrinterInstalled(printer)) {
    if (!printer.HasUri()) {
      std::move(callback).Run(std::nullopt);
      return;
    }

    // If the printer is autoconf compatible or has a valid PPD reference then
    // continue with normal setup.
    if (printer.ppd_reference().IsFilled()) {
      printers_manager->QueryPrinterForAutoConf(
          printer,
          base::BindOnce(OnPrinterQueriedForAutoConfMetricsOnly,
                         printers_manager, std::move(callback), printer));
      return;
    }

    // CupsPrintersManager should have marked compatible USB printers as having
    // a valid PPD reference or autoconf, so this USB printer is incompatible.
    if (printer.IsUsbProtocol()) {
      std::move(callback).Run(std::nullopt);
      return;
    }

    printers_manager->QueryPrinterForAutoConf(
        printer, base::BindOnce(OnPrinterQueriedForAutoConf, printers_manager,
                                std::move(callback), printer));
    return;
  }

  printers_manager->QueryPrinterForAutoConf(
      printer, base::BindOnce(OnPrinterQueriedForAutoConfMetricsOnly,
                              printers_manager, std::move(callback), printer));
}

}  // namespace

LocalPrinter* LocalPrinterImpl::Get() {
  CHECK(g_instance);
  return g_instance;
}

LocalPrinterImpl::LocalPrinterImpl() {
  CHECK(!g_instance);
  g_instance = this;
}

LocalPrinterImpl::~LocalPrinterImpl() {
  g_instance = nullptr;
}

void LocalPrinterImpl::GetPrinters(const AccountId& accountId,
                                   LocalPrinter::GetPrintersCallback callback) {
  std::move(callback).Run(GetLocalPrinters(accountId));
}

void LocalPrinterImpl::GetCapability(
    const AccountId& accountId,
    const std::string& printer_id,
    LocalPrinter::GetCapabilityCallback callback) {
  content::BrowserContext* context =
      ash::BrowserContextHelper::Get()->GetBrowserContextByAccountId(accountId);
  ash::CupsPrintersManager* printers_manager =
      ash::CupsPrintersManagerFactory::GetForBrowserContext(context);
  DCHECK(printers_manager);
  std::optional<chromeos::Printer> printer =
      printers_manager->GetPrinter(printer_id);
  if (!printer) {
    // If the printer was removed, the lookup will fail.
    std::move(callback).Run(std::nullopt);
    return;
  }

  if (ash::features::IsOAuthIppEnabled()) {
    ash::printing::oauth2::AuthorizationZonesManager* auth_manager =
        ash::printing::oauth2::AuthorizationZonesManagerFactory::
            GetForBrowserContext(context);
    DCHECK(auth_manager);
    auto authenticator = std::make_unique<ash::printing::PrinterAuthenticator>(
        printers_manager, auth_manager, *printer);
    ash::printing::PrinterAuthenticator* authenticator_ptr =
        authenticator.get();
    authenticator_ptr->ObtainAccessTokenIfNeeded(
        base::BindOnce(OnPrinterAuthenticated, std::move(authenticator),
                       printers_manager, *printer, std::move(callback)));
  } else {
    OnPrinterAuthenticated(nullptr, printers_manager, *printer,
                           std::move(callback),
                           ash::printing::oauth2::StatusCode::kOK, "");
  }
}

}  // namespace ash