1 // Copyright (c) 2012 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.
5 #include "chrome/browser/printing/printing_message_filter.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/printing/printer_query.h"
12 #include "chrome/browser/printing/print_job_manager.h"
13 #include "chrome/browser/printing/printing_ui_web_contents_observer.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/profiles/profile_io_data.h"
16 #include "chrome/common/print_messages.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/render_view_host.h"
19 #include "content/public/browser/web_contents.h"
21 #if defined(ENABLE_FULL_PRINTING)
22 #include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
25 #if defined(OS_CHROMEOS)
30 #include "base/file_util.h"
31 #include "base/lazy_instance.h"
32 #include "chrome/browser/printing/print_dialog_cloud.h"
33 #include "content/public/browser/web_contents_view.h"
36 #if defined(OS_ANDROID)
37 #include "base/strings/string_number_conversions.h"
38 #include "chrome/browser/printing/print_view_manager_basic.h"
39 #include "printing/printing_context_android.h"
42 using content::BrowserThread;
46 #if defined(OS_CHROMEOS)
47 typedef std::map<int, base::FilePath> SequenceToPathMap;
49 struct PrintingSequencePathMap {
50 SequenceToPathMap map;
54 // No locking, only access on the FILE thread.
55 static base::LazyInstance<PrintingSequencePathMap>
56 g_printing_file_descriptor_map = LAZY_INSTANCE_INITIALIZER;
59 void RenderParamsFromPrintSettings(const printing::PrintSettings& settings,
60 PrintMsg_Print_Params* params) {
61 params->page_size = settings.page_setup_device_units().physical_size();
62 params->content_size.SetSize(
63 settings.page_setup_device_units().content_area().width(),
64 settings.page_setup_device_units().content_area().height());
65 params->printable_area.SetRect(
66 settings.page_setup_device_units().printable_area().x(),
67 settings.page_setup_device_units().printable_area().y(),
68 settings.page_setup_device_units().printable_area().width(),
69 settings.page_setup_device_units().printable_area().height());
70 params->margin_top = settings.page_setup_device_units().content_area().y();
71 params->margin_left = settings.page_setup_device_units().content_area().x();
72 params->dpi = settings.dpi();
73 // Currently hardcoded at 1.25. See PrintSettings' constructor.
74 params->min_shrink = settings.min_shrink();
75 // Currently hardcoded at 2.0. See PrintSettings' constructor.
76 params->max_shrink = settings.max_shrink();
77 // Currently hardcoded at 72dpi. See PrintSettings' constructor.
78 params->desired_dpi = settings.desired_dpi();
79 // Always use an invalid cookie.
80 params->document_cookie = 0;
81 params->selection_only = settings.selection_only();
82 params->supports_alpha_blend = settings.supports_alpha_blend();
83 params->should_print_backgrounds = settings.should_print_backgrounds();
84 params->display_header_footer = settings.display_header_footer();
85 params->title = settings.title();
86 params->url = settings.url();
91 PrintingMessageFilter::PrintingMessageFilter(int render_process_id,
93 : profile_io_data_(ProfileIOData::FromResourceContext(
94 profile->GetResourceContext())),
95 render_process_id_(render_process_id),
96 queue_(g_browser_process->print_job_manager()->queue()) {
100 PrintingMessageFilter::~PrintingMessageFilter() {
103 void PrintingMessageFilter::OverrideThreadForMessage(
104 const IPC::Message& message, BrowserThread::ID* thread) {
105 #if defined(OS_CHROMEOS)
106 if (message.type() == PrintHostMsg_AllocateTempFileForPrinting::ID ||
107 message.type() == PrintHostMsg_TempFileForPrintingWritten::ID) {
108 *thread = BrowserThread::FILE;
110 #elif defined(OS_ANDROID)
111 if (message.type() == PrintHostMsg_AllocateTempFileForPrinting::ID ||
112 message.type() == PrintHostMsg_TempFileForPrintingWritten::ID) {
113 *thread = BrowserThread::UI;
118 bool PrintingMessageFilter::OnMessageReceived(const IPC::Message& message,
119 bool* message_was_ok) {
121 IPC_BEGIN_MESSAGE_MAP_EX(PrintingMessageFilter, message, *message_was_ok)
123 IPC_MESSAGE_HANDLER(PrintHostMsg_DuplicateSection, OnDuplicateSection)
125 #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
126 IPC_MESSAGE_HANDLER(PrintHostMsg_AllocateTempFileForPrinting,
127 OnAllocateTempFileForPrinting)
128 IPC_MESSAGE_HANDLER(PrintHostMsg_TempFileForPrintingWritten,
129 OnTempFileForPrintingWritten)
131 IPC_MESSAGE_HANDLER(PrintHostMsg_IsPrintingEnabled, OnIsPrintingEnabled)
132 IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_GetDefaultPrintSettings,
133 OnGetDefaultPrintSettings)
134 IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_ScriptedPrint, OnScriptedPrint)
135 IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_UpdatePrintSettings,
136 OnUpdatePrintSettings)
137 #if defined(ENABLE_FULL_PRINTING)
138 IPC_MESSAGE_HANDLER(PrintHostMsg_CheckForCancel, OnCheckForCancel)
140 IPC_MESSAGE_UNHANDLED(handled = false)
141 IPC_END_MESSAGE_MAP()
146 void PrintingMessageFilter::OnDuplicateSection(
147 base::SharedMemoryHandle renderer_handle,
148 base::SharedMemoryHandle* browser_handle) {
149 // Duplicate the handle in this process right now so the memory is kept alive
150 // (even if it is not mapped)
151 base::SharedMemory shared_buf(renderer_handle, true, PeerHandle());
152 shared_buf.GiveToProcess(base::GetCurrentProcessHandle(), browser_handle);
156 #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
157 void PrintingMessageFilter::OnAllocateTempFileForPrinting(
159 base::FileDescriptor* temp_file_fd,
160 int* sequence_number) {
161 #if defined(OS_CHROMEOS)
162 // TODO(thestig): Use |render_view_id| for Chrome OS.
163 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
164 temp_file_fd->fd = *sequence_number = -1;
165 temp_file_fd->auto_close = false;
167 SequenceToPathMap* map = &g_printing_file_descriptor_map.Get().map;
168 *sequence_number = g_printing_file_descriptor_map.Get().sequence++;
171 if (base::CreateTemporaryFile(&path)) {
172 int fd = open(path.value().c_str(), O_WRONLY);
174 SequenceToPathMap::iterator it = map->find(*sequence_number);
175 if (it != map->end()) {
176 NOTREACHED() << "Sequence number already in use. seq=" <<
179 (*map)[*sequence_number] = path;
180 temp_file_fd->fd = fd;
181 temp_file_fd->auto_close = true;
185 #elif defined(OS_ANDROID)
186 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
187 content::WebContents* wc = GetWebContentsForRenderView(render_view_id);
190 printing::PrintViewManagerBasic* print_view_manager =
191 printing::PrintViewManagerBasic::FromWebContents(wc);
192 // The file descriptor is originally created in & passed from the Android
193 // side, and it will handle the closing.
194 const base::FileDescriptor& file_descriptor =
195 print_view_manager->file_descriptor();
196 temp_file_fd->fd = file_descriptor.fd;
197 temp_file_fd->auto_close = false;
201 void PrintingMessageFilter::OnTempFileForPrintingWritten(int render_view_id,
202 int sequence_number) {
203 #if defined(OS_CHROMEOS)
204 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
205 SequenceToPathMap* map = &g_printing_file_descriptor_map.Get().map;
206 SequenceToPathMap::iterator it = map->find(sequence_number);
207 if (it == map->end()) {
208 NOTREACHED() << "Got a sequence that we didn't pass to the "
209 "renderer: " << sequence_number;
212 BrowserThread::PostTask(
213 BrowserThread::UI, FROM_HERE,
214 base::Bind(&PrintingMessageFilter::CreatePrintDialogForFile,
215 this, render_view_id, it->second));
217 // Erase the entry in the map.
219 #elif defined(OS_ANDROID)
220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
221 content::WebContents* wc = GetWebContentsForRenderView(render_view_id);
224 printing::PrintViewManagerBasic* print_view_manager =
225 printing::PrintViewManagerBasic::FromWebContents(wc);
226 const base::FileDescriptor& file_descriptor =
227 print_view_manager->file_descriptor();
228 printing::PrintingContextAndroid::PdfWritingDone(file_descriptor.fd, true);
229 // Invalidate the file descriptor so it doesn't accidentally get reused.
230 print_view_manager->set_file_descriptor(base::FileDescriptor(-1, false));
233 #endif // defined(OS_CHROMEOS) || defined(OS_ANDROID)
235 #if defined(OS_CHROMEOS)
236 void PrintingMessageFilter::CreatePrintDialogForFile(
238 const base::FilePath& path) {
239 content::WebContents* wc = GetWebContentsForRenderView(render_view_id);
242 print_dialog_cloud::CreatePrintDialogForFile(
243 wc->GetBrowserContext(),
244 wc->GetView()->GetTopLevelNativeWindow(),
248 std::string("application/pdf"),
251 #endif // defined(OS_CHROMEOS)
253 content::WebContents* PrintingMessageFilter::GetWebContentsForRenderView(
254 int render_view_id) {
255 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
256 content::RenderViewHost* view = content::RenderViewHost::FromID(
257 render_process_id_, render_view_id);
258 return view ? content::WebContents::FromRenderViewHost(view) : NULL;
261 struct PrintingMessageFilter::GetPrintSettingsForRenderViewParams {
262 printing::PrinterQuery::GetSettingsAskParam ask_user_for_settings;
263 int expected_page_count;
265 printing::MarginType margin_type;
268 void PrintingMessageFilter::GetPrintSettingsForRenderView(
270 GetPrintSettingsForRenderViewParams params,
271 const base::Closure& callback,
272 scoped_refptr<printing::PrinterQuery> printer_query) {
273 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
274 content::WebContents* wc = GetWebContentsForRenderView(render_view_id);
276 scoped_ptr<PrintingUIWebContentsObserver> wc_observer(
277 new PrintingUIWebContentsObserver(wc));
278 BrowserThread::PostTask(
279 BrowserThread::IO, FROM_HERE,
280 base::Bind(&printing::PrinterQuery::GetSettings, printer_query,
281 params.ask_user_for_settings, base::Passed(&wc_observer),
282 params.expected_page_count, params.has_selection,
283 params.margin_type, callback));
285 BrowserThread::PostTask(
286 BrowserThread::IO, FROM_HERE,
287 base::Bind(&PrintingMessageFilter::OnGetPrintSettingsFailed, this,
288 callback, printer_query));
292 void PrintingMessageFilter::OnGetPrintSettingsFailed(
293 const base::Closure& callback,
294 scoped_refptr<printing::PrinterQuery> printer_query) {
295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
296 printer_query->GetSettingsDone(printing::PrintSettings(),
297 printing::PrintingContext::FAILED);
301 void PrintingMessageFilter::OnIsPrintingEnabled(bool* is_enabled) {
302 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
303 *is_enabled = profile_io_data_->printing_enabled()->GetValue();
306 void PrintingMessageFilter::OnGetDefaultPrintSettings(IPC::Message* reply_msg) {
307 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
308 scoped_refptr<printing::PrinterQuery> printer_query;
309 if (!profile_io_data_->printing_enabled()->GetValue()) {
310 // Reply with NULL query.
311 OnGetDefaultPrintSettingsReply(printer_query, reply_msg);
314 printer_query = queue_->PopPrinterQuery(0);
316 printer_query = queue_->CreatePrinterQuery();
318 // Loads default settings. This is asynchronous, only the IPC message sender
319 // will hang until the settings are retrieved.
320 GetPrintSettingsForRenderViewParams params;
321 params.ask_user_for_settings = printing::PrinterQuery::DEFAULTS;
322 params.expected_page_count = 0;
323 params.has_selection = false;
324 params.margin_type = printing::DEFAULT_MARGINS;
325 BrowserThread::PostTask(
326 BrowserThread::UI, FROM_HERE,
327 base::Bind(&PrintingMessageFilter::GetPrintSettingsForRenderView, this,
328 reply_msg->routing_id(), params,
329 base::Bind(&PrintingMessageFilter::OnGetDefaultPrintSettingsReply,
330 this, printer_query, reply_msg),
334 void PrintingMessageFilter::OnGetDefaultPrintSettingsReply(
335 scoped_refptr<printing::PrinterQuery> printer_query,
336 IPC::Message* reply_msg) {
337 PrintMsg_Print_Params params;
338 if (!printer_query.get() ||
339 printer_query->last_status() != printing::PrintingContext::OK) {
342 RenderParamsFromPrintSettings(printer_query->settings(), ¶ms);
343 params.document_cookie = printer_query->cookie();
345 PrintHostMsg_GetDefaultPrintSettings::WriteReplyParams(reply_msg, params);
347 // If printing was enabled.
348 if (printer_query.get()) {
349 // If user hasn't cancelled.
350 if (printer_query->cookie() && printer_query->settings().dpi()) {
351 queue_->QueuePrinterQuery(printer_query.get());
353 printer_query->StopWorker();
358 void PrintingMessageFilter::OnScriptedPrint(
359 const PrintHostMsg_ScriptedPrint_Params& params,
360 IPC::Message* reply_msg) {
361 scoped_refptr<printing::PrinterQuery> printer_query =
362 queue_->PopPrinterQuery(params.cookie);
364 printer_query = queue_->CreatePrinterQuery();
365 GetPrintSettingsForRenderViewParams settings_params;
366 settings_params.ask_user_for_settings = printing::PrinterQuery::ASK_USER;
367 settings_params.expected_page_count = params.expected_pages_count;
368 settings_params.has_selection = params.has_selection;
369 settings_params.margin_type = params.margin_type;
371 BrowserThread::PostTask(
372 BrowserThread::UI, FROM_HERE,
373 base::Bind(&PrintingMessageFilter::GetPrintSettingsForRenderView, this,
374 reply_msg->routing_id(), settings_params,
375 base::Bind(&PrintingMessageFilter::OnScriptedPrintReply, this,
376 printer_query, reply_msg),
380 void PrintingMessageFilter::OnScriptedPrintReply(
381 scoped_refptr<printing::PrinterQuery> printer_query,
382 IPC::Message* reply_msg) {
383 PrintMsg_PrintPages_Params params;
384 #if defined(OS_ANDROID)
385 // We need to save the routing ID here because Send method below deletes the
386 // |reply_msg| before we can get the routing ID for the Android code.
387 int routing_id = reply_msg->routing_id();
389 if (printer_query->last_status() != printing::PrintingContext::OK ||
390 !printer_query->settings().dpi()) {
393 RenderParamsFromPrintSettings(printer_query->settings(), ¶ms.params);
394 params.params.document_cookie = printer_query->cookie();
396 printing::PageRange::GetPages(printer_query->settings().ranges());
398 PrintHostMsg_ScriptedPrint::WriteReplyParams(reply_msg, params);
400 if (params.params.dpi && params.params.document_cookie) {
401 #if defined(OS_ANDROID)
403 const base::string16& device_name = printer_query->settings().device_name();
404 if (base::StringToInt(device_name, &file_descriptor)) {
405 BrowserThread::PostTask(
406 BrowserThread::UI, FROM_HERE,
407 base::Bind(&PrintingMessageFilter::UpdateFileDescriptor, this,
408 routing_id, file_descriptor));
411 queue_->QueuePrinterQuery(printer_query.get());
413 printer_query->StopWorker();
417 #if defined(OS_ANDROID)
418 void PrintingMessageFilter::UpdateFileDescriptor(int render_view_id, int fd) {
419 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
420 content::WebContents* wc = GetWebContentsForRenderView(render_view_id);
423 printing::PrintViewManagerBasic* print_view_manager =
424 printing::PrintViewManagerBasic::FromWebContents(wc);
425 print_view_manager->set_file_descriptor(base::FileDescriptor(fd, false));
429 void PrintingMessageFilter::OnUpdatePrintSettings(
430 int document_cookie, const base::DictionaryValue& job_settings,
431 IPC::Message* reply_msg) {
432 scoped_refptr<printing::PrinterQuery> printer_query;
433 if (!profile_io_data_->printing_enabled()->GetValue()) {
434 // Reply with NULL query.
435 OnUpdatePrintSettingsReply(printer_query, reply_msg);
438 printer_query = queue_->PopPrinterQuery(document_cookie);
440 printer_query = queue_->CreatePrinterQuery();
441 printer_query->SetSettings(
443 base::Bind(&PrintingMessageFilter::OnUpdatePrintSettingsReply, this,
444 printer_query, reply_msg));
447 void PrintingMessageFilter::OnUpdatePrintSettingsReply(
448 scoped_refptr<printing::PrinterQuery> printer_query,
449 IPC::Message* reply_msg) {
450 PrintMsg_PrintPages_Params params;
451 if (!printer_query.get() ||
452 printer_query->last_status() != printing::PrintingContext::OK) {
455 RenderParamsFromPrintSettings(printer_query->settings(), ¶ms.params);
456 params.params.document_cookie = printer_query->cookie();
458 printing::PageRange::GetPages(printer_query->settings().ranges());
460 PrintHostMsg_UpdatePrintSettings::WriteReplyParams(reply_msg, params);
462 // If user hasn't cancelled.
463 if (printer_query.get()) {
464 if (printer_query->cookie() && printer_query->settings().dpi()) {
465 queue_->QueuePrinterQuery(printer_query.get());
467 printer_query->StopWorker();
472 #if defined(ENABLE_FULL_PRINTING)
473 void PrintingMessageFilter::OnCheckForCancel(int32 preview_ui_id,
474 int preview_request_id,
476 PrintPreviewUI::GetCurrentPrintPreviewStatus(preview_ui_id,