summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsanjeevr@google.com <sanjeevr@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-20 02:03:48 +0000
committersanjeevr@google.com <sanjeevr@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-20 02:03:48 +0000
commit1bd056e69770a53c2778c46918e42e2b8eed6de3 (patch)
tree4534fd75545de891c0a1a3ce549f1ade764e877a
parent4d19b9e62e2e7ce6eff44e3d37617159361b8050 (diff)
downloadchromium_src-1bd056e69770a53c2778c46918e42e2b8eed6de3.zip
chromium_src-1bd056e69770a53c2778c46918e42e2b8eed6de3.tar.gz
chromium_src-1bd056e69770a53c2778c46918e42e2b8eed6de3.tar.bz2
First cut of Cloud Print Proxy implementation. The code is not enabled for now. Soon the cloud print proxy code will move from the browser process to a background process called the service process.
BUG=None TEST=None for now Review URL: http://codereview.chromium.org/1566047 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@45000 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/printing/cloud_print/cloud_print_consts.cc30
-rw-r--r--chrome/browser/printing/cloud_print/cloud_print_consts.h37
-rw-r--r--chrome/browser/printing/cloud_print/cloud_print_helpers.cc161
-rw-r--r--chrome/browser/printing/cloud_print/cloud_print_helpers.h71
-rw-r--r--chrome/browser/printing/cloud_print/cloud_print_proxy_backend.cc496
-rw-r--r--chrome/browser/printing/cloud_print/cloud_print_proxy_backend.h70
-rw-r--r--chrome/browser/printing/cloud_print/cloud_print_proxy_service.cc103
-rw-r--r--chrome/browser/printing/cloud_print/cloud_print_proxy_service.h59
-rw-r--r--chrome/browser/printing/cloud_print/job_status_updater.cc90
-rw-r--r--chrome/browser/printing/cloud_print/job_status_updater.h64
-rw-r--r--chrome/browser/printing/cloud_print/printer_info.h125
-rw-r--r--chrome/browser/printing/cloud_print/printer_info_linux.cc80
-rw-r--r--chrome/browser/printing/cloud_print/printer_info_mac.cc80
-rw-r--r--chrome/browser/printing/cloud_print/printer_info_win.cc509
-rw-r--r--chrome/browser/printing/cloud_print/printer_job_handler.cc560
-rw-r--r--chrome/browser/printing/cloud_print/printer_job_handler.h240
-rw-r--r--chrome/browser/profile.cc17
-rw-r--r--chrome/browser/profile.h7
-rw-r--r--chrome/chrome_browser.gypi16
-rw-r--r--chrome/chrome_dll.gypi1
-rw-r--r--chrome/common/pref_names.cc6
-rw-r--r--chrome/common/pref_names.h4
-rw-r--r--chrome/test/testing_profile.h1
23 files changed, 2827 insertions, 0 deletions
diff --git a/chrome/browser/printing/cloud_print/cloud_print_consts.cc b/chrome/browser/printing/cloud_print/cloud_print_consts.cc
new file mode 100644
index 0000000..d1c4685
--- /dev/null
+++ b/chrome/browser/printing/cloud_print/cloud_print_consts.cc
@@ -0,0 +1,30 @@
+// 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.
+
+// Constant defines used in the cloud print proxy code
+
+#include "chrome/browser/printing/cloud_print/cloud_print_consts.h"
+
+const char kProxyIdValue[] = "proxy";
+const char kPrinterNameValue[] = "printer";
+const char kPrinterDescValue[] = "description";
+const char kPrinterCapsValue[] = "capabilities";
+const char kPrinterDefaultsValue[] = "defaults";
+const char kPrinterStatusValue[] = "status";
+
+// Values in the respone JSON from the cloud print server
+const wchar_t kPrinterListValue[] = L"printers";
+const wchar_t kSuccessValue[] = L"success";
+const wchar_t kNameValue[] = L"name";
+const wchar_t kIdValue[] = L"id";
+const wchar_t kTicketUrlValue[] = L"ticketUrl";
+const wchar_t kFileUrlValue[] = L"fileUrl";
+const wchar_t kJobListValue[] = L"jobs";
+const wchar_t kTitleValue[] = L"title";
+const wchar_t kPrinterCapsHashValue[] = L"capsHash";
+
+// TODO(sanjeevr): Change this to a real one. Also read this from prefs instead
+// of hardcoding.
+const char kCloudPrintServerUrl[] = "https://<TBD>";
+
diff --git a/chrome/browser/printing/cloud_print/cloud_print_consts.h b/chrome/browser/printing/cloud_print/cloud_print_consts.h
new file mode 100644
index 0000000..13a091f
--- /dev/null
+++ b/chrome/browser/printing/cloud_print/cloud_print_consts.h
@@ -0,0 +1,37 @@
+// 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.
+
+#ifndef CHROME_BROWSER_PRINTING_CLOUD_PRINT_CLOUD_PRINT_CONSTS_H_
+#define CHROME_BROWSER_PRINTING_CLOUD_PRINT_CLOUD_PRINT_CONSTS_H_
+
+#include "base/basictypes.h"
+
+// Constant defines used in the cloud print proxy code
+extern const char kProxyIdValue[];
+extern const char kPrinterNameValue[];
+extern const char kPrinterDescValue[];
+extern const char kPrinterCapsValue[];
+extern const char kPrinterDefaultsValue[];
+extern const char kPrinterStatusValue[];
+// Values in the respone JSON from the cloud print server
+extern const wchar_t kPrinterListValue[];
+extern const wchar_t kSuccessValue[];
+extern const wchar_t kNameValue[];
+extern const wchar_t kIdValue[];
+extern const wchar_t kTicketUrlValue[];
+extern const wchar_t kFileUrlValue[];
+extern const wchar_t kJobListValue[];
+extern const wchar_t kTitleValue[];
+extern const wchar_t kPrinterCapsHashValue[];
+
+extern const char kCloudPrintServerUrl[];
+
+// Max interval between retrying connection to the server
+const int64 kMaxRetryInterval = 5*60*1000; // 5 minutes in millseconds
+const int64 kBaseRetryInterval = 5*1000; // 5 seconds
+const int kMaxRetryCount = 5;
+const int64 kJobStatusUpdateInterval = 10*1000; // 10 seconds
+
+#endif // CHROME_BROWSER_PRINTING_CLOUD_PRINT_CLOUD_PRINT_CONSTS_H_
+
diff --git a/chrome/browser/printing/cloud_print/cloud_print_helpers.cc b/chrome/browser/printing/cloud_print/cloud_print_helpers.cc
new file mode 100644
index 0000000..336a203
--- /dev/null
+++ b/chrome/browser/printing/cloud_print/cloud_print_helpers.cc
@@ -0,0 +1,161 @@
+// 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_helpers.h"
+
+#include "base/json/json_reader.h"
+#include "base/rand_util.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "base/task.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/net/url_fetcher.h"
+#include "chrome/browser/printing/cloud_print/cloud_print_consts.h"
+#include "chrome/browser/profile.h"
+
+std::string StringFromJobStatus(cloud_print::PrintJobStatus status) {
+ std::string ret;
+ switch (status) {
+ case cloud_print::PRINT_JOB_STATUS_IN_PROGRESS:
+ ret = "in_progress";
+ break;
+ case cloud_print::PRINT_JOB_STATUS_ERROR:
+ ret = "error";
+ break;
+ case cloud_print::PRINT_JOB_STATUS_COMPLETED:
+ ret = "done";
+ break;
+ default:
+ ret = "unknown";
+ NOTREACHED();
+ break;
+ }
+ return ret;
+}
+
+GURL CloudPrintHelpers::GetUrlForPrinterRegistration() {
+ return GURL(StringPrintf("%s/printing/register", kCloudPrintServerUrl));
+}
+
+GURL CloudPrintHelpers::GetUrlForPrinterUpdate(const std::string& printer_id) {
+ return GURL(StringPrintf("%s/printing/update?printerid=%s",
+ kCloudPrintServerUrl, printer_id.c_str()));
+}
+
+GURL CloudPrintHelpers::GetUrlForPrinterDelete(const std::string& printer_id) {
+ return GURL(StringPrintf("%s/printing/delete?printerid=%s",
+ kCloudPrintServerUrl, printer_id.c_str()));
+}
+
+GURL CloudPrintHelpers::GetUrlForPrinterList(const std::string& proxy_id) {
+ return GURL(StringPrintf(
+ "%s/printing/list?proxy=%s",
+ kCloudPrintServerUrl, proxy_id.c_str()));
+}
+
+GURL CloudPrintHelpers::GetUrlForJobFetch(const std::string& printer_id) {
+ return GURL(StringPrintf(
+ "%s/printing/fetch?printerid=%s",
+ kCloudPrintServerUrl, printer_id.c_str()));
+}
+
+GURL CloudPrintHelpers::GetUrlForJobStatusUpdate(
+ const std::string& job_id, cloud_print::PrintJobStatus status) {
+ std::string status_string = StringFromJobStatus(status);
+ return GURL(StringPrintf(
+ "%s/printing/control?jobid=%s&status=%s",
+ kCloudPrintServerUrl, job_id.c_str(), status_string.c_str()));
+}
+
+GURL CloudPrintHelpers::GetUrlForJobStatusUpdate(
+ const std::string& job_id, const cloud_print::PrintJobDetails& details) {
+ std::string status_string = StringFromJobStatus(details.status);
+ return GURL(StringPrintf(
+ "%s/printing/control?jobid=%s&status=%s&code=%d&message=%s"
+ "&numpages=%d&pagesprinted=%d",
+ kCloudPrintServerUrl, job_id.c_str(), status_string.c_str(),
+ details.platform_status_flags, details.status_message.c_str(),
+ details.total_pages, details.pages_printed));
+}
+
+bool CloudPrintHelpers::ParseResponseJSON(
+ const std::string& response_data, bool* succeeded,
+ DictionaryValue** response_dict) {
+ scoped_ptr<Value> message_value(base::JSONReader::Read(response_data, false));
+ DCHECK(message_value.get());
+ if (!message_value.get()) {
+ return false;
+ }
+ if (!message_value->IsType(Value::TYPE_DICTIONARY)) {
+ NOTREACHED();
+ return false;
+ }
+ DictionaryValue* response_dict_local =
+ static_cast<DictionaryValue*>(message_value.get());
+ if (succeeded) {
+ response_dict_local->GetBoolean(kSuccessValue, succeeded);
+ }
+ if (response_dict) {
+ *response_dict = response_dict_local;
+ message_value.release();
+ }
+ return true;
+}
+
+void CloudPrintHelpers::PrepCloudPrintRequest(URLFetcher* request,
+ const std::string& auth_token) {
+ request->set_request_context(
+ Profile::GetDefaultRequestContext());
+ std::string headers = "Authorization: GoogleLogin auth=";
+ headers += auth_token;
+ request->set_extra_request_headers(headers);
+}
+
+void CloudPrintHelpers::HandleServerError(int* error_count, int max_retry_count,
+ int64 max_retry_interval,
+ int64 base_retry_interval,
+ Task* task_to_retry,
+ Task* task_on_give_up) {
+ (*error_count)++;
+ if ((-1 != max_retry_count) && (*error_count > max_retry_count)) {
+ if (task_on_give_up) {
+ MessageLoop::current()->PostTask(FROM_HERE, task_on_give_up);
+ }
+ } else {
+ int64 retry_interval = base_retry_interval * (*error_count);
+ if ((-1 != max_retry_interval) && (retry_interval > max_retry_interval)) {
+ retry_interval = max_retry_interval;
+ }
+ MessageLoop::current()->PostDelayedTask(FROM_HERE, task_to_retry,
+ retry_interval);
+ }
+}
+
+void CloudPrintHelpers::AddMultipartValueForUpload(
+ const std::string& value_name, const std::string& value,
+ const std::string& mime_boundary, const std::string& content_type,
+ std::string* post_data) {
+ DCHECK(post_data);
+ // First line is the boundary
+ post_data->append("--" + mime_boundary + "\r\n");
+ // Next line is the Content-disposition
+ post_data->append(StringPrintf("Content-Disposition: form-data; "
+ "name=\"%s\"\r\n", value_name.c_str()));
+ if (!content_type.empty()) {
+ // If Content-type is specified, the next line is that
+ post_data->append(StringPrintf("Content-Type: %s\r\n",
+ content_type.c_str()));
+ }
+ // Leave an empty line and append the value.
+ post_data->append(StringPrintf("\r\n%s\r\n", value.c_str()));
+}
+
+// Create a MIME boundary marker (27 '-' characters followed by 16 hex digits).
+void CloudPrintHelpers::CreateMimeBoundaryForUpload(std::string* out) {
+ int r1 = base::RandInt(0, kint32max);
+ int r2 = base::RandInt(0, kint32max);
+ SStringPrintf(out, "---------------------------%08X%08X", r1, r2);
+}
+
diff --git a/chrome/browser/printing/cloud_print/cloud_print_helpers.h b/chrome/browser/printing/cloud_print/cloud_print_helpers.h
new file mode 100644
index 0000000..46f0e5b
--- /dev/null
+++ b/chrome/browser/printing/cloud_print/cloud_print_helpers.h
@@ -0,0 +1,71 @@
+// 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.
+
+#ifndef CHROME_BROWSER_PRINTING_CLOUD_PRINT_CLOUD_PRINT_HELPERS_H_
+#define CHROME_BROWSER_PRINTING_CLOUD_PRINT_CLOUD_PRINT_HELPERS_H_
+
+#include <string>
+
+#include "chrome/browser/printing/cloud_print/printer_info.h"
+#include "googleurl/src/gurl.h"
+
+class DictionaryValue;
+class Task;
+class URLFetcher;
+
+// Helper methods for the cloud print proxy code.
+class CloudPrintHelpers {
+ public:
+ static GURL GetUrlForPrinterRegistration();
+ static GURL GetUrlForPrinterUpdate(const std::string& printer_id);
+ static GURL GetUrlForPrinterDelete(const std::string& printer_id);
+ static GURL GetUrlForPrinterList(const std::string& proxy_id);
+ static GURL GetUrlForJobFetch(const std::string& printer_id);
+ static GURL GetUrlForJobStatusUpdate(const std::string& job_id,
+ cloud_print::PrintJobStatus status);
+ static GURL GetUrlForJobStatusUpdate(
+ const std::string& job_id, const cloud_print::PrintJobDetails& details);
+ // Parses the response data for any cloud print server request. The method
+ // returns false if there was an error in parsing the JSON. The succeeded
+ // value returns the value of the "success" value in the response JSON.
+ // Returns the response as a dictionary value.
+ static bool ParseResponseJSON(const std::string& response_data,
+ bool* succeeded, DictionaryValue** response_dict);
+ // Sets up common parameters for a cloud print request
+ // (such as the GAIA auth token in the request headers, the request context
+ // etc).
+ static void PrepCloudPrintRequest(URLFetcher* request,
+ const std::string& auth_token);
+ // Strictly speaking, this helper method is not specific to cloud printing.
+ // It handles the logic to retry tasks when the server returns an error.
+ // The parameters are as below:
+ // |error_count| Contains the current number of consecutive failed attempts.
+ // This method increments it (in/out)
+ // |max_retry_count| Number of retries before giving up. -1 implies no limit.
+ // |max_retry_interval| Maximum amount of time (in ms) we are willing to
+ // wait between retries. -1 implies no limit.
+ // |base_retry_interval| Starting value of the retry interval. This
+ // method progressively increases the interval on each retry.
+ // |task_to_retry| The task to be retried.
+ // |task_on_give_up| Task to be performed when we give up. This is only
+ // valid when max_retry_count is not -1. It can be NULL.
+ static void HandleServerError(int* error_count, int max_retry_count,
+ int64 max_retry_interval,
+ int64 base_retry_interval,
+ Task* task_to_retry, Task* task_on_give_up);
+ // Prepares one value as part of a multi-part upload request.
+ static void AddMultipartValueForUpload(
+ const std::string& value_name, const std::string& value,
+ const std::string& mime_boundary, const std::string& content_type,
+ std::string* post_data);
+// Create a MIME boundary marker (27 '-' characters followed by 16 hex digits).
+ static void CreateMimeBoundaryForUpload(std::string *out);
+
+ private:
+ CloudPrintHelpers() {
+ }
+};
+
+#endif // CHROME_BROWSER_PRINTING_CLOUD_PRINT_CLOUD_PRINT_HELPERS_H_
+
diff --git a/chrome/browser/printing/cloud_print/cloud_print_proxy_backend.cc b/chrome/browser/printing/cloud_print/cloud_print_proxy_backend.cc
new file mode 100644
index 0000000..dbc6640
--- /dev/null
+++ b/chrome/browser/printing/cloud_print/cloud_print_proxy_backend.cc
@@ -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);
+}
+
diff --git a/chrome/browser/printing/cloud_print/cloud_print_proxy_backend.h b/chrome/browser/printing/cloud_print/cloud_print_proxy_backend.h
new file mode 100644
index 0000000..675ae12
--- /dev/null
+++ b/chrome/browser/printing/cloud_print/cloud_print_proxy_backend.h
@@ -0,0 +1,70 @@
+// 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.
+
+#ifndef CHROME_BROWSER_PRINTING_CLOUD_PRINT_CLOUD_PRINT_PROXY_BACKEND_H_
+#define CHROME_BROWSER_PRINTING_CLOUD_PRINT_CLOUD_PRINT_PROXY_BACKEND_H_
+
+#include <map>
+#include <string>
+
+#include "base/thread.h"
+#include "chrome/browser/net/url_fetcher.h"
+#include "chrome/browser/printing/cloud_print/printer_info.h"
+
+class CloudPrintProxyService;
+class DictionaryValue;
+
+// CloudPrintProxyFrontend is the interface used by CloudPrintProxyBackend to
+// communicate with the entity that created it and, presumably, is interested in
+// cloud print proxy related activity.
+// NOTE: All methods will be invoked by a CloudPrintProxyBackend on the same
+// thread used to create that CloudPrintProxyBackend.
+class CloudPrintProxyFrontend {
+ public:
+ CloudPrintProxyFrontend() {}
+
+ // There is a list of printers available that can be registered.
+ virtual void OnPrinterListAvailable(
+ const cloud_print::PrinterList& printer_list) = 0;
+
+ protected:
+ // Don't delete through SyncFrontend interface.
+ virtual ~CloudPrintProxyFrontend() {
+ }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CloudPrintProxyFrontend);
+};
+
+class CloudPrintProxyBackend {
+ public:
+ explicit CloudPrintProxyBackend(CloudPrintProxyFrontend* frontend);
+ ~CloudPrintProxyBackend();
+
+ bool Initialize(const std::string& auth_token, const std::string& proxy_id);
+ void Shutdown();
+ void RegisterPrinters(const cloud_print::PrinterList& printer_list);
+ void HandlePrinterNotification(const std::string& printer_id);
+
+ private:
+ // The real guts of SyncBackendHost, to keep the public client API clean.
+ class Core;
+ // A thread we dedicate for use to perform initialization and
+ // authentication.
+ base::Thread core_thread_;
+ // Our core, which communicates with AuthWatcher for GAIA authentication and
+ // which contains printer registration code.
+ scoped_refptr<Core> core_;
+ // A reference to the MessageLoop used to construct |this|, so we know how
+ // to safely talk back to the SyncFrontend.
+ MessageLoop* const frontend_loop_;
+ // The frontend which is responsible for displaying UI and updating Prefs
+ CloudPrintProxyFrontend* frontend_;
+
+ friend class base::RefCountedThreadSafe<CloudPrintProxyBackend::Core>;
+
+ DISALLOW_COPY_AND_ASSIGN(CloudPrintProxyBackend);
+};
+
+#endif // CHROME_BROWSER_PRINTING_CLOUD_PRINT_CLOUD_PRINT_PROXY_BACKEND_H_
+
diff --git a/chrome/browser/printing/cloud_print/cloud_print_proxy_service.cc b/chrome/browser/printing/cloud_print/cloud_print_proxy_service.cc
new file mode 100644
index 0000000..75ec15c
--- /dev/null
+++ b/chrome/browser/printing/cloud_print/cloud_print_proxy_service.cc
@@ -0,0 +1,103 @@
+// 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_service.h"
+
+#include <stack>
+#include <vector>
+
+#include "base/path_service.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/profile_manager.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/notification_type.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/browser/pref_service.h"
+
+CloudPrintProxyService::CloudPrintProxyService(Profile* profile) {
+}
+
+CloudPrintProxyService::~CloudPrintProxyService() {
+ Shutdown();
+}
+
+void CloudPrintProxyService::Initialize() {
+ PrefService* prefs = g_browser_process->local_state();
+ DCHECK(prefs);
+ prefs->RegisterStringPref(prefs::kCloudPrintProxyId, L"");
+ prefs->RegisterStringPref(prefs::kCloudPrintProxyName, L"");
+ prefs->RegisterStringPref(prefs::kCloudPrintAuthToken, L"");
+}
+
+
+void CloudPrintProxyService::EnableForUser(const std::string& auth_token) {
+ if (backend_.get())
+ return;
+
+ PrefService* prefs = g_browser_process->local_state();
+ DCHECK(prefs);
+ std::string proxy_id =
+ WideToUTF8(prefs->GetString(prefs::kCloudPrintProxyId));
+ if (proxy_id.empty()) {
+ // TODO(sanjeevr): Determine whether the proxy id should be server generated
+ proxy_id = cloud_print::GenerateProxyId();
+ prefs->SetString(prefs::kCloudPrintProxyId, UTF8ToWide(proxy_id));
+ }
+ std::string token_to_use = auth_token;
+ if (token_to_use.empty()) {
+ token_to_use = WideToUTF8(prefs->GetString(prefs::kCloudPrintAuthToken));
+ } else {
+ prefs->SetString(prefs::kCloudPrintAuthToken, UTF8ToWide(token_to_use));
+ }
+
+ backend_.reset(new CloudPrintProxyBackend(this));
+ backend_->Initialize(token_to_use, proxy_id);
+}
+
+void CloudPrintProxyService::DisableForUser() {
+ PrefService* prefs = g_browser_process->local_state();
+ DCHECK(prefs);
+ prefs->ClearPref(prefs::kCloudPrintAuthToken);
+ Shutdown();
+}
+
+void CloudPrintProxyService::HandlePrinterNotification(
+ const std::string& printer_id) {
+ if (backend_.get())
+ backend_->HandlePrinterNotification(printer_id);
+}
+
+void CloudPrintProxyService::Shutdown() {
+ if (backend_.get())
+ backend_->Shutdown();
+ backend_.reset();
+}
+
+// Notification methods from the backend. Called on UI thread.
+void CloudPrintProxyService::OnPrinterListAvailable(
+ const cloud_print::PrinterList& printer_list) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ // Here we will trim the list to eliminate printers already registered.
+ // If there are any more printers left in the list after trimming, we will
+ // show the print selection UI. Any printers left in the list after the user
+ // selection process will then be registered.
+ backend_->RegisterPrinters(printer_list);
+}
+
+// Called when authentication is done. Called on UI thread.
+// Note that sid can be empty. This is a temp function to steal the sid
+// from the Bookmarks Sync code. When the common GAIA signin code is done,
+// The CloudPrintProxyService will simply get a notification when authentication
+// done with an lsid and a sid.
+void CloudPrintProxyService::OnAuthenticated(const std::string& sid) {
+ FilePath user_data_dir;
+ PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ Profile* profile = profile_manager->GetDefaultProfile(user_data_dir);
+ profile->GetCloudPrintProxyService()->EnableForUser(sid);
+}
+
diff --git a/chrome/browser/printing/cloud_print/cloud_print_proxy_service.h b/chrome/browser/printing/cloud_print/cloud_print_proxy_service.h
new file mode 100644
index 0000000..92985e2
--- /dev/null
+++ b/chrome/browser/printing/cloud_print/cloud_print_proxy_service.h
@@ -0,0 +1,59 @@
+// 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.
+
+#ifndef CHROME_BROWSER_PRINTING_CLOUD_PRINT_CLOUD_PRINT_PROXY_SERVICE_H_
+#define CHROME_BROWSER_PRINTING_CLOUD_PRINT_CLOUD_PRINT_PROXY_SERVICE_H_
+
+#include <string>
+#include <map>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/observer_list.h"
+#include "base/scoped_ptr.h"
+#include "chrome/browser/profile.h"
+
+#include "chrome/browser/printing/cloud_print/cloud_print_proxy_backend.h"
+
+class Profile;
+
+// Layer between the browser user interface and the cloud print proxy backend.
+class CloudPrintProxyService : public CloudPrintProxyFrontend {
+ public:
+ explicit CloudPrintProxyService(Profile* profile);
+ virtual ~CloudPrintProxyService();
+
+ // Initializes the object. This should be called every time an object of this
+ // class is constructed.
+ void Initialize();
+
+ // Enables/disables cloud printing for the user
+ virtual void EnableForUser(const std::string& auth_token);
+ virtual void DisableForUser();
+ // Notification received from the server for a particular printer.
+ // We need to inform the backend to look for jobs for this printer.
+ void HandlePrinterNotification(const std::string& printer_id);
+
+ // Notification methods from the backend. Called on UI thread.
+ void OnPrinterListAvailable(const cloud_print::PrinterList& printer_list);
+
+ // Called when authentication is done. Called on UI thread.
+ // Note that sid can be empty. This is placeholder code until we can get
+ // common GAIA signin code in place.
+ // TODO(sanjeevr): Remove this.
+ static void OnAuthenticated(const std::string& auth_token);
+
+ protected:
+ void Shutdown();
+
+ // Our asynchronous backend to communicate with sync components living on
+ // other threads.
+ scoped_ptr<CloudPrintProxyBackend> backend_;
+
+ DISALLOW_COPY_AND_ASSIGN(CloudPrintProxyService);
+};
+
+#endif // CHROME_BROWSER_PRINTING_CLOUD_PRINT_CLOUD_PRINT_PROXY_SERVICE_H_
+
diff --git a/chrome/browser/printing/cloud_print/job_status_updater.cc b/chrome/browser/printing/cloud_print/job_status_updater.cc
new file mode 100644
index 0000000..a17c451
--- /dev/null
+++ b/chrome/browser/printing/cloud_print/job_status_updater.cc
@@ -0,0 +1,90 @@
+// 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/job_status_updater.h"
+
+#include "base/json/json_reader.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/printing/cloud_print/cloud_print_consts.h"
+#include "chrome/browser/printing/cloud_print/cloud_print_helpers.h"
+#include "googleurl/src/gurl.h"
+
+JobStatusUpdater::JobStatusUpdater(const std::string& printer_name,
+ const std::string& job_id,
+ cloud_print::PlatformJobId& local_job_id,
+ const std::string& auth_token,
+ Delegate* delegate)
+ : printer_name_(printer_name), job_id_(job_id),
+ local_job_id_(local_job_id), auth_token_(auth_token),
+ delegate_(delegate), stopped_(false) {
+ DCHECK(delegate_);
+}
+
+// Start checking the status of the local print job.
+void JobStatusUpdater::UpdateStatus() {
+ // It does not matter if we had already sent out an update and are waiting for
+ // a response. This is a new update and we will simply cancel the old request
+ // and send a new one.
+ if (!stopped_) {
+ bool need_update = false;
+ // If the job has already been completed, we just need to update the server
+ // with that status. The *only* reason we would come back here in that case
+ // is if our last server update attempt failed.
+ if (last_job_details_.status == cloud_print::PRINT_JOB_STATUS_COMPLETED) {
+ need_update = true;
+ } else {
+ cloud_print::PrintJobDetails details;
+ if (cloud_print::GetJobDetails(printer_name_, local_job_id_, &details)) {
+ if (details != last_job_details_) {
+ last_job_details_ = details;
+ need_update = true;
+ }
+ } else {
+ // If GetJobDetails failed, the most likely case is that the job no
+ // longer exists in the OS queue. We are going to assume it is done in
+ // this case.
+ last_job_details_.Clear();
+ last_job_details_.status = cloud_print::PRINT_JOB_STATUS_COMPLETED;
+ need_update = true;
+ }
+ }
+ if (need_update) {
+ GURL update_url = CloudPrintHelpers::GetUrlForJobStatusUpdate(
+ job_id_, last_job_details_);
+ request_.reset(new URLFetcher(update_url, URLFetcher::GET, this));
+ CloudPrintHelpers::PrepCloudPrintRequest(request_.get(), auth_token_);
+ request_->Start();
+ }
+ }
+}
+
+void JobStatusUpdater::Stop() {
+ request_.reset();
+ DCHECK(delegate_);
+ stopped_ = true;
+ delegate_->OnJobCompleted(this);
+}
+
+// URLFetcher::Delegate implementation.
+void JobStatusUpdater::OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const URLRequestStatus& status,
+ int response_code,
+ const ResponseCookies& cookies,
+ const std::string& data) {
+ int64 next_update_interval = kJobStatusUpdateInterval;
+ if (!status.is_success() || (response_code != 200)) {
+ next_update_interval *= 10;
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, NewRunnableMethod(this, &JobStatusUpdater::UpdateStatus),
+ next_update_interval);
+ } else if (last_job_details_.status ==
+ cloud_print::PRINT_JOB_STATUS_COMPLETED) {
+ MessageLoop::current()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &JobStatusUpdater::Stop));
+ }
+}
+
diff --git a/chrome/browser/printing/cloud_print/job_status_updater.h b/chrome/browser/printing/cloud_print/job_status_updater.h
new file mode 100644
index 0000000..d480621
--- /dev/null
+++ b/chrome/browser/printing/cloud_print/job_status_updater.h
@@ -0,0 +1,64 @@
+// 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.
+
+#ifndef CHROME_BROWSER_PRINTING_CLOUD_PRINT_JOB_STATUS_UPDATER_H_
+#define CHROME_BROWSER_PRINTING_CLOUD_PRINT_JOB_STATUS_UPDATER_H_
+
+#include <string>
+
+#include "base/file_path.h"
+#include "base/ref_counted.h"
+#include "base/thread.h"
+#include "chrome/browser/printing/cloud_print/printer_info.h"
+#include "chrome/browser/net/url_fetcher.h"
+#include "net/url_request/url_request_status.h"
+
+// Periodically monitors the status of a local print job and updates the
+// cloud print server accordingly. When the job has been completed this
+// object releases the reference to itself which should cause it to
+// self-destruct.
+class JobStatusUpdater : public base::RefCountedThreadSafe<JobStatusUpdater>,
+ public URLFetcher::Delegate {
+ public:
+ class Delegate {
+ public:
+ virtual bool OnJobCompleted(JobStatusUpdater* updater) = 0;
+ };
+
+ JobStatusUpdater(const std::string& printer_name,
+ const std::string& job_id,
+ cloud_print::PlatformJobId& local_job_id,
+ const std::string& auth_token,
+ Delegate* delegate);
+ // Checks the status of the local print job and sends an update.
+ void UpdateStatus();
+ void Stop();
+ // 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);
+ private:
+ std::string printer_name_;
+ std::string job_id_;
+ cloud_print::PlatformJobId local_job_id_;
+ cloud_print::PrintJobDetails last_job_details_;
+ scoped_ptr<URLFetcher> request_;
+ std::string auth_token_;
+ Delegate* delegate_;
+ // A flag that is set to true in Stop() and will ensure the next scheduled
+ // task will do nothing.
+ bool stopped_;
+ DISALLOW_COPY_AND_ASSIGN(JobStatusUpdater);
+};
+
+// This typedef is to workaround the issue with certain versions of
+// Visual Studio where it gets confused between multiple Delegate
+// classes and gives a C2500 error. (I saw this error on the try bots -
+// the workaround was not needed for my machine).
+typedef JobStatusUpdater::Delegate JobStatusUpdaterDelegate;
+
+#endif // CHROME_BROWSER_PRINTING_CLOUD_PRINT_JOB_STATUS_UPDATER_H_
+
diff --git a/chrome/browser/printing/cloud_print/printer_info.h b/chrome/browser/printing/cloud_print/printer_info.h
new file mode 100644
index 0000000..1bb1746
--- /dev/null
+++ b/chrome/browser/printing/cloud_print/printer_info.h
@@ -0,0 +1,125 @@
+// 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.
+
+#ifndef CHROME_BROWSER_PRINTING_CLOUD_PRINT_PRINTER_INFO_H_
+#define CHROME_BROWSER_PRINTING_CLOUD_PRINT_PRINTER_INFO_H_
+
+#include <string>
+#include <vector>
+
+#include "base/file_path.h"
+
+// This is the interface for platform-specific code for cloud print
+namespace cloud_print {
+
+typedef int PlatformJobId;
+
+struct PrinterBasicInfo {
+ std::string printer_name;
+ std::string printer_description;
+ int printer_status;
+ PrinterBasicInfo() : printer_status(0) {
+ }
+};
+
+typedef std::vector<PrinterBasicInfo> PrinterList;
+
+struct PrinterCapsAndDefaults {
+ std::string printer_capabilities;
+ std::string caps_mime_type;
+ std::string printer_defaults;
+ std::string defaults_mime_type;
+};
+
+enum PrintJobStatus {
+ PRINT_JOB_STATUS_INVALID,
+ PRINT_JOB_STATUS_IN_PROGRESS,
+ PRINT_JOB_STATUS_ERROR,
+ PRINT_JOB_STATUS_COMPLETED
+};
+
+struct PrintJobDetails {
+ PrintJobStatus status;
+ int platform_status_flags;
+ std::string status_message;
+ int total_pages;
+ int pages_printed;
+ PrintJobDetails() : status(PRINT_JOB_STATUS_INVALID),
+ platform_status_flags(0), total_pages(0),
+ pages_printed(0) {
+ }
+ void Clear() {
+ status = PRINT_JOB_STATUS_INVALID;
+ platform_status_flags = 0;
+ status_message.clear();
+ total_pages = 0;
+ pages_printed = 0;
+ }
+ bool operator ==(const PrintJobDetails& other) const {
+ return (status == other.status) &&
+ (platform_status_flags == other.platform_status_flags) &&
+ (status_message == other.status_message) &&
+ (total_pages == other.total_pages) &&
+ (pages_printed == other.pages_printed);
+ }
+ bool operator !=(const PrintJobDetails& other) const {
+ return !(*this == other);
+ }
+};
+
+// Enumerates the list of installed local and network printers.
+void EnumeratePrinters(PrinterList* printer_list);
+// Gets the capabilities and defaults for a specific printer.
+bool GetPrinterCapsAndDefaults(const std::string& printer_name,
+ PrinterCapsAndDefaults* printer_info);
+bool ValidatePrintTicket(const std::string& printer_name,
+ const std::string& print_ticket_data);
+std::string GenerateProxyId();
+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);
+
+bool GetJobDetails(const std::string& printer_name,
+ PlatformJobId job_id,
+ PrintJobDetails *job_details);
+bool IsValidPrinter(const std::string& printer_name);
+
+// A class that watches changes to a printer or a print server.
+// The set of notifications are very coarse-grained (even though the Windows
+// API allows for listening to fine-grained details about a printer, this class
+// does not support that level of fine-grained control.
+class PrinterChangeNotifier {
+ public:
+ class Delegate {
+ public:
+ virtual void OnPrinterAdded() = 0;
+ virtual void OnPrinterDeleted() = 0;
+ virtual void OnPrinterChanged() = 0;
+ virtual void OnJobChanged() = 0;
+ };
+ PrinterChangeNotifier();
+ ~PrinterChangeNotifier();
+ bool StartWatching(const std::string& printer_name, Delegate* delegate);
+ bool StopWatching();
+ bool GetCurrentPrinterInfo(PrinterBasicInfo* printer_info);
+ private:
+ // Internal state maintained by the PrinterChangeNotifier class.
+ class NotificationState;
+ NotificationState* state_;
+ DISALLOW_COPY_AND_ASSIGN(PrinterChangeNotifier);
+};
+
+// This typedef is to workaround the issue with certain versions of
+// Visual Studio where it gets confused between multiple Delegate
+// classes and gives a C2500 error. (I saw this error on the try bots -
+// the workaround was not needed for my machine).
+typedef PrinterChangeNotifier::Delegate PrinterChangeNotifierDelegate;
+
+} // namespace cloud_print
+
+#endif // CHROME_BROWSER_PRINTING_CLOUD_PRINT_PRINTER_INFO_H_
+
diff --git a/chrome/browser/printing/cloud_print/printer_info_linux.cc b/chrome/browser/printing/cloud_print/printer_info_linux.cc
new file mode 100644
index 0000000..0715bb8
--- /dev/null
+++ b/chrome/browser/printing/cloud_print/printer_info_linux.cc
@@ -0,0 +1,80 @@
+// 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 "base/logging.h"
+
+// TODO(sanjeevr): Implement the Linux interfaces.
+namespace cloud_print {
+
+void EnumeratePrinters(PrinterList* printer_list) {
+ DCHECK(printer_list);
+ NOTIMPLEMENTED();
+}
+
+bool GetPrinterCapsAndDefaults(const std::string& printer_name,
+ PrinterCapsAndDefaults* printer_info) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool ValidatePrintTicket(const std::string& printer_name,
+ const std::string& print_ticket_data) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+std::string GenerateProxyId() {
+ NOTIMPLEMENTED();
+ return std::string();
+}
+
+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) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool GetJobDetails(const std::string& printer_name,
+ PlatformJobId job_id,
+ PrintJobDetails *job_details) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool IsValidPrinter(const std::string& printer_name) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+PrinterChangeNotifier::PrinterChangeNotifier() : state_(NULL) {
+}
+
+PrinterChangeNotifier::~PrinterChangeNotifier() {
+ StopWatching();
+}
+
+bool PrinterChangeNotifier::StartWatching(const std::string& printer_name,
+ Delegate* delegate) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool PrinterChangeNotifier::StopWatching() {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool PrinterChangeNotifier::GetCurrentPrinterInfo(
+ PrinterBasicInfo* printer_info) {
+ NOTIMPLEMENTED();
+ return false;
+}
+} // namespace cloud_print
+
diff --git a/chrome/browser/printing/cloud_print/printer_info_mac.cc b/chrome/browser/printing/cloud_print/printer_info_mac.cc
new file mode 100644
index 0000000..dd0dbad
--- /dev/null
+++ b/chrome/browser/printing/cloud_print/printer_info_mac.cc
@@ -0,0 +1,80 @@
+// 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 "base/logging.h"
+
+// TODO(sanjeevr): Implement the Mac interfaces.
+namespace cloud_print {
+
+void EnumeratePrinters(PrinterList* printer_list) {
+ DCHECK(printer_list);
+ NOTIMPLEMENTED();
+}
+
+bool GetPrinterCapsAndDefaults(const std::string& printer_name,
+ PrinterCapsAndDefaults* printer_info) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool ValidatePrintTicket(const std::string& printer_name,
+ const std::string& print_ticket_data) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+std::string GenerateProxyId() {
+ NOTIMPLEMENTED();
+ return std::string();
+}
+
+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) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool GetJobDetails(const std::string& printer_name,
+ PlatformJobId job_id,
+ PrintJobDetails *job_details) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool IsValidPrinter(const std::string& printer_name) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+PrinterChangeNotifier::PrinterChangeNotifier() : state_(NULL) {
+}
+
+PrinterChangeNotifier::~PrinterChangeNotifier() {
+ StopWatching();
+}
+
+bool PrinterChangeNotifier::StartWatching(const std::string& printer_name,
+ Delegate* delegate) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool PrinterChangeNotifier::StopWatching() {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool PrinterChangeNotifier::GetCurrentPrinterInfo(
+ PrinterBasicInfo* printer_info) {
+ NOTIMPLEMENTED();
+ return false;
+}
+} // namespace cloud_print
+
diff --git a/chrome/browser/printing/cloud_print/printer_info_win.cc b/chrome/browser/printing/cloud_print/printer_info_win.cc
new file mode 100644
index 0000000..b1ec208
--- /dev/null
+++ b/chrome/browser/printing/cloud_print/printer_info_win.cc
@@ -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
+
diff --git a/chrome/browser/printing/cloud_print/printer_job_handler.cc b/chrome/browser/printing/cloud_print/printer_job_handler.cc
new file mode 100644
index 0000000..a27fdf0
--- /dev/null
+++ b/chrome/browser/printing/cloud_print/printer_job_handler.cc
@@ -0,0 +1,560 @@
+// 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_job_handler.h"
+
+#include "base/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/md5.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.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/job_status_updater.h"
+#include "googleurl/src/gurl.h"
+#include "net/http/http_response_headers.h"
+
+PrinterJobHandler::PrinterJobHandler(
+ const cloud_print::PrinterBasicInfo& printer_info,
+ const std::string& printer_id,
+ const std::string& caps_hash,
+ const std::string& auth_token,
+ Delegate* delegate)
+ : printer_info_(printer_info),
+ printer_id_(printer_id),
+ auth_token_(auth_token),
+ last_caps_hash_(caps_hash),
+ delegate_(delegate),
+ local_job_id_(-1),
+ next_response_handler_(NULL),
+ server_error_count_(0),
+ print_thread_("Chrome_CloudPrintJobPrintThread"),
+ shutting_down_(false),
+ server_job_available_(false),
+ printer_update_pending_(true),
+ printer_delete_pending_(false),
+ task_in_progress_(false) {
+}
+
+bool PrinterJobHandler::Initialize() {
+ if (cloud_print::IsValidPrinter(printer_info_.printer_name)) {
+ printer_change_notifier_.StartWatching(printer_info_.printer_name, this);
+ NotifyJobAvailable();
+ } else {
+ // This printer does not exist any more. Delete it from the server.
+ OnPrinterDeleted();
+ }
+ return true;
+}
+
+PrinterJobHandler::~PrinterJobHandler() {
+ printer_change_notifier_.StopWatching();
+}
+
+void PrinterJobHandler::Reset() {
+ print_data_url_.clear();
+ job_details_.Clear();
+ request_.reset();
+ print_thread_.Stop();
+}
+
+void PrinterJobHandler::Start() {
+ if (task_in_progress_) {
+ // Multiple Starts can get posted because of multiple notifications
+ // We want to ignore the other ones that happen when a task is in progress.
+ return;
+ }
+ Reset();
+ if (!shutting_down_) {
+ // Check if we have work to do.
+ if (HavePendingTasks()) {
+ if (printer_delete_pending_) {
+ printer_delete_pending_ = false;
+ task_in_progress_ = true;
+ MakeServerRequest(
+ CloudPrintHelpers::GetUrlForPrinterDelete(printer_id_),
+ &PrinterJobHandler::HandlePrinterDeleteResponse);
+ }
+ if (!task_in_progress_ && printer_update_pending_) {
+ printer_update_pending_ = false;
+ task_in_progress_ = UpdatePrinterInfo();
+ }
+ if (!task_in_progress_ && server_job_available_) {
+ task_in_progress_ = true;
+ server_job_available_ = false;
+ // We need to fetch any pending jobs for this printer
+ MakeServerRequest(CloudPrintHelpers::GetUrlForJobFetch(printer_id_),
+ &PrinterJobHandler::HandleJobMetadataResponse);
+ }
+ }
+ }
+}
+
+void PrinterJobHandler::Stop() {
+ task_in_progress_ = false;
+ Reset();
+ if (HavePendingTasks()) {
+ MessageLoop::current()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &PrinterJobHandler::Start));
+ }
+}
+
+void PrinterJobHandler::NotifyJobAvailable() {
+ server_job_available_ = true;
+ if (!task_in_progress_) {
+ MessageLoop::current()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &PrinterJobHandler::Start));
+ }
+}
+
+bool PrinterJobHandler::UpdatePrinterInfo() {
+ // We need to update the parts of the printer info that have changed
+ // (could be printer name, description, status or capabilities).
+ cloud_print::PrinterBasicInfo printer_info;
+ printer_change_notifier_.GetCurrentPrinterInfo(&printer_info);
+ cloud_print::PrinterCapsAndDefaults printer_caps;
+ std::string post_data;
+ std::string mime_boundary;
+ if (cloud_print::GetPrinterCapsAndDefaults(printer_info.printer_name,
+ &printer_caps)) {
+ std::string caps_hash = MD5String(printer_caps.printer_capabilities);
+ CloudPrintHelpers::CreateMimeBoundaryForUpload(&mime_boundary);
+ if (caps_hash != last_caps_hash_) {
+ // Hashes don't match, we need to upload new capabilities (the defaults
+ // go for free along with the capabilities)
+ last_caps_hash_ = caps_hash;
+ CloudPrintHelpers::AddMultipartValueForUpload(
+ kPrinterCapsValue, printer_caps.printer_capabilities,
+ mime_boundary, printer_caps.caps_mime_type, &post_data);
+ CloudPrintHelpers::AddMultipartValueForUpload(
+ kPrinterDefaultsValue, printer_caps.printer_defaults,
+ mime_boundary, printer_caps.defaults_mime_type,
+ &post_data);
+ CloudPrintHelpers::AddMultipartValueForUpload(
+ WideToUTF8(kPrinterCapsHashValue).c_str(), caps_hash, mime_boundary,
+ std::string(), &post_data);
+ }
+ }
+ if (printer_info.printer_name != printer_info_.printer_name) {
+ CloudPrintHelpers::AddMultipartValueForUpload(kPrinterNameValue,
+ printer_info.printer_name,
+ mime_boundary,
+ std::string(), &post_data);
+ }
+ if (printer_info.printer_description != printer_info_.printer_description) {
+ CloudPrintHelpers::AddMultipartValueForUpload(
+ kPrinterDescValue, printer_info.printer_description, mime_boundary,
+ std::string() , &post_data);
+ }
+ if (printer_info.printer_status != printer_info_.printer_status) {
+ CloudPrintHelpers::AddMultipartValueForUpload(
+ kPrinterStatusValue, StringPrintf("%d", printer_info.printer_status),
+ mime_boundary, std::string(), &post_data);
+ }
+ printer_info_ = printer_info;
+ bool ret = false;
+ if (!post_data.empty()) {
+ // 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::GetUrlForPrinterUpdate(printer_id_),
+ URLFetcher::POST, this));
+ CloudPrintHelpers::PrepCloudPrintRequest(request_.get(), auth_token_);
+ request_->set_upload_data(mime_type, post_data);
+ next_response_handler_ = &PrinterJobHandler::HandlePrinterUpdateResponse;
+ request_->Start();
+ ret = true;
+ }
+ return ret;
+}
+
+// URLFetcher::Delegate implementation.
+void PrinterJobHandler::OnURLFetchComplete(
+ const URLFetcher* source, const GURL& url, const URLRequestStatus& status,
+ int response_code, const ResponseCookies& cookies,
+ const std::string& data) {
+ if (!shutting_down_) {
+ DCHECK(source == request_.get());
+ // We need a next response handler because we are strictly a sequential
+ // state machine. We need each response handler to tell us which state to
+ // advance to next.
+ DCHECK(next_response_handler_);
+ if (!(this->*next_response_handler_)(source, url, status,
+ response_code, cookies, data)) {
+ // By contract, if the response handler returns false, it wants us to
+ // retry the request (upto the usual limit after which we give up and
+ // send the state machine to the Stop state);
+ HandleServerError(url);
+ }
+ }
+}
+
+// JobStatusUpdater::Delegate implementation
+bool PrinterJobHandler::OnJobCompleted(JobStatusUpdater* updater) {
+ bool ret = false;
+ for (JobStatusUpdaterList::iterator index = job_status_updater_list_.begin();
+ index != job_status_updater_list_.end(); index++) {
+ if (index->get() == updater) {
+ job_status_updater_list_.erase(index);
+ ret = true;
+ break;
+ }
+ }
+ return ret;
+}
+
+ // cloud_print::PrinterChangeNotifier::Delegate implementation
+void PrinterJobHandler::OnPrinterAdded() {
+ // Should never get this notification for a printer
+ NOTREACHED();
+}
+
+void PrinterJobHandler::OnPrinterDeleted() {
+ printer_delete_pending_ = true;
+ if (!task_in_progress_) {
+ MessageLoop::current()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &PrinterJobHandler::Start));
+ }
+}
+
+void PrinterJobHandler::OnPrinterChanged() {
+ printer_update_pending_ = true;
+ if (!task_in_progress_) {
+ MessageLoop::current()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &PrinterJobHandler::Start));
+ }
+}
+
+void PrinterJobHandler::OnJobChanged() {
+ // Some job on the printer changed. Loop through all our JobStatusUpdaters
+ // and have them check for updates.
+ for (JobStatusUpdaterList::iterator index = job_status_updater_list_.begin();
+ index != job_status_updater_list_.end(); index++) {
+ MessageLoop::current()->PostTask(
+ FROM_HERE, NewRunnableMethod(index->get(),
+ &JobStatusUpdater::UpdateStatus));
+ }
+}
+
+bool PrinterJobHandler::HandlePrinterUpdateResponse(
+ const URLFetcher* source, const GURL& url, const URLRequestStatus& status,
+ int response_code, const ResponseCookies& cookies,
+ const std::string& data) {
+ bool ret = false;
+ // If there was a network error or a non-200 response (which, for our purposes
+ // is the same as a network error), we want to retry.
+ if (status.is_success() && (response_code == 200)) {
+ bool succeeded = false;
+ DictionaryValue* response_dict = NULL;
+ CloudPrintHelpers::ParseResponseJSON(data, &succeeded, &response_dict);
+ // If we get valid JSON back, we are done.
+ if (NULL != response_dict) {
+ ret = true;
+ }
+ }
+ if (ret) {
+ // We are done here. Go to the Stop state
+ MessageLoop::current()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &PrinterJobHandler::Stop));
+ } else {
+ // Since we failed to update the server, set the flag again.
+ printer_update_pending_ = true;
+ }
+ return ret;
+}
+
+bool PrinterJobHandler::HandlePrinterDeleteResponse(
+ const URLFetcher* source, const GURL& url, const URLRequestStatus& status,
+ int response_code, const ResponseCookies& cookies,
+ const std::string& data) {
+ bool ret = false;
+ // If there was a network error or a non-200 response (which, for our purposes
+ // is the same as a network error), we want to retry.
+ if (status.is_success() && (response_code == 200)) {
+ bool succeeded = false;
+ DictionaryValue* response_dict = NULL;
+ CloudPrintHelpers::ParseResponseJSON(data, &succeeded, &response_dict);
+ // If we get valid JSON back, we are done.
+ if (NULL != response_dict) {
+ ret = true;
+ }
+ }
+ if (ret) {
+ // The printer has been deleted. Shutdown the handler class.
+ MessageLoop::current()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &PrinterJobHandler::Shutdown));
+ } else {
+ // Since we failed to update the server, set the flag again.
+ printer_delete_pending_ = true;
+ }
+ return ret;
+}
+
+bool PrinterJobHandler::HandleJobMetadataResponse(
+ const URLFetcher* source, const GURL& url, const URLRequestStatus& status,
+ int response_code, const ResponseCookies& cookies,
+ const std::string& data) {
+ // If there was a network error or a non-200 response (which, for our purposes
+ // is the same as a network error), we want to retry.
+ if (!status.is_success() || (response_code != 200)) {
+ return false;
+ }
+ bool succeeded = false;
+ DictionaryValue* response_dict = NULL;
+ CloudPrintHelpers::ParseResponseJSON(data, &succeeded, &response_dict);
+ if (NULL == response_dict) {
+ // If we did not get a valid JSON response, we need to retry.
+ return false;
+ }
+ Task* next_task = NULL;
+ if (succeeded) {
+ ListValue* job_list = NULL;
+ response_dict->GetList(kJobListValue, &job_list);
+ if (job_list) {
+ // Even though it is a job list, for now we are only interested in the
+ // first job
+ DictionaryValue* job_data = NULL;
+ if (job_list->GetDictionary(0, &job_data)) {
+ job_data->GetString(kIdValue, &job_details_.job_id_);
+ job_data->GetString(kTitleValue, &job_details_.job_title_);
+ std::string print_ticket_url;
+ job_data->GetString(kTicketUrlValue, &print_ticket_url);
+ job_data->GetString(kFileUrlValue, &print_data_url_);
+ next_task = NewRunnableMethod(
+ this, &PrinterJobHandler::MakeServerRequest,
+ GURL(print_ticket_url.c_str()),
+ &PrinterJobHandler::HandlePrintTicketResponse);
+ }
+ }
+ }
+ if (!next_task) {
+ // If we got a valid JSON but there were no jobs, we are done
+ next_task = NewRunnableMethod(this, &PrinterJobHandler::Stop);
+ }
+ delete response_dict;
+ DCHECK(next_task);
+ MessageLoop::current()->PostTask(FROM_HERE, next_task);
+ return true;
+}
+
+bool PrinterJobHandler::HandlePrintTicketResponse(
+ const URLFetcher* source, const GURL& url, const URLRequestStatus& status,
+ int response_code, const ResponseCookies& cookies,
+ const std::string& data) {
+ // If there was a network error or a non-200 response (which, for our purposes
+ // is the same as a network error), we want to retry.
+ if (!status.is_success() || (response_code != 200)) {
+ return false;
+ }
+ if (cloud_print::ValidatePrintTicket(printer_info_.printer_name, data)) {
+ job_details_.print_ticket_ = data;
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &PrinterJobHandler::MakeServerRequest,
+ GURL(print_data_url_.c_str()),
+ &PrinterJobHandler::HandlePrintDataResponse));
+ } else {
+ // The print ticket was not valid. We are done here.
+ MessageLoop::current()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &PrinterJobHandler::JobFailed,
+ INVALID_JOB_DATA));
+ }
+ return true;
+}
+
+bool PrinterJobHandler::HandlePrintDataResponse(const URLFetcher* source,
+ const GURL& url,
+ const URLRequestStatus& status,
+ int response_code,
+ const ResponseCookies& cookies,
+ const std::string& data) {
+ // If there was a network error or a non-200 response (which, for our purposes
+ // is the same as a network error), we want to retry.
+ if (!status.is_success() || (response_code != 200)) {
+ return false;
+ }
+ Task* next_task = NULL;
+ if (file_util::CreateTemporaryFile(&job_details_.print_data_file_path_)) {
+ int ret = file_util::WriteFile(job_details_.print_data_file_path_,
+ data.c_str(),
+ data.length());
+ source->response_headers()->GetMimeType(
+ &job_details_.print_data_mime_type_);
+ DCHECK(ret == static_cast<int>(data.length()));
+ if (ret == static_cast<int>(data.length())) {
+ next_task = NewRunnableMethod(this, &PrinterJobHandler::StartPrinting);
+ }
+ }
+ // If there was no task allocated above, then there was an error in
+ // saving the print data, bail out here.
+ if (!next_task) {
+ next_task = NewRunnableMethod(this, &PrinterJobHandler::JobFailed,
+ JOB_DOWNLOAD_FAILED);
+ }
+ MessageLoop::current()->PostTask(FROM_HERE, next_task);
+ return true;
+}
+
+void PrinterJobHandler::StartPrinting() {
+ // We are done with the request object for now.
+ request_.reset();
+ if (!shutting_down_) {
+ if (!print_thread_.Start()) {
+ JobFailed(PRINT_FAILED);
+ } else {
+ print_thread_.message_loop()->PostTask(
+ FROM_HERE, NewRunnableFunction(&PrinterJobHandler::DoPrint,
+ job_details_,
+ printer_info_.printer_name, this,
+ MessageLoop::current()));
+ }
+ }
+}
+
+void PrinterJobHandler::JobFailed(PrintJobError error) {
+ if (!shutting_down_) {
+ UpdateJobStatus(cloud_print::PRINT_JOB_STATUS_ERROR, error);
+ }
+}
+
+void PrinterJobHandler::JobSpooled(cloud_print::PlatformJobId local_job_id) {
+ if (!shutting_down_) {
+ local_job_id_ = local_job_id;
+ UpdateJobStatus(cloud_print::PRINT_JOB_STATUS_IN_PROGRESS, SUCCESS);
+ print_thread_.Stop();
+ }
+}
+
+void PrinterJobHandler::Shutdown() {
+ Reset();
+ shutting_down_ = true;
+ while (!job_status_updater_list_.empty()) {
+ // Calling Stop() will cause the OnJobCompleted to be called which will
+ // remove the updater object from the list.
+ job_status_updater_list_.front()->Stop();
+ }
+ if (delegate_) {
+ delegate_->OnPrinterJobHandlerShutdown(this, printer_id_);
+ }
+}
+
+void PrinterJobHandler::HandleServerError(const GURL& url) {
+ Task* task_to_retry = NewRunnableMethod(this,
+ &PrinterJobHandler::MakeServerRequest,
+ url, next_response_handler_);
+ Task* task_on_give_up = NewRunnableMethod(this, &PrinterJobHandler::Stop);
+ CloudPrintHelpers::HandleServerError(&server_error_count_, kMaxRetryCount,
+ -1, kBaseRetryInterval, task_to_retry,
+ task_on_give_up);
+}
+
+void PrinterJobHandler::UpdateJobStatus(cloud_print::PrintJobStatus status,
+ PrintJobError error) {
+ if (!shutting_down_) {
+ if (!job_details_.job_id_.empty()) {
+ ResponseHandler response_handler = NULL;
+ if (error == SUCCESS) {
+ response_handler =
+ &PrinterJobHandler::HandleSuccessStatusUpdateResponse;
+ } else {
+ response_handler =
+ &PrinterJobHandler::HandleFailureStatusUpdateResponse;
+ }
+ MakeServerRequest(
+ CloudPrintHelpers::GetUrlForJobStatusUpdate(job_details_.job_id_,
+ status),
+ response_handler);
+ }
+ }
+}
+
+bool PrinterJobHandler::HandleSuccessStatusUpdateResponse(
+ const URLFetcher* source, const GURL& url, const URLRequestStatus& status,
+ int response_code, const ResponseCookies& cookies,
+ const std::string& data) {
+ // If there was a network error or a non-200 response (which, for our purposes
+ // is the same as a network error), we want to retry.
+ if (!status.is_success() || (response_code != 200)) {
+ return false;
+ }
+ // The print job has been spooled locally. We now need to create an object
+ // that monitors the status of the job and updates the server.
+ scoped_refptr<JobStatusUpdater> job_status_updater =
+ new JobStatusUpdater(printer_info_.printer_name, job_details_.job_id_,
+ local_job_id_, auth_token_, this);
+ job_status_updater_list_.push_back(job_status_updater);
+ MessageLoop::current()->PostTask(
+ FROM_HERE, NewRunnableMethod(job_status_updater.get(),
+ &JobStatusUpdater::UpdateStatus));
+ bool succeeded = false;
+ CloudPrintHelpers::ParseResponseJSON(data, &succeeded, NULL);
+ if (succeeded) {
+ // Since we just printed successfully, we want to look for more jobs.
+ server_job_available_ = true;
+ }
+ MessageLoop::current()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &PrinterJobHandler::Stop));
+ return true;
+}
+
+bool PrinterJobHandler::HandleFailureStatusUpdateResponse(
+ const URLFetcher* source, const GURL& url, const URLRequestStatus& status,
+ int response_code, const ResponseCookies& cookies,
+ const std::string& data) {
+ // If there was a network error or a non-200 response (which, for our purposes
+ // is the same as a network error), we want to retry.
+ if (!status.is_success() || (response_code != 200)) {
+ return false;
+ }
+ MessageLoop::current()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &PrinterJobHandler::Stop));
+ return true;
+}
+
+void PrinterJobHandler::MakeServerRequest(const GURL& url,
+ ResponseHandler response_handler) {
+ if (!shutting_down_) {
+ request_.reset(new URLFetcher(url, URLFetcher::GET, this));
+ server_error_count_ = 0;
+ CloudPrintHelpers::PrepCloudPrintRequest(request_.get(), auth_token_);
+ // Set up the next response handler
+ next_response_handler_ = response_handler;
+ request_->Start();
+ }
+}
+
+bool PrinterJobHandler::HavePendingTasks() {
+ return server_job_available_ || printer_update_pending_ ||
+ printer_delete_pending_;
+}
+
+
+void PrinterJobHandler::DoPrint(const JobDetails& job_details,
+ const std::string& printer_name,
+ PrinterJobHandler* job_handler,
+ MessageLoop* job_message_loop) {
+ DCHECK(job_handler);
+ DCHECK(job_message_loop);
+ cloud_print::PlatformJobId job_id = -1;
+ if (cloud_print::SpoolPrintJob(job_details.print_ticket_,
+ job_details.print_data_file_path_,
+ job_details.print_data_mime_type_,
+ printer_name,
+ job_details.job_title_, &job_id)) {
+ job_message_loop->PostTask(FROM_HERE,
+ NewRunnableMethod(job_handler,
+ &PrinterJobHandler::JobSpooled,
+ job_id));
+ } else {
+ job_message_loop->PostTask(FROM_HERE,
+ NewRunnableMethod(job_handler,
+ &PrinterJobHandler::JobFailed,
+ PRINT_FAILED));
+ }
+}
+
diff --git a/chrome/browser/printing/cloud_print/printer_job_handler.h b/chrome/browser/printing/cloud_print/printer_job_handler.h
new file mode 100644
index 0000000..15e5cd5
--- /dev/null
+++ b/chrome/browser/printing/cloud_print/printer_job_handler.h
@@ -0,0 +1,240 @@
+// 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.
+
+#ifndef CHROME_BROWSER_PRINTING_CLOUD_PRINT_PRINTER_JOB_HANDLER_H_
+#define CHROME_BROWSER_PRINTING_CLOUD_PRINT_PRINTER_JOB_HANDLER_H_
+
+#include <list>
+#include <string>
+
+#include "base/file_path.h"
+#include "base/ref_counted.h"
+#include "base/thread.h"
+#include "chrome/browser/printing/cloud_print/job_status_updater.h"
+#include "chrome/browser/printing/cloud_print/printer_info.h"
+#include "chrome/browser/net/url_fetcher.h"
+#include "net/url_request/url_request_status.h"
+
+// A class that handles cloud print jobs for a particular printer. This class
+// imlements a state machine that transitions from Start to various states. The
+// various states are shown in the below diagram.
+// the status on the server.
+
+// Start --> No pending tasks --> Done
+// |
+// |
+// | Have Pending tasks
+// |
+// |
+// <----Delete Pending -- | ---Update Pending----->
+// | | |
+// | | |
+// | | |
+// Delete Printer from server | Update Printer info on server
+// Shutdown | Go to Stop
+// |
+// | Job Available
+// |
+// |
+// Fetch Next Job Metadata
+// Fetch Print Ticket
+// Fetch Print Data
+// Spool Print Job
+// Create Job StatusUpdater for job
+// Mark job as "in progress" on server
+// (On any unrecoverable error in any of the above steps go to Stop)
+// Go to Stop
+// |
+// |
+// |
+// |
+// |
+// |
+// |
+// Stop
+// (If there are pending tasks go back to Start)
+
+typedef URLFetcher::Delegate URLFetcherDelegate;
+
+class PrinterJobHandler : public base::RefCountedThreadSafe<PrinterJobHandler>,
+ public URLFetcherDelegate,
+ public JobStatusUpdaterDelegate,
+ public cloud_print::PrinterChangeNotifierDelegate {
+ enum PrintJobError {
+ SUCCESS,
+ JOB_DOWNLOAD_FAILED,
+ INVALID_JOB_DATA,
+ PRINT_FAILED,
+ };
+ struct JobDetails {
+ std::string job_id_;
+ std::string job_title_;
+ std::string print_ticket_;
+ FilePath print_data_file_path_;
+ std::string print_data_mime_type_;
+ void Clear() {
+ job_id_.clear();
+ job_title_.clear();
+ print_ticket_.clear();
+ print_data_mime_type_.clear();
+ print_data_file_path_ = FilePath();
+ }
+ };
+
+ public:
+ class Delegate {
+ public:
+ virtual void OnPrinterJobHandlerShutdown(
+ PrinterJobHandler* job_handler, const std::string& printer_id) = 0;
+ };
+
+ // Begin public interface
+ PrinterJobHandler(const cloud_print::PrinterBasicInfo& printer_info,
+ const std::string& printer_id,
+ const std::string& caps_hash,
+ const std::string& auth_token,
+ Delegate* delegate);
+ ~PrinterJobHandler();
+ bool Initialize();
+ // Notifies the JobHandler that a job is available
+ void NotifyJobAvailable();
+ // Shutdown everything (the process is exiting).
+ void Shutdown();
+ // End public interface
+
+ // Begin Delegate implementations
+
+ // 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);
+ // JobStatusUpdater::Delegate implementation
+ virtual bool OnJobCompleted(JobStatusUpdater* updater);
+ // cloud_print::PrinterChangeNotifier::Delegate implementation
+ virtual void OnPrinterAdded();
+ virtual void OnPrinterDeleted();
+ virtual void OnPrinterChanged();
+ virtual void OnJobChanged();
+
+ // End Delegate implementations
+
+ private:
+ // Prototype for a response handler. The return value indicates whether the
+ // request should be retried, false means "retry", true means "do not retry"
+ typedef bool (PrinterJobHandler::*ResponseHandler)(
+ const URLFetcher* source, const GURL& url,
+ const URLRequestStatus& status, int response_code,
+ const ResponseCookies& cookies, const std::string& data);
+ // Begin request handlers for each state in the state machine
+ bool HandlePrinterUpdateResponse(const URLFetcher* source, const GURL& url,
+ const URLRequestStatus& status,
+ int response_code,
+ const ResponseCookies& cookies,
+ const std::string& data);
+ bool HandlePrinterDeleteResponse(const URLFetcher* source, const GURL& url,
+ const URLRequestStatus& status,
+ int response_code,
+ const ResponseCookies& cookies,
+ const std::string& data);
+ bool HandleJobMetadataResponse(const URLFetcher* source, const GURL& url,
+ const URLRequestStatus& status,
+ int response_code,
+ const ResponseCookies& cookies,
+ const std::string& data);
+ bool HandlePrintTicketResponse(const URLFetcher* source,
+ const GURL& url,
+ const URLRequestStatus& status,
+ int response_code,
+ const ResponseCookies& cookies,
+ const std::string& data);
+ bool HandlePrintDataResponse(const URLFetcher* source,
+ const GURL& url,
+ const URLRequestStatus& status,
+ int response_code,
+ const ResponseCookies& cookies,
+ const std::string& data);
+ bool HandleSuccessStatusUpdateResponse(const URLFetcher* source,
+ const GURL& url,
+ const URLRequestStatus& status,
+ int response_code,
+ const ResponseCookies& cookies,
+ const std::string& data);
+ bool HandleFailureStatusUpdateResponse(const URLFetcher* source,
+ const GURL& url,
+ const URLRequestStatus& status,
+ int response_code,
+ const ResponseCookies& cookies,
+ const std::string& data);
+ // End request handlers for each state in the state machine
+
+ // Start the state machine. Based on the flags set this could mean updating
+ // printer information, deleting the printer from the server or looking for
+ // new print jobs
+ void Start();
+
+ // End the state machine. If there are pending tasks, we will post a Start
+ // again.
+ void Stop();
+
+ void StartPrinting();
+ void HandleServerError(const GURL& url);
+ void Reset();
+ void UpdateJobStatus(cloud_print::PrintJobStatus status, PrintJobError error);
+ void MakeServerRequest(const GURL& url, ResponseHandler response_handler);
+ void JobFailed(PrintJobError error);
+ void JobSpooled(cloud_print::PlatformJobId local_job_id);
+ // Returns false if printer info is up to date and no updating is needed.
+ bool UpdatePrinterInfo();
+ bool HavePendingTasks();
+
+ static void DoPrint(const JobDetails& job_details,
+ const std::string& printer_name,
+ PrinterJobHandler* job_handler,
+ MessageLoop* job_message_loop);
+
+
+ scoped_ptr<URLFetcher> request_;
+ cloud_print::PrinterBasicInfo printer_info_;
+ std::string printer_id_;
+ std::string auth_token_;
+ std::string last_caps_hash_;
+ std::string print_data_url_;
+ JobDetails job_details_;
+ Delegate* delegate_;
+ // Once the job has been spooled to the local spooler, this specifies the
+ // job id of the job on the local spooler.
+ cloud_print::PlatformJobId local_job_id_;
+ ResponseHandler next_response_handler_;
+ // The number of consecutive times that connecting to the server failed.
+ int server_error_count_;
+ // The thread on which the actual print operation happens
+ base::Thread print_thread_;
+ // There may be pending tasks in the message queue when Shutdown is called.
+ // We set this flag so as to do nothing in those tasks.
+ bool shutting_down_;
+
+ // Flags that specify various pending server updates
+ bool server_job_available_;
+ bool printer_update_pending_;
+ bool printer_delete_pending_;
+
+ // Some task in the state machine is in progress.
+ bool task_in_progress_;
+ cloud_print::PrinterChangeNotifier printer_change_notifier_;
+ typedef std::list< scoped_refptr<JobStatusUpdater> > JobStatusUpdaterList;
+ JobStatusUpdaterList job_status_updater_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrinterJobHandler);
+};
+
+// This typedef is to workaround the issue with certain versions of
+// Visual Studio where it gets confused between multiple Delegate
+// classes and gives a C2500 error. (I saw this error on the try bots -
+// the workaround was not needed for my machine).
+typedef PrinterJobHandler::Delegate PrinterJobHandlerDelegate;
+
+#endif // CHROME_BROWSER_PRINTING_CLOUD_PRINT_PRINTER_JOB_HANDLER_H_
+
diff --git a/chrome/browser/profile.cc b/chrome/browser/profile.cc
index 8227d23..92b2ff8 100644
--- a/chrome/browser/profile.cc
+++ b/chrome/browser/profile.cc
@@ -43,6 +43,7 @@
#include "chrome/browser/password_manager/password_store_default.h"
#include "chrome/browser/password_manager/password_store_linux.h"
#include "chrome/browser/privacy_blacklist/blacklist.h"
+#include "chrome/browser/printing/cloud_print/cloud_print_proxy_service.h"
#include "chrome/browser/profile_manager.h"
#include "chrome/browser/renderer_host/render_process_host.h"
#include "chrome/browser/search_engines/template_url_fetcher.h"
@@ -496,6 +497,10 @@ class OffTheRecordProfileImpl : public Profile,
return NULL;
}
+ virtual CloudPrintProxyService* GetCloudPrintProxyService() {
+ return NULL;
+ }
+
virtual bool IsSameProfile(Profile* profile) {
return (profile == this) || (profile == profile_);
}
@@ -1448,6 +1453,12 @@ ProfileSyncService* ProfileImpl::GetProfileSyncService() {
return sync_service_.get();
}
+CloudPrintProxyService* ProfileImpl::GetCloudPrintProxyService() {
+ if (!cloud_print_proxy_service_.get())
+ InitCloudPrintProxyService();
+ return cloud_print_proxy_service_.get();
+}
+
void ProfileImpl::InitSyncService() {
profile_sync_factory_.reset(
new ProfileSyncFactoryImpl(this,
@@ -1456,3 +1467,9 @@ void ProfileImpl::InitSyncService() {
profile_sync_factory_->CreateProfileSyncService());
sync_service_->Initialize();
}
+
+void ProfileImpl::InitCloudPrintProxyService() {
+ cloud_print_proxy_service_.reset(new CloudPrintProxyService(this));
+ cloud_print_proxy_service_->Initialize();
+}
+
diff --git a/chrome/browser/profile.h b/chrome/browser/profile.h
index feee692..751d2e2 100644
--- a/chrome/browser/profile.h
+++ b/chrome/browser/profile.h
@@ -75,6 +75,7 @@ class VisitedLinkEventListener;
class WebDataService;
class WebKitContext;
class WebResourceService;
+class CloudPrintProxyService;
typedef intptr_t ProfileId;
@@ -351,6 +352,9 @@ class Profile {
// Returns the ProfileSyncService, creating if not yet created.
virtual ProfileSyncService* GetProfileSyncService() = 0;
+ // Returns the CloudPrintProxyService, creating if not yet created.
+ virtual CloudPrintProxyService* GetCloudPrintProxyService() = 0;
+
// Return whether 2 profiles are the same. 2 profiles are the same if they
// represent the same profile. This can happen if there is pointer equality
// or if one profile is the off the record version of another profile (or vice
@@ -516,6 +520,8 @@ class ProfileImpl : public Profile,
virtual NTPResourceCache* GetNTPResourceCache();
virtual ProfileSyncService* GetProfileSyncService();
void InitSyncService();
+ virtual CloudPrintProxyService* GetCloudPrintProxyService();
+ void InitCloudPrintProxyService();
// NotificationObserver implementation.
virtual void Observe(NotificationType type,
@@ -571,6 +577,7 @@ class ProfileImpl : public Profile,
scoped_ptr<ProfileSyncFactory> profile_sync_factory_;
scoped_ptr<ProfileSyncService> sync_service_;
+ scoped_ptr<CloudPrintProxyService> cloud_print_proxy_service_;
scoped_refptr<ChromeURLRequestContextGetter> request_context_;
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 55a856a..f759a62 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1735,6 +1735,22 @@
'browser/printing/print_view_manager.h',
'browser/printing/printer_query.cc',
'browser/printing/printer_query.h',
+ 'browser/printing/cloud_print/cloud_print_consts.cc',
+ 'browser/printing/cloud_print/cloud_print_consts.h',
+ 'browser/printing/cloud_print/cloud_print_helpers.cc',
+ 'browser/printing/cloud_print/cloud_print_helpers.h',
+ 'browser/printing/cloud_print/cloud_print_proxy_backend.cc',
+ 'browser/printing/cloud_print/cloud_print_proxy_backend.h',
+ 'browser/printing/cloud_print/cloud_print_proxy_service.cc',
+ 'browser/printing/cloud_print/cloud_print_proxy_service.h',
+ 'browser/printing/cloud_print/job_status_updater.cc',
+ 'browser/printing/cloud_print/job_status_updater.h',
+ 'browser/printing/cloud_print/printer_info_linux.cc',
+ 'browser/printing/cloud_print/printer_info_mac.cc',
+ 'browser/printing/cloud_print/printer_info_win.cc',
+ 'browser/printing/cloud_print/printer_info.h',
+ 'browser/printing/cloud_print/printer_job_handler.cc',
+ 'browser/printing/cloud_print/printer_job_handler.h',
'browser/privacy_blacklist/blacklist.h',
'browser/privacy_blacklist/blacklist.cc',
'browser/privacy_blacklist/blacklist_interceptor.h',
diff --git a/chrome/chrome_dll.gypi b/chrome/chrome_dll.gypi
index ac5845f..79f291f 100644
--- a/chrome/chrome_dll.gypi
+++ b/chrome/chrome_dll.gypi
@@ -35,6 +35,7 @@
'urlmon.dll',
'imm32.dll',
'iphlpapi.dll',
+ 'prntvpt.dll',
],
# Set /SUBSYSTEM:WINDOWS for chrome.dll (for consistency).
'SubSystem': '2',
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 4f0e23c..2670eec 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -752,4 +752,10 @@ const wchar_t kGeolocationAccessToken[] = L"geolocation.access_token";
// LoginDatabase.
const wchar_t kLoginDatabaseMigrated[] = L"login_database.migrated";
+// The unique id for this instance of the cloud print proxy.
+const wchar_t kCloudPrintProxyId[] = L"cloud_print.proxy_id";
+// The human-readable name of the Cloud Print proxy. User-supplied.
+const wchar_t kCloudPrintProxyName[] = L"cloud_print.proxy_name";
+// The GAIA auth token (we may need to move it outside of prefs)
+const wchar_t kCloudPrintAuthToken[] = L"cloud_print_auth_token";
} // namespace prefs
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index ebb6658..1201f94 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -277,6 +277,10 @@ extern const wchar_t kGeolocationContentSettings[];
extern const wchar_t kLoginDatabaseMigrated[];
+extern const wchar_t kCloudPrintProxyId[];
+extern const wchar_t kCloudPrintProxyName[];
+extern const wchar_t kCloudPrintAuthToken[];
+
} // namespace prefs
#endif // CHROME_COMMON_PREF_NAMES_H_
diff --git a/chrome/test/testing_profile.h b/chrome/test/testing_profile.h
index 814d8b9..b4e97d5 100644
--- a/chrome/test/testing_profile.h
+++ b/chrome/test/testing_profile.h
@@ -255,6 +255,7 @@ class TestingProfile : public Profile {
// Creates and initializes a profile sync service if the tests require one.
virtual ProfileSyncService* GetProfileSyncService();
+ virtual CloudPrintProxyService* GetCloudPrintProxyService() { return NULL; }
protected:
// The path of the profile; the various database and other files are relative