Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / service / cloud_print / print_system_win.cc
index f46c2af..8d585d3 100644 (file)
@@ -2,12 +2,26 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/service/cloud_print/print_system_win.h"
+#include "chrome/service/cloud_print/print_system.h"
 
+#include "base/file_util.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/win/object_watcher.h"
+#include "base/win/scoped_bstr.h"
+#include "base/win/scoped_comptr.h"
+#include "base/win/scoped_hdc.h"
+#include "chrome/common/cloud_print/cloud_print_constants.h"
 #include "chrome/common/crash_keys.h"
+#include "chrome/service/cloud_print/cdd_conversion_win.h"
+#include "chrome/service/service_process.h"
+#include "chrome/service/service_utility_process_host.h"
+#include "grit/generated_resources.h"
 #include "printing/backend/win_helper.h"
+#include "printing/emf_win.h"
+#include "printing/page_range.h"
+#include "printing/printing_utils.h"
+#include "ui/base/l10n/l10n_util.h"
 
 namespace cloud_print {
 
@@ -197,13 +211,470 @@ class PrinterWatcherWin
   DISALLOW_COPY_AND_ASSIGN(PrinterWatcherWin);
 };
 
-}  // namespace
+class JobSpoolerWin : public PrintSystem::JobSpooler {
+ public:
+  JobSpoolerWin() : core_(new Core) {}
+
+  // PrintSystem::JobSpooler implementation.
+  virtual bool Spool(const std::string& print_ticket,
+                     const std::string& print_ticket_mime_type,
+                     const base::FilePath& print_data_file_path,
+                     const std::string& print_data_mime_type,
+                     const std::string& printer_name,
+                     const std::string& job_title,
+                     const std::vector<std::string>& tags,
+                     JobSpooler::Delegate* delegate) OVERRIDE {
+    // TODO(gene): add tags handling.
+    scoped_refptr<printing::PrintBackend> print_backend(
+        printing::PrintBackend::CreateInstance(NULL));
+    crash_keys::ScopedPrinterInfo crash_key(
+        print_backend->GetPrinterDriverInfo(printer_name));
+    return core_->Spool(print_ticket, print_ticket_mime_type,
+                        print_data_file_path, print_data_mime_type,
+                        printer_name, job_title, delegate);
+  }
+
+ protected:
+  virtual ~JobSpoolerWin() {}
+
+ private:
+  // We use a Core class because we want a separate RefCountedThreadSafe
+  // implementation for ServiceUtilityProcessHost::Client.
+  class Core : public ServiceUtilityProcessHost::Client,
+               public base::win::ObjectWatcher::Delegate {
+   public:
+    Core()
+        : last_page_printed_(-1),
+          job_id_(-1),
+          delegate_(NULL),
+          saved_dc_(0) {
+    }
+
+    ~Core() {}
+
+    bool Spool(const std::string& print_ticket,
+               const std::string& print_ticket_mime_type,
+               const base::FilePath& print_data_file_path,
+               const std::string& print_data_mime_type,
+               const std::string& printer_name,
+               const std::string& job_title,
+               JobSpooler::Delegate* delegate) {
+      if (delegate_) {
+        // We are already in the process of printing.
+        NOTREACHED();
+        return false;
+      }
+      base::string16 printer_wide = base::UTF8ToWide(printer_name);
+      last_page_printed_ = -1;
+      // We only support PDF and XPS documents for now.
+      if (print_data_mime_type == kContentTypePDF) {
+        scoped_ptr<DEVMODE[]> dev_mode;
+        if (print_ticket_mime_type == kContentTypeJSON) {
+          dev_mode = CjtToDevMode(printer_wide, print_ticket);
+        } else {
+          DCHECK(print_ticket_mime_type == kContentTypeXML);
+          dev_mode = printing::XpsTicketToDevMode(printer_wide, print_ticket);
+        }
+
+        if (!dev_mode) {
+          NOTREACHED();
+          return false;
+        }
+
+        HDC dc = CreateDC(L"WINSPOOL", printer_wide.c_str(), NULL,
+                          dev_mode.get());
+        if (!dc) {
+          NOTREACHED();
+          return false;
+        }
+        DOCINFO di = {0};
+        di.cbSize = sizeof(DOCINFO);
+        base::string16 doc_name = base::UTF8ToUTF16(job_title);
+        DCHECK(printing::SimplifyDocumentTitle(doc_name) == doc_name);
+        di.lpszDocName = doc_name.c_str();
+        job_id_ = StartDoc(dc, &di);
+        if (job_id_ <= 0)
+          return false;
+
+        printer_dc_.Set(dc);
+        saved_dc_ = SaveDC(printer_dc_.Get());
+        print_data_file_path_ = print_data_file_path;
+        delegate_ = delegate;
+        RenderNextPDFPages();
+      } else if (print_data_mime_type == kContentTypeXPS) {
+        DCHECK(print_ticket_mime_type == kContentTypeXML);
+        bool ret = PrintXPSDocument(printer_name,
+                                    job_title,
+                                    print_data_file_path,
+                                    print_ticket);
+        if (ret)
+          delegate_ = delegate;
+        return ret;
+      } else {
+        NOTREACHED();
+        return false;
+      }
+      return true;
+    }
+
+    void PreparePageDCForPrinting(HDC, double scale_factor) {
+      SetGraphicsMode(printer_dc_.Get(), GM_ADVANCED);
+      // Setup the matrix to translate and scale to the right place. Take in
+      // account the scale factor.
+      // Note that the printing output is relative to printable area of
+      // the page. That is 0,0 is offset by PHYSICALOFFSETX/Y from the page.
+      int offset_x = ::GetDeviceCaps(printer_dc_.Get(), PHYSICALOFFSETX);
+      int offset_y = ::GetDeviceCaps(printer_dc_.Get(), PHYSICALOFFSETY);
+      XFORM xform = {0};
+      xform.eDx = static_cast<float>(-offset_x);
+      xform.eDy = static_cast<float>(-offset_y);
+      xform.eM11 = xform.eM22 = 1.0 / scale_factor;
+      SetWorldTransform(printer_dc_.Get(), &xform);
+    }
+
+    // ServiceUtilityProcessHost::Client implementation.
+    virtual void OnRenderPDFPagesToMetafileSucceeded(
+        const printing::Emf& metafile,
+        int highest_rendered_page_number,
+        double scale_factor) OVERRIDE {
+      PreparePageDCForPrinting(printer_dc_.Get(), scale_factor);
+      metafile.SafePlayback(printer_dc_.Get());
+      bool done_printing = (highest_rendered_page_number !=
+          last_page_printed_ + kPageCountPerBatch);
+      last_page_printed_ = highest_rendered_page_number;
+      if (done_printing)
+        PrintJobDone();
+      else
+        RenderNextPDFPages();
+    }
+
+    // base::win::ObjectWatcher::Delegate implementation.
+    virtual void OnObjectSignaled(HANDLE object) OVERRIDE {
+      DCHECK(xps_print_job_);
+      DCHECK(object == job_progress_event_.Get());
+      ResetEvent(job_progress_event_.Get());
+      if (!delegate_)
+        return;
+      XPS_JOB_STATUS job_status = {0};
+      xps_print_job_->GetJobStatus(&job_status);
+      if ((job_status.completion == XPS_JOB_CANCELLED) ||
+          (job_status.completion == XPS_JOB_FAILED)) {
+        delegate_->OnJobSpoolFailed();
+      } else if (job_status.jobId ||
+                  (job_status.completion == XPS_JOB_COMPLETED)) {
+        // Note: In the case of the XPS document being printed to the
+        // Microsoft XPS Document Writer, it seems to skip spooling the job
+        // and goes to the completed state without ever assigning a job id.
+        delegate_->OnJobSpoolSucceeded(job_status.jobId);
+      } else {
+        job_progress_watcher_.StopWatching();
+        job_progress_watcher_.StartWatching(job_progress_event_.Get(), this);
+      }
+    }
 
-PrintSystemWin::PrintSystemWin() {
+    virtual void OnRenderPDFPagesToMetafileFailed() OVERRIDE {
+      PrintJobDone();
+    }
+
+    virtual void OnChildDied() OVERRIDE {
+      PrintJobDone();
+    }
+
+   private:
+    // Helper class to allow PrintXPSDocument() to have multiple exits.
+    class PrintJobCanceler {
+     public:
+      explicit PrintJobCanceler(
+          base::win::ScopedComPtr<IXpsPrintJob>* job_ptr)
+          : job_ptr_(job_ptr) {
+      }
+      ~PrintJobCanceler() {
+        if (job_ptr_ && *job_ptr_) {
+          (*job_ptr_)->Cancel();
+          job_ptr_->Release();
+        }
+      }
+
+      void reset() { job_ptr_ = NULL; }
+
+     private:
+      base::win::ScopedComPtr<IXpsPrintJob>* job_ptr_;
+
+      DISALLOW_COPY_AND_ASSIGN(PrintJobCanceler);
+    };
+
+    void PrintJobDone() {
+      // If there is no delegate, then there is nothing pending to process.
+      if (!delegate_)
+        return;
+      RestoreDC(printer_dc_.Get(), saved_dc_);
+      EndDoc(printer_dc_.Get());
+      if (-1 == last_page_printed_) {
+        delegate_->OnJobSpoolFailed();
+      } else {
+        delegate_->OnJobSpoolSucceeded(job_id_);
+      }
+      delegate_ = NULL;
+    }
+
+    void RenderNextPDFPages() {
+      printing::PageRange range;
+      // Render 10 pages at a time.
+      range.from = last_page_printed_ + 1;
+      range.to = last_page_printed_ + kPageCountPerBatch;
+      std::vector<printing::PageRange> page_ranges;
+      page_ranges.push_back(range);
+
+      int printer_dpi = ::GetDeviceCaps(printer_dc_.Get(), LOGPIXELSX);
+      int dc_width = GetDeviceCaps(printer_dc_.Get(), PHYSICALWIDTH);
+      int dc_height = GetDeviceCaps(printer_dc_.Get(), PHYSICALHEIGHT);
+      gfx::Rect render_area(0, 0, dc_width, dc_height);
+      g_service_process->io_thread()->message_loop_proxy()->PostTask(
+          FROM_HERE,
+          base::Bind(&JobSpoolerWin::Core::RenderPDFPagesInSandbox, this,
+                      print_data_file_path_, render_area, printer_dpi,
+                      page_ranges, base::MessageLoopProxy::current()));
+    }
+
+    // Called on the service process IO thread.
+    void RenderPDFPagesInSandbox(
+        const base::FilePath& pdf_path, const gfx::Rect& render_area,
+        int render_dpi, const std::vector<printing::PageRange>& page_ranges,
+        const scoped_refptr<base::MessageLoopProxy>&
+            client_message_loop_proxy) {
+      DCHECK(g_service_process->io_thread()->message_loop_proxy()->
+          BelongsToCurrentThread());
+      scoped_ptr<ServiceUtilityProcessHost> utility_host(
+          new ServiceUtilityProcessHost(this, client_message_loop_proxy));
+      // TODO(gene): For now we disabling autorotation for CloudPrinting.
+      // Landscape/Portrait setting is passed in the print ticket and
+      // server is generating portrait PDF always.
+      // We should enable autorotation once server will be able to generate
+      // PDF that matches paper size and orientation.
+      if (utility_host->StartRenderPDFPagesToMetafile(
+              pdf_path,
+              printing::PdfRenderSettings(render_area, render_dpi, false),
+              page_ranges)) {
+        // The object will self-destruct when the child process dies.
+        utility_host.release();
+      }
+    }
+
+    bool PrintXPSDocument(const std::string& printer_name,
+                          const std::string& job_title,
+                          const base::FilePath& print_data_file_path,
+                          const std::string& print_ticket) {
+      if (!printing::XPSPrintModule::Init())
+        return false;
+
+      job_progress_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL));
+      if (!job_progress_event_.Get())
+        return false;
+
+      PrintJobCanceler job_canceler(&xps_print_job_);
+      base::win::ScopedComPtr<IXpsPrintJobStream> doc_stream;
+      base::win::ScopedComPtr<IXpsPrintJobStream> print_ticket_stream;
+      if (FAILED(printing::XPSPrintModule::StartXpsPrintJob(
+              base::UTF8ToWide(printer_name).c_str(),
+              base::UTF8ToWide(job_title).c_str(),
+              NULL, job_progress_event_.Get(), NULL, NULL, NULL,
+              xps_print_job_.Receive(), doc_stream.Receive(),
+              print_ticket_stream.Receive())))
+        return false;
+
+      ULONG print_bytes_written = 0;
+      if (FAILED(print_ticket_stream->Write(print_ticket.c_str(),
+                                            print_ticket.length(),
+                                            &print_bytes_written)))
+        return false;
+      DCHECK_EQ(print_ticket.length(), print_bytes_written);
+      if (FAILED(print_ticket_stream->Close()))
+        return false;
+
+      std::string document_data;
+      base::ReadFileToString(print_data_file_path, &document_data);
+      ULONG doc_bytes_written = 0;
+      if (FAILED(doc_stream->Write(document_data.c_str(),
+                                    document_data.length(),
+                                    &doc_bytes_written)))
+        return false;
+      DCHECK_EQ(document_data.length(), doc_bytes_written);
+      if (FAILED(doc_stream->Close()))
+        return false;
+
+      job_progress_watcher_.StartWatching(job_progress_event_.Get(), this);
+      job_canceler.reset();
+      return true;
+    }
+
+    // Some Cairo-generated PDFs from Chrome OS result in huge metafiles.
+    // So the PageCountPerBatch is set to 1 for now.
+    // TODO(sanjeevr): Figure out a smarter way to determine the pages per
+    // batch. Filed a bug to track this at
+    // http://code.google.com/p/chromium/issues/detail?id=57350.
+    static const int kPageCountPerBatch = 1;
+
+    int last_page_printed_;
+    PlatformJobId job_id_;
+    PrintSystem::JobSpooler::Delegate* delegate_;
+    int saved_dc_;
+    base::win::ScopedCreateDC printer_dc_;
+    base::FilePath print_data_file_path_;
+    base::win::ScopedHandle job_progress_event_;
+    base::win::ObjectWatcher job_progress_watcher_;
+    base::win::ScopedComPtr<IXpsPrintJob> xps_print_job_;
+
+    DISALLOW_COPY_AND_ASSIGN(Core);
+  };
+  scoped_refptr<Core> core_;
+
+  DISALLOW_COPY_AND_ASSIGN(JobSpoolerWin);
+};
+
+// A helper class to handle the response from the utility process to the
+// request to fetch printer capabilities and defaults.
+class PrinterCapsHandler : public ServiceUtilityProcessHost::Client {
+ public:
+  PrinterCapsHandler(
+      const std::string& printer_name,
+      const PrintSystem::PrinterCapsAndDefaultsCallback& callback)
+          : printer_name_(printer_name), callback_(callback) {
+  }
+
+  // ServiceUtilityProcessHost::Client implementation.
+  virtual void OnChildDied() OVERRIDE {
+    OnGetPrinterCapsAndDefaults(false, printer_name_,
+                                printing::PrinterCapsAndDefaults());
+  }
+
+  virtual void OnGetPrinterCapsAndDefaults(
+      bool succeeded,
+      const std::string& printer_name,
+      const printing::PrinterCapsAndDefaults& caps_and_defaults) OVERRIDE {
+    callback_.Run(succeeded, printer_name, caps_and_defaults);
+    callback_.Reset();
+    Release();
+  }
+
+  virtual void OnGetPrinterSemanticCapsAndDefaults(
+      bool succeeded,
+      const std::string& printer_name,
+      const printing::PrinterSemanticCapsAndDefaults& semantic_info) OVERRIDE {
+    printing::PrinterCapsAndDefaults printer_info;
+    if (succeeded) {
+      printer_info.caps_mime_type = kContentTypeJSON;
+      printer_info.printer_capabilities = CapabilitiesToCdd(semantic_info);
+    }
+    callback_.Run(succeeded, printer_name, printer_info);
+    callback_.Reset();
+    Release();
+  }
+
+  void StartGetPrinterCapsAndDefaults() {
+    g_service_process->io_thread()->message_loop_proxy()->PostTask(
+        FROM_HERE,
+        base::Bind(&PrinterCapsHandler::GetPrinterCapsAndDefaultsImpl, this,
+                    base::MessageLoopProxy::current()));
+  }
+
+  void StartGetPrinterSemanticCapsAndDefaults() {
+    g_service_process->io_thread()->message_loop_proxy()->PostTask(
+        FROM_HERE,
+        base::Bind(&PrinterCapsHandler::GetPrinterSemanticCapsAndDefaultsImpl,
+                   this, base::MessageLoopProxy::current()));
+  }
+
+ private:
+  void GetPrinterCapsAndDefaultsImpl(
+      const scoped_refptr<base::MessageLoopProxy>&
+          client_message_loop_proxy) {
+    DCHECK(g_service_process->io_thread()->message_loop_proxy()->
+        BelongsToCurrentThread());
+    scoped_ptr<ServiceUtilityProcessHost> utility_host(
+        new ServiceUtilityProcessHost(this, client_message_loop_proxy));
+    if (utility_host->StartGetPrinterCapsAndDefaults(printer_name_)) {
+      // The object will self-destruct when the child process dies.
+      utility_host.release();
+    } else {
+      client_message_loop_proxy->PostTask(
+          FROM_HERE,
+          base::Bind(&PrinterCapsHandler::OnChildDied, this));
+    }
+  }
+
+  void GetPrinterSemanticCapsAndDefaultsImpl(
+      const scoped_refptr<base::MessageLoopProxy>&
+          client_message_loop_proxy) {
+    DCHECK(g_service_process->io_thread()->message_loop_proxy()->
+        BelongsToCurrentThread());
+    scoped_ptr<ServiceUtilityProcessHost> utility_host(
+        new ServiceUtilityProcessHost(this, client_message_loop_proxy));
+    if (utility_host->StartGetPrinterSemanticCapsAndDefaults(printer_name_)) {
+      // The object will self-destruct when the child process dies.
+      utility_host.release();
+    } else {
+      client_message_loop_proxy->PostTask(
+          FROM_HERE,
+          base::Bind(&PrinterCapsHandler::OnChildDied, this));
+    }
+  }
+
+  std::string printer_name_;
+  PrintSystem::PrinterCapsAndDefaultsCallback callback_;
+};
+
+class PrintSystemWin : public PrintSystem {
+ public:
+  PrintSystemWin();
+
+  // PrintSystem implementation.
+  virtual PrintSystemResult Init() OVERRIDE;
+  virtual PrintSystem::PrintSystemResult EnumeratePrinters(
+      printing::PrinterList* printer_list) OVERRIDE;
+  virtual void GetPrinterCapsAndDefaults(
+      const std::string& printer_name,
+      const PrinterCapsAndDefaultsCallback& callback) OVERRIDE;
+  virtual bool IsValidPrinter(const std::string& printer_name) OVERRIDE;
+  virtual bool ValidatePrintTicket(
+      const std::string& printer_name,
+      const std::string& print_ticket_data,
+      const std::string& print_ticket_data_mime_type) OVERRIDE;
+  virtual bool GetJobDetails(const std::string& printer_name,
+                             PlatformJobId job_id,
+                             PrintJobDetails *job_details) OVERRIDE;
+  virtual PrintSystem::PrintServerWatcher* CreatePrintServerWatcher() OVERRIDE;
+  virtual PrintSystem::PrinterWatcher* CreatePrinterWatcher(
+      const std::string& printer_name) OVERRIDE;
+  virtual PrintSystem::JobSpooler* CreateJobSpooler() OVERRIDE;
+  virtual bool UseCddAndCjt() OVERRIDE;
+  virtual std::string GetSupportedMimeTypes() OVERRIDE;
+
+ private:
+  std::string PrintSystemWin::GetPrinterDriverInfo(
+      const std::string& printer_name) const;
+
+  scoped_refptr<printing::PrintBackend> print_backend_;
+  bool use_cdd_;
+  DISALLOW_COPY_AND_ASSIGN(PrintSystemWin);
+};
+
+PrintSystemWin::PrintSystemWin() : use_cdd_(false) {
   print_backend_ = printing::PrintBackend::CreateInstance(NULL);
 }
 
-PrintSystemWin::~PrintSystemWin() {
+PrintSystem::PrintSystemResult PrintSystemWin::Init() {
+  use_cdd_ = !printing::XPSModule::Init();
+
+  if (!use_cdd_) {
+    HPTPROVIDER provider = NULL;
+    HRESULT hr = printing::XPSModule::OpenProvider(L"", 1, &provider);
+    if (provider)
+      printing::XPSModule::CloseProvider(provider);
+    // Use cdd if error is different from expected.
+    use_cdd_ = (hr != HRESULT_FROM_WIN32(ERROR_INVALID_PRINTER_NAME));
+  }
+
+  return PrintSystemResult(true, std::string());
 }
 
 PrintSystem::PrintSystemResult PrintSystemWin::EnumeratePrinters(
@@ -212,10 +683,71 @@ PrintSystem::PrintSystemResult PrintSystemWin::EnumeratePrinters(
   return PrintSystemResult(ret, std::string());
 }
 
+void PrintSystemWin::GetPrinterCapsAndDefaults(
+    const std::string& printer_name,
+    const PrinterCapsAndDefaultsCallback& callback) {
+  // Launch as child process to retrieve the capabilities and defaults because
+  // this involves invoking a printer driver DLL and crashes have been known to
+  // occur.
+  PrinterCapsHandler* handler = new PrinterCapsHandler(printer_name, callback);
+  handler->AddRef();
+  if (use_cdd_)
+    handler->StartGetPrinterSemanticCapsAndDefaults();
+  else
+    handler->StartGetPrinterCapsAndDefaults();
+}
+
 bool PrintSystemWin::IsValidPrinter(const std::string& printer_name) {
   return print_backend_->IsValidPrinter(printer_name);
 }
 
+bool PrintSystemWin::ValidatePrintTicket(
+    const std::string& printer_name,
+    const std::string& print_ticket_data,
+    const std::string& print_ticket_data_mime_type) {
+  crash_keys::ScopedPrinterInfo crash_key(GetPrinterDriverInfo(printer_name));
+
+  if (use_cdd_) {
+    return print_ticket_data_mime_type == kContentTypeJSON &&
+           IsValidCjt(print_ticket_data);
+  }
+  DCHECK(print_ticket_data_mime_type == kContentTypeXML);
+
+  printing::ScopedXPSInitializer xps_initializer;
+  if (!xps_initializer.initialized()) {
+    // TODO(sanjeevr): Handle legacy proxy case (with no prntvpt.dll)
+    return false;
+  }
+  bool ret = false;
+  HPTPROVIDER provider = NULL;
+  printing::XPSModule::OpenProvider(base::UTF8ToWide(printer_name), 1,
+                                    &provider);
+  if (provider) {
+    base::win::ScopedComPtr<IStream> print_ticket_stream;
+    CreateStreamOnHGlobal(NULL, TRUE, print_ticket_stream.Receive());
+    ULONG bytes_written = 0;
+    print_ticket_stream->Write(print_ticket_data.c_str(),
+                               print_ticket_data.length(),
+                               &bytes_written);
+    DCHECK(bytes_written == print_ticket_data.length());
+    LARGE_INTEGER pos = {0};
+    ULARGE_INTEGER new_pos = {0};
+    print_ticket_stream->Seek(pos, STREAM_SEEK_SET, &new_pos);
+    base::win::ScopedBstr error;
+    base::win::ScopedComPtr<IStream> result_ticket_stream;
+    CreateStreamOnHGlobal(NULL, TRUE, result_ticket_stream.Receive());
+    ret = SUCCEEDED(printing::XPSModule::MergeAndValidatePrintTicket(
+        provider,
+        print_ticket_stream.get(),
+        NULL,
+        kPTJobScope,
+        result_ticket_stream.get(),
+        error.Receive()));
+    printing::XPSModule::CloseProvider(provider);
+  }
+  return ret;
+}
+
 bool PrintSystemWin::GetJobDetails(const std::string& printer_name,
                                    PlatformJobId job_id,
                                    PrintJobDetails *job_details) {
@@ -272,9 +804,34 @@ PrintSystem::PrinterWatcher* PrintSystemWin::CreatePrinterWatcher(
   return new PrinterWatcherWin(printer_name);
 }
 
+PrintSystem::JobSpooler* PrintSystemWin::CreateJobSpooler() {
+  return new JobSpoolerWin();
+}
+
+bool PrintSystemWin::UseCddAndCjt() {
+  return use_cdd_;
+}
+
+std::string PrintSystemWin::GetSupportedMimeTypes() {
+  std::string result;
+  if (!use_cdd_) {
+    result = kContentTypeXPS;
+    result += ",";
+  }
+  result += kContentTypePDF;
+  return result;
+}
+
 std::string PrintSystemWin::GetPrinterDriverInfo(
     const std::string& printer_name) const {
   return print_backend_->GetPrinterDriverInfo(printer_name);
 }
 
+}  // namespace
+
+scoped_refptr<PrintSystem> PrintSystem::CreateInstance(
+    const base::DictionaryValue* print_system_settings) {
+  return new PrintSystemWin;
+}
+
 }  // namespace cloud_print