1 // Copyright 2014 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.
5 #include "printing/printing_context_system_dialog_win.h"
9 #include "base/auto_reset.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/task/current_thread.h"
12 #include "printing/backend/win_helper.h"
13 #include "printing/buildflags/buildflags.h"
14 #include "printing/mojom/print.mojom.h"
15 #include "printing/print_settings_initializer_win.h"
16 #include "skia/ext/skia_utils_win.h"
18 #if BUILDFLAG(ENABLE_OOP_PRINTING)
19 #include "printing/printing_features.h"
24 HWND PrintingContextSystemDialogWin::GetWindow() {
25 #if BUILDFLAG(ENABLE_OOP_PRINTING)
26 if (features::ShouldPrintJobOop()) {
27 // Delving through the view tree to get to root window happens separately
28 // in the browser process (i.e., not in `PrintingContextSystemDialogWin`)
29 // before sending the identified window owner to the Print Backend service.
30 // This means that this call is happening in the service, and thus should
31 // just use the parent view as-is instead of looking for the root window.
32 // TODO(crbug.com/809738) Pursue having a service-level instantiation of
33 // `PrintingContextSystemDialogWin` for this behavior. That would ensure
34 // this logic would be compile-time driven and only invoked by the service.
35 return reinterpret_cast<HWND>(delegate_->GetParentView());
38 return GetRootWindow(delegate_->GetParentView());
41 PrintingContextSystemDialogWin::PrintingContextSystemDialogWin(
43 : PrintingContextWin(delegate) {}
45 PrintingContextSystemDialogWin::~PrintingContextSystemDialogWin() {}
47 void PrintingContextSystemDialogWin::AskUserForSettings(
51 PrintSettingsCallback callback) {
52 DCHECK(!in_print_job_);
54 HWND window = GetWindow();
57 // Show the OS-dependent dialog box.
59 // - OK, the settings are reset and reinitialized with the new settings. OK is
61 // - Apply then Cancel, the settings are reset and reinitialized with the new
62 // settings. CANCEL is returned.
63 // - Cancel, the settings are not changed, the previous setting, if it was
64 // initialized before, are kept. CANCEL is returned.
65 // On failure, the settings are reset and FAILED is returned.
66 PRINTDLGEX dialog_options = {sizeof(PRINTDLGEX)};
67 dialog_options.hwndOwner = window;
68 // Disable options we don't support currently.
69 // TODO(maruel): Reuse the previously loaded settings!
70 dialog_options.Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE |
71 PD_NOCURRENTPAGE | PD_HIDEPRINTTOFILE;
73 dialog_options.Flags |= PD_NOSELECTION;
75 PRINTPAGERANGE ranges[32];
76 dialog_options.nStartPage = START_PAGE_GENERAL;
78 // Default initialize to print all the pages.
79 memset(ranges, 0, sizeof(ranges));
80 ranges[0].nFromPage = 1;
81 ranges[0].nToPage = max_pages;
82 dialog_options.nPageRanges = 1;
83 dialog_options.nMaxPageRanges = std::size(ranges);
84 dialog_options.nMinPage = 1;
85 dialog_options.nMaxPage = max_pages;
86 dialog_options.lpPageRanges = ranges;
88 // No need to bother, we don't know how many pages are available.
89 dialog_options.Flags |= PD_NOPAGENUMS;
92 if (ShowPrintDialog(&dialog_options) != S_OK) {
94 std::move(callback).Run(mojom::ResultCode::kFailed);
98 // TODO(maruel): Support PD_PRINTTOFILE.
99 std::move(callback).Run(ParseDialogResultEx(dialog_options));
102 HRESULT PrintingContextSystemDialogWin::ShowPrintDialog(PRINTDLGEX* options) {
103 // Runs always on the UI thread.
104 static bool is_dialog_shown = false;
107 // Block opening dialog from nested task. It crashes PrintDlgEx.
108 base::AutoReset<bool> auto_reset(&is_dialog_shown, true);
110 // Note that this cannot use ui::BaseShellDialog as the print dialog is
111 // system modal: opening it from a background thread can cause Windows to
112 // get the wrong Z-order which will make the print dialog appear behind the
113 // browser frame (but still being modal) so neither the browser frame nor
114 // the print dialog will get any input. See http://crbug.com/342697
115 // http://crbug.com/180997 for details.
116 base::CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop allow;
118 return PrintDlgEx(options);
121 bool PrintingContextSystemDialogWin::InitializeSettingsWithRanges(
122 const DEVMODE& dev_mode,
123 const std::wstring& new_device_name,
124 const PRINTPAGERANGE* ranges,
126 bool selection_only) {
127 DCHECK(GetDeviceCaps(context(), CLIPCAPS));
128 DCHECK(GetDeviceCaps(context(), RASTERCAPS) & RC_STRETCHDIB);
129 DCHECK(GetDeviceCaps(context(), RASTERCAPS) & RC_BITMAP64);
130 // Some printers don't advertise these.
131 // DCHECK(GetDeviceCaps(context(), RASTERCAPS) & RC_SCALING);
132 // DCHECK(GetDeviceCaps(context(), SHADEBLENDCAPS) & SB_CONST_ALPHA);
133 // DCHECK(GetDeviceCaps(context(), SHADEBLENDCAPS) & SB_PIXEL_ALPHA);
135 // StretchDIBits() support is needed for printing.
136 if (!(GetDeviceCaps(context(), RASTERCAPS) & RC_STRETCHDIB) ||
137 !(GetDeviceCaps(context(), RASTERCAPS) & RC_BITMAP64)) {
143 DCHECK(!in_print_job_);
145 PageRanges ranges_vector;
146 if (!selection_only) {
147 // Convert the PRINTPAGERANGE array to a PrintSettings::PageRanges vector.
148 ranges_vector.reserve(number_ranges);
149 for (int i = 0; i < number_ranges; ++i) {
151 // Transfer from 1-based to 0-based.
152 range.from = ranges[i].nFromPage - 1;
153 range.to = ranges[i].nToPage - 1;
154 ranges_vector.push_back(range);
158 settings_->set_ranges(ranges_vector);
159 settings_->set_device_name(base::WideToUTF16(new_device_name));
160 settings_->set_selection_only(selection_only);
161 PrintSettingsInitializerWin::InitPrintSettings(context(), dev_mode,
167 mojom::ResultCode PrintingContextSystemDialogWin::ParseDialogResultEx(
168 const PRINTDLGEX& dialog_options) {
169 // If the user clicked OK or Apply then Cancel, but not only Cancel.
170 if (dialog_options.dwResultAction != PD_RESULT_CANCEL) {
171 // Start fresh, but preserve is_modifiable print setting.
172 bool is_modifiable = settings_->is_modifiable();
174 settings_->set_is_modifiable(is_modifiable);
176 DEVMODE* dev_mode = NULL;
177 if (dialog_options.hDevMode) {
179 reinterpret_cast<DEVMODE*>(GlobalLock(dialog_options.hDevMode));
183 std::wstring device_name;
184 if (dialog_options.hDevNames) {
185 DEVNAMES* dev_names =
186 reinterpret_cast<DEVNAMES*>(GlobalLock(dialog_options.hDevNames));
189 device_name = reinterpret_cast<const wchar_t*>(dev_names) +
190 dev_names->wDeviceOffset;
191 GlobalUnlock(dialog_options.hDevNames);
195 bool success = false;
196 if (dev_mode && !device_name.empty()) {
197 set_context(dialog_options.hDC);
198 PRINTPAGERANGE* page_ranges = NULL;
199 DWORD num_page_ranges = 0;
200 bool print_selection_only = false;
201 if (dialog_options.Flags & PD_PAGENUMS) {
202 page_ranges = dialog_options.lpPageRanges;
203 num_page_ranges = dialog_options.nPageRanges;
205 if (dialog_options.Flags & PD_SELECTION) {
206 print_selection_only = true;
209 InitializeSettingsWithRanges(*dev_mode, device_name, page_ranges,
210 num_page_ranges, print_selection_only);
213 if (!success && dialog_options.hDC) {
214 DeleteDC(dialog_options.hDC);
219 GlobalUnlock(dialog_options.hDevMode);
222 if (dialog_options.hDC) {
223 DeleteDC(dialog_options.hDC);
227 if (dialog_options.hDevMode != NULL)
228 GlobalFree(dialog_options.hDevMode);
229 if (dialog_options.hDevNames != NULL)
230 GlobalFree(dialog_options.hDevNames);
232 switch (dialog_options.dwResultAction) {
233 case PD_RESULT_PRINT:
234 return context() ? mojom::ResultCode::kSuccess
235 : mojom::ResultCode::kFailed;
236 case PD_RESULT_APPLY:
237 return context() ? mojom::ResultCode::kCanceled
238 : mojom::ResultCode::kFailed;
239 case PD_RESULT_CANCEL:
240 return mojom::ResultCode::kCanceled;
242 return mojom::ResultCode::kFailed;
246 } // namespace printing