1// Copyright (c) 2011 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 "base/process_util.h"
8#include "chrome/browser/browser_process.h"
9#include "chrome/browser/printing/printer_query.h"
10#include "chrome/browser/printing/print_job_manager.h"
11#include "chrome/common/print_messages.h"
12#include "content/common/view_messages.h"
13
14#if defined(OS_CHROMEOS)
15#include <fcntl.h>
16
17#include "base/file_util.h"
18#include "base/lazy_instance.h"
19#include "chrome/browser/printing/print_dialog_cloud.h"
20#else
21#include "base/command_line.h"
22#include "chrome/common/chrome_switches.h"
23#endif
24
25namespace {
26
27#if defined(OS_CHROMEOS)
28typedef std::map<int, FilePath> SequenceToPathMap;
29
30struct PrintingSequencePathMap {
31  SequenceToPathMap map;
32  int sequence;
33};
34
35// No locking, only access on the FILE thread.
36static base::LazyInstance<PrintingSequencePathMap>
37    g_printing_file_descriptor_map(base::LINKER_INITIALIZED);
38#endif
39
40void RenderParamsFromPrintSettings(const printing::PrintSettings& settings,
41                                   PrintMsg_Print_Params* params) {
42  params->page_size = settings.page_setup_device_units().physical_size();
43  params->printable_size.SetSize(
44      settings.page_setup_device_units().content_area().width(),
45      settings.page_setup_device_units().content_area().height());
46  params->margin_top = settings.page_setup_device_units().content_area().x();
47  params->margin_left = settings.page_setup_device_units().content_area().y();
48  params->dpi = settings.dpi();
49  // Currently hardcoded at 1.25. See PrintSettings' constructor.
50  params->min_shrink = settings.min_shrink;
51  // Currently hardcoded at 2.0. See PrintSettings' constructor.
52  params->max_shrink = settings.max_shrink;
53  // Currently hardcoded at 72dpi. See PrintSettings' constructor.
54  params->desired_dpi = settings.desired_dpi;
55  // Always use an invalid cookie.
56  params->document_cookie = 0;
57  params->selection_only = settings.selection_only;
58  params->supports_alpha_blend = settings.supports_alpha_blend();
59}
60
61}  // namespace
62
63PrintingMessageFilter::PrintingMessageFilter()
64    : print_job_manager_(g_browser_process->print_job_manager()) {
65#if defined(OS_CHROMEOS)
66  cloud_print_enabled_ = true;
67#else
68  cloud_print_enabled_ = CommandLine::ForCurrentProcess()->HasSwitch(
69      switches::kEnableCloudPrint);
70#endif
71}
72
73PrintingMessageFilter::~PrintingMessageFilter() {
74}
75
76void PrintingMessageFilter::OverrideThreadForMessage(
77    const IPC::Message& message, BrowserThread::ID* thread) {
78#if defined(OS_CHROMEOS)
79  if (message.type() == PrintHostMsg_AllocateTempFileForPrinting::ID ||
80      message.type() == PrintHostMsg_TempFileForPrintingWritten::ID) {
81    *thread = BrowserThread::FILE;
82  }
83#endif
84}
85
86bool PrintingMessageFilter::OnMessageReceived(const IPC::Message& message,
87                                              bool* message_was_ok) {
88  bool handled = true;
89  IPC_BEGIN_MESSAGE_MAP_EX(PrintingMessageFilter, message, *message_was_ok)
90#if defined(OS_WIN)
91    IPC_MESSAGE_HANDLER(PrintHostMsg_DuplicateSection, OnDuplicateSection)
92#endif
93#if defined(OS_CHROMEOS)
94    IPC_MESSAGE_HANDLER(PrintHostMsg_AllocateTempFileForPrinting,
95                        OnAllocateTempFileForPrinting)
96    IPC_MESSAGE_HANDLER(PrintHostMsg_TempFileForPrintingWritten,
97                        OnTempFileForPrintingWritten)
98#endif
99    IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_GetDefaultPrintSettings,
100                                    OnGetDefaultPrintSettings)
101    IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_ScriptedPrint, OnScriptedPrint)
102    IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_UpdatePrintSettings,
103                                    OnUpdatePrintSettings)
104    IPC_MESSAGE_UNHANDLED(handled = false)
105  IPC_END_MESSAGE_MAP()
106  return handled;
107}
108
109#if defined(OS_WIN)
110void PrintingMessageFilter::OnDuplicateSection(
111    base::SharedMemoryHandle renderer_handle,
112    base::SharedMemoryHandle* browser_handle) {
113  // Duplicate the handle in this process right now so the memory is kept alive
114  // (even if it is not mapped)
115  base::SharedMemory shared_buf(renderer_handle, true, peer_handle());
116  shared_buf.GiveToProcess(base::GetCurrentProcessHandle(), browser_handle);
117}
118#endif
119
120#if defined(OS_CHROMEOS)
121void PrintingMessageFilter::OnAllocateTempFileForPrinting(
122    base::FileDescriptor* temp_file_fd, int* sequence_number) {
123  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
124  temp_file_fd->fd = *sequence_number = -1;
125  temp_file_fd->auto_close = false;
126
127  SequenceToPathMap* map = &g_printing_file_descriptor_map.Get().map;
128  *sequence_number = g_printing_file_descriptor_map.Get().sequence++;
129
130  FilePath path;
131  if (file_util::CreateTemporaryFile(&path)) {
132    int fd = open(path.value().c_str(), O_WRONLY);
133    if (fd >= 0) {
134      SequenceToPathMap::iterator it = map->find(*sequence_number);
135      if (it != map->end()) {
136        NOTREACHED() << "Sequence number already in use. seq=" <<
137            *sequence_number;
138      } else {
139        (*map)[*sequence_number] = path;
140        temp_file_fd->fd = fd;
141        temp_file_fd->auto_close = true;
142      }
143    }
144  }
145}
146
147void PrintingMessageFilter::OnTempFileForPrintingWritten(int sequence_number) {
148  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
149  SequenceToPathMap* map = &g_printing_file_descriptor_map.Get().map;
150  SequenceToPathMap::iterator it = map->find(sequence_number);
151  if (it == map->end()) {
152    NOTREACHED() << "Got a sequence that we didn't pass to the "
153                    "renderer: " << sequence_number;
154    return;
155  }
156
157  if (cloud_print_enabled_)
158    print_dialog_cloud::CreatePrintDialogForFile(
159        it->second,
160        string16(),
161        std::string("application/pdf"),
162        true);
163  else
164    NOTIMPLEMENTED();
165
166  // Erase the entry in the map.
167  map->erase(it);
168}
169#endif  // defined(OS_CHROMEOS)
170
171void PrintingMessageFilter::OnGetDefaultPrintSettings(IPC::Message* reply_msg) {
172  scoped_refptr<printing::PrinterQuery> printer_query;
173  if (!print_job_manager_->printing_enabled()) {
174    // Reply with NULL query.
175    OnGetDefaultPrintSettingsReply(printer_query, reply_msg);
176    return;
177  }
178
179  print_job_manager_->PopPrinterQuery(0, &printer_query);
180  if (!printer_query.get()) {
181    printer_query = new printing::PrinterQuery;
182  }
183
184  CancelableTask* task = NewRunnableMethod(
185      this,
186      &PrintingMessageFilter::OnGetDefaultPrintSettingsReply,
187      printer_query,
188      reply_msg);
189  // Loads default settings. This is asynchronous, only the IPC message sender
190  // will hang until the settings are retrieved.
191  printer_query->GetSettings(printing::PrinterQuery::DEFAULTS,
192                             NULL,
193                             0,
194                             false,
195                             true,
196                             task);
197}
198
199void PrintingMessageFilter::OnGetDefaultPrintSettingsReply(
200    scoped_refptr<printing::PrinterQuery> printer_query,
201    IPC::Message* reply_msg) {
202  PrintMsg_Print_Params params;
203  if (!printer_query.get() ||
204      printer_query->last_status() != printing::PrintingContext::OK) {
205    memset(&params, 0, sizeof(params));
206  } else {
207    RenderParamsFromPrintSettings(printer_query->settings(), &params);
208    params.document_cookie = printer_query->cookie();
209  }
210  PrintHostMsg_GetDefaultPrintSettings::WriteReplyParams(reply_msg, params);
211  Send(reply_msg);
212  // If printing was enabled.
213  if (printer_query.get()) {
214    // If user hasn't cancelled.
215    if (printer_query->cookie() && printer_query->settings().dpi()) {
216      print_job_manager_->QueuePrinterQuery(printer_query.get());
217    } else {
218      printer_query->StopWorker();
219    }
220  }
221}
222
223void PrintingMessageFilter::OnScriptedPrint(
224    const PrintHostMsg_ScriptedPrint_Params& params,
225    IPC::Message* reply_msg) {
226  gfx::NativeView host_view =
227      gfx::NativeViewFromIdInBrowser(params.host_window_id);
228
229  scoped_refptr<printing::PrinterQuery> printer_query;
230  print_job_manager_->PopPrinterQuery(params.cookie, &printer_query);
231  if (!printer_query.get()) {
232    printer_query = new printing::PrinterQuery;
233  }
234
235  CancelableTask* task = NewRunnableMethod(
236      this,
237      &PrintingMessageFilter::OnScriptedPrintReply,
238      printer_query,
239      params.routing_id,
240      reply_msg);
241
242  printer_query->GetSettings(printing::PrinterQuery::ASK_USER,
243                             host_view,
244                             params.expected_pages_count,
245                             params.has_selection,
246                             params.use_overlays,
247                             task);
248}
249
250void PrintingMessageFilter::OnScriptedPrintReply(
251    scoped_refptr<printing::PrinterQuery> printer_query,
252    int routing_id,
253    IPC::Message* reply_msg) {
254  PrintMsg_PrintPages_Params params;
255  if (printer_query->last_status() != printing::PrintingContext::OK ||
256      !printer_query->settings().dpi()) {
257    memset(&params, 0, sizeof(params));
258  } else {
259    RenderParamsFromPrintSettings(printer_query->settings(), &params.params);
260    params.params.document_cookie = printer_query->cookie();
261    params.pages =
262        printing::PageRange::GetPages(printer_query->settings().ranges);
263  }
264  PrintHostMsg_ScriptedPrint::WriteReplyParams(reply_msg, params);
265  Send(reply_msg);
266  if (params.params.dpi && params.params.document_cookie) {
267    print_job_manager_->QueuePrinterQuery(printer_query.get());
268  } else {
269    printer_query->StopWorker();
270  }
271}
272
273void PrintingMessageFilter::OnUpdatePrintSettings(
274    int document_cookie, const DictionaryValue& job_settings,
275    IPC::Message* reply_msg) {
276  scoped_refptr<printing::PrinterQuery> printer_query;
277  print_job_manager_->PopPrinterQuery(document_cookie, &printer_query);
278  if (printer_query.get()) {
279    CancelableTask* task = NewRunnableMethod(
280        this,
281        &PrintingMessageFilter::OnUpdatePrintSettingsReply,
282        printer_query,
283        reply_msg);
284    printer_query->SetSettings(job_settings, task);
285  }
286}
287
288void PrintingMessageFilter::OnUpdatePrintSettingsReply(
289    scoped_refptr<printing::PrinterQuery> printer_query,
290    IPC::Message* reply_msg) {
291  PrintMsg_PrintPages_Params params;
292  if (printer_query->last_status() != printing::PrintingContext::OK) {
293    memset(&params, 0, sizeof(params));
294  } else {
295    RenderParamsFromPrintSettings(printer_query->settings(), &params.params);
296    params.params.document_cookie = printer_query->cookie();
297    params.pages =
298        printing::PageRange::GetPages(printer_query->settings().ranges);
299  }
300  PrintHostMsg_UpdatePrintSettings::WriteReplyParams(reply_msg, params);
301  Send(reply_msg);
302  // If user hasn't cancelled.
303  if (printer_query->cookie() && printer_query->settings().dpi())
304    print_job_manager_->QueuePrinterQuery(printer_query.get());
305  else
306    printer_query->StopWorker();
307}
308