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/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 "grit/generated_resources.h" 24 #include "printing/backend/win_helper.h" 25 #include "printing/emf_win.h" 26 #include "printing/page_range.h" 27 #include "printing/printing_utils.h" 28 #include "ui/base/l10n/l10n_util.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_, PRINTER_CHANGE_PRINTER|PRINTER_CHANGE_JOB, 0, NULL)); 72 if (printer_change_.IsValid()) { 73 ret = watcher_.StartWatching(printer_change_, 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_, this); 112 } 113 114 bool GetCurrentPrinterInfo(printing::PrinterBasicInfo* printer_info) { 115 DCHECK(printer_info); 116 return InitBasicPrinterInfo(printer_, 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() 251 : last_page_printed_(-1), 252 job_id_(-1), 253 delegate_(NULL), 254 saved_dc_(0) { 255 } 256 257 ~Core() {} 258 259 bool Spool(const std::string& print_ticket, 260 const std::string& print_ticket_mime_type, 261 const base::FilePath& print_data_file_path, 262 const std::string& print_data_mime_type, 263 const std::string& printer_name, 264 const std::string& job_title, 265 JobSpooler::Delegate* delegate) { 266 if (delegate_) { 267 // We are already in the process of printing. 268 NOTREACHED(); 269 return false; 270 } 271 base::string16 printer_wide = base::UTF8ToWide(printer_name); 272 last_page_printed_ = -1; 273 // We only support PDF and XPS documents for now. 274 if (print_data_mime_type == kContentTypePDF) { 275 scoped_ptr<DEVMODE, base::FreeDeleter> dev_mode; 276 if (print_ticket_mime_type == kContentTypeJSON) { 277 dev_mode = CjtToDevMode(printer_wide, print_ticket); 278 } else { 279 DCHECK(print_ticket_mime_type == kContentTypeXML); 280 dev_mode = printing::XpsTicketToDevMode(printer_wide, print_ticket); 281 } 282 283 if (!dev_mode) { 284 NOTREACHED(); 285 return false; 286 } 287 288 HDC dc = CreateDC(L"WINSPOOL", printer_wide.c_str(), NULL, 289 dev_mode.get()); 290 if (!dc) { 291 NOTREACHED(); 292 return false; 293 } 294 DOCINFO di = {0}; 295 di.cbSize = sizeof(DOCINFO); 296 base::string16 doc_name = base::UTF8ToUTF16(job_title); 297 DCHECK(printing::SimplifyDocumentTitle(doc_name) == doc_name); 298 di.lpszDocName = doc_name.c_str(); 299 job_id_ = StartDoc(dc, &di); 300 if (job_id_ <= 0) 301 return false; 302 303 printer_dc_.Set(dc); 304 saved_dc_ = SaveDC(printer_dc_.Get()); 305 print_data_file_path_ = print_data_file_path; 306 delegate_ = delegate; 307 RenderNextPDFPages(); 308 } else if (print_data_mime_type == kContentTypeXPS) { 309 DCHECK(print_ticket_mime_type == kContentTypeXML); 310 bool ret = PrintXPSDocument(printer_name, 311 job_title, 312 print_data_file_path, 313 print_ticket); 314 if (ret) 315 delegate_ = delegate; 316 return ret; 317 } else { 318 NOTREACHED(); 319 return false; 320 } 321 return true; 322 } 323 324 void PreparePageDCForPrinting(HDC, double scale_factor) { 325 SetGraphicsMode(printer_dc_.Get(), GM_ADVANCED); 326 // Setup the matrix to translate and scale to the right place. Take in 327 // account the scale factor. 328 // Note that the printing output is relative to printable area of 329 // the page. That is 0,0 is offset by PHYSICALOFFSETX/Y from the page. 330 int offset_x = ::GetDeviceCaps(printer_dc_.Get(), PHYSICALOFFSETX); 331 int offset_y = ::GetDeviceCaps(printer_dc_.Get(), PHYSICALOFFSETY); 332 XFORM xform = {0}; 333 xform.eDx = static_cast<float>(-offset_x); 334 xform.eDy = static_cast<float>(-offset_y); 335 xform.eM11 = xform.eM22 = 1.0 / scale_factor; 336 SetWorldTransform(printer_dc_.Get(), &xform); 337 } 338 339 // ServiceUtilityProcessHost::Client implementation. 340 virtual void OnRenderPDFPagesToMetafileSucceeded( 341 const printing::Emf& metafile, 342 int highest_rendered_page_number, 343 double scale_factor) OVERRIDE { 344 PreparePageDCForPrinting(printer_dc_.Get(), scale_factor); 345 metafile.SafePlayback(printer_dc_.Get()); 346 bool done_printing = (highest_rendered_page_number != 347 last_page_printed_ + kPageCountPerBatch); 348 last_page_printed_ = highest_rendered_page_number; 349 if (done_printing) 350 PrintJobDone(); 351 else 352 RenderNextPDFPages(); 353 } 354 355 // base::win::ObjectWatcher::Delegate implementation. 356 virtual void OnObjectSignaled(HANDLE object) OVERRIDE { 357 DCHECK(xps_print_job_); 358 DCHECK(object == job_progress_event_.Get()); 359 ResetEvent(job_progress_event_.Get()); 360 if (!delegate_) 361 return; 362 XPS_JOB_STATUS job_status = {0}; 363 xps_print_job_->GetJobStatus(&job_status); 364 if ((job_status.completion == XPS_JOB_CANCELLED) || 365 (job_status.completion == XPS_JOB_FAILED)) { 366 delegate_->OnJobSpoolFailed(); 367 } else if (job_status.jobId || 368 (job_status.completion == XPS_JOB_COMPLETED)) { 369 // Note: In the case of the XPS document being printed to the 370 // Microsoft XPS Document Writer, it seems to skip spooling the job 371 // and goes to the completed state without ever assigning a job id. 372 delegate_->OnJobSpoolSucceeded(job_status.jobId); 373 } else { 374 job_progress_watcher_.StopWatching(); 375 job_progress_watcher_.StartWatching(job_progress_event_.Get(), this); 376 } 377 } 378 379 virtual void OnRenderPDFPagesToMetafileFailed() OVERRIDE { 380 PrintJobDone(); 381 } 382 383 virtual void OnChildDied() OVERRIDE { 384 PrintJobDone(); 385 } 386 387 private: 388 // Helper class to allow PrintXPSDocument() to have multiple exits. 389 class PrintJobCanceler { 390 public: 391 explicit PrintJobCanceler( 392 base::win::ScopedComPtr<IXpsPrintJob>* job_ptr) 393 : job_ptr_(job_ptr) { 394 } 395 ~PrintJobCanceler() { 396 if (job_ptr_ && *job_ptr_) { 397 (*job_ptr_)->Cancel(); 398 job_ptr_->Release(); 399 } 400 } 401 402 void reset() { job_ptr_ = NULL; } 403 404 private: 405 base::win::ScopedComPtr<IXpsPrintJob>* job_ptr_; 406 407 DISALLOW_COPY_AND_ASSIGN(PrintJobCanceler); 408 }; 409 410 void PrintJobDone() { 411 // If there is no delegate, then there is nothing pending to process. 412 if (!delegate_) 413 return; 414 RestoreDC(printer_dc_.Get(), saved_dc_); 415 EndDoc(printer_dc_.Get()); 416 if (-1 == last_page_printed_) { 417 delegate_->OnJobSpoolFailed(); 418 } else { 419 delegate_->OnJobSpoolSucceeded(job_id_); 420 } 421 delegate_ = NULL; 422 } 423 424 void RenderNextPDFPages() { 425 printing::PageRange range; 426 // Render 10 pages at a time. 427 range.from = last_page_printed_ + 1; 428 range.to = last_page_printed_ + kPageCountPerBatch; 429 std::vector<printing::PageRange> page_ranges; 430 page_ranges.push_back(range); 431 432 int printer_dpi = ::GetDeviceCaps(printer_dc_.Get(), LOGPIXELSX); 433 int dc_width = GetDeviceCaps(printer_dc_.Get(), PHYSICALWIDTH); 434 int dc_height = GetDeviceCaps(printer_dc_.Get(), PHYSICALHEIGHT); 435 gfx::Rect render_area(0, 0, dc_width, dc_height); 436 g_service_process->io_thread()->message_loop_proxy()->PostTask( 437 FROM_HERE, 438 base::Bind(&JobSpoolerWin::Core::RenderPDFPagesInSandbox, this, 439 print_data_file_path_, render_area, printer_dpi, 440 page_ranges, base::MessageLoopProxy::current())); 441 } 442 443 // Called on the service process IO thread. 444 void RenderPDFPagesInSandbox( 445 const base::FilePath& pdf_path, const gfx::Rect& render_area, 446 int render_dpi, const std::vector<printing::PageRange>& page_ranges, 447 const scoped_refptr<base::MessageLoopProxy>& 448 client_message_loop_proxy) { 449 DCHECK(g_service_process->io_thread()->message_loop_proxy()-> 450 BelongsToCurrentThread()); 451 scoped_ptr<ServiceUtilityProcessHost> utility_host( 452 new ServiceUtilityProcessHost(this, client_message_loop_proxy)); 453 // TODO(gene): For now we disabling autorotation for CloudPrinting. 454 // Landscape/Portrait setting is passed in the print ticket and 455 // server is generating portrait PDF always. 456 // We should enable autorotation once server will be able to generate 457 // PDF that matches paper size and orientation. 458 if (utility_host->StartRenderPDFPagesToMetafile( 459 pdf_path, 460 printing::PdfRenderSettings(render_area, render_dpi, false), 461 page_ranges)) { 462 // The object will self-destruct when the child process dies. 463 utility_host.release(); 464 } 465 } 466 467 bool PrintXPSDocument(const std::string& printer_name, 468 const std::string& job_title, 469 const base::FilePath& print_data_file_path, 470 const std::string& print_ticket) { 471 if (!printing::XPSPrintModule::Init()) 472 return false; 473 474 job_progress_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL)); 475 if (!job_progress_event_.Get()) 476 return false; 477 478 PrintJobCanceler job_canceler(&xps_print_job_); 479 base::win::ScopedComPtr<IXpsPrintJobStream> doc_stream; 480 base::win::ScopedComPtr<IXpsPrintJobStream> print_ticket_stream; 481 if (FAILED(printing::XPSPrintModule::StartXpsPrintJob( 482 base::UTF8ToWide(printer_name).c_str(), 483 base::UTF8ToWide(job_title).c_str(), 484 NULL, job_progress_event_.Get(), NULL, NULL, NULL, 485 xps_print_job_.Receive(), doc_stream.Receive(), 486 print_ticket_stream.Receive()))) 487 return false; 488 489 ULONG print_bytes_written = 0; 490 if (FAILED(print_ticket_stream->Write(print_ticket.c_str(), 491 print_ticket.length(), 492 &print_bytes_written))) 493 return false; 494 DCHECK_EQ(print_ticket.length(), print_bytes_written); 495 if (FAILED(print_ticket_stream->Close())) 496 return false; 497 498 std::string document_data; 499 base::ReadFileToString(print_data_file_path, &document_data); 500 ULONG doc_bytes_written = 0; 501 if (FAILED(doc_stream->Write(document_data.c_str(), 502 document_data.length(), 503 &doc_bytes_written))) 504 return false; 505 DCHECK_EQ(document_data.length(), doc_bytes_written); 506 if (FAILED(doc_stream->Close())) 507 return false; 508 509 job_progress_watcher_.StartWatching(job_progress_event_.Get(), this); 510 job_canceler.reset(); 511 return true; 512 } 513 514 // Some Cairo-generated PDFs from Chrome OS result in huge metafiles. 515 // So the PageCountPerBatch is set to 1 for now. 516 // TODO(sanjeevr): Figure out a smarter way to determine the pages per 517 // batch. Filed a bug to track this at 518 // http://code.google.com/p/chromium/issues/detail?id=57350. 519 static const int kPageCountPerBatch = 1; 520 521 int last_page_printed_; 522 PlatformJobId job_id_; 523 PrintSystem::JobSpooler::Delegate* delegate_; 524 int saved_dc_; 525 base::win::ScopedCreateDC printer_dc_; 526 base::FilePath print_data_file_path_; 527 base::win::ScopedHandle job_progress_event_; 528 base::win::ObjectWatcher job_progress_watcher_; 529 base::win::ScopedComPtr<IXpsPrintJob> xps_print_job_; 530 531 DISALLOW_COPY_AND_ASSIGN(Core); 532 }; 533 scoped_refptr<Core> core_; 534 535 DISALLOW_COPY_AND_ASSIGN(JobSpoolerWin); 536 }; 537 538 // A helper class to handle the response from the utility process to the 539 // request to fetch printer capabilities and defaults. 540 class PrinterCapsHandler : public ServiceUtilityProcessHost::Client { 541 public: 542 PrinterCapsHandler( 543 const std::string& printer_name, 544 const PrintSystem::PrinterCapsAndDefaultsCallback& callback) 545 : printer_name_(printer_name), callback_(callback) { 546 } 547 548 // ServiceUtilityProcessHost::Client implementation. 549 virtual void OnChildDied() OVERRIDE { 550 OnGetPrinterCapsAndDefaults(false, printer_name_, 551 printing::PrinterCapsAndDefaults()); 552 } 553 554 virtual void OnGetPrinterCapsAndDefaults( 555 bool succeeded, 556 const std::string& printer_name, 557 const printing::PrinterCapsAndDefaults& caps_and_defaults) OVERRIDE { 558 callback_.Run(succeeded, printer_name, caps_and_defaults); 559 callback_.Reset(); 560 Release(); 561 } 562 563 virtual void OnGetPrinterSemanticCapsAndDefaults( 564 bool succeeded, 565 const std::string& printer_name, 566 const printing::PrinterSemanticCapsAndDefaults& semantic_info) OVERRIDE { 567 printing::PrinterCapsAndDefaults printer_info; 568 if (succeeded) { 569 printer_info.caps_mime_type = kContentTypeJSON; 570 scoped_ptr<base::DictionaryValue> description( 571 PrinterSemanticCapsAndDefaultsToCdd(semantic_info)); 572 if (description) { 573 base::JSONWriter::WriteWithOptions( 574 description.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, 575 &printer_info.printer_capabilities); 576 } 577 } 578 callback_.Run(succeeded, printer_name, printer_info); 579 callback_.Reset(); 580 Release(); 581 } 582 583 void StartGetPrinterCapsAndDefaults() { 584 g_service_process->io_thread()->message_loop_proxy()->PostTask( 585 FROM_HERE, 586 base::Bind(&PrinterCapsHandler::GetPrinterCapsAndDefaultsImpl, this, 587 base::MessageLoopProxy::current())); 588 } 589 590 void StartGetPrinterSemanticCapsAndDefaults() { 591 g_service_process->io_thread()->message_loop_proxy()->PostTask( 592 FROM_HERE, 593 base::Bind(&PrinterCapsHandler::GetPrinterSemanticCapsAndDefaultsImpl, 594 this, base::MessageLoopProxy::current())); 595 } 596 597 private: 598 void GetPrinterCapsAndDefaultsImpl( 599 const scoped_refptr<base::MessageLoopProxy>& 600 client_message_loop_proxy) { 601 DCHECK(g_service_process->io_thread()->message_loop_proxy()-> 602 BelongsToCurrentThread()); 603 scoped_ptr<ServiceUtilityProcessHost> utility_host( 604 new ServiceUtilityProcessHost(this, client_message_loop_proxy)); 605 if (utility_host->StartGetPrinterCapsAndDefaults(printer_name_)) { 606 // The object will self-destruct when the child process dies. 607 utility_host.release(); 608 } else { 609 client_message_loop_proxy->PostTask( 610 FROM_HERE, 611 base::Bind(&PrinterCapsHandler::OnChildDied, this)); 612 } 613 } 614 615 void GetPrinterSemanticCapsAndDefaultsImpl( 616 const scoped_refptr<base::MessageLoopProxy>& 617 client_message_loop_proxy) { 618 DCHECK(g_service_process->io_thread()->message_loop_proxy()-> 619 BelongsToCurrentThread()); 620 scoped_ptr<ServiceUtilityProcessHost> utility_host( 621 new ServiceUtilityProcessHost(this, client_message_loop_proxy)); 622 if (utility_host->StartGetPrinterSemanticCapsAndDefaults(printer_name_)) { 623 // The object will self-destruct when the child process dies. 624 utility_host.release(); 625 } else { 626 client_message_loop_proxy->PostTask( 627 FROM_HERE, 628 base::Bind(&PrinterCapsHandler::OnChildDied, this)); 629 } 630 } 631 632 std::string printer_name_; 633 PrintSystem::PrinterCapsAndDefaultsCallback callback_; 634 }; 635 636 class PrintSystemWin : public PrintSystem { 637 public: 638 PrintSystemWin(); 639 640 // PrintSystem implementation. 641 virtual PrintSystemResult Init() OVERRIDE; 642 virtual PrintSystem::PrintSystemResult EnumeratePrinters( 643 printing::PrinterList* printer_list) OVERRIDE; 644 virtual void GetPrinterCapsAndDefaults( 645 const std::string& printer_name, 646 const PrinterCapsAndDefaultsCallback& callback) OVERRIDE; 647 virtual bool IsValidPrinter(const std::string& printer_name) OVERRIDE; 648 virtual bool ValidatePrintTicket( 649 const std::string& printer_name, 650 const std::string& print_ticket_data, 651 const std::string& print_ticket_data_mime_type) OVERRIDE; 652 virtual bool GetJobDetails(const std::string& printer_name, 653 PlatformJobId job_id, 654 PrintJobDetails *job_details) OVERRIDE; 655 virtual PrintSystem::PrintServerWatcher* CreatePrintServerWatcher() OVERRIDE; 656 virtual PrintSystem::PrinterWatcher* CreatePrinterWatcher( 657 const std::string& printer_name) OVERRIDE; 658 virtual PrintSystem::JobSpooler* CreateJobSpooler() OVERRIDE; 659 virtual bool UseCddAndCjt() OVERRIDE; 660 virtual std::string GetSupportedMimeTypes() OVERRIDE; 661 662 private: 663 std::string PrintSystemWin::GetPrinterDriverInfo( 664 const std::string& printer_name) const; 665 666 scoped_refptr<printing::PrintBackend> print_backend_; 667 bool use_cdd_; 668 DISALLOW_COPY_AND_ASSIGN(PrintSystemWin); 669 }; 670 671 PrintSystemWin::PrintSystemWin() : use_cdd_(true) { 672 print_backend_ = printing::PrintBackend::CreateInstance(NULL); 673 } 674 675 PrintSystem::PrintSystemResult PrintSystemWin::Init() { 676 use_cdd_ = !CommandLine::ForCurrentProcess()->HasSwitch( 677 switches::kEnableCloudPrintXps); 678 679 if (!use_cdd_) 680 use_cdd_ = !printing::XPSModule::Init(); 681 682 if (!use_cdd_) { 683 HPTPROVIDER provider = NULL; 684 HRESULT hr = printing::XPSModule::OpenProvider(L"", 1, &provider); 685 if (provider) 686 printing::XPSModule::CloseProvider(provider); 687 // Use cdd if error is different from expected. 688 use_cdd_ = (hr != HRESULT_FROM_WIN32(ERROR_INVALID_PRINTER_NAME)); 689 } 690 691 return PrintSystemResult(true, std::string()); 692 } 693 694 PrintSystem::PrintSystemResult PrintSystemWin::EnumeratePrinters( 695 printing::PrinterList* printer_list) { 696 bool ret = print_backend_->EnumeratePrinters(printer_list); 697 return PrintSystemResult(ret, std::string()); 698 } 699 700 void PrintSystemWin::GetPrinterCapsAndDefaults( 701 const std::string& printer_name, 702 const PrinterCapsAndDefaultsCallback& callback) { 703 // Launch as child process to retrieve the capabilities and defaults because 704 // this involves invoking a printer driver DLL and crashes have been known to 705 // occur. 706 PrinterCapsHandler* handler = new PrinterCapsHandler(printer_name, callback); 707 handler->AddRef(); 708 if (use_cdd_) 709 handler->StartGetPrinterSemanticCapsAndDefaults(); 710 else 711 handler->StartGetPrinterCapsAndDefaults(); 712 } 713 714 bool PrintSystemWin::IsValidPrinter(const std::string& printer_name) { 715 return print_backend_->IsValidPrinter(printer_name); 716 } 717 718 bool PrintSystemWin::ValidatePrintTicket( 719 const std::string& printer_name, 720 const std::string& print_ticket_data, 721 const std::string& print_ticket_data_mime_type) { 722 crash_keys::ScopedPrinterInfo crash_key(GetPrinterDriverInfo(printer_name)); 723 724 if (use_cdd_) { 725 return print_ticket_data_mime_type == kContentTypeJSON && 726 IsValidCjt(print_ticket_data); 727 } 728 DCHECK(print_ticket_data_mime_type == kContentTypeXML); 729 730 printing::ScopedXPSInitializer xps_initializer; 731 if (!xps_initializer.initialized()) { 732 // TODO(sanjeevr): Handle legacy proxy case (with no prntvpt.dll) 733 return false; 734 } 735 bool ret = false; 736 HPTPROVIDER provider = NULL; 737 printing::XPSModule::OpenProvider(base::UTF8ToWide(printer_name), 1, 738 &provider); 739 if (provider) { 740 base::win::ScopedComPtr<IStream> print_ticket_stream; 741 CreateStreamOnHGlobal(NULL, TRUE, print_ticket_stream.Receive()); 742 ULONG bytes_written = 0; 743 print_ticket_stream->Write(print_ticket_data.c_str(), 744 print_ticket_data.length(), 745 &bytes_written); 746 DCHECK(bytes_written == print_ticket_data.length()); 747 LARGE_INTEGER pos = {0}; 748 ULARGE_INTEGER new_pos = {0}; 749 print_ticket_stream->Seek(pos, STREAM_SEEK_SET, &new_pos); 750 base::win::ScopedBstr error; 751 base::win::ScopedComPtr<IStream> result_ticket_stream; 752 CreateStreamOnHGlobal(NULL, TRUE, result_ticket_stream.Receive()); 753 ret = SUCCEEDED(printing::XPSModule::MergeAndValidatePrintTicket( 754 provider, 755 print_ticket_stream.get(), 756 NULL, 757 kPTJobScope, 758 result_ticket_stream.get(), 759 error.Receive())); 760 printing::XPSModule::CloseProvider(provider); 761 } 762 return ret; 763 } 764 765 bool PrintSystemWin::GetJobDetails(const std::string& printer_name, 766 PlatformJobId job_id, 767 PrintJobDetails *job_details) { 768 crash_keys::ScopedPrinterInfo crash_key( 769 print_backend_->GetPrinterDriverInfo(printer_name)); 770 DCHECK(job_details); 771 printing::ScopedPrinterHandle printer_handle; 772 std::wstring printer_name_wide = base::UTF8ToWide(printer_name); 773 printer_handle.OpenPrinter(printer_name_wide.c_str()); 774 DCHECK(printer_handle.IsValid()); 775 bool ret = false; 776 if (printer_handle.IsValid()) { 777 DWORD bytes_needed = 0; 778 GetJob(printer_handle, job_id, 1, NULL, 0, &bytes_needed); 779 DWORD last_error = GetLastError(); 780 if (ERROR_INVALID_PARAMETER != last_error) { 781 // ERROR_INVALID_PARAMETER normally means that the job id is not valid. 782 DCHECK(last_error == ERROR_INSUFFICIENT_BUFFER); 783 scoped_ptr<BYTE[]> job_info_buffer(new BYTE[bytes_needed]); 784 if (GetJob(printer_handle, job_id, 1, job_info_buffer.get(), bytes_needed, 785 &bytes_needed)) { 786 JOB_INFO_1 *job_info = 787 reinterpret_cast<JOB_INFO_1 *>(job_info_buffer.get()); 788 if (job_info->pStatus) { 789 base::WideToUTF8(job_info->pStatus, wcslen(job_info->pStatus), 790 &job_details->status_message); 791 } 792 job_details->platform_status_flags = job_info->Status; 793 if ((job_info->Status & JOB_STATUS_COMPLETE) || 794 (job_info->Status & JOB_STATUS_PRINTED)) { 795 job_details->status = PRINT_JOB_STATUS_COMPLETED; 796 } else if (job_info->Status & JOB_STATUS_ERROR) { 797 job_details->status = PRINT_JOB_STATUS_ERROR; 798 } else { 799 job_details->status = PRINT_JOB_STATUS_IN_PROGRESS; 800 } 801 job_details->total_pages = job_info->TotalPages; 802 job_details->pages_printed = job_info->PagesPrinted; 803 ret = true; 804 } 805 } 806 } 807 return ret; 808 } 809 810 PrintSystem::PrintServerWatcher* 811 PrintSystemWin::CreatePrintServerWatcher() { 812 return new PrintServerWatcherWin(); 813 } 814 815 PrintSystem::PrinterWatcher* PrintSystemWin::CreatePrinterWatcher( 816 const std::string& printer_name) { 817 DCHECK(!printer_name.empty()); 818 return new PrinterWatcherWin(printer_name); 819 } 820 821 PrintSystem::JobSpooler* PrintSystemWin::CreateJobSpooler() { 822 return new JobSpoolerWin(); 823 } 824 825 bool PrintSystemWin::UseCddAndCjt() { 826 return use_cdd_; 827 } 828 829 std::string PrintSystemWin::GetSupportedMimeTypes() { 830 std::string result; 831 if (!use_cdd_) { 832 result = kContentTypeXPS; 833 result += ","; 834 } 835 result += kContentTypePDF; 836 return result; 837 } 838 839 std::string PrintSystemWin::GetPrinterDriverInfo( 840 const std::string& printer_name) const { 841 return print_backend_->GetPrinterDriverInfo(printer_name); 842 } 843 844 } // namespace 845 846 scoped_refptr<PrintSystem> PrintSystem::CreateInstance( 847 const base::DictionaryValue* print_system_settings) { 848 return new PrintSystemWin; 849 } 850 851 } // namespace cloud_print 852