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(), ¶ms); 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(), ¶ms.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(), ¶ms.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