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