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/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(), &params);
    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(), &params.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(), &params.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