[M120 Migration][VD] Remove accessing oom_score_adj in zygote process
[platform/framework/web/chromium-efl.git] / printing / printing_context_mac.mm
1 // Copyright 2011 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "printing/printing_context_mac.h"
6
7 #import <AppKit/AppKit.h>
8 #include <CoreFoundation/CoreFoundation.h>
9 #import <QuartzCore/QuartzCore.h>
10 #include <cups/cups.h>
11
12 #import <iomanip>
13 #import <numeric>
14
15 #include "base/apple/bridging.h"
16 #include "base/apple/foundation_util.h"
17 #include "base/apple/osstatus_logging.h"
18 #include "base/apple/scoped_cftyperef.h"
19 #include "base/apple/scoped_typeref.h"
20 #include "base/check_op.h"
21 #include "base/strings/string_piece.h"
22 #include "base/strings/sys_string_conversions.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "base/values.h"
25 #include "build/build_config.h"
26 #include "printing/buildflags/buildflags.h"
27 #include "printing/metafile.h"
28 #include "printing/mojom/print.mojom.h"
29 #include "printing/print_job_constants_cups.h"
30 #include "printing/print_settings_initializer_mac.h"
31 #include "printing/printing_features.h"
32 #include "printing/units.h"
33
34 #if BUILDFLAG(ENABLE_OOP_PRINTING_NO_OOP_BASIC_PRINT_DIALOG)
35 #include "base/numerics/safe_conversions.h"
36 #include "base/types/expected.h"
37 #endif
38
39 namespace printing {
40
41 namespace {
42
43 template <typename T>
44 struct ScopedPMTypeTraits {
45   static T InvalidValue() { return nullptr; }
46   static T Retain(T object) {
47     PMRetain(object);
48     return object;
49   }
50   static void Release(T object) { PMRelease(object); }
51 };
52
53 template <typename T>
54 using ScopedPMType = base::apple::ScopedTypeRef<T, ScopedPMTypeTraits<T>>;
55
56 const int kMaxPaperSizeDifferenceInPoints = 2;
57
58 // Return true if PPD name of paper is equal.
59 bool IsPaperNameEqual(CFStringRef name1, const PMPaper& paper2) {
60   CFStringRef name2 = nullptr;
61   return (name1 && PMPaperGetPPDPaperName(paper2, &name2) == noErr) &&
62          (CFStringCompare(name1, name2, kCFCompareCaseInsensitive) ==
63           kCFCompareEqualTo);
64 }
65
66 PMPaper MatchPaper(CFArrayRef paper_list,
67                    CFStringRef name,
68                    double width,
69                    double height) {
70   double best_match = std::numeric_limits<double>::max();
71   PMPaper best_matching_paper = nullptr;
72
73   CFIndex num_papers = CFArrayGetCount(paper_list);
74   for (CFIndex i = 0; i < num_papers; ++i) {
75     PMPaper paper = (PMPaper)CFArrayGetValueAtIndex(paper_list, i);
76     double paper_width = 0.0;
77     double paper_height = 0.0;
78     PMPaperGetWidth(paper, &paper_width);
79     PMPaperGetHeight(paper, &paper_height);
80     double difference =
81         std::max(fabs(width - paper_width), fabs(height - paper_height));
82
83     // Ignore papers with size too different from expected.
84     if (difference > kMaxPaperSizeDifferenceInPoints) {
85       continue;
86     }
87
88     if (name && IsPaperNameEqual(name, paper))
89       return paper;
90
91     if (difference < best_match) {
92       best_matching_paper = paper;
93       best_match = difference;
94     }
95   }
96   return best_matching_paper;
97 }
98
99 bool IsIppColorModelColorful(mojom::ColorModel color_model) {
100   // Accept `kUnknownColorModel` as it can occur with raw CUPS printers.
101   // Treat it similarly to the behavior in  `GetColorModelForModel()`.
102   if (color_model == mojom::ColorModel::kUnknownColorModel) {
103     return false;
104   }
105   return IsColorModelSelected(color_model).value();
106 }
107
108 #if BUILDFLAG(ENABLE_OOP_PRINTING_NO_OOP_BASIC_PRINT_DIALOG)
109
110 // The set of "capture" routines run in the browser process.
111 // The set of "apply" routines run in the Print Backend service.
112
113 base::expected<std::vector<uint8_t>, mojom::ResultCode>
114 CaptureSystemPrintSettings(PMPrintSettings& print_settings) {
115   CFDataRef data_ref = nullptr;
116   OSStatus status = PMPrintSettingsCreateDataRepresentation(
117       print_settings, &data_ref, kPMDataFormatXMLDefault);
118   if (status != noErr) {
119     OSSTATUS_LOG(ERROR, status)
120         << "Failed to create data representation of print settings";
121     return base::unexpected(mojom::ResultCode::kFailed);
122   }
123
124   base::apple::ScopedCFTypeRef<CFDataRef> scoped_data_ref(data_ref);
125   uint32_t data_size = CFDataGetLength(data_ref);
126   std::vector<uint8_t> capture_data(data_size);
127   CFDataGetBytes(data_ref, CFRangeMake(0, data_size),
128                  static_cast<UInt8*>(&capture_data.front()));
129   return capture_data;
130 }
131
132 base::expected<std::vector<uint8_t>, mojom::ResultCode> CaptureSystemPageFormat(
133     PMPageFormat& page_format) {
134   CFDataRef data_ref = nullptr;
135   OSStatus status = PMPageFormatCreateDataRepresentation(
136       page_format, &data_ref, kPMDataFormatXMLDefault);
137   if (status != noErr) {
138     OSSTATUS_LOG(ERROR, status)
139         << "Failed to create data representation of page format";
140     return base::unexpected(mojom::ResultCode::kFailed);
141   }
142
143   uint32_t data_size = CFDataGetLength(data_ref);
144   std::vector<uint8_t> capture_data(data_size);
145   CFDataGetBytes(data_ref, CFRangeMake(0, data_size),
146                  static_cast<UInt8*>(&capture_data.front()));
147   return capture_data;
148 }
149
150 base::expected<base::apple::ScopedCFTypeRef<CFStringRef>, mojom::ResultCode>
151 CaptureSystemDestinationFormat(PMPrintSession& print_session,
152                                PMPrintSettings& print_settings) {
153   CFStringRef destination_format_ref = nullptr;
154   OSStatus status = PMSessionCopyDestinationFormat(
155       print_session, print_settings, &destination_format_ref);
156   if (status != noErr) {
157     OSSTATUS_LOG(ERROR, status) << "Failed to get printing destination format";
158     return base::unexpected(mojom::ResultCode::kFailed);
159   }
160   return base::apple::ScopedCFTypeRef<CFStringRef>(destination_format_ref);
161 }
162
163 base::expected<base::apple::ScopedCFTypeRef<CFURLRef>, mojom::ResultCode>
164 CaptureSystemDestinationLocation(PMPrintSession& print_session,
165                                  PMPrintSettings& print_settings) {
166   CFURLRef destination_location_ref = nullptr;
167   OSStatus status = PMSessionCopyDestinationLocation(
168       print_session, print_settings, &destination_location_ref);
169   if (status != noErr) {
170     OSSTATUS_LOG(ERROR, status)
171         << "Failed to get printing destination location";
172     return base::unexpected(mojom::ResultCode::kFailed);
173   }
174   return base::apple::ScopedCFTypeRef<CFURLRef>(destination_location_ref);
175 }
176
177 mojom::ResultCode CaptureSystemPrintDialogData(NSPrintInfo* print_info,
178                                                PrintSettings* settings) {
179   PMPrintSettings print_settings =
180       (PMPrintSettings)[print_info PMPrintSettings];
181
182   base::expected<std::vector<uint8_t>, mojom::ResultCode> print_settings_data =
183       CaptureSystemPrintSettings(print_settings);
184   if (!print_settings_data.has_value()) {
185     return print_settings_data.error();
186   }
187
188   PMPageFormat page_format =
189       static_cast<PMPageFormat>([print_info PMPageFormat]);
190
191   base::expected<std::vector<uint8_t>, mojom::ResultCode> page_format_data =
192       CaptureSystemPageFormat(page_format);
193   if (!page_format_data.has_value()) {
194     return page_format_data.error();
195   }
196
197   PMPrintSession print_session =
198       static_cast<PMPrintSession>([print_info PMPrintSession]);
199
200   PMDestinationType destination_type = kPMDestinationInvalid;
201   PMSessionGetDestinationType(print_session, print_settings, &destination_type);
202
203   base::expected<base::apple::ScopedCFTypeRef<CFStringRef>, mojom::ResultCode>
204       destination_format =
205           CaptureSystemDestinationFormat(print_session, print_settings);
206   if (!destination_format.has_value()) {
207     return destination_format.error();
208   }
209
210   base::expected<base::apple::ScopedCFTypeRef<CFURLRef>, mojom::ResultCode>
211       destination_location =
212           CaptureSystemDestinationLocation(print_session, print_settings);
213   if (!destination_location.has_value()) {
214     return destination_location.error();
215   }
216
217   base::Value::Dict dialog_data;
218   dialog_data.Set(kMacSystemPrintDialogDataPrintSettings,
219                   std::move(print_settings_data.value()));
220   dialog_data.Set(kMacSystemPrintDialogDataPageFormat,
221                   std::move(page_format_data.value()));
222   dialog_data.Set(kMacSystemPrintDialogDataDestinationType, destination_type);
223   if (destination_format.value()) {
224     dialog_data.Set(
225         kMacSystemPrintDialogDataDestinationFormat,
226         base::SysCFStringRefToUTF8(destination_format.value().get()));
227   }
228   if (destination_location.value()) {
229     dialog_data.Set(kMacSystemPrintDialogDataDestinationLocation,
230                     base::SysCFStringRefToUTF8(
231                         CFURLGetString(destination_location.value().get())));
232   }
233   settings->set_system_print_dialog_data(std::move(dialog_data));
234   return mojom::ResultCode::kSuccess;
235 }
236
237 void ApplySystemPrintSettings(const base::Value::Dict& system_print_dialog_data,
238                               NSPrintInfo* print_info,
239                               PMPrintSession& print_session,
240                               PMPrintSettings& print_settings) {
241   const base::Value::BlobStorage* data =
242       system_print_dialog_data.FindBlob(kMacSystemPrintDialogDataPrintSettings);
243   CHECK(data);
244   uint32_t data_size = data->size();
245   CHECK_GT(data_size, 0u);
246   CFDataRef data_ref =
247       CFDataCreate(kCFAllocatorDefault,
248                    static_cast<const UInt8*>(&data->front()), data_size);
249   CHECK(data_ref);
250   base::apple::ScopedCFTypeRef<CFDataRef> scoped_data_ref(data_ref);
251
252   ScopedPMType<PMPrintSettings> new_print_settings;
253   OSStatus status = PMPrintSettingsCreateWithDataRepresentation(
254       data_ref, new_print_settings.InitializeInto());
255   CHECK_EQ(status, noErr) << logging::DescriptionFromOSStatus(status);
256
257   status = PMSessionValidatePrintSettings(
258       print_session, new_print_settings.get(), kPMDontWantBoolean);
259   CHECK_EQ(status, noErr) << logging::DescriptionFromOSStatus(status);
260   status = PMCopyPrintSettings(new_print_settings.get(), print_settings);
261   CHECK_EQ(status, noErr) << logging::DescriptionFromOSStatus(status);
262
263   [print_info updateFromPMPrintSettings];
264 }
265
266 void ApplySystemPageFormat(const base::Value::Dict& system_print_dialog_data,
267                            NSPrintInfo* print_info,
268                            PMPrintSession& print_session,
269                            PMPageFormat& page_format) {
270   const base::Value::BlobStorage* data =
271       system_print_dialog_data.FindBlob(kMacSystemPrintDialogDataPageFormat);
272   CHECK(data);
273   uint32_t data_size = data->size();
274   CHECK_GT(data_size, 0u);
275   CFDataRef data_ref =
276       CFDataCreate(kCFAllocatorDefault,
277                    static_cast<const UInt8*>(&data->front()), data_size);
278   CHECK(data_ref);
279
280   ScopedPMType<PMPageFormat> new_page_format;
281   OSStatus status = PMPageFormatCreateWithDataRepresentation(
282       data_ref, new_page_format.InitializeInto());
283   CHECK_EQ(status, noErr) << logging::DescriptionFromOSStatus(status);
284   status = PMSessionValidatePageFormat(print_session, page_format,
285                                        kPMDontWantBoolean);
286   status = PMCopyPageFormat(new_page_format.get(), page_format);
287   CHECK_EQ(status, noErr) << logging::DescriptionFromOSStatus(status);
288
289   [print_info updateFromPMPageFormat];
290 }
291
292 void ApplySystemDestination(const std::u16string& device_name,
293                             const base::Value::Dict& system_print_dialog_data,
294                             PMPrintSession& print_session,
295                             PMPrintSettings& print_settings) {
296   absl::optional<int> destination_type = system_print_dialog_data.FindInt(
297       kMacSystemPrintDialogDataDestinationType);
298
299   CHECK(destination_type.has_value());
300   CHECK(base::IsValueInRangeForNumericType<uint16_t>(*destination_type));
301
302   const std::string* destination_format_str =
303       system_print_dialog_data.FindString(
304           kMacSystemPrintDialogDataDestinationFormat);
305   const std::string* destination_location_str =
306       system_print_dialog_data.FindString(
307           kMacSystemPrintDialogDataDestinationLocation);
308
309   base::apple::ScopedCFTypeRef<CFStringRef> destination_format;
310   if (destination_format_str) {
311     destination_format.reset(
312         base::SysUTF8ToCFStringRef(*destination_format_str));
313   }
314
315   base::apple::ScopedCFTypeRef<CFURLRef> destination_location;
316   if (destination_location_str) {
317     destination_location.reset(CFURLCreateWithFileSystemPath(
318         kCFAllocatorDefault,
319         base::SysUTF8ToCFStringRef(*destination_location_str).get(),
320         kCFURLPOSIXPathStyle,
321         /*isDirectory=*/FALSE));
322   }
323
324   base::apple::ScopedCFTypeRef<CFStringRef> destination_name(
325       base::SysUTF16ToCFStringRef(device_name));
326   ScopedPMType<PMPrinter> printer(
327       PMPrinterCreateFromPrinterID(destination_name.get()));
328   CHECK(printer);
329   OSStatus status = PMSessionSetCurrentPMPrinter(print_session, printer.get());
330   CHECK_EQ(status, noErr) << logging::DescriptionFromOSStatus(status);
331
332   status = PMSessionSetDestination(
333       print_session, print_settings,
334       static_cast<PMDestinationType>(*destination_type),
335       destination_format.get(), destination_location.get());
336   CHECK_EQ(status, noErr) << logging::DescriptionFromOSStatus(status);
337 }
338
339 void ApplySystemPrintDialogData(
340     const std::u16string& device_name,
341     const base::Value::Dict& system_print_dialog_data,
342     NSPrintInfo* print_info) {
343   PMPrintSession print_session =
344       static_cast<PMPrintSession>([print_info PMPrintSession]);
345   PMPrintSettings print_settings =
346       static_cast<PMPrintSettings>([print_info PMPrintSettings]);
347   PMPageFormat page_format =
348       static_cast<PMPageFormat>([print_info PMPageFormat]);
349
350   ApplySystemDestination(device_name, system_print_dialog_data, print_session,
351                          print_settings);
352   ApplySystemPrintSettings(system_print_dialog_data, print_info, print_session,
353                            print_settings);
354   ApplySystemPageFormat(system_print_dialog_data, print_info, print_session,
355                         page_format);
356 }
357 #endif  // BUILDFLAG(ENABLE_OOP_PRINTING_NO_OOP_BASIC_PRINT_DIALOG)
358
359 }  // namespace
360
361 // static
362 std::unique_ptr<PrintingContext> PrintingContext::CreateImpl(
363     Delegate* delegate,
364     bool skip_system_calls) {
365   auto context = std::make_unique<PrintingContextMac>(delegate);
366 #if BUILDFLAG(ENABLE_OOP_PRINTING)
367   if (skip_system_calls)
368     context->set_skip_system_calls();
369 #endif
370   return context;
371 }
372
373 PrintingContextMac::PrintingContextMac(Delegate* delegate)
374     : PrintingContext(delegate),
375       print_info_([NSPrintInfo.sharedPrintInfo copy]) {}
376
377 PrintingContextMac::~PrintingContextMac() {
378   ReleaseContext();
379 }
380
381 void PrintingContextMac::AskUserForSettings(int max_pages,
382                                             bool has_selection,
383                                             bool is_scripted,
384                                             PrintSettingsCallback callback) {
385   // Exceptions can also happen when the NSPrintPanel is being
386   // deallocated, so it must be autoreleased within this scope.
387   @autoreleasepool {
388     DCHECK(NSThread.isMainThread);
389
390     // We deliberately don't feed max_pages into the dialog, because setting
391     // NSPrintLastPage makes the print dialog pre-select the option to only
392     // print a range.
393
394     // TODO(stuartmorgan): implement 'print selection only' (probably requires
395     // adding a new custom view to the panel on 10.5; 10.6 has
396     // NSPrintPanelShowsPrintSelection).
397     NSPrintPanel* panel = [NSPrintPanel printPanel];
398     panel.options |= NSPrintPanelShowsPaperSize | NSPrintPanelShowsOrientation |
399                      NSPrintPanelShowsScaling;
400
401     // Set the print job title text.
402     gfx::NativeView parent_view = delegate_->GetParentView();
403     if (parent_view) {
404       NSString* job_title = parent_view.GetNativeNSView().window.title;
405       if (job_title) {
406         PMPrintSettings print_settings =
407             static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
408         PMPrintSettingsSetJobName(print_settings,
409                                   base::apple::NSToCFPtrCast(job_title));
410         [print_info_ updateFromPMPrintSettings];
411       }
412     }
413
414     // TODO(stuartmorgan): We really want a tab sheet here, not a modal window.
415     // Will require restructuring the PrintingContext API to use a callback.
416
417     // This function may be called in the middle of a CATransaction, where
418     // running a modal panel is forbidden. That situation isn't ideal, but from
419     // this code's POV the right answer is to defer running the panel until
420     // after the current transaction. See https://crbug.com/849538.
421     __block auto block_callback = std::move(callback);
422     [CATransaction setCompletionBlock:^{
423       NSInteger selection = [panel runModalWithPrintInfo:print_info_];
424       if (selection == NSModalResponseOK) {
425         print_info_ = [panel printInfo];
426         settings_->set_ranges(GetPageRangesFromPrintInfo());
427         InitPrintSettingsFromPrintInfo();
428         mojom::ResultCode result = mojom::ResultCode::kSuccess;
429 #if BUILDFLAG(ENABLE_OOP_PRINTING_NO_OOP_BASIC_PRINT_DIALOG)
430         if (features::ShouldPrintJobOop()) {
431           result = CaptureSystemPrintDialogData(print_info_, settings_.get());
432         }
433 #endif
434         std::move(block_callback).Run(result);
435       } else {
436         std::move(block_callback).Run(mojom::ResultCode::kCanceled);
437       }
438     }];
439   }
440 }
441
442 gfx::Size PrintingContextMac::GetPdfPaperSizeDeviceUnits() {
443   // NOTE: Reset |print_info_| with a copy of |sharedPrintInfo| so as to start
444   // with a clean slate.
445   print_info_ = [[NSPrintInfo sharedPrintInfo] copy];
446   UpdatePageFormatWithPaperInfo();
447
448   PMPageFormat page_format =
449       static_cast<PMPageFormat>([print_info_ PMPageFormat]);
450   PMRect paper_rect;
451   PMGetAdjustedPaperRect(page_format, &paper_rect);
452
453   // Device units are in points. Units per inch is 72.
454   gfx::Size physical_size_device_units((paper_rect.right - paper_rect.left),
455                                        (paper_rect.bottom - paper_rect.top));
456   DCHECK(settings_->device_units_per_inch() == kPointsPerInch);
457   return physical_size_device_units;
458 }
459
460 mojom::ResultCode PrintingContextMac::UseDefaultSettings() {
461   DCHECK(!in_print_job_);
462
463   print_info_ = [[NSPrintInfo sharedPrintInfo] copy];
464   settings_->set_ranges(GetPageRangesFromPrintInfo());
465   InitPrintSettingsFromPrintInfo();
466
467   return mojom::ResultCode::kSuccess;
468 }
469
470 mojom::ResultCode PrintingContextMac::UpdatePrinterSettings(
471     const PrinterSettings& printer_settings) {
472   DCHECK(!printer_settings.show_system_dialog);
473   DCHECK(!in_print_job_);
474
475   // NOTE: Reset |print_info_| with a copy of |sharedPrintInfo| so as to start
476   // with a clean slate.
477   print_info_ = [[NSPrintInfo sharedPrintInfo] copy];
478
479   if (printer_settings.external_preview) {
480     if (!SetPrintPreviewJob())
481       return OnError();
482   } else {
483     // Don't need this for preview.
484     if (!SetPrinter(base::UTF16ToUTF8(settings_->device_name())) ||
485         !SetCopiesInPrintSettings(settings_->copies()) ||
486         !SetCollateInPrintSettings(settings_->collate()) ||
487         !SetDuplexModeInPrintSettings(settings_->duplex_mode()) ||
488         !SetOutputColor(static_cast<int>(settings_->color())) ||
489         !SetResolution(settings_->dpi_size())) {
490       return OnError();
491     }
492   }
493
494   if (!UpdatePageFormatWithPaperInfo() ||
495       !SetOrientationIsLandscape(settings_->landscape())) {
496     return OnError();
497   }
498
499   [print_info_ updateFromPMPrintSettings];
500
501   InitPrintSettingsFromPrintInfo();
502   return mojom::ResultCode::kSuccess;
503 }
504
505 bool PrintingContextMac::SetPrintPreviewJob() {
506   PMPrintSession print_session =
507       static_cast<PMPrintSession>([print_info_ PMPrintSession]);
508   PMPrintSettings print_settings =
509       static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
510   return PMSessionSetDestination(print_session, print_settings,
511                                  kPMDestinationPreview, nullptr,
512                                  nullptr) == noErr;
513 }
514
515 void PrintingContextMac::InitPrintSettingsFromPrintInfo() {
516   PMPrintSession print_session =
517       static_cast<PMPrintSession>([print_info_ PMPrintSession]);
518   PMPageFormat page_format =
519       static_cast<PMPageFormat>([print_info_ PMPageFormat]);
520   PMPrinter printer;
521   PMSessionGetCurrentPrinter(print_session, &printer);
522   PrintSettingsInitializerMac::InitPrintSettings(printer, page_format,
523                                                  settings_.get());
524 }
525
526 bool PrintingContextMac::SetPrinter(const std::string& device_name) {
527   DCHECK(print_info_);
528   PMPrintSession print_session =
529       static_cast<PMPrintSession>([print_info_ PMPrintSession]);
530
531   PMPrinter current_printer;
532   if (PMSessionGetCurrentPrinter(print_session, &current_printer) != noErr)
533     return false;
534
535   CFStringRef current_printer_id = PMPrinterGetID(current_printer);
536   if (!current_printer_id)
537     return false;
538
539   base::apple::ScopedCFTypeRef<CFStringRef> new_printer_id(
540       base::SysUTF8ToCFStringRef(device_name));
541   if (!new_printer_id.get())
542     return false;
543
544   if (CFStringCompare(new_printer_id.get(), current_printer_id, 0) ==
545       kCFCompareEqualTo) {
546     return true;
547   }
548
549   ScopedPMType<PMPrinter> new_printer(
550       PMPrinterCreateFromPrinterID(new_printer_id.get()));
551   if (!new_printer) {
552     return false;
553   }
554
555   return PMSessionSetCurrentPMPrinter(print_session, new_printer.get()) ==
556          noErr;
557 }
558
559 bool PrintingContextMac::UpdatePageFormatWithPaperInfo() {
560   PMPrintSession print_session =
561       static_cast<PMPrintSession>([print_info_ PMPrintSession]);
562
563   PMPageFormat default_page_format =
564       static_cast<PMPageFormat>([print_info_ PMPageFormat]);
565
566   PMPrinter current_printer = nullptr;
567   if (PMSessionGetCurrentPrinter(print_session, &current_printer) != noErr)
568     return false;
569
570   double page_width = 0.0;
571   double page_height = 0.0;
572   base::apple::ScopedCFTypeRef<CFStringRef> paper_name;
573   PMPaperMargins margins = {0};
574
575   const PrintSettings::RequestedMedia& media = settings_->requested_media();
576   if (media.IsDefault()) {
577     PMPaper default_paper;
578     if (PMGetPageFormatPaper(default_page_format, &default_paper) != noErr ||
579         PMPaperGetWidth(default_paper, &page_width) != noErr ||
580         PMPaperGetHeight(default_paper, &page_height) != noErr) {
581       return false;
582     }
583
584     // Ignore result, because we can continue without following.
585     CFStringRef tmp_paper_name = nullptr;
586     PMPaperGetPPDPaperName(default_paper, &tmp_paper_name);
587     PMPaperGetMargins(default_paper, &margins);
588     paper_name.reset(tmp_paper_name, base::scoped_policy::RETAIN);
589   } else {
590     const double kMultiplier =
591         kPointsPerInch / static_cast<float>(kMicronsPerInch);
592     page_width = media.size_microns.width() * kMultiplier;
593     page_height = media.size_microns.height() * kMultiplier;
594     paper_name.reset(base::SysUTF8ToCFStringRef(media.vendor_id));
595   }
596
597   CFArrayRef paper_list = nullptr;
598   if (PMPrinterGetPaperList(current_printer, &paper_list) != noErr)
599     return false;
600
601   PMPaper best_matching_paper =
602       MatchPaper(paper_list, paper_name.get(), page_width, page_height);
603
604   if (best_matching_paper)
605     return UpdatePageFormatWithPaper(best_matching_paper, default_page_format);
606
607   // Do nothing if unmatched paper was default system paper.
608   if (media.IsDefault())
609     return true;
610
611   ScopedPMType<PMPaper> paper;
612   if (PMPaperCreateCustom(current_printer, CFSTR("Custom paper ID"),
613                           CFSTR("Custom paper"), page_width, page_height,
614                           &margins, paper.InitializeInto()) != noErr) {
615     return false;
616   }
617   return UpdatePageFormatWithPaper(paper.get(), default_page_format);
618 }
619
620 bool PrintingContextMac::UpdatePageFormatWithPaper(PMPaper paper,
621                                                    PMPageFormat page_format) {
622   ScopedPMType<PMPageFormat> new_format;
623   if (PMCreatePageFormatWithPMPaper(new_format.InitializeInto(), paper) !=
624       noErr) {
625     return false;
626   }
627   // Copy over the original format with the new page format.
628   bool result = (PMCopyPageFormat(new_format.get(), page_format) == noErr);
629   [print_info_ updateFromPMPageFormat];
630   return result;
631 }
632
633 bool PrintingContextMac::SetCopiesInPrintSettings(int copies) {
634   if (copies < 1)
635     return false;
636
637   PMPrintSettings print_settings =
638       static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
639   return PMSetCopies(print_settings, copies, false) == noErr;
640 }
641
642 bool PrintingContextMac::SetCollateInPrintSettings(bool collate) {
643   PMPrintSettings print_settings =
644       static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
645   return PMSetCollate(print_settings, collate) == noErr;
646 }
647
648 bool PrintingContextMac::SetOrientationIsLandscape(bool landscape) {
649   PMPageFormat page_format =
650       static_cast<PMPageFormat>([print_info_ PMPageFormat]);
651
652   PMOrientation orientation = landscape ? kPMLandscape : kPMPortrait;
653
654   if (PMSetOrientation(page_format, orientation, false) != noErr)
655     return false;
656
657   PMPrintSession print_session =
658       static_cast<PMPrintSession>([print_info_ PMPrintSession]);
659
660   PMSessionValidatePageFormat(print_session, page_format, kPMDontWantBoolean);
661
662   [print_info_ updateFromPMPageFormat];
663   return true;
664 }
665
666 bool PrintingContextMac::SetDuplexModeInPrintSettings(mojom::DuplexMode mode) {
667   PMDuplexMode duplexSetting;
668   switch (mode) {
669     case mojom::DuplexMode::kLongEdge:
670       duplexSetting = kPMDuplexNoTumble;
671       break;
672     case mojom::DuplexMode::kShortEdge:
673       duplexSetting = kPMDuplexTumble;
674       break;
675     case mojom::DuplexMode::kSimplex:
676       duplexSetting = kPMDuplexNone;
677       break;
678     default:  // kUnknownDuplexMode
679       return true;
680   }
681
682   PMPrintSettings print_settings =
683       static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
684   return PMSetDuplex(print_settings, duplexSetting) == noErr;
685 }
686
687 bool PrintingContextMac::SetOutputColor(int color_mode) {
688   const mojom::ColorModel color_model = ColorModeToColorModel(color_mode);
689
690   if (!base::FeatureList::IsEnabled(features::kCupsIppPrintingBackend)) {
691     std::string color_setting_name;
692     std::string color_value;
693     GetColorModelForModel(color_model, &color_setting_name, &color_value);
694     return SetKeyValue(color_setting_name, color_value);
695   }
696
697   // First, set the default CUPS IPP output color.
698   if (!SetKeyValue(CUPS_PRINT_COLOR_MODE,
699                    GetIppColorModelForModel(color_model))) {
700     return false;
701   }
702
703   // Even when interfacing with printer settings using CUPS IPP, the print job
704   // may still expect PPD color values if the printer was added to the system
705   // with a PPD. To avoid parsing PPDs (which is the point of using CUPS IPP),
706   // set every single known PPD color setting and hope that one of them sticks.
707   const bool is_color = IsIppColorModelColorful(color_model);
708   for (const auto& setting : GetKnownPpdColorSettings()) {
709     const base::StringPiece& color_setting_name = setting.name;
710     const base::StringPiece& color_value =
711         is_color ? setting.color : setting.bw;
712     if (!SetKeyValue(color_setting_name, color_value))
713       return false;
714   }
715
716   return true;
717 }
718
719 bool PrintingContextMac::SetResolution(const gfx::Size& dpi_size) {
720   if (dpi_size.IsEmpty())
721     return true;
722
723   PMPrintSession print_session =
724       static_cast<PMPrintSession>([print_info_ PMPrintSession]);
725   PMPrinter current_printer;
726   if (PMSessionGetCurrentPrinter(print_session, &current_printer) != noErr)
727     return false;
728
729   PMResolution resolution;
730   resolution.hRes = dpi_size.width();
731   resolution.vRes = dpi_size.height();
732
733   PMPrintSettings print_settings =
734       static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
735   return PMPrinterSetOutputResolution(current_printer, print_settings,
736                                       &resolution) == noErr;
737 }
738
739 bool PrintingContextMac::SetKeyValue(base::StringPiece key,
740                                      base::StringPiece value) {
741   PMPrintSettings print_settings =
742       static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
743   base::apple::ScopedCFTypeRef<CFStringRef> cf_key =
744       base::SysUTF8ToCFStringRef(key);
745   base::apple::ScopedCFTypeRef<CFStringRef> cf_value =
746       base::SysUTF8ToCFStringRef(value);
747
748   return PMPrintSettingsSetValue(print_settings, cf_key.get(), cf_value.get(),
749                                  /*locked=*/false) == noErr;
750 }
751
752 PageRanges PrintingContextMac::GetPageRangesFromPrintInfo() {
753   PageRanges page_ranges;
754   NSDictionary* print_info_dict = [print_info_ dictionary];
755   if (![print_info_dict[NSPrintAllPages] boolValue]) {
756     PageRange range;
757     range.from = [print_info_dict[NSPrintFirstPage] intValue] - 1;
758     range.to = [print_info_dict[NSPrintLastPage] intValue] - 1;
759     page_ranges.push_back(range);
760   }
761   return page_ranges;
762 }
763
764 mojom::ResultCode PrintingContextMac::NewDocument(
765     const std::u16string& document_name) {
766   DCHECK(!in_print_job_);
767
768   in_print_job_ = true;
769
770   if (skip_system_calls())
771     return mojom::ResultCode::kSuccess;
772
773 #if BUILDFLAG(ENABLE_OOP_PRINTING_NO_OOP_BASIC_PRINT_DIALOG)
774   if (features::ShouldPrintJobOop() &&
775       !settings_->system_print_dialog_data().empty()) {
776     // NOTE: Reset `print_info_` with a copy of `sharedPrintInfo` so as to
777     // start with a clean slate.
778     print_info_ = [[NSPrintInfo sharedPrintInfo] copy];
779
780     ApplySystemPrintDialogData(settings_->device_name(),
781                                settings_->system_print_dialog_data(),
782                                print_info_);
783   }
784 #endif
785
786   PMPrintSession print_session =
787       static_cast<PMPrintSession>([print_info_ PMPrintSession]);
788   PMPrintSettings print_settings =
789       static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
790   PMPageFormat page_format =
791       static_cast<PMPageFormat>([print_info_ PMPageFormat]);
792
793   base::apple::ScopedCFTypeRef<CFStringRef> job_title =
794       base::SysUTF16ToCFStringRef(document_name);
795   PMPrintSettingsSetJobName(print_settings, job_title.get());
796
797   OSStatus status = PMSessionBeginCGDocumentNoDialog(
798       print_session, print_settings, page_format);
799   if (status != noErr)
800     return OnError();
801
802   return mojom::ResultCode::kSuccess;
803 }
804
805 mojom::ResultCode PrintingContextMac::NewPage() {
806   if (abort_printing_)
807     return mojom::ResultCode::kCanceled;
808   DCHECK(in_print_job_);
809   DCHECK(!context_);
810
811   PMPrintSession print_session =
812       static_cast<PMPrintSession>([print_info_ PMPrintSession]);
813   PMPageFormat page_format =
814       static_cast<PMPageFormat>([print_info_ PMPageFormat]);
815   OSStatus status;
816   status = PMSessionBeginPageNoDialog(print_session, page_format, nullptr);
817   if (status != noErr)
818     return OnError();
819   status = PMSessionGetCGGraphicsContext(print_session, &context_);
820   if (status != noErr)
821     return OnError();
822
823   return mojom::ResultCode::kSuccess;
824 }
825
826 mojom::ResultCode PrintingContextMac::PageDone() {
827   if (abort_printing_)
828     return mojom::ResultCode::kCanceled;
829   DCHECK(in_print_job_);
830   DCHECK(context_);
831
832   PMPrintSession print_session =
833       static_cast<PMPrintSession>([print_info_ PMPrintSession]);
834   OSStatus status = PMSessionEndPageNoDialog(print_session);
835   if (status != noErr)
836     OnError();
837   context_ = nullptr;
838
839   return mojom::ResultCode::kSuccess;
840 }
841
842 mojom::ResultCode PrintingContextMac::PrintDocument(
843     const MetafilePlayer& metafile,
844     const PrintSettings& settings,
845     uint32_t num_pages) {
846   const PageSetup& page_setup = settings.page_setup_device_units();
847   const CGRect paper_rect = gfx::Rect(page_setup.physical_size()).ToCGRect();
848
849   for (size_t metafile_page_number = 1; metafile_page_number <= num_pages;
850        metafile_page_number++) {
851     mojom::ResultCode result = NewPage();
852     if (result != mojom::ResultCode::kSuccess)
853       return result;
854     if (!metafile.RenderPage(metafile_page_number, context_, paper_rect,
855                              /*autorotate=*/true, /*fit_to_page=*/false)) {
856       return mojom::ResultCode::kFailed;
857     }
858     result = PageDone();
859     if (result != mojom::ResultCode::kSuccess)
860       return result;
861   }
862   return mojom::ResultCode::kSuccess;
863 }
864
865 mojom::ResultCode PrintingContextMac::DocumentDone() {
866   if (abort_printing_)
867     return mojom::ResultCode::kCanceled;
868   DCHECK(in_print_job_);
869
870   PMPrintSession print_session =
871       static_cast<PMPrintSession>([print_info_ PMPrintSession]);
872   OSStatus status = PMSessionEndDocumentNoDialog(print_session);
873   if (status != noErr)
874     OnError();
875
876   ResetSettings();
877   return mojom::ResultCode::kSuccess;
878 }
879
880 void PrintingContextMac::Cancel() {
881   abort_printing_ = true;
882   in_print_job_ = false;
883   context_ = nullptr;
884
885   PMPrintSession print_session =
886       static_cast<PMPrintSession>([print_info_ PMPrintSession]);
887   PMSessionEndPageNoDialog(print_session);
888 }
889
890 void PrintingContextMac::ReleaseContext() {
891   print_info_ = nil;
892   context_ = nullptr;
893 }
894
895 printing::NativeDrawingContext PrintingContextMac::context() const {
896   return context_;
897 }
898
899 }  // namespace printing