Home | History | Annotate | Download | only in printing
      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(), &params);
    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(), &params.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(), &params.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