1 // Copyright 2014 The Chromium Authors. All rights reserved.
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 "ui/gtk/printing/print_dialog_gtk.h"
6 
7 #include <gtk/gtkunixprint.h>
8 
9 #include <algorithm>
10 #include <cmath>
11 #include <memory>
12 #include <string>
13 #include <utility>
14 #include <vector>
15 
16 #include "base/bind.h"
17 #include "base/files/file_util.h"
18 #include "base/logging.h"
19 #include "base/macros.h"
20 #include "base/no_destructor.h"
21 #include "base/sequence_checker.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/task/post_task.h"
24 #include "base/task/thread_pool.h"
25 #include "base/threading/sequenced_task_runner_handle.h"
26 #include "base/values.h"
27 #include "printing/metafile.h"
28 #include "printing/print_job_constants.h"
29 #include "printing/print_settings.h"
30 #include "ui/aura/window.h"
31 #include "ui/gtk/gtk_ui.h"
32 #include "ui/gtk/gtk_ui_delegate.h"
33 #include "ui/gtk/gtk_util.h"
34 #include "ui/gtk/printing/printing_gtk_util.h"
35 
36 #if defined(USE_CUPS)
37 #include "printing/mojom/print.mojom.h"
38 #endif
39 
40 using printing::PageRanges;
41 using printing::PrintSettings;
42 
43 namespace {
44 
45 #if defined(USE_CUPS)
46 // CUPS Duplex attribute and values.
47 const char kCUPSDuplex[] = "cups-Duplex";
48 const char kDuplexNone[] = "None";
49 const char kDuplexTumble[] = "DuplexTumble";
50 const char kDuplexNoTumble[] = "DuplexNoTumble";
51 #endif
52 
53 constexpr int kPaperSizeTresholdMicrons = 100;
54 constexpr int kMicronsInMm = 1000;
55 
56 // Checks whether |gtk_paper_size| can be used to represent user selected media.
57 // In fuzzy match mode checks that paper sizes are "close enough" (less than
58 // 1mm difference). In the exact mode, looks for the paper with the same PPD
59 // name and "close enough" size.
PaperSizeMatch(GtkPaperSize * gtk_paper_size,const PrintSettings::RequestedMedia & media,bool fuzzy_match)60 bool PaperSizeMatch(GtkPaperSize* gtk_paper_size,
61                     const PrintSettings::RequestedMedia& media,
62                     bool fuzzy_match) {
63   if (!gtk_paper_size)
64     return false;
65 
66   gfx::Size paper_size_microns(
67       static_cast<int>(gtk_paper_size_get_width(gtk_paper_size, GTK_UNIT_MM) *
68                            kMicronsInMm +
69                        0.5),
70       static_cast<int>(gtk_paper_size_get_height(gtk_paper_size, GTK_UNIT_MM) *
71                            kMicronsInMm +
72                        0.5));
73   int diff = std::max(
74       std::abs(paper_size_microns.width() - media.size_microns.width()),
75       std::abs(paper_size_microns.height() - media.size_microns.height()));
76   bool close_enough = diff <= kPaperSizeTresholdMicrons;
77   if (fuzzy_match)
78     return close_enough;
79 
80   return close_enough && !media.vendor_id.empty() &&
81          media.vendor_id == gtk_paper_size_get_ppd_name(gtk_paper_size);
82 }
83 
84 // Looks up a paper size matching (in terms of PaperSizeMatch) the user selected
85 // media in the paper size list reported by GTK. Returns nullptr if there's no
86 // match found.
FindPaperSizeMatch(GList * gtk_paper_sizes,const PrintSettings::RequestedMedia & media)87 GtkPaperSize* FindPaperSizeMatch(GList* gtk_paper_sizes,
88                                  const PrintSettings::RequestedMedia& media) {
89   GtkPaperSize* first_fuzzy_match = nullptr;
90   for (GList* p = gtk_paper_sizes; p && p->data; p = g_list_next(p)) {
91     GtkPaperSize* gtk_paper_size = static_cast<GtkPaperSize*>(p->data);
92     if (PaperSizeMatch(gtk_paper_size, media, false))
93       return gtk_paper_size;
94 
95     if (!first_fuzzy_match && PaperSizeMatch(gtk_paper_size, media, true))
96       first_fuzzy_match = gtk_paper_size;
97   }
98   return first_fuzzy_match;
99 }
100 
101 class StickyPrintSettingGtk {
102  public:
StickyPrintSettingGtk()103   StickyPrintSettingGtk() : last_used_settings_(gtk_print_settings_new()) {}
~StickyPrintSettingGtk()104   ~StickyPrintSettingGtk() {
105     NOTREACHED();  // Intended to be used with base::NoDestructor.
106   }
107 
settings()108   GtkPrintSettings* settings() { return last_used_settings_; }
109 
SetLastUsedSettings(GtkPrintSettings * settings)110   void SetLastUsedSettings(GtkPrintSettings* settings) {
111     DCHECK(last_used_settings_);
112     g_object_unref(last_used_settings_);
113     last_used_settings_ = gtk_print_settings_copy(settings);
114   }
115 
116  private:
117   GtkPrintSettings* last_used_settings_;
118 
119   DISALLOW_COPY_AND_ASSIGN(StickyPrintSettingGtk);
120 };
121 
GetLastUsedSettings()122 StickyPrintSettingGtk& GetLastUsedSettings() {
123   static base::NoDestructor<StickyPrintSettingGtk> settings;
124   return *settings;
125 }
126 
127 // Helper class to track GTK printers.
128 class GtkPrinterList {
129  public:
GtkPrinterList()130   GtkPrinterList() { gtk_enumerate_printers(SetPrinter, this, nullptr, TRUE); }
131 
~GtkPrinterList()132   ~GtkPrinterList() {
133     for (GtkPrinter* printer : printers_)
134       g_object_unref(printer);
135   }
136 
137   // Can return nullptr if there's no default printer. E.g. Printer on a laptop
138   // is "home_printer", but the laptop is at work.
default_printer()139   GtkPrinter* default_printer() { return default_printer_; }
140 
141   // Can return nullptr if the printer cannot be found due to:
142   // - Printer list out of sync with printer dialog UI.
143   // - Querying for non-existant printers like 'Print to PDF'.
GetPrinterWithName(const std::string & name)144   GtkPrinter* GetPrinterWithName(const std::string& name) {
145     if (name.empty())
146       return nullptr;
147 
148     for (GtkPrinter* printer : printers_) {
149       if (gtk_printer_get_name(printer) == name)
150         return printer;
151     }
152 
153     return nullptr;
154   }
155 
156  private:
157   // Callback function used by gtk_enumerate_printers() to get all printer.
SetPrinter(GtkPrinter * printer,gpointer data)158   static gboolean SetPrinter(GtkPrinter* printer, gpointer data) {
159     GtkPrinterList* printer_list = reinterpret_cast<GtkPrinterList*>(data);
160     if (gtk_printer_is_default(printer))
161       printer_list->default_printer_ = printer;
162 
163     g_object_ref(printer);
164     printer_list->printers_.push_back(printer);
165 
166     return FALSE;
167   }
168 
169   std::vector<GtkPrinter*> printers_;
170   GtkPrinter* default_printer_ = nullptr;
171 };
172 
173 }  // namespace
174 
175 // static
CreatePrintDialog(PrintingContextLinux * context)176 printing::PrintDialogGtkInterface* PrintDialogGtk::CreatePrintDialog(
177     PrintingContextLinux* context) {
178   return new PrintDialogGtk(context);
179 }
180 
PrintDialogGtk(PrintingContextLinux * context)181 PrintDialogGtk::PrintDialogGtk(PrintingContextLinux* context)
182     : base::RefCountedDeleteOnSequence<PrintDialogGtk>(
183           base::SequencedTaskRunnerHandle::Get()),
184       context_(context) {}
185 
~PrintDialogGtk()186 PrintDialogGtk::~PrintDialogGtk() {
187   DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
188 
189   if (dialog_) {
190     aura::Window* parent = gtk::GetAuraTransientParent(dialog_);
191     if (parent) {
192       parent->RemoveObserver(this);
193       gtk::ClearAuraTransientParent(dialog_, parent);
194     }
195     gtk_widget_destroy(dialog_);
196     dialog_ = nullptr;
197   }
198   if (gtk_settings_) {
199     g_object_unref(gtk_settings_);
200     gtk_settings_ = nullptr;
201   }
202   if (page_setup_) {
203     g_object_unref(page_setup_);
204     page_setup_ = nullptr;
205   }
206   if (printer_) {
207     g_object_unref(printer_);
208     printer_ = nullptr;
209   }
210 }
211 
UseDefaultSettings()212 void PrintDialogGtk::UseDefaultSettings() {
213   DCHECK(!page_setup_);
214   DCHECK(!printer_);
215 
216   // |gtk_settings_| is a new copy.
217   gtk_settings_ = gtk_print_settings_copy(GetLastUsedSettings().settings());
218   page_setup_ = gtk_page_setup_new();
219 
220   InitPrintSettings(std::make_unique<PrintSettings>());
221 }
222 
UpdateSettings(std::unique_ptr<printing::PrintSettings> settings)223 void PrintDialogGtk::UpdateSettings(
224     std::unique_ptr<printing::PrintSettings> settings) {
225   if (!gtk_settings_)
226     gtk_settings_ = gtk_print_settings_copy(GetLastUsedSettings().settings());
227 
228   auto printer_list = std::make_unique<GtkPrinterList>();
229   printer_ = printer_list->GetPrinterWithName(
230       base::UTF16ToUTF8(settings->device_name()));
231   if (printer_) {
232     g_object_ref(printer_);
233     gtk_print_settings_set_printer(gtk_settings_,
234                                    gtk_printer_get_name(printer_));
235     if (!page_setup_) {
236       page_setup_ = gtk_printer_get_default_page_size(printer_);
237     }
238   }
239 
240   gtk_print_settings_set_n_copies(gtk_settings_, settings->copies());
241   gtk_print_settings_set_collate(gtk_settings_, settings->collate());
242   if (settings->dpi_horizontal() > 0 && settings->dpi_vertical() > 0) {
243     gtk_print_settings_set_resolution_xy(
244         gtk_settings_, settings->dpi_horizontal(), settings->dpi_vertical());
245 #if defined(USE_CUPS)
246     std::string dpi = base::NumberToString(settings->dpi_horizontal());
247     if (settings->dpi_horizontal() != settings->dpi_vertical())
248       dpi += "x" + base::NumberToString(settings->dpi_vertical());
249     dpi += "dpi";
250 
251     // The resolution attribute (case-insensitive) has decent coverage
252     // in the CUPS PPD API (Resolution, SetResolution, JCLResolution,
253     // CNRes_PGP). See
254     // https://chromium.googlesource.com/chromiumos/third_party/cups/+/49a182a4c42d/cups/mark.c#266
255     // for more information.
256     //
257     // Many PPDs use pdftopdf directly to generate the print data and pdftopdf
258     // uses the CUPS PPD API internally to handle resolution selection.
259     //
260     // Many third-party filters such as the Brother print filter that
261     // do not use the CUPS PPD API are case sensitive and tend to support
262     // the Resolution PPD attribute. For this reason "cups-Resolution"
263     // makes the most sense here.
264     //
265     // TODO(crbug.com/1119956): Since PrintBackendCUPS parses the PPD file in
266     // Chromium, it should be possible to store the resolution attribute name
267     // as well as a map from the gfx::Size resolution to the std::string
268     // serialized value (in case a non-standard value such as 500x500dpi is
269     // present) in the PrinterCapsAndDefaults object. This object then needs to
270     // be passed over here (there are a couple ways this can be done) where it
271     // can be used to lookup the CUPS PPD resolution name and serialized DPI
272     // value to use. The main benefit of the approach would be full support
273     // for the HPPrintQuality and LXResolution PPD attributes which some PPD
274     // files use.
275     gtk_print_settings_set(gtk_settings_, "cups-Resolution", dpi.c_str());
276 #endif
277   }
278 
279 #if defined(USE_CUPS)
280   // Set advanced settings first so they can be overridden by user applied
281   // settings.
282   for (const auto& pair : settings->advanced_settings()) {
283     if (!pair.second.is_string())
284       continue;
285     static constexpr char kSettingNamePrefix[] = "cups-";
286     const std::string setting_name = kSettingNamePrefix + pair.first;
287     gtk_print_settings_set(gtk_settings_, setting_name.c_str(),
288                            pair.second.GetString().c_str());
289   }
290 
291   std::string color_value;
292   std::string color_setting_name;
293   printing::GetColorModelForModel(settings->color(), &color_setting_name,
294                                   &color_value);
295   gtk_print_settings_set(gtk_settings_, color_setting_name.c_str(),
296                          color_value.c_str());
297 
298   if (settings->duplex_mode() !=
299       printing::mojom::DuplexMode::kUnknownDuplexMode) {
300     const char* cups_duplex_mode = nullptr;
301     switch (settings->duplex_mode()) {
302       case printing::mojom::DuplexMode::kLongEdge:
303         cups_duplex_mode = kDuplexNoTumble;
304         break;
305       case printing::mojom::DuplexMode::kShortEdge:
306         cups_duplex_mode = kDuplexTumble;
307         break;
308       case printing::mojom::DuplexMode::kSimplex:
309         cups_duplex_mode = kDuplexNone;
310         break;
311       default:  // kUnknownDuplexMode
312         NOTREACHED();
313         break;
314     }
315     gtk_print_settings_set(gtk_settings_, kCUPSDuplex, cups_duplex_mode);
316   }
317 #endif
318 
319   if (!page_setup_)
320     page_setup_ = gtk_page_setup_new();
321 
322   if (page_setup_ && !settings->requested_media().IsDefault()) {
323     const PrintSettings::RequestedMedia& requested_media =
324         settings->requested_media();
325     GtkPaperSize* gtk_current_paper_size =
326         gtk_page_setup_get_paper_size(page_setup_);
327     if (!PaperSizeMatch(gtk_current_paper_size, requested_media,
328                         true /*fuzzy_match*/)) {
329       GList* gtk_paper_sizes =
330           gtk_paper_size_get_paper_sizes(false /*include_custom*/);
331       if (gtk_paper_sizes) {
332         GtkPaperSize* matching_gtk_paper_size =
333             FindPaperSizeMatch(gtk_paper_sizes, requested_media);
334         if (matching_gtk_paper_size) {
335           VLOG(1) << "Using listed paper size";
336           gtk_page_setup_set_paper_size(page_setup_, matching_gtk_paper_size);
337         } else {
338           VLOG(1) << "Using custom paper size";
339           GtkPaperSize* custom_size = gtk_paper_size_new_custom(
340               requested_media.vendor_id.c_str(),
341               requested_media.vendor_id.c_str(),
342               requested_media.size_microns.width() / kMicronsInMm,
343               requested_media.size_microns.height() / kMicronsInMm,
344               GTK_UNIT_MM);
345           gtk_page_setup_set_paper_size(page_setup_, custom_size);
346           gtk_paper_size_free(custom_size);
347         }
348         g_list_free_full(gtk_paper_sizes,
349                          reinterpret_cast<GDestroyNotify>(gtk_paper_size_free));
350       }
351     } else {
352       VLOG(1) << "Using default paper size";
353     }
354   }
355 
356   gtk_print_settings_set_orientation(
357       gtk_settings_, settings->landscape() ? GTK_PAGE_ORIENTATION_LANDSCAPE
358                                            : GTK_PAGE_ORIENTATION_PORTRAIT);
359 
360   InitPrintSettings(std::move(settings));
361 }
362 
ShowDialog(gfx::NativeView parent_view,bool has_selection,PrintingContextLinux::PrintSettingsCallback callback)363 void PrintDialogGtk::ShowDialog(
364     gfx::NativeView parent_view,
365     bool has_selection,
366     PrintingContextLinux::PrintSettingsCallback callback) {
367   callback_ = std::move(callback);
368   DCHECK(callback_);
369 
370   dialog_ = gtk_print_unix_dialog_new(nullptr, nullptr);
371   gtk::SetGtkTransientForAura(dialog_, parent_view);
372   if (parent_view)
373     parent_view->AddObserver(this);
374   g_signal_connect(dialog_, "delete-event",
375                    G_CALLBACK(gtk_widget_hide_on_delete), nullptr);
376 
377   // Handle the case when the existing |gtk_settings_| has "selection" selected
378   // as the page range, but |has_selection| is false.
379   if (!has_selection) {
380     GtkPrintPages range = gtk_print_settings_get_print_pages(gtk_settings_);
381     if (range == GTK_PRINT_PAGES_SELECTION)
382       gtk_print_settings_set_print_pages(gtk_settings_, GTK_PRINT_PAGES_ALL);
383   }
384 
385   // Set modal so user cannot focus the same tab and press print again.
386   gtk_window_set_modal(GTK_WINDOW(dialog_), TRUE);
387 
388   // Since we only generate PDF, only show printers that support PDF.
389   // TODO(thestig) Add more capabilities to support?
390   GtkPrintCapabilities cap = static_cast<GtkPrintCapabilities>(
391       GTK_PRINT_CAPABILITY_GENERATE_PS | GTK_PRINT_CAPABILITY_GENERATE_PDF | GTK_PRINT_CAPABILITY_PAGE_SET |
392       GTK_PRINT_CAPABILITY_COPIES | GTK_PRINT_CAPABILITY_COLLATE |
393       GTK_PRINT_CAPABILITY_REVERSE);
394   gtk_print_unix_dialog_set_manual_capabilities(GTK_PRINT_UNIX_DIALOG(dialog_),
395                                                 cap);
396   gtk_print_unix_dialog_set_embed_page_setup(GTK_PRINT_UNIX_DIALOG(dialog_),
397                                              TRUE);
398   gtk_print_unix_dialog_set_support_selection(GTK_PRINT_UNIX_DIALOG(dialog_),
399                                               TRUE);
400   gtk_print_unix_dialog_set_has_selection(GTK_PRINT_UNIX_DIALOG(dialog_),
401                                           has_selection);
402   gtk_print_unix_dialog_set_settings(GTK_PRINT_UNIX_DIALOG(dialog_),
403                                      gtk_settings_);
404   g_signal_connect(dialog_, "response", G_CALLBACK(OnResponseThunk), this);
405   gtk_widget_show(dialog_);
406 
407   gtk::GtkUi::GetDelegate()->ShowGtkWindow(GTK_WINDOW(dialog_));
408 }
409 
PrintDocument(const printing::MetafilePlayer & metafile,const base::string16 & document_name)410 void PrintDialogGtk::PrintDocument(const printing::MetafilePlayer& metafile,
411                                    const base::string16& document_name) {
412   // This runs on the print worker thread, does not block the UI thread.
413   DCHECK(!owning_task_runner()->RunsTasksInCurrentSequence());
414 
415   // The document printing tasks can outlive the PrintingContext that created
416   // this dialog.
417   AddRef();
418 
419   bool success = base::CreateTemporaryFile(&path_to_pdf_);
420 
421   if (success) {
422     base::File file;
423     file.Initialize(path_to_pdf_,
424                     base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
425     success = metafile.SaveTo(&file);
426     file.Close();
427     if (!success)
428       base::DeleteFile(path_to_pdf_);
429   }
430 
431   if (!success) {
432     LOG(ERROR) << "Saving metafile failed";
433     // Matches AddRef() above.
434     Release();
435     return;
436   }
437 
438   // No errors, continue printing.
439   owning_task_runner()->PostTask(
440       FROM_HERE, base::BindOnce(&PrintDialogGtk::SendDocumentToPrinter, this,
441                                 document_name));
442 }
443 
AddRefToDialog()444 void PrintDialogGtk::AddRefToDialog() {
445   AddRef();
446 }
447 
ReleaseDialog()448 void PrintDialogGtk::ReleaseDialog() {
449   Release();
450 }
451 
OnResponse(GtkWidget * dialog,int response_id)452 void PrintDialogGtk::OnResponse(GtkWidget* dialog, int response_id) {
453   int num_matched_handlers = g_signal_handlers_disconnect_by_func(
454       dialog_, reinterpret_cast<gpointer>(&OnResponseThunk), this);
455   CHECK_EQ(1, num_matched_handlers);
456 
457   gtk_widget_hide(dialog_);
458 
459   switch (response_id) {
460     case GTK_RESPONSE_OK: {
461       if (gtk_settings_)
462         g_object_unref(gtk_settings_);
463       gtk_settings_ =
464           gtk_print_unix_dialog_get_settings(GTK_PRINT_UNIX_DIALOG(dialog_));
465 
466       if (printer_)
467         g_object_unref(printer_);
468       printer_ = gtk_print_unix_dialog_get_selected_printer(
469           GTK_PRINT_UNIX_DIALOG(dialog_));
470       g_object_ref(printer_);
471 
472       if (page_setup_)
473         g_object_unref(page_setup_);
474       page_setup_ =
475           gtk_print_unix_dialog_get_page_setup(GTK_PRINT_UNIX_DIALOG(dialog_));
476       g_object_ref(page_setup_);
477 
478       // Handle page ranges.
479       PageRanges ranges_vector;
480       gint num_ranges;
481       bool print_selection_only = false;
482       switch (gtk_print_settings_get_print_pages(gtk_settings_)) {
483         case GTK_PRINT_PAGES_RANGES: {
484           GtkPageRange* gtk_range =
485               gtk_print_settings_get_page_ranges(gtk_settings_, &num_ranges);
486           if (gtk_range) {
487             for (int i = 0; i < num_ranges; ++i) {
488               printing::PageRange range;
489               range.from = gtk_range[i].start;
490               range.to = gtk_range[i].end;
491               ranges_vector.push_back(range);
492             }
493             g_free(gtk_range);
494           }
495           break;
496         }
497         case GTK_PRINT_PAGES_SELECTION:
498           print_selection_only = true;
499           break;
500         case GTK_PRINT_PAGES_ALL:
501           // Leave |ranges_vector| empty to indicate print all pages.
502           break;
503         case GTK_PRINT_PAGES_CURRENT:
504         default:
505           NOTREACHED();
506           break;
507       }
508 
509       auto settings = std::make_unique<PrintSettings>();
510       settings->set_is_modifiable(context_->settings().is_modifiable());
511       settings->set_ranges(ranges_vector);
512       settings->set_selection_only(print_selection_only);
513       InitPrintSettingsGtk(gtk_settings_, page_setup_, settings.get());
514       context_->InitWithSettings(std::move(settings));
515       std::move(callback_).Run(PrintingContextLinux::OK);
516       return;
517     }
518     case GTK_RESPONSE_DELETE_EVENT:  // Fall through.
519     case GTK_RESPONSE_CANCEL: {
520       std::move(callback_).Run(PrintingContextLinux::CANCEL);
521       return;
522     }
523     case GTK_RESPONSE_APPLY:
524     default: {
525       NOTREACHED();
526     }
527   }
528 }
529 
OnJobCompletedThunk(GtkPrintJob * print_job,gpointer user_data,const GError * error)530 static void OnJobCompletedThunk(GtkPrintJob* print_job,
531                                 gpointer user_data,
532                                 const GError* error) {
533   static_cast<PrintDialogGtk*>(user_data)->OnJobCompleted(print_job, error);
534 }
SendDocumentToPrinter(const base::string16 & document_name)535 void PrintDialogGtk::SendDocumentToPrinter(
536     const base::string16& document_name) {
537   DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
538 
539   // If |printer_| is nullptr then somehow the GTK printer list changed out
540   // under us. In which case, just bail out.
541   if (!printer_) {
542     // Matches AddRef() in PrintDocument();
543     Release();
544     return;
545   }
546 
547   // Save the settings for next time.
548   GetLastUsedSettings().SetLastUsedSettings(gtk_settings_);
549 
550   GtkPrintJob* print_job =
551       gtk_print_job_new(base::UTF16ToUTF8(document_name).c_str(), printer_,
552                         gtk_settings_, page_setup_);
553   gtk_print_job_set_source_file(print_job, path_to_pdf_.value().c_str(),
554                                 nullptr);
555   gtk_print_job_send(print_job, OnJobCompletedThunk, this, nullptr);
556 }
557 
OnJobCompleted(GtkPrintJob * print_job,const GError * error)558 void PrintDialogGtk::OnJobCompleted(GtkPrintJob* print_job,
559                                     const GError* error) {
560   if (error)
561     LOG(ERROR) << "Printing failed: " << error->message;
562   if (print_job)
563     g_object_unref(print_job);
564 
565   base::ThreadPool::PostTask(
566       FROM_HERE,
567       {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
568        base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
569       base::BindOnce(base::GetDeleteFileCallback(), path_to_pdf_));
570   // Printing finished. Matches AddRef() in PrintDocument();
571   Release();
572 }
573 
InitPrintSettings(std::unique_ptr<PrintSettings> settings)574 void PrintDialogGtk::InitPrintSettings(
575     std::unique_ptr<PrintSettings> settings) {
576   InitPrintSettingsGtk(gtk_settings_, page_setup_, settings.get());
577   context_->InitWithSettings(std::move(settings));
578 }
579 
OnWindowDestroying(aura::Window * window)580 void PrintDialogGtk::OnWindowDestroying(aura::Window* window) {
581   DCHECK_EQ(gtk::GetAuraTransientParent(dialog_), window);
582 
583   gtk::ClearAuraTransientParent(dialog_, window);
584   window->RemoveObserver(this);
585   if (callback_)
586     std::move(callback_).Run(PrintingContextLinux::CANCEL);
587 }
588