| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "printing/backend/test_print_backend.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "base/check.h" |
| #include "base/containers/contains.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "build/build_config.h" |
| #include "printing/backend/print_backend.h" |
| #include "printing/mojom/print.mojom.h" |
| |
| #if BUILDFLAG(IS_WIN) |
| #include "base/strings/string_number_conversions.h" |
| #include "base/types/expected.h" |
| #endif // BUILDFLAG(IS_WIN) |
| |
| namespace printing { |
| |
| namespace { |
| |
| #if BUILDFLAG(IS_WIN) |
| // Default XML with feature not of interest. |
| constexpr char kXmlDefaultCapabilities[] = |
| R"(<?xml version="1.0" encoding="UTF-8"?> |
| <psf:PrintCapabilities> |
| <!-- Need at least one psf:Feature for |
| ParseValueForXpsPrinterCapabilities() to consider it valid XML --> |
| <psf:Feature name="TestFeature"> |
| </psf:Feature> |
| </psf:PrintCapabilities>)"; |
| #endif // BUILDFLAG(IS_WIN) |
| |
| mojom::ResultCode ReportErrorAccessDenied(const base::Location& from_here) { |
| DLOG(ERROR) << from_here.ToString() << " failed, access denied"; |
| return mojom::ResultCode::kAccessDenied; |
| } |
| |
| mojom::ResultCode ReportErrorNoData(const base::Location& from_here) { |
| DLOG(ERROR) << from_here.ToString() << " failed, no data"; |
| return mojom::ResultCode::kFailed; |
| } |
| |
| mojom::ResultCode ReportErrorNoDevice(const base::Location& from_here) { |
| DLOG(ERROR) << from_here.ToString() << " failed, no such device"; |
| return mojom::ResultCode::kFailed; |
| } |
| |
| #if BUILDFLAG(IS_WIN) |
| mojom::ResultCode ReportErrorNotImplemented(const base::Location& from_here) { |
| DLOG(ERROR) << from_here.ToString() << " failed, method not implemented"; |
| return mojom::ResultCode::kFailed; |
| } |
| #endif // BUILDFLAG(IS_WIN) |
| |
| } // namespace |
| |
| TestPrintBackend::TestPrintBackend() = default; |
| |
| TestPrintBackend::~TestPrintBackend() = default; |
| |
| mojom::ResultCode TestPrintBackend::EnumeratePrinters( |
| PrinterList& printer_list) { |
| DCHECK(printer_list.empty()); |
| if (printer_map_.empty()) |
| return mojom::ResultCode::kSuccess; |
| |
| for (const auto& entry : printer_map_) { |
| const std::unique_ptr<PrinterData>& data = entry.second; |
| |
| // Can only return basic info for printers which have registered info. |
| if (data->info) |
| printer_list.emplace_back(*data->info); |
| } |
| return mojom::ResultCode::kSuccess; |
| } |
| |
| mojom::ResultCode TestPrintBackend::GetDefaultPrinterName( |
| std::string& default_printer) { |
| default_printer = default_printer_name_; |
| return mojom::ResultCode::kSuccess; |
| } |
| |
| mojom::ResultCode TestPrintBackend::GetPrinterBasicInfo( |
| const std::string& printer_name, |
| PrinterBasicInfo* printer_info) { |
| auto found = printer_map_.find(printer_name); |
| if (found == printer_map_.end()) { |
| // Matching entry not found. |
| return ReportErrorNoDevice(FROM_HERE); |
| } |
| |
| const std::unique_ptr<PrinterData>& data = found->second; |
| if (data->blocked_by_permissions) |
| return ReportErrorAccessDenied(FROM_HERE); |
| |
| // Basic info might not have been provided. |
| if (!data->info) |
| return ReportErrorNoData(FROM_HERE); |
| |
| *printer_info = *data->info; |
| return mojom::ResultCode::kSuccess; |
| } |
| |
| mojom::ResultCode TestPrintBackend::GetPrinterSemanticCapsAndDefaults( |
| const std::string& printer_name, |
| PrinterSemanticCapsAndDefaults* printer_caps) { |
| auto found = printer_map_.find(printer_name); |
| if (found == printer_map_.end()) |
| return ReportErrorNoDevice(FROM_HERE); |
| |
| const std::unique_ptr<PrinterData>& data = found->second; |
| if (data->blocked_by_permissions) |
| return ReportErrorAccessDenied(FROM_HERE); |
| |
| // Capabilities might not have been provided. |
| if (!data->caps) |
| return ReportErrorNoData(FROM_HERE); |
| |
| *printer_caps = *data->caps; |
| #if BUILDFLAG(IS_WIN) |
| // The Windows implementation does not load the printable area for all |
| // paper sizes, only for the default size. Mimic this behavior by |
| // defaulting the printable area to the physical size for all other paper |
| // sizes. |
| for (auto& paper : printer_caps->papers) { |
| if (paper != printer_caps->default_paper) { |
| paper.set_printable_area_to_paper_size(); |
| } |
| } |
| #endif |
| return mojom::ResultCode::kSuccess; |
| } |
| |
| #if BUILDFLAG(IS_WIN) |
| mojom::ResultCode TestPrintBackend::GetPrinterCapsAndDefaults( |
| const std::string& printer_name, |
| PrinterCapsAndDefaults* printer_caps) { |
| return ReportErrorNotImplemented(FROM_HERE); |
| } |
| |
| absl::optional<gfx::Rect> TestPrintBackend::GetPaperPrintableArea( |
| const std::string& printer_name, |
| const std::string& paper_vendor_id, |
| const gfx::Size& paper_size_um) { |
| auto found = printer_map_.find(printer_name); |
| if (found == printer_map_.end()) { |
| return absl::nullopt; |
| } |
| |
| const std::unique_ptr<PrinterData>& data = found->second; |
| if (data->blocked_by_permissions) { |
| return absl::nullopt; |
| } |
| |
| // Capabilities might not have been provided. |
| if (!data->caps) { |
| return absl::nullopt; |
| } |
| |
| // Windows uses non-zero IDs to represent specific standard paper sizes. |
| unsigned id; |
| if (base::StringToUint(paper_vendor_id, &id) && id) { |
| PrinterSemanticCapsAndDefaults::Papers& papers = data->caps->papers; |
| for (auto paper = papers.begin(); paper != papers.end(); ++paper) { |
| if (paper->vendor_id() == paper_vendor_id) { |
| return paper->printable_area_um(); |
| } |
| } |
| |
| // No match for the specified paper identification. |
| return absl::nullopt; |
| } |
| |
| // Custom paper size. For testing just treat as match to paper size. |
| return gfx::Rect(paper_size_um); |
| } |
| #endif // BUILDFLAG(IS_WIN) |
| |
| std::vector<std::string> TestPrintBackend::GetPrinterDriverInfo( |
| const std::string& printer_name) { |
| // not implemented |
| return std::vector<std::string>(); |
| } |
| |
| bool TestPrintBackend::IsValidPrinter(const std::string& printer_name) { |
| return base::Contains(printer_map_, printer_name); |
| } |
| |
| #if BUILDFLAG(IS_WIN) |
| base::expected<std::string, mojom::ResultCode> |
| TestPrintBackend::GetXmlPrinterCapabilitiesForXpsDriver( |
| const std::string& printer_name) { |
| auto found = printer_map_.find(printer_name); |
| if (found == printer_map_.end()) |
| return base::unexpected(ReportErrorNoDevice(FROM_HERE)); |
| |
| const PrinterData* data = found->second.get(); |
| if (data->blocked_by_permissions) |
| return base::unexpected(ReportErrorAccessDenied(FROM_HERE)); |
| |
| // XML capabilities might not have been provided. |
| if (data->capabilities_xml.empty()) |
| return base::unexpected(ReportErrorNoData(FROM_HERE)); |
| |
| return data->capabilities_xml; |
| } |
| #endif // BUILDFLAG(IS_WIN) |
| |
| void TestPrintBackend::SetDefaultPrinterName(const std::string& printer_name) { |
| if (default_printer_name_ == printer_name) |
| return; |
| |
| auto found = printer_map_.find(printer_name); |
| if (found == printer_map_.end()) { |
| DLOG(ERROR) << "Unable to set an unknown printer as the default. Unknown " |
| << "printer name: " << printer_name; |
| return; |
| } |
| |
| // Previous default printer is no longer the default. |
| const std::unique_ptr<PrinterData>& new_default_data = found->second; |
| if (!default_printer_name_.empty()) |
| printer_map_[default_printer_name_]->info->is_default = false; |
| |
| // Now update new printer as default. |
| default_printer_name_ = printer_name; |
| if (!default_printer_name_.empty()) |
| new_default_data->info->is_default = true; |
| } |
| |
| void TestPrintBackend::AddValidPrinter( |
| const std::string& printer_name, |
| std::unique_ptr<PrinterSemanticCapsAndDefaults> caps, |
| std::unique_ptr<PrinterBasicInfo> info) { |
| AddPrinter(printer_name, std::move(caps), std::move(info), |
| /*blocked_by_permissions=*/false); |
| #if BUILDFLAG(IS_WIN) |
| SetXmlCapabilitiesForPrinter(printer_name, kXmlDefaultCapabilities); |
| #endif // BUILDFLAG(IS_WIN) |
| } |
| |
| void TestPrintBackend::AddInvalidDataPrinter(const std::string& printer_name) { |
| // The blank fields in default `PrinterBasicInfo` cause Mojom data validation |
| // errors. |
| AddPrinter(printer_name, std::make_unique<PrinterSemanticCapsAndDefaults>(), |
| std::make_unique<PrinterBasicInfo>(), |
| /*blocked_by_permissions=*/false); |
| } |
| |
| void TestPrintBackend::AddAccessDeniedPrinter(const std::string& printer_name) { |
| AddPrinter(printer_name, /*caps=*/nullptr, /*info=*/nullptr, |
| /*blocked_by_permissions=*/true); |
| } |
| |
| #if BUILDFLAG(IS_WIN) |
| void TestPrintBackend::SetXmlCapabilitiesForPrinter( |
| const std::string& printer_name, |
| const std::string& capabilities_xml) { |
| auto found = printer_map_.find(printer_name); |
| if (found == printer_map_.end()) { |
| DLOG(ERROR) << "Unable to find printer. Unknown printer name: " |
| << printer_name; |
| return; |
| } |
| found->second->capabilities_xml = capabilities_xml; |
| } |
| #endif // BUILDFLAG(IS_WIN) |
| |
| void TestPrintBackend::AddPrinter( |
| const std::string& printer_name, |
| std::unique_ptr<PrinterSemanticCapsAndDefaults> caps, |
| std::unique_ptr<PrinterBasicInfo> info, |
| bool blocked_by_permissions) { |
| DCHECK(!printer_name.empty()); |
| |
| const bool is_default = info && info->is_default; |
| printer_map_[printer_name] = std::make_unique<PrinterData>( |
| std::move(caps), std::move(info), blocked_by_permissions); |
| |
| // Ensure that default settings are honored if more than one is attempted to |
| // be marked as default or if this prior default should no longer be so. |
| if (is_default) |
| SetDefaultPrinterName(printer_name); |
| else if (default_printer_name_ == printer_name) |
| default_printer_name_.clear(); |
| } |
| |
| TestPrintBackend::PrinterData::PrinterData( |
| std::unique_ptr<PrinterSemanticCapsAndDefaults> caps, |
| std::unique_ptr<PrinterBasicInfo> info, |
| bool blocked_by_permissions) |
| : caps(std::move(caps)), |
| info(std::move(info)), |
| blocked_by_permissions(blocked_by_permissions) {} |
| |
| TestPrintBackend::PrinterData::~PrinterData() = default; |
| |
| } // namespace printing |