| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/service/cloud_print/print_system.h" | 5 #include "chrome/service/cloud_print/print_system.h" |
| 6 | 6 |
| 7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
| 8 #include "base/files/file_util.h" | 8 #include "base/files/file_util.h" |
| 9 #include "base/json/json_writer.h" | 9 #include "base/json/json_writer.h" |
| 10 #include "base/memory/scoped_ptr.h" | 10 #include "base/memory/scoped_ptr.h" |
| 11 #include "base/strings/utf_string_conversions.h" | 11 #include "base/strings/utf_string_conversions.h" |
| 12 #include "base/win/object_watcher.h" | 12 #include "base/win/object_watcher.h" |
| 13 #include "base/win/scoped_bstr.h" | 13 #include "base/win/scoped_bstr.h" |
| 14 #include "base/win/scoped_comptr.h" | 14 #include "base/win/scoped_comptr.h" |
| 15 #include "base/win/scoped_hdc.h" | 15 #include "base/win/scoped_hdc.h" |
| 16 #include "chrome/common/chrome_switches.h" | 16 #include "chrome/common/chrome_switches.h" |
| 17 #include "chrome/common/cloud_print/cloud_print_cdd_conversion.h" | 17 #include "chrome/common/cloud_print/cloud_print_cdd_conversion.h" |
| 18 #include "chrome/common/cloud_print/cloud_print_constants.h" | 18 #include "chrome/common/cloud_print/cloud_print_constants.h" |
| 19 #include "chrome/common/crash_keys.h" | 19 #include "chrome/common/crash_keys.h" |
| 20 #include "chrome/service/cloud_print/cdd_conversion_win.h" | 20 #include "chrome/service/cloud_print/cdd_conversion_win.h" |
| 21 #include "chrome/service/service_process.h" | 21 #include "chrome/service/service_process.h" |
| 22 #include "chrome/service/service_utility_process_host.h" | 22 #include "chrome/service/service_utility_process_host.h" |
| 23 #include "printing/backend/win_helper.h" | 23 #include "printing/backend/win_helper.h" |
| 24 #include "printing/emf_win.h" | 24 #include "printing/emf_win.h" |
| 25 #include "printing/page_range.h" | 25 #include "printing/page_range.h" |
| 26 #include "printing/pdf_render_settings.h" |
| 26 #include "printing/printing_utils.h" | 27 #include "printing/printing_utils.h" |
| 28 #include "ui/gfx/geometry/rect.h" |
| 27 | 29 |
| 28 namespace cloud_print { | 30 namespace cloud_print { |
| 29 | 31 |
| 30 namespace { | 32 namespace { |
| 31 | 33 |
| 32 class PrintSystemWatcherWin : public base::win::ObjectWatcher::Delegate { | 34 class PrintSystemWatcherWin : public base::win::ObjectWatcher::Delegate { |
| 33 public: | 35 public: |
| 34 PrintSystemWatcherWin() | 36 PrintSystemWatcherWin() |
| 35 : delegate_(NULL), | 37 : delegate_(NULL), |
| 36 did_signal_(false) { | 38 did_signal_(false) { |
| (...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 238 | 240 |
| 239 protected: | 241 protected: |
| 240 virtual ~JobSpoolerWin() {} | 242 virtual ~JobSpoolerWin() {} |
| 241 | 243 |
| 242 private: | 244 private: |
| 243 // We use a Core class because we want a separate RefCountedThreadSafe | 245 // We use a Core class because we want a separate RefCountedThreadSafe |
| 244 // implementation for ServiceUtilityProcessHost::Client. | 246 // implementation for ServiceUtilityProcessHost::Client. |
| 245 class Core : public ServiceUtilityProcessHost::Client, | 247 class Core : public ServiceUtilityProcessHost::Client, |
| 246 public base::win::ObjectWatcher::Delegate { | 248 public base::win::ObjectWatcher::Delegate { |
| 247 public: | 249 public: |
| 248 Core() | 250 Core() : job_id_(-1), delegate_(NULL), saved_dc_(0) {} |
| 249 : last_page_printed_(-1), | |
| 250 job_id_(-1), | |
| 251 delegate_(NULL), | |
| 252 saved_dc_(0) { | |
| 253 } | |
| 254 | 251 |
| 255 ~Core() {} | 252 ~Core() {} |
| 256 | 253 |
| 257 bool Spool(const std::string& print_ticket, | 254 bool Spool(const std::string& print_ticket, |
| 258 const std::string& print_ticket_mime_type, | 255 const std::string& print_ticket_mime_type, |
| 259 const base::FilePath& print_data_file_path, | 256 const base::FilePath& print_data_file_path, |
| 260 const std::string& print_data_mime_type, | 257 const std::string& print_data_mime_type, |
| 261 const std::string& printer_name, | 258 const std::string& printer_name, |
| 262 const std::string& job_title, | 259 const std::string& job_title, |
| 263 JobSpooler::Delegate* delegate) { | 260 JobSpooler::Delegate* delegate) { |
| 264 if (delegate_) { | 261 if (delegate_) { |
| 265 // We are already in the process of printing. | 262 // We are already in the process of printing. |
| 266 NOTREACHED(); | 263 NOTREACHED(); |
| 267 return false; | 264 return false; |
| 268 } | 265 } |
| 269 base::string16 printer_wide = base::UTF8ToWide(printer_name); | 266 base::string16 printer_wide = base::UTF8ToWide(printer_name); |
| 270 last_page_printed_ = -1; | |
| 271 // We only support PDF and XPS documents for now. | 267 // We only support PDF and XPS documents for now. |
| 272 if (print_data_mime_type == kContentTypePDF) { | 268 if (print_data_mime_type == kContentTypePDF) { |
| 273 scoped_ptr<DEVMODE, base::FreeDeleter> dev_mode; | 269 scoped_ptr<DEVMODE, base::FreeDeleter> dev_mode; |
| 274 if (print_ticket_mime_type == kContentTypeJSON) { | 270 if (print_ticket_mime_type == kContentTypeJSON) { |
| 275 dev_mode = CjtToDevMode(printer_wide, print_ticket); | 271 dev_mode = CjtToDevMode(printer_wide, print_ticket); |
| 276 } else { | 272 } else { |
| 277 DCHECK(print_ticket_mime_type == kContentTypeXML); | 273 DCHECK(print_ticket_mime_type == kContentTypeXML); |
| 278 dev_mode = printing::XpsTicketToDevMode(printer_wide, print_ticket); | 274 dev_mode = printing::XpsTicketToDevMode(printer_wide, print_ticket); |
| 279 } | 275 } |
| 280 | 276 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 295 DCHECK(printing::SimplifyDocumentTitle(doc_name) == doc_name); | 291 DCHECK(printing::SimplifyDocumentTitle(doc_name) == doc_name); |
| 296 di.lpszDocName = doc_name.c_str(); | 292 di.lpszDocName = doc_name.c_str(); |
| 297 job_id_ = StartDoc(dc, &di); | 293 job_id_ = StartDoc(dc, &di); |
| 298 if (job_id_ <= 0) | 294 if (job_id_ <= 0) |
| 299 return false; | 295 return false; |
| 300 | 296 |
| 301 printer_dc_.Set(dc); | 297 printer_dc_.Set(dc); |
| 302 saved_dc_ = SaveDC(printer_dc_.Get()); | 298 saved_dc_ = SaveDC(printer_dc_.Get()); |
| 303 print_data_file_path_ = print_data_file_path; | 299 print_data_file_path_ = print_data_file_path; |
| 304 delegate_ = delegate; | 300 delegate_ = delegate; |
| 305 RenderNextPDFPages(); | 301 RenderPDFPages(); |
| 306 } else if (print_data_mime_type == kContentTypeXPS) { | 302 } else if (print_data_mime_type == kContentTypeXPS) { |
| 307 DCHECK(print_ticket_mime_type == kContentTypeXML); | 303 DCHECK(print_ticket_mime_type == kContentTypeXML); |
| 308 bool ret = PrintXPSDocument(printer_name, | 304 bool ret = PrintXPSDocument(printer_name, |
| 309 job_title, | 305 job_title, |
| 310 print_data_file_path, | 306 print_data_file_path, |
| 311 print_ticket); | 307 print_ticket); |
| 312 if (ret) | 308 if (ret) |
| 313 delegate_ = delegate; | 309 delegate_ = delegate; |
| 314 return ret; | 310 return ret; |
| 315 } else { | 311 } else { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 328 int offset_x = ::GetDeviceCaps(printer_dc_.Get(), PHYSICALOFFSETX); | 324 int offset_x = ::GetDeviceCaps(printer_dc_.Get(), PHYSICALOFFSETX); |
| 329 int offset_y = ::GetDeviceCaps(printer_dc_.Get(), PHYSICALOFFSETY); | 325 int offset_y = ::GetDeviceCaps(printer_dc_.Get(), PHYSICALOFFSETY); |
| 330 XFORM xform = {0}; | 326 XFORM xform = {0}; |
| 331 xform.eDx = static_cast<float>(-offset_x); | 327 xform.eDx = static_cast<float>(-offset_x); |
| 332 xform.eDy = static_cast<float>(-offset_y); | 328 xform.eDy = static_cast<float>(-offset_y); |
| 333 xform.eM11 = xform.eM22 = 1.0 / scale_factor; | 329 xform.eM11 = xform.eM22 = 1.0 / scale_factor; |
| 334 SetWorldTransform(printer_dc_.Get(), &xform); | 330 SetWorldTransform(printer_dc_.Get(), &xform); |
| 335 } | 331 } |
| 336 | 332 |
| 337 // ServiceUtilityProcessHost::Client implementation. | 333 // ServiceUtilityProcessHost::Client implementation. |
| 338 virtual void OnRenderPDFPagesToMetafileSucceeded( | 334 virtual void OnRenderPDFPagesToMetafilePageDone( |
| 339 const printing::MetafilePlayer& metafile, | 335 double scale_factor, |
| 340 int highest_rendered_page_number, | 336 const printing::MetafilePlayer& emf) OVERRIDE { |
| 341 double scale_factor) OVERRIDE { | |
| 342 PreparePageDCForPrinting(printer_dc_.Get(), scale_factor); | 337 PreparePageDCForPrinting(printer_dc_.Get(), scale_factor); |
| 343 metafile.SafePlayback(printer_dc_.Get()); | 338 emf.SafePlayback(printer_dc_.Get()); |
| 344 bool done_printing = (highest_rendered_page_number != | |
| 345 last_page_printed_ + kPageCountPerBatch); | |
| 346 last_page_printed_ = highest_rendered_page_number; | |
| 347 if (done_printing) | |
| 348 PrintJobDone(); | |
| 349 else | |
| 350 RenderNextPDFPages(); | |
| 351 } | 339 } |
| 352 | 340 |
| 341 // ServiceUtilityProcessHost::Client implementation. |
| 342 virtual void OnRenderPDFPagesToMetafileSucceeded() OVERRIDE { |
| 343 PrintJobDone(true); |
| 344 } |
| 345 |
| 346 virtual void OnRenderPDFPagesToMetafileFailed() OVERRIDE { |
| 347 PrintJobDone(false); |
| 348 } |
| 349 |
| 350 virtual void OnChildDied() OVERRIDE { PrintJobDone(false); } |
| 351 |
| 353 // base::win::ObjectWatcher::Delegate implementation. | 352 // base::win::ObjectWatcher::Delegate implementation. |
| 354 virtual void OnObjectSignaled(HANDLE object) OVERRIDE { | 353 virtual void OnObjectSignaled(HANDLE object) OVERRIDE { |
| 355 DCHECK(xps_print_job_); | 354 DCHECK(xps_print_job_); |
| 356 DCHECK(object == job_progress_event_.Get()); | 355 DCHECK(object == job_progress_event_.Get()); |
| 357 ResetEvent(job_progress_event_.Get()); | 356 ResetEvent(job_progress_event_.Get()); |
| 358 if (!delegate_) | 357 if (!delegate_) |
| 359 return; | 358 return; |
| 360 XPS_JOB_STATUS job_status = {0}; | 359 XPS_JOB_STATUS job_status = {0}; |
| 361 xps_print_job_->GetJobStatus(&job_status); | 360 xps_print_job_->GetJobStatus(&job_status); |
| 362 if ((job_status.completion == XPS_JOB_CANCELLED) || | 361 if ((job_status.completion == XPS_JOB_CANCELLED) || |
| 363 (job_status.completion == XPS_JOB_FAILED)) { | 362 (job_status.completion == XPS_JOB_FAILED)) { |
| 364 delegate_->OnJobSpoolFailed(); | 363 delegate_->OnJobSpoolFailed(); |
| 365 } else if (job_status.jobId || | 364 } else if (job_status.jobId || |
| 366 (job_status.completion == XPS_JOB_COMPLETED)) { | 365 (job_status.completion == XPS_JOB_COMPLETED)) { |
| 367 // Note: In the case of the XPS document being printed to the | 366 // Note: In the case of the XPS document being printed to the |
| 368 // Microsoft XPS Document Writer, it seems to skip spooling the job | 367 // Microsoft XPS Document Writer, it seems to skip spooling the job |
| 369 // and goes to the completed state without ever assigning a job id. | 368 // and goes to the completed state without ever assigning a job id. |
| 370 delegate_->OnJobSpoolSucceeded(job_status.jobId); | 369 delegate_->OnJobSpoolSucceeded(job_status.jobId); |
| 371 } else { | 370 } else { |
| 372 job_progress_watcher_.StopWatching(); | 371 job_progress_watcher_.StopWatching(); |
| 373 job_progress_watcher_.StartWatching(job_progress_event_.Get(), this); | 372 job_progress_watcher_.StartWatching(job_progress_event_.Get(), this); |
| 374 } | 373 } |
| 375 } | 374 } |
| 376 | 375 |
| 377 virtual void OnRenderPDFPagesToMetafileFailed() OVERRIDE { | |
| 378 PrintJobDone(); | |
| 379 } | |
| 380 | |
| 381 virtual void OnChildDied() OVERRIDE { | |
| 382 PrintJobDone(); | |
| 383 } | |
| 384 | |
| 385 private: | 376 private: |
| 386 // Helper class to allow PrintXPSDocument() to have multiple exits. | 377 // Helper class to allow PrintXPSDocument() to have multiple exits. |
| 387 class PrintJobCanceler { | 378 class PrintJobCanceler { |
| 388 public: | 379 public: |
| 389 explicit PrintJobCanceler( | 380 explicit PrintJobCanceler( |
| 390 base::win::ScopedComPtr<IXpsPrintJob>* job_ptr) | 381 base::win::ScopedComPtr<IXpsPrintJob>* job_ptr) |
| 391 : job_ptr_(job_ptr) { | 382 : job_ptr_(job_ptr) { |
| 392 } | 383 } |
| 393 ~PrintJobCanceler() { | 384 ~PrintJobCanceler() { |
| 394 if (job_ptr_ && *job_ptr_) { | 385 if (job_ptr_ && *job_ptr_) { |
| 395 (*job_ptr_)->Cancel(); | 386 (*job_ptr_)->Cancel(); |
| 396 job_ptr_->Release(); | 387 job_ptr_->Release(); |
| 397 } | 388 } |
| 398 } | 389 } |
| 399 | 390 |
| 400 void reset() { job_ptr_ = NULL; } | 391 void reset() { job_ptr_ = NULL; } |
| 401 | 392 |
| 402 private: | 393 private: |
| 403 base::win::ScopedComPtr<IXpsPrintJob>* job_ptr_; | 394 base::win::ScopedComPtr<IXpsPrintJob>* job_ptr_; |
| 404 | 395 |
| 405 DISALLOW_COPY_AND_ASSIGN(PrintJobCanceler); | 396 DISALLOW_COPY_AND_ASSIGN(PrintJobCanceler); |
| 406 }; | 397 }; |
| 407 | 398 |
| 408 void PrintJobDone() { | 399 void PrintJobDone(bool success) { |
| 409 // If there is no delegate, then there is nothing pending to process. | 400 // If there is no delegate, then there is nothing pending to process. |
| 410 if (!delegate_) | 401 if (!delegate_) |
| 411 return; | 402 return; |
| 412 RestoreDC(printer_dc_.Get(), saved_dc_); | 403 RestoreDC(printer_dc_.Get(), saved_dc_); |
| 413 EndDoc(printer_dc_.Get()); | 404 EndDoc(printer_dc_.Get()); |
| 414 if (-1 == last_page_printed_) { | 405 if (success) { |
| 406 delegate_->OnJobSpoolSucceeded(job_id_); |
| 407 } else { |
| 415 delegate_->OnJobSpoolFailed(); | 408 delegate_->OnJobSpoolFailed(); |
| 416 } else { | |
| 417 delegate_->OnJobSpoolSucceeded(job_id_); | |
| 418 } | 409 } |
| 419 delegate_ = NULL; | 410 delegate_ = NULL; |
| 420 } | 411 } |
| 421 | 412 |
| 422 void RenderNextPDFPages() { | 413 void RenderPDFPages() { |
| 423 printing::PageRange range; | |
| 424 // Render 10 pages at a time. | |
| 425 range.from = last_page_printed_ + 1; | |
| 426 range.to = last_page_printed_ + kPageCountPerBatch; | |
| 427 std::vector<printing::PageRange> page_ranges; | |
| 428 page_ranges.push_back(range); | |
| 429 | |
| 430 int printer_dpi = ::GetDeviceCaps(printer_dc_.Get(), LOGPIXELSX); | 414 int printer_dpi = ::GetDeviceCaps(printer_dc_.Get(), LOGPIXELSX); |
| 431 int dc_width = GetDeviceCaps(printer_dc_.Get(), PHYSICALWIDTH); | 415 int dc_width = GetDeviceCaps(printer_dc_.Get(), PHYSICALWIDTH); |
| 432 int dc_height = GetDeviceCaps(printer_dc_.Get(), PHYSICALHEIGHT); | 416 int dc_height = GetDeviceCaps(printer_dc_.Get(), PHYSICALHEIGHT); |
| 433 gfx::Rect render_area(0, 0, dc_width, dc_height); | 417 gfx::Rect render_area(0, 0, dc_width, dc_height); |
| 434 g_service_process->io_thread()->message_loop_proxy()->PostTask( | 418 g_service_process->io_thread()->message_loop_proxy()->PostTask( |
| 435 FROM_HERE, | 419 FROM_HERE, |
| 436 base::Bind(&JobSpoolerWin::Core::RenderPDFPagesInSandbox, this, | 420 base::Bind(&JobSpoolerWin::Core::RenderPDFPagesInSandbox, |
| 437 print_data_file_path_, render_area, printer_dpi, | 421 this, |
| 438 page_ranges, base::MessageLoopProxy::current())); | 422 print_data_file_path_, |
| 423 render_area, |
| 424 printer_dpi, |
| 425 base::MessageLoopProxy::current())); |
| 439 } | 426 } |
| 440 | 427 |
| 441 // Called on the service process IO thread. | 428 // Called on the service process IO thread. |
| 442 void RenderPDFPagesInSandbox( | 429 void RenderPDFPagesInSandbox(const base::FilePath& pdf_path, |
| 443 const base::FilePath& pdf_path, const gfx::Rect& render_area, | 430 const gfx::Rect& render_area, |
| 444 int render_dpi, const std::vector<printing::PageRange>& page_ranges, | 431 int render_dpi, |
| 445 const scoped_refptr<base::MessageLoopProxy>& | 432 const scoped_refptr<base::MessageLoopProxy>& |
| 446 client_message_loop_proxy) { | 433 client_message_loop_proxy) { |
| 447 DCHECK(g_service_process->io_thread()->message_loop_proxy()-> | 434 DCHECK(g_service_process->io_thread()->message_loop_proxy()-> |
| 448 BelongsToCurrentThread()); | 435 BelongsToCurrentThread()); |
| 449 scoped_ptr<ServiceUtilityProcessHost> utility_host( | 436 scoped_ptr<ServiceUtilityProcessHost> utility_host( |
| 450 new ServiceUtilityProcessHost(this, client_message_loop_proxy)); | 437 new ServiceUtilityProcessHost(this, client_message_loop_proxy)); |
| 451 // TODO(gene): For now we disabling autorotation for CloudPrinting. | 438 // TODO(gene): For now we disabling autorotation for CloudPrinting. |
| 452 // Landscape/Portrait setting is passed in the print ticket and | 439 // Landscape/Portrait setting is passed in the print ticket and |
| 453 // server is generating portrait PDF always. | 440 // server is generating portrait PDF always. |
| 454 // We should enable autorotation once server will be able to generate | 441 // We should enable autorotation once server will be able to generate |
| 455 // PDF that matches paper size and orientation. | 442 // PDF that matches paper size and orientation. |
| 456 if (utility_host->StartRenderPDFPagesToMetafile( | 443 if (utility_host->StartRenderPDFPagesToMetafile( |
| 457 pdf_path, | 444 pdf_path, |
| 458 printing::PdfRenderSettings(render_area, render_dpi, false), | 445 printing::PdfRenderSettings(render_area, render_dpi, false))) { |
| 459 page_ranges)) { | |
| 460 // The object will self-destruct when the child process dies. | 446 // The object will self-destruct when the child process dies. |
| 461 utility_host.release(); | 447 utility_host.release(); |
| 462 } | 448 } |
| 463 } | 449 } |
| 464 | 450 |
| 465 bool PrintXPSDocument(const std::string& printer_name, | 451 bool PrintXPSDocument(const std::string& printer_name, |
| 466 const std::string& job_title, | 452 const std::string& job_title, |
| 467 const base::FilePath& print_data_file_path, | 453 const base::FilePath& print_data_file_path, |
| 468 const std::string& print_ticket) { | 454 const std::string& print_ticket) { |
| 469 if (!printing::XPSPrintModule::Init()) | 455 if (!printing::XPSPrintModule::Init()) |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 502 return false; | 488 return false; |
| 503 DCHECK_EQ(document_data.length(), doc_bytes_written); | 489 DCHECK_EQ(document_data.length(), doc_bytes_written); |
| 504 if (FAILED(doc_stream->Close())) | 490 if (FAILED(doc_stream->Close())) |
| 505 return false; | 491 return false; |
| 506 | 492 |
| 507 job_progress_watcher_.StartWatching(job_progress_event_.Get(), this); | 493 job_progress_watcher_.StartWatching(job_progress_event_.Get(), this); |
| 508 job_canceler.reset(); | 494 job_canceler.reset(); |
| 509 return true; | 495 return true; |
| 510 } | 496 } |
| 511 | 497 |
| 512 // Some Cairo-generated PDFs from Chrome OS result in huge metafiles. | |
| 513 // So the PageCountPerBatch is set to 1 for now. | |
| 514 // TODO(sanjeevr): Figure out a smarter way to determine the pages per | |
| 515 // batch. Filed a bug to track this at | |
| 516 // http://code.google.com/p/chromium/issues/detail?id=57350. | |
| 517 static const int kPageCountPerBatch = 1; | |
| 518 | |
| 519 int last_page_printed_; | |
| 520 PlatformJobId job_id_; | 498 PlatformJobId job_id_; |
| 521 PrintSystem::JobSpooler::Delegate* delegate_; | 499 PrintSystem::JobSpooler::Delegate* delegate_; |
| 522 int saved_dc_; | 500 int saved_dc_; |
| 523 base::win::ScopedCreateDC printer_dc_; | 501 base::win::ScopedCreateDC printer_dc_; |
| 524 base::FilePath print_data_file_path_; | 502 base::FilePath print_data_file_path_; |
| 525 base::win::ScopedHandle job_progress_event_; | 503 base::win::ScopedHandle job_progress_event_; |
| 526 base::win::ObjectWatcher job_progress_watcher_; | 504 base::win::ObjectWatcher job_progress_watcher_; |
| 527 base::win::ScopedComPtr<IXpsPrintJob> xps_print_job_; | 505 base::win::ScopedComPtr<IXpsPrintJob> xps_print_job_; |
| 528 | 506 |
| 529 DISALLOW_COPY_AND_ASSIGN(Core); | 507 DISALLOW_COPY_AND_ASSIGN(Core); |
| (...skipping 310 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 840 } | 818 } |
| 841 | 819 |
| 842 } // namespace | 820 } // namespace |
| 843 | 821 |
| 844 scoped_refptr<PrintSystem> PrintSystem::CreateInstance( | 822 scoped_refptr<PrintSystem> PrintSystem::CreateInstance( |
| 845 const base::DictionaryValue* print_system_settings) { | 823 const base::DictionaryValue* print_system_settings) { |
| 846 return new PrintSystemWin; | 824 return new PrintSystemWin; |
| 847 } | 825 } |
| 848 | 826 |
| 849 } // namespace cloud_print | 827 } // namespace cloud_print |
| OLD | NEW |