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