Index: chrome/browser/printing/cloud_print/cloud_print_proxy_backend.cc |
=================================================================== |
--- chrome/browser/printing/cloud_print/cloud_print_proxy_backend.cc (revision 0) |
+++ chrome/browser/printing/cloud_print/cloud_print_proxy_backend.cc (revision 0) |
@@ -0,0 +1,496 @@ |
+// 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/cloud_print_proxy_backend.h" |
+ |
+#include "base/file_util.h" |
+#include "base/md5.h" |
+#include "base/string_util.h" |
+#include "base/utf_string_conversions.h" |
+#include "base/values.h" |
+#include "chrome/browser/profile.h" |
+#include "chrome/browser/printing/cloud_print/cloud_print_consts.h" |
+#include "chrome/browser/printing/cloud_print/cloud_print_helpers.h" |
+#include "chrome/browser/printing/cloud_print/cloud_print_proxy_service.h" |
+#include "chrome/browser/printing/cloud_print/printer_job_handler.h" |
+#include "googleurl/src/gurl.h" |
+#include "net/url_request/url_request_status.h" |
+ |
+// The real guts of SyncBackendHost, to keep the public client API clean. |
+class CloudPrintProxyBackend::Core |
+ : public base::RefCountedThreadSafe<CloudPrintProxyBackend::Core>, |
+ public URLFetcherDelegate, |
+ public cloud_print::PrinterChangeNotifierDelegate, |
+ public PrinterJobHandlerDelegate { |
+ public: |
+ explicit Core(CloudPrintProxyBackend* backend); |
+ // Note: |
+ // |
+ // The Do* methods are the various entry points from CloudPrintProxyBackend |
+ // It calls us on a dedicated thread to actually perform synchronous |
+ // (and potentially blocking) syncapi operations. |
+ // |
+ // Called on the CloudPrintProxyBackend core_thread_ to perform |
+ // initialization |
+ void DoInitialize(const std::string& auth_token, |
+ const std::string& proxy_id); |
+ // Called on the CloudPrintProxyBackend core_thread_ to perform |
+ // shutdown. |
+ void DoShutdown(); |
+ void DoRegisterSelectedPrinters( |
+ const cloud_print::PrinterList& printer_list); |
+ void DoHandlePrinterNotification(const std::string& printer_id); |
+ |
+ // URLFetcher::Delegate implementation. |
+ virtual void OnURLFetchComplete(const URLFetcher* source, const GURL& url, |
+ const URLRequestStatus& status, |
+ int response_code, |
+ const ResponseCookies& cookies, |
+ const std::string& data); |
+// cloud_print::PrinterChangeNotifier::Delegate implementation |
+ virtual void OnPrinterAdded(); |
+ virtual void OnPrinterDeleted() { |
+ } |
+ virtual void OnPrinterChanged() { |
+ } |
+ virtual void OnJobChanged() { |
+ } |
+ // PrinterJobHandler::Delegate implementation |
+ void OnPrinterJobHandlerShutdown(PrinterJobHandler* job_handler, |
+ const std::string& printer_id); |
+ |
+ protected: |
+ // FrontendNotification defines parameters for NotifyFrontend. Each enum |
+ // value corresponds to the one CloudPrintProcyService method that |
+ // NotifyFrontend should invoke. |
+ enum FrontendNotification { |
+ PRINTER_LIST_AVAILABLE, // OnPrinterListAvailable |
+ }; |
+ // Prototype for a response handler. |
+ typedef void (CloudPrintProxyBackend::Core::*ResponseHandler)( |
+ const URLFetcher* source, const GURL& url, |
+ const URLRequestStatus& status, int response_code, |
+ const ResponseCookies& cookies, const std::string& data); |
+ // Begin response handlers |
+ void HandlePrinterListResponse(const URLFetcher* source, const GURL& url, |
+ const URLRequestStatus& status, |
+ int response_code, |
+ const ResponseCookies& cookies, |
+ const std::string& data); |
+ void HandleRegisterPrinterResponse(const URLFetcher* source, |
+ const GURL& url, |
+ const URLRequestStatus& status, |
+ int response_code, |
+ const ResponseCookies& cookies, |
+ const std::string& data); |
+ // End response handlers |
+ |
+ // NotifyFrontend is how the Core communicates with the frontend across |
+ // threads. |
+ void NotifyFrontend(FrontendNotification notification); |
+ // Starts a new printer registration process. |
+ void StartRegistration(); |
+ // Ends the printer registration process. |
+ void EndRegistration(); |
+ // Registers printer capabilities and defaults for the next printer in the |
+ // list with the cloud print server. |
+ void RegisterNextPrinter(); |
+ // Retrieves the list of registered printers for this user/proxy combination |
+ // from the cloud print server. |
+ void GetRegisteredPrinters(); |
+ void HandleServerError(Task* task_to_retry); |
+ // Removes the given printer from the list. Returns false if the printer |
+ // did not exist in the list. |
+ bool RemovePrinterFromList(const std::string& printer_name); |
+ // Initializes a job handler object for the specified printer. The job |
+ // handler is responsible for checking for pending print jobs for this |
+ // printer and print them. |
+ void InitJobHandlerForPrinter(DictionaryValue* printer_data); |
+ |
+ // Our parent CloudPrintProxyBackend |
+ CloudPrintProxyBackend* backend_; |
+ // The list of printers to be registered with the cloud print server. |
+ // To begin with,this list is initialized with the list of local and network |
+ // printers available. Then we query the server for the list of printers |
+ // already registered. We trim this list to remove the printers already |
+ // registered. We then pass a copy of this list to the frontend to give the |
+ // user a chance to further trim the list. When the frontend gives us the |
+ // final list we make a copy into this so that we can start registering. |
+ cloud_print::PrinterList printer_list_; |
+ // The URLFetcher instance for the current request |
+ scoped_ptr<URLFetcher> request_; |
+ // The index of the nex printer to be uploaded. |
+ size_t next_upload_index_; |
+ // The unique id for this proxy |
+ std::string proxy_id_; |
+ // The GAIA auth token |
+ std::string auth_token_; |
+ // The number of consecutive times that connecting to the server failed. |
+ int server_error_count_; |
+ // Cached info about the last printer that we tried to upload. We cache this |
+ // so we won't have to requery the printer if the upload fails and we need |
+ // to retry. |
+ std::string last_uploaded_printer_name_; |
+ cloud_print::PrinterCapsAndDefaults last_uploaded_printer_info_; |
+ // A map of printer id to job handler. |
+ typedef std::map<std::string, scoped_refptr<PrinterJobHandler> > |
+ JobHandlerMap; |
+ JobHandlerMap job_handler_map_; |
+ ResponseHandler next_response_handler_; |
+ cloud_print::PrinterChangeNotifier printer_change_notifier_; |
+ bool new_printers_available_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(Core); |
+}; |
+ |
+CloudPrintProxyBackend::CloudPrintProxyBackend( |
+ CloudPrintProxyFrontend* frontend) |
+ : core_thread_("Chrome_CloudPrintProxyCoreThread"), |
+ frontend_loop_(MessageLoop::current()), |
+ frontend_(frontend) { |
+ DCHECK(frontend_); |
+ core_ = new Core(this); |
+} |
+ |
+CloudPrintProxyBackend::~CloudPrintProxyBackend() { |
+ DCHECK(!core_); |
+} |
+ |
+bool CloudPrintProxyBackend::Initialize(const std::string& auth_token, |
+ const std::string& proxy_id) { |
+ if (!core_thread_.Start()) |
+ return false; |
+ core_thread_.message_loop()->PostTask(FROM_HERE, |
+ NewRunnableMethod( |
+ core_.get(), &CloudPrintProxyBackend::Core::DoInitialize, auth_token, |
+ proxy_id)); |
+ return true; |
+} |
+ |
+void CloudPrintProxyBackend::Shutdown() { |
+ core_thread_.message_loop()->PostTask(FROM_HERE, |
+ NewRunnableMethod(core_.get(), |
+ &CloudPrintProxyBackend::Core::DoShutdown)); |
+ core_thread_.Stop(); |
+ core_ = NULL; // Releases reference to core_. |
+} |
+ |
+void CloudPrintProxyBackend::RegisterPrinters( |
+ const cloud_print::PrinterList& printer_list) { |
+ core_thread_.message_loop()->PostTask(FROM_HERE, |
+ NewRunnableMethod( |
+ core_.get(), |
+ &CloudPrintProxyBackend::Core::DoRegisterSelectedPrinters, |
+ printer_list)); |
+} |
+ |
+void CloudPrintProxyBackend::HandlePrinterNotification( |
+ const std::string& printer_id) { |
+ core_thread_.message_loop()->PostTask(FROM_HERE, |
+ NewRunnableMethod( |
+ core_.get(), |
+ &CloudPrintProxyBackend::Core::DoHandlePrinterNotification, |
+ printer_id)); |
+} |
+ |
+CloudPrintProxyBackend::Core::Core(CloudPrintProxyBackend* backend) |
+ : backend_(backend), next_upload_index_(0), server_error_count_(0), |
+ next_response_handler_(NULL), new_printers_available_(false) { |
+} |
+ |
+void CloudPrintProxyBackend::Core::DoInitialize(const std::string& auth_token, |
+ const std::string& proxy_id) { |
+ DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); |
+ printer_change_notifier_.StartWatching(std::string(), this); |
+ proxy_id_ = proxy_id; |
+ auth_token_ = auth_token; |
+ StartRegistration(); |
+} |
+ |
+void CloudPrintProxyBackend::Core::StartRegistration() { |
+ printer_list_.clear(); |
+ cloud_print::EnumeratePrinters(&printer_list_); |
+ server_error_count_ = 0; |
+ // Now we need to ask the server about printers that were registered on the |
+ // server so that we can trim this list. |
+ GetRegisteredPrinters(); |
+} |
+ |
+void CloudPrintProxyBackend::Core::EndRegistration() { |
+ request_.reset(); |
+ if (new_printers_available_) { |
+ new_printers_available_ = false; |
+ StartRegistration(); |
+ } |
+} |
+ |
+void CloudPrintProxyBackend::Core::DoShutdown() { |
+ // Need to kill all running jobs. |
+ while (!job_handler_map_.empty()) { |
+ JobHandlerMap::iterator index = job_handler_map_.begin(); |
+ // Shutdown will call our OnPrinterJobHandlerShutdown method which will |
+ // remove this from the map. |
+ index->second->Shutdown(); |
+ } |
+} |
+ |
+void CloudPrintProxyBackend::Core::DoRegisterSelectedPrinters( |
+ const cloud_print::PrinterList& printer_list) { |
+ server_error_count_ = 0; |
+ printer_list_.assign(printer_list.begin(), printer_list.end()); |
+ DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); |
+ next_upload_index_ = 0; |
+ RegisterNextPrinter(); |
+} |
+ |
+void CloudPrintProxyBackend::Core::DoHandlePrinterNotification( |
+ const std::string& printer_id) { |
+ JobHandlerMap::iterator index = job_handler_map_.find(printer_id); |
+ if (index != job_handler_map_.end()) |
+ index->second->NotifyJobAvailable(); |
+} |
+ |
+void CloudPrintProxyBackend::Core::GetRegisteredPrinters() { |
+ request_.reset( |
+ new URLFetcher(CloudPrintHelpers::GetUrlForPrinterList(proxy_id_), |
+ URLFetcher::GET, this)); |
+ CloudPrintHelpers::PrepCloudPrintRequest(request_.get(), auth_token_); |
+ next_response_handler_ = |
+ &CloudPrintProxyBackend::Core::HandlePrinterListResponse; |
+ request_->Start(); |
+} |
+ |
+void CloudPrintProxyBackend::Core::RegisterNextPrinter() { |
+ // For the next printer to be uploaded, create a multi-part post request to |
+ // upload the printer capabilities and the printer defaults. |
+ if (next_upload_index_ < printer_list_.size()) { |
+ const cloud_print::PrinterBasicInfo& info = |
+ printer_list_.at(next_upload_index_); |
+ bool have_printer_info = true; |
+ // If we are retrying a previous upload, we don't need to fetch the caps |
+ // and defaults again. |
+ if (info.printer_name != last_uploaded_printer_name_) { |
+ have_printer_info = cloud_print::GetPrinterCapsAndDefaults( |
+ info.printer_name.c_str(), &last_uploaded_printer_info_); |
+ } |
+ if (have_printer_info) { |
+ last_uploaded_printer_name_ = info.printer_name; |
+ std::string mime_boundary; |
+ CloudPrintHelpers::CreateMimeBoundaryForUpload(&mime_boundary); |
+ std::string post_data; |
+ CloudPrintHelpers::AddMultipartValueForUpload(kProxyIdValue, proxy_id_, |
+ mime_boundary, |
+ std::string(), &post_data); |
+ CloudPrintHelpers::AddMultipartValueForUpload(kPrinterNameValue, |
+ info.printer_name, |
+ mime_boundary, |
+ std::string(), &post_data); |
+ CloudPrintHelpers::AddMultipartValueForUpload(kPrinterDescValue, |
+ info.printer_description, |
+ mime_boundary, |
+ std::string() , &post_data); |
+ CloudPrintHelpers::AddMultipartValueForUpload( |
+ kPrinterStatusValue, StringPrintf("%d", info.printer_status), |
+ mime_boundary, std::string(), &post_data); |
+ CloudPrintHelpers::AddMultipartValueForUpload( |
+ kPrinterCapsValue, last_uploaded_printer_info_.printer_capabilities, |
+ mime_boundary, last_uploaded_printer_info_.caps_mime_type, |
+ &post_data); |
+ CloudPrintHelpers::AddMultipartValueForUpload( |
+ kPrinterDefaultsValue, last_uploaded_printer_info_.printer_defaults, |
+ mime_boundary, last_uploaded_printer_info_.defaults_mime_type, |
+ &post_data); |
+ // Send a hash of the printer capabilities to the server. We will use this |
+ // later to check if the capabilities have changed |
+ CloudPrintHelpers::AddMultipartValueForUpload( |
+ WideToUTF8(kPrinterCapsHashValue).c_str(), |
+ MD5String(last_uploaded_printer_info_.printer_capabilities), |
+ mime_boundary, std::string(), &post_data); |
+ // Terminate the request body |
+ post_data.append("--" + mime_boundary + "--\r\n"); |
+ std::string mime_type("multipart/form-data; boundary="); |
+ mime_type += mime_boundary; |
+ request_.reset( |
+ new URLFetcher(CloudPrintHelpers::GetUrlForPrinterRegistration(), |
+ URLFetcher::POST, this)); |
+ CloudPrintHelpers::PrepCloudPrintRequest(request_.get(), auth_token_); |
+ request_->set_upload_data(mime_type, post_data); |
+ next_response_handler_ = |
+ &CloudPrintProxyBackend::Core::HandleRegisterPrinterResponse; |
+ request_->Start(); |
+ } else { |
+ NOTREACHED(); |
+ } |
+ } else { |
+ EndRegistration(); |
+ } |
+} |
+ |
+// URLFetcher::Delegate implementation. |
+void CloudPrintProxyBackend::Core::OnURLFetchComplete( |
+ const URLFetcher* source, const GURL& url, const URLRequestStatus& status, |
+ int response_code, const ResponseCookies& cookies, |
+ const std::string& data) { |
+ DCHECK(source == request_.get()); |
+ // We need a next response handler |
+ DCHECK(next_response_handler_); |
+ (this->*next_response_handler_)(source, url, status, response_code, |
+ cookies, data); |
+} |
+ |
+void CloudPrintProxyBackend::Core::NotifyFrontend( |
+ FrontendNotification notification) { |
+ switch (notification) { |
+ case PRINTER_LIST_AVAILABLE: |
+ backend_->frontend_->OnPrinterListAvailable(printer_list_); |
+ break; |
+ default: |
+ NOTREACHED(); |
+ break; |
+ } |
+} |
+ |
+void CloudPrintProxyBackend::Core::HandlePrinterListResponse( |
+ const URLFetcher* source, const GURL& url, const URLRequestStatus& status, |
+ int response_code, const ResponseCookies& cookies, |
+ const std::string& data) { |
+ if (status.is_success()) { |
+ server_error_count_ = 0; |
+ if (response_code == 200) { |
+ // Parse the response JSON for the list of printers already registered. |
+ bool succeeded = false; |
+ DictionaryValue* response_dict_temp = NULL; |
+ CloudPrintHelpers::ParseResponseJSON(data, &succeeded, |
+ &response_dict_temp); |
+ scoped_ptr<DictionaryValue> response_dict; |
+ response_dict.reset(response_dict_temp); |
+ if (succeeded) { |
+ DCHECK(response_dict.get()); |
+ ListValue* printer_list = NULL; |
+ response_dict->GetList(kPrinterListValue, &printer_list); |
+ // There may be no "printers" value in the JSON |
+ if (printer_list) { |
+ for (size_t index = 0; index < printer_list->GetSize(); index++) { |
+ DictionaryValue* printer_data = NULL; |
+ if (printer_list->GetDictionary(index, &printer_data)) { |
+ std::string printer_name; |
+ printer_data->GetString(kNameValue, &printer_name); |
+ RemovePrinterFromList(printer_name); |
+ InitJobHandlerForPrinter(printer_data); |
+ } else { |
+ NOTREACHED(); |
+ } |
+ } |
+ } |
+ } |
+ } |
+ MessageLoop::current()->DeleteSoon(FROM_HERE, request_.release()); |
+ if (!printer_list_.empty()) { |
+ // Let the frontend know that we have a list of printers available. |
+ backend_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, |
+ &Core::NotifyFrontend, PRINTER_LIST_AVAILABLE)); |
+ } else { |
+ // No more work to be done here. |
+ MessageLoop::current()->PostTask( |
+ FROM_HERE, NewRunnableMethod(this, &Core::EndRegistration)); |
+ } |
+ } else { |
+ HandleServerError(NewRunnableMethod(this, &Core::GetRegisteredPrinters)); |
+ } |
+} |
+ |
+void CloudPrintProxyBackend::Core::InitJobHandlerForPrinter( |
+ DictionaryValue* printer_data) { |
+ DCHECK(printer_data); |
+ std::string printer_id; |
+ printer_data->GetString(kIdValue, &printer_id); |
+ DCHECK(!printer_id.empty()); |
+ JobHandlerMap::iterator index = job_handler_map_.find(printer_id); |
+ // We might already have a job handler for this printer |
+ if (index == job_handler_map_.end()) { |
+ cloud_print::PrinterBasicInfo printer_info; |
+ printer_data->GetString(kNameValue, &printer_info.printer_name); |
+ DCHECK(!printer_info.printer_name.empty()); |
+ printer_data->GetString(UTF8ToWide(kPrinterDescValue), |
+ &printer_info.printer_description); |
+ printer_data->GetInteger(UTF8ToWide(kPrinterStatusValue), |
+ &printer_info.printer_status); |
+ std::string caps_hash; |
+ printer_data->GetString(kPrinterCapsHashValue, &caps_hash); |
+ scoped_refptr<PrinterJobHandler> job_handler; |
+ job_handler = new PrinterJobHandler(printer_info, printer_id, caps_hash, |
+ auth_token_, this); |
+ job_handler_map_[printer_id] = job_handler; |
+ job_handler->Initialize(); |
+ } |
+} |
+ |
+void CloudPrintProxyBackend::Core::HandleRegisterPrinterResponse( |
+ const URLFetcher* source, const GURL& url, const URLRequestStatus& status, |
+ int response_code, const ResponseCookies& cookies, |
+ const std::string& data) { |
+ Task* next_task = |
+ NewRunnableMethod(this, |
+ &CloudPrintProxyBackend::Core::RegisterNextPrinter); |
+ if (status.is_success() && (response_code == 200)) { |
+ bool succeeded = false; |
+ DictionaryValue* response_dict = NULL; |
+ CloudPrintHelpers::ParseResponseJSON(data, &succeeded, &response_dict); |
+ if (succeeded) { |
+ DCHECK(response_dict); |
+ ListValue* printer_list = NULL; |
+ response_dict->GetList(kPrinterListValue, &printer_list); |
+ // There should be a "printers" value in the JSON |
+ DCHECK(printer_list); |
+ if (printer_list) { |
+ DictionaryValue* printer_data = NULL; |
+ if (printer_list->GetDictionary(0, &printer_data)) { |
+ InitJobHandlerForPrinter(printer_data); |
+ } |
+ } |
+ } |
+ server_error_count_ = 0; |
+ next_upload_index_++; |
+ MessageLoop::current()->PostTask(FROM_HERE, next_task); |
+ } else { |
+ HandleServerError(next_task); |
+ } |
+} |
+ |
+void CloudPrintProxyBackend::Core::HandleServerError(Task* task_to_retry) { |
+ CloudPrintHelpers::HandleServerError( |
+ &server_error_count_, -1, kMaxRetryInterval, kBaseRetryInterval, |
+ task_to_retry, NULL); |
+} |
+ |
+bool CloudPrintProxyBackend::Core::RemovePrinterFromList( |
+ const std::string& printer_name) { |
+ bool ret = false; |
+ for (cloud_print::PrinterList::iterator index = printer_list_.begin(); |
+ index != printer_list_.end(); index++) { |
+ if (0 == base::strcasecmp(index->printer_name.c_str(), |
+ printer_name.c_str())) { |
+ index = printer_list_.erase(index); |
+ ret = true; |
+ break; |
+ } |
+ } |
+ return ret; |
+} |
+ |
+// cloud_print::PrinterChangeNotifier::Delegate implementation |
+void CloudPrintProxyBackend::Core::OnPrinterAdded() { |
+ if (request_.get()) { |
+ new_printers_available_ = true; |
+ } else { |
+ StartRegistration(); |
+ } |
+} |
+ |
+// PrinterJobHandler::Delegate implementation |
+void CloudPrintProxyBackend::Core::OnPrinterJobHandlerShutdown( |
+ PrinterJobHandler* job_handler, const std::string& printer_id) { |
+ job_handler_map_.erase(printer_id); |
+} |
+ |
Property changes on: chrome\browser\printing\cloud_print\cloud_print_proxy_backend.cc |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |