| Index: chrome/browser/printing/cloud_print/printer_info_win.cc | 
| =================================================================== | 
| --- chrome/browser/printing/cloud_print/printer_info_win.cc	(revision 0) | 
| +++ chrome/browser/printing/cloud_print/printer_info_win.cc	(revision 0) | 
| @@ -0,0 +1,509 @@ | 
| +// Copyright (c) 2010 The Chromium Authors. All rights reserved. | 
| +// Use of this source code is governed by a BSD-style license that can be | 
| +// found in the LICENSE file. | 
| + | 
| +#include "chrome/browser/printing/cloud_print/printer_info.h" | 
| + | 
| +#include <windows.h> | 
| +#include <objidl.h> | 
| +#include <ocidl.h> | 
| +#include <olectl.h> | 
| +#include <prntvpt.h> | 
| +#include <winspool.h> | 
| + | 
| +#include "base/lock.h" | 
| +#include "base/object_watcher.h" | 
| +#include "base/scoped_bstr_win.h" | 
| +#include "base/scoped_comptr_win.h" | 
| +#include "base/scoped_handle_win.h" | 
| +#include "base/scoped_ptr.h" | 
| +#include "base/utf_string_conversions.h" | 
| + | 
| +#pragma comment(lib, "prntvpt.lib") | 
| +#pragma comment(lib, "rpcrt4.lib") | 
| + | 
| +namespace { | 
| + | 
| +class DevMode { | 
| + public: | 
| +  DevMode() : dm_(NULL) {} | 
| +  ~DevMode() { Free(); } | 
| + | 
| +  void Allocate(int size) { | 
| +    Free(); | 
| +    dm_ = reinterpret_cast<DEVMODE*>(new char[size]); | 
| +  } | 
| + | 
| +  void Free() { | 
| +    if (dm_) | 
| +      delete dm_; | 
| +    dm_ = NULL; | 
| +  } | 
| + | 
| +  DEVMODE* dm_; | 
| + | 
| + private: | 
| +  DISALLOW_COPY_AND_ASSIGN(DevMode); | 
| +}; | 
| + | 
| +bool InitXPSModule() { | 
| +  HMODULE prntvpt_module = LoadLibrary(L"prntvpt.dll"); | 
| +  return (NULL != prntvpt_module); | 
| +} | 
| + | 
| +inline HRESULT GetLastErrorHR() { | 
| +  LONG error = GetLastError(); | 
| +  return HRESULT_FROM_WIN32(error); | 
| +} | 
| + | 
| +HRESULT StreamFromPrintTicket(const std::string& print_ticket, | 
| +                              IStream** stream) { | 
| +  DCHECK(stream); | 
| +  HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, stream); | 
| +  if (FAILED(hr)) { | 
| +    return hr; | 
| +  } | 
| +  ULONG bytes_written = 0; | 
| +  (*stream)->Write(print_ticket.c_str(), print_ticket.length(), &bytes_written); | 
| +  DCHECK(bytes_written == print_ticket.length()); | 
| +  LARGE_INTEGER pos = {0}; | 
| +  ULARGE_INTEGER new_pos = {0}; | 
| +  (*stream)->Seek(pos, STREAM_SEEK_SET, &new_pos); | 
| +  return S_OK; | 
| +} | 
| + | 
| +HRESULT StreamOnHGlobalToString(IStream* stream, std::string* out) { | 
| +  DCHECK(stream); | 
| +  DCHECK(out); | 
| +  HGLOBAL hdata = NULL; | 
| +  HRESULT hr = GetHGlobalFromStream(stream, &hdata); | 
| +  if (SUCCEEDED(hr)) { | 
| +    DCHECK(hdata); | 
| +    ScopedHGlobal<char> locked_data(hdata); | 
| +    out->assign(locked_data.release(), locked_data.Size()); | 
| +  } | 
| +  return hr; | 
| +} | 
| + | 
| +HRESULT PrintTicketToDevMode(const std::string& printer_name, | 
| +                             const std::string& print_ticket, | 
| +                             DevMode* dev_mode) { | 
| +  DCHECK(dev_mode); | 
| + | 
| +  ScopedComPtr<IStream> pt_stream; | 
| +  HRESULT hr = StreamFromPrintTicket(print_ticket, pt_stream.Receive()); | 
| +  if (FAILED(hr)) | 
| +    return hr; | 
| + | 
| +  HPTPROVIDER provider = NULL; | 
| +  hr = PTOpenProvider(UTF8ToWide(printer_name).c_str(), 1, &provider); | 
| +  if (SUCCEEDED(hr)) { | 
| +    ULONG size = 0; | 
| +    DEVMODE* dm = NULL; | 
| +    hr = PTConvertPrintTicketToDevMode(provider, | 
| +                                       pt_stream, | 
| +                                       kUserDefaultDevmode, | 
| +                                       kPTDocumentScope, | 
| +                                       &size, | 
| +                                       &dm, | 
| +                                       NULL); | 
| +    if (SUCCEEDED(hr)) { | 
| +      dev_mode->Allocate(size); | 
| +      memcpy(dev_mode->dm_, dm, size); | 
| +      PTReleaseMemory(dm); | 
| +    } | 
| +    PTCloseProvider(provider); | 
| +  } | 
| +  return hr; | 
| +} | 
| + | 
| +HRESULT PrintPdf2DC(HDC dc, const FilePath& pdf_filename) { | 
| +  HRESULT hr = E_NOTIMPL; | 
| +  // TODO(sanjeevr): Implement this. | 
| +  NOTIMPLEMENTED(); | 
| +  return hr; | 
| +} | 
| + | 
| +}  // namespace | 
| + | 
| +namespace cloud_print { | 
| + | 
| +void EnumeratePrinters(PrinterList* printer_list) { | 
| +  DCHECK(printer_list); | 
| +  DWORD bytes_needed = 0; | 
| +  DWORD count_returned = 0; | 
| +  BOOL ret = EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS, NULL, 2, | 
| +                          NULL, 0, &bytes_needed, &count_returned); | 
| +  if (0 != bytes_needed) { | 
| +    scoped_ptr<BYTE> printer_info_buffer(new BYTE[bytes_needed]); | 
| +    ret = EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS, NULL, 2, | 
| +                       printer_info_buffer.get(), bytes_needed, &bytes_needed, | 
| +                       &count_returned); | 
| +    DCHECK(ret); | 
| +    PRINTER_INFO_2* printer_info = | 
| +        reinterpret_cast<PRINTER_INFO_2*>(printer_info_buffer.get()); | 
| +    for (DWORD index = 0; index < count_returned; index++) { | 
| +      PrinterBasicInfo info; | 
| +      info.printer_name = WideToUTF8(printer_info[index].pPrinterName); | 
| +      if (printer_info[index].pComment) | 
| +        info.printer_description = WideToUTF8(printer_info[index].pComment); | 
| +      info.printer_status = printer_info[index].Status; | 
| +      printer_list->push_back(info); | 
| +    } | 
| +  } | 
| +} | 
| + | 
| +bool GetPrinterCapsAndDefaults(const std::string& printer_name, | 
| +                               PrinterCapsAndDefaults* printer_info) { | 
| +  if (!InitXPSModule()) { | 
| +    // TODO(sanjeevr): Handle legacy proxy case (with no prntvpt.dll) | 
| +    return false; | 
| +  } | 
| +  if (!IsValidPrinter(printer_name)) { | 
| +    return false; | 
| +  } | 
| +  DCHECK(printer_info); | 
| +  HPTPROVIDER provider = NULL; | 
| +  std::wstring printer_name_wide = UTF8ToWide(printer_name); | 
| +  HRESULT hr = PTOpenProvider(printer_name_wide.c_str(), 1, &provider); | 
| +  DCHECK(SUCCEEDED(hr)); | 
| +  if (provider) { | 
| +    ScopedComPtr<IStream> print_capabilities_stream; | 
| +    hr = CreateStreamOnHGlobal(NULL, TRUE, | 
| +                               print_capabilities_stream.Receive()); | 
| +    DCHECK(SUCCEEDED(hr)); | 
| +    if (print_capabilities_stream) { | 
| +      ScopedBstr error; | 
| +      hr = PTGetPrintCapabilities(provider, NULL, print_capabilities_stream, | 
| +                                  error.Receive()); | 
| +      DCHECK(SUCCEEDED(hr)); | 
| +      if (FAILED(hr)) { | 
| +        return false; | 
| +      } | 
| +      hr = StreamOnHGlobalToString(print_capabilities_stream.get(), | 
| +                                   &printer_info->printer_capabilities); | 
| +      DCHECK(SUCCEEDED(hr)); | 
| +      printer_info->caps_mime_type = "text/xml"; | 
| +    } | 
| +    // TODO(sanjeevr): Add ScopedPrinterHandle | 
| +    HANDLE printer_handle = NULL; | 
| +    OpenPrinter(const_cast<LPTSTR>(printer_name_wide.c_str()), &printer_handle, | 
| +                NULL); | 
| +    DCHECK(printer_handle); | 
| +    if (printer_handle) { | 
| +      DWORD devmode_size = DocumentProperties( | 
| +          NULL, printer_handle, const_cast<LPTSTR>(printer_name_wide.c_str()), | 
| +          NULL, NULL, 0); | 
| +      DCHECK(0 != devmode_size); | 
| +      scoped_ptr<BYTE> devmode_out_buffer(new BYTE[devmode_size]); | 
| +      DEVMODE* devmode_out = | 
| +          reinterpret_cast<DEVMODE*>(devmode_out_buffer.get()); | 
| +      DocumentProperties( | 
| +          NULL, printer_handle, const_cast<LPTSTR>(printer_name_wide.c_str()), | 
| +          devmode_out, NULL, DM_OUT_BUFFER); | 
| +      ScopedComPtr<IStream> printer_defaults_stream; | 
| +      hr = CreateStreamOnHGlobal(NULL, TRUE, | 
| +                                 printer_defaults_stream.Receive()); | 
| +      DCHECK(SUCCEEDED(hr)); | 
| +      if (printer_defaults_stream) { | 
| +        hr = PTConvertDevModeToPrintTicket(provider, devmode_size, | 
| +                                           devmode_out, kPTJobScope, | 
| +                                           printer_defaults_stream); | 
| +        DCHECK(SUCCEEDED(hr)); | 
| +        if (SUCCEEDED(hr)) { | 
| +          hr = StreamOnHGlobalToString(printer_defaults_stream.get(), | 
| +                                       &printer_info->printer_defaults); | 
| +          DCHECK(SUCCEEDED(hr)); | 
| +          printer_info->defaults_mime_type = "text/xml"; | 
| +        } | 
| +      } | 
| +      ClosePrinter(printer_handle); | 
| +    } | 
| +    PTCloseProvider(provider); | 
| +  } | 
| +  return true; | 
| +} | 
| + | 
| +bool ValidatePrintTicket(const std::string& printer_name, | 
| +                         const std::string& print_ticket_data) { | 
| +  if (!InitXPSModule()) { | 
| +    // TODO(sanjeevr): Handle legacy proxy case (with no prntvpt.dll) | 
| +    return false; | 
| +  } | 
| +  bool ret = false; | 
| +  HPTPROVIDER provider = NULL; | 
| +  PTOpenProvider(UTF8ToWide(printer_name.c_str()).c_str(), 1, &provider); | 
| +  if (provider) { | 
| +    ScopedComPtr<IStream> print_ticket_stream; | 
| +    CreateStreamOnHGlobal(NULL, TRUE, print_ticket_stream.Receive()); | 
| +    ULONG bytes_written = 0; | 
| +    print_ticket_stream->Write(print_ticket_data.c_str(), | 
| +                               print_ticket_data.length(), | 
| +                               &bytes_written); | 
| +    DCHECK(bytes_written == print_ticket_data.length()); | 
| +    LARGE_INTEGER pos = {0}; | 
| +    ULARGE_INTEGER new_pos = {0}; | 
| +    print_ticket_stream->Seek(pos, STREAM_SEEK_SET, &new_pos); | 
| +    ScopedBstr error; | 
| +    ScopedComPtr<IStream> result_ticket_stream; | 
| +    CreateStreamOnHGlobal(NULL, TRUE, result_ticket_stream.Receive()); | 
| +    ret = SUCCEEDED(PTMergeAndValidatePrintTicket(provider, | 
| +                                                  print_ticket_stream.get(), | 
| +                                                  NULL, | 
| +                                                  kPTJobScope, | 
| +                                                  result_ticket_stream.get(), | 
| +                                                  error.Receive())); | 
| +    PTCloseProvider(provider); | 
| +  } | 
| +  return ret; | 
| +} | 
| + | 
| +std::string GenerateProxyId() { | 
| +  GUID proxy_id = {0}; | 
| +  HRESULT hr = UuidCreate(&proxy_id); | 
| +  DCHECK(SUCCEEDED(hr)); | 
| +  wchar_t* proxy_id_as_string = NULL; | 
| +  UuidToString(&proxy_id, reinterpret_cast<RPC_WSTR *>(&proxy_id_as_string)); | 
| +  DCHECK(proxy_id_as_string); | 
| +  std::string ret; | 
| +  WideToUTF8(proxy_id_as_string, wcslen(proxy_id_as_string), &ret); | 
| +  RpcStringFree(reinterpret_cast<RPC_WSTR *>(&proxy_id_as_string)); | 
| +  return ret; | 
| +} | 
| + | 
| +bool SpoolPrintJob(const std::string& print_ticket, | 
| +                   const FilePath& print_data_file_path, | 
| +                   const std::string& print_data_mime_type, | 
| +                   const std::string& printer_name, | 
| +                   const std::string& job_title, | 
| +                   PlatformJobId* job_id_ret) { | 
| +  if (!InitXPSModule()) { | 
| +    // TODO(sanjeevr): Handle legacy proxy case (with no prntvpt.dll) | 
| +    return false; | 
| +  } | 
| +  DevMode pt_dev_mode; | 
| +  HRESULT hr = PrintTicketToDevMode(printer_name, print_ticket, &pt_dev_mode); | 
| +  if (FAILED(hr)) { | 
| +    NOTREACHED(); | 
| +    return false; | 
| +  } | 
| +  ScopedHDC dc(CreateDC(L"WINSPOOL", UTF8ToWide(printer_name).c_str(), NULL, | 
| +                        pt_dev_mode.dm_)); | 
| +  if (!dc.Get()) { | 
| +    NOTREACHED(); | 
| +    return false; | 
| +  } | 
| +  hr = E_FAIL; | 
| +  DOCINFO di = {0}; | 
| +  di.cbSize = sizeof(DOCINFO); | 
| +  std::wstring doc_name = UTF8ToWide(job_title); | 
| +  di.lpszDocName = doc_name.c_str(); | 
| +  int job_id = StartDoc(dc.Get(), &di); | 
| +  if (SP_ERROR != job_id) { | 
| +    if (print_data_mime_type == "application/pdf") { | 
| +      hr = PrintPdf2DC(dc.Get(), print_data_file_path); | 
| +    } else { | 
| +      NOTREACHED(); | 
| +    } | 
| +    EndDoc(dc.Get()); | 
| +    if (SUCCEEDED(hr) && job_id_ret) { | 
| +      *job_id_ret = job_id; | 
| +    } | 
| +  } | 
| +  return SUCCEEDED(hr); | 
| +} | 
| + | 
| +bool GetJobDetails(const std::string& printer_name, | 
| +                   PlatformJobId job_id, | 
| +                   PrintJobDetails *job_details) { | 
| +  DCHECK(job_details); | 
| +  HANDLE printer_handle = NULL; | 
| +  std::wstring printer_name_wide = UTF8ToWide(printer_name); | 
| +  OpenPrinter(const_cast<LPTSTR>(printer_name_wide.c_str()), &printer_handle, | 
| +              NULL); | 
| +  DCHECK(printer_handle); | 
| +  bool ret = false; | 
| +  if (printer_handle) { | 
| +    DWORD bytes_needed = 0; | 
| +    GetJob(printer_handle, job_id, 1, NULL, 0, &bytes_needed); | 
| +    DWORD last_error = GetLastError(); | 
| +    if (ERROR_INVALID_PARAMETER != last_error) { | 
| +      // ERROR_INVALID_PARAMETER normally means that the job id is not valid. | 
| +      DCHECK(last_error == ERROR_INSUFFICIENT_BUFFER); | 
| +      scoped_ptr<BYTE> job_info_buffer(new BYTE[bytes_needed]); | 
| +      if (GetJob(printer_handle, job_id, 1, job_info_buffer.get(), bytes_needed, | 
| +                &bytes_needed)) { | 
| +        JOB_INFO_1 *job_info = | 
| +            reinterpret_cast<JOB_INFO_1 *>(job_info_buffer.get()); | 
| +        if (job_info->pStatus) { | 
| +          WideToUTF8(job_info->pStatus, wcslen(job_info->pStatus), | 
| +                     &job_details->status_message); | 
| +        } | 
| +        job_details->platform_status_flags = job_info->Status; | 
| +        if ((job_info->Status & JOB_STATUS_COMPLETE) || | 
| +            (job_info->Status & JOB_STATUS_PRINTED)) { | 
| +          job_details->status = PRINT_JOB_STATUS_COMPLETED; | 
| +        } else if (job_info->Status & JOB_STATUS_ERROR) { | 
| +          job_details->status = PRINT_JOB_STATUS_ERROR; | 
| +        } else { | 
| +          job_details->status = PRINT_JOB_STATUS_IN_PROGRESS; | 
| +        } | 
| +        job_details->total_pages = job_info->TotalPages; | 
| +        job_details->pages_printed = job_info->PagesPrinted; | 
| +        ret = true; | 
| +      } | 
| +    } | 
| +    ClosePrinter(printer_handle); | 
| +  } | 
| +  return ret; | 
| +} | 
| + | 
| +bool IsValidPrinter(const std::string& printer_name) { | 
| +  std::wstring printer_name_wide = UTF8ToWide(printer_name); | 
| +  HANDLE printer_handle = NULL; | 
| +  OpenPrinter(const_cast<LPTSTR>(printer_name_wide.c_str()), &printer_handle, | 
| +              NULL); | 
| +  bool ret = false; | 
| +  if (printer_handle) { | 
| +    ret = true; | 
| +    ClosePrinter(printer_handle); | 
| +  } | 
| +  return ret; | 
| +} | 
| + | 
| +class PrinterChangeNotifier::NotificationState | 
| +    : public base::ObjectWatcher::Delegate { | 
| + public: | 
| +  NotificationState() : printer_(NULL), printer_change_(NULL), delegate_(NULL) { | 
| +  } | 
| +  ~NotificationState() { | 
| +    Stop(); | 
| +  } | 
| +  bool Start(const std::string& printer_name, | 
| +             PrinterChangeNotifier::Delegate* delegate) { | 
| +    delegate_ = delegate; | 
| +    // An empty printer name means watch the current server, we need to pass | 
| +    // NULL to OpenPrinter. | 
| +    LPTSTR printer_name_to_use = NULL; | 
| +    std::wstring printer_name_wide; | 
| +    if (!printer_name.empty()) { | 
| +      printer_name_wide = UTF8ToWide(printer_name); | 
| +      printer_name_to_use = const_cast<LPTSTR>(printer_name_wide.c_str()); | 
| +    } | 
| +    bool ret = false; | 
| +    OpenPrinter(printer_name_to_use, &printer_, NULL); | 
| +    if (printer_) { | 
| +      printer_change_ = FindFirstPrinterChangeNotification( | 
| +          printer_, PRINTER_CHANGE_PRINTER|PRINTER_CHANGE_JOB, 0, NULL); | 
| +      if (printer_change_) { | 
| +        ret = watcher_.StartWatching(printer_change_, this); | 
| +      } | 
| +    } | 
| +    if (!ret) { | 
| +      Stop(); | 
| +    } | 
| +    return ret; | 
| +  } | 
| +  bool Stop() { | 
| +    watcher_.StopWatching(); | 
| +    if (printer_) { | 
| +      ClosePrinter(printer_); | 
| +      printer_ = NULL; | 
| +    } | 
| +    if (printer_change_) { | 
| +      FindClosePrinterChangeNotification(printer_change_); | 
| +      printer_change_ = NULL; | 
| +    } | 
| +    return true; | 
| +  } | 
| + | 
| +  void OnObjectSignaled(HANDLE object) { | 
| +    DWORD change = 0; | 
| +    FindNextPrinterChangeNotification(object, &change, NULL, NULL); | 
| + | 
| +    if (change != ((PRINTER_CHANGE_PRINTER|PRINTER_CHANGE_JOB) & | 
| +                  (~PRINTER_CHANGE_FAILED_CONNECTION_PRINTER))) { | 
| +      // For printer connections, we get spurious change notifications with | 
| +      // all flags set except PRINTER_CHANGE_FAILED_CONNECTION_PRINTER. | 
| +      // Ignore these. | 
| +      if (change & PRINTER_CHANGE_ADD_PRINTER) { | 
| +        delegate_->OnPrinterAdded(); | 
| +      } else if (change & PRINTER_CHANGE_DELETE_PRINTER) { | 
| +        delegate_->OnPrinterDeleted(); | 
| +      } else if (change & PRINTER_CHANGE_SET_PRINTER) { | 
| +        delegate_->OnPrinterChanged(); | 
| +      } | 
| +      if (change & PRINTER_CHANGE_JOB) { | 
| +        delegate_->OnJobChanged(); | 
| +      } | 
| +    } | 
| +    watcher_.StartWatching(printer_change_, this); | 
| +  } | 
| +  HANDLE printer_handle() const { | 
| +    return printer_; | 
| +  } | 
| + private: | 
| +  base::ObjectWatcher watcher_; | 
| +  HANDLE printer_;            // The printer being watched | 
| +  HANDLE printer_change_;     // Returned by FindFirstPrinterChangeNotifier | 
| +  PrinterChangeNotifier::Delegate* delegate_;  // Delegate to notify | 
| +  bool did_signal_;           // DoneWaiting was called | 
| +}; | 
| + | 
| +PrinterChangeNotifier::PrinterChangeNotifier() : state_(NULL) { | 
| +} | 
| + | 
| +PrinterChangeNotifier::~PrinterChangeNotifier() { | 
| +  StopWatching(); | 
| +} | 
| + | 
| +bool PrinterChangeNotifier::StartWatching(const std::string& printer_name, | 
| +                                          Delegate* delegate) { | 
| +  if (state_) { | 
| +    NOTREACHED(); | 
| +    return false; | 
| +  } | 
| +  state_ = new NotificationState; | 
| +  if (!state_->Start(printer_name, delegate)) { | 
| +    StopWatching(); | 
| +    return false; | 
| +  } | 
| +  return true; | 
| +} | 
| + | 
| +bool PrinterChangeNotifier::StopWatching() { | 
| +  if (!state_) { | 
| +    return false; | 
| +  } | 
| +  state_->Stop(); | 
| +  delete state_; | 
| +  state_ = NULL; | 
| +  return true; | 
| +} | 
| + | 
| +bool PrinterChangeNotifier::GetCurrentPrinterInfo( | 
| +    PrinterBasicInfo* printer_info) { | 
| +  if (!state_) { | 
| +    return false; | 
| +  } | 
| +  DCHECK(printer_info); | 
| +  DWORD bytes_needed = 0; | 
| +  bool ret = false; | 
| +  GetPrinter(state_->printer_handle(), 2, NULL, 0, &bytes_needed); | 
| +  if (0 != bytes_needed) { | 
| +    scoped_ptr<BYTE> printer_info_buffer(new BYTE[bytes_needed]); | 
| +    if (GetPrinter(state_->printer_handle(), 2, printer_info_buffer.get(), | 
| +                   bytes_needed, &bytes_needed)) { | 
| +      PRINTER_INFO_2* printer_info_win = | 
| +          reinterpret_cast<PRINTER_INFO_2*>(printer_info_buffer.get()); | 
| +      printer_info->printer_name = WideToUTF8(printer_info_win->pPrinterName); | 
| +      printer_info->printer_description = | 
| +          WideToUTF8(printer_info_win->pComment); | 
| +      printer_info->printer_status = printer_info_win->Status; | 
| +      ret = true; | 
| +    } | 
| +  } | 
| +  return ret; | 
| +} | 
| +}  // namespace cloud_print | 
| + | 
|  | 
| Property changes on: chrome\browser\printing\cloud_print\printer_info_win.cc | 
| ___________________________________________________________________ | 
| Added: svn:eol-style | 
| + LF | 
|  | 
|  |