• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetInitiatorTab(TabContents * preview_tab)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.
GetSettingsDictionary(const ListValue * args)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:
PrintSystemTaskProxy(const base::WeakPtr<PrintPreviewHandler> & handler,printing::PrintBackend * print_backend)90   PrintSystemTaskProxy(const base::WeakPtr<PrintPreviewHandler>& handler,
91                              printing::PrintBackend* print_backend)
92       : handler_(handler),
93         print_backend_(print_backend) {
94   }
95 
EnumeratePrinters()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 
SendPrinterList(ListValue * printers,FundamentalValue * default_printer_index)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 
GetPrinterCapabilities(const std::string & printer_name)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 
SendPrinterCapabilities(DictionaryValue * settings_info)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 
~PrintSystemTaskProxy()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|.
PrintToPdfTask(printing::Metafile * metafile,const FilePath & path)193   PrintToPdfTask(printing::Metafile* metafile, const FilePath& path)
194       : metafile_(metafile), path_(path) {
195   }
196 
~PrintToPdfTask()197   ~PrintToPdfTask() {}
198 
199   // Task implementation
Run()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 
PrintPreviewHandler()215 PrintPreviewHandler::PrintPreviewHandler()
216     : print_backend_(printing::PrintBackend::CreateInstance(NULL)) {
217 }
218 
~PrintPreviewHandler()219 PrintPreviewHandler::~PrintPreviewHandler() {
220   if (select_file_dialog_.get())
221     select_file_dialog_->ListenerDestroyed();
222 }
223 
RegisterMessages()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 
HandleGetPrinters(const ListValue *)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 
HandleGetPreview(const ListValue * args)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 
HandlePrint(const ListValue * args)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 
HandleGetPrinterCapabilities(const ListValue * args)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 
SendPrinterCapabilities(const DictionaryValue & settings_info)314 void PrintPreviewHandler::SendPrinterCapabilities(
315     const DictionaryValue& settings_info) {
316   web_ui_->CallJavascriptFunction("updateWithPrinterCapabilities",
317                                   settings_info);
318 }
319 
SendPrinterList(const ListValue & printers,const FundamentalValue & default_printer_index)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 
SelectFile(const FilePath & default_filename)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 
FileSelected(const FilePath & path,int index,void * params)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