[M120 Migration][VD] Remove accessing oom_score_adj in zygote process
[platform/framework/web/chromium-efl.git] / printing / printing_context_chromeos.cc
1 // Copyright 2016 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_chromeos.h"
6
7 #include <cups/cups.h>
8 #include <stdint.h>
9 #include <unicode/ulocdata.h>
10
11 #include <map>
12 #include <memory>
13 #include <utility>
14 #include <vector>
15
16 #include "base/logging.h"
17 #include "base/memory/ptr_util.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "printing/backend/cups_connection.h"
22 #include "printing/backend/cups_ipp_constants.h"
23 #include "printing/backend/cups_ipp_helper.h"
24 #include "printing/backend/cups_printer.h"
25 #include "printing/backend/print_backend_utils.h"
26 #include "printing/buildflags/buildflags.h"
27 #include "printing/client_info_helpers.h"
28 #include "printing/metafile.h"
29 #include "printing/mojom/print.mojom.h"
30 #include "printing/print_job_constants.h"
31 #include "printing/print_settings.h"
32 #include "printing/printing_utils.h"
33 #include "printing/units.h"
34
35 namespace printing {
36
37 namespace {
38
39 // We only support sending username for secure printers.
40 const char kUsernamePlaceholder[] = "chronos";
41
42 // We only support sending document name for secure printers.
43 const char kDocumentNamePlaceholder[] = "-";
44
45 bool IsUriSecure(base::StringPiece uri) {
46   return base::StartsWith(uri, "ipps:") || base::StartsWith(uri, "https:") ||
47          base::StartsWith(uri, "usb:") || base::StartsWith(uri, "ippusb:");
48 }
49
50 // Populates the 'client-info' attribute of the IPP collection `options`. Each
51 // item in `client_infos` represents one collection in 'client-info'.
52 // Invalid 'client-info' items will be dropped.
53 void EncodeClientInfo(const std::vector<mojom::IppClientInfo>& client_infos,
54                       ipp_t* options) {
55   std::vector<ScopedIppPtr> option_values;
56   std::vector<const ipp_t*> raw_option_values;
57   option_values.reserve(client_infos.size());
58   raw_option_values.reserve(client_infos.size());
59
60   for (const mojom::IppClientInfo& client_info : client_infos) {
61     if (!ValidateClientInfoItem(client_info)) {
62       LOG(WARNING) << "Invalid client-info item skipped";
63       continue;
64     }
65
66     // Create a temporary collection object owned by this function.
67     ipp_t* collection = ippNew();
68     option_values.emplace_back(WrapIpp(collection));
69     raw_option_values.emplace_back(collection);
70
71     ippAddString(collection, IPP_TAG_ZERO, IPP_TAG_NAME, kIppClientName,
72                  nullptr, client_info.client_name.c_str());
73     ippAddInteger(collection, IPP_TAG_ZERO, IPP_TAG_ENUM, kIppClientType,
74                   static_cast<int>(client_info.client_type));
75     ippAddString(collection, IPP_TAG_ZERO, IPP_TAG_TEXT,
76                  kIppClientStringVersion, nullptr,
77                  client_info.client_string_version.c_str());
78
79     if (client_info.client_version.has_value()) {
80       ippAddOctetString(collection, IPP_TAG_ZERO, kIppClientVersion,
81                         client_info.client_version.value().data(),
82                         client_info.client_version.value().size());
83     }
84
85     if (client_info.client_patches.has_value()) {
86       ippAddString(collection, IPP_TAG_ZERO, IPP_TAG_TEXT, kIppClientPatches,
87                    nullptr, client_info.client_patches.value().c_str());
88     }
89   }
90
91   if (raw_option_values.empty()) {
92     return;
93   }
94
95   // Now add the client-info list to the options.
96   ippAddCollections(options, IPP_TAG_OPERATION, kIppClientInfo,
97                     raw_option_values.size(), raw_option_values.data());
98 }
99
100 // Construct the IPP media-col attribute specifying media size, margins, source,
101 // etc., and add it to 'options'.
102 void EncodeMediaCol(ipp_t* options,
103                     const gfx::Size& size_um,
104                     const gfx::Rect& printable_area_um,
105                     bool borderless,
106                     const std::string& source,
107                     const std::string& type) {
108   // The size and printable area in microns were calculated from the size and
109   // margins in PWG units, so we can losslessly convert them back. If
110   // borderless printing was requested, though, set all margins to zero.
111   DCHECK_EQ(size_um.width() % kMicronsPerPwgUnit, 0);
112   DCHECK_EQ(size_um.height() % kMicronsPerPwgUnit, 0);
113   int width = size_um.width() / kMicronsPerPwgUnit;
114   int height = size_um.height() / kMicronsPerPwgUnit;
115   int bottom_margin = 0, left_margin = 0, right_margin = 0, top_margin = 0;
116   if (!borderless) {
117     PwgMarginsFromSizeAndPrintableArea(size_um, printable_area_um,
118                                        &bottom_margin, &left_margin,
119                                        &right_margin, &top_margin);
120   }
121
122   ScopedIppPtr media_col = WrapIpp(ippNew());
123   ScopedIppPtr media_size = WrapIpp(ippNew());
124   ippAddInteger(media_size.get(), IPP_TAG_ZERO, IPP_TAG_INTEGER, kIppXDimension,
125                 width);
126   ippAddInteger(media_size.get(), IPP_TAG_ZERO, IPP_TAG_INTEGER, kIppYDimension,
127                 height);
128   ippAddCollection(media_col.get(), IPP_TAG_ZERO, kIppMediaSize,
129                    media_size.get());
130   ippAddInteger(media_col.get(), IPP_TAG_ZERO, IPP_TAG_INTEGER,
131                 kIppMediaBottomMargin, bottom_margin);
132   ippAddInteger(media_col.get(), IPP_TAG_ZERO, IPP_TAG_INTEGER,
133                 kIppMediaLeftMargin, left_margin);
134   ippAddInteger(media_col.get(), IPP_TAG_ZERO, IPP_TAG_INTEGER,
135                 kIppMediaRightMargin, right_margin);
136   ippAddInteger(media_col.get(), IPP_TAG_ZERO, IPP_TAG_INTEGER,
137                 kIppMediaTopMargin, top_margin);
138   if (!source.empty()) {
139     ippAddString(media_col.get(), IPP_TAG_ZERO, IPP_TAG_KEYWORD,
140                  kIppMediaSource, nullptr, source.c_str());
141   }
142   if (!type.empty()) {
143     ippAddString(media_col.get(), IPP_TAG_ZERO, IPP_TAG_KEYWORD, kIppMediaType,
144                  nullptr, type.c_str());
145   }
146
147   ippAddCollection(options, IPP_TAG_JOB, kIppMediaCol, media_col.get());
148 }
149
150 std::string GetCollateString(bool collate) {
151   return collate ? kCollated : kUncollated;
152 }
153
154 void SetPrintableArea(PrintSettings* settings,
155                       const PrintSettings::RequestedMedia& media,
156                       const gfx::Rect& printable_area_um) {
157   if (!media.size_microns.IsEmpty()) {
158     float device_microns_per_device_unit =
159         static_cast<float>(kMicronsPerInch) / settings->device_units_per_inch();
160     gfx::Size paper_size =
161         gfx::Size(media.size_microns.width() / device_microns_per_device_unit,
162                   media.size_microns.height() / device_microns_per_device_unit);
163
164     gfx::Rect paper_rect =
165         gfx::Rect(printable_area_um.x() / device_microns_per_device_unit,
166                   printable_area_um.y() / device_microns_per_device_unit,
167                   printable_area_um.width() / device_microns_per_device_unit,
168                   printable_area_um.height() / device_microns_per_device_unit);
169     settings->SetPrinterPrintableArea(paper_size, paper_rect,
170                                       /*landscape_needs_flip=*/true);
171   }
172 }
173
174 }  // namespace
175
176 ScopedIppPtr SettingsToIPPOptions(const PrintSettings& settings,
177                                   const gfx::Rect& printable_area_um) {
178   ScopedIppPtr scoped_options = WrapIpp(ippNew());
179   ipp_t* options = scoped_options.get();
180
181   const char* sides = nullptr;
182   switch (settings.duplex_mode()) {
183     case mojom::DuplexMode::kSimplex:
184       sides = CUPS_SIDES_ONE_SIDED;
185       break;
186     case mojom::DuplexMode::kLongEdge:
187       sides = CUPS_SIDES_TWO_SIDED_PORTRAIT;
188       break;
189     case mojom::DuplexMode::kShortEdge:
190       sides = CUPS_SIDES_TWO_SIDED_LANDSCAPE;
191       break;
192     default:
193       NOTREACHED();
194   }
195
196   // duplexing
197   ippAddString(options, IPP_TAG_JOB, IPP_TAG_KEYWORD, kIppDuplex, nullptr,
198                sides);
199   // color
200   ippAddString(options, IPP_TAG_JOB, IPP_TAG_KEYWORD, kIppColor, nullptr,
201                GetIppColorModelForModel(settings.color()).c_str());
202   // copies
203   ippAddInteger(options, IPP_TAG_JOB, IPP_TAG_INTEGER, kIppCopies,
204                 settings.copies());
205   // collate
206   ippAddString(options, IPP_TAG_JOB, IPP_TAG_KEYWORD, kIppCollate, nullptr,
207                GetCollateString(settings.collate()).c_str());
208
209   if (!settings.pin_value().empty()) {
210     ippAddOctetString(options, IPP_TAG_OPERATION, kIppPin,
211                       settings.pin_value().data(), settings.pin_value().size());
212     ippAddString(options, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, kIppPinEncryption,
213                  nullptr, kPinEncryptionNone);
214   }
215
216   // resolution
217   if (settings.dpi_horizontal() > 0 && settings.dpi_vertical() > 0) {
218     ippAddResolution(options, IPP_TAG_JOB, kIppResolution, IPP_RES_PER_INCH,
219                      settings.dpi_horizontal(), settings.dpi_vertical());
220   }
221
222   std::map<std::string, std::vector<int>> multival;
223   std::string media_source;
224   for (const auto& setting : settings.advanced_settings()) {
225     const std::string& key = setting.first;
226     const std::string& value = setting.second.GetString();
227     if (value.empty()) {
228       continue;
229     }
230     if (key == kIppMediaSource) {
231       media_source = value;
232       continue;
233     }
234
235     // Check for multivalue enum ("attribute/value").
236     size_t pos = key.find('/');
237     if (pos == std::string::npos) {
238       // Regular value.
239       ippAddString(options, IPP_TAG_JOB, IPP_TAG_KEYWORD, key.c_str(), nullptr,
240                    value.c_str());
241       continue;
242     }
243     // Store selected enum values.
244     if (value == kOptionTrue) {
245       std::string option_name = key.substr(0, pos);
246       std::string enum_string = key.substr(pos + 1);
247       int enum_value = ippEnumValue(option_name.c_str(), enum_string.c_str());
248       DCHECK_NE(enum_value, -1);
249       multival[option_name].push_back(enum_value);
250     }
251   }
252
253   // Construct the IPP media-col attribute specifying media size, margins,
254   // source, etc.
255   EncodeMediaCol(options, settings.requested_media().size_microns,
256                  printable_area_um, settings.borderless(), media_source,
257                  settings.media_type());
258
259   // Add multivalue enum options.
260   for (const auto& it : multival) {
261     ippAddIntegers(options, IPP_TAG_JOB, IPP_TAG_ENUM, it.first.c_str(),
262                    it.second.size(), it.second.data());
263   }
264
265   // OAuth access token
266   if (!settings.oauth_token().empty()) {
267     ippAddString(options, IPP_TAG_JOB, IPP_TAG_NAME,
268                  kSettingChromeOSAccessOAuthToken, nullptr,
269                  settings.oauth_token().c_str());
270   }
271
272   // IPP client-info attribute.
273   if (!settings.client_infos().empty()) {
274     EncodeClientInfo(settings.client_infos(), options);
275   }
276
277   return scoped_options;
278 }
279
280 // static
281 std::unique_ptr<PrintingContext> PrintingContext::CreateImpl(
282     Delegate* delegate,
283     bool skip_system_calls) {
284   auto context = std::make_unique<PrintingContextChromeos>(delegate);
285 #if BUILDFLAG(ENABLE_OOP_PRINTING)
286   if (skip_system_calls)
287     context->set_skip_system_calls();
288 #endif
289   return context;
290 }
291
292 // static
293 std::unique_ptr<PrintingContextChromeos>
294 PrintingContextChromeos::CreateForTesting(
295     Delegate* delegate,
296     std::unique_ptr<CupsConnection> connection) {
297   // Private ctor.
298   return base::WrapUnique(
299       new PrintingContextChromeos(delegate, std::move(connection)));
300 }
301
302 PrintingContextChromeos::PrintingContextChromeos(Delegate* delegate)
303     : PrintingContext(delegate),
304       connection_(CupsConnection::Create()),
305       ipp_options_(WrapIpp(nullptr)) {}
306
307 PrintingContextChromeos::PrintingContextChromeos(
308     Delegate* delegate,
309     std::unique_ptr<CupsConnection> connection)
310     : PrintingContext(delegate),
311       connection_(std::move(connection)),
312       ipp_options_(WrapIpp(nullptr)) {}
313
314 PrintingContextChromeos::~PrintingContextChromeos() {
315   ReleaseContext();
316 }
317
318 void PrintingContextChromeos::AskUserForSettings(
319     int max_pages,
320     bool has_selection,
321     bool is_scripted,
322     PrintSettingsCallback callback) {
323   // We don't want to bring up a dialog here.  Ever.  This should not be called.
324   NOTREACHED();
325 }
326
327 mojom::ResultCode PrintingContextChromeos::UseDefaultSettings() {
328   DCHECK(!in_print_job_);
329
330   ResetSettings();
331
332   std::string device_name = base::UTF16ToUTF8(settings_->device_name());
333   if (device_name.empty())
334     return OnError();
335
336   // TODO(skau): https://crbug.com/613779. See UpdatePrinterSettings for more
337   // info.
338   if (settings_->dpi() == 0) {
339     DVLOG(1) << "Using Default DPI";
340     settings_->set_dpi(kDefaultPdfDpi);
341   }
342
343   // Retrieve device information and set it
344   if (InitializeDevice(device_name) != mojom::ResultCode::kSuccess) {
345     LOG(ERROR) << "Could not initialize printer";
346     return OnError();
347   }
348
349   // Set printable area
350   DCHECK(printer_);
351   PrinterSemanticCapsAndDefaults::Paper paper = DefaultPaper(*printer_);
352
353   PrintSettings::RequestedMedia media;
354   media.vendor_id = paper.vendor_id();
355   media.size_microns = paper.size_um();
356   settings_->set_requested_media(media);
357   SetPrintableArea(settings_.get(), media, paper.printable_area_um());
358
359   return mojom::ResultCode::kSuccess;
360 }
361
362 gfx::Size PrintingContextChromeos::GetPdfPaperSizeDeviceUnits() {
363   int32_t width = 0;
364   int32_t height = 0;
365   UErrorCode error = U_ZERO_ERROR;
366   ulocdata_getPaperSize(delegate_->GetAppLocale().c_str(), &height, &width,
367                         &error);
368   if (error > U_ZERO_ERROR) {
369     // If the call failed, assume a paper size of 8.5 x 11 inches.
370     LOG(WARNING) << "ulocdata_getPaperSize failed, using 8.5 x 11, error: "
371                  << error;
372     width =
373         static_cast<int>(kLetterWidthInch * settings_->device_units_per_inch());
374     height = static_cast<int>(kLetterHeightInch *
375                               settings_->device_units_per_inch());
376   } else {
377     // ulocdata_getPaperSize returns the width and height in mm.
378     // Convert this to pixels based on the dpi.
379     float multiplier = settings_->device_units_per_inch() / kMicronsPerMil;
380     width *= multiplier;
381     height *= multiplier;
382   }
383   return gfx::Size(width, height);
384 }
385
386 mojom::ResultCode PrintingContextChromeos::UpdatePrinterSettings(
387     const PrinterSettings& printer_settings) {
388   DCHECK(!printer_settings.show_system_dialog);
389
390   if (InitializeDevice(base::UTF16ToUTF8(settings_->device_name())) !=
391       mojom::ResultCode::kSuccess) {
392     return OnError();
393   }
394
395   // TODO(skau): Convert to DCHECK when https://crbug.com/613779 is resolved
396   // Print quality suffers when this is set to the resolution reported by the
397   // printer but print quality is fine at this resolution. UseDefaultSettings
398   // exhibits the same problem.
399   if (settings_->dpi() == 0) {
400     DVLOG(1) << "Using Default DPI";
401     settings_->set_dpi(kDefaultPdfDpi);
402   }
403
404   // compute paper size
405   PrintSettings::RequestedMedia media = settings_->requested_media();
406
407   DCHECK(printer_);
408   if (media.IsDefault()) {
409     PrinterSemanticCapsAndDefaults::Paper paper = DefaultPaper(*printer_);
410
411     media.vendor_id = paper.vendor_id();
412     media.size_microns = paper.size_um();
413     settings_->set_requested_media(media);
414   }
415
416   gfx::Rect printable_area_um =
417       GetPrintableAreaForSize(*printer_, media.size_microns);
418   SetPrintableArea(settings_.get(), media, printable_area_um);
419   ipp_options_ = SettingsToIPPOptions(*settings_, printable_area_um);
420   send_user_info_ = settings_->send_user_info();
421   if (send_user_info_) {
422     DCHECK(printer_);
423     username_ = IsUriSecure(printer_->GetUri()) ? settings_->username()
424                                                 : kUsernamePlaceholder;
425   }
426
427   return mojom::ResultCode::kSuccess;
428 }
429
430 mojom::ResultCode PrintingContextChromeos::InitializeDevice(
431     const std::string& device) {
432   DCHECK(!in_print_job_);
433
434   std::unique_ptr<CupsPrinter> printer = connection_->GetPrinter(device);
435   if (!printer) {
436     LOG(WARNING) << "Could not initialize device";
437     return OnError();
438   }
439
440   printer_ = std::move(printer);
441
442   return mojom::ResultCode::kSuccess;
443 }
444
445 mojom::ResultCode PrintingContextChromeos::NewDocument(
446     const std::u16string& document_name) {
447   DCHECK(!in_print_job_);
448   in_print_job_ = true;
449
450   if (skip_system_calls())
451     return mojom::ResultCode::kSuccess;
452
453   std::string converted_name;
454   if (send_user_info_) {
455     DCHECK(printer_);
456     converted_name = IsUriSecure(printer_->GetUri())
457                          ? base::UTF16ToUTF8(document_name)
458                          : kDocumentNamePlaceholder;
459   }
460
461   ipp_status_t create_status = printer_->CreateJob(
462       &job_id_, converted_name, username_, ipp_options_.get());
463
464   if (job_id_ == 0) {
465     DLOG(WARNING) << "Creating cups job failed"
466                   << ippErrorString(create_status);
467     return OnError();
468   }
469
470   // we only send one document, so it's always the last one
471   if (!printer_->StartDocument(job_id_, converted_name, true, username_,
472                                ipp_options_.get())) {
473     LOG(ERROR) << "Starting document failed";
474     return OnError();
475   }
476
477   return mojom::ResultCode::kSuccess;
478 }
479
480 mojom::ResultCode PrintingContextChromeos::PrintDocument(
481     const MetafilePlayer& metafile,
482     const PrintSettings& settings,
483     uint32_t num_pages) {
484   if (abort_printing_)
485     return mojom::ResultCode::kCanceled;
486   DCHECK(in_print_job_);
487
488 #if BUILDFLAG(USE_CUPS)
489   std::vector<char> buffer;
490   if (!metafile.GetDataAsVector(&buffer))
491     return mojom::ResultCode::kFailed;
492
493   return StreamData(buffer);
494 #else
495   NOTREACHED();
496   return mojom::ResultCode::kFailed;
497 #endif  // BUILDFLAG(USE_CUPS)
498 }
499
500 mojom::ResultCode PrintingContextChromeos::DocumentDone() {
501   if (abort_printing_)
502     return mojom::ResultCode::kCanceled;
503
504   DCHECK(in_print_job_);
505
506   if (!printer_->FinishDocument()) {
507     LOG(WARNING) << "Finishing document failed";
508     return OnError();
509   }
510
511   ipp_status_t job_status = printer_->CloseJob(job_id_, username_);
512   job_id_ = 0;
513
514   if (job_status != IPP_STATUS_OK) {
515     LOG(WARNING) << "Closing job failed";
516     return OnError();
517   }
518
519   ResetSettings();
520   return mojom::ResultCode::kSuccess;
521 }
522
523 void PrintingContextChromeos::Cancel() {
524   abort_printing_ = true;
525   in_print_job_ = false;
526 }
527
528 void PrintingContextChromeos::ReleaseContext() {
529   printer_.reset();
530 }
531
532 printing::NativeDrawingContext PrintingContextChromeos::context() const {
533   // Intentional No-op.
534   return nullptr;
535 }
536
537 mojom::ResultCode PrintingContextChromeos::StreamData(
538     const std::vector<char>& buffer) {
539   if (abort_printing_)
540     return mojom::ResultCode::kCanceled;
541
542   DCHECK(in_print_job_);
543   DCHECK(printer_);
544
545   if (!printer_->StreamData(buffer))
546     return OnError();
547
548   return mojom::ResultCode::kSuccess;
549 }
550
551 }  // namespace printing