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