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