#include "chrome/services/printing/print_backend_service_impl.h"
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "base/check.h"
#include "base/containers/adapters.h"
#include "base/containers/heap_array.h"
#include "base/containers/span.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/not_fatal_until.h"
#include "base/notreached.h"
#include "base/ranges/algorithm.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/threading/sequence_bound.h"
#include "base/types/expected.h"
#include "base/types/expected_macros.h"
#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/common/printing/printing_init.h"
#include "chrome/services/printing/public/mojom/print_backend_service.mojom.h"
#include "components/crash/core/common/crash_keys.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "printing/backend/print_backend.h"
#include "printing/buildflags/buildflags.h"
#include "printing/metafile.h"
#include "printing/metafile_skia.h"
#include "printing/mojom/print.mojom.h"
#include "printing/printed_document.h"
#include "printing/printing_context.h"
#if BUILDFLAG(IS_MAC)
#include "base/threading/thread_restrictions.h"
#include "chrome/common/printing/printer_capabilities_mac.h"
#endif
#if BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(USE_CUPS)
#include "printing/backend/cups_connection_pool.h"
#endif
#if BUILDFLAG(IS_LINUX)
#include "base/no_destructor.h"
#include "ui/linux/linux_ui.h"
#include "ui/linux/linux_ui_delegate_stub.h"
#include "ui/linux/linux_ui_factory.h"
#endif
#if BUILDFLAG(IS_WIN)
#include "base/containers/queue.h"
#include "base/types/expected.h"
#include "base/win/win_util.h"
#include "chrome/services/printing/public/mojom/printer_xml_parser.mojom.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "printing/backend/xps_utils_win.h"
#include "printing/emf_win.h"
#include "printing/printed_page_win.h"
#include "printing/printing_features.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/native_widget_types.h"
#endif
namespace printing {
namespace {
#if BUILDFLAG(IS_LINUX)
void InstantiateLinuxUiDelegate() { … }
#endif
scoped_refptr<base::SequencedTaskRunner> GetPrintingTaskRunner() { … }
std::unique_ptr<Metafile> CreateMetafile(mojom::MetafileDataType data_type) { … }
struct RenderData { … };
std::optional<RenderData> PrepareRenderData(
int document_cookie,
mojom::MetafileDataType page_data_type,
const base::ReadOnlySharedMemoryRegion& serialized_data) { … }
class DocumentContainer { … };
PrintBackendServiceImpl::StartPrintingResult
DocumentContainer::StartPrintingReadyDocument() { … }
#if BUILDFLAG(IS_WIN)
mojom::ResultCode DocumentContainer::DoRenderPrintedPage(
uint32_t page_index,
mojom::MetafileDataType page_data_type,
base::ReadOnlySharedMemoryRegion serialized_page,
gfx::Size page_size,
gfx::Rect page_content_rect,
float shrink_factor) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(1) << "Render printed page " << page_index << " for document "
<< document_->cookie();
std::optional<RenderData> render_data =
PrepareRenderData(document_->cookie(), page_data_type, serialized_page);
if (!render_data) {
DLOG(ERROR) << "Failure preparing render data for document "
<< document_->cookie();
context_->Cancel();
return mojom::ResultCode::kFailed;
}
document_->SetPage(page_index, std::move(render_data->metafile),
shrink_factor, page_size, page_content_rect);
mojom::ResultCode result = document_->RenderPrintedPage(
*document_->GetPage(page_index), context_.get());
if (result != mojom::ResultCode::kSuccess) {
DLOG(ERROR) << "Failure rendering page " << page_index << " of document "
<< document_->cookie() << ", error: " << result;
context_->Cancel();
}
return result;
}
#endif
mojom::ResultCode DocumentContainer::DoRenderPrintedDocument(
uint32_t page_count,
mojom::MetafileDataType data_type,
base::ReadOnlySharedMemoryRegion serialized_document) { … }
mojom::ResultCode DocumentContainer::DoDocumentDone() { … }
void DocumentContainer::DoCancel() { … }
}
class PrintBackendServiceImpl::DocumentHelper { … };
SandboxedPrintBackendHostImpl::SandboxedPrintBackendHostImpl(
mojo::PendingReceiver<mojom::SandboxedPrintBackendHost> receiver)
: … { … }
SandboxedPrintBackendHostImpl::~SandboxedPrintBackendHostImpl() = default;
void SandboxedPrintBackendHostImpl::BindBackend(
mojo::PendingReceiver<mojom::PrintBackendService> receiver) { … }
UnsandboxedPrintBackendHostImpl::UnsandboxedPrintBackendHostImpl(
mojo::PendingReceiver<mojom::UnsandboxedPrintBackendHost> receiver)
: … { … }
UnsandboxedPrintBackendHostImpl::~UnsandboxedPrintBackendHostImpl() = default;
void UnsandboxedPrintBackendHostImpl::BindBackend(
mojo::PendingReceiver<mojom::PrintBackendService> receiver) { … }
PrintBackendServiceImpl::PrintingContextDelegate::PrintingContextDelegate() =
default;
PrintBackendServiceImpl::PrintingContextDelegate::~PrintingContextDelegate() =
default;
gfx::NativeView
PrintBackendServiceImpl::PrintingContextDelegate::GetParentView() { … }
std::string PrintBackendServiceImpl::PrintingContextDelegate::GetAppLocale() { … }
#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
void PrintBackendServiceImpl::PrintingContextDelegate::SetParentWindow(
uint32_t parent_window_id) {
#if BUILDFLAG(IS_WIN)
parent_native_view_ = reinterpret_cast<gfx::NativeView>(
base::win::Uint32ToHandle(parent_window_id));
#else
NOTREACHED_IN_MIGRATION();
#endif
}
#endif
void PrintBackendServiceImpl::PrintingContextDelegate::SetAppLocale(
const std::string& locale) { … }
struct PrintBackendServiceImpl::ContextContainer { … };
PrintBackendServiceImpl::PrintBackendServiceImpl(
mojo::PendingReceiver<mojom::PrintBackendService> receiver)
: … { … }
PrintBackendServiceImpl::~PrintBackendServiceImpl() = default;
void PrintBackendServiceImpl::InitCommon(
#if BUILDFLAG(IS_WIN)
const std::string& locale,
mojo::PendingRemote<mojom::PrinterXmlParser> remote
#else
const std::string& locale
#endif
) { … }
void PrintBackendServiceImpl::Init(
#if BUILDFLAG(IS_WIN)
const std::string& locale,
mojo::PendingRemote<mojom::PrinterXmlParser> remote
#else
const std::string& locale
#endif
) { … }
void PrintBackendServiceImpl::Poke() { … }
void PrintBackendServiceImpl::EnumeratePrinters(
mojom::PrintBackendService::EnumeratePrintersCallback callback) { … }
void PrintBackendServiceImpl::GetDefaultPrinterName(
mojom::PrintBackendService::GetDefaultPrinterNameCallback callback) { … }
#if BUILDFLAG(IS_CHROMEOS_ASH)
void PrintBackendServiceImpl::GetPrinterSemanticCapsAndDefaults(
const std::string& printer_name,
mojom::PrintBackendService::GetPrinterSemanticCapsAndDefaultsCallback
callback) {
DCHECK(print_backend_);
crash_keys_ = std::make_unique<crash_keys::ScopedPrinterInfo>(
printer_name, print_backend_->GetPrinterDriverInfo(printer_name));
PrinterSemanticCapsAndDefaults printer_caps;
const mojom::ResultCode result =
print_backend_->GetPrinterSemanticCapsAndDefaults(printer_name,
&printer_caps);
if (result != mojom::ResultCode::kSuccess) {
std::move(callback).Run(
mojom::PrinterSemanticCapsAndDefaultsResult::NewResultCode(result));
return;
}
std::move(callback).Run(
mojom::PrinterSemanticCapsAndDefaultsResult::NewPrinterCaps(
std::move(printer_caps)));
}
#endif
void PrintBackendServiceImpl::FetchCapabilities(
const std::string& printer_name,
mojom::PrintBackendService::FetchCapabilitiesCallback callback) { … }
#if BUILDFLAG(IS_WIN)
void PrintBackendServiceImpl::GetPaperPrintableArea(
const std::string& printer_name,
const PrintSettings::RequestedMedia& media,
mojom::PrintBackendService::GetPaperPrintableAreaCallback callback) {
CHECK(print_backend_);
crash_keys_ = std::make_unique<crash_keys::ScopedPrinterInfo>(
printer_name, print_backend_->GetPrinterDriverInfo(printer_name));
std::optional<gfx::Rect> printable_area_um =
print_backend_->GetPaperPrintableArea(printer_name, media.vendor_id,
media.size_microns);
std::move(callback).Run(printable_area_um.value_or(gfx::Rect()));
}
#endif
void PrintBackendServiceImpl::EstablishPrintingContext(uint32_t context_id
#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
,
uint32_t parent_window_id
#endif
) { … }
void PrintBackendServiceImpl::UseDefaultSettings(
uint32_t context_id,
mojom::PrintBackendService::UseDefaultSettingsCallback callback) { … }
#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
void PrintBackendServiceImpl::AskUserForSettings(
uint32_t context_id,
int max_pages,
bool has_selection,
bool is_scripted,
mojom::PrintBackendService::AskUserForSettingsCallback callback) {
PrintingContext* context = GetPrintingContext(context_id);
CHECK(context) << "No context found for id " << context_id;
context->AskUserForSettings(
max_pages, has_selection, is_scripted,
base::BindOnce(&PrintBackendServiceImpl::OnDidAskUserForSettings,
base::Unretained(this), context_id, std::move(callback)));
}
#endif
void PrintBackendServiceImpl::UpdatePrintSettings(
uint32_t context_id,
base::Value::Dict job_settings,
mojom::PrintBackendService::UpdatePrintSettingsCallback callback) { … }
void PrintBackendServiceImpl::StartPrinting(
uint32_t context_id,
int document_cookie,
const std::u16string& document_name,
#if !BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
const std::optional<PrintSettings>& settings,
#endif
mojom::PrintBackendService::StartPrintingCallback callback) { … }
#if BUILDFLAG(IS_WIN)
void PrintBackendServiceImpl::RenderPrintedPage(
int32_t document_cookie,
uint32_t page_index,
mojom::MetafileDataType page_data_type,
base::ReadOnlySharedMemoryRegion serialized_page,
const gfx::Size& page_size,
const gfx::Rect& page_content_rect,
float shrink_factor,
mojom::PrintBackendService::RenderPrintedPageCallback callback) {
DocumentHelper* document_helper = GetDocumentHelper(document_cookie);
DCHECK(document_helper);
document_helper->document_container()
.AsyncCall(&DocumentContainer::DoRenderPrintedPage)
.WithArgs(page_index, page_data_type, std::move(serialized_page),
page_size, page_content_rect, shrink_factor)
.Then(std::move(callback));
}
#endif
void PrintBackendServiceImpl::RenderPrintedDocument(
int32_t document_cookie,
uint32_t page_count,
mojom::MetafileDataType data_type,
base::ReadOnlySharedMemoryRegion serialized_document,
mojom::PrintBackendService::RenderPrintedDocumentCallback callback) { … }
void PrintBackendServiceImpl::DocumentDone(
int document_cookie,
mojom::PrintBackendService::DocumentDoneCallback callback) { … }
void PrintBackendServiceImpl::Cancel(
int document_cookie,
mojom::PrintBackendService::CancelCallback callback) { … }
#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
void PrintBackendServiceImpl::OnDidAskUserForSettings(
uint32_t context_id,
mojom::PrintBackendService::AskUserForSettingsCallback callback,
mojom::ResultCode result) {
auto* context = GetPrintingContext(context_id);
DCHECK(context);
if (result != mojom::ResultCode::kSuccess) {
DLOG(ERROR) << "Did not get user settings, error: " << result;
persistent_printing_contexts_.erase(context_id);
std::move(callback).Run(mojom::PrintSettingsResult::NewResultCode(result));
return;
}
std::string printer_name =
base::UTF16ToUTF8(context->settings().device_name());
crash_keys_ = std::make_unique<crash_keys::ScopedPrinterInfo>(
printer_name, print_backend_->GetPrinterDriverInfo(printer_name));
std::move(callback).Run(mojom::PrintSettingsResult::NewSettings(
*context->TakeAndResetSettings()));
}
#endif
void PrintBackendServiceImpl::OnDidStartPrintingReadyDocument(
DocumentHelper& document_helper,
StartPrintingResult printing_result) { … }
void PrintBackendServiceImpl::OnDidDocumentDone(
DocumentHelper& document_helper,
mojom::PrintBackendService::DocumentDoneCallback callback,
mojom::ResultCode result) { … }
void PrintBackendServiceImpl::OnDidCancel(
DocumentHelper& document_helper,
mojom::PrintBackendService::CancelCallback callback) { … }
std::unique_ptr<PrintBackendServiceImpl::PrintingContextDelegate>
PrintBackendServiceImpl::CreatePrintingContextDelegate() { … }
PrintingContext* PrintBackendServiceImpl::GetPrintingContext(
uint32_t context_id) { … }
PrintBackendServiceImpl::DocumentHelper*
PrintBackendServiceImpl::GetDocumentHelper(int document_cookie) { … }
void PrintBackendServiceImpl::RemoveDocumentHelper(
DocumentHelper& document_helper) { … }
#if BUILDFLAG(IS_WIN)
base::expected<XpsCapabilities, mojom::ResultCode>
PrintBackendServiceImpl::GetXpsCapabilities(const std::string& printer_name) {
ASSIGN_OR_RETURN(
std::string xml,
print_backend_->GetXmlPrinterCapabilitiesForXpsDriver(printer_name),
[&](mojom::ResultCode error) {
DLOG(ERROR) << "Failure getting XPS capabilities of printer "
<< printer_name << ", error: " << error;
return error;
});
mojom::PrinterCapabilitiesValueResultPtr value_result;
if (!xml_parser_remote_->ParseXmlForPrinterCapabilities(xml, &value_result)) {
DLOG(ERROR) << "Failure parsing XML of XPS capabilities of printer "
<< printer_name
<< ", error: ParseXmlForPrinterCapabilities failed";
return base::unexpected(mojom::ResultCode::kFailed);
}
if (value_result->is_result_code()) {
DLOG(ERROR) << "Failure parsing XML of XPS capabilities of printer "
<< printer_name
<< ", error: " << value_result->get_result_code();
return base::unexpected(value_result->get_result_code());
}
return ParseValueForXpsPrinterCapabilities(value_result->get_capabilities())
.transform_error([&](mojom::ResultCode code) {
DLOG(ERROR) << "Failure parsing value of XPS capabilities of printer "
<< printer_name << ", error: " << code;
return code;
});
}
#endif
}