0

Use document from preview for System Dialog printing on Windows.

System dialog shows only properties of selected printers, no system dialog with printers.
Removed global Ctrl+Shift+P shortcut on windows.

BUG=374321

Review URL: https://codereview.chromium.org/480303002

Cr-Commit-Position: refs/heads/master@{#292032}
This commit is contained in:
vitalybuka
2014-08-26 16:41:45 -07:00
committed by Commit bot
parent fa29c3e301
commit 92ab8ce04d
26 changed files with 231 additions and 477 deletions

@ -48,6 +48,9 @@ PrintViewManager::~PrintViewManager() {
}
bool PrintViewManager::PrintForSystemDialogNow() {
#if defined(OS_WIN)
NOTREACHED();
#endif
return PrintNowInternal(new PrintMsg_PrintForSystemDialog(routing_id()));
}

@ -424,7 +424,11 @@ void PrintingMessageFilter::OnUpdatePrintSettingsReply(
params.params.document_cookie = printer_query->cookie();
params.pages = PageRange::GetPages(printer_query->settings().ranges());
}
PrintHostMsg_UpdatePrintSettings::WriteReplyParams(reply_msg, params);
PrintHostMsg_UpdatePrintSettings::WriteReplyParams(
reply_msg,
params,
printer_query &&
(printer_query->last_status() == printing::PrintingContext::CANCEL));
Send(reply_msg);
// If user hasn't cancelled.
if (printer_query.get()) {

@ -273,12 +273,18 @@ cr.define('print_preview', function() {
* @param {!print_preview.DocumentInfo} documentInfo Document data model.
* @param {boolean=} opt_isOpenPdfInPreview Whether to open the PDF in the
* system's preview application.
* @param {boolean=} opt_showSystemDialog Whether to open system dialog for
* advanced settings.
*/
startPrint: function(destination, printTicketStore, cloudPrintInterface,
documentInfo, opt_isOpenPdfInPreview) {
documentInfo, opt_isOpenPdfInPreview,
opt_showSystemDialog) {
assert(printTicketStore.isTicketValid(),
'Trying to print when ticket is not valid');
assert(!opt_showSystemDialog || (cr.isWindows && destination.isLocal),
'Implemented for Windows only');
var ticket = {
'pageRange': printTicketStore.pageRange.getDocumentPageRanges(),
'mediaSize': printTicketStore.mediaSize.getValue(),
@ -304,7 +310,8 @@ cr.define('print_preview', function() {
'requestID': -1,
'fitToPageEnabled': printTicketStore.fitToPage.getValue(),
'pageWidth': documentInfo.pageSize.width,
'pageHeight': documentInfo.pageSize.height
'pageHeight': documentInfo.pageSize.height,
'showSystemDialog': opt_showSystemDialog
};
if (!destination.isLocal) {
@ -347,6 +354,7 @@ cr.define('print_preview', function() {
/** Shows the system's native printing dialog. */
startShowSystemDialog: function() {
assert(!cr.isWindows);
chrome.send('showSystemDialog');
},

@ -230,6 +230,13 @@ cr.define('print_preview', function() {
*/
this.isInAppKioskMode_ = false;
/**
* Whether Print with System Dialog option is available.
* @type {boolean}
* @private
*/
this.isSystemDialogAvailable_ = false;
/**
* State of the print preview UI.
* @type {print_preview.PrintPreview.UiState_}
@ -243,6 +250,13 @@ cr.define('print_preview', function() {
* @private
*/
this.isPreviewGenerationInProgress_ = true;
/**
* Whether to show system dialog before next printing.
* @type {boolean}
* @private
*/
this.showSystemDialogBeforeNextPrint_ = false;
};
/**
@ -538,7 +552,9 @@ cr.define('print_preview', function() {
this.printTicketStore_,
this.cloudPrintInterface_,
this.documentInfo_,
this.uiState_ == PrintPreview.UiState_.OPENING_PDF_PREVIEW);
this.uiState_ == PrintPreview.UiState_.OPENING_PDF_PREVIEW,
this.showSystemDialogBeforeNextPrint_);
this.showSystemDialogBeforeNextPrint_ = false;
}
return PrintPreview.PrintAttemptResult_.PRINTED;
},
@ -558,6 +574,13 @@ cr.define('print_preview', function() {
* @private
*/
openSystemPrintDialog_: function() {
if (!this.shouldShowSystemDialogLink_())
return;
if (cr.isWindows) {
this.showSystemDialogBeforeNextPrint_ = true;
this.printDocumentOrOpenPdfPreview_(false /*isPdfPreview*/);
return;
}
setIsVisible($('system-dialog-throbber'), true);
this.setIsEnabled_(false);
this.uiState_ = PrintPreview.UiState_.OPENING_NATIVE_PRINT_DIALOG;
@ -599,9 +622,9 @@ cr.define('print_preview', function() {
this.appState_.setInitialized();
$('document-title').innerText = settings.documentTitle;
setIsVisible($('system-dialog-link'),
!settings.hidePrintWithSystemDialogLink &&
!settings.isInAppKioskMode);
this.isSystemDialogAvailable_ = !settings.hidePrintWithSystemDialogLink &&
!settings.isInAppKioskMode;
setIsVisible($('system-dialog-link'), this.shouldShowSystemDialogLink_());
},
/**
@ -1080,6 +1103,23 @@ cr.define('print_preview', function() {
}
},
/**
* Returns true if "Print using system dialog" link should be shown for
* current destination.
* @return {boolean} Returns true if link should be shown.
*/
shouldShowSystemDialogLink_: function() {
if (!this.isSystemDialogAvailable_)
return false;
if (!cr.isWindows)
return true;
var selectedDest = this.destinationStore_.selectedDestination;
return selectedDest &&
selectedDest.origin == print_preview.Destination.Origin.LOCAL &&
selectedDest.id !=
print_preview.Destination.GooglePromotedId.SAVE_AS_PDF;
},
/**
* Called when the open-cloud-print-dialog link is clicked. Opens the Google
* Cloud Print web dialog.
@ -1105,6 +1145,9 @@ cr.define('print_preview', function() {
setIsVisible(
$('cloud-print-dialog-link'),
selectedDest && !cr.isChromeOS && !selectedDest.isLocal);
setIsVisible(
$('system-dialog-link'),
this.shouldShowSystemDialogLink_());
if (selectedDest && this.isInKioskAutoPrintMode_) {
this.onPrintButtonClick_();
}

@ -832,8 +832,15 @@ void PrintPreviewHandler::HandlePrint(const base::ListValue* args) {
ReportUserActionHistogram(PRINT_WITH_CLOUD_PRINT);
SendCloudPrintJob(data.get());
} else {
UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToPrinter", page_count);
ReportUserActionHistogram(PRINT_TO_PRINTER);
bool system_dialog = false;
settings->GetBoolean(printing::kSettingShowSystemDialog, &system_dialog);
if (system_dialog) {
UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.SystemDialog", page_count);
ReportUserActionHistogram(FALLBACK_TO_ADVANCED_SETTINGS_DIALOG);
} else {
UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToPrinter", page_count);
ReportUserActionHistogram(PRINT_TO_PRINTER);
}
ReportPrintSettingsStats(*settings);
// This tries to activate the initiator as well, so do not clear the

@ -383,10 +383,11 @@ IPC_SYNC_MESSAGE_ROUTED0_1(PrintHostMsg_GetDefaultPrintSettings,
// The renderer wants to update the current print settings with new
// |job_settings|.
IPC_SYNC_MESSAGE_ROUTED2_1(PrintHostMsg_UpdatePrintSettings,
IPC_SYNC_MESSAGE_ROUTED2_2(PrintHostMsg_UpdatePrintSettings,
int /* document_cookie */,
base::DictionaryValue /* job_settings */,
PrintMsg_PrintPages_Params /* current_settings */)
PrintMsg_PrintPages_Params /* current_settings */,
bool /* canceled */)
// It's the renderer that controls the printing process when it is generated
// by javascript. This step is about showing UI to the user to select the

@ -162,7 +162,10 @@ void ChromeMockRenderThread::OnCheckForCancel(int32 preview_ui_id,
void ChromeMockRenderThread::OnUpdatePrintSettings(
int document_cookie,
const base::DictionaryValue& job_settings,
PrintMsg_PrintPages_Params* params) {
PrintMsg_PrintPages_Params* params,
bool* canceled) {
if (canceled)
*canceled = false;
// Check and make sure the required settings are all there.
// We don't actually care about the values.
std::string dummy_string;

@ -96,7 +96,8 @@ class ChromeMockRenderThread : public content::MockRenderThread {
// For print preview, PrintWebViewHelper will update settings.
void OnUpdatePrintSettings(int document_cookie,
const base::DictionaryValue& job_settings,
PrintMsg_PrintPages_Params* params);
PrintMsg_PrintPages_Params* params,
bool* canceled);
// A mock printer device used for printing tests.
scoped_ptr<MockPrinter> printer_;

@ -1464,8 +1464,13 @@ bool PrintWebViewHelper::UpdatePrintSettings(
int cookie = print_pages_params_ ?
print_pages_params_->params.document_cookie : 0;
PrintMsg_PrintPages_Params settings;
bool canceled = false;
Send(new PrintHostMsg_UpdatePrintSettings(
routing_id(), cookie, *job_settings, &settings));
routing_id(), cookie, *job_settings, &settings, &canceled));
if (canceled) {
notify_browser_of_print_failure_ = false;
return false;
}
if (!job_settings->GetInteger(kPreviewUIID, &settings.params.preview_ui_id)) {
NOTREACHED();

@ -488,4 +488,36 @@ scoped_ptr<DEVMODE, base::FreeDeleter> CreateDevMode(HANDLE printer,
return out.Pass();
}
scoped_ptr<DEVMODE, base::FreeDeleter> PromptDevMode(
HANDLE printer,
const base::string16& printer_name,
DEVMODE* in,
HWND window,
bool* canceled) {
LONG buffer_size =
DocumentProperties(window,
printer,
const_cast<wchar_t*>(printer_name.c_str()),
NULL,
NULL,
0);
if (buffer_size < static_cast<int>(sizeof(DEVMODE)))
return scoped_ptr<DEVMODE, base::FreeDeleter>();
scoped_ptr<DEVMODE, base::FreeDeleter> out(
reinterpret_cast<DEVMODE*>(malloc(buffer_size)));
DWORD flags = (in ? (DM_IN_BUFFER) : 0) | DM_OUT_BUFFER | DM_IN_PROMPT;
LONG result = DocumentProperties(window,
printer,
const_cast<wchar_t*>(printer_name.c_str()),
out.get(),
in,
flags);
if (canceled)
*canceled = (result == IDCANCEL);
if (result != IDOK)
return scoped_ptr<DEVMODE, base::FreeDeleter>();
CHECK_GE(buffer_size, out.get()->dmSize + out.get()->dmDriverExtra);
return out.Pass();
}
} // namespace printing

@ -186,6 +186,14 @@ PRINTING_EXPORT scoped_ptr<DEVMODE, base::FreeDeleter> CreateDevMode(
HANDLE printer,
DEVMODE* in);
// Prompts for new DEVMODE. If |in| is not NULL copy settings from there.
PRINTING_EXPORT scoped_ptr<DEVMODE, base::FreeDeleter> PromptDevMode(
HANDLE printer,
const base::string16& printer_name,
DEVMODE* in,
HWND window,
bool* canceled);
} // namespace printing
#endif // PRINTING_BACKEND_WIN_HELPER_H_

@ -163,6 +163,9 @@ const char kSettingShouldPrintBackgrounds[] = "shouldPrintBackgrounds";
// Whether to print selection only.
const char kSettingShouldPrintSelectionOnly[] = "shouldPrintSelectionOnly";
// Whether to print selection only.
const char kSettingShowSystemDialog[] = "showSystemDialog";
// Indices used to represent first preview page and complete preview document.
const int FIRST_PAGE_INDEX = 0;
const int COMPLETE_PREVIEW_DOCUMENT_INDEX = -1;

@ -63,6 +63,7 @@ PRINTING_EXPORT extern const char kSettingPrinterOptions[];
PRINTING_EXPORT extern const char kSettingTicket[];
PRINTING_EXPORT extern const char kSettingShouldPrintBackgrounds[];
PRINTING_EXPORT extern const char kSettingShouldPrintSelectionOnly[];
PRINTING_EXPORT extern const char kSettingShowSystemDialog[];
PRINTING_EXPORT extern const int FIRST_PAGE_INDEX;
PRINTING_EXPORT extern const int COMPLETE_PREVIEW_DOCUMENT_INDEX;

@ -114,7 +114,11 @@ PrintingContext::Result PrintingContext::UpdatePrintSettings(
return OK;
}
return UpdatePrinterSettings(open_in_external_preview);
bool show_system_dialog = false;
job_settings.GetBoolean(printing::kSettingShowSystemDialog,
&show_system_dialog);
return UpdatePrinterSettings(open_in_external_preview, show_system_dialog);
}
} // namespace printing

@ -72,7 +72,8 @@ class PRINTING_EXPORT PrintingContext {
// Updates printer settings.
// |external_preview| is true if pdf is going to be opened in external
// preview. Used by MacOS only now to open Preview.app.
virtual Result UpdatePrinterSettings(bool external_preview) = 0;
virtual Result UpdatePrinterSettings(bool external_preview,
bool show_system_dialog) = 0;
// Updates Print Settings. |job_settings| contains all print job
// settings information. |ranges| has the new page range settings.

@ -170,7 +170,9 @@ gfx::Size PrintingContextAndroid::GetPdfPaperSizeDeviceUnits() {
}
PrintingContext::Result PrintingContextAndroid::UpdatePrinterSettings(
bool external_preview) {
bool external_preview,
bool show_system_dialog) {
DCHECK(!show_system_dialog);
DCHECK(!in_print_job_);
// Intentional No-op.

@ -37,7 +37,8 @@ class PRINTING_EXPORT PrintingContextAndroid : public PrintingContext {
const PrintSettingsCallback& callback) OVERRIDE;
virtual Result UseDefaultSettings() OVERRIDE;
virtual gfx::Size GetPdfPaperSizeDeviceUnits() OVERRIDE;
virtual Result UpdatePrinterSettings(bool external_preview) OVERRIDE;
virtual Result UpdatePrinterSettings(bool external_preview,
bool show_system_dialog) OVERRIDE;
virtual Result InitWithSettings(const PrintSettings& settings) OVERRIDE;
virtual Result NewDocument(const base::string16& document_name) OVERRIDE;
virtual Result NewPage() OVERRIDE;

@ -106,7 +106,9 @@ gfx::Size PrintingContextLinux::GetPdfPaperSizeDeviceUnits() {
}
PrintingContext::Result PrintingContextLinux::UpdatePrinterSettings(
bool external_preview) {
bool external_preview,
bool show_system_dialog) {
DCHECK(!show_system_dialog);
DCHECK(!in_print_job_);
DCHECK(!external_preview) << "Not implemented";

@ -43,7 +43,8 @@ class PRINTING_EXPORT PrintingContextLinux : public PrintingContext {
const PrintSettingsCallback& callback) OVERRIDE;
virtual gfx::Size GetPdfPaperSizeDeviceUnits() OVERRIDE;
virtual Result UseDefaultSettings() OVERRIDE;
virtual Result UpdatePrinterSettings(bool external_preview) OVERRIDE;
virtual Result UpdatePrinterSettings(bool external_preview,
bool show_system_dialog) OVERRIDE;
virtual Result InitWithSettings(const PrintSettings& settings) OVERRIDE;
virtual Result NewDocument(const base::string16& document_name) OVERRIDE;
virtual Result NewPage() OVERRIDE;

@ -31,7 +31,8 @@ class PRINTING_EXPORT PrintingContextMac : public PrintingContext {
const PrintSettingsCallback& callback) OVERRIDE;
virtual Result UseDefaultSettings() OVERRIDE;
virtual gfx::Size GetPdfPaperSizeDeviceUnits() OVERRIDE;
virtual Result UpdatePrinterSettings(bool external_preview) OVERRIDE;
virtual Result UpdatePrinterSettings(bool external_preview,
bool show_system_dialog) OVERRIDE;
virtual Result InitWithSettings(const PrintSettings& settings) OVERRIDE;
virtual Result NewDocument(const base::string16& document_name) OVERRIDE;
virtual Result NewPage() OVERRIDE;

@ -168,7 +168,9 @@ PrintingContext::Result PrintingContextMac::UseDefaultSettings() {
}
PrintingContext::Result PrintingContextMac::UpdatePrinterSettings(
bool external_preview) {
bool external_preview,
bool show_system_dialog) {
DCHECK(!show_system_dialog);
DCHECK(!in_print_job_);
// NOTE: Reset |print_info_| with a copy of |sharedPrintInfo| so as to start

@ -75,7 +75,9 @@ gfx::Size PrintingContextNoSystemDialog::GetPdfPaperSizeDeviceUnits() {
}
PrintingContext::Result PrintingContextNoSystemDialog::UpdatePrinterSettings(
bool external_preview) {
bool external_preview,
bool show_system_dialog) {
DCHECK(!show_system_dialog);
if (settings_.dpi() == 0)
UseDefaultSettings();

@ -27,7 +27,8 @@ class PRINTING_EXPORT PrintingContextNoSystemDialog : public PrintingContext {
const PrintSettingsCallback& callback) OVERRIDE;
virtual Result UseDefaultSettings() OVERRIDE;
virtual gfx::Size GetPdfPaperSizeDeviceUnits() OVERRIDE;
virtual Result UpdatePrinterSettings(bool external_preview) OVERRIDE;
virtual Result UpdatePrinterSettings(bool external_preview,
bool show_system_dialog) OVERRIDE;
virtual Result InitWithSettings(const PrintSettings& settings) OVERRIDE;
virtual Result NewDocument(const base::string16& document_name) OVERRIDE;
virtual Result NewPage() OVERRIDE;

@ -50,7 +50,7 @@ scoped_ptr<PrintingContext> PrintingContext::Create(Delegate* delegate) {
}
PrintingContextWin::PrintingContextWin(Delegate* delegate)
: PrintingContext(delegate), context_(NULL), dialog_box_(NULL) {
: PrintingContext(delegate), context_(NULL) {
}
PrintingContextWin::~PrintingContextWin() {
@ -61,63 +61,26 @@ void PrintingContextWin::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));
NOTIMPLEMENTED();
}
PrintingContext::Result PrintingContextWin::UseDefaultSettings() {
DCHECK(!in_print_job_);
PRINTDLG dialog_options = { sizeof(PRINTDLG) };
dialog_options.Flags = PD_RETURNDC | PD_RETURNDEFAULT;
if (PrintDlg(&dialog_options))
return ParseDialogResult(dialog_options);
scoped_refptr<PrintBackend> backend = PrintBackend::CreateInstance(NULL);
base::string16 default_printer =
base::UTF8ToWide(backend->GetDefaultPrinterName());
if (!default_printer.empty()) {
ScopedPrinterHandle printer;
if (printer.OpenPrinter(default_printer.c_str())) {
scoped_ptr<DEVMODE, base::FreeDeleter> dev_mode =
CreateDevMode(printer, NULL);
if (InitializeSettings(default_printer, dev_mode.get()) == OK)
return OK;
}
}
ReleaseContext();
// No default printer configured, do we have any printers at all?
DWORD bytes_needed = 0;
@ -142,23 +105,15 @@ PrintingContext::Result PrintingContextWin::UseDefaultSettings() {
continue;
scoped_ptr<DEVMODE, base::FreeDeleter> dev_mode =
CreateDevMode(printer, NULL);
if (!dev_mode || !AllocateContext(info_2->pPrinterName, dev_mode.get(),
&context_)) {
continue;
}
if (InitializeSettings(*dev_mode.get(), info_2->pPrinterName, NULL, 0,
false)) {
if (InitializeSettings(info_2->pPrinterName, dev_mode.get()) == OK)
return OK;
}
ReleaseContext();
}
if (context_)
return OK;
}
}
ResetSettings();
return FAILED;
return OnError();
}
gfx::Size PrintingContextWin::GetPdfPaperSizeDeviceUnits() {
@ -192,7 +147,8 @@ gfx::Size PrintingContextWin::GetPdfPaperSizeDeviceUnits() {
}
PrintingContext::Result PrintingContextWin::UpdatePrinterSettings(
bool external_preview) {
bool external_preview,
bool show_system_dialog) {
DCHECK(!in_print_job_);
DCHECK(!external_preview) << "Not implemented";
@ -256,19 +212,14 @@ PrintingContext::Result PrintingContextWin::UpdatePrinterSettings(
}
// Update data using DocumentProperties.
scoped_dev_mode = CreateDevMode(printer, scoped_dev_mode.get());
if (!scoped_dev_mode)
return OnError();
// Set printer then refresh printer settings.
if (!AllocateContext(settings_.device_name(), scoped_dev_mode.get(),
&context_)) {
return OnError();
if (show_system_dialog) {
scoped_dev_mode = ShowPrintDialog(
printer, delegate_->GetParentView(), scoped_dev_mode.get());
} else {
scoped_dev_mode = CreateDevMode(printer, scoped_dev_mode.get());
}
PrintSettingsInitializerWin::InitPrintSettings(context_,
*scoped_dev_mode.get(),
&settings_);
return OK;
// Set printer then refresh printer settings.
return InitializeSettings(settings_.device_name(), scoped_dev_mode.get());
}
PrintingContext::Result PrintingContextWin::InitWithSettings(
@ -279,18 +230,13 @@ PrintingContext::Result PrintingContextWin::InitWithSettings(
// TODO(maruel): settings_.ToDEVMODE()
ScopedPrinterHandle printer;
if (!printer.OpenPrinter(settings_.device_name().c_str())) {
if (!printer.OpenPrinter(settings_.device_name().c_str()))
return FAILED;
}
Result status = OK;
scoped_ptr<DEVMODE, base::FreeDeleter> dev_mode =
CreateDevMode(printer, NULL);
if (!GetPrinterSettings(printer, settings_.device_name()))
status = FAILED;
if (status != OK)
ResetSettings();
return status;
return InitializeSettings(settings_.device_name(), dev_mode.get());
}
PrintingContext::Result PrintingContextWin::NewDocument(
@ -374,10 +320,6 @@ void PrintingContextWin::Cancel() {
in_print_job_ = false;
if (context_)
CancelDC(context_);
if (dialog_box_) {
DestroyWindow(dialog_box_);
dialog_box_dismissed_ = true;
}
}
void PrintingContextWin::ReleaseContext() {
@ -401,154 +343,31 @@ BOOL PrintingContextWin::AbortProc(HDC hdc, int nCode) {
return true;
}
bool PrintingContextWin::InitializeSettings(const DEVMODE& dev_mode,
const std::wstring& new_device_name,
const PRINTPAGERANGE* ranges,
int number_ranges,
bool selection_only) {
PrintingContext::Result PrintingContextWin::InitializeSettings(
const std::wstring& device_name,
DEVMODE* dev_mode) {
if (!dev_mode)
return OnError();
ReleaseContext();
context_ = CreateDC(L"WINSPOOL", device_name.c_str(), NULL, dev_mode);
if (!context_)
return OnError();
skia::InitializeDC(context_);
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_device_name(device_name);
PrintSettingsInitializerWin::InitPrintSettings(
context_, *dev_mode, &settings_);
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;
return OK;
}
bool PrintingContextWin::GetPrinterSettings(HANDLE printer,
const std::wstring& device_name) {
DCHECK(!in_print_job_);
scoped_ptr<DEVMODE, base::FreeDeleter> dev_mode =
CreateDevMode(printer, NULL);
if (!dev_mode || !AllocateContext(device_name, dev_mode.get(), &context_)) {
ResetSettings();
return false;
}
return InitializeSettings(*dev_mode.get(), device_name, NULL, 0, false);
}
// static
bool PrintingContextWin::AllocateContext(const std::wstring& device_name,
const DEVMODE* dev_mode,
gfx::NativeDrawingContext* context) {
*context = CreateDC(L"WINSPOOL", device_name.c_str(), NULL, dev_mode);
DCHECK(*context);
return *context != NULL;
}
PrintingContext::Result PrintingContextWin::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()) {
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);
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;
}
}
HRESULT PrintingContextWin::ShowPrintDialog(PRINTDLGEX* options) {
scoped_ptr<DEVMODE, base::FreeDeleter> PrintingContextWin::ShowPrintDialog(
HANDLE printer,
gfx::NativeView parent_view,
DEVMODE* dev_mode) {
// 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
@ -558,57 +377,20 @@ HRESULT PrintingContextWin::ShowPrintDialog(PRINTDLGEX* options) {
base::MessageLoop::ScopedNestableTaskAllower allow(
base::MessageLoop::current());
return PrintDlgEx(options);
}
bool canceled = false;
scoped_ptr<DEVMODE, base::FreeDeleter> result =
PromptDevMode(printer,
settings_.device_name(),
dev_mode,
GetRootWindow(parent_view),
&canceled);
PrintingContext::Result PrintingContextWin::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);
if (canceled) {
result.reset();
abort_printing_ = true;
}
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()) {
context_ = dialog_options.hDC;
success = InitializeSettings(*dev_mode, device_name, NULL, 0, false);
}
if (!success && dialog_options.hDC) {
DeleteDC(dialog_options.hDC);
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;
return result.Pass();
}
} // namespace printing

@ -29,7 +29,8 @@ class PRINTING_EXPORT PrintingContextWin : public PrintingContext {
const PrintSettingsCallback& callback) OVERRIDE;
virtual Result UseDefaultSettings() OVERRIDE;
virtual gfx::Size GetPdfPaperSizeDeviceUnits() OVERRIDE;
virtual Result UpdatePrinterSettings(bool external_preview) OVERRIDE;
virtual Result UpdatePrinterSettings(bool external_preview,
bool show_system_dialog) OVERRIDE;
virtual Result InitWithSettings(const PrintSettings& settings) OVERRIDE;
virtual Result NewDocument(const base::string16& document_name) OVERRIDE;
virtual Result NewPage() OVERRIDE;
@ -39,45 +40,24 @@ class PRINTING_EXPORT PrintingContextWin : public PrintingContext {
virtual void ReleaseContext() OVERRIDE;
virtual gfx::NativeDrawingContext context() const OVERRIDE;
// Allocates the HDC for a specific DEVMODE.
static bool AllocateContext(const std::wstring& printer_name,
const DEVMODE* dev_mode,
gfx::NativeDrawingContext* context);
protected:
virtual HRESULT ShowPrintDialog(PRINTDLGEX* options);
virtual scoped_ptr<DEVMODE, base::FreeDeleter> ShowPrintDialog(
HANDLE printer,
gfx::NativeView parent_view,
DEVMODE* dev_mode);
private:
// Class that manages the PrintDlgEx() callbacks. This is meant to be a
// temporary object used during the Print... dialog display.
class CallbackHandler;
// 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.
bool InitializeSettings(const DEVMODE& dev_mode,
const std::wstring& new_device_name,
const PRINTPAGERANGE* ranges,
int number_ranges,
bool selection_only);
// Retrieves the printer's default low-level settings. On Windows, context_ is
// allocated with this call.
bool GetPrinterSettings(HANDLE printer,
const std::wstring& device_name);
// Parses the result of a PRINTDLGEX result.
Result ParseDialogResultEx(const PRINTDLGEX& dialog_options);
Result ParseDialogResult(const PRINTDLG& dialog_options);
virtual Result InitializeSettings(const base::string16& device_name,
DEVMODE* dev_mode);
// The selected printer context.
HDC context_;
// The dialog box for the time it is shown.
volatile HWND dialog_box_;
DISALLOW_COPY_AND_ASSIGN(PrintingContextWin);
};

@ -2,20 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <ocidl.h>
#include <commdlg.h>
#include <string>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "printing/backend/printing_info_win.h"
#include "printing/backend/win_helper.h"
#include "printing/printing_test.h"
#include "printing/printing_context.h"
#include "printing/printing_context_win.h"
#include "printing/printing_test.h"
#include "printing/print_settings.h"
#include "testing/gtest/include/gtest/gtest.h"
@ -40,123 +29,6 @@ class PrintingContextTest : public PrintingTest<testing::Test>,
PrintingContext::Result result_;
};
class MockPrintingContextWin : public PrintingContextWin {
public:
MockPrintingContextWin(Delegate* delegate) : PrintingContextWin(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;
}
if (!PrintingContextWin::AllocateContext(
printer_name, dev_mode, &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, Base) {
if (IsTestCaseDisabled())
return;
@ -174,20 +46,4 @@ TEST_F(PrintingContextTest, Base) {
EXPECT_TRUE(ModifyWorldTransform(context->context(), NULL, MWT_IDENTITY));
}
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);
}
} // namespace printing