printing_context_system_dialog_win.cc 8.6 KB

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