diff options
author | vitalybuka <vitalybuka@chromium.org> | 2014-09-09 12:53:33 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-09-09 19:59:05 +0000 |
commit | f9d0c0ca61adcc78669177583c701607d70cdb0f (patch) | |
tree | 87512338a2aa0516805c1b942e199cd0fdcf1bcb /printing | |
parent | 75844fdb8b70aa957fc8bc6bba019c2026fde28f (diff) | |
download | chromium_src-f9d0c0ca61adcc78669177583c701607d70cdb0f.zip chromium_src-f9d0c0ca61adcc78669177583c701607d70cdb0f.tar.gz chromium_src-f9d0c0ca61adcc78669177583c701607d70cdb0f.tar.bz2 |
Restored printing windows context for builds without preview.
This is code placed into separate file that was removed in https://codereview.chromium.org/478183005
Code is still used by CEF.
BUG=374321
NOTRY=true
Review URL: https://codereview.chromium.org/557663002
Cr-Commit-Position: refs/heads/master@{#293992}
Diffstat (limited to 'printing')
-rw-r--r-- | printing/printing.gyp | 8 | ||||
-rw-r--r-- | printing/printing_context_system_dialog_win.cc | 269 | ||||
-rw-r--r-- | printing/printing_context_system_dialog_win.h | 56 | ||||
-rw-r--r-- | printing/printing_context_win.cc | 44 | ||||
-rw-r--r-- | printing/printing_context_win.h | 24 | ||||
-rw-r--r-- | printing/printing_context_win_unittest.cc | 141 |
6 files changed, 507 insertions, 35 deletions
diff --git a/printing/printing.gyp b/printing/printing.gyp index fe2aeda..43f903d 100644 --- a/printing/printing.gyp +++ b/printing/printing.gyp @@ -33,6 +33,7 @@ 'backend/print_backend_consts.cc', 'backend/print_backend_consts.h', 'backend/print_backend_dummy.cc', + 'backend/print_backend_win.cc', 'backend/printing_info_win.cc', 'backend/printing_info_win.h', 'emf_win.cc', @@ -78,6 +79,10 @@ 'printed_pages_source.h', 'printing_context.cc', 'printing_context.h', + 'printing_context_system_dialog_win.cc', + 'printing_context_system_dialog_win.h', + 'printing_context_win.cc', + 'printing_context_win.h', 'printing_utils.cc', 'printing_utils.h', 'units.cc', @@ -120,9 +125,6 @@ 'sources': [ 'backend/win_helper.cc', 'backend/win_helper.h', - 'backend/print_backend_win.cc', - 'printing_context_win.cc', - 'printing_context_win.h', ], }], ['chromeos==1',{ diff --git a/printing/printing_context_system_dialog_win.cc b/printing/printing_context_system_dialog_win.cc new file mode 100644 index 0000000..4e1e8f1 --- /dev/null +++ b/printing/printing_context_system_dialog_win.cc @@ -0,0 +1,269 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "printing/printing_context_system_dialog_win.h" + +#include "base/message_loop/message_loop.h" +#include "printing/backend/win_helper.h" +#include "printing/print_settings_initializer_win.h" +#include "skia/ext/platform_device.h" + +namespace printing { + +PrintingContextSytemDialogWin::PrintingContextSytemDialogWin(Delegate* delegate) + : PrintingContextWin(delegate), dialog_box_(NULL) { +} + +PrintingContextSytemDialogWin::~PrintingContextSytemDialogWin() { +} + +void PrintingContextSytemDialogWin::AskUserForSettings( + int max_pages, + bool has_selection, + const PrintSettingsCallback& callback) { + DCHECK(!in_print_job_); + dialog_box_dismissed_ = false; + + HWND window = GetRootWindow(delegate_->GetParentView()); + DCHECK(window); + + // Show the OS-dependent dialog box. + // If the user press + // - OK, the settings are reset and reinitialized with the new settings. OK + // is + // returned. + // - Apply then Cancel, the settings are reset and reinitialized with the + // new + // settings. CANCEL is returned. + // - Cancel, the settings are not changed, the previous setting, if it was + // initialized before, are kept. CANCEL is returned. + // On failure, the settings are reset and FAILED is returned. + PRINTDLGEX dialog_options = {sizeof(PRINTDLGEX)}; + dialog_options.hwndOwner = window; + // Disable options we don't support currently. + // TODO(maruel): Reuse the previously loaded settings! + dialog_options.Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE | + PD_NOCURRENTPAGE | PD_HIDEPRINTTOFILE; + if (!has_selection) + dialog_options.Flags |= PD_NOSELECTION; + + PRINTPAGERANGE ranges[32]; + dialog_options.nStartPage = START_PAGE_GENERAL; + if (max_pages) { + // Default initialize to print all the pages. + memset(ranges, 0, sizeof(ranges)); + ranges[0].nFromPage = 1; + ranges[0].nToPage = max_pages; + dialog_options.nPageRanges = 1; + dialog_options.nMaxPageRanges = arraysize(ranges); + dialog_options.nMinPage = 1; + dialog_options.nMaxPage = max_pages; + dialog_options.lpPageRanges = ranges; + } else { + // No need to bother, we don't know how many pages are available. + dialog_options.Flags |= PD_NOPAGENUMS; + } + + if (ShowPrintDialog(&dialog_options) != S_OK) { + ResetSettings(); + callback.Run(FAILED); + } + + // TODO(maruel): Support PD_PRINTTOFILE. + callback.Run(ParseDialogResultEx(dialog_options)); +} + +void PrintingContextSytemDialogWin::Cancel() { + PrintingContextWin::Cancel(); + if (dialog_box_) { + DestroyWindow(dialog_box_); + dialog_box_dismissed_ = true; + } +} + +HRESULT PrintingContextSytemDialogWin::ShowPrintDialog(PRINTDLGEX* options) { + // Note that this cannot use ui::BaseShellDialog as the print dialog is + // system modal: opening it from a background thread can cause Windows to + // get the wrong Z-order which will make the print dialog appear behind the + // browser frame (but still being modal) so neither the browser frame nor + // the print dialog will get any input. See http://crbug.com/342697 + // http://crbug.com/180997 for details. + base::MessageLoop::ScopedNestableTaskAllower allow( + base::MessageLoop::current()); + + return PrintDlgEx(options); +} + +bool PrintingContextSytemDialogWin::InitializeSettings( + const DEVMODE& dev_mode, + const std::wstring& new_device_name, + const PRINTPAGERANGE* ranges, + int number_ranges, + bool selection_only) { + DCHECK(GetDeviceCaps(context(), CLIPCAPS)); + DCHECK(GetDeviceCaps(context(), RASTERCAPS) & RC_STRETCHDIB); + DCHECK(GetDeviceCaps(context(), RASTERCAPS) & RC_BITMAP64); + // Some printers don't advertise these. + // DCHECK(GetDeviceCaps(context(), RASTERCAPS) & RC_SCALING); + // DCHECK(GetDeviceCaps(context(), SHADEBLENDCAPS) & SB_CONST_ALPHA); + // DCHECK(GetDeviceCaps(context(), SHADEBLENDCAPS) & SB_PIXEL_ALPHA); + + // StretchDIBits() support is needed for printing. + if (!(GetDeviceCaps(context(), RASTERCAPS) & RC_STRETCHDIB) || + !(GetDeviceCaps(context(), RASTERCAPS) & RC_BITMAP64)) { + NOTREACHED(); + ResetSettings(); + return false; + } + + DCHECK(!in_print_job_); + DCHECK(context()); + PageRanges ranges_vector; + if (!selection_only) { + // Convert the PRINTPAGERANGE array to a PrintSettings::PageRanges vector. + ranges_vector.reserve(number_ranges); + for (int i = 0; i < number_ranges; ++i) { + PageRange range; + // Transfer from 1-based to 0-based. + range.from = ranges[i].nFromPage - 1; + range.to = ranges[i].nToPage - 1; + ranges_vector.push_back(range); + } + } + + settings_.set_ranges(ranges_vector); + settings_.set_device_name(new_device_name); + settings_.set_selection_only(selection_only); + PrintSettingsInitializerWin::InitPrintSettings( + context(), dev_mode, &settings_); + + return true; +} + +PrintingContext::Result PrintingContextSytemDialogWin::ParseDialogResultEx( + const PRINTDLGEX& dialog_options) { + // If the user clicked OK or Apply then Cancel, but not only Cancel. + if (dialog_options.dwResultAction != PD_RESULT_CANCEL) { + // Start fresh. + ResetSettings(); + + DEVMODE* dev_mode = NULL; + if (dialog_options.hDevMode) { + dev_mode = + reinterpret_cast<DEVMODE*>(GlobalLock(dialog_options.hDevMode)); + DCHECK(dev_mode); + } + + std::wstring device_name; + if (dialog_options.hDevNames) { + DEVNAMES* dev_names = + reinterpret_cast<DEVNAMES*>(GlobalLock(dialog_options.hDevNames)); + DCHECK(dev_names); + if (dev_names) { + device_name = reinterpret_cast<const wchar_t*>(dev_names) + + dev_names->wDeviceOffset; + GlobalUnlock(dialog_options.hDevNames); + } + } + + bool success = false; + if (dev_mode && !device_name.empty()) { + set_context(dialog_options.hDC); + PRINTPAGERANGE* page_ranges = NULL; + DWORD num_page_ranges = 0; + bool print_selection_only = false; + if (dialog_options.Flags & PD_PAGENUMS) { + page_ranges = dialog_options.lpPageRanges; + num_page_ranges = dialog_options.nPageRanges; + } + if (dialog_options.Flags & PD_SELECTION) { + print_selection_only = true; + } + success = InitializeSettings(*dev_mode, + device_name, + page_ranges, + num_page_ranges, + print_selection_only); + } + + if (!success && dialog_options.hDC) { + DeleteDC(dialog_options.hDC); + set_context(NULL); + } + + if (dev_mode) { + GlobalUnlock(dialog_options.hDevMode); + } + } else { + if (dialog_options.hDC) { + DeleteDC(dialog_options.hDC); + } + } + + if (dialog_options.hDevMode != NULL) + GlobalFree(dialog_options.hDevMode); + if (dialog_options.hDevNames != NULL) + GlobalFree(dialog_options.hDevNames); + + switch (dialog_options.dwResultAction) { + case PD_RESULT_PRINT: + return context() ? OK : FAILED; + case PD_RESULT_APPLY: + return context() ? CANCEL : FAILED; + case PD_RESULT_CANCEL: + return CANCEL; + default: + return FAILED; + } +} + +PrintingContext::Result PrintingContextSytemDialogWin::ParseDialogResult( + const PRINTDLG& dialog_options) { + // If the user clicked OK or Apply then Cancel, but not only Cancel. + // Start fresh. + ResetSettings(); + + DEVMODE* dev_mode = NULL; + if (dialog_options.hDevMode) { + dev_mode = reinterpret_cast<DEVMODE*>(GlobalLock(dialog_options.hDevMode)); + DCHECK(dev_mode); + } + + std::wstring device_name; + if (dialog_options.hDevNames) { + DEVNAMES* dev_names = + reinterpret_cast<DEVNAMES*>(GlobalLock(dialog_options.hDevNames)); + DCHECK(dev_names); + if (dev_names) { + device_name = reinterpret_cast<const wchar_t*>( + reinterpret_cast<const wchar_t*>(dev_names) + + dev_names->wDeviceOffset); + GlobalUnlock(dialog_options.hDevNames); + } + } + + bool success = false; + if (dev_mode && !device_name.empty()) { + set_context(dialog_options.hDC); + success = InitializeSettings(*dev_mode, device_name, NULL, 0, false); + } + + if (!success && dialog_options.hDC) { + DeleteDC(dialog_options.hDC); + set_context(NULL); + } + + if (dev_mode) { + GlobalUnlock(dialog_options.hDevMode); + } + + if (dialog_options.hDevMode != NULL) + GlobalFree(dialog_options.hDevMode); + if (dialog_options.hDevNames != NULL) + GlobalFree(dialog_options.hDevNames); + + return context() ? OK : FAILED; +} + +} // namespace printing diff --git a/printing/printing_context_system_dialog_win.h b/printing/printing_context_system_dialog_win.h new file mode 100644 index 0000000..9f07b0e --- /dev/null +++ b/printing/printing_context_system_dialog_win.h @@ -0,0 +1,56 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PRINTING_PRINTING_CONTEXT_SYSTEM_DIALOG_WIN_H_ +#define PRINTING_PRINTING_CONTEXT_SYSTEM_DIALOG_WIN_H_ + +#include <ocidl.h> +#include <commdlg.h> + +#include <string> + +#include "printing/printing_context_win.h" +#include "ui/gfx/native_widget_types.h" + +namespace printing { + +class PRINTING_EXPORT PrintingContextSytemDialogWin + : public PrintingContextWin { + public: + explicit PrintingContextSytemDialogWin(Delegate* delegate); + virtual ~PrintingContextSytemDialogWin(); + + // PrintingContext implementation. + virtual void AskUserForSettings( + int max_pages, + bool has_selection, + const PrintSettingsCallback& callback) OVERRIDE; + virtual void Cancel() OVERRIDE; + + private: + friend class MockPrintingContextWin; + + virtual HRESULT ShowPrintDialog(PRINTDLGEX* options); + + // Reads the settings from the selected device context. Updates settings_ and + // its margins. + bool InitializeSettings(const DEVMODE& dev_mode, + const std::wstring& new_device_name, + const PRINTPAGERANGE* ranges, + int number_ranges, + bool selection_only); + + // Parses the result of a PRINTDLGEX result. + Result ParseDialogResultEx(const PRINTDLGEX& dialog_options); + Result ParseDialogResult(const PRINTDLG& dialog_options); + + // The dialog box for the time it is shown. + volatile HWND dialog_box_; + + DISALLOW_COPY_AND_ASSIGN(PrintingContextSytemDialogWin); +}; + +} // namespace printing + +#endif // PRINTING_PRINTING_CONTEXT_SYSTEM_DIALOG_WIN_H_ diff --git a/printing/printing_context_win.cc b/printing/printing_context_win.cc index 3e4e6e9..42ebb1a 100644 --- a/printing/printing_context_win.cc +++ b/printing/printing_context_win.cc @@ -12,6 +12,7 @@ #include "printing/backend/win_helper.h" #include "printing/print_settings_initializer_win.h" #include "printing/printed_document.h" +#include "printing/printing_context_system_dialog_win.h" #include "printing/printing_utils.h" #include "printing/units.h" #include "skia/ext/platform_device.h" @@ -21,32 +22,16 @@ #include "ui/aura/window.h" #endif -namespace { - -HWND GetRootWindow(gfx::NativeView view) { - HWND window = NULL; -#if defined(USE_AURA) - if (view) - window = view->GetHost()->GetAcceleratedWidget(); -#else - if (view && IsWindow(view)) { - window = GetAncestor(view, GA_ROOTOWNER); - } -#endif - if (!window) { - // TODO(maruel): bug 1214347 Get the right browser window instead. - return GetDesktopWindow(); - } - return window; -} - -} // anonymous namespace - namespace printing { // static scoped_ptr<PrintingContext> PrintingContext::Create(Delegate* delegate) { +#if defined(DISABLE_BASIC_PRINTING) return make_scoped_ptr<PrintingContext>(new PrintingContextWin(delegate)); +#else // DISABLE_BASIC_PRINTING + return make_scoped_ptr<PrintingContext>( + new PrintingContextSytemDialogWin(delegate)); +#endif // DISABLE_BASIC_PRINTING } PrintingContextWin::PrintingContextWin(Delegate* delegate) @@ -364,6 +349,23 @@ PrintingContext::Result PrintingContextWin::InitializeSettings( return OK; } +HWND PrintingContextWin::GetRootWindow(gfx::NativeView view) { + HWND window = NULL; +#if defined(USE_AURA) + if (view) + window = view->GetHost()->GetAcceleratedWidget(); +#else + if (view && IsWindow(view)) { + window = GetAncestor(view, GA_ROOTOWNER); + } +#endif + if (!window) { + // TODO(maruel): crbug.com/1214347 Get the right browser window instead. + return GetDesktopWindow(); + } + return window; +} + scoped_ptr<DEVMODE, base::FreeDeleter> PrintingContextWin::ShowPrintDialog( HANDLE printer, gfx::NativeView parent_view, diff --git a/printing/printing_context_win.h b/printing/printing_context_win.h index 99a7e3e..e7869dc 100644 --- a/printing/printing_context_win.h +++ b/printing/printing_context_win.h @@ -5,18 +5,16 @@ #ifndef PRINTING_PRINTING_CONTEXT_WIN_H_ #define PRINTING_PRINTING_CONTEXT_WIN_H_ -#include <ocidl.h> -#include <commdlg.h> - #include <string> #include "base/memory/scoped_ptr.h" -#include "build/build_config.h" #include "printing/printing_context.h" #include "ui/gfx/native_widget_types.h" namespace printing { +class PrintSettings; + class PRINTING_EXPORT PrintingContextWin : public PrintingContext { public: explicit PrintingContextWin(Delegate* delegate); @@ -41,20 +39,26 @@ class PRINTING_EXPORT PrintingContextWin : public PrintingContext { virtual gfx::NativeDrawingContext context() const OVERRIDE; protected: + static HWND GetRootWindow(gfx::NativeView view); + + // Reads the settings from the selected device context. Updates settings_ and + // its margins. + virtual Result InitializeSettings(const base::string16& device_name, + DEVMODE* dev_mode); + + HDC contest() const { return context_; } + + void set_context(HDC context) { context_ = context; } + + private: virtual scoped_ptr<DEVMODE, base::FreeDeleter> ShowPrintDialog( HANDLE printer, gfx::NativeView parent_view, DEVMODE* dev_mode); - private: // Used in response to the user canceling the printing. static BOOL CALLBACK AbortProc(HDC hdc, int nCode); - // Reads the settings from the selected device context. Updates settings_ and - // its margins. - virtual Result InitializeSettings(const base::string16& device_name, - DEVMODE* dev_mode); - // The selected printer context. HDC context_; diff --git a/printing/printing_context_win_unittest.cc b/printing/printing_context_win_unittest.cc index 5147249..eb6de5ca 100644 --- a/printing/printing_context_win_unittest.cc +++ b/printing/printing_context_win_unittest.cc @@ -4,8 +4,13 @@ #include "printing/printing_context_win.h" -#include "printing/printing_test.h" +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "printing/backend/printing_info_win.h" +#include "printing/backend/win_helper.h" #include "printing/print_settings.h" +#include "printing/printing_context_system_dialog_win.h" +#include "printing/printing_test.h" #include "testing/gtest/include/gtest/gtest.h" namespace printing { @@ -29,6 +34,140 @@ class PrintingContextTest : public PrintingTest<testing::Test>, PrintingContext::Result result_; }; +class MockPrintingContextWin : public PrintingContextSytemDialogWin { + public: + MockPrintingContextWin(Delegate* delegate) + : PrintingContextSytemDialogWin(delegate) {} + + protected: + // This is a fake PrintDlgEx implementation that sets the right fields in + // |lppd| to trigger a bug in older revisions of PrintingContext. + HRESULT ShowPrintDialog(PRINTDLGEX* lppd) OVERRIDE { + // The interesting bits: + // Pretend the user hit print + lppd->dwResultAction = PD_RESULT_PRINT; + + // Pretend the page range is 1-5, but since lppd->Flags does not have + // PD_SELECTION set, this really shouldn't matter. + lppd->nPageRanges = 1; + lppd->lpPageRanges[0].nFromPage = 1; + lppd->lpPageRanges[0].nToPage = 5; + + base::string16 printer_name = PrintingContextTest::GetDefaultPrinter(); + ScopedPrinterHandle printer; + if (!printer.OpenPrinter(printer_name.c_str())) + return E_FAIL; + + scoped_ptr<uint8[]> buffer; + const DEVMODE* dev_mode = NULL; + HRESULT result = S_OK; + lppd->hDC = NULL; + lppd->hDevMode = NULL; + lppd->hDevNames = NULL; + + PrinterInfo2 info_2; + if (info_2.Init(printer)) { + dev_mode = info_2.get()->pDevMode; + } + if (!dev_mode) { + result = E_FAIL; + goto Cleanup; + } + + lppd->hDC = CreateDC(L"WINSPOOL", printer_name.c_str(), NULL, dev_mode); + if (!lppd->hDC) { + result = E_FAIL; + goto Cleanup; + } + + size_t dev_mode_size = dev_mode->dmSize + dev_mode->dmDriverExtra; + lppd->hDevMode = GlobalAlloc(GMEM_MOVEABLE, dev_mode_size); + if (!lppd->hDevMode) { + result = E_FAIL; + goto Cleanup; + } + void* dev_mode_ptr = GlobalLock(lppd->hDevMode); + if (!dev_mode_ptr) { + result = E_FAIL; + goto Cleanup; + } + memcpy(dev_mode_ptr, dev_mode, dev_mode_size); + GlobalUnlock(lppd->hDevMode); + dev_mode_ptr = NULL; + + size_t driver_size = + 2 + sizeof(wchar_t) * lstrlen(info_2.get()->pDriverName); + size_t printer_size = + 2 + sizeof(wchar_t) * lstrlen(info_2.get()->pPrinterName); + size_t port_size = 2 + sizeof(wchar_t) * lstrlen(info_2.get()->pPortName); + size_t dev_names_size = + sizeof(DEVNAMES) + driver_size + printer_size + port_size; + lppd->hDevNames = GlobalAlloc(GHND, dev_names_size); + if (!lppd->hDevNames) { + result = E_FAIL; + goto Cleanup; + } + void* dev_names_ptr = GlobalLock(lppd->hDevNames); + if (!dev_names_ptr) { + result = E_FAIL; + goto Cleanup; + } + DEVNAMES* dev_names = reinterpret_cast<DEVNAMES*>(dev_names_ptr); + dev_names->wDefault = 1; + dev_names->wDriverOffset = sizeof(DEVNAMES) / sizeof(wchar_t); + memcpy(reinterpret_cast<uint8*>(dev_names_ptr) + dev_names->wDriverOffset, + info_2.get()->pDriverName, + driver_size); + dev_names->wDeviceOffset = + dev_names->wDriverOffset + driver_size / sizeof(wchar_t); + memcpy(reinterpret_cast<uint8*>(dev_names_ptr) + dev_names->wDeviceOffset, + info_2.get()->pPrinterName, + printer_size); + dev_names->wOutputOffset = + dev_names->wDeviceOffset + printer_size / sizeof(wchar_t); + memcpy(reinterpret_cast<uint8*>(dev_names_ptr) + dev_names->wOutputOffset, + info_2.get()->pPortName, + port_size); + GlobalUnlock(lppd->hDevNames); + dev_names_ptr = NULL; + + Cleanup: + // Note: This section does proper deallocation/free of DC/global handles. We + // did not use ScopedHGlobal or ScopedHandle because they did not + // perform what we need. Goto's are used based on Windows programming + // idiom, to avoid deeply nested if's, and try-catch-finally is not + // allowed in Chromium. + if (FAILED(result)) { + if (lppd->hDC) { + DeleteDC(lppd->hDC); + } + if (lppd->hDevMode) { + GlobalFree(lppd->hDevMode); + } + if (lppd->hDevNames) { + GlobalFree(lppd->hDevNames); + } + } + return result; + } +}; + +TEST_F(PrintingContextTest, PrintAll) { + base::MessageLoop message_loop; + if (IsTestCaseDisabled()) + return; + + MockPrintingContextWin context(this); + context.AskUserForSettings( + 123, + false, + base::Bind(&PrintingContextTest::PrintSettingsCallback, + base::Unretained(this))); + EXPECT_EQ(PrintingContext::OK, result()); + PrintSettings settings = context.settings(); + EXPECT_EQ(settings.ranges().size(), 0); +} + TEST_F(PrintingContextTest, Base) { if (IsTestCaseDisabled()) return; |