blob: 292c6d3f5af0221bc0d57990c9a48ba4a6b19094 [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/ash/crosapi/local_printer_ash.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/check.h"
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/values.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/printing/cups_print_job.h"
#include "chrome/browser/chromeos/printing/cups_print_job_manager.h"
#include "chrome/browser/chromeos/printing/cups_print_job_manager_factory.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/history/print_job_info.pb.h"
#include "chrome/browser/chromeos/printing/ppd_provider_factory.h"
#include "chrome/browser/chromeos/printing/print_management/printing_manager.h"
#include "chrome/browser/chromeos/printing/print_management/printing_manager_factory.h"
#include "chrome/browser/chromeos/printing/print_server.h"
#include "chrome/browser/chromeos/printing/print_servers_manager.h"
#include "chrome/browser/chromeos/printing/printer_configurer.h"
#include "chrome/browser/chromeos/printing/printer_setup_util.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/settings_window_manager_chromeos.h"
#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
#include "chrome/common/pref_names.h"
#include "chromeos/crosapi/mojom/local_printer.mojom.h"
#include "chromeos/printing/ppd_provider.h"
#include "components/prefs/pref_service.h"
#include "components/printing/browser/prefs_util.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/bindings/remote_set.h"
#include "printing/backend/print_backend.h"
#include "printing/mojom/print.mojom.h"
#include "printing/print_job_constants.h"
#include "printing/print_settings.h"
#include "printing/printing_features.h"
#include "printing/printing_utils.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
namespace crosapi {
namespace {
// Generates and returns a url for a PPD license which is empty if
// an error occurs e.g. the ppd provider callback failed.
// When bound to a ppd provider scoped refptr, the reference count
// will be decremented once the callback is done executing and the
// ppd provider destroyed if it hits zero.
GURL GenerateEulaUrl(scoped_refptr<chromeos::PpdProvider>,
chromeos::PpdProvider::CallbackResultCode result,
const std::string& license) {
if (result != chromeos::PpdProvider::CallbackResultCode::SUCCESS ||
license.empty()) {
return GURL();
}
return chromeos::PrinterConfigurer::GeneratePrinterEulaUrl(license);
}
// Destroys the PrinterConfigurer object once the callback has finished
// running.
mojom::CapabilitiesResponsePtr OnSetUpPrinter(
std::unique_ptr<chromeos::PrinterConfigurer>,
PrefService* prefs,
const chromeos::Printer& printer,
const absl::optional<printing::PrinterSemanticCapsAndDefaults>& caps) {
return mojom::CapabilitiesResponse::New(
LocalPrinterAsh::PrinterToMojom(printer), printer.HasSecureProtocol(),
caps, prefs->GetInteger(prefs::kPrintingAllowedColorModes),
prefs->GetInteger(prefs::kPrintingAllowedDuplexModes),
static_cast<printing::mojom::PinModeRestriction>(
prefs->GetInteger(prefs::kPrintingAllowedPinModes)),
static_cast<printing::mojom::ColorModeRestriction>(
prefs->GetInteger(prefs::kPrintingColorDefault)),
static_cast<printing::mojom::DuplexModeRestriction>(
prefs->GetInteger(prefs::kPrintingDuplexDefault)),
static_cast<printing::mojom::PinModeRestriction>(
prefs->GetInteger(prefs::kPrintingPinDefault)),
0); // deprecated
}
} // namespace
LocalPrinterAsh::LocalPrinterAsh() = default;
LocalPrinterAsh::~LocalPrinterAsh() = default;
// static
mojom::PrintServersConfigPtr LocalPrinterAsh::ConfigToMojom(
const chromeos::PrintServersConfig& config) {
mojom::PrintServersConfigPtr ptr = mojom::PrintServersConfig::New();
ptr->fetching_mode = config.fetching_mode;
for (const chromeos::PrintServer& server : config.print_servers) {
ptr->print_servers.push_back(mojom::PrintServer::New(
server.GetId(), server.GetUrl(), server.GetName()));
}
return ptr;
}
// static
mojom::LocalDestinationInfoPtr LocalPrinterAsh::PrinterToMojom(
const chromeos::Printer& printer) {
return mojom::LocalDestinationInfo::New(
printer.id(), printer.display_name(), printer.description(),
printer.source() == chromeos::Printer::SRC_POLICY,
printer.uri().GetNormalized(/*always_print_port=*/true));
}
// static
mojom::PrinterStatusPtr LocalPrinterAsh::StatusToMojom(
const chromeos::CupsPrinterStatus& status) {
mojom::PrinterStatusPtr ptr = mojom::PrinterStatus::New();
ptr->printer_id = status.GetPrinterId();
ptr->timestamp = status.GetTimestamp();
for (const auto& reason : status.GetStatusReasons()) {
if (reason.GetReason() == mojom::StatusReason::Reason::kNoError)
continue;
ptr->status_reasons.push_back(
mojom::StatusReason::New(reason.GetReason(), reason.GetSeverity()));
}
return ptr;
}
void LocalPrinterAsh::BindReceiver(
mojo::PendingReceiver<mojom::LocalPrinter> pending_receiver) {
receivers_.Add(this, std::move(pending_receiver));
}
void LocalPrinterAsh::OnPrintJobCreated(
base::WeakPtr<chromeos::CupsPrintJob> job) {
NotifyPrintJobUpdate(job, mojom::PrintJobStatus::kCreated);
}
void LocalPrinterAsh::OnPrintJobStarted(
base::WeakPtr<chromeos::CupsPrintJob> job) {
NotifyPrintJobUpdate(job, mojom::PrintJobStatus::kStarted);
}
void LocalPrinterAsh::OnPrintJobUpdated(
base::WeakPtr<chromeos::CupsPrintJob> job) {
NotifyPrintJobUpdate(job, mojom::PrintJobStatus::kUpdated);
}
void LocalPrinterAsh::OnPrintJobSuspended(
base::WeakPtr<chromeos::CupsPrintJob> job) {
NotifyPrintJobUpdate(job, mojom::PrintJobStatus::kSuspended);
}
void LocalPrinterAsh::OnPrintJobResumed(
base::WeakPtr<chromeos::CupsPrintJob> job) {
NotifyPrintJobUpdate(job, mojom::PrintJobStatus::kResumed);
}
void LocalPrinterAsh::OnPrintJobDone(
base::WeakPtr<chromeos::CupsPrintJob> job) {
NotifyPrintJobUpdate(job, mojom::PrintJobStatus::kDone);
}
void LocalPrinterAsh::OnPrintJobError(
base::WeakPtr<chromeos::CupsPrintJob> job) {
NotifyPrintJobUpdate(job, mojom::PrintJobStatus::kError);
}
void LocalPrinterAsh::OnPrintJobCancelled(
base::WeakPtr<chromeos::CupsPrintJob> job) {
NotifyPrintJobUpdate(job, mojom::PrintJobStatus::kCancelled);
}
void LocalPrinterAsh::NotifyPrintJobUpdate(
base::WeakPtr<chromeos::CupsPrintJob> job,
mojom::PrintJobStatus status) {
if (!job) {
LOG(WARNING) << "Ignoring invalid print job";
return;
}
for (auto& remote : print_job_remotes_)
remote->OnPrintJobUpdate(job->printer().id(), job->job_id(), status);
if (job->source() != mojom::PrintJob::Source::EXTENSION)
return;
for (auto& remote : extension_print_job_remotes_)
remote->OnPrintJobUpdate(job->printer().id(), job->job_id(), status);
}
void LocalPrinterAsh::OnPrintServersChanged(
const chromeos::PrintServersConfig& config) {
for (auto& remote : print_server_remotes_)
remote->OnPrintServersChanged(LocalPrinterAsh::ConfigToMojom(config));
}
void LocalPrinterAsh::OnServerPrintersChanged(
const std::vector<chromeos::PrinterDetector::DetectedPrinter>&) {
for (auto& remote : print_server_remotes_)
remote->OnServerPrintersChanged();
}
void LocalPrinterAsh::RegisterObservers() {
if (observers_registered_)
return;
Profile* profile = GetProfile();
if (!profile)
return;
observers_registered_ = true;
auto* print_servers_manager =
chromeos::CupsPrintersManagerFactory::GetForBrowserContext(profile)
->GetPrintServersManager();
if (print_servers_manager) {
print_servers_manager->AddObserver(this);
} else {
// This can occur during browser tests.
LOG(ERROR) << "PrintServersManager object not found";
}
auto* print_job_manager =
chromeos::CupsPrintJobManagerFactory::GetForBrowserContext(profile);
print_job_manager->AddObserver(this);
}
void LocalPrinterAsh::GetPrinters(GetPrintersCallback callback) {
Profile* profile = GetProfile();
DCHECK(profile);
// Printing is not allowed during OOBE.
DCHECK(!chromeos::ProfileHelper::IsSigninProfile(profile));
chromeos::CupsPrintersManager* printers_manager =
chromeos::CupsPrintersManagerFactory::GetForBrowserContext(profile);
std::vector<mojom::LocalDestinationInfoPtr> printers;
for (chromeos::PrinterClass pc :
{chromeos::PrinterClass::kSaved, chromeos::PrinterClass::kEnterprise,
chromeos::PrinterClass::kAutomatic}) {
for (const chromeos::Printer& p : printers_manager->GetPrinters(pc)) {
VLOG(1) << "Found printer " << p.display_name() << " with device name "
<< p.id();
printers.push_back(PrinterToMojom(p));
}
}
std::move(callback).Run(std::move(printers));
}
void LocalPrinterAsh::GetCapability(const std::string& printer_id,
GetCapabilityCallback callback) {
Profile* profile = GetProfile();
DCHECK(profile);
chromeos::CupsPrintersManager* printers_manager =
chromeos::CupsPrintersManagerFactory::GetForBrowserContext(profile);
absl::optional<chromeos::Printer> printer =
printers_manager->GetPrinter(printer_id);
if (!printer) {
// If the printer was removed, the lookup will fail.
std::move(callback).Run(nullptr);
return;
}
std::unique_ptr<chromeos::PrinterConfigurer> printer_configurer =
CreatePrinterConfigurer(profile);
chromeos::PrinterConfigurer* ptr = printer_configurer.get();
printing::SetUpPrinter(
printers_manager, ptr, *printer,
base::BindOnce(OnSetUpPrinter, std::move(printer_configurer),
profile->GetPrefs(), *printer)
.Then(std::move(callback)));
}
void LocalPrinterAsh::GetEulaUrl(const std::string& printer_id,
GetEulaUrlCallback callback) {
Profile* profile = GetProfile();
DCHECK(profile);
chromeos::CupsPrintersManager* printers_manager =
chromeos::CupsPrintersManagerFactory::GetForBrowserContext(profile);
absl::optional<chromeos::Printer> printer =
printers_manager->GetPrinter(printer_id);
if (!printer) {
// If the printer does not exist, fetching for the license will fail.
std::move(callback).Run(GURL());
return;
}
scoped_refptr<chromeos::PpdProvider> ppd_provider =
CreatePpdProvider(profile);
ppd_provider->ResolvePpdLicense(
printer->ppd_reference().effective_make_and_model,
base::BindOnce(GenerateEulaUrl, ppd_provider).Then(std::move(callback)));
}
void LocalPrinterAsh::GetStatus(const std::string& printer_id,
GetStatusCallback callback) {
Profile* profile = GetProfile();
DCHECK(profile);
chromeos::CupsPrintersManager* printers_manager =
chromeos::CupsPrintersManagerFactory::GetForBrowserContext(profile);
printers_manager->FetchPrinterStatus(
printer_id, base::BindOnce(StatusToMojom).Then(std::move(callback)));
}
void LocalPrinterAsh::ShowSystemPrintSettings(
ShowSystemPrintSettingsCallback callback) {
Profile* profile = GetProfile();
DCHECK(profile);
chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
profile, chromeos::settings::mojom::kPrintingDetailsSubpagePath);
std::move(callback).Run();
}
void LocalPrinterAsh::CreatePrintJob(mojom::PrintJobPtr job,
CreatePrintJobCallback callback) {
Profile* profile = GetProfile();
DCHECK(profile);
chromeos::CupsPrintJobManager* print_job_manager =
chromeos::CupsPrintJobManagerFactory::GetForBrowserContext(profile);
chromeos::printing::proto::PrintSettings settings;
settings.set_color(
printing::IsColorModelSelected(job->color_mode)
? chromeos::printing::proto::PrintSettings_ColorMode_COLOR
: chromeos::printing::proto::PrintSettings_ColorMode_BLACK_AND_WHITE);
settings.set_duplex(
static_cast<chromeos::printing::proto::PrintSettings_DuplexMode>(
job->duplex_mode));
settings.set_copies(job->copies);
chromeos::printing::proto::MediaSize media_size;
media_size.set_width(job->media_size.width());
media_size.set_height(job->media_size.height());
media_size.set_vendor_id(job->media_vendor_id);
*settings.mutable_media_size() = media_size;
print_job_manager->CreatePrintJob(job->device_name, job->title, job->job_id,
job->page_count, job->source,
job->source_id, std::move(settings));
std::move(callback).Run();
}
void LocalPrinterAsh::CancelPrintJob(const std::string& printer_id,
unsigned int job_id,
CancelPrintJobCallback callback) {
Profile* profile = GetProfile();
DCHECK(profile);
chromeos::printing::print_management::PrintingManagerFactory::GetForProfile(
profile)
->CancelPrintJob(
chromeos::CupsPrintJob::CreateUniqueId(printer_id, job_id),
std::move(callback));
}
void LocalPrinterAsh::GetPrintServersConfig(
GetPrintServersConfigCallback callback) {
Profile* profile = GetProfile();
DCHECK(profile);
chromeos::PrintServersManager* print_servers_manager =
chromeos::CupsPrintersManagerFactory::GetForBrowserContext(profile)
->GetPrintServersManager();
std::move(callback).Run(
ConfigToMojom(print_servers_manager->GetPrintServersConfig()));
}
void LocalPrinterAsh::ChoosePrintServers(
const std::vector<std::string>& print_server_ids,
ChoosePrintServersCallback callback) {
Profile* profile = GetProfile();
DCHECK(profile);
chromeos::PrintServersManager* print_servers_manager =
chromeos::CupsPrintersManagerFactory::GetForBrowserContext(profile)
->GetPrintServersManager();
print_servers_manager->ChoosePrintServer(print_server_ids);
std::move(callback).Run();
}
void LocalPrinterAsh::AddPrintServerObserver(
mojo::PendingRemote<mojom::PrintServerObserver> remote,
AddPrintServerObserverCallback callback) {
RegisterObservers();
print_server_remotes_.Add(std::move(remote));
std::move(callback).Run();
}
void LocalPrinterAsh::GetPolicies(GetPoliciesCallback callback) {
Profile* profile = GetProfile();
PrefService* prefs = profile->GetPrefs();
mojom::PoliciesPtr policies = mojom::Policies::New();
if (prefs->HasPrefPath(prefs::kPrintHeaderFooter)) {
(prefs->IsManagedPreference(prefs::kPrintHeaderFooter)
? policies->print_header_footer_allowed
: policies->print_header_footer_default) =
prefs->GetBoolean(prefs::kPrintHeaderFooter)
? mojom::Policies::OptionalBool::kTrue
: mojom::Policies::OptionalBool::kFalse;
}
if (prefs->HasPrefPath(prefs::kPrintingAllowedBackgroundGraphicsModes)) {
policies->allowed_background_graphics_modes =
static_cast<mojom::Policies::BackgroundGraphicsModeRestriction>(
prefs->GetInteger(prefs::kPrintingAllowedBackgroundGraphicsModes));
}
if (prefs->HasPrefPath(prefs::kPrintingBackgroundGraphicsDefault)) {
policies->background_graphics_default =
static_cast<mojom::Policies::BackgroundGraphicsModeRestriction>(
prefs->GetInteger(prefs::kPrintingBackgroundGraphicsDefault));
}
policies->paper_size_default = printing::ParsePaperSizeDefault(*prefs);
if (prefs->HasPrefPath(prefs::kPrintingMaxSheetsAllowed)) {
int max_sheets = prefs->GetInteger(prefs::kPrintingMaxSheetsAllowed);
if (max_sheets >= 0) {
policies->max_sheets_allowed = max_sheets;
policies->max_sheets_allowed_has_value = true;
}
}
std::move(callback).Run(std::move(policies));
}
void LocalPrinterAsh::GetUsernamePerPolicy(
GetUsernamePerPolicyCallback callback) {
Profile* profile = GetProfile();
const std::string username = chromeos::ProfileHelper::Get()
->GetUserByProfile(profile)
->display_email();
std::move(callback).Run(profile->GetPrefs()->GetBoolean(
prefs::kPrintingSendUsernameAndFilenameEnabled)
? absl::make_optional(username)
: absl::nullopt);
}
void LocalPrinterAsh::GetPrinterTypeDenyList(
GetPrinterTypeDenyListCallback callback) {
Profile* profile = GetProfile();
PrefService* prefs = profile->GetPrefs();
std::vector<printing::mojom::PrinterType> deny_list;
if (!prefs->HasPrefPath(prefs::kPrinterTypeDenyList)) {
std::move(callback).Run(deny_list);
return;
}
const base::Value* deny_list_from_prefs =
prefs->Get(prefs::kPrinterTypeDenyList);
if (!deny_list_from_prefs) {
std::move(callback).Run(deny_list);
return;
}
deny_list.reserve(deny_list_from_prefs->GetList().size());
for (const base::Value& deny_list_value : deny_list_from_prefs->GetList()) {
const std::string& deny_list_str = deny_list_value.GetString();
printing::mojom::PrinterType printer_type;
if (deny_list_str == "privet")
printer_type = printing::mojom::PrinterType::kPrivet;
else if (deny_list_str == "extension")
printer_type = printing::mojom::PrinterType::kExtension;
else if (deny_list_str == "pdf")
printer_type = printing::mojom::PrinterType::kPdf;
else if (deny_list_str == "local")
printer_type = printing::mojom::PrinterType::kLocal;
else if (deny_list_str == "cloud")
printer_type = printing::mojom::PrinterType::kCloud;
else
continue;
deny_list.push_back(printer_type);
}
std::move(callback).Run(deny_list);
}
Profile* LocalPrinterAsh::GetProfile() {
DCHECK(user_manager::UserManager::IsInitialized());
DCHECK(user_manager::UserManager::Get()->IsUserLoggedIn());
return ProfileManager::GetPrimaryUserProfile();
}
void LocalPrinterAsh::AddPrintJobObserver(
mojo::PendingRemote<mojom::PrintJobObserver> remote,
mojom::PrintJobSource source,
AddPrintJobObserverCallback callback) {
RegisterObservers();
if (source == mojom::PrintJobSource::kExtension)
extension_print_job_remotes_.Add(std::move(remote));
if (source == mojom::PrintJobSource::kAny)
print_job_remotes_.Add(std::move(remote));
std::move(callback).Run();
}
scoped_refptr<chromeos::PpdProvider> LocalPrinterAsh::CreatePpdProvider(
Profile* profile) {
return chromeos::CreatePpdProvider(profile);
}
std::unique_ptr<chromeos::PrinterConfigurer>
LocalPrinterAsh::CreatePrinterConfigurer(Profile* profile) {
return chromeos::PrinterConfigurer::Create(profile);
}
} // namespace crosapi