Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1544)

Unified Diff: chrome/browser/printing/cloud_print/cloud_print_proxy_backend.cc

Issue 1566047: First cut of Cloud Print Proxy implementation. The code is not enabled for no... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Final review changes Created 10 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698