blob: 351354eb4c27634f0d51041f7d0184156b697d4b [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/printing/printer_setup_util.h"
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/optional.h"
#include "base/stl_util.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/threading/scoped_blocking_call.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/printing/cups_printers_manager.h"
#include "chrome/browser/chromeos/printing/cups_printers_manager_factory.h"
#include "chrome/browser/chromeos/printing/printer_configurer.h"
#include "chrome/browser/printing/print_backend_service.h"
#include "chromeos/printing/printer_configuration.h"
#include "components/crash/core/common/crash_keys.h"
#include "content/public/browser/browser_thread.h"
#include "printing/printing_features.h"
namespace printing {
namespace {
void LogPrinterSetup(const chromeos::Printer& printer,
chromeos::PrinterSetupResult result) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
base::UmaHistogramEnumeration(
printer.IsZeroconf()
? "Printing.CUPS.ZeroconfPrinterSetupResult.PrintPreview"
: "Printing.CUPS.PrinterSetupResult.PrintPreview",
result, chromeos::PrinterSetupResult::kMaxValue);
switch (result) {
case chromeos::PrinterSetupResult::kSuccess: {
VLOG(1) << "Printer setup successful for " << printer.id()
<< " fetching properties";
if (printer.IsUsbProtocol()) {
// Record UMA for USB printer setup source.
chromeos::PrinterConfigurer::RecordUsbPrinterSetupSource(
chromeos::UsbPrinterSetupSource::kPrintPreview);
}
return;
}
case chromeos::PrinterSetupResult::kPrinterUnreachable:
case chromeos::PrinterSetupResult::kPrinterSentWrongResponse:
case chromeos::PrinterSetupResult::kPpdNotFound:
case chromeos::PrinterSetupResult::kPpdUnretrievable:
// Prompt user to update configuration or check internet connection.
// TODO(skau): Fill me in
LOG(WARNING) << ResultCodeToMessage(result);
break;
case chromeos::PrinterSetupResult::kFatalError:
case chromeos::PrinterSetupResult::kDbusError:
case chromeos::PrinterSetupResult::kNativePrintersNotAllowed:
case chromeos::PrinterSetupResult::kPpdTooLarge:
case chromeos::PrinterSetupResult::kInvalidPpd:
case chromeos::PrinterSetupResult::kIoError:
case chromeos::PrinterSetupResult::kMemoryAllocationError:
case chromeos::PrinterSetupResult::kBadUri:
case chromeos::PrinterSetupResult::kDbusNoReply:
case chromeos::PrinterSetupResult::kDbusTimeout:
LOG(ERROR) << ResultCodeToMessage(result);
break;
case chromeos::PrinterSetupResult::kInvalidPrinterUpdate:
case chromeos::PrinterSetupResult::kEditSuccess:
case chromeos::PrinterSetupResult::kPrinterIsNotAutoconfigurable:
case chromeos::PrinterSetupResult::kComponentUnavailable:
case chromeos::PrinterSetupResult::kMaxValue:
LOG(ERROR) << "Unexpected error in printer setup: "
<< ResultCodeToMessage(result);
break;
}
}
// This runs on a ThreadPoolForegroundWorker and not the UI thread.
base::Optional<PrinterSemanticCapsAndDefaults>
FetchCapabilitiesOnBlockingTaskRunner(const std::string& device_name,
const std::string& locale) {
auto print_backend = PrintBackend::CreateInstance(locale);
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
VLOG(1) << "Get printer capabilities start for " << device_name;
crash_keys::ScopedPrinterInfo crash_key(
print_backend->GetPrinterDriverInfo(device_name));
auto caps = base::make_optional<PrinterSemanticCapsAndDefaults>();
if (!print_backend->GetPrinterSemanticCapsAndDefaults(device_name, &*caps)) {
// Failed to get capabilities, but proceed to assemble the settings to
// return what information we do have.
LOG(WARNING) << "Failed to get capabilities for " << device_name;
return base::nullopt;
}
return caps;
}
void FetchCapabilities(
const std::string& printer_id,
base::OnceCallback<
void(const base::Optional<PrinterSemanticCapsAndDefaults>&)> cb) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (base::FeatureList::IsEnabled(features::kEnableOopPrintDrivers)) {
VLOG(1) << "Fetching printer capabilities via service";
GetPrintBackendService(g_browser_process->GetApplicationLocale(),
printer_id)
->GetPrinterSemanticCapsAndDefaults(printer_id, std::move(cb));
} else {
VLOG(1) << "Fetching printer capabilities in-process";
// USER_VISIBLE because the result is displayed in the print preview dialog.
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
base::BindOnce(FetchCapabilitiesOnBlockingTaskRunner, printer_id,
g_browser_process->GetApplicationLocale()),
std::move(cb));
}
}
void OnPrinterInstalled(
chromeos::CupsPrintersManager* printers_manager,
const chromeos::Printer& printer,
base::OnceCallback<
void(const base::Optional<PrinterSemanticCapsAndDefaults>&)> cb,
chromeos::PrinterSetupResult result) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
LogPrinterSetup(printer, result);
if (result != chromeos::PrinterSetupResult::kSuccess) {
std::move(cb).Run(base::nullopt);
return;
}
printers_manager->PrinterInstalled(printer, /*is_automatic=*/true);
// Fetch settings off of the UI thread and invoke callback.
FetchCapabilities(printer.id(), std::move(cb));
}
} // namespace
void SetUpPrinter(
chromeos::CupsPrintersManager* printers_manager,
chromeos::PrinterConfigurer* printer_configurer,
const chromeos::Printer& printer,
base::OnceCallback<
void(const base::Optional<PrinterSemanticCapsAndDefaults>&)> cb) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// Log printer configuration for selected printer.
base::UmaHistogramEnumeration("Printing.CUPS.ProtocolUsed",
printer.GetProtocol(),
chromeos::Printer::kProtocolMax);
if (printers_manager->IsPrinterInstalled(printer)) {
// Skip setup if the printer does not need to be installed.
// Fetch settings off of the UI thread and invoke callback.
FetchCapabilities(printer.id(), std::move(cb));
return;
}
printer_configurer->SetUpPrinter(
printer, base::BindOnce(OnPrinterInstalled, printers_manager, printer,
std::move(cb)));
}
} // namespace printing