1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include <gtk/gtk.h>
7 #include <gtk/gtkunixprint.h>
8 #include <stdlib.h>
9
10 #include "mozilla/ArrayUtils.h"
11 #include "mozilla/Services.h"
12
13 #include "MozContainer.h"
14 #include "nsIPrintSettings.h"
15 #include "nsIWidget.h"
16 #include "nsPrintDialogGTK.h"
17 #include "nsPrintSettingsGTK.h"
18 #include "nsString.h"
19 #include "nsReadableUtils.h"
20 #include "nsIStringBundle.h"
21 #include "nsIPrintSettingsService.h"
22 #include "nsPIDOMWindow.h"
23 #include "nsIGIOService.h"
24 #include "WidgetUtils.h"
25 #include "nsIObserverService.h"
26
27 // for gdk_x11_window_get_xid
28 #include <gdk/gdkx.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <gio/gunixfdlist.h>
33 #include "gfxPlatformGtk.h"
34
35 // for dlsym
36 #include <dlfcn.h>
37 #include "MainThreadUtils.h"
38
39 using namespace mozilla;
40 using namespace mozilla::widget;
41
42 static const char header_footer_tags[][4] = {"", "&T", "&U", "&D", "&P", "&PT"};
43
44 #define CUSTOM_VALUE_INDEX gint(ArrayLength(header_footer_tags))
45
get_gtk_window_for_nsiwidget(nsIWidget * widget)46 static GtkWindow* get_gtk_window_for_nsiwidget(nsIWidget* widget) {
47 return GTK_WINDOW(widget->GetNativeData(NS_NATIVE_SHELLWIDGET));
48 }
49
ShowCustomDialog(GtkComboBox * changed_box,gpointer user_data)50 static void ShowCustomDialog(GtkComboBox* changed_box, gpointer user_data) {
51 if (gtk_combo_box_get_active(changed_box) != CUSTOM_VALUE_INDEX) {
52 g_object_set_data(G_OBJECT(changed_box), "previous-active",
53 GINT_TO_POINTER(gtk_combo_box_get_active(changed_box)));
54 return;
55 }
56
57 GtkWindow* printDialog = GTK_WINDOW(user_data);
58 nsCOMPtr<nsIStringBundleService> bundleSvc =
59 do_GetService(NS_STRINGBUNDLE_CONTRACTID);
60
61 nsCOMPtr<nsIStringBundle> printBundle;
62 bundleSvc->CreateBundle("chrome://global/locale/printdialog.properties",
63 getter_AddRefs(printBundle));
64 nsAutoString intlString;
65
66 printBundle->GetStringFromName("headerFooterCustom", intlString);
67 GtkWidget* prompt_dialog = gtk_dialog_new_with_buttons(
68 NS_ConvertUTF16toUTF8(intlString).get(), printDialog,
69 (GtkDialogFlags)(GTK_DIALOG_MODAL), GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
70 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, nullptr);
71 gtk_dialog_set_default_response(GTK_DIALOG(prompt_dialog),
72 GTK_RESPONSE_ACCEPT);
73 gtk_dialog_set_alternative_button_order(
74 GTK_DIALOG(prompt_dialog), GTK_RESPONSE_ACCEPT, GTK_RESPONSE_REJECT, -1);
75
76 printBundle->GetStringFromName("customHeaderFooterPrompt", intlString);
77 GtkWidget* custom_label =
78 gtk_label_new(NS_ConvertUTF16toUTF8(intlString).get());
79 GtkWidget* custom_entry = gtk_entry_new();
80 GtkWidget* question_icon =
81 gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG);
82
83 // To be convenient, prefill the textbox with the existing value, if any, and
84 // select it all so they can easily both edit it and type in a new one.
85 const char* current_text =
86 (const char*)g_object_get_data(G_OBJECT(changed_box), "custom-text");
87 if (current_text) {
88 gtk_entry_set_text(GTK_ENTRY(custom_entry), current_text);
89 gtk_editable_select_region(GTK_EDITABLE(custom_entry), 0, -1);
90 }
91 gtk_entry_set_activates_default(GTK_ENTRY(custom_entry), TRUE);
92
93 GtkWidget* custom_vbox = gtk_vbox_new(TRUE, 2);
94 gtk_box_pack_start(GTK_BOX(custom_vbox), custom_label, FALSE, FALSE, 0);
95 gtk_box_pack_start(GTK_BOX(custom_vbox), custom_entry, FALSE, FALSE,
96 5); // Make entry 5px underneath label
97 GtkWidget* custom_hbox = gtk_hbox_new(FALSE, 2);
98 gtk_box_pack_start(GTK_BOX(custom_hbox), question_icon, FALSE, FALSE, 0);
99 gtk_box_pack_start(GTK_BOX(custom_hbox), custom_vbox, FALSE, FALSE,
100 10); // Make question icon 10px away from content
101 gtk_container_set_border_width(GTK_CONTAINER(custom_hbox), 2);
102 gtk_widget_show_all(custom_hbox);
103
104 gtk_box_pack_start(
105 GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(prompt_dialog))),
106 custom_hbox, FALSE, FALSE, 0);
107 gint diag_response = gtk_dialog_run(GTK_DIALOG(prompt_dialog));
108
109 if (diag_response == GTK_RESPONSE_ACCEPT) {
110 const gchar* response_text = gtk_entry_get_text(GTK_ENTRY(custom_entry));
111 g_object_set_data_full(G_OBJECT(changed_box), "custom-text",
112 strdup(response_text), (GDestroyNotify)free);
113 g_object_set_data(G_OBJECT(changed_box), "previous-active",
114 GINT_TO_POINTER(CUSTOM_VALUE_INDEX));
115 } else {
116 // Go back to the previous index
117 gint previous_active = GPOINTER_TO_INT(
118 g_object_get_data(G_OBJECT(changed_box), "previous-active"));
119 gtk_combo_box_set_active(changed_box, previous_active);
120 }
121
122 gtk_widget_destroy(prompt_dialog);
123 }
124
125 class nsPrintDialogWidgetGTK {
126 public:
127 nsPrintDialogWidgetGTK(nsPIDOMWindowOuter* aParent,
128 nsIPrintSettings* aPrintSettings);
~nsPrintDialogWidgetGTK()129 ~nsPrintDialogWidgetGTK() { gtk_widget_destroy(dialog); }
130 NS_ConvertUTF16toUTF8 GetUTF8FromBundle(const char* aKey);
131 gint Run();
132
133 nsresult ImportSettings(nsIPrintSettings* aNSSettings);
134 nsresult ExportSettings(nsIPrintSettings* aNSSettings);
135
136 private:
137 GtkWidget* dialog;
138 GtkWidget* shrink_to_fit_toggle;
139 GtkWidget* print_bg_colors_toggle;
140 GtkWidget* print_bg_images_toggle;
141 GtkWidget* selection_only_toggle;
142 GtkWidget* header_dropdown[3]; // {left, center, right}
143 GtkWidget* footer_dropdown[3];
144
145 nsCOMPtr<nsIStringBundle> printBundle;
146
147 bool useNativeSelection;
148
149 GtkWidget* ConstructHeaderFooterDropdown(const char16_t* currentString);
150 const char* OptionWidgetToString(GtkWidget* dropdown);
151
152 /* Code to copy between GTK and NS print settings structures.
153 * In the following,
154 * "Import" means to copy from NS to GTK
155 * "Export" means to copy from GTK to NS
156 */
157 void ExportHeaderFooter(nsIPrintSettings* aNS);
158 };
159
nsPrintDialogWidgetGTK(nsPIDOMWindowOuter * aParent,nsIPrintSettings * aSettings)160 nsPrintDialogWidgetGTK::nsPrintDialogWidgetGTK(nsPIDOMWindowOuter* aParent,
161 nsIPrintSettings* aSettings) {
162 nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(aParent);
163 NS_ASSERTION(widget, "Need a widget for dialog to be modal.");
164 GtkWindow* gtkParent = get_gtk_window_for_nsiwidget(widget);
165 NS_ASSERTION(gtkParent, "Need a GTK window for dialog to be modal.");
166
167 nsCOMPtr<nsIStringBundleService> bundleSvc =
168 do_GetService(NS_STRINGBUNDLE_CONTRACTID);
169 bundleSvc->CreateBundle("chrome://global/locale/printdialog.properties",
170 getter_AddRefs(printBundle));
171
172 dialog = gtk_print_unix_dialog_new(GetUTF8FromBundle("printTitleGTK").get(),
173 gtkParent);
174
175 gtk_print_unix_dialog_set_manual_capabilities(
176 GTK_PRINT_UNIX_DIALOG(dialog),
177 GtkPrintCapabilities(
178 GTK_PRINT_CAPABILITY_PAGE_SET | GTK_PRINT_CAPABILITY_COPIES |
179 GTK_PRINT_CAPABILITY_COLLATE | GTK_PRINT_CAPABILITY_REVERSE |
180 GTK_PRINT_CAPABILITY_SCALE | GTK_PRINT_CAPABILITY_GENERATE_PDF));
181
182 // The vast majority of magic numbers in this widget construction are padding.
183 // e.g. for the set_border_width below, 12px matches that of just about every
184 // other window.
185 GtkWidget* custom_options_tab = gtk_vbox_new(FALSE, 0);
186 gtk_container_set_border_width(GTK_CONTAINER(custom_options_tab), 12);
187 GtkWidget* tab_label =
188 gtk_label_new(GetUTF8FromBundle("optionsTabLabelGTK").get());
189
190 // Check buttons for shrink-to-fit and print selection
191 GtkWidget* check_buttons_container = gtk_vbox_new(TRUE, 2);
192 shrink_to_fit_toggle = gtk_check_button_new_with_mnemonic(
193 GetUTF8FromBundle("shrinkToFit").get());
194 gtk_box_pack_start(GTK_BOX(check_buttons_container), shrink_to_fit_toggle,
195 FALSE, FALSE, 0);
196
197 // GTK+2.18 and above allow us to add a "Selection" option to the main
198 // settings screen, rather than adding an option on a custom tab like we must
199 // do on older versions.
200 bool canSelectText;
201 aSettings->GetPrintOptions(nsIPrintSettings::kEnableSelectionRB,
202 &canSelectText);
203 if (gtk_major_version > 2 ||
204 (gtk_major_version == 2 && gtk_minor_version >= 18)) {
205 useNativeSelection = true;
206 g_object_set(dialog, "support-selection", TRUE, "has-selection",
207 canSelectText, "embed-page-setup", TRUE, nullptr);
208 } else {
209 useNativeSelection = false;
210 selection_only_toggle = gtk_check_button_new_with_mnemonic(
211 GetUTF8FromBundle("selectionOnly").get());
212 gtk_widget_set_sensitive(selection_only_toggle, canSelectText);
213 gtk_box_pack_start(GTK_BOX(check_buttons_container), selection_only_toggle,
214 FALSE, FALSE, 0);
215 }
216
217 // Check buttons for printing background
218 GtkWidget* appearance_buttons_container = gtk_vbox_new(TRUE, 2);
219 print_bg_colors_toggle = gtk_check_button_new_with_mnemonic(
220 GetUTF8FromBundle("printBGColors").get());
221 print_bg_images_toggle = gtk_check_button_new_with_mnemonic(
222 GetUTF8FromBundle("printBGImages").get());
223 gtk_box_pack_start(GTK_BOX(appearance_buttons_container),
224 print_bg_colors_toggle, FALSE, FALSE, 0);
225 gtk_box_pack_start(GTK_BOX(appearance_buttons_container),
226 print_bg_images_toggle, FALSE, FALSE, 0);
227
228 // "Appearance" options label, bold and center-aligned
229 GtkWidget* appearance_label = gtk_label_new(nullptr);
230 char* pangoMarkup = g_markup_printf_escaped(
231 "<b>%s</b>", GetUTF8FromBundle("printBGOptions").get());
232 gtk_label_set_markup(GTK_LABEL(appearance_label), pangoMarkup);
233 g_free(pangoMarkup);
234 gtk_misc_set_alignment(GTK_MISC(appearance_label), 0, 0);
235
236 GtkWidget* appearance_container = gtk_alignment_new(0, 0, 0, 0);
237 gtk_alignment_set_padding(GTK_ALIGNMENT(appearance_container), 8, 0, 12, 0);
238 gtk_container_add(GTK_CONTAINER(appearance_container),
239 appearance_buttons_container);
240
241 GtkWidget* appearance_vertical_squasher = gtk_vbox_new(FALSE, 0);
242 gtk_box_pack_start(GTK_BOX(appearance_vertical_squasher), appearance_label,
243 FALSE, FALSE, 0);
244 gtk_box_pack_start(GTK_BOX(appearance_vertical_squasher),
245 appearance_container, FALSE, FALSE, 0);
246
247 // "Header & Footer" options label, bold and center-aligned
248 GtkWidget* header_footer_label = gtk_label_new(nullptr);
249 pangoMarkup = g_markup_printf_escaped(
250 "<b>%s</b>", GetUTF8FromBundle("headerFooter").get());
251 gtk_label_set_markup(GTK_LABEL(header_footer_label), pangoMarkup);
252 g_free(pangoMarkup);
253 gtk_misc_set_alignment(GTK_MISC(header_footer_label), 0, 0);
254
255 GtkWidget* header_footer_container = gtk_alignment_new(0, 0, 0, 0);
256 gtk_alignment_set_padding(GTK_ALIGNMENT(header_footer_container), 8, 0, 12,
257 0);
258
259 // --- Table for making the header and footer options ---
260 GtkWidget* header_footer_table = gtk_table_new(3, 3, FALSE); // 3x3 table
261 nsString header_footer_str[3];
262
263 aSettings->GetHeaderStrLeft(header_footer_str[0]);
264 aSettings->GetHeaderStrCenter(header_footer_str[1]);
265 aSettings->GetHeaderStrRight(header_footer_str[2]);
266
267 for (unsigned int i = 0; i < ArrayLength(header_dropdown); i++) {
268 header_dropdown[i] =
269 ConstructHeaderFooterDropdown(header_footer_str[i].get());
270 // Those 4 magic numbers in the middle provide the position in the table.
271 // The last two numbers mean 2 px padding on every side.
272 gtk_table_attach(GTK_TABLE(header_footer_table), header_dropdown[i], i,
273 (i + 1), 0, 1, (GtkAttachOptions)0, (GtkAttachOptions)0, 2,
274 2);
275 }
276
277 const char labelKeys[][7] = {"left", "center", "right"};
278 for (unsigned int i = 0; i < ArrayLength(labelKeys); i++) {
279 gtk_table_attach(GTK_TABLE(header_footer_table),
280 gtk_label_new(GetUTF8FromBundle(labelKeys[i]).get()), i,
281 (i + 1), 1, 2, (GtkAttachOptions)0, (GtkAttachOptions)0, 2,
282 2);
283 }
284
285 aSettings->GetFooterStrLeft(header_footer_str[0]);
286 aSettings->GetFooterStrCenter(header_footer_str[1]);
287 aSettings->GetFooterStrRight(header_footer_str[2]);
288
289 for (unsigned int i = 0; i < ArrayLength(footer_dropdown); i++) {
290 footer_dropdown[i] =
291 ConstructHeaderFooterDropdown(header_footer_str[i].get());
292 gtk_table_attach(GTK_TABLE(header_footer_table), footer_dropdown[i], i,
293 (i + 1), 2, 3, (GtkAttachOptions)0, (GtkAttachOptions)0, 2,
294 2);
295 }
296 // ---
297
298 gtk_container_add(GTK_CONTAINER(header_footer_container),
299 header_footer_table);
300
301 GtkWidget* header_footer_vertical_squasher = gtk_vbox_new(FALSE, 0);
302 gtk_box_pack_start(GTK_BOX(header_footer_vertical_squasher),
303 header_footer_label, FALSE, FALSE, 0);
304 gtk_box_pack_start(GTK_BOX(header_footer_vertical_squasher),
305 header_footer_container, FALSE, FALSE, 0);
306
307 // Construction of everything
308 gtk_box_pack_start(GTK_BOX(custom_options_tab), check_buttons_container,
309 FALSE, FALSE, 10); // 10px padding
310 gtk_box_pack_start(GTK_BOX(custom_options_tab), appearance_vertical_squasher,
311 FALSE, FALSE, 10);
312 gtk_box_pack_start(GTK_BOX(custom_options_tab),
313 header_footer_vertical_squasher, FALSE, FALSE, 0);
314
315 gtk_print_unix_dialog_add_custom_tab(GTK_PRINT_UNIX_DIALOG(dialog),
316 custom_options_tab, tab_label);
317 gtk_widget_show_all(custom_options_tab);
318 }
319
GetUTF8FromBundle(const char * aKey)320 NS_ConvertUTF16toUTF8 nsPrintDialogWidgetGTK::GetUTF8FromBundle(
321 const char* aKey) {
322 nsAutoString intlString;
323 printBundle->GetStringFromName(aKey, intlString);
324 return NS_ConvertUTF16toUTF8(
325 intlString); // Return the actual object so we don't lose reference
326 }
327
OptionWidgetToString(GtkWidget * dropdown)328 const char* nsPrintDialogWidgetGTK::OptionWidgetToString(GtkWidget* dropdown) {
329 gint index = gtk_combo_box_get_active(GTK_COMBO_BOX(dropdown));
330
331 NS_ASSERTION(index <= CUSTOM_VALUE_INDEX,
332 "Index of dropdown is higher than expected!");
333
334 if (index == CUSTOM_VALUE_INDEX)
335 return (const char*)g_object_get_data(G_OBJECT(dropdown), "custom-text");
336 else
337 return header_footer_tags[index];
338 }
339
Run()340 gint nsPrintDialogWidgetGTK::Run() {
341 const gint response = gtk_dialog_run(GTK_DIALOG(dialog));
342 gtk_widget_hide(dialog);
343 return response;
344 }
345
ExportHeaderFooter(nsIPrintSettings * aNS)346 void nsPrintDialogWidgetGTK::ExportHeaderFooter(nsIPrintSettings* aNS) {
347 const char* header_footer_str;
348 header_footer_str = OptionWidgetToString(header_dropdown[0]);
349 aNS->SetHeaderStrLeft(NS_ConvertUTF8toUTF16(header_footer_str));
350
351 header_footer_str = OptionWidgetToString(header_dropdown[1]);
352 aNS->SetHeaderStrCenter(NS_ConvertUTF8toUTF16(header_footer_str));
353
354 header_footer_str = OptionWidgetToString(header_dropdown[2]);
355 aNS->SetHeaderStrRight(NS_ConvertUTF8toUTF16(header_footer_str));
356
357 header_footer_str = OptionWidgetToString(footer_dropdown[0]);
358 aNS->SetFooterStrLeft(NS_ConvertUTF8toUTF16(header_footer_str));
359
360 header_footer_str = OptionWidgetToString(footer_dropdown[1]);
361 aNS->SetFooterStrCenter(NS_ConvertUTF8toUTF16(header_footer_str));
362
363 header_footer_str = OptionWidgetToString(footer_dropdown[2]);
364 aNS->SetFooterStrRight(NS_ConvertUTF8toUTF16(header_footer_str));
365 }
366
ImportSettings(nsIPrintSettings * aNSSettings)367 nsresult nsPrintDialogWidgetGTK::ImportSettings(nsIPrintSettings* aNSSettings) {
368 MOZ_ASSERT(aNSSettings, "aSettings must not be null");
369 NS_ENSURE_TRUE(aNSSettings, NS_ERROR_FAILURE);
370
371 nsCOMPtr<nsPrintSettingsGTK> aNSSettingsGTK(do_QueryInterface(aNSSettings));
372 if (!aNSSettingsGTK) return NS_ERROR_FAILURE;
373
374 GtkPrintSettings* settings = aNSSettingsGTK->GetGtkPrintSettings();
375 GtkPageSetup* setup = aNSSettingsGTK->GetGtkPageSetup();
376
377 bool geckoBool;
378 aNSSettings->GetShrinkToFit(&geckoBool);
379 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(shrink_to_fit_toggle),
380 geckoBool);
381
382 aNSSettings->GetPrintBGColors(&geckoBool);
383 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(print_bg_colors_toggle),
384 geckoBool);
385
386 aNSSettings->GetPrintBGImages(&geckoBool);
387 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(print_bg_images_toggle),
388 geckoBool);
389
390 gtk_print_unix_dialog_set_settings(GTK_PRINT_UNIX_DIALOG(dialog), settings);
391 gtk_print_unix_dialog_set_page_setup(GTK_PRINT_UNIX_DIALOG(dialog), setup);
392
393 return NS_OK;
394 }
395
ExportSettings(nsIPrintSettings * aNSSettings)396 nsresult nsPrintDialogWidgetGTK::ExportSettings(nsIPrintSettings* aNSSettings) {
397 MOZ_ASSERT(aNSSettings, "aSettings must not be null");
398 NS_ENSURE_TRUE(aNSSettings, NS_ERROR_FAILURE);
399
400 GtkPrintSettings* settings =
401 gtk_print_unix_dialog_get_settings(GTK_PRINT_UNIX_DIALOG(dialog));
402 GtkPageSetup* setup =
403 gtk_print_unix_dialog_get_page_setup(GTK_PRINT_UNIX_DIALOG(dialog));
404 GtkPrinter* printer =
405 gtk_print_unix_dialog_get_selected_printer(GTK_PRINT_UNIX_DIALOG(dialog));
406 if (settings && setup && printer) {
407 ExportHeaderFooter(aNSSettings);
408
409 aNSSettings->SetOutputFormat(nsIPrintSettings::kOutputFormatNative);
410
411 // Print-to-file is true by default. This must be turned off or else
412 // printing won't occur! (We manually copy the spool file when this flag is
413 // set, because we love our embedders) Even if it is print-to-file in GTK's
414 // case, GTK does The Right Thing when we send the job.
415 aNSSettings->SetPrintToFile(false);
416
417 aNSSettings->SetShrinkToFit(
418 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(shrink_to_fit_toggle)));
419
420 aNSSettings->SetPrintBGColors(gtk_toggle_button_get_active(
421 GTK_TOGGLE_BUTTON(print_bg_colors_toggle)));
422 aNSSettings->SetPrintBGImages(gtk_toggle_button_get_active(
423 GTK_TOGGLE_BUTTON(print_bg_images_toggle)));
424
425 // Try to save native settings in the session object
426 nsCOMPtr<nsPrintSettingsGTK> aNSSettingsGTK(do_QueryInterface(aNSSettings));
427 if (aNSSettingsGTK) {
428 aNSSettingsGTK->SetGtkPrintSettings(settings);
429 aNSSettingsGTK->SetGtkPageSetup(setup);
430 aNSSettingsGTK->SetGtkPrinter(printer);
431 bool printSelectionOnly;
432 if (useNativeSelection) {
433 _GtkPrintPages pageSetting =
434 (_GtkPrintPages)gtk_print_settings_get_print_pages(settings);
435 printSelectionOnly = (pageSetting == _GTK_PRINT_PAGES_SELECTION);
436 } else {
437 printSelectionOnly = gtk_toggle_button_get_active(
438 GTK_TOGGLE_BUTTON(selection_only_toggle));
439 }
440 aNSSettingsGTK->SetForcePrintSelectionOnly(printSelectionOnly);
441 }
442 }
443
444 if (settings) g_object_unref(settings);
445 return NS_OK;
446 }
447
ConstructHeaderFooterDropdown(const char16_t * currentString)448 GtkWidget* nsPrintDialogWidgetGTK::ConstructHeaderFooterDropdown(
449 const char16_t* currentString) {
450 GtkWidget* dropdown = gtk_combo_box_text_new();
451 const char hf_options[][22] = {"headerFooterBlank", "headerFooterTitle",
452 "headerFooterURL", "headerFooterDate",
453 "headerFooterPage", "headerFooterPageTotal",
454 "headerFooterCustom"};
455
456 for (unsigned int i = 0; i < ArrayLength(hf_options); i++) {
457 gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(dropdown), nullptr,
458 GetUTF8FromBundle(hf_options[i]).get());
459 }
460
461 bool shouldBeCustom = true;
462 NS_ConvertUTF16toUTF8 currentStringUTF8(currentString);
463
464 for (unsigned int i = 0; i < ArrayLength(header_footer_tags); i++) {
465 if (!strcmp(currentStringUTF8.get(), header_footer_tags[i])) {
466 gtk_combo_box_set_active(GTK_COMBO_BOX(dropdown), i);
467 g_object_set_data(G_OBJECT(dropdown), "previous-active",
468 GINT_TO_POINTER(i));
469 shouldBeCustom = false;
470 break;
471 }
472 }
473
474 if (shouldBeCustom) {
475 gtk_combo_box_set_active(GTK_COMBO_BOX(dropdown), CUSTOM_VALUE_INDEX);
476 g_object_set_data(G_OBJECT(dropdown), "previous-active",
477 GINT_TO_POINTER(CUSTOM_VALUE_INDEX));
478 char* custom_string = strdup(currentStringUTF8.get());
479 g_object_set_data_full(G_OBJECT(dropdown), "custom-text", custom_string,
480 (GDestroyNotify)free);
481 }
482
483 g_signal_connect(dropdown, "changed", (GCallback)ShowCustomDialog, dialog);
484 return dropdown;
485 }
486
487 NS_IMPL_ISUPPORTS(nsPrintDialogServiceGTK, nsIPrintDialogService)
488
489 nsPrintDialogServiceGTK::nsPrintDialogServiceGTK() = default;
490
491 nsPrintDialogServiceGTK::~nsPrintDialogServiceGTK() = default;
492
493 NS_IMETHODIMP
Init()494 nsPrintDialogServiceGTK::Init() { return NS_OK; }
495
496 // Used to obtain window handle. The portal use this handle
497 // to ensure that print dialog is modal.
498 typedef void (*WindowHandleExported)(GtkWindow* window, const char* handle,
499 gpointer user_data);
500
501 typedef void (*GtkWindowHandleExported)(GtkWindow* window, const char* handle,
502 gpointer user_data);
503 #ifdef MOZ_WAYLAND
504 # if !GTK_CHECK_VERSION(3, 22, 0)
505 typedef void (*GdkWaylandWindowExported)(GdkWindow* window, const char* handle,
506 gpointer user_data);
507 # endif
508
509 typedef struct {
510 GtkWindow* window;
511 WindowHandleExported callback;
512 gpointer user_data;
513 } WaylandWindowHandleExportedData;
514
wayland_window_handle_exported(GdkWindow * window,const char * wayland_handle_str,gpointer user_data)515 static void wayland_window_handle_exported(GdkWindow* window,
516 const char* wayland_handle_str,
517 gpointer user_data) {
518 WaylandWindowHandleExportedData* data =
519 static_cast<WaylandWindowHandleExportedData*>(user_data);
520 char* handle_str;
521
522 handle_str = g_strdup_printf("wayland:%s", wayland_handle_str);
523 data->callback(data->window, handle_str, data->user_data);
524 g_free(handle_str);
525 }
526 #endif
527
528 // Get window handle for the portal, taken from gtk/gtkwindow.c
529 // (currently not exported)
window_export_handle(GtkWindow * window,GtkWindowHandleExported callback,gpointer user_data)530 static gboolean window_export_handle(GtkWindow* window,
531 GtkWindowHandleExported callback,
532 gpointer user_data) {
533 if (gfxPlatformGtk::GetPlatform()->IsX11Display()) {
534 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
535 char* handle_str;
536 guint32 xid = (guint32)gdk_x11_window_get_xid(gdk_window);
537
538 handle_str = g_strdup_printf("x11:%x", xid);
539 callback(window, handle_str, user_data);
540 g_free(handle_str);
541 return true;
542 }
543 #ifdef MOZ_WAYLAND
544 else {
545 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
546 WaylandWindowHandleExportedData* data;
547
548 data = g_new0(WaylandWindowHandleExportedData, 1);
549 data->window = window;
550 data->callback = callback;
551 data->user_data = user_data;
552
553 static auto s_gdk_wayland_window_export_handle =
554 reinterpret_cast<gboolean (*)(GdkWindow*, GdkWaylandWindowExported,
555 gpointer, GDestroyNotify)>(
556 dlsym(RTLD_DEFAULT, "gdk_wayland_window_export_handle"));
557 if (!s_gdk_wayland_window_export_handle ||
558 !s_gdk_wayland_window_export_handle(
559 gdk_window, wayland_window_handle_exported, data, g_free)) {
560 g_free(data);
561 return false;
562 } else {
563 return true;
564 }
565 }
566 #endif
567
568 g_warning("Couldn't export handle, unsupported windowing system");
569
570 return false;
571 }
572 /**
573 * Communication class with the GTK print portal handler
574 *
575 * To print document from flatpak we need to use print portal because
576 * printers are not directly accessible in the sandboxed environment.
577 *
578 * At first we request portal to show the print dialog to let user choose
579 * printer settings. We use DBUS interface for that (PreparePrint method).
580 *
581 * Next we force application to print to temporary file and after the writing
582 * to the file is finished we pass its file descriptor to the portal.
583 * Portal will pass duplicate of the file descriptor to the printer which
584 * user selected before (by DBUS Print method).
585 *
586 * Since DBUS communication is done async while nsPrintDialogServiceGTK::Show
587 * is expecting sync execution, we need to create a new GMainLoop during the
588 * print portal dialog is running. The loop is stopped after the dialog
589 * is closed.
590 */
591 class nsFlatpakPrintPortal : public nsIObserver {
592 NS_DECL_ISUPPORTS
593 NS_DECL_NSIOBSERVER
594 public:
595 explicit nsFlatpakPrintPortal(nsPrintSettingsGTK* aPrintSettings);
596 nsresult PreparePrintRequest(GtkWindow* aWindow);
597 static void OnWindowExportHandleDone(GtkWindow* aWindow,
598 const char* aWindowHandleStr,
599 gpointer aUserData);
600 void PreparePrint(GtkWindow* aWindow, const char* aWindowHandleStr);
601 static void OnPreparePrintResponse(GDBusConnection* connection,
602 const char* sender_name,
603 const char* object_path,
604 const char* interface_name,
605 const char* signal_name,
606 GVariant* parameters, gpointer data);
607 GtkPrintOperationResult GetResult();
608
609 private:
610 virtual ~nsFlatpakPrintPortal();
611 void FinishPrintDialog(GVariant* parameters);
612 nsCOMPtr<nsPrintSettingsGTK> mPrintAndPageSettings;
613 GDBusProxy* mProxy;
614 guint32 mToken;
615 GMainLoop* mLoop;
616 GtkPrintOperationResult mResult;
617 guint mResponseSignalId;
618 GtkWindow* mParentWindow;
619 };
620
NS_IMPL_ISUPPORTS(nsFlatpakPrintPortal,nsIObserver)621 NS_IMPL_ISUPPORTS(nsFlatpakPrintPortal, nsIObserver)
622
623 nsFlatpakPrintPortal::nsFlatpakPrintPortal(nsPrintSettingsGTK* aPrintSettings)
624 : mPrintAndPageSettings(aPrintSettings),
625 mProxy(nullptr),
626 mLoop(nullptr),
627 mResponseSignalId(0),
628 mParentWindow(nullptr) {}
629
630 /**
631 * Creates GDBusProxy, query for window handle and create a new GMainLoop.
632 *
633 * The GMainLoop is to be run from GetResult() and be quitted during
634 * FinishPrintDialog.
635 *
636 * @param aWindow toplevel application window which is used as parent of print
637 * dialog
638 */
PreparePrintRequest(GtkWindow * aWindow)639 nsresult nsFlatpakPrintPortal::PreparePrintRequest(GtkWindow* aWindow) {
640 MOZ_ASSERT(aWindow, "aWindow must not be null");
641 MOZ_ASSERT(mPrintAndPageSettings, "mPrintAndPageSettings must not be null");
642
643 GError* error = nullptr;
644 mProxy = g_dbus_proxy_new_for_bus_sync(
645 G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, nullptr,
646 "org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop",
647 "org.freedesktop.portal.Print", nullptr, &error);
648 if (mProxy == nullptr) {
649 NS_WARNING(
650 nsPrintfCString("Unable to create dbus proxy: %s", error->message)
651 .get());
652 g_error_free(error);
653 return NS_ERROR_FAILURE;
654 }
655
656 // The window handler is returned async, we will continue by PreparePrint
657 // method when it is returned.
658 if (!window_export_handle(
659 aWindow, &nsFlatpakPrintPortal::OnWindowExportHandleDone, this)) {
660 NS_WARNING("Unable to get window handle for creating modal print dialog.");
661 return NS_ERROR_FAILURE;
662 }
663
664 mLoop = g_main_loop_new(NULL, FALSE);
665 return NS_OK;
666 }
667
OnWindowExportHandleDone(GtkWindow * aWindow,const char * aWindowHandleStr,gpointer aUserData)668 void nsFlatpakPrintPortal::OnWindowExportHandleDone(
669 GtkWindow* aWindow, const char* aWindowHandleStr, gpointer aUserData) {
670 nsFlatpakPrintPortal* printPortal =
671 static_cast<nsFlatpakPrintPortal*>(aUserData);
672 printPortal->PreparePrint(aWindow, aWindowHandleStr);
673 }
674
675 /**
676 * Ask print portal to show the print dialog.
677 *
678 * Print and page settings and window handle are passed to the portal to prefill
679 * last used settings.
680 */
PreparePrint(GtkWindow * aWindow,const char * aWindowHandleStr)681 void nsFlatpakPrintPortal::PreparePrint(GtkWindow* aWindow,
682 const char* aWindowHandleStr) {
683 GtkPrintSettings* gtkSettings = mPrintAndPageSettings->GetGtkPrintSettings();
684 GtkPageSetup* pageSetup = mPrintAndPageSettings->GetGtkPageSetup();
685
686 // We need to remember GtkWindow to unexport window handle after it is
687 // no longer needed by the portal dialog (apply only on non-X11 sessions).
688 if (gfxPlatformGtk::GetPlatform()->IsWaylandDisplay()) {
689 mParentWindow = aWindow;
690 }
691
692 GVariantBuilder opt_builder;
693 g_variant_builder_init(&opt_builder, G_VARIANT_TYPE_VARDICT);
694 char* token = g_strdup_printf("mozilla%d", g_random_int_range(0, G_MAXINT));
695 g_variant_builder_add(&opt_builder, "{sv}", "handle_token",
696 g_variant_new_string(token));
697 g_free(token);
698 GVariant* options = g_variant_builder_end(&opt_builder);
699 static auto s_gtk_print_settings_to_gvariant =
700 reinterpret_cast<GVariant* (*)(GtkPrintSettings*)>(
701 dlsym(RTLD_DEFAULT, "gtk_print_settings_to_gvariant"));
702 static auto s_gtk_page_setup_to_gvariant =
703 reinterpret_cast<GVariant* (*)(GtkPageSetup*)>(
704 dlsym(RTLD_DEFAULT, "gtk_page_setup_to_gvariant"));
705 if (!s_gtk_print_settings_to_gvariant || !s_gtk_page_setup_to_gvariant) {
706 mResult = GTK_PRINT_OPERATION_RESULT_ERROR;
707 FinishPrintDialog(nullptr);
708 return;
709 }
710
711 // Get translated window title
712 nsCOMPtr<nsIStringBundleService> bundleSvc =
713 do_GetService(NS_STRINGBUNDLE_CONTRACTID);
714 nsCOMPtr<nsIStringBundle> printBundle;
715 bundleSvc->CreateBundle("chrome://global/locale/printdialog.properties",
716 getter_AddRefs(printBundle));
717 nsAutoString intlPrintTitle;
718 printBundle->GetStringFromName("printTitleGTK", intlPrintTitle);
719
720 GError* error = nullptr;
721 GVariant* ret = g_dbus_proxy_call_sync(
722 mProxy, "PreparePrint",
723 g_variant_new(
724 "(ss@a{sv}@a{sv}@a{sv})", aWindowHandleStr,
725 NS_ConvertUTF16toUTF8(intlPrintTitle).get(), // Title of the window
726 s_gtk_print_settings_to_gvariant(gtkSettings),
727 s_gtk_page_setup_to_gvariant(pageSetup), options),
728 G_DBUS_CALL_FLAGS_NONE, -1, nullptr, &error);
729 if (ret == nullptr) {
730 NS_WARNING(
731 nsPrintfCString("Unable to call dbus proxy: %s", error->message).get());
732 g_error_free(error);
733 mResult = GTK_PRINT_OPERATION_RESULT_ERROR;
734 FinishPrintDialog(nullptr);
735 return;
736 }
737
738 const char* handle = nullptr;
739 g_variant_get(ret, "(&o)", &handle);
740 if (strcmp(aWindowHandleStr, handle) != 0) {
741 aWindowHandleStr = g_strdup(handle);
742 if (mResponseSignalId) {
743 g_dbus_connection_signal_unsubscribe(
744 g_dbus_proxy_get_connection(G_DBUS_PROXY(mProxy)), mResponseSignalId);
745 }
746 }
747 mResponseSignalId = g_dbus_connection_signal_subscribe(
748 g_dbus_proxy_get_connection(G_DBUS_PROXY(mProxy)),
749 "org.freedesktop.portal.Desktop", "org.freedesktop.portal.Request",
750 "Response", aWindowHandleStr, NULL, G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
751 &nsFlatpakPrintPortal::OnPreparePrintResponse, this, NULL);
752 }
753
OnPreparePrintResponse(GDBusConnection * connection,const char * sender_name,const char * object_path,const char * interface_name,const char * signal_name,GVariant * parameters,gpointer data)754 void nsFlatpakPrintPortal::OnPreparePrintResponse(
755 GDBusConnection* connection, const char* sender_name,
756 const char* object_path, const char* interface_name,
757 const char* signal_name, GVariant* parameters, gpointer data) {
758 nsFlatpakPrintPortal* printPortal = static_cast<nsFlatpakPrintPortal*>(data);
759 printPortal->FinishPrintDialog(parameters);
760 }
761
762 /**
763 * When the dialog is accepted, read print and page settings and token.
764 *
765 * Token is later used for printing portal as print operation identifier.
766 * Print and page settings are modified in-place and stored to
767 * mPrintAndPageSettings.
768 */
FinishPrintDialog(GVariant * parameters)769 void nsFlatpakPrintPortal::FinishPrintDialog(GVariant* parameters) {
770 // This ends GetResult() method
771 if (mLoop) {
772 g_main_loop_quit(mLoop);
773 mLoop = nullptr;
774 }
775
776 if (!parameters) {
777 // mResult should be already defined
778 return;
779 }
780
781 guint32 response;
782 GVariant* options;
783
784 g_variant_get(parameters, "(u@a{sv})", &response, &options);
785 mResult = GTK_PRINT_OPERATION_RESULT_CANCEL;
786 if (response == 0) {
787 GVariant* v;
788
789 char* filename;
790 char* uri;
791 v = g_variant_lookup_value(options, "settings", G_VARIANT_TYPE_VARDICT);
792 static auto s_gtk_print_settings_new_from_gvariant =
793 reinterpret_cast<GtkPrintSettings* (*)(GVariant*)>(
794 dlsym(RTLD_DEFAULT, "gtk_print_settings_new_from_gvariant"));
795
796 GtkPrintSettings* printSettings = s_gtk_print_settings_new_from_gvariant(v);
797 g_variant_unref(v);
798
799 v = g_variant_lookup_value(options, "page-setup", G_VARIANT_TYPE_VARDICT);
800 static auto s_gtk_page_setup_new_from_gvariant =
801 reinterpret_cast<GtkPageSetup* (*)(GVariant*)>(
802 dlsym(RTLD_DEFAULT, "gtk_page_setup_new_from_gvariant"));
803 GtkPageSetup* pageSetup = s_gtk_page_setup_new_from_gvariant(v);
804 g_variant_unref(v);
805
806 g_variant_lookup(options, "token", "u", &mToken);
807
808 // Force printing to file because only filedescriptor of the file
809 // can be passed to portal
810 int fd = g_file_open_tmp("gtkprintXXXXXX", &filename, NULL);
811 uri = g_filename_to_uri(filename, NULL, NULL);
812 gtk_print_settings_set(printSettings, GTK_PRINT_SETTINGS_OUTPUT_URI, uri);
813 g_free(uri);
814 close(fd);
815
816 // Save native settings in the session object
817 mPrintAndPageSettings->SetGtkPrintSettings(printSettings);
818 mPrintAndPageSettings->SetGtkPageSetup(pageSetup);
819
820 // Portal consumes PDF file
821 mPrintAndPageSettings->SetOutputFormat(nsIPrintSettings::kOutputFormatPDF);
822
823 // We need to set to print to file
824 mPrintAndPageSettings->SetPrintToFile(true);
825
826 mResult = GTK_PRINT_OPERATION_RESULT_APPLY;
827 }
828 }
829
830 /**
831 * Get result of the print dialog.
832 *
833 * This call blocks until FinishPrintDialog is called.
834 *
835 */
GetResult()836 GtkPrintOperationResult nsFlatpakPrintPortal::GetResult() {
837 // If the mLoop has not been initialized we haven't go thru PreparePrint
838 // method
839 if (!NS_IsMainThread() || !mLoop) {
840 return GTK_PRINT_OPERATION_RESULT_ERROR;
841 }
842 // Calling g_main_loop_run stops current code until g_main_loop_quit is called
843 g_main_loop_run(mLoop);
844
845 // Free resources we've allocated in order to show print dialog.
846 #ifdef MOZ_WAYLAND
847 if (mParentWindow) {
848 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(mParentWindow));
849 static auto s_gdk_wayland_window_unexport_handle =
850 reinterpret_cast<void (*)(GdkWindow*)>(
851 dlsym(RTLD_DEFAULT, "gdk_wayland_window_unexport_handle"));
852 if (s_gdk_wayland_window_unexport_handle) {
853 s_gdk_wayland_window_unexport_handle(gdk_window);
854 }
855 }
856 #endif
857 return mResult;
858 }
859
860 /**
861 * Send file descriptor of the file which contains document to the portal to
862 * finish the print operation.
863 */
864 NS_IMETHODIMP
Observe(nsISupports * aObject,const char * aTopic,const char16_t * aData)865 nsFlatpakPrintPortal::Observe(nsISupports* aObject, const char* aTopic,
866 const char16_t* aData) {
867 // Check that written file match to the stored filename in case multiple
868 // print operations are in progress.
869 nsAutoString filenameStr;
870 mPrintAndPageSettings->GetToFileName(filenameStr);
871 if (!nsDependentString(aData).Equals(filenameStr)) {
872 // Different file is finished, not for this instance
873 return NS_OK;
874 }
875 int fd, idx;
876 fd = open(NS_ConvertUTF16toUTF8(filenameStr).get(), O_RDONLY | O_CLOEXEC);
877 static auto s_g_unix_fd_list_new = reinterpret_cast<GUnixFDList* (*)(void)>(
878 dlsym(RTLD_DEFAULT, "g_unix_fd_list_new"));
879 NS_ASSERTION(s_g_unix_fd_list_new,
880 "Cannot find g_unix_fd_list_new function.");
881
882 GUnixFDList* fd_list = s_g_unix_fd_list_new();
883 static auto s_g_unix_fd_list_append =
884 reinterpret_cast<gint (*)(GUnixFDList*, gint, GError**)>(
885 dlsym(RTLD_DEFAULT, "g_unix_fd_list_append"));
886 idx = s_g_unix_fd_list_append(fd_list, fd, NULL);
887 close(fd);
888
889 GVariantBuilder opt_builder;
890 g_variant_builder_init(&opt_builder, G_VARIANT_TYPE_VARDICT);
891 g_variant_builder_add(&opt_builder, "{sv}", "token",
892 g_variant_new_uint32(mToken));
893 g_dbus_proxy_call_with_unix_fd_list(
894 mProxy, "Print",
895 g_variant_new("(ssh@a{sv})", "", /* window */
896 "Print", /* title */
897 idx, g_variant_builder_end(&opt_builder)),
898 G_DBUS_CALL_FLAGS_NONE, -1, fd_list, NULL,
899 NULL, // TODO portal result cb function
900 nullptr); // data
901 g_object_unref(fd_list);
902
903 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
904 // Let the nsFlatpakPrintPortal instance die
905 os->RemoveObserver(this, "print-to-file-finished");
906 return NS_OK;
907 }
908
~nsFlatpakPrintPortal()909 nsFlatpakPrintPortal::~nsFlatpakPrintPortal() {
910 if (mProxy) {
911 if (mResponseSignalId) {
912 g_dbus_connection_signal_unsubscribe(
913 g_dbus_proxy_get_connection(G_DBUS_PROXY(mProxy)), mResponseSignalId);
914 }
915 g_object_unref(mProxy);
916 }
917 if (mLoop) g_main_loop_quit(mLoop);
918 }
919
920 NS_IMETHODIMP
Show(nsPIDOMWindowOuter * aParent,nsIPrintSettings * aSettings)921 nsPrintDialogServiceGTK::Show(nsPIDOMWindowOuter* aParent,
922 nsIPrintSettings* aSettings) {
923 MOZ_ASSERT(aParent, "aParent must not be null");
924 MOZ_ASSERT(aSettings, "aSettings must not be null");
925
926 // Check for the flatpak portal first
927 nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
928 bool shouldUsePortal;
929 giovfs->ShouldUseFlatpakPortal(&shouldUsePortal);
930 if (shouldUsePortal && gtk_check_version(3, 22, 0) == nullptr) {
931 nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(aParent);
932 NS_ASSERTION(widget, "Need a widget for dialog to be modal.");
933 GtkWindow* gtkParent = get_gtk_window_for_nsiwidget(widget);
934 NS_ASSERTION(gtkParent, "Need a GTK window for dialog to be modal.");
935
936 nsCOMPtr<nsPrintSettingsGTK> printSettingsGTK(do_QueryInterface(aSettings));
937 RefPtr<nsFlatpakPrintPortal> fpPrintPortal =
938 new nsFlatpakPrintPortal(printSettingsGTK);
939
940 nsresult rv = fpPrintPortal->PreparePrintRequest(gtkParent);
941 NS_ENSURE_SUCCESS(rv, rv);
942
943 // This blocks until nsFlatpakPrintPortal::FinishPrintDialog is called
944 GtkPrintOperationResult printDialogResult = fpPrintPortal->GetResult();
945
946 switch (printDialogResult) {
947 case GTK_PRINT_OPERATION_RESULT_APPLY: {
948 nsCOMPtr<nsIObserverService> os =
949 mozilla::services::GetObserverService();
950 NS_ENSURE_STATE(os);
951 // Observer waits until notified that the file with the content
952 // to print has been written.
953 rv = os->AddObserver(fpPrintPortal, "print-to-file-finished", false);
954 NS_ENSURE_SUCCESS(rv, rv);
955 break;
956 }
957 case GTK_PRINT_OPERATION_RESULT_CANCEL:
958 rv = NS_ERROR_ABORT;
959 break;
960 default:
961 NS_WARNING("Unexpected response");
962 rv = NS_ERROR_ABORT;
963 }
964 return rv;
965 }
966
967 nsPrintDialogWidgetGTK printDialog(aParent, aSettings);
968 nsresult rv = printDialog.ImportSettings(aSettings);
969
970 NS_ENSURE_SUCCESS(rv, rv);
971
972 const gint response = printDialog.Run();
973
974 // Handle the result
975 switch (response) {
976 case GTK_RESPONSE_OK: // Proceed
977 rv = printDialog.ExportSettings(aSettings);
978 break;
979
980 case GTK_RESPONSE_CANCEL:
981 case GTK_RESPONSE_CLOSE:
982 case GTK_RESPONSE_DELETE_EVENT:
983 case GTK_RESPONSE_NONE:
984 rv = NS_ERROR_ABORT;
985 break;
986
987 case GTK_RESPONSE_APPLY: // Print preview
988 default:
989 NS_WARNING("Unexpected response");
990 rv = NS_ERROR_ABORT;
991 }
992 return rv;
993 }
994
995 NS_IMETHODIMP
ShowPageSetup(nsPIDOMWindowOuter * aParent,nsIPrintSettings * aNSSettings)996 nsPrintDialogServiceGTK::ShowPageSetup(nsPIDOMWindowOuter* aParent,
997 nsIPrintSettings* aNSSettings) {
998 MOZ_ASSERT(aParent, "aParent must not be null");
999 MOZ_ASSERT(aNSSettings, "aSettings must not be null");
1000 NS_ENSURE_TRUE(aNSSettings, NS_ERROR_FAILURE);
1001
1002 nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(aParent);
1003 NS_ASSERTION(widget, "Need a widget for dialog to be modal.");
1004 GtkWindow* gtkParent = get_gtk_window_for_nsiwidget(widget);
1005 NS_ASSERTION(gtkParent, "Need a GTK window for dialog to be modal.");
1006
1007 nsCOMPtr<nsPrintSettingsGTK> aNSSettingsGTK(do_QueryInterface(aNSSettings));
1008 if (!aNSSettingsGTK) return NS_ERROR_FAILURE;
1009
1010 // We need to init the prefs here because aNSSettings in its current form is a
1011 // dummy in both uses of the word
1012 nsCOMPtr<nsIPrintSettingsService> psService =
1013 do_GetService("@mozilla.org/gfx/printsettings-service;1");
1014 if (psService) {
1015 nsString printName;
1016 aNSSettings->GetPrinterName(printName);
1017 if (printName.IsVoid()) {
1018 psService->GetLastUsedPrinterName(printName);
1019 aNSSettings->SetPrinterName(printName);
1020 }
1021 psService->InitPrintSettingsFromPrefs(aNSSettings, true,
1022 nsIPrintSettings::kInitSaveAll);
1023 }
1024
1025 GtkPrintSettings* gtkSettings = aNSSettingsGTK->GetGtkPrintSettings();
1026 GtkPageSetup* oldPageSetup = aNSSettingsGTK->GetGtkPageSetup();
1027
1028 GtkPageSetup* newPageSetup =
1029 gtk_print_run_page_setup_dialog(gtkParent, oldPageSetup, gtkSettings);
1030
1031 aNSSettingsGTK->SetGtkPageSetup(newPageSetup);
1032
1033 // Now newPageSetup has a refcount of 2 (SetGtkPageSetup will addref), put it
1034 // to 1 so if this gets replaced we don't leak.
1035 g_object_unref(newPageSetup);
1036
1037 if (psService)
1038 psService->SavePrintSettingsToPrefs(
1039 aNSSettings, true,
1040 nsIPrintSettings::kInitSaveOrientation |
1041 nsIPrintSettings::kInitSavePaperSize |
1042 nsIPrintSettings::kInitSaveUnwriteableMargins);
1043
1044 return NS_OK;
1045 }
1046