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/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/browser/web_contents_view.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->date = settings.date; 85 params->title = settings.title; 86 params->url = settings.url; 87 } 88 89 } // namespace 90 91 PrintingMessageFilter::PrintingMessageFilter(int render_process_id, 92 Profile* profile) 93 : print_job_manager_(g_browser_process->print_job_manager()), 94 profile_io_data_(ProfileIOData::FromResourceContext( 95 profile->GetResourceContext())), 96 render_process_id_(render_process_id) { 97 } 98 99 PrintingMessageFilter::~PrintingMessageFilter() { 100 } 101 102 void PrintingMessageFilter::OverrideThreadForMessage( 103 const IPC::Message& message, BrowserThread::ID* thread) { 104 #if defined(OS_CHROMEOS) 105 if (message.type() == PrintHostMsg_AllocateTempFileForPrinting::ID || 106 message.type() == PrintHostMsg_TempFileForPrintingWritten::ID) { 107 *thread = BrowserThread::FILE; 108 } 109 #elif defined(OS_ANDROID) 110 if (message.type() == PrintHostMsg_AllocateTempFileForPrinting::ID || 111 message.type() == PrintHostMsg_TempFileForPrintingWritten::ID) { 112 *thread = BrowserThread::UI; 113 } 114 #endif 115 } 116 117 bool PrintingMessageFilter::OnMessageReceived(const IPC::Message& message, 118 bool* message_was_ok) { 119 bool handled = true; 120 IPC_BEGIN_MESSAGE_MAP_EX(PrintingMessageFilter, message, *message_was_ok) 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(BrowserThread::CurrentlyOn(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 (file_util::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(BrowserThread::CurrentlyOn(BrowserThread::UI)); 186 content::WebContents* wc = GetWebContentsForRenderView(render_view_id); 187 printing::PrintViewManagerBasic* print_view_manager = 188 printing::PrintViewManagerBasic::FromWebContents(wc); 189 // The file descriptor is originally created in & passed from the Android 190 // side, and it will handle the closing. 191 const base::FileDescriptor& file_descriptor = 192 print_view_manager->file_descriptor(); 193 temp_file_fd->fd = file_descriptor.fd; 194 temp_file_fd->auto_close = false; 195 #endif 196 } 197 198 void PrintingMessageFilter::OnTempFileForPrintingWritten(int render_view_id, 199 int sequence_number) { 200 #if defined(OS_CHROMEOS) 201 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 202 SequenceToPathMap* map = &g_printing_file_descriptor_map.Get().map; 203 SequenceToPathMap::iterator it = map->find(sequence_number); 204 if (it == map->end()) { 205 NOTREACHED() << "Got a sequence that we didn't pass to the " 206 "renderer: " << sequence_number; 207 return; 208 } 209 BrowserThread::PostTask( 210 BrowserThread::UI, FROM_HERE, 211 base::Bind(&PrintingMessageFilter::CreatePrintDialogForFile, 212 this, render_view_id, it->second)); 213 214 // Erase the entry in the map. 215 map->erase(it); 216 #elif defined(OS_ANDROID) 217 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 218 content::WebContents* wc = GetWebContentsForRenderView(render_view_id); 219 printing::PrintViewManagerBasic* print_view_manager = 220 printing::PrintViewManagerBasic::FromWebContents(wc); 221 const base::FileDescriptor& file_descriptor = 222 print_view_manager->file_descriptor(); 223 printing::PrintingContextAndroid::PdfWritingDone(file_descriptor.fd, true); 224 // Invalidate the file descriptor so it doesn't accidentally get reused. 225 print_view_manager->set_file_descriptor(base::FileDescriptor(-1, false)); 226 #endif 227 } 228 #endif // defined(OS_CHROMEOS) || defined(OS_ANDROID) 229 230 #if defined(OS_CHROMEOS) 231 void PrintingMessageFilter::CreatePrintDialogForFile( 232 int render_view_id, 233 const base::FilePath& path) { 234 content::WebContents* wc = GetWebContentsForRenderView(render_view_id); 235 print_dialog_cloud::CreatePrintDialogForFile( 236 wc->GetBrowserContext(), 237 wc->GetView()->GetTopLevelNativeWindow(), 238 path, 239 wc->GetTitle(), 240 string16(), 241 std::string("application/pdf"), 242 false); 243 } 244 #endif // defined(OS_CHROMEOS) 245 246 content::WebContents* PrintingMessageFilter::GetWebContentsForRenderView( 247 int render_view_id) { 248 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 249 content::RenderViewHost* view = content::RenderViewHost::FromID( 250 render_process_id_, render_view_id); 251 return content::WebContents::FromRenderViewHost(view); 252 } 253 254 struct PrintingMessageFilter::GetPrintSettingsForRenderViewParams { 255 printing::PrinterQuery::GetSettingsAskParam ask_user_for_settings; 256 int expected_page_count; 257 bool has_selection; 258 printing::MarginType margin_type; 259 }; 260 261 void PrintingMessageFilter::GetPrintSettingsForRenderView( 262 int render_view_id, 263 GetPrintSettingsForRenderViewParams params, 264 const base::Closure& callback, 265 scoped_refptr<printing::PrinterQuery> printer_query) { 266 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 267 content::WebContents* wc = GetWebContentsForRenderView(render_view_id); 268 269 BrowserThread::PostTask( 270 BrowserThread::IO, FROM_HERE, 271 base::Bind(&printing::PrinterQuery::GetSettings, printer_query, 272 params.ask_user_for_settings, wc->GetView()->GetNativeView(), 273 params.expected_page_count, params.has_selection, 274 params.margin_type, callback)); 275 } 276 277 void PrintingMessageFilter::OnIsPrintingEnabled(bool* is_enabled) { 278 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 279 *is_enabled = profile_io_data_->printing_enabled()->GetValue(); 280 } 281 282 void PrintingMessageFilter::OnGetDefaultPrintSettings(IPC::Message* reply_msg) { 283 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 284 scoped_refptr<printing::PrinterQuery> printer_query; 285 if (!profile_io_data_->printing_enabled()->GetValue()) { 286 // Reply with NULL query. 287 OnGetDefaultPrintSettingsReply(printer_query, reply_msg); 288 return; 289 } 290 print_job_manager_->PopPrinterQuery(0, &printer_query); 291 if (!printer_query.get()) { 292 printer_query = new printing::PrinterQuery; 293 printer_query->SetWorkerDestination(print_job_manager_->destination()); 294 } 295 296 // Loads default settings. This is asynchronous, only the IPC message sender 297 // will hang until the settings are retrieved. 298 GetPrintSettingsForRenderViewParams params; 299 params.ask_user_for_settings = printing::PrinterQuery::DEFAULTS; 300 params.expected_page_count = 0; 301 params.has_selection = false; 302 params.margin_type = printing::DEFAULT_MARGINS; 303 BrowserThread::PostTask( 304 BrowserThread::UI, FROM_HERE, 305 base::Bind(&PrintingMessageFilter::GetPrintSettingsForRenderView, this, 306 reply_msg->routing_id(), params, 307 base::Bind(&PrintingMessageFilter::OnGetDefaultPrintSettingsReply, 308 this, printer_query, reply_msg), 309 printer_query)); 310 } 311 312 void PrintingMessageFilter::OnGetDefaultPrintSettingsReply( 313 scoped_refptr<printing::PrinterQuery> printer_query, 314 IPC::Message* reply_msg) { 315 PrintMsg_Print_Params params; 316 if (!printer_query.get() || 317 printer_query->last_status() != printing::PrintingContext::OK) { 318 params.Reset(); 319 } else { 320 RenderParamsFromPrintSettings(printer_query->settings(), ¶ms); 321 params.document_cookie = printer_query->cookie(); 322 } 323 PrintHostMsg_GetDefaultPrintSettings::WriteReplyParams(reply_msg, params); 324 Send(reply_msg); 325 // If printing was enabled. 326 if (printer_query.get()) { 327 // If user hasn't cancelled. 328 if (printer_query->cookie() && printer_query->settings().dpi()) { 329 print_job_manager_->QueuePrinterQuery(printer_query.get()); 330 } else { 331 printer_query->StopWorker(); 332 } 333 } 334 } 335 336 void PrintingMessageFilter::OnScriptedPrint( 337 const PrintHostMsg_ScriptedPrint_Params& params, 338 IPC::Message* reply_msg) { 339 scoped_refptr<printing::PrinterQuery> printer_query; 340 print_job_manager_->PopPrinterQuery(params.cookie, &printer_query); 341 if (!printer_query.get()) { 342 printer_query = new printing::PrinterQuery; 343 printer_query->SetWorkerDestination(print_job_manager_->destination()); 344 } 345 GetPrintSettingsForRenderViewParams settings_params; 346 settings_params.ask_user_for_settings = printing::PrinterQuery::ASK_USER; 347 settings_params.expected_page_count = params.expected_pages_count; 348 settings_params.has_selection = params.has_selection; 349 settings_params.margin_type = params.margin_type; 350 351 BrowserThread::PostTask( 352 BrowserThread::UI, FROM_HERE, 353 base::Bind(&PrintingMessageFilter::GetPrintSettingsForRenderView, this, 354 reply_msg->routing_id(), settings_params, 355 base::Bind(&PrintingMessageFilter::OnScriptedPrintReply, this, 356 printer_query, reply_msg), 357 printer_query)); 358 } 359 360 void PrintingMessageFilter::OnScriptedPrintReply( 361 scoped_refptr<printing::PrinterQuery> printer_query, 362 IPC::Message* reply_msg) { 363 PrintMsg_PrintPages_Params params; 364 #if defined(OS_ANDROID) 365 // We need to save the routing ID here because Send method below deletes the 366 // |reply_msg| before we can get the routing ID for the Android code. 367 int routing_id = reply_msg->routing_id(); 368 #endif 369 if (printer_query->last_status() != printing::PrintingContext::OK || 370 !printer_query->settings().dpi()) { 371 params.Reset(); 372 } else { 373 RenderParamsFromPrintSettings(printer_query->settings(), ¶ms.params); 374 params.params.document_cookie = printer_query->cookie(); 375 params.pages = 376 printing::PageRange::GetPages(printer_query->settings().ranges); 377 } 378 PrintHostMsg_ScriptedPrint::WriteReplyParams(reply_msg, params); 379 Send(reply_msg); 380 if (params.params.dpi && params.params.document_cookie) { 381 #if defined(OS_ANDROID) 382 int file_descriptor; 383 const string16& device_name = printer_query->settings().device_name(); 384 if (base::StringToInt(device_name, &file_descriptor)) { 385 BrowserThread::PostTask( 386 BrowserThread::UI, FROM_HERE, 387 base::Bind(&PrintingMessageFilter::UpdateFileDescriptor, this, 388 routing_id, file_descriptor)); 389 } 390 #endif 391 print_job_manager_->QueuePrinterQuery(printer_query.get()); 392 } else { 393 printer_query->StopWorker(); 394 } 395 } 396 397 #if defined(OS_ANDROID) 398 void PrintingMessageFilter::UpdateFileDescriptor(int render_view_id, int fd) { 399 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 400 content::WebContents* wc = GetWebContentsForRenderView(render_view_id); 401 printing::PrintViewManagerBasic* print_view_manager = 402 printing::PrintViewManagerBasic::FromWebContents(wc); 403 print_view_manager->set_file_descriptor(base::FileDescriptor(fd, false)); 404 } 405 #endif 406 407 void PrintingMessageFilter::OnUpdatePrintSettings( 408 int document_cookie, const DictionaryValue& job_settings, 409 IPC::Message* reply_msg) { 410 scoped_refptr<printing::PrinterQuery> printer_query; 411 if (!profile_io_data_->printing_enabled()->GetValue()) { 412 // Reply with NULL query. 413 OnUpdatePrintSettingsReply(printer_query, reply_msg); 414 return; 415 } 416 417 print_job_manager_->PopPrinterQuery(document_cookie, &printer_query); 418 if (!printer_query.get()) { 419 printer_query = new printing::PrinterQuery; 420 printer_query->SetWorkerDestination(print_job_manager_->destination()); 421 } 422 printer_query->SetSettings( 423 job_settings, 424 base::Bind(&PrintingMessageFilter::OnUpdatePrintSettingsReply, this, 425 printer_query, reply_msg)); 426 } 427 428 void PrintingMessageFilter::OnUpdatePrintSettingsReply( 429 scoped_refptr<printing::PrinterQuery> printer_query, 430 IPC::Message* reply_msg) { 431 PrintMsg_PrintPages_Params params; 432 if (!printer_query.get() || 433 printer_query->last_status() != printing::PrintingContext::OK) { 434 params.Reset(); 435 } else { 436 RenderParamsFromPrintSettings(printer_query->settings(), ¶ms.params); 437 params.params.document_cookie = printer_query->cookie(); 438 params.pages = 439 printing::PageRange::GetPages(printer_query->settings().ranges); 440 } 441 PrintHostMsg_UpdatePrintSettings::WriteReplyParams(reply_msg, params); 442 Send(reply_msg); 443 // If user hasn't cancelled. 444 if (printer_query.get()) { 445 if (printer_query->cookie() && printer_query->settings().dpi()) 446 print_job_manager_->QueuePrinterQuery(printer_query.get()); 447 else 448 printer_query->StopWorker(); 449 } 450 } 451 452 #if defined(ENABLE_FULL_PRINTING) 453 void PrintingMessageFilter::OnCheckForCancel(int32 preview_ui_id, 454 int preview_request_id, 455 bool* cancel) { 456 PrintPreviewUI::GetCurrentPrintPreviewStatus(preview_ui_id, 457 preview_request_id, 458 cancel); 459 } 460 #endif 461