From a4db67d8bcfc6174c887e895b88d19efeea3d31d Mon Sep 17 00:00:00 2001 From: "sanjeevr@chromium.org" Date: Wed, 21 Jul 2010 20:15:30 +0000 Subject: Used the service utility process host to render PDFs in a sandbox for the Windows cloud print proxy. Also made the print spooling asynchronous. BUG=None TEST=Test cloud print proxy on all supported platforms. Review URL: http://codereview.chromium.org/3051006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@53238 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/service/cloud_print/print_system.h | 75 ++++--- chrome/service/cloud_print/print_system_cups.cc | 135 +++++++----- chrome/service/cloud_print/print_system_win.cc | 241 ++++++++++++++++------ chrome/service/cloud_print/printer_job_handler.cc | 64 +++--- chrome/service/cloud_print/printer_job_handler.h | 24 ++- 5 files changed, 369 insertions(+), 170 deletions(-) (limited to 'chrome/service') diff --git a/chrome/service/cloud_print/print_system.h b/chrome/service/cloud_print/print_system.h index b7d0ce5..1d83a87 100644 --- a/chrome/service/cloud_print/print_system.h +++ b/chrome/service/cloud_print/print_system.h @@ -81,35 +81,6 @@ struct PrintJobDetails { // functionality on some platforms, while reusing core (CUPS) functions. class PrintSystem : public base::RefCountedThreadSafe { public: - virtual ~PrintSystem() {} - - // Enumerates the list of installed local and network printers. - virtual void EnumeratePrinters(PrinterList* printer_list) = 0; - - // Gets the capabilities and defaults for a specific printer. - virtual bool GetPrinterCapsAndDefaults(const std::string& printer_name, - PrinterCapsAndDefaults* printer_info) = 0; - - // Returns true if ticket is valid. - virtual bool ValidatePrintTicket(const std::string& printer_name, - const std::string& print_ticket_data) = 0; - - // Send job to the printer. - virtual bool SpoolPrintJob(const std::string& print_ticket, - const FilePath& print_data_file_path, - const std::string& print_data_mime_type, - const std::string& printer_name, - const std::string& job_title, - PlatformJobId* job_id_ret) = 0; - - // Get details for already spooled job. - virtual bool GetJobDetails(const std::string& printer_name, - PlatformJobId job_id, - PrintJobDetails *job_details) = 0; - - // Returns true if printer_name points to a valid printer. - virtual bool IsValidPrinter(const std::string& printer_name) = 0; - class PrintServerWatcher : public base::RefCountedThreadSafe { public: @@ -141,11 +112,56 @@ class PrintSystem : public base::RefCountedThreadSafe { virtual bool GetCurrentPrinterInfo(PrinterBasicInfo* printer_info) = 0; }; + class JobSpooler : public base::RefCountedThreadSafe { + public: + // Callback interface for JobSpooler notifications. + class Delegate { + public: + virtual ~Delegate() { } + virtual void OnJobSpoolSucceeded(const PlatformJobId& job_id) = 0; + virtual void OnJobSpoolFailed() = 0; + }; + + virtual ~JobSpooler() {} + // Spool job to the printer asynchronously. Caller will be notified via + // |delegate|. Note that only one print job can be in progress at any given + // time. Subsequent calls to Spool (before the Delegate::OnJobSpoolSucceeded + // or Delegate::OnJobSpoolFailed methods are called) can fail. + virtual bool Spool(const std::string& print_ticket, + const 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) = 0; + }; + + virtual ~PrintSystem() {} + + // Enumerates the list of installed local and network printers. + virtual void EnumeratePrinters(PrinterList* printer_list) = 0; + + // Gets the capabilities and defaults for a specific printer. + virtual bool GetPrinterCapsAndDefaults(const std::string& printer_name, + PrinterCapsAndDefaults* printer_info) = 0; + + // Returns true if ticket is valid. + virtual bool ValidatePrintTicket(const std::string& printer_name, + const std::string& print_ticket_data) = 0; + + // Get details for already spooled job. + virtual bool GetJobDetails(const std::string& printer_name, + PlatformJobId job_id, + PrintJobDetails *job_details) = 0; + + // Returns true if printer_name points to a valid printer. + virtual bool IsValidPrinter(const std::string& printer_name) = 0; + // Factory methods to create corresponding watcher. Callee is responsible // for deleting objects. Return NULL if failed. virtual PrintServerWatcher* CreatePrintServerWatcher() = 0; virtual PrinterWatcher* CreatePrinterWatcher( const std::string& printer_name) = 0; + virtual JobSpooler* CreateJobSpooler() = 0; // Generate unique for proxy. static std::string GenerateProxyId(); @@ -164,6 +180,7 @@ class PrintSystem : public base::RefCountedThreadSafe { // the workaround was not needed for my machine). typedef PrintSystem::PrintServerWatcher::Delegate PrintServerWatcherDelegate; typedef PrintSystem::PrinterWatcher::Delegate PrinterWatcherDelegate; +typedef PrintSystem::JobSpooler::Delegate JobSpoolerDelegate; } // namespace cloud_print diff --git a/chrome/service/cloud_print/print_system_cups.cc b/chrome/service/cloud_print/print_system_cups.cc index b624f40..dc1314c 100644 --- a/chrome/service/cloud_print/print_system_cups.cc +++ b/chrome/service/cloud_print/print_system_cups.cc @@ -124,6 +124,7 @@ class PrintSystemCUPS : public PrintSystem { public: explicit PrintSystemCUPS(const GURL& print_server_url); + // PrintSystem implementation. virtual void EnumeratePrinters(PrinterList* printer_list); virtual bool GetPrinterCapsAndDefaults(const std::string& printer_name, @@ -132,19 +133,13 @@ class PrintSystemCUPS : public PrintSystem { virtual bool ValidatePrintTicket(const std::string& printer_name, const std::string& print_ticket_data); - virtual bool SpoolPrintJob(const std::string& print_ticket, - const FilePath& print_data_file_path, - const std::string& print_data_mime_type, - const std::string& printer_name, - const std::string& job_title, - PlatformJobId* job_id_ret); - virtual bool GetJobDetails(const std::string& printer_name, PlatformJobId job_id, PrintJobDetails *job_details); virtual bool IsValidPrinter(const std::string& printer_name); + // TODO(gene): Add implementation for CUPS print server watcher. class PrintServerWatcherCUPS : public PrintSystem::PrintServerWatcher { @@ -212,11 +207,53 @@ class PrintSystemCUPS : public PrintSystem { DISALLOW_COPY_AND_ASSIGN(PrinterWatcherCUPS); }; + class JobSpoolerCUPS : public PrintSystem::JobSpooler { + public: + explicit JobSpoolerCUPS(PrintSystemCUPS* print_system) + : print_system_(print_system) { + DCHECK(print_system_.get()); + } + // PrintSystem::JobSpooler implementation. + virtual bool Spool(const std::string& print_ticket, + const 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) { + DCHECK(delegate); + int job_id = print_system_->SpoolPrintJob( + print_ticket, print_data_file_path, print_data_mime_type, + printer_name, job_title); + MessageLoop::current()->PostTask(FROM_HERE, + NewRunnableFunction( + &JobSpoolerCUPS::NotifyDelegate, + delegate, + job_id)); + return true; + } + + static void NotifyDelegate(JobSpooler::Delegate* delegate, int job_id) { + if (job_id) + delegate->OnJobSpoolSucceeded(job_id); + else + delegate->OnJobSpoolFailed(); + } + private: + scoped_refptr print_system_; + DISALLOW_COPY_AND_ASSIGN(JobSpoolerCUPS); + }; + virtual PrintSystem::PrintServerWatcher* CreatePrintServerWatcher(); virtual PrintSystem::PrinterWatcher* CreatePrinterWatcher( const std::string& printer_name); + virtual PrintSystem::JobSpooler* CreateJobSpooler(); // Helper functions. + PlatformJobId SpoolPrintJob(const std::string& print_ticket, + const FilePath& print_data_file_path, + const std::string& print_data_mime_type, + const std::string& printer_name, + const std::string& job_title); bool GetPrinterInfo(const std::string& printer_name, PrinterBasicInfo* info); bool ParsePrintTicket(const std::string& print_ticket, std::map* options); @@ -228,10 +265,10 @@ class PrintSystemCUPS : public PrintSystem { // in the 2(), it does not work in CUPS prior to 1.4. int GetDests(cups_dest_t** dests); FilePath GetPPD(const char* name); - int PrintFile(const char* name, const char* filename, const char* title, - int num_options, cups_option_t* options); int GetJobs(cups_job_t** jobs, const char* name, int myjobs, int whichjobs); + int PrintFile(const char* name, const char* filename, const char* title, + int num_options, cups_option_t* options); GURL print_server_url_; }; @@ -336,47 +373,6 @@ bool PrintSystemCUPS::ParsePrintTicket(const std::string& print_ticket, return true; } -bool PrintSystemCUPS::SpoolPrintJob(const std::string& print_ticket, - const FilePath& print_data_file_path, - const std::string& print_data_mime_type, - const std::string& printer_name, - const std::string& job_title, - PlatformJobId* job_id_ret) { - DCHECK(job_id_ret); - - LOG(INFO) << "CP_CUPS: Spooling print job for: " << printer_name; - - // We need to store options as char* string for the duration of the - // cupsPrintFile2 call. We'll use map here to store options, since - // Dictionary value from JSON parser returns wchat_t. - std::map options; - bool res = ParsePrintTicket(print_ticket, &options); - DCHECK(res); // If print ticket is invalid we still print using defaults. - - std::vector cups_options; - std::map::iterator it; - for (it = options.begin(); it != options.end(); ++it) { - cups_option_t opt; - opt.name = const_cast(it->first.c_str()); - opt.value = const_cast(it->second.c_str()); - cups_options.push_back(opt); - } - - int job_id = PrintFile(printer_name.c_str(), - print_data_file_path.value().c_str(), - job_title.c_str(), - cups_options.size(), - &(cups_options[0])); - - LOG(INFO) << "CP_CUPS: Job spooled, id: " << job_id; - - if (job_id == 0) - return false; - - *job_id_ret = job_id; - return true; -} - bool PrintSystemCUPS::GetJobDetails(const std::string& printer_name, PlatformJobId job_id, PrintJobDetails *job_details) { @@ -462,6 +458,10 @@ PrintSystem::PrinterWatcher* PrintSystemCUPS::CreatePrinterWatcher( return new PrinterWatcherCUPS(this, printer_name); } +PrintSystem::JobSpooler* PrintSystemCUPS::CreateJobSpooler() { + return new JobSpoolerCUPS(this); +} + std::string PrintSystem::GenerateProxyId() { // TODO(gene): This code should generate a unique id for proxy. ID should be // unique for this user. Rand may return the same number. We'll need to change @@ -534,4 +534,39 @@ int PrintSystemCUPS::GetJobs(cups_job_t** jobs, const char* name, } } +PlatformJobId PrintSystemCUPS::SpoolPrintJob( + const std::string& print_ticket, + const FilePath& print_data_file_path, + const std::string& print_data_mime_type, + const std::string& printer_name, + const std::string& job_title) { + LOG(INFO) << "CP_CUPS: Spooling print job for: " << printer_name; + + // We need to store options as char* string for the duration of the + // cupsPrintFile2 call. We'll use map here to store options, since + // Dictionary value from JSON parser returns wchat_t. + std::map options; + bool res = ParsePrintTicket(print_ticket, &options); + DCHECK(res); // If print ticket is invalid we still print using defaults. + + std::vector cups_options; + std::map::iterator it; + for (it = options.begin(); it != options.end(); ++it) { + cups_option_t opt; + opt.name = const_cast(it->first.c_str()); + opt.value = const_cast(it->second.c_str()); + cups_options.push_back(opt); + } + + int job_id = PrintFile(printer_name.c_str(), + print_data_file_path.value().c_str(), + job_title.c_str(), + cups_options.size(), + &(cups_options[0])); + + LOG(INFO) << "CP_CUPS: Job spooled, id: " << job_id; + + return job_id; +} + } // namespace cloud_print diff --git a/chrome/service/cloud_print/print_system_win.cc b/chrome/service/cloud_print/print_system_win.cc index d1e00c8..da16fa2 100644 --- a/chrome/service/cloud_print/print_system_win.cc +++ b/chrome/service/cloud_print/print_system_win.cc @@ -12,12 +12,18 @@ #include #include "base/lock.h" +#include "base/file_util.h" #include "base/object_watcher.h" #include "base/scoped_bstr_win.h" #include "base/scoped_comptr_win.h" #include "base/scoped_handle_win.h" #include "base/scoped_ptr.h" #include "base/utf_string_conversions.h" +#include "chrome/service/service_process.h" +#include "chrome/service/service_utility_process_host.h" +#include "gfx/rect.h" +#include "printing/native_metafile.h" +#include "printing/page_range.h" #pragma comment(lib, "prntvpt.lib") #pragma comment(lib, "rpcrt4.lib") @@ -117,13 +123,6 @@ HRESULT PrintTicketToDevMode(const std::string& printer_name, return hr; } -HRESULT PrintPdf2DC(HDC dc, const FilePath& pdf_filename) { - HRESULT hr = E_NOTIMPL; - // TODO(sanjeevr): Implement this. - NOTIMPLEMENTED(); - return hr; -} - } // namespace namespace cloud_print { @@ -255,13 +254,6 @@ class PrintSystemWin : public PrintSystem { virtual bool ValidatePrintTicket(const std::string& printer_name, const std::string& print_ticket_data); - virtual bool SpoolPrintJob(const std::string& print_ticket, - const FilePath& print_data_file_path, - const std::string& print_data_mime_type, - const std::string& printer_name, - const std::string& job_title, - PlatformJobId* job_id_ret); - virtual bool GetJobDetails(const std::string& printer_name, PlatformJobId job_id, PrintJobDetails *job_details); @@ -291,13 +283,10 @@ class PrintSystemWin : public PrintSystem { delegate_->OnPrinterAdded(); } virtual void OnPrinterDeleted() { - NOTREACHED(); } virtual void OnPrinterChanged() { - NOTREACHED(); } virtual void OnJobChanged() { - NOTREACHED(); } private: @@ -349,9 +338,180 @@ class PrintSystemWin : public PrintSystem { DISALLOW_COPY_AND_ASSIGN(PrinterWatcherWin); }; + class JobSpoolerWin : public PrintSystem::JobSpooler { + public: + JobSpoolerWin() : core_(new Core) {} + // PrintSystem::JobSpooler implementation. + virtual bool Spool(const std::string& print_ticket, + const 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) { + return core_->Spool(print_ticket, print_data_file_path, + print_data_mime_type, printer_name, job_title, + delegate); + } + private: + // We use a Core class because we want a separate RefCountedThreadSafe + // implementation for ServiceUtilityProcessHost::Client. + class Core : public ServiceUtilityProcessHost::Client { + public: + Core() + : last_page_printed_(-1), job_id_(-1), delegate_(NULL), saved_dc_(0) { + } + ~Core() { + } + bool Spool(const std::string& print_ticket, + const 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; + } + last_page_printed_ = -1; + // We only support PDFs for now. + if (print_data_mime_type != "application/pdf") { + NOTREACHED(); + return false; + } + + if (!InitXPSModule()) { + // TODO(sanjeevr): Handle legacy proxy case (with no prntvpt.dll) + return false; + } + DevMode pt_dev_mode; + HRESULT hr = PrintTicketToDevMode(printer_name, print_ticket, + &pt_dev_mode); + if (FAILED(hr)) { + NOTREACHED(); + return false; + } + HDC dc = CreateDC(L"WINSPOOL", UTF8ToWide(printer_name).c_str(), + NULL, pt_dev_mode.dm_); + if (!dc) { + NOTREACHED(); + return false; + } + hr = E_FAIL; + DOCINFO di = {0}; + di.cbSize = sizeof(DOCINFO); + std::wstring doc_name = UTF8ToWide(job_title); + di.lpszDocName = doc_name.c_str(); + job_id_ = StartDoc(dc, &di); + if (SP_ERROR == job_id_) + return false; + + printer_dc_.Set(dc); + + int printer_dpi = ::GetDeviceCaps(printer_dc_.Get(), LOGPIXELSX); + saved_dc_ = SaveDC(printer_dc_.Get()); + SetGraphicsMode(printer_dc_.Get(), GM_ADVANCED); + XFORM xform = {0}; + xform.eM11 = xform.eM22 = static_cast(printer_dpi) / + static_cast(GetDeviceCaps(GetDC(NULL), LOGPIXELSX)); + ModifyWorldTransform(printer_dc_.Get(), &xform, MWT_LEFTMULTIPLY); + print_data_file_path_ = print_data_file_path; + delegate_ = delegate; + RenderNextPDFPages(); + return true; + } + + // ServiceUtilityProcessHost::Client implementation. + virtual void OnRenderPDFPagesToMetafileSucceeded( + const printing::NativeMetafile& metafile, + int highest_rendered_page_number) { + 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(); + } + virtual void OnRenderPDFPagesToMetafileFailed() { + PrintJobDone(); + } + virtual void OnChildDied() { + PrintJobDone(); + } + private: + 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 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, + NewRunnableMethod( + this, + &JobSpoolerWin::Core::RenderPDFPagesInSandbox, + print_data_file_path_, + render_area, + printer_dpi, + page_ranges, + base::MessageLoopProxy::CreateForCurrentThread())); + } + // Called on the service process IO thread. + void RenderPDFPagesInSandbox( + const FilePath& pdf_path, const gfx::Rect& render_area, + int render_dpi, const std::vector& page_ranges, + const scoped_refptr& + client_message_loop_proxy) { + DCHECK(g_service_process->io_thread()->message_loop_proxy()-> + BelongsToCurrentThread()); + scoped_ptr utility_host( + new ServiceUtilityProcessHost(this, client_message_loop_proxy)); + if (utility_host->StartRenderPDFPagesToMetafile(pdf_path, + render_area, + render_dpi, + page_ranges)) { + // The object will self-destruct when the child process dies. + utility_host.release(); + } + } + static const int kPageCountPerBatch = 10; + int last_page_printed_; + PlatformJobId job_id_; + PrintSystem::JobSpooler::Delegate* delegate_; + int saved_dc_; + ScopedHDC printer_dc_; + FilePath print_data_file_path_; + DISALLOW_COPY_AND_ASSIGN(JobSpoolerWin::Core); + }; + scoped_refptr core_; + DISALLOW_COPY_AND_ASSIGN(JobSpoolerWin); + }; + virtual PrintSystem::PrintServerWatcher* CreatePrintServerWatcher(); virtual PrintSystem::PrinterWatcher* CreatePrinterWatcher( const std::string& printer_name); + virtual PrintSystem::JobSpooler* CreateJobSpooler(); }; void PrintSystemWin::EnumeratePrinters(PrinterList* printer_list) { @@ -486,48 +646,6 @@ bool PrintSystemWin::ValidatePrintTicket( return ret; } -bool PrintSystemWin::SpoolPrintJob(const std::string& print_ticket, - const FilePath& print_data_file_path, - const std::string& print_data_mime_type, - const std::string& printer_name, - const std::string& job_title, - PlatformJobId* job_id_ret) { - if (!InitXPSModule()) { - // TODO(sanjeevr): Handle legacy proxy case (with no prntvpt.dll) - return false; - } - DevMode pt_dev_mode; - HRESULT hr = PrintTicketToDevMode(printer_name, print_ticket, &pt_dev_mode); - if (FAILED(hr)) { - NOTREACHED(); - return false; - } - ScopedHDC dc(CreateDC(L"WINSPOOL", UTF8ToWide(printer_name).c_str(), NULL, - pt_dev_mode.dm_)); - if (!dc.Get()) { - NOTREACHED(); - return false; - } - hr = E_FAIL; - DOCINFO di = {0}; - di.cbSize = sizeof(DOCINFO); - std::wstring doc_name = UTF8ToWide(job_title); - di.lpszDocName = doc_name.c_str(); - int job_id = StartDoc(dc.Get(), &di); - if (SP_ERROR != job_id) { - if (print_data_mime_type == "application/pdf") { - hr = PrintPdf2DC(dc.Get(), print_data_file_path); - } else { - NOTREACHED(); - } - EndDoc(dc.Get()); - if (SUCCEEDED(hr) && job_id_ret) { - *job_id_ret = job_id; - } - } - return SUCCEEDED(hr); -} - bool PrintSystemWin::GetJobDetails(const std::string& printer_name, PlatformJobId job_id, PrintJobDetails *job_details) { @@ -597,6 +715,11 @@ PrintSystem::PrinterWatcher* PrintSystemWin::CreatePrinterWatcher( return new PrinterWatcherWin(printer_name); } +PrintSystem::JobSpooler* +PrintSystemWin::CreateJobSpooler() { + return new JobSpoolerWin(); +} + std::string PrintSystem::GenerateProxyId() { GUID proxy_id = {0}; HRESULT hr = UuidCreate(&proxy_id); diff --git a/chrome/service/cloud_print/printer_job_handler.cc b/chrome/service/cloud_print/printer_job_handler.cc index 040c449..d5cef62 100644 --- a/chrome/service/cloud_print/printer_job_handler.cc +++ b/chrome/service/cloud_print/printer_job_handler.cc @@ -36,6 +36,8 @@ PrinterJobHandler::PrinterJobHandler( next_failure_handler_(NULL), server_error_count_(0), print_thread_("Chrome_CloudPrintJobPrintThread"), + job_handler_message_loop_proxy_( + base::MessageLoopProxy::CreateForCurrentThread()), shutting_down_(false), server_job_available_(false), printer_update_pending_(true), @@ -431,11 +433,9 @@ void PrinterJobHandler::StartPrinting() { JobFailed(PRINT_FAILED); } else { print_thread_.message_loop()->PostTask( - FROM_HERE, NewRunnableFunction(&PrinterJobHandler::DoPrint, - job_details_, - printer_info_.printer_name, - print_system_, this, - MessageLoop::current())); + FROM_HERE, NewRunnableMethod(this, &PrinterJobHandler::DoPrint, + job_details_, + printer_info_.printer_name)); } } } @@ -590,29 +590,41 @@ void PrinterJobHandler::FailedFetchingJobData() { } } +// The following methods are called on |print_thread_|. It is not safe to +// access any members other than |job_handler_message_loop_proxy_|, +// |job_spooler_| and |print_system_|. void PrinterJobHandler::DoPrint(const JobDetails& job_details, - const std::string& printer_name, - scoped_refptr print_system, - PrinterJobHandler* job_handler, - MessageLoop* job_message_loop) { - DCHECK(job_handler); - DCHECK(job_message_loop); - LOG(INFO) << "CP_PROXY: Printing: " << printer_name; - cloud_print::PlatformJobId job_id = -1; - if (print_system->SpoolPrintJob(job_details.print_ticket_, - job_details.print_data_file_path_, - job_details.print_data_mime_type_, - printer_name, - job_details.job_title_, &job_id)) { - job_message_loop->PostTask(FROM_HERE, - NewRunnableMethod(job_handler, - &PrinterJobHandler::JobSpooled, - job_id)); + const std::string& printer_name) { + job_spooler_ = print_system_->CreateJobSpooler(); + DCHECK(job_spooler_); + if (job_spooler_) { + job_spooler_->Spool(job_details.print_ticket_, + job_details.print_data_file_path_, + job_details.print_data_mime_type_, + printer_name, + job_details.job_title_, + this); } else { - job_message_loop->PostTask(FROM_HERE, - NewRunnableMethod(job_handler, - &PrinterJobHandler::JobFailed, - PRINT_FAILED)); + OnJobSpoolFailed(); } } +void PrinterJobHandler::OnJobSpoolSucceeded( + const cloud_print::PlatformJobId& job_id) { + DCHECK(MessageLoop::current() == print_thread_.message_loop()); + job_spooler_ = NULL; + job_handler_message_loop_proxy_->PostTask(FROM_HERE, + NewRunnableMethod(this, + &PrinterJobHandler::JobSpooled, + job_id)); +} + +void PrinterJobHandler::OnJobSpoolFailed() { + DCHECK(MessageLoop::current() == print_thread_.message_loop()); + job_spooler_ = NULL; + job_handler_message_loop_proxy_->PostTask(FROM_HERE, + NewRunnableMethod(this, + &PrinterJobHandler::JobFailed, + PRINT_FAILED)); +} + diff --git a/chrome/service/cloud_print/printer_job_handler.h b/chrome/service/cloud_print/printer_job_handler.h index c24e7b6..81f802b 100644 --- a/chrome/service/cloud_print/printer_job_handler.h +++ b/chrome/service/cloud_print/printer_job_handler.h @@ -10,6 +10,7 @@ #include "base/file_path.h" #include "base/ref_counted.h" +#include "base/message_loop_proxy.h" #include "base/thread.h" #include "chrome/service/cloud_print/job_status_updater.h" #include "chrome/service/cloud_print/print_system.h" @@ -61,7 +62,8 @@ typedef URLFetcher::Delegate URLFetcherDelegate; class PrinterJobHandler : public base::RefCountedThreadSafe, public URLFetcherDelegate, public JobStatusUpdaterDelegate, - public cloud_print::PrinterWatcherDelegate { + public cloud_print::PrinterWatcherDelegate, + public cloud_print::JobSpoolerDelegate { enum PrintJobError { SUCCESS, JOB_DOWNLOAD_FAILED, @@ -121,6 +123,11 @@ class PrinterJobHandler : public base::RefCountedThreadSafe, virtual void OnPrinterChanged(); virtual void OnJobChanged(); + // cloud_print::JobSpoolerDelegate implementation. + // Called on print_thread_. + virtual void OnJobSpoolSucceeded(const cloud_print::PlatformJobId& job_id); + virtual void OnJobSpoolFailed(); + // End Delegate implementations private: @@ -207,11 +214,9 @@ class PrinterJobHandler : public base::RefCountedThreadSafe, bool HavePendingTasks(); void FailedFetchingJobData(); - static void DoPrint(const JobDetails& job_details, - const std::string& printer_name, - scoped_refptr print_system, - PrinterJobHandler* job_handler, - MessageLoop* job_message_loop); + // Called on print_thread_. + void DoPrint(const JobDetails& job_details, + const std::string& printer_name); scoped_ptr request_; scoped_refptr print_system_; @@ -232,6 +237,13 @@ class PrinterJobHandler : public base::RefCountedThreadSafe, int server_error_count_; // The thread on which the actual print operation happens base::Thread print_thread_; + // The Job spooler object. This is only non-NULL during a print operation. + // It lives and dies on |print_thread_| + scoped_refptr job_spooler_; + // The message loop proxy representing the thread on which this object + // was created. Used by the print thread. + scoped_refptr job_handler_message_loop_proxy_; + // There may be pending tasks in the message queue when Shutdown is called. // We set this flag so as to do nothing in those tasks. bool shutting_down_; -- cgit v1.1