• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/printing/printing_message_filter.h"
6 
7 #include <string>
8 
9 #include "base/bind.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"
20 
21 #if defined(ENABLE_FULL_PRINTING)
22 #include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
23 #endif
24 
25 #if defined(OS_CHROMEOS)
26 #include <fcntl.h>
27 
28 #include <map>
29 
30 #include "base/file_util.h"
31 #include "base/lazy_instance.h"
32 #include "chrome/browser/printing/print_dialog_cloud.h"
33 #endif
34 
35 #if defined(OS_ANDROID)
36 #include "base/strings/string_number_conversions.h"
37 #include "chrome/browser/printing/print_view_manager_basic.h"
38 #include "printing/printing_context_android.h"
39 #endif
40 
41 using content::BrowserThread;
42 
43 namespace {
44 
45 #if defined(OS_CHROMEOS)
46 typedef std::map<int, base::FilePath> SequenceToPathMap;
47 
48 struct PrintingSequencePathMap {
49   SequenceToPathMap map;
50   int sequence;
51 };
52 
53 // No locking, only access on the FILE thread.
54 static base::LazyInstance<PrintingSequencePathMap>
55     g_printing_file_descriptor_map = LAZY_INSTANCE_INITIALIZER;
56 #endif
57 
RenderParamsFromPrintSettings(const printing::PrintSettings & settings,PrintMsg_Print_Params * params)58 void RenderParamsFromPrintSettings(const printing::PrintSettings& settings,
59                                    PrintMsg_Print_Params* params) {
60   params->page_size = settings.page_setup_device_units().physical_size();
61   params->content_size.SetSize(
62       settings.page_setup_device_units().content_area().width(),
63       settings.page_setup_device_units().content_area().height());
64   params->printable_area.SetRect(
65       settings.page_setup_device_units().printable_area().x(),
66       settings.page_setup_device_units().printable_area().y(),
67       settings.page_setup_device_units().printable_area().width(),
68       settings.page_setup_device_units().printable_area().height());
69   params->margin_top = settings.page_setup_device_units().content_area().y();
70   params->margin_left = settings.page_setup_device_units().content_area().x();
71   params->dpi = settings.dpi();
72   // Currently hardcoded at 1.25. See PrintSettings' constructor.
73   params->min_shrink = settings.min_shrink();
74   // Currently hardcoded at 2.0. See PrintSettings' constructor.
75   params->max_shrink = settings.max_shrink();
76   // Currently hardcoded at 72dpi. See PrintSettings' constructor.
77   params->desired_dpi = settings.desired_dpi();
78   // Always use an invalid cookie.
79   params->document_cookie = 0;
80   params->selection_only = settings.selection_only();
81   params->supports_alpha_blend = settings.supports_alpha_blend();
82   params->should_print_backgrounds = settings.should_print_backgrounds();
83   params->display_header_footer = settings.display_header_footer();
84   params->title = settings.title();
85   params->url = settings.url();
86 }
87 
88 }  // namespace
89 
PrintingMessageFilter(int render_process_id,Profile * profile)90 PrintingMessageFilter::PrintingMessageFilter(int render_process_id,
91                                              Profile* profile)
92     : BrowserMessageFilter(PrintMsgStart),
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()) {
97   DCHECK(queue_);
98 }
99 
~PrintingMessageFilter()100 PrintingMessageFilter::~PrintingMessageFilter() {
101 }
102 
OverrideThreadForMessage(const IPC::Message & message,BrowserThread::ID * thread)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;
109   }
110 #elif defined(OS_ANDROID)
111   if (message.type() == PrintHostMsg_AllocateTempFileForPrinting::ID ||
112       message.type() == PrintHostMsg_TempFileForPrintingWritten::ID) {
113     *thread = BrowserThread::UI;
114   }
115 #endif
116 }
117 
OnMessageReceived(const IPC::Message & message)118 bool PrintingMessageFilter::OnMessageReceived(const IPC::Message& message) {
119   bool handled = true;
120   IPC_BEGIN_MESSAGE_MAP(PrintingMessageFilter, message)
121 #if defined(OS_WIN)
122     IPC_MESSAGE_HANDLER(PrintHostMsg_DuplicateSection, OnDuplicateSection)
123 #endif
124 #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
125     IPC_MESSAGE_HANDLER(PrintHostMsg_AllocateTempFileForPrinting,
126                         OnAllocateTempFileForPrinting)
127     IPC_MESSAGE_HANDLER(PrintHostMsg_TempFileForPrintingWritten,
128                         OnTempFileForPrintingWritten)
129 #endif
130     IPC_MESSAGE_HANDLER(PrintHostMsg_IsPrintingEnabled, OnIsPrintingEnabled)
131     IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_GetDefaultPrintSettings,
132                                     OnGetDefaultPrintSettings)
133     IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_ScriptedPrint, OnScriptedPrint)
134     IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_UpdatePrintSettings,
135                                     OnUpdatePrintSettings)
136 #if defined(ENABLE_FULL_PRINTING)
137     IPC_MESSAGE_HANDLER(PrintHostMsg_CheckForCancel, OnCheckForCancel)
138 #endif
139     IPC_MESSAGE_UNHANDLED(handled = false)
140   IPC_END_MESSAGE_MAP()
141   return handled;
142 }
143 
144 #if defined(OS_WIN)
OnDuplicateSection(base::SharedMemoryHandle renderer_handle,base::SharedMemoryHandle * browser_handle)145 void PrintingMessageFilter::OnDuplicateSection(
146     base::SharedMemoryHandle renderer_handle,
147     base::SharedMemoryHandle* browser_handle) {
148   // Duplicate the handle in this process right now so the memory is kept alive
149   // (even if it is not mapped)
150   base::SharedMemory shared_buf(renderer_handle, true, PeerHandle());
151   shared_buf.GiveToProcess(base::GetCurrentProcessHandle(), browser_handle);
152 }
153 #endif
154 
155 #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
OnAllocateTempFileForPrinting(int render_view_id,base::FileDescriptor * temp_file_fd,int * sequence_number)156 void PrintingMessageFilter::OnAllocateTempFileForPrinting(
157     int render_view_id,
158     base::FileDescriptor* temp_file_fd,
159     int* sequence_number) {
160 #if defined(OS_CHROMEOS)
161   // TODO(thestig): Use |render_view_id| for Chrome OS.
162   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
163   temp_file_fd->fd = *sequence_number = -1;
164   temp_file_fd->auto_close = false;
165 
166   SequenceToPathMap* map = &g_printing_file_descriptor_map.Get().map;
167   *sequence_number = g_printing_file_descriptor_map.Get().sequence++;
168 
169   base::FilePath path;
170   if (base::CreateTemporaryFile(&path)) {
171     int fd = open(path.value().c_str(), O_WRONLY);
172     if (fd >= 0) {
173       SequenceToPathMap::iterator it = map->find(*sequence_number);
174       if (it != map->end()) {
175         NOTREACHED() << "Sequence number already in use. seq=" <<
176             *sequence_number;
177       } else {
178         (*map)[*sequence_number] = path;
179         temp_file_fd->fd = fd;
180         temp_file_fd->auto_close = true;
181       }
182     }
183   }
184 #elif defined(OS_ANDROID)
185   DCHECK_CURRENTLY_ON(BrowserThread::UI);
186   content::WebContents* wc = GetWebContentsForRenderView(render_view_id);
187   if (!wc)
188     return;
189   printing::PrintViewManagerBasic* print_view_manager =
190       printing::PrintViewManagerBasic::FromWebContents(wc);
191   // The file descriptor is originally created in & passed from the Android
192   // side, and it will handle the closing.
193   const base::FileDescriptor& file_descriptor =
194       print_view_manager->file_descriptor();
195   temp_file_fd->fd = file_descriptor.fd;
196   temp_file_fd->auto_close = false;
197 #endif
198 }
199 
OnTempFileForPrintingWritten(int render_view_id,int sequence_number)200 void PrintingMessageFilter::OnTempFileForPrintingWritten(int render_view_id,
201                                                          int sequence_number) {
202 #if defined(OS_CHROMEOS)
203   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
204   SequenceToPathMap* map = &g_printing_file_descriptor_map.Get().map;
205   SequenceToPathMap::iterator it = map->find(sequence_number);
206   if (it == map->end()) {
207     NOTREACHED() << "Got a sequence that we didn't pass to the "
208                     "renderer: " << sequence_number;
209     return;
210   }
211   BrowserThread::PostTask(
212       BrowserThread::UI, FROM_HERE,
213       base::Bind(&PrintingMessageFilter::CreatePrintDialogForFile,
214                  this, render_view_id, it->second));
215 
216   // Erase the entry in the map.
217   map->erase(it);
218 #elif defined(OS_ANDROID)
219   DCHECK_CURRENTLY_ON(BrowserThread::UI);
220   content::WebContents* wc = GetWebContentsForRenderView(render_view_id);
221   if (!wc)
222     return;
223   printing::PrintViewManagerBasic* print_view_manager =
224       printing::PrintViewManagerBasic::FromWebContents(wc);
225   const base::FileDescriptor& file_descriptor =
226       print_view_manager->file_descriptor();
227   printing::PrintingContextAndroid::PdfWritingDone(file_descriptor.fd, true);
228   // Invalidate the file descriptor so it doesn't accidentally get reused.
229   print_view_manager->set_file_descriptor(base::FileDescriptor(-1, false));
230 #endif
231 }
232 #endif  // defined(OS_CHROMEOS) || defined(OS_ANDROID)
233 
234 #if defined(OS_CHROMEOS)
CreatePrintDialogForFile(int render_view_id,const base::FilePath & path)235 void PrintingMessageFilter::CreatePrintDialogForFile(
236     int render_view_id,
237     const base::FilePath& path) {
238   content::WebContents* wc = GetWebContentsForRenderView(render_view_id);
239   if (!wc)
240     return;
241   print_dialog_cloud::CreatePrintDialogForFile(
242       wc->GetBrowserContext(),
243       wc->GetTopLevelNativeWindow(),
244       path,
245       wc->GetTitle(),
246       base::string16(),
247       std::string("application/pdf"));
248 }
249 #endif  // defined(OS_CHROMEOS)
250 
GetWebContentsForRenderView(int render_view_id)251 content::WebContents* PrintingMessageFilter::GetWebContentsForRenderView(
252     int render_view_id) {
253   DCHECK_CURRENTLY_ON(BrowserThread::UI);
254   content::RenderViewHost* view = content::RenderViewHost::FromID(
255       render_process_id_, render_view_id);
256   return view ? content::WebContents::FromRenderViewHost(view) : NULL;
257 }
258 
259 struct PrintingMessageFilter::GetPrintSettingsForRenderViewParams {
260   printing::PrinterQuery::GetSettingsAskParam ask_user_for_settings;
261   int expected_page_count;
262   bool has_selection;
263   printing::MarginType margin_type;
264 };
265 
GetPrintSettingsForRenderView(int render_view_id,GetPrintSettingsForRenderViewParams params,const base::Closure & callback,scoped_refptr<printing::PrinterQuery> printer_query)266 void PrintingMessageFilter::GetPrintSettingsForRenderView(
267     int render_view_id,
268     GetPrintSettingsForRenderViewParams params,
269     const base::Closure& callback,
270     scoped_refptr<printing::PrinterQuery> printer_query) {
271   DCHECK_CURRENTLY_ON(BrowserThread::UI);
272   content::WebContents* wc = GetWebContentsForRenderView(render_view_id);
273   if (wc) {
274     scoped_ptr<PrintingUIWebContentsObserver> wc_observer(
275         new PrintingUIWebContentsObserver(wc));
276     BrowserThread::PostTask(
277         BrowserThread::IO, FROM_HERE,
278         base::Bind(&printing::PrinterQuery::GetSettings, printer_query,
279                    params.ask_user_for_settings, base::Passed(&wc_observer),
280                    params.expected_page_count, params.has_selection,
281                    params.margin_type, callback));
282   } else {
283     BrowserThread::PostTask(
284         BrowserThread::IO, FROM_HERE,
285         base::Bind(&PrintingMessageFilter::OnGetPrintSettingsFailed, this,
286                    callback, printer_query));
287   }
288 }
289 
OnGetPrintSettingsFailed(const base::Closure & callback,scoped_refptr<printing::PrinterQuery> printer_query)290 void PrintingMessageFilter::OnGetPrintSettingsFailed(
291     const base::Closure& callback,
292     scoped_refptr<printing::PrinterQuery> printer_query) {
293   DCHECK_CURRENTLY_ON(BrowserThread::IO);
294   printer_query->GetSettingsDone(printing::PrintSettings(),
295                                  printing::PrintingContext::FAILED);
296   callback.Run();
297 }
298 
OnIsPrintingEnabled(bool * is_enabled)299 void PrintingMessageFilter::OnIsPrintingEnabled(bool* is_enabled) {
300   DCHECK_CURRENTLY_ON(BrowserThread::IO);
301   *is_enabled = profile_io_data_->printing_enabled()->GetValue();
302 }
303 
OnGetDefaultPrintSettings(IPC::Message * reply_msg)304 void PrintingMessageFilter::OnGetDefaultPrintSettings(IPC::Message* reply_msg) {
305   DCHECK_CURRENTLY_ON(BrowserThread::IO);
306   scoped_refptr<printing::PrinterQuery> printer_query;
307   if (!profile_io_data_->printing_enabled()->GetValue()) {
308     // Reply with NULL query.
309     OnGetDefaultPrintSettingsReply(printer_query, reply_msg);
310     return;
311   }
312   printer_query = queue_->PopPrinterQuery(0);
313   if (!printer_query)
314     printer_query = queue_->CreatePrinterQuery();
315 
316   // Loads default settings. This is asynchronous, only the IPC message sender
317   // will hang until the settings are retrieved.
318   GetPrintSettingsForRenderViewParams params;
319   params.ask_user_for_settings = printing::PrinterQuery::DEFAULTS;
320   params.expected_page_count = 0;
321   params.has_selection = false;
322   params.margin_type = printing::DEFAULT_MARGINS;
323   BrowserThread::PostTask(
324       BrowserThread::UI, FROM_HERE,
325       base::Bind(&PrintingMessageFilter::GetPrintSettingsForRenderView, this,
326           reply_msg->routing_id(), params,
327           base::Bind(&PrintingMessageFilter::OnGetDefaultPrintSettingsReply,
328               this, printer_query, reply_msg),
329           printer_query));
330 }
331 
OnGetDefaultPrintSettingsReply(scoped_refptr<printing::PrinterQuery> printer_query,IPC::Message * reply_msg)332 void PrintingMessageFilter::OnGetDefaultPrintSettingsReply(
333     scoped_refptr<printing::PrinterQuery> printer_query,
334     IPC::Message* reply_msg) {
335   PrintMsg_Print_Params params;
336   if (!printer_query.get() ||
337       printer_query->last_status() != printing::PrintingContext::OK) {
338     params.Reset();
339   } else {
340     RenderParamsFromPrintSettings(printer_query->settings(), &params);
341     params.document_cookie = printer_query->cookie();
342   }
343   PrintHostMsg_GetDefaultPrintSettings::WriteReplyParams(reply_msg, params);
344   Send(reply_msg);
345   // If printing was enabled.
346   if (printer_query.get()) {
347     // If user hasn't cancelled.
348     if (printer_query->cookie() && printer_query->settings().dpi()) {
349       queue_->QueuePrinterQuery(printer_query.get());
350     } else {
351       printer_query->StopWorker();
352     }
353   }
354 }
355 
OnScriptedPrint(const PrintHostMsg_ScriptedPrint_Params & params,IPC::Message * reply_msg)356 void PrintingMessageFilter::OnScriptedPrint(
357     const PrintHostMsg_ScriptedPrint_Params& params,
358     IPC::Message* reply_msg) {
359   scoped_refptr<printing::PrinterQuery> printer_query =
360       queue_->PopPrinterQuery(params.cookie);
361   if (!printer_query)
362     printer_query = queue_->CreatePrinterQuery();
363   GetPrintSettingsForRenderViewParams settings_params;
364   settings_params.ask_user_for_settings = printing::PrinterQuery::ASK_USER;
365   settings_params.expected_page_count = params.expected_pages_count;
366   settings_params.has_selection = params.has_selection;
367   settings_params.margin_type = params.margin_type;
368 
369   BrowserThread::PostTask(
370       BrowserThread::UI, FROM_HERE,
371       base::Bind(&PrintingMessageFilter::GetPrintSettingsForRenderView, this,
372                  reply_msg->routing_id(), settings_params,
373                  base::Bind(&PrintingMessageFilter::OnScriptedPrintReply, this,
374                             printer_query, reply_msg),
375                  printer_query));
376 }
377 
OnScriptedPrintReply(scoped_refptr<printing::PrinterQuery> printer_query,IPC::Message * reply_msg)378 void PrintingMessageFilter::OnScriptedPrintReply(
379     scoped_refptr<printing::PrinterQuery> printer_query,
380     IPC::Message* reply_msg) {
381   PrintMsg_PrintPages_Params params;
382 #if defined(OS_ANDROID)
383   // We need to save the routing ID here because Send method below deletes the
384   // |reply_msg| before we can get the routing ID for the Android code.
385   int routing_id = reply_msg->routing_id();
386 #endif
387   if (printer_query->last_status() != printing::PrintingContext::OK ||
388       !printer_query->settings().dpi()) {
389     params.Reset();
390   } else {
391     RenderParamsFromPrintSettings(printer_query->settings(), &params.params);
392     params.params.document_cookie = printer_query->cookie();
393     params.pages =
394         printing::PageRange::GetPages(printer_query->settings().ranges());
395   }
396   PrintHostMsg_ScriptedPrint::WriteReplyParams(reply_msg, params);
397   Send(reply_msg);
398   if (params.params.dpi && params.params.document_cookie) {
399 #if defined(OS_ANDROID)
400     int file_descriptor;
401     const base::string16& device_name = printer_query->settings().device_name();
402     if (base::StringToInt(device_name, &file_descriptor)) {
403       BrowserThread::PostTask(
404           BrowserThread::UI, FROM_HERE,
405           base::Bind(&PrintingMessageFilter::UpdateFileDescriptor, this,
406                      routing_id, file_descriptor));
407     }
408 #endif
409     queue_->QueuePrinterQuery(printer_query.get());
410   } else {
411     printer_query->StopWorker();
412   }
413 }
414 
415 #if defined(OS_ANDROID)
UpdateFileDescriptor(int render_view_id,int fd)416 void PrintingMessageFilter::UpdateFileDescriptor(int render_view_id, int fd) {
417   DCHECK_CURRENTLY_ON(BrowserThread::UI);
418   content::WebContents* wc = GetWebContentsForRenderView(render_view_id);
419   if (!wc)
420     return;
421   printing::PrintViewManagerBasic* print_view_manager =
422       printing::PrintViewManagerBasic::FromWebContents(wc);
423   print_view_manager->set_file_descriptor(base::FileDescriptor(fd, false));
424 }
425 #endif
426 
OnUpdatePrintSettings(int document_cookie,const base::DictionaryValue & job_settings,IPC::Message * reply_msg)427 void PrintingMessageFilter::OnUpdatePrintSettings(
428     int document_cookie, const base::DictionaryValue& job_settings,
429     IPC::Message* reply_msg) {
430   scoped_refptr<printing::PrinterQuery> printer_query;
431   if (!profile_io_data_->printing_enabled()->GetValue()) {
432     // Reply with NULL query.
433     OnUpdatePrintSettingsReply(printer_query, reply_msg);
434     return;
435   }
436   printer_query = queue_->PopPrinterQuery(document_cookie);
437   if (!printer_query)
438     printer_query = queue_->CreatePrinterQuery();
439   printer_query->SetSettings(
440       job_settings,
441       base::Bind(&PrintingMessageFilter::OnUpdatePrintSettingsReply, this,
442                  printer_query, reply_msg));
443 }
444 
OnUpdatePrintSettingsReply(scoped_refptr<printing::PrinterQuery> printer_query,IPC::Message * reply_msg)445 void PrintingMessageFilter::OnUpdatePrintSettingsReply(
446     scoped_refptr<printing::PrinterQuery> printer_query,
447     IPC::Message* reply_msg) {
448   PrintMsg_PrintPages_Params params;
449   if (!printer_query.get() ||
450       printer_query->last_status() != printing::PrintingContext::OK) {
451     params.Reset();
452   } else {
453     RenderParamsFromPrintSettings(printer_query->settings(), &params.params);
454     params.params.document_cookie = printer_query->cookie();
455     params.pages =
456         printing::PageRange::GetPages(printer_query->settings().ranges());
457   }
458   PrintHostMsg_UpdatePrintSettings::WriteReplyParams(reply_msg, params);
459   Send(reply_msg);
460   // If user hasn't cancelled.
461   if (printer_query.get()) {
462     if (printer_query->cookie() && printer_query->settings().dpi()) {
463       queue_->QueuePrinterQuery(printer_query.get());
464     } else {
465       printer_query->StopWorker();
466     }
467   }
468 }
469 
470 #if defined(ENABLE_FULL_PRINTING)
OnCheckForCancel(int32 preview_ui_id,int preview_request_id,bool * cancel)471 void PrintingMessageFilter::OnCheckForCancel(int32 preview_ui_id,
472                                              int preview_request_id,
473                                              bool* cancel) {
474   PrintPreviewUI::GetCurrentPrintPreviewStatus(preview_ui_id,
475                                                preview_request_id,
476                                                cancel);
477 }
478 #endif
479