Home | History | Annotate | Download | only in webui
      1 // Copyright (c) 2011 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/ui/webui/print_preview_handler.h"
      6 
      7 #include <string>
      8 
      9 #include "base/i18n/file_util_icu.h"
     10 #include "base/json/json_reader.h"
     11 #include "base/path_service.h"
     12 #include "base/threading/thread.h"
     13 #include "base/threading/thread_restrictions.h"
     14 #include "base/utf_string_conversions.h"
     15 #include "base/values.h"
     16 #include "chrome/browser/platform_util.h"
     17 #include "chrome/browser/printing/print_preview_tab_controller.h"
     18 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
     19 #include "chrome/browser/ui/webui/print_preview_ui_html_source.h"
     20 #include "chrome/browser/ui/webui/print_preview_ui.h"
     21 #include "chrome/common/chrome_paths.h"
     22 #include "chrome/common/print_messages.h"
     23 #include "content/browser/browser_thread.h"
     24 #include "content/browser/renderer_host/render_view_host.h"
     25 #include "content/browser/tab_contents/tab_contents.h"
     26 #include "printing/backend/print_backend.h"
     27 #include "printing/metafile.h"
     28 #include "printing/metafile_impl.h"
     29 #include "printing/print_job_constants.h"
     30 
     31 #if defined(OS_POSIX) && !defined(OS_CHROMEOS)
     32 #include <cups/cups.h>
     33 
     34 #include "base/file_util.h"
     35 #endif
     36 
     37 namespace {
     38 
     39 const bool kColorDefaultValue = false;
     40 const bool kLandscapeDefaultValue = false;
     41 
     42 const char kDisableColorOption[] = "disableColorOption";
     43 const char kSetColorAsDefault[] = "setColorAsDefault";
     44 
     45 #if defined(OS_POSIX) && !defined(OS_CHROMEOS)
     46 const char kColorDevice[] = "ColorDevice";
     47 #endif
     48 
     49 TabContents* GetInitiatorTab(TabContents* preview_tab) {
     50   printing::PrintPreviewTabController* tab_controller =
     51       printing::PrintPreviewTabController::GetInstance();
     52   if (!tab_controller)
     53     return NULL;
     54   return tab_controller->GetInitiatorTab(preview_tab);
     55 }
     56 
     57 // Get the print job settings dictionary from |args|. The caller takes
     58 // ownership of the returned DictionaryValue. Returns NULL on failure.
     59 DictionaryValue* GetSettingsDictionary(const ListValue* args) {
     60   std::string json_str;
     61   if (!args->GetString(0, &json_str)) {
     62     NOTREACHED() << "Could not read JSON argument";
     63     return NULL;
     64   }
     65   if (json_str.empty()) {
     66     NOTREACHED() << "Empty print job settings";
     67     return NULL;
     68   }
     69   scoped_ptr<DictionaryValue> settings(static_cast<DictionaryValue*>(
     70       base::JSONReader::Read(json_str, false)));
     71   if (!settings.get() || !settings->IsType(Value::TYPE_DICTIONARY)) {
     72     NOTREACHED() << "Print job settings must be a dictionary.";
     73     return NULL;
     74   }
     75 
     76   if (settings->empty()) {
     77     NOTREACHED() << "Print job settings dictionary is empty";
     78     return NULL;
     79   }
     80 
     81   return settings.release();
     82 }
     83 
     84 }  // namespace
     85 
     86 class PrintSystemTaskProxy
     87     : public base::RefCountedThreadSafe<PrintSystemTaskProxy,
     88                                         BrowserThread::DeleteOnUIThread> {
     89  public:
     90   PrintSystemTaskProxy(const base::WeakPtr<PrintPreviewHandler>& handler,
     91                              printing::PrintBackend* print_backend)
     92       : handler_(handler),
     93         print_backend_(print_backend) {
     94   }
     95 
     96   void EnumeratePrinters() {
     97     ListValue* printers = new ListValue;
     98     int default_printer_index = -1;
     99 
    100     printing::PrinterList printer_list;
    101     print_backend_->EnumeratePrinters(&printer_list);
    102     int i = 0;
    103     for (printing::PrinterList::iterator index = printer_list.begin();
    104          index != printer_list.end(); ++index, ++i) {
    105       printers->Append(new StringValue(index->printer_name));
    106       if (index->is_default)
    107         default_printer_index = i;
    108     }
    109 
    110     BrowserThread::PostTask(
    111         BrowserThread::UI, FROM_HERE,
    112         NewRunnableMethod(this,
    113                           &PrintSystemTaskProxy::SendPrinterList,
    114                           printers,
    115                           new FundamentalValue(default_printer_index)));
    116   }
    117 
    118   void SendPrinterList(ListValue* printers,
    119                        FundamentalValue* default_printer_index) {
    120     if (handler_)
    121       handler_->SendPrinterList(*printers, *default_printer_index);
    122     delete printers;
    123     delete default_printer_index;
    124   }
    125 
    126   void GetPrinterCapabilities(const std::string& printer_name) {
    127     printing::PrinterCapsAndDefaults printer_info;
    128     bool supports_color = true;
    129     if (!print_backend_->GetPrinterCapsAndDefaults(printer_name,
    130                                                    &printer_info)) {
    131       return;
    132     }
    133 
    134   #if defined(OS_POSIX) && !defined(OS_CHROMEOS)
    135     FilePath ppd_file_path;
    136     if (!file_util::CreateTemporaryFile(&ppd_file_path))
    137       return;
    138 
    139     int data_size = printer_info.printer_capabilities.length();
    140     if (data_size != file_util::WriteFile(
    141                          ppd_file_path,
    142                          printer_info.printer_capabilities.data(),
    143                          data_size)) {
    144       file_util::Delete(ppd_file_path, false);
    145       return;
    146     }
    147 
    148     ppd_file_t* ppd = ppdOpenFile(ppd_file_path.value().c_str());
    149     if (ppd) {
    150       ppd_attr_t* attr = ppdFindAttr(ppd, kColorDevice, NULL);
    151       if (attr && attr->value)
    152         supports_color = ppd->color_device;
    153       ppdClose(ppd);
    154     }
    155     file_util::Delete(ppd_file_path, false);
    156   #elif defined(OS_WIN) || defined(OS_CHROMEOS)
    157     NOTIMPLEMENTED();
    158   #endif
    159 
    160     DictionaryValue settings_info;
    161     settings_info.SetBoolean(kDisableColorOption, !supports_color);
    162     settings_info.SetBoolean(kSetColorAsDefault, false);
    163     BrowserThread::PostTask(
    164         BrowserThread::UI, FROM_HERE,
    165         NewRunnableMethod(this,
    166                           &PrintSystemTaskProxy::SendPrinterCapabilities,
    167                           settings_info.DeepCopy()));
    168   }
    169 
    170   void SendPrinterCapabilities(DictionaryValue* settings_info) {
    171     if (handler_)
    172       handler_->SendPrinterCapabilities(*settings_info);
    173     delete settings_info;
    174   }
    175 
    176  private:
    177   friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
    178   friend class DeleteTask<PrintSystemTaskProxy>;
    179 
    180   ~PrintSystemTaskProxy() {}
    181 
    182   base::WeakPtr<PrintPreviewHandler> handler_;
    183 
    184   scoped_refptr<printing::PrintBackend> print_backend_;
    185 
    186   DISALLOW_COPY_AND_ASSIGN(PrintSystemTaskProxy);
    187 };
    188 
    189 // A Task implementation that stores a PDF file on disk.
    190 class PrintToPdfTask : public Task {
    191  public:
    192   // Takes ownership of |metafile|.
    193   PrintToPdfTask(printing::Metafile* metafile, const FilePath& path)
    194       : metafile_(metafile), path_(path) {
    195   }
    196 
    197   ~PrintToPdfTask() {}
    198 
    199   // Task implementation
    200   virtual void Run() {
    201     metafile_->SaveTo(path_);
    202   }
    203 
    204  private:
    205   // The metafile holding the PDF data.
    206   scoped_ptr<printing::Metafile> metafile_;
    207 
    208   // The absolute path where the file will be saved.
    209   FilePath path_;
    210 };
    211 
    212 // static
    213 FilePath* PrintPreviewHandler::last_saved_path_ = NULL;
    214 
    215 PrintPreviewHandler::PrintPreviewHandler()
    216     : print_backend_(printing::PrintBackend::CreateInstance(NULL)) {
    217 }
    218 
    219 PrintPreviewHandler::~PrintPreviewHandler() {
    220   if (select_file_dialog_.get())
    221     select_file_dialog_->ListenerDestroyed();
    222 }
    223 
    224 void PrintPreviewHandler::RegisterMessages() {
    225   web_ui_->RegisterMessageCallback("getPrinters",
    226       NewCallback(this, &PrintPreviewHandler::HandleGetPrinters));
    227   web_ui_->RegisterMessageCallback("getPreview",
    228       NewCallback(this, &PrintPreviewHandler::HandleGetPreview));
    229   web_ui_->RegisterMessageCallback("print",
    230       NewCallback(this, &PrintPreviewHandler::HandlePrint));
    231   web_ui_->RegisterMessageCallback("getPrinterCapabilities",
    232       NewCallback(this, &PrintPreviewHandler::HandleGetPrinterCapabilities));
    233 }
    234 
    235 void PrintPreviewHandler::HandleGetPrinters(const ListValue*) {
    236   scoped_refptr<PrintSystemTaskProxy> task =
    237       new PrintSystemTaskProxy(AsWeakPtr(), print_backend_.get());
    238   BrowserThread::PostTask(
    239       BrowserThread::FILE, FROM_HERE,
    240       NewRunnableMethod(task.get(),
    241                         &PrintSystemTaskProxy::EnumeratePrinters));
    242 }
    243 
    244 void PrintPreviewHandler::HandleGetPreview(const ListValue* args) {
    245   TabContents* initiator_tab = GetInitiatorTab(web_ui_->tab_contents());
    246   if (!initiator_tab)
    247     return;
    248   scoped_ptr<DictionaryValue> settings(GetSettingsDictionary(args));
    249   if (!settings.get())
    250     return;
    251 
    252   RenderViewHost* rvh = initiator_tab->render_view_host();
    253   rvh->Send(new PrintMsg_PrintPreview(rvh->routing_id(), *settings));
    254 }
    255 
    256 void PrintPreviewHandler::HandlePrint(const ListValue* args) {
    257   TabContents* initiator_tab = GetInitiatorTab(web_ui_->tab_contents());
    258   if (initiator_tab) {
    259     RenderViewHost* rvh = initiator_tab->render_view_host();
    260     rvh->Send(new PrintMsg_ResetScriptedPrintCount(rvh->routing_id()));
    261   }
    262 
    263   scoped_ptr<DictionaryValue> settings(GetSettingsDictionary(args));
    264   if (!settings.get())
    265     return;
    266 
    267   bool print_to_pdf = false;
    268   settings->GetBoolean(printing::kSettingPrintToPDF, &print_to_pdf);
    269 
    270   if (print_to_pdf) {
    271     // Pre-populating select file dialog with print job title.
    272     TabContentsWrapper* wrapper =
    273         TabContentsWrapper::GetCurrentWrapperForContents(
    274             web_ui_->tab_contents());
    275 
    276     string16 print_job_title_utf16 =
    277         wrapper->print_view_manager()->RenderSourceName();
    278 
    279 #if defined(OS_WIN)
    280     FilePath::StringType print_job_title(print_job_title_utf16);
    281 #elif defined(OS_POSIX)
    282     FilePath::StringType print_job_title = UTF16ToUTF8(print_job_title_utf16);
    283 #endif
    284 
    285     file_util::ReplaceIllegalCharactersInPath(&print_job_title, '_');
    286     FilePath default_filename(print_job_title);
    287     default_filename =
    288         default_filename.ReplaceExtension(FILE_PATH_LITERAL("pdf"));
    289 
    290     SelectFile(default_filename);
    291   } else {
    292     RenderViewHost* rvh = web_ui_->GetRenderViewHost();
    293     rvh->Send(new PrintMsg_PrintForPrintPreview(rvh->routing_id(), *settings));
    294   }
    295 }
    296 
    297 void PrintPreviewHandler::HandleGetPrinterCapabilities(
    298     const ListValue* args) {
    299   std::string printer_name;
    300   bool ret = args->GetString(0, &printer_name);
    301   if (!ret || printer_name.empty())
    302     return;
    303 
    304   scoped_refptr<PrintSystemTaskProxy> task =
    305       new PrintSystemTaskProxy(AsWeakPtr(), print_backend_.get());
    306 
    307   BrowserThread::PostTask(
    308       BrowserThread::FILE, FROM_HERE,
    309       NewRunnableMethod(task.get(),
    310                         &PrintSystemTaskProxy::GetPrinterCapabilities,
    311                         printer_name));
    312 }
    313 
    314 void PrintPreviewHandler::SendPrinterCapabilities(
    315     const DictionaryValue& settings_info) {
    316   web_ui_->CallJavascriptFunction("updateWithPrinterCapabilities",
    317                                   settings_info);
    318 }
    319 
    320 void PrintPreviewHandler::SendPrinterList(
    321     const ListValue& printers,
    322     const FundamentalValue& default_printer_index) {
    323   web_ui_->CallJavascriptFunction("setPrinters", printers,
    324                                   default_printer_index);
    325 }
    326 
    327 void PrintPreviewHandler::SelectFile(const FilePath& default_filename) {
    328   SelectFileDialog::FileTypeInfo file_type_info;
    329   file_type_info.extensions.resize(1);
    330   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("pdf"));
    331 
    332   // Initializing last_saved_path_ if it is not already initialized.
    333   if (!last_saved_path_) {
    334     last_saved_path_ = new FilePath();
    335     // Allowing IO operation temporarily. It is ok to do so here because
    336     // the select file dialog performs IO anyway in order to display the
    337     // folders and also it is modal.
    338     base::ThreadRestrictions::ScopedAllowIO allow_io;
    339     PathService::Get(chrome::DIR_USER_DOCUMENTS, last_saved_path_);
    340   }
    341 
    342   if (!select_file_dialog_.get())
    343     select_file_dialog_ = SelectFileDialog::Create(this);
    344 
    345   select_file_dialog_->SelectFile(
    346       SelectFileDialog::SELECT_SAVEAS_FILE,
    347       string16(),
    348       last_saved_path_->Append(default_filename),
    349       &file_type_info,
    350       0,
    351       FILE_PATH_LITERAL(""),
    352       web_ui_->tab_contents(),
    353       platform_util::GetTopLevel(
    354           web_ui_->tab_contents()->GetNativeView()),
    355       NULL);
    356 }
    357 
    358 void PrintPreviewHandler::FileSelected(const FilePath& path,
    359                                        int index, void* params) {
    360   PrintPreviewUIHTMLSource::PrintPreviewData data;
    361   PrintPreviewUI* pp_ui = reinterpret_cast<PrintPreviewUI*>(web_ui_);
    362   pp_ui->html_source()->GetPrintPreviewData(&data);
    363   DCHECK(data.first != NULL);
    364   DCHECK(data.second > 0);
    365 
    366   printing::PreviewMetafile* metafile = new printing::PreviewMetafile;
    367   metafile->InitFromData(data.first->memory(), data.second);
    368 
    369   // Updating last_saved_path_ to the newly selected folder.
    370   *last_saved_path_ = path.DirName();
    371 
    372   PrintToPdfTask* task = new PrintToPdfTask(metafile, path);
    373   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, task);
    374 }
    375