| // Copyright 2020 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/mojom/print_backend_mojom_traits.h" |
| |
| #include <map> |
| |
| #include "base/containers/contains.h" |
| #include "base/logging.h" |
| #include "build/build_config.h" |
| #include "ui/gfx/geometry/mojom/geometry.mojom-shared.h" |
| #include "ui/gfx/geometry/mojom/geometry_mojom_traits.h" |
| |
| // Implementations of std::less<> here are for purposes of detecting duplicate |
| // entries in arrays. They do not require strict checks of all fields, but |
| // instead focus on identifying attributes that would be used to clearly |
| // distinguish properties to a user. E.g., if two entries have the same |
| // displayable name but different corresponding values, consider that to be a |
| // duplicate for these purposes. |
| namespace std { |
| |
| template <> |
| struct less<::gfx::Size> { |
| bool operator()(const ::gfx::Size& lhs, const ::gfx::Size& rhs) const { |
| if (lhs.width() < rhs.width()) |
| return true; |
| return lhs.height() < rhs.height(); |
| } |
| }; |
| |
| template <> |
| struct less<::printing::PrinterSemanticCapsAndDefaults::Paper> { |
| bool operator()( |
| const ::printing::PrinterSemanticCapsAndDefaults::Paper& lhs, |
| const ::printing::PrinterSemanticCapsAndDefaults::Paper& rhs) const { |
| if (lhs.display_name() < rhs.display_name()) { |
| return true; |
| } |
| return lhs.vendor_id() < rhs.vendor_id(); |
| } |
| }; |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| template <> |
| struct less<::printing::AdvancedCapability> { |
| bool operator()(const ::printing::AdvancedCapability& lhs, |
| const ::printing::AdvancedCapability& rhs) const { |
| if (lhs.name < rhs.name) |
| return true; |
| return lhs.display_name < rhs.display_name; |
| } |
| }; |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| } // namespace std |
| |
| namespace mojo { |
| |
| namespace { |
| template <class Key> |
| class DuplicateChecker { |
| public: |
| bool HasDuplicates(const std::vector<Key>& items) { |
| std::map<Key, bool> items_encountered; |
| for (auto it = items.begin(); it != items.end(); ++it) { |
| auto found = items_encountered.find(*it); |
| if (found != items_encountered.end()) |
| return true; |
| items_encountered[*it] = true; |
| } |
| return false; |
| } |
| }; |
| |
| } // namespace |
| |
| // static |
| bool StructTraits<printing::mojom::PrinterBasicInfoDataView, |
| printing::PrinterBasicInfo>:: |
| Read(printing::mojom::PrinterBasicInfoDataView data, |
| printing::PrinterBasicInfo* out) { |
| if (!data.ReadPrinterName(&out->printer_name) || |
| !data.ReadDisplayName(&out->display_name) || |
| !data.ReadPrinterDescription(&out->printer_description)) { |
| return false; |
| } |
| out->printer_status = data.printer_status(); |
| out->is_default = data.is_default(); |
| if (!data.ReadOptions(&out->options)) |
| return false; |
| |
| // There should be a non-empty value for `printer_name` since it needs to |
| // uniquely identify the printer with the operating system among multiple |
| // possible destinations. |
| if (out->printer_name.empty()) { |
| DLOG(ERROR) << "The printer name must not be empty."; |
| return false; |
| } |
| // There should be a non-empty value for `display_name` since it needs to |
| // uniquely identify the printer in user dialogs among multiple possible |
| // destinations. |
| if (out->display_name.empty()) { |
| DLOG(ERROR) << "The printer's display name must not be empty."; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // static |
| bool StructTraits<printing::mojom::PaperDataView, |
| printing::PrinterSemanticCapsAndDefaults::Paper>:: |
| Read(printing::mojom::PaperDataView data, |
| printing::PrinterSemanticCapsAndDefaults::Paper* out) { |
| std::string display_name; |
| std::string vendor_id; |
| gfx::Size size_um; |
| absl::optional<gfx::Rect> maybe_printable_area_um; |
| if (!data.ReadDisplayName(&display_name) || !data.ReadVendorId(&vendor_id) || |
| !data.ReadSizeUm(&size_um) || |
| !data.ReadPrintableAreaUm(&maybe_printable_area_um)) { |
| return false; |
| } |
| int max_height_um = data.max_height_um(); |
| bool has_borderless_variant = data.has_borderless_variant(); |
| |
| // For backwards compatibility, allow printable area to be missing. Set the |
| // default printable area to be the page size. |
| gfx::Rect printable_area_um = |
| maybe_printable_area_um.value_or(gfx::Rect(size_um)); |
| |
| // Allow empty Papers, since PrinterSemanticCapsAndDefaults can have empty |
| // default Papers. |
| if (display_name.empty() && vendor_id.empty() && size_um.IsEmpty() && |
| printable_area_um.IsEmpty() && max_height_um == 0) { |
| *out = printing::PrinterSemanticCapsAndDefaults::Paper(); |
| return true; |
| } |
| |
| // If `max_height_um` is specified, ensure it's larger than size. |
| if (max_height_um > 0 && max_height_um < size_um.height()) { |
| return false; |
| } |
| |
| // Invalid if the printable area is empty or if the printable area is out of |
| // bounds of the paper size. `max_height_um` doesn't need to be checked here |
| // since `printable_area_um` is always relative to `size_um`. |
| if (printable_area_um.IsEmpty() || |
| !gfx::Rect(size_um).Contains(printable_area_um)) { |
| return false; |
| } |
| *out = printing::PrinterSemanticCapsAndDefaults::Paper( |
| display_name, vendor_id, size_um, printable_area_um, max_height_um, |
| has_borderless_variant); |
| return true; |
| } |
| |
| // static |
| bool StructTraits<printing::mojom::MediaTypeDataView, |
| printing::PrinterSemanticCapsAndDefaults::MediaType>:: |
| Read(printing::mojom::MediaTypeDataView data, |
| printing::PrinterSemanticCapsAndDefaults::MediaType* out) { |
| return data.ReadDisplayName(&out->display_name) && |
| data.ReadVendorId(&out->vendor_id); |
| } |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| // static |
| printing::mojom::AdvancedCapabilityType |
| EnumTraits<printing::mojom::AdvancedCapabilityType, |
| ::printing::AdvancedCapability::Type>:: |
| ToMojom(::printing::AdvancedCapability::Type input) { |
| switch (input) { |
| case ::printing::AdvancedCapability::Type::kBoolean: |
| return printing::mojom::AdvancedCapabilityType::kBoolean; |
| case ::printing::AdvancedCapability::Type::kFloat: |
| return printing::mojom::AdvancedCapabilityType::kFloat; |
| case ::printing::AdvancedCapability::Type::kInteger: |
| return printing::mojom::AdvancedCapabilityType::kInteger; |
| case ::printing::AdvancedCapability::Type::kString: |
| return printing::mojom::AdvancedCapabilityType::kString; |
| } |
| NOTREACHED(); |
| return printing::mojom::AdvancedCapabilityType::kString; |
| } |
| |
| // static |
| bool EnumTraits<printing::mojom::AdvancedCapabilityType, |
| ::printing::AdvancedCapability::Type>:: |
| FromMojom(printing::mojom::AdvancedCapabilityType input, |
| ::printing::AdvancedCapability::Type* output) { |
| switch (input) { |
| case printing::mojom::AdvancedCapabilityType::kBoolean: |
| *output = ::printing::AdvancedCapability::Type::kBoolean; |
| return true; |
| case printing::mojom::AdvancedCapabilityType::kFloat: |
| *output = ::printing::AdvancedCapability::Type::kFloat; |
| return true; |
| case printing::mojom::AdvancedCapabilityType::kInteger: |
| *output = ::printing::AdvancedCapability::Type::kInteger; |
| return true; |
| case printing::mojom::AdvancedCapabilityType::kString: |
| *output = ::printing::AdvancedCapability::Type::kString; |
| return true; |
| } |
| NOTREACHED(); |
| return false; |
| } |
| |
| // static |
| bool StructTraits<printing::mojom::AdvancedCapabilityValueDataView, |
| ::printing::AdvancedCapabilityValue>:: |
| Read(printing::mojom::AdvancedCapabilityValueDataView data, |
| ::printing::AdvancedCapabilityValue* out) { |
| return data.ReadName(&out->name) && data.ReadDisplayName(&out->display_name); |
| } |
| |
| // static |
| bool StructTraits<printing::mojom::AdvancedCapabilityDataView, |
| ::printing::AdvancedCapability>:: |
| Read(printing::mojom::AdvancedCapabilityDataView data, |
| ::printing::AdvancedCapability* out) { |
| return data.ReadName(&out->name) && |
| data.ReadDisplayName(&out->display_name) && |
| data.ReadType(&out->type) && |
| data.ReadDefaultValue(&out->default_value) && |
| data.ReadValues(&out->values); |
| } |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| #if BUILDFLAG(IS_WIN) |
| // static |
| bool StructTraits<printing::mojom::PageOutputQualityAttributeDataView, |
| printing::PageOutputQualityAttribute>:: |
| Read(printing::mojom::PageOutputQualityAttributeDataView data, |
| printing::PageOutputQualityAttribute* out) { |
| return data.ReadDisplayName(&out->display_name) && data.ReadName(&out->name); |
| } |
| |
| // static |
| bool StructTraits<printing::mojom::PageOutputQualityDataView, |
| printing::PageOutputQuality>:: |
| Read(printing::mojom::PageOutputQualityDataView data, |
| printing::PageOutputQuality* out) { |
| return data.ReadQualities(&out->qualities) && |
| data.ReadDefaultQuality(&out->default_quality); |
| } |
| #endif |
| |
| // static |
| bool StructTraits<printing::mojom::PrinterSemanticCapsAndDefaultsDataView, |
| printing::PrinterSemanticCapsAndDefaults>:: |
| Read(printing::mojom::PrinterSemanticCapsAndDefaultsDataView data, |
| printing::PrinterSemanticCapsAndDefaults* out) { |
| absl::optional<printing::PrinterSemanticCapsAndDefaults::MediaTypes> |
| media_types; |
| absl::optional<printing::PrinterSemanticCapsAndDefaults::MediaType> |
| default_media_type; |
| |
| out->collate_capable = data.collate_capable(); |
| out->collate_default = data.collate_default(); |
| out->copies_max = data.copies_max(); |
| if (!data.ReadDuplexModes(&out->duplex_modes) || |
| !data.ReadDuplexDefault(&out->duplex_default)) { |
| return false; |
| } |
| out->color_changeable = data.color_changeable(); |
| out->color_default = data.color_default(); |
| if (!data.ReadColorModel(&out->color_model) || |
| !data.ReadBwModel(&out->bw_model) || !data.ReadPapers(&out->papers) || |
| !data.ReadUserDefinedPapers(&out->user_defined_papers) || |
| !data.ReadDefaultPaper(&out->default_paper) || |
| !data.ReadDpis(&out->dpis) || !data.ReadDefaultDpi(&out->default_dpi)) { |
| return false; |
| } |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| out->pin_supported = data.pin_supported(); |
| if (!data.ReadAdvancedCapabilities(&out->advanced_capabilities)) |
| return false; |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| // Extra validity checks. |
| |
| // Can not have less than one copy. |
| if (out->copies_max < 1) { |
| DLOG(ERROR) << "Must have copies_max greater than zero."; |
| return false; |
| } |
| |
| // There should not be duplicates in certain arrays. |
| DuplicateChecker<printing::mojom::DuplexMode> duplex_modes_dup_checker; |
| if (duplex_modes_dup_checker.HasDuplicates(out->duplex_modes)) { |
| DLOG(ERROR) << "Duplicate duplex_modes detected."; |
| return false; |
| } |
| |
| DuplicateChecker<printing::PrinterSemanticCapsAndDefaults::Paper> |
| user_defined_papers_dup_checker; |
| if (user_defined_papers_dup_checker.HasDuplicates(out->user_defined_papers)) { |
| DLOG(ERROR) << "Duplicate user_defined_papers detected."; |
| return false; |
| } |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| DuplicateChecker<printing::AdvancedCapability> |
| advanced_capabilities_dup_checker; |
| if (advanced_capabilities_dup_checker.HasDuplicates( |
| out->advanced_capabilities)) { |
| DLOG(ERROR) << "Duplicate advanced_capabilities detected."; |
| return false; |
| } |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| #if BUILDFLAG(IS_WIN) |
| if (!data.ReadPageOutputQuality(&out->page_output_quality)) { |
| return false; |
| } |
| DuplicateChecker<printing::PageOutputQualityAttribute> |
| page_output_quality_dup_checker; |
| if (out->page_output_quality) { |
| printing::PageOutputQualityAttributes qualities = |
| out->page_output_quality->qualities; |
| absl::optional<std::string> default_quality = |
| out->page_output_quality->default_quality; |
| |
| // If non-null `default_quality`, there should be a matching element in |
| // `qualities` array. |
| if (default_quality) { |
| if (!base::Contains(qualities, *default_quality, |
| &printing::PageOutputQualityAttribute::name)) { |
| DLOG(ERROR) << "Non-null default quality, but page output qualities " |
| "does not contain default quality"; |
| return false; |
| } |
| } |
| |
| // There should be no duplicates in `qualities` array. |
| if (page_output_quality_dup_checker.HasDuplicates(qualities)) { |
| DLOG(ERROR) << "Duplicate page output qualities detected."; |
| return false; |
| } |
| } |
| #endif |
| |
| if (!data.ReadMediaTypes(&media_types) || |
| !data.ReadDefaultMediaType(&default_media_type)) { |
| return false; |
| } |
| |
| if (media_types.has_value()) { |
| out->media_types = media_types.value(); |
| } |
| if (default_media_type.has_value()) { |
| out->default_media_type = default_media_type.value(); |
| } |
| |
| return true; |
| } |
| |
| } // namespace mojo |