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