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 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 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 100 PrintingMessageFilter::~PrintingMessageFilter() { 101 } 102 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 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) 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) 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 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) 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 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 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 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 299 void PrintingMessageFilter::OnIsPrintingEnabled(bool* is_enabled) { 300 DCHECK_CURRENTLY_ON(BrowserThread::IO); 301 *is_enabled = profile_io_data_->printing_enabled()->GetValue(); 302 } 303 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 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(), ¶ms); 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 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 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(), ¶ms.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) 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 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 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(), ¶ms.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) 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