Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / service / cloud_print / print_system_win.cc
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/service/cloud_print/print_system.h"
6
7 #include "base/command_line.h"
8 #include "base/files/file_util.h"
9 #include "base/json/json_writer.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/win/object_watcher.h"
13 #include "base/win/scoped_bstr.h"
14 #include "base/win/scoped_comptr.h"
15 #include "base/win/scoped_hdc.h"
16 #include "chrome/common/chrome_switches.h"
17 #include "chrome/common/cloud_print/cloud_print_cdd_conversion.h"
18 #include "chrome/common/cloud_print/cloud_print_constants.h"
19 #include "chrome/common/crash_keys.h"
20 #include "chrome/service/cloud_print/cdd_conversion_win.h"
21 #include "chrome/service/service_process.h"
22 #include "chrome/service/service_utility_process_host.h"
23 #include "printing/backend/win_helper.h"
24 #include "printing/emf_win.h"
25 #include "printing/page_range.h"
26 #include "printing/pdf_render_settings.h"
27 #include "printing/printing_utils.h"
28 #include "ui/gfx/geometry/rect.h"
29
30 namespace cloud_print {
31
32 namespace {
33
34 class PrintSystemWatcherWin : public base::win::ObjectWatcher::Delegate {
35  public:
36   PrintSystemWatcherWin()
37       : delegate_(NULL),
38         did_signal_(false) {
39   }
40   ~PrintSystemWatcherWin() {
41     Stop();
42   }
43
44   class Delegate {
45    public:
46     virtual ~Delegate() {}
47     virtual void OnPrinterAdded() = 0;
48     virtual void OnPrinterDeleted() = 0;
49     virtual void OnPrinterChanged() = 0;
50     virtual void OnJobChanged() = 0;
51   };
52
53   bool Start(const std::string& printer_name, Delegate* delegate) {
54     scoped_refptr<printing::PrintBackend> print_backend(
55         printing::PrintBackend::CreateInstance(NULL));
56     printer_info_ = print_backend->GetPrinterDriverInfo(printer_name);
57     crash_keys::ScopedPrinterInfo crash_key(printer_info_);
58
59     delegate_ = delegate;
60     // An empty printer name means watch the current server, we need to pass
61     // NULL to OpenPrinter.
62     LPTSTR printer_name_to_use = NULL;
63     std::wstring printer_name_wide;
64     if (!printer_name.empty()) {
65       printer_name_wide = base::UTF8ToWide(printer_name);
66       printer_name_to_use = const_cast<LPTSTR>(printer_name_wide.c_str());
67     }
68     bool ret = false;
69     if (printer_.OpenPrinter(printer_name_to_use)) {
70       printer_change_.Set(FindFirstPrinterChangeNotification(
71           printer_.Get(), PRINTER_CHANGE_PRINTER|PRINTER_CHANGE_JOB, 0, NULL));
72       if (printer_change_.IsValid()) {
73         ret = watcher_.StartWatching(printer_change_.Get(), this);
74       }
75     }
76     if (!ret) {
77       Stop();
78     }
79     return ret;
80   }
81
82   bool Stop() {
83     watcher_.StopWatching();
84     printer_.Close();
85     printer_change_.Close();
86     return true;
87   }
88
89   // base::ObjectWatcher::Delegate method
90   virtual void OnObjectSignaled(HANDLE object) {
91     crash_keys::ScopedPrinterInfo crash_key(printer_info_);
92     DWORD change = 0;
93     FindNextPrinterChangeNotification(object, &change, NULL, NULL);
94
95     if (change != ((PRINTER_CHANGE_PRINTER|PRINTER_CHANGE_JOB) &
96                   (~PRINTER_CHANGE_FAILED_CONNECTION_PRINTER))) {
97       // For printer connections, we get spurious change notifications with
98       // all flags set except PRINTER_CHANGE_FAILED_CONNECTION_PRINTER.
99       // Ignore these.
100       if (change & PRINTER_CHANGE_ADD_PRINTER) {
101         delegate_->OnPrinterAdded();
102       } else if (change & PRINTER_CHANGE_DELETE_PRINTER) {
103         delegate_->OnPrinterDeleted();
104       } else if (change & PRINTER_CHANGE_SET_PRINTER) {
105         delegate_->OnPrinterChanged();
106       }
107       if (change & PRINTER_CHANGE_JOB) {
108         delegate_->OnJobChanged();
109       }
110     }
111     watcher_.StartWatching(printer_change_.Get(), this);
112   }
113
114   bool GetCurrentPrinterInfo(printing::PrinterBasicInfo* printer_info) {
115     DCHECK(printer_info);
116     return InitBasicPrinterInfo(printer_.Get(), printer_info);
117   }
118
119  private:
120   base::win::ObjectWatcher watcher_;
121   printing::ScopedPrinterHandle printer_;  // The printer being watched
122   // Returned by FindFirstPrinterChangeNotifier.
123   printing::ScopedPrinterChangeHandle printer_change_;
124   Delegate* delegate_;           // Delegate to notify
125   bool did_signal_;              // DoneWaiting was called
126   std::string printer_info_;     // For crash reporting.
127 };
128
129 class PrintServerWatcherWin
130   : public PrintSystem::PrintServerWatcher,
131     public PrintSystemWatcherWin::Delegate {
132  public:
133   PrintServerWatcherWin() : delegate_(NULL) {}
134
135   // PrintSystem::PrintServerWatcher implementation.
136   virtual bool StartWatching(
137       PrintSystem::PrintServerWatcher::Delegate* delegate) OVERRIDE{
138     delegate_ = delegate;
139     return watcher_.Start(std::string(), this);
140   }
141
142   virtual bool StopWatching() OVERRIDE{
143     bool ret = watcher_.Stop();
144     delegate_ = NULL;
145     return ret;
146   }
147
148   // PrintSystemWatcherWin::Delegate implementation.
149   virtual void OnPrinterAdded() OVERRIDE {
150     delegate_->OnPrinterAdded();
151   }
152   virtual void OnPrinterDeleted() OVERRIDE {}
153   virtual void OnPrinterChanged() OVERRIDE {}
154   virtual void OnJobChanged() OVERRIDE {}
155
156  protected:
157   virtual ~PrintServerWatcherWin() {}
158
159  private:
160   PrintSystem::PrintServerWatcher::Delegate* delegate_;
161   PrintSystemWatcherWin watcher_;
162
163   DISALLOW_COPY_AND_ASSIGN(PrintServerWatcherWin);
164 };
165
166 class PrinterWatcherWin
167     : public PrintSystem::PrinterWatcher,
168       public PrintSystemWatcherWin::Delegate {
169  public:
170   explicit PrinterWatcherWin(const std::string& printer_name)
171       : printer_name_(printer_name),
172         delegate_(NULL) {
173   }
174
175   // PrintSystem::PrinterWatcher implementation.
176   virtual bool StartWatching(
177       PrintSystem::PrinterWatcher::Delegate* delegate) OVERRIDE {
178     delegate_ = delegate;
179     return watcher_.Start(printer_name_, this);
180   }
181
182   virtual bool StopWatching() OVERRIDE {
183     bool ret = watcher_.Stop();
184     delegate_ = NULL;
185     return ret;
186   }
187
188   virtual bool GetCurrentPrinterInfo(
189       printing::PrinterBasicInfo* printer_info) OVERRIDE {
190     return watcher_.GetCurrentPrinterInfo(printer_info);
191   }
192
193   // PrintSystemWatcherWin::Delegate implementation.
194   virtual void OnPrinterAdded() OVERRIDE {
195     NOTREACHED();
196   }
197   virtual void OnPrinterDeleted() OVERRIDE {
198     delegate_->OnPrinterDeleted();
199   }
200   virtual void OnPrinterChanged() OVERRIDE {
201     delegate_->OnPrinterChanged();
202   }
203   virtual void OnJobChanged() OVERRIDE {
204     delegate_->OnJobChanged();
205   }
206
207  protected:
208   virtual ~PrinterWatcherWin() {}
209
210  private:
211   std::string printer_name_;
212   PrintSystem::PrinterWatcher::Delegate* delegate_;
213   PrintSystemWatcherWin watcher_;
214
215   DISALLOW_COPY_AND_ASSIGN(PrinterWatcherWin);
216 };
217
218 class JobSpoolerWin : public PrintSystem::JobSpooler {
219  public:
220   JobSpoolerWin() : core_(new Core) {}
221
222   // PrintSystem::JobSpooler implementation.
223   virtual bool Spool(const std::string& print_ticket,
224                      const std::string& print_ticket_mime_type,
225                      const base::FilePath& print_data_file_path,
226                      const std::string& print_data_mime_type,
227                      const std::string& printer_name,
228                      const std::string& job_title,
229                      const std::vector<std::string>& tags,
230                      JobSpooler::Delegate* delegate) OVERRIDE {
231     // TODO(gene): add tags handling.
232     scoped_refptr<printing::PrintBackend> print_backend(
233         printing::PrintBackend::CreateInstance(NULL));
234     crash_keys::ScopedPrinterInfo crash_key(
235         print_backend->GetPrinterDriverInfo(printer_name));
236     return core_->Spool(print_ticket, print_ticket_mime_type,
237                         print_data_file_path, print_data_mime_type,
238                         printer_name, job_title, delegate);
239   }
240
241  protected:
242   virtual ~JobSpoolerWin() {}
243
244  private:
245   // We use a Core class because we want a separate RefCountedThreadSafe
246   // implementation for ServiceUtilityProcessHost::Client.
247   class Core : public ServiceUtilityProcessHost::Client,
248                public base::win::ObjectWatcher::Delegate {
249    public:
250     Core() : job_id_(-1), delegate_(NULL), saved_dc_(0) {}
251
252     ~Core() {}
253
254     bool Spool(const std::string& print_ticket,
255                const std::string& print_ticket_mime_type,
256                const base::FilePath& print_data_file_path,
257                const std::string& print_data_mime_type,
258                const std::string& printer_name,
259                const std::string& job_title,
260                JobSpooler::Delegate* delegate) {
261       if (delegate_) {
262         // We are already in the process of printing.
263         NOTREACHED();
264         return false;
265       }
266       base::string16 printer_wide = base::UTF8ToWide(printer_name);
267       // We only support PDF and XPS documents for now.
268       if (print_data_mime_type == kContentTypePDF) {
269         scoped_ptr<DEVMODE, base::FreeDeleter> dev_mode;
270         if (print_ticket_mime_type == kContentTypeJSON) {
271           dev_mode = CjtToDevMode(printer_wide, print_ticket);
272         } else {
273           DCHECK(print_ticket_mime_type == kContentTypeXML);
274           dev_mode = printing::XpsTicketToDevMode(printer_wide, print_ticket);
275         }
276
277         if (!dev_mode) {
278           NOTREACHED();
279           return false;
280         }
281
282         HDC dc = CreateDC(L"WINSPOOL", printer_wide.c_str(), NULL,
283                           dev_mode.get());
284         if (!dc) {
285           NOTREACHED();
286           return false;
287         }
288         DOCINFO di = {0};
289         di.cbSize = sizeof(DOCINFO);
290         base::string16 doc_name = base::UTF8ToUTF16(job_title);
291         DCHECK(printing::SimplifyDocumentTitle(doc_name) == doc_name);
292         di.lpszDocName = doc_name.c_str();
293         job_id_ = StartDoc(dc, &di);
294         if (job_id_ <= 0)
295           return false;
296
297         printer_dc_.Set(dc);
298         saved_dc_ = SaveDC(printer_dc_.Get());
299         print_data_file_path_ = print_data_file_path;
300         delegate_ = delegate;
301         RenderPDFPages();
302       } else if (print_data_mime_type == kContentTypeXPS) {
303         DCHECK(print_ticket_mime_type == kContentTypeXML);
304         bool ret = PrintXPSDocument(printer_name,
305                                     job_title,
306                                     print_data_file_path,
307                                     print_ticket);
308         if (ret)
309           delegate_ = delegate;
310         return ret;
311       } else {
312         NOTREACHED();
313         return false;
314       }
315       return true;
316     }
317
318     void PreparePageDCForPrinting(HDC, double scale_factor) {
319       SetGraphicsMode(printer_dc_.Get(), GM_ADVANCED);
320       // Setup the matrix to translate and scale to the right place. Take in
321       // account the scale factor.
322       // Note that the printing output is relative to printable area of
323       // the page. That is 0,0 is offset by PHYSICALOFFSETX/Y from the page.
324       int offset_x = ::GetDeviceCaps(printer_dc_.Get(), PHYSICALOFFSETX);
325       int offset_y = ::GetDeviceCaps(printer_dc_.Get(), PHYSICALOFFSETY);
326       XFORM xform = {0};
327       xform.eDx = static_cast<float>(-offset_x);
328       xform.eDy = static_cast<float>(-offset_y);
329       xform.eM11 = xform.eM22 = 1.0 / scale_factor;
330       SetWorldTransform(printer_dc_.Get(), &xform);
331     }
332
333     // ServiceUtilityProcessHost::Client implementation.
334     virtual void OnRenderPDFPagesToMetafilePageDone(
335         double scale_factor,
336         const printing::MetafilePlayer& emf) OVERRIDE {
337       PreparePageDCForPrinting(printer_dc_.Get(), scale_factor);
338       ::StartPage(printer_dc_.Get());
339       emf.SafePlayback(printer_dc_.Get());
340       ::EndPage(printer_dc_.Get());
341     }
342
343     // ServiceUtilityProcessHost::Client implementation.
344     virtual void OnRenderPDFPagesToMetafileDone(bool success) OVERRIDE {
345       PrintJobDone(success);
346     }
347
348     virtual void OnChildDied() OVERRIDE { PrintJobDone(false); }
349
350     // base::win::ObjectWatcher::Delegate implementation.
351     virtual void OnObjectSignaled(HANDLE object) OVERRIDE {
352       DCHECK(xps_print_job_);
353       DCHECK(object == job_progress_event_.Get());
354       ResetEvent(job_progress_event_.Get());
355       if (!delegate_)
356         return;
357       XPS_JOB_STATUS job_status = {0};
358       xps_print_job_->GetJobStatus(&job_status);
359       if ((job_status.completion == XPS_JOB_CANCELLED) ||
360           (job_status.completion == XPS_JOB_FAILED)) {
361         delegate_->OnJobSpoolFailed();
362       } else if (job_status.jobId ||
363                   (job_status.completion == XPS_JOB_COMPLETED)) {
364         // Note: In the case of the XPS document being printed to the
365         // Microsoft XPS Document Writer, it seems to skip spooling the job
366         // and goes to the completed state without ever assigning a job id.
367         delegate_->OnJobSpoolSucceeded(job_status.jobId);
368       } else {
369         job_progress_watcher_.StopWatching();
370         job_progress_watcher_.StartWatching(job_progress_event_.Get(), this);
371       }
372     }
373
374    private:
375     // Helper class to allow PrintXPSDocument() to have multiple exits.
376     class PrintJobCanceler {
377      public:
378       explicit PrintJobCanceler(
379           base::win::ScopedComPtr<IXpsPrintJob>* job_ptr)
380           : job_ptr_(job_ptr) {
381       }
382       ~PrintJobCanceler() {
383         if (job_ptr_ && *job_ptr_) {
384           (*job_ptr_)->Cancel();
385           job_ptr_->Release();
386         }
387       }
388
389       void reset() { job_ptr_ = NULL; }
390
391      private:
392       base::win::ScopedComPtr<IXpsPrintJob>* job_ptr_;
393
394       DISALLOW_COPY_AND_ASSIGN(PrintJobCanceler);
395     };
396
397     void PrintJobDone(bool success) {
398       // If there is no delegate, then there is nothing pending to process.
399       if (!delegate_)
400         return;
401       RestoreDC(printer_dc_.Get(), saved_dc_);
402       EndDoc(printer_dc_.Get());
403       if (success) {
404         delegate_->OnJobSpoolSucceeded(job_id_);
405       } else {
406         delegate_->OnJobSpoolFailed();
407       }
408       delegate_ = NULL;
409     }
410
411     void RenderPDFPages() {
412       int printer_dpi = ::GetDeviceCaps(printer_dc_.Get(), LOGPIXELSX);
413       int dc_width = GetDeviceCaps(printer_dc_.Get(), PHYSICALWIDTH);
414       int dc_height = GetDeviceCaps(printer_dc_.Get(), PHYSICALHEIGHT);
415       gfx::Rect render_area(0, 0, dc_width, dc_height);
416       g_service_process->io_thread()->message_loop_proxy()->PostTask(
417           FROM_HERE,
418           base::Bind(&JobSpoolerWin::Core::RenderPDFPagesInSandbox,
419                      this,
420                      print_data_file_path_,
421                      render_area,
422                      printer_dpi,
423                      base::MessageLoopProxy::current()));
424     }
425
426     // Called on the service process IO thread.
427     void RenderPDFPagesInSandbox(const base::FilePath& pdf_path,
428                                  const gfx::Rect& render_area,
429                                  int render_dpi,
430                                  const scoped_refptr<base::MessageLoopProxy>&
431                                      client_message_loop_proxy) {
432       DCHECK(g_service_process->io_thread()->message_loop_proxy()->
433           BelongsToCurrentThread());
434       scoped_ptr<ServiceUtilityProcessHost> utility_host(
435           new ServiceUtilityProcessHost(this, client_message_loop_proxy));
436       // TODO(gene): For now we disabling autorotation for CloudPrinting.
437       // Landscape/Portrait setting is passed in the print ticket and
438       // server is generating portrait PDF always.
439       // We should enable autorotation once server will be able to generate
440       // PDF that matches paper size and orientation.
441       if (utility_host->StartRenderPDFPagesToMetafile(
442               pdf_path,
443               printing::PdfRenderSettings(render_area, render_dpi, false))) {
444         // The object will self-destruct when the child process dies.
445         utility_host.release();
446       } else {
447         client_message_loop_proxy->PostTask(
448             FROM_HERE, base::Bind(&Core::PrintJobDone, this, false));
449       }
450     }
451
452     bool PrintXPSDocument(const std::string& printer_name,
453                           const std::string& job_title,
454                           const base::FilePath& print_data_file_path,
455                           const std::string& print_ticket) {
456       if (!printing::XPSPrintModule::Init())
457         return false;
458
459       job_progress_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL));
460       if (!job_progress_event_.Get())
461         return false;
462
463       PrintJobCanceler job_canceler(&xps_print_job_);
464       base::win::ScopedComPtr<IXpsPrintJobStream> doc_stream;
465       base::win::ScopedComPtr<IXpsPrintJobStream> print_ticket_stream;
466       if (FAILED(printing::XPSPrintModule::StartXpsPrintJob(
467               base::UTF8ToWide(printer_name).c_str(),
468               base::UTF8ToWide(job_title).c_str(),
469               NULL, job_progress_event_.Get(), NULL, NULL, NULL,
470               xps_print_job_.Receive(), doc_stream.Receive(),
471               print_ticket_stream.Receive())))
472         return false;
473
474       ULONG print_bytes_written = 0;
475       if (FAILED(print_ticket_stream->Write(print_ticket.c_str(),
476                                             print_ticket.length(),
477                                             &print_bytes_written)))
478         return false;
479       DCHECK_EQ(print_ticket.length(), print_bytes_written);
480       if (FAILED(print_ticket_stream->Close()))
481         return false;
482
483       std::string document_data;
484       base::ReadFileToString(print_data_file_path, &document_data);
485       ULONG doc_bytes_written = 0;
486       if (FAILED(doc_stream->Write(document_data.c_str(),
487                                     document_data.length(),
488                                     &doc_bytes_written)))
489         return false;
490       DCHECK_EQ(document_data.length(), doc_bytes_written);
491       if (FAILED(doc_stream->Close()))
492         return false;
493
494       job_progress_watcher_.StartWatching(job_progress_event_.Get(), this);
495       job_canceler.reset();
496       return true;
497     }
498
499     PlatformJobId job_id_;
500     PrintSystem::JobSpooler::Delegate* delegate_;
501     int saved_dc_;
502     base::win::ScopedCreateDC printer_dc_;
503     base::FilePath print_data_file_path_;
504     base::win::ScopedHandle job_progress_event_;
505     base::win::ObjectWatcher job_progress_watcher_;
506     base::win::ScopedComPtr<IXpsPrintJob> xps_print_job_;
507
508     DISALLOW_COPY_AND_ASSIGN(Core);
509   };
510   scoped_refptr<Core> core_;
511
512   DISALLOW_COPY_AND_ASSIGN(JobSpoolerWin);
513 };
514
515 // A helper class to handle the response from the utility process to the
516 // request to fetch printer capabilities and defaults.
517 class PrinterCapsHandler : public ServiceUtilityProcessHost::Client {
518  public:
519   PrinterCapsHandler(
520       const std::string& printer_name,
521       const PrintSystem::PrinterCapsAndDefaultsCallback& callback)
522           : printer_name_(printer_name), callback_(callback) {
523   }
524
525   // ServiceUtilityProcessHost::Client implementation.
526   virtual void OnChildDied() OVERRIDE {
527     OnGetPrinterCapsAndDefaults(false, printer_name_,
528                                 printing::PrinterCapsAndDefaults());
529   }
530
531   virtual void OnGetPrinterCapsAndDefaults(
532       bool succeeded,
533       const std::string& printer_name,
534       const printing::PrinterCapsAndDefaults& caps_and_defaults) OVERRIDE {
535     callback_.Run(succeeded, printer_name, caps_and_defaults);
536     callback_.Reset();
537     Release();
538   }
539
540   virtual void OnGetPrinterSemanticCapsAndDefaults(
541       bool succeeded,
542       const std::string& printer_name,
543       const printing::PrinterSemanticCapsAndDefaults& semantic_info) OVERRIDE {
544     printing::PrinterCapsAndDefaults printer_info;
545     if (succeeded) {
546       printer_info.caps_mime_type = kContentTypeJSON;
547       scoped_ptr<base::DictionaryValue> description(
548           PrinterSemanticCapsAndDefaultsToCdd(semantic_info));
549       if (description) {
550         base::JSONWriter::WriteWithOptions(
551             description.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT,
552             &printer_info.printer_capabilities);
553       }
554     }
555     callback_.Run(succeeded, printer_name, printer_info);
556     callback_.Reset();
557     Release();
558   }
559
560   void StartGetPrinterCapsAndDefaults() {
561     g_service_process->io_thread()->message_loop_proxy()->PostTask(
562         FROM_HERE,
563         base::Bind(&PrinterCapsHandler::GetPrinterCapsAndDefaultsImpl, this,
564                     base::MessageLoopProxy::current()));
565   }
566
567   void StartGetPrinterSemanticCapsAndDefaults() {
568     g_service_process->io_thread()->message_loop_proxy()->PostTask(
569         FROM_HERE,
570         base::Bind(&PrinterCapsHandler::GetPrinterSemanticCapsAndDefaultsImpl,
571                    this, base::MessageLoopProxy::current()));
572   }
573
574  private:
575   void GetPrinterCapsAndDefaultsImpl(
576       const scoped_refptr<base::MessageLoopProxy>&
577           client_message_loop_proxy) {
578     DCHECK(g_service_process->io_thread()->message_loop_proxy()->
579         BelongsToCurrentThread());
580     scoped_ptr<ServiceUtilityProcessHost> utility_host(
581         new ServiceUtilityProcessHost(this, client_message_loop_proxy));
582     if (utility_host->StartGetPrinterCapsAndDefaults(printer_name_)) {
583       // The object will self-destruct when the child process dies.
584       utility_host.release();
585     } else {
586       client_message_loop_proxy->PostTask(
587           FROM_HERE,
588           base::Bind(&PrinterCapsHandler::OnChildDied, this));
589     }
590   }
591
592   void GetPrinterSemanticCapsAndDefaultsImpl(
593       const scoped_refptr<base::MessageLoopProxy>&
594           client_message_loop_proxy) {
595     DCHECK(g_service_process->io_thread()->message_loop_proxy()->
596         BelongsToCurrentThread());
597     scoped_ptr<ServiceUtilityProcessHost> utility_host(
598         new ServiceUtilityProcessHost(this, client_message_loop_proxy));
599     if (utility_host->StartGetPrinterSemanticCapsAndDefaults(printer_name_)) {
600       // The object will self-destruct when the child process dies.
601       utility_host.release();
602     } else {
603       client_message_loop_proxy->PostTask(
604           FROM_HERE,
605           base::Bind(&PrinterCapsHandler::OnChildDied, this));
606     }
607   }
608
609   std::string printer_name_;
610   PrintSystem::PrinterCapsAndDefaultsCallback callback_;
611 };
612
613 class PrintSystemWin : public PrintSystem {
614  public:
615   PrintSystemWin();
616
617   // PrintSystem implementation.
618   virtual PrintSystemResult Init() OVERRIDE;
619   virtual PrintSystem::PrintSystemResult EnumeratePrinters(
620       printing::PrinterList* printer_list) OVERRIDE;
621   virtual void GetPrinterCapsAndDefaults(
622       const std::string& printer_name,
623       const PrinterCapsAndDefaultsCallback& callback) OVERRIDE;
624   virtual bool IsValidPrinter(const std::string& printer_name) OVERRIDE;
625   virtual bool ValidatePrintTicket(
626       const std::string& printer_name,
627       const std::string& print_ticket_data,
628       const std::string& print_ticket_data_mime_type) OVERRIDE;
629   virtual bool GetJobDetails(const std::string& printer_name,
630                              PlatformJobId job_id,
631                              PrintJobDetails *job_details) OVERRIDE;
632   virtual PrintSystem::PrintServerWatcher* CreatePrintServerWatcher() OVERRIDE;
633   virtual PrintSystem::PrinterWatcher* CreatePrinterWatcher(
634       const std::string& printer_name) OVERRIDE;
635   virtual PrintSystem::JobSpooler* CreateJobSpooler() OVERRIDE;
636   virtual bool UseCddAndCjt() OVERRIDE;
637   virtual std::string GetSupportedMimeTypes() OVERRIDE;
638
639  private:
640   std::string PrintSystemWin::GetPrinterDriverInfo(
641       const std::string& printer_name) const;
642
643   scoped_refptr<printing::PrintBackend> print_backend_;
644   bool use_cdd_;
645   DISALLOW_COPY_AND_ASSIGN(PrintSystemWin);
646 };
647
648 PrintSystemWin::PrintSystemWin() : use_cdd_(true) {
649   print_backend_ = printing::PrintBackend::CreateInstance(NULL);
650 }
651
652 PrintSystem::PrintSystemResult PrintSystemWin::Init() {
653   use_cdd_ = !CommandLine::ForCurrentProcess()->HasSwitch(
654       switches::kEnableCloudPrintXps);
655
656   if (!use_cdd_)
657     use_cdd_ = !printing::XPSModule::Init();
658
659   if (!use_cdd_) {
660     HPTPROVIDER provider = NULL;
661     HRESULT hr = printing::XPSModule::OpenProvider(L"", 1, &provider);
662     if (provider)
663       printing::XPSModule::CloseProvider(provider);
664     // Use cdd if error is different from expected.
665     use_cdd_ = (hr != HRESULT_FROM_WIN32(ERROR_INVALID_PRINTER_NAME));
666   }
667
668   return PrintSystemResult(true, std::string());
669 }
670
671 PrintSystem::PrintSystemResult PrintSystemWin::EnumeratePrinters(
672     printing::PrinterList* printer_list) {
673   bool ret = print_backend_->EnumeratePrinters(printer_list);
674   return PrintSystemResult(ret, std::string());
675 }
676
677 void PrintSystemWin::GetPrinterCapsAndDefaults(
678     const std::string& printer_name,
679     const PrinterCapsAndDefaultsCallback& callback) {
680   // Launch as child process to retrieve the capabilities and defaults because
681   // this involves invoking a printer driver DLL and crashes have been known to
682   // occur.
683   PrinterCapsHandler* handler = new PrinterCapsHandler(printer_name, callback);
684   handler->AddRef();
685   if (use_cdd_)
686     handler->StartGetPrinterSemanticCapsAndDefaults();
687   else
688     handler->StartGetPrinterCapsAndDefaults();
689 }
690
691 bool PrintSystemWin::IsValidPrinter(const std::string& printer_name) {
692   return print_backend_->IsValidPrinter(printer_name);
693 }
694
695 bool PrintSystemWin::ValidatePrintTicket(
696     const std::string& printer_name,
697     const std::string& print_ticket_data,
698     const std::string& print_ticket_data_mime_type) {
699   crash_keys::ScopedPrinterInfo crash_key(GetPrinterDriverInfo(printer_name));
700
701   if (use_cdd_) {
702     return print_ticket_data_mime_type == kContentTypeJSON &&
703            IsValidCjt(print_ticket_data);
704   }
705   DCHECK(print_ticket_data_mime_type == kContentTypeXML);
706
707   printing::ScopedXPSInitializer xps_initializer;
708   if (!xps_initializer.initialized()) {
709     // TODO(sanjeevr): Handle legacy proxy case (with no prntvpt.dll)
710     return false;
711   }
712   bool ret = false;
713   HPTPROVIDER provider = NULL;
714   printing::XPSModule::OpenProvider(base::UTF8ToWide(printer_name), 1,
715                                     &provider);
716   if (provider) {
717     base::win::ScopedComPtr<IStream> print_ticket_stream;
718     CreateStreamOnHGlobal(NULL, TRUE, print_ticket_stream.Receive());
719     ULONG bytes_written = 0;
720     print_ticket_stream->Write(print_ticket_data.c_str(),
721                                print_ticket_data.length(),
722                                &bytes_written);
723     DCHECK(bytes_written == print_ticket_data.length());
724     LARGE_INTEGER pos = {0};
725     ULARGE_INTEGER new_pos = {0};
726     print_ticket_stream->Seek(pos, STREAM_SEEK_SET, &new_pos);
727     base::win::ScopedBstr error;
728     base::win::ScopedComPtr<IStream> result_ticket_stream;
729     CreateStreamOnHGlobal(NULL, TRUE, result_ticket_stream.Receive());
730     ret = SUCCEEDED(printing::XPSModule::MergeAndValidatePrintTicket(
731         provider,
732         print_ticket_stream.get(),
733         NULL,
734         kPTJobScope,
735         result_ticket_stream.get(),
736         error.Receive()));
737     printing::XPSModule::CloseProvider(provider);
738   }
739   return ret;
740 }
741
742 bool PrintSystemWin::GetJobDetails(const std::string& printer_name,
743                                    PlatformJobId job_id,
744                                    PrintJobDetails *job_details) {
745   crash_keys::ScopedPrinterInfo crash_key(
746       print_backend_->GetPrinterDriverInfo(printer_name));
747   DCHECK(job_details);
748   printing::ScopedPrinterHandle printer_handle;
749   std::wstring printer_name_wide = base::UTF8ToWide(printer_name);
750   printer_handle.OpenPrinter(printer_name_wide.c_str());
751   DCHECK(printer_handle.IsValid());
752   bool ret = false;
753   if (printer_handle.IsValid()) {
754     DWORD bytes_needed = 0;
755     GetJob(printer_handle.Get(), job_id, 1, NULL, 0, &bytes_needed);
756     DWORD last_error = GetLastError();
757     if (ERROR_INVALID_PARAMETER != last_error) {
758       // ERROR_INVALID_PARAMETER normally means that the job id is not valid.
759       DCHECK(last_error == ERROR_INSUFFICIENT_BUFFER);
760       scoped_ptr<BYTE[]> job_info_buffer(new BYTE[bytes_needed]);
761       if (GetJob(printer_handle.Get(), job_id, 1, job_info_buffer.get(),
762                  bytes_needed, &bytes_needed)) {
763         JOB_INFO_1 *job_info =
764             reinterpret_cast<JOB_INFO_1 *>(job_info_buffer.get());
765         if (job_info->pStatus) {
766           base::WideToUTF8(job_info->pStatus, wcslen(job_info->pStatus),
767                            &job_details->status_message);
768         }
769         job_details->platform_status_flags = job_info->Status;
770         if ((job_info->Status & JOB_STATUS_COMPLETE) ||
771             (job_info->Status & JOB_STATUS_PRINTED)) {
772           job_details->status = PRINT_JOB_STATUS_COMPLETED;
773         } else if (job_info->Status & JOB_STATUS_ERROR) {
774           job_details->status = PRINT_JOB_STATUS_ERROR;
775         } else {
776           job_details->status = PRINT_JOB_STATUS_IN_PROGRESS;
777         }
778         job_details->total_pages = job_info->TotalPages;
779         job_details->pages_printed = job_info->PagesPrinted;
780         ret = true;
781       }
782     }
783   }
784   return ret;
785 }
786
787 PrintSystem::PrintServerWatcher*
788 PrintSystemWin::CreatePrintServerWatcher() {
789   return new PrintServerWatcherWin();
790 }
791
792 PrintSystem::PrinterWatcher* PrintSystemWin::CreatePrinterWatcher(
793     const std::string& printer_name) {
794   DCHECK(!printer_name.empty());
795   return new PrinterWatcherWin(printer_name);
796 }
797
798 PrintSystem::JobSpooler* PrintSystemWin::CreateJobSpooler() {
799   return new JobSpoolerWin();
800 }
801
802 bool PrintSystemWin::UseCddAndCjt() {
803   return use_cdd_;
804 }
805
806 std::string PrintSystemWin::GetSupportedMimeTypes() {
807   std::string result;
808   if (!use_cdd_) {
809     result = kContentTypeXPS;
810     result += ",";
811   }
812   result += kContentTypePDF;
813   return result;
814 }
815
816 std::string PrintSystemWin::GetPrinterDriverInfo(
817     const std::string& printer_name) const {
818   return print_backend_->GetPrinterDriverInfo(printer_name);
819 }
820
821 }  // namespace
822
823 scoped_refptr<PrintSystem> PrintSystem::CreateInstance(
824     const base::DictionaryValue* print_system_settings) {
825   return new PrintSystemWin;
826 }
827
828 }  // namespace cloud_print