blob: 871dfba056aae89a4fe10f3c8063c5bfe99cf2f6 [file] [log] [blame]
// Copyright 2019 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/extensions/printing/printing_api_handler.h"
#include <utility>
#include "base/bind.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/no_destructor.h"
#include "base/task/post_task.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/chromeos/extensions/printing/printing_api_utils.h"
#include "chrome/browser/chromeos/printing/cups_print_job.h"
#include "chrome/browser/chromeos/printing/cups_printers_manager.h"
#include "chrome/browser/chromeos/printing/cups_wrapper.h"
#include "chrome/browser/chromeos/printing/printer_configurer.h"
#include "chrome/browser/chromeos/printing/printer_error_codes.h"
#include "chrome/browser/printing/print_preview_sticky_settings.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
#include "chromeos/printing/printer_configuration.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/printing/common/cloud_print_cdd_conversion.h"
#include "content/public/browser/browser_context.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_registry.h"
#include "printing/backend/cups_jobs.h"
#include "printing/backend/print_backend.h"
namespace extensions {
namespace {
constexpr char kInvalidPrinterIdError[] = "Invalid printer ID";
constexpr char kNoActivePrintJobWithIdError[] =
"No active print job with given ID";
} // namespace
// static
std::unique_ptr<PrintingAPIHandler> PrintingAPIHandler::CreateForTesting(
content::BrowserContext* browser_context,
EventRouter* event_router,
ExtensionRegistry* extension_registry,
chromeos::CupsPrintJobManager* print_job_manager,
chromeos::CupsPrintersManager* printers_manager,
std::unique_ptr<PrintJobController> print_job_controller,
std::unique_ptr<chromeos::PrinterConfigurer> printer_configurer,
std::unique_ptr<chromeos::CupsWrapper> cups_wrapper) {
return base::WrapUnique(new PrintingAPIHandler(
browser_context, event_router, extension_registry, print_job_manager,
printers_manager, std::move(print_job_controller),
std::move(printer_configurer), std::move(cups_wrapper)));
}
PrintingAPIHandler::PrintingAPIHandler(content::BrowserContext* browser_context)
: PrintingAPIHandler(
browser_context,
EventRouter::Get(browser_context),
ExtensionRegistry::Get(browser_context),
chromeos::CupsPrintJobManagerFactory::GetForBrowserContext(
browser_context),
chromeos::CupsPrintersManagerFactory::GetForBrowserContext(
browser_context),
PrintJobController::Create(
chromeos::CupsPrintJobManagerFactory::GetForBrowserContext(
browser_context)),
chromeos::PrinterConfigurer::Create(
Profile::FromBrowserContext(browser_context)),
chromeos::CupsWrapper::Create()) {}
PrintingAPIHandler::PrintingAPIHandler(
content::BrowserContext* browser_context,
EventRouter* event_router,
ExtensionRegistry* extension_registry,
chromeos::CupsPrintJobManager* print_job_manager,
chromeos::CupsPrintersManager* printers_manager,
std::unique_ptr<PrintJobController> print_job_controller,
std::unique_ptr<chromeos::PrinterConfigurer> printer_configurer,
std::unique_ptr<chromeos::CupsWrapper> cups_wrapper)
: browser_context_(browser_context),
event_router_(event_router),
extension_registry_(extension_registry),
print_job_manager_(print_job_manager),
printers_manager_(printers_manager),
print_job_controller_(std::move(print_job_controller)),
printer_capabilities_provider_(printers_manager,
std::move(printer_configurer)),
cups_wrapper_(std::move(cups_wrapper)) {
print_job_manager_observation_.Observe(print_job_manager_);
}
PrintingAPIHandler::~PrintingAPIHandler() = default;
// static
BrowserContextKeyedAPIFactory<PrintingAPIHandler>*
PrintingAPIHandler::GetFactoryInstance() {
static base::NoDestructor<BrowserContextKeyedAPIFactory<PrintingAPIHandler>>
instance;
return instance.get();
}
// static
PrintingAPIHandler* PrintingAPIHandler::Get(
content::BrowserContext* browser_context) {
return BrowserContextKeyedAPIFactory<PrintingAPIHandler>::Get(
browser_context);
}
// static
void PrintingAPIHandler::RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterListPref(prefs::kPrintingAPIExtensionsAllowlist);
}
void PrintingAPIHandler::SubmitJob(
gfx::NativeWindow native_window,
scoped_refptr<const extensions::Extension> extension,
std::unique_ptr<api::printing::SubmitJob::Params> params,
PrintJobSubmitter::SubmitJobCallback callback) {
auto print_job_submitter = std::make_unique<PrintJobSubmitter>(
native_window, browser_context_, printers_manager_,
&printer_capabilities_provider_, print_job_controller_.get(),
&pdf_flattener_, std::move(extension), std::move(params->request));
PrintJobSubmitter* print_job_submitter_ptr = print_job_submitter.get();
print_job_submitter_ptr->Start(base::BindOnce(
&PrintingAPIHandler::OnPrintJobSubmitted, weak_ptr_factory_.GetWeakPtr(),
std::move(print_job_submitter), std::move(callback)));
}
void PrintingAPIHandler::OnPrintJobSubmitted(
std::unique_ptr<PrintJobSubmitter> print_job_submitter,
PrintJobSubmitter::SubmitJobCallback callback,
absl::optional<api::printing::SubmitJobStatus> status,
std::unique_ptr<std::string> job_id,
absl::optional<std::string> error) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), status, std::move(job_id), error));
}
absl::optional<std::string> PrintingAPIHandler::CancelJob(
const std::string& extension_id,
const std::string& job_id) {
auto it = print_jobs_extension_ids_.find(job_id);
// If there was no print job with specified id sent by the extension return
// an error.
if (it == print_jobs_extension_ids_.end() || it->second != extension_id)
return kNoActivePrintJobWithIdError;
// If we can't cancel the print job (e.g. it's in terminated state) return an
// error.
if (!print_job_controller_->CancelPrintJob(job_id))
return kNoActivePrintJobWithIdError;
// Return no error otherwise.
return absl::nullopt;
}
std::vector<api::printing::Printer> PrintingAPIHandler::GetPrinters() {
PrefService* prefs =
Profile::FromBrowserContext(browser_context_)->GetPrefs();
absl::optional<DefaultPrinterRules> default_printer_rules =
GetDefaultPrinterRules(prefs->GetString(
prefs::kPrintPreviewDefaultDestinationSelectionRules));
auto* sticky_settings = printing::PrintPreviewStickySettings::GetInstance();
sticky_settings->RestoreFromPrefs(prefs);
base::flat_map<std::string, int> recently_used_ranks =
sticky_settings->GetPrinterRecentlyUsedRanks();
static constexpr chromeos::PrinterClass kPrinterClasses[] = {
chromeos::PrinterClass::kEnterprise,
chromeos::PrinterClass::kSaved,
chromeos::PrinterClass::kAutomatic,
};
std::vector<api::printing::Printer> all_printers;
for (auto printer_class : kPrinterClasses) {
const std::vector<chromeos::Printer>& printers =
printers_manager_->GetPrinters(printer_class);
for (const auto& printer : printers) {
all_printers.emplace_back(
PrinterToIdl(printer, default_printer_rules, recently_used_ranks));
}
}
return all_printers;
}
void PrintingAPIHandler::GetPrinterInfo(const std::string& printer_id,
GetPrinterInfoCallback callback) {
if (!printers_manager_->GetPrinter(printer_id)) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), /*capabilities=*/absl::nullopt,
/*status=*/absl::nullopt, kInvalidPrinterIdError));
return;
}
printer_capabilities_provider_.GetPrinterCapabilities(
printer_id, base::BindOnce(&PrintingAPIHandler::GetPrinterStatus,
weak_ptr_factory_.GetWeakPtr(), printer_id,
std::move(callback)));
}
void PrintingAPIHandler::GetPrinterStatus(
const std::string& printer_id,
GetPrinterInfoCallback callback,
absl::optional<printing::PrinterSemanticCapsAndDefaults> capabilities) {
if (!capabilities.has_value()) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), /*capabilities=*/absl::nullopt,
api::printing::PRINTER_STATUS_UNREACHABLE,
/*error=*/absl::nullopt));
return;
}
base::Value capabilities_value =
cloud_print::PrinterSemanticCapsAndDefaultsToCdd(capabilities.value());
cups_wrapper_->QueryCupsPrinterStatus(
printer_id,
base::BindOnce(&PrintingAPIHandler::OnPrinterStatusRetrieved,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
std::move(capabilities_value)));
}
void PrintingAPIHandler::OnPrinterStatusRetrieved(
GetPrinterInfoCallback callback,
base::Value capabilities,
std::unique_ptr<::printing::PrinterStatus> printer_status) {
if (!printer_status) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), std::move(capabilities),
api::printing::PRINTER_STATUS_UNREACHABLE,
/*error=*/absl::nullopt));
return;
}
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
std::move(callback), std::move(capabilities),
PrinterStatusToIdl(chromeos::PrinterErrorCodeFromPrinterStatusReasons(
*printer_status)),
/*error=*/absl::nullopt));
}
void PrintingAPIHandler::SetPrintJobControllerForTesting(
std::unique_ptr<PrintJobController> print_job_controller) {
print_job_controller_ = std::move(print_job_controller);
}
void PrintingAPIHandler::OnPrintJobCreated(
base::WeakPtr<chromeos::CupsPrintJob> job) {
DispatchJobStatusChangedEvent(api::printing::JOB_STATUS_PENDING, job);
if (!job || job->source() != printing::PrintJob::Source::EXTENSION)
return;
const std::string& extension_id = job->source_id();
const std::string& job_id = job->GetUniqueId();
print_jobs_extension_ids_[job_id] = extension_id;
print_job_controller_->OnPrintJobCreated(extension_id, job_id, job);
}
void PrintingAPIHandler::OnPrintJobStarted(
base::WeakPtr<chromeos::CupsPrintJob> job) {
DispatchJobStatusChangedEvent(api::printing::JOB_STATUS_IN_PROGRESS, job);
}
void PrintingAPIHandler::OnPrintJobDone(
base::WeakPtr<chromeos::CupsPrintJob> job) {
DispatchJobStatusChangedEvent(api::printing::JOB_STATUS_PRINTED, job);
FinishJob(job);
}
void PrintingAPIHandler::OnPrintJobError(
base::WeakPtr<chromeos::CupsPrintJob> job) {
DispatchJobStatusChangedEvent(api::printing::JOB_STATUS_FAILED, job);
FinishJob(job);
}
void PrintingAPIHandler::OnPrintJobCancelled(
base::WeakPtr<chromeos::CupsPrintJob> job) {
DispatchJobStatusChangedEvent(api::printing::JOB_STATUS_CANCELED, job);
FinishJob(job);
}
void PrintingAPIHandler::DispatchJobStatusChangedEvent(
api::printing::JobStatus job_status,
base::WeakPtr<chromeos::CupsPrintJob> job) {
if (!job || job->source() != printing::PrintJob::Source::EXTENSION)
return;
auto event =
std::make_unique<Event>(events::PRINTING_ON_JOB_STATUS_CHANGED,
api::printing::OnJobStatusChanged::kEventName,
api::printing::OnJobStatusChanged::Create(
job->GetUniqueId(), job_status));
if (extension_registry_->enabled_extensions().Contains(job->source_id()))
event_router_->DispatchEventToExtension(job->source_id(), std::move(event));
}
void PrintingAPIHandler::FinishJob(base::WeakPtr<chromeos::CupsPrintJob> job) {
if (!job || job->source() != printing::PrintJob::Source::EXTENSION)
return;
const std::string& job_id = job->GetUniqueId();
print_jobs_extension_ids_.erase(job_id);
print_job_controller_->OnPrintJobFinished(job_id);
}
template <>
KeyedService*
BrowserContextKeyedAPIFactory<PrintingAPIHandler>::BuildServiceInstanceFor(
content::BrowserContext* context) const {
Profile* profile = Profile::FromBrowserContext(context);
// We do not want an instance of PrintingAPIHandler on the lock screen.
// This will lead to multiple printing notifications.
if (!chromeos::ProfileHelper::IsRegularProfile(profile)) {
return nullptr;
}
return new PrintingAPIHandler(context);
}
} // namespace extensions