Upstream version 7.35.139.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / local_discovery / privet_http_impl.cc
index c236255..9582f47 100644 (file)
@@ -4,13 +4,25 @@
 
 #include "chrome/browser/local_discovery/privet_http_impl.h"
 
+#include <algorithm>
+#include <vector>
+
 #include "base/bind.h"
 #include "base/message_loop/message_loop.h"
+#include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/local_discovery/privet_constants.h"
+#include "components/cloud_devices/printer_description.h"
 #include "net/base/url_util.h"
+#include "printing/pwg_raster_settings.h"
+#include "printing/units.h"
+#include "ui/gfx/text_elider.h"
 #include "url/gurl.h"
 
+using namespace cloud_devices::printer;
+
 namespace local_discovery {
 
 namespace {
@@ -18,27 +30,32 @@ const char kUrlPlaceHolder[] = "http://host/";
 const char kPrivetRegisterActionArgName[] = "action";
 const char kPrivetRegisterUserArgName[] = "user";
 
-const char kPrivetInfoPath[] = "/privet/info";
-const char kPrivetRegisterPath[] = "/privet/register";
-const char kPrivetCapabilitiesPath[] = "/privet/capabilities";
-const char kPrivetSubmitdocPath[] = "/privet/printer/submitdoc";
-const char kPrivetCreatejobPath[] = "/privet/printer/createjob";
-
-const char kPrivetURLKeyUser[] = "user";
-const char kPrivetURLKeyJobname[] = "jobname";
+const char kPrivetURLKeyUserName[] = "user_name";
+const char kPrivetURLKeyClientName[] = "client_name";
+const char kPrivetURLKeyJobname[] = "job_name";
 const char kPrivetURLKeyOffline[] = "offline";
 const char kPrivetURLValueOffline[] = "1";
+const char kPrivetURLValueClientName[] = "Chrome";
 
 const char kPrivetContentTypePDF[] = "application/pdf";
 const char kPrivetContentTypePWGRaster[] = "image/pwg-raster";
+const char kPrivetContentTypeAny[] = "*/*";
+const char kPrivetContentTypeCJT[] = "application/json";
 
-const char kPrivetCDDKeySupportedContentTypes[] =
-    "printer.supported_content_type";
+const char kPrivetStorageListPath[] = "/privet/storage/list";
+const char kPrivetStorageContentPath[] = "/privet/storage/content";
+const char kPrivetStorageParamPathFormat[] = "path=%s";
 
-const char kPrivetCDDKeyContentType[] = "content_type";
+const char kPrivetKeyJobID[] = "job_id";
 
 const int kPrivetCancelationTimeoutSeconds = 3;
 
+const int kPrivetLocalPrintMaxRetries = 2;
+
+const int kPrivetLocalPrintDefaultTimeout = 5;
+
+const size_t kPrivetLocalPrintMaxJobNameLength = 64;
+
 GURL CreatePrivetURL(const std::string& path) {
   GURL url(kUrlPlaceHolder);
   GURL::Replacements replacements;
@@ -53,12 +70,23 @@ GURL CreatePrivetRegisterURL(const std::string& action,
   return net::AppendQueryParameter(url, kPrivetRegisterUserArgName, user);
 }
 
+GURL CreatePrivetParamURL(const std::string& path,
+                          const std::string& query_params) {
+  GURL url(kUrlPlaceHolder);
+  GURL::Replacements replacements;
+  replacements.SetPathStr(path);
+  if (!query_params.empty()) {
+    replacements.SetQueryStr(query_params);
+  }
+  return url.ReplaceComponents(replacements);
+}
+
 }  // namespace
 
 PrivetInfoOperationImpl::PrivetInfoOperationImpl(
     PrivetHTTPClientImpl* privet_client,
-    PrivetInfoOperation::Delegate* delegate)
-    : privet_client_(privet_client), delegate_(delegate) {
+    const PrivetJSONOperation::ResultCallback& callback)
+    : privet_client_(privet_client), callback_(callback) {
 }
 
 PrivetInfoOperationImpl::~PrivetInfoOperationImpl() {
@@ -80,19 +108,13 @@ PrivetHTTPClient* PrivetInfoOperationImpl::GetHTTPClient() {
 
 void PrivetInfoOperationImpl::OnError(PrivetURLFetcher* fetcher,
                                       PrivetURLFetcher::ErrorType error) {
-  if (error == PrivetURLFetcher::RESPONSE_CODE_ERROR) {
-    delegate_->OnPrivetInfoDone(this, fetcher->response_code(), NULL);
-  } else {
-    delegate_->OnPrivetInfoDone(this, kPrivetHTTPCodeInternalFailure, NULL);
-  }
+  callback_.Run(NULL);
 }
 
 void PrivetInfoOperationImpl::OnParsedJson(PrivetURLFetcher* fetcher,
                                            const base::DictionaryValue* value,
                                            bool has_error) {
-  if (!has_error)
-    privet_client_->CacheInfo(value);
-  delegate_->OnPrivetInfoDone(this, fetcher->response_code(), value);
+  callback_.Run(value);
 }
 
 PrivetRegisterOperationImpl::PrivetRegisterOperationImpl(
@@ -238,10 +260,9 @@ void PrivetRegisterOperationImpl::CompleteResponse(
 }
 
 void PrivetRegisterOperationImpl::OnPrivetInfoDone(
-    PrivetInfoOperation* operation,
-    int http_code,
     const base::DictionaryValue* value) {
-   // TODO (noamsml): Simplify error case.
+  // TODO(noamsml): Simplify error case and depracate HTTP error value in
+  // OnPrivetRegisterError.
   if (!value) {
     delegate_->OnPrivetRegisterError(this,
                                      kPrivetActionNameInfo,
@@ -256,7 +277,7 @@ void PrivetRegisterOperationImpl::OnPrivetInfoDone(
       delegate_->OnPrivetRegisterError(this,
                                        kPrivetActionNameInfo,
                                         FAILURE_JSON_ERROR,
-                                       http_code,
+                                       -1,
                                        value);
     } else {
       delegate_->OnPrivetRegisterError(this,
@@ -283,7 +304,9 @@ void PrivetRegisterOperationImpl::OnPrivetInfoDone(
 }
 
 void PrivetRegisterOperationImpl::StartInfoOperation() {
-  info_operation_ = privet_client_->CreateInfoOperation(this);
+  info_operation_ = privet_client_->CreateInfoOperation(
+      base::Bind(&PrivetRegisterOperationImpl::OnPrivetInfoDone,
+                 base::Unretained(this)));
   info_operation_->Start();
 }
 
@@ -318,51 +341,131 @@ void PrivetRegisterOperationImpl::Cancelation::Cleanup() {
   // the message loop.
 }
 
-PrivetCapabilitiesOperationImpl::PrivetCapabilitiesOperationImpl(
+PrivetJSONOperationImpl::PrivetJSONOperationImpl(
+    PrivetHTTPClientImpl* privet_client,
+    const std::string& path,
+    const std::string& query_params,
+    const PrivetJSONOperation::ResultCallback& callback)
+    : privet_client_(privet_client), path_(path), query_params_(query_params),
+      callback_(callback) {
+}
+
+PrivetJSONOperationImpl::~PrivetJSONOperationImpl() {
+}
+
+void PrivetJSONOperationImpl::Start() {
+  url_fetcher_ = privet_client_->CreateURLFetcher(
+      CreatePrivetParamURL(path_, query_params_), net::URLFetcher::GET, this);
+  url_fetcher_->DoNotRetryOnTransientError();
+  url_fetcher_->Start();
+}
+
+PrivetHTTPClient* PrivetJSONOperationImpl::GetHTTPClient() {
+  return privet_client_;
+}
+
+void PrivetJSONOperationImpl::OnError(
+    PrivetURLFetcher* fetcher,
+    PrivetURLFetcher::ErrorType error) {
+  callback_.Run(NULL);
+}
+
+void PrivetJSONOperationImpl::OnParsedJson(
+    PrivetURLFetcher* fetcher,
+    const base::DictionaryValue* value,
+    bool has_error) {
+  callback_.Run(value);
+}
+
+void PrivetJSONOperationImpl::OnNeedPrivetToken(
+    PrivetURLFetcher* fetcher,
+    const PrivetURLFetcher::TokenCallback& callback) {
+  privet_client_->RefreshPrivetToken(callback);
+}
+
+PrivetDataReadOperationImpl::PrivetDataReadOperationImpl(
     PrivetHTTPClientImpl* privet_client,
-    PrivetCapabilitiesOperation::Delegate* delegate)
-    : privet_client_(privet_client), delegate_(delegate) {
+    const std::string& path,
+    const std::string& query_params,
+    const PrivetDataReadOperation::ResultCallback& callback)
+    : privet_client_(privet_client), path_(path), query_params_(query_params),
+      callback_(callback), has_range_(false), save_to_file_(false) {
 }
 
-PrivetCapabilitiesOperationImpl::~PrivetCapabilitiesOperationImpl() {
+PrivetDataReadOperationImpl::~PrivetDataReadOperationImpl() {
 }
 
-void PrivetCapabilitiesOperationImpl::Start() {
+
+void PrivetDataReadOperationImpl::Start() {
   url_fetcher_ = privet_client_->CreateURLFetcher(
-      CreatePrivetURL(kPrivetCapabilitiesPath), net::URLFetcher::GET, this);
+      CreatePrivetParamURL(path_, query_params_), net::URLFetcher::GET, this);
   url_fetcher_->DoNotRetryOnTransientError();
+
+  if (has_range_) {
+    url_fetcher_->SetByteRange(range_start_, range_end_);
+  }
+
+  if (save_to_file_) {
+    url_fetcher_->SaveResponseToFile();
+  }
+
   url_fetcher_->Start();
 }
 
-PrivetHTTPClient* PrivetCapabilitiesOperationImpl::GetHTTPClient() {
+void PrivetDataReadOperationImpl::SetDataRange(int range_start, int range_end) {
+  has_range_ = true;
+  range_start_ = range_start;
+  range_end_ = range_end;
+}
+
+void PrivetDataReadOperationImpl::SaveDataToFile() {
+  save_to_file_ = false;
+}
+
+PrivetHTTPClient* PrivetDataReadOperationImpl::GetHTTPClient() {
   return privet_client_;
 }
 
-void PrivetCapabilitiesOperationImpl::OnError(
+void PrivetDataReadOperationImpl::OnError(
     PrivetURLFetcher* fetcher,
     PrivetURLFetcher::ErrorType error) {
-  delegate_->OnPrivetCapabilities(this, -1, NULL);
+  callback_.Run(RESPONSE_TYPE_ERROR, std::string(), base::FilePath());
 }
 
-void PrivetCapabilitiesOperationImpl::OnParsedJson(
+void PrivetDataReadOperationImpl::OnParsedJson(
     PrivetURLFetcher* fetcher,
     const base::DictionaryValue* value,
     bool has_error) {
-  delegate_->OnPrivetCapabilities(this, fetcher->response_code(), value);
+  NOTREACHED();
 }
 
-void PrivetCapabilitiesOperationImpl::OnNeedPrivetToken(
+void PrivetDataReadOperationImpl::OnNeedPrivetToken(
     PrivetURLFetcher* fetcher,
     const PrivetURLFetcher::TokenCallback& callback) {
   privet_client_->RefreshPrivetToken(callback);
 }
 
+bool PrivetDataReadOperationImpl::OnRawData(PrivetURLFetcher* fetcher,
+                                            bool is_file,
+                                            const std::string& data_str,
+                                            const base::FilePath& file_path) {
+  ResponseType type = (is_file) ? RESPONSE_TYPE_FILE : RESPONSE_TYPE_STRING;
+  callback_.Run(type, data_str, file_path);
+  return true;
+}
+
 PrivetLocalPrintOperationImpl::PrivetLocalPrintOperationImpl(
     PrivetHTTPClientImpl* privet_client,
     PrivetLocalPrintOperation::Delegate* delegate)
-    : privet_client_(privet_client), delegate_(delegate),
-      use_pdf_(false), has_capabilities_(false), has_extended_workflow_(false),
-      started_(false), offline_(false) {
+    : privet_client_(privet_client),
+      delegate_(delegate),
+      use_pdf_(false),
+      has_extended_workflow_(false),
+      started_(false),
+      offline_(false),
+      dpi_(printing::kDefaultPdfDpi),
+      invalid_job_retries_(0),
+      weak_factory_(this) {
 }
 
 PrivetLocalPrintOperationImpl::~PrivetLocalPrintOperationImpl() {
@@ -371,29 +474,19 @@ PrivetLocalPrintOperationImpl::~PrivetLocalPrintOperationImpl() {
 void PrivetLocalPrintOperationImpl::Start() {
   DCHECK(!started_);
 
-  current_request_ =
-      base::Bind(&PrivetLocalPrintOperationImpl::StartInitialRequest,
-                 base::Unretained(this));
-
   // We need to get the /info response so we can know which APIs are available.
   // TODO(noamsml): Use cached info when available.
-  info_operation_ = privet_client_->CreateInfoOperation(this);
+  info_operation_ = privet_client_->CreateInfoOperation(
+      base::Bind(&PrivetLocalPrintOperationImpl::OnPrivetInfoDone,
+                 base::Unretained(this)));
   info_operation_->Start();
 
   started_ = true;
 }
 
-void PrivetLocalPrintOperationImpl::StartCurrentRequest() {
-  DCHECK(!current_request_.is_null());
-  current_request_.Run();
-}
-
 void PrivetLocalPrintOperationImpl::OnPrivetInfoDone(
-    PrivetInfoOperation* operation,
-    int http_code,
     const base::DictionaryValue* value) {
   if (value && !value->HasKey(kPrivetKeyError)) {
-    has_capabilities_ = false;
     has_extended_workflow_ = false;
     bool has_printing = false;
 
@@ -402,9 +495,7 @@ void PrivetLocalPrintOperationImpl::OnPrivetInfoDone(
       for (size_t i = 0; i < api_list->GetSize(); i++) {
         std::string api;
         api_list->GetString(i, &api);
-        if (api == kPrivetCapabilitiesPath) {
-          has_capabilities_ = true;
-        } else if (api == kPrivetSubmitdocPath) {
+        if (api == kPrivetSubmitdocPath) {
           has_printing = true;
         } else if (api == kPrivetCreatejobPath) {
           has_extended_workflow_ = true;
@@ -417,31 +508,39 @@ void PrivetLocalPrintOperationImpl::OnPrivetInfoDone(
       return;
     }
 
-    StartCurrentRequest();
+    StartInitialRequest();
   } else {
-    delegate_->OnPrivetPrintingError(this, http_code);
+    delegate_->OnPrivetPrintingError(this, -1);
   }
 }
 
 void PrivetLocalPrintOperationImpl::StartInitialRequest() {
-  if (has_capabilities_) {
-    GetCapabilities();
+  use_pdf_ = false;
+  ContentTypesCapability content_types;
+  if (content_types.LoadFrom(capabilities_)) {
+    use_pdf_ = content_types.Contains(kPrivetContentTypePDF) ||
+               content_types.Contains(kPrivetContentTypeAny);
+  }
+
+  if (use_pdf_) {
+    StartPrinting();
   } else {
-    // Since we have no capabiltties, the only reasonable format we can
-    // request is PWG Raster.
-    use_pdf_ = false;
-    delegate_->OnPrivetPrintingRequestPWGRaster(this);
+    DpiCapability dpis;
+    if (dpis.LoadFrom(capabilities_)) {
+      dpi_ = std::max(dpis.GetDefault().horizontal, dpis.GetDefault().vertical);
+    }
+    StartConvertToPWG();
   }
 }
 
-void PrivetLocalPrintOperationImpl::GetCapabilities() {
+void PrivetLocalPrintOperationImpl::DoCreatejob() {
   current_response_ = base::Bind(
-      &PrivetLocalPrintOperationImpl::OnCapabilities,
+      &PrivetLocalPrintOperationImpl::OnCreatejobResponse,
       base::Unretained(this));
 
   url_fetcher_= privet_client_->CreateURLFetcher(
-      CreatePrivetURL(kPrivetCapabilitiesPath), net::URLFetcher::GET, this);
-  url_fetcher_->DoNotRetryOnTransientError();
+      CreatePrivetURL(kPrivetCreatejobPath), net::URLFetcher::POST, this);
+  url_fetcher_->SetUploadData(kPrivetContentTypeCJT, ticket_.ToString());
 
   url_fetcher_->Start();
 }
@@ -453,16 +552,31 @@ void PrivetLocalPrintOperationImpl::DoSubmitdoc() {
 
   GURL url = CreatePrivetURL(kPrivetSubmitdocPath);
 
+  url = net::AppendQueryParameter(url,
+                                  kPrivetURLKeyClientName,
+                                  kPrivetURLValueClientName);
+
   if (!user_.empty()) {
     url = net::AppendQueryParameter(url,
-                                    kPrivetURLKeyUser,
+                                    kPrivetURLKeyUserName,
                                     user_);
   }
 
+  base::string16 shortened_jobname;
+
+  gfx::ElideString(base::UTF8ToUTF16(jobname_),
+                   kPrivetLocalPrintMaxJobNameLength,
+                   &shortened_jobname);
+
   if (!jobname_.empty()) {
+    url = net::AppendQueryParameter(
+        url, kPrivetURLKeyJobname, base::UTF16ToUTF8(shortened_jobname));
+  }
+
+  if (!jobid_.empty()) {
     url = net::AppendQueryParameter(url,
-                                    kPrivetURLKeyJobname,
-                                    jobname_);
+                                    kPrivetKeyJobID,
+                                    jobid_);
   }
 
   if (offline_) {
@@ -474,49 +588,165 @@ void PrivetLocalPrintOperationImpl::DoSubmitdoc() {
   url_fetcher_= privet_client_->CreateURLFetcher(
       url, net::URLFetcher::POST, this);
 
-  DCHECK(!data_.empty());
-  url_fetcher_->SetUploadData(
-      use_pdf_ ? kPrivetContentTypePDF : kPrivetContentTypePWGRaster,
-      data_);
+  if (!use_pdf_) {
+    url_fetcher_->SetUploadFilePath(kPrivetContentTypePWGRaster,
+                                    pwg_file_path_);
+  } else {
+    // TODO(noamsml): Move to file-based upload data?
+    std::string data_str((const char*)data_->front(), data_->size());
+    url_fetcher_->SetUploadData(kPrivetContentTypePDF, data_str);
+  }
 
   url_fetcher_->Start();
 }
 
-void PrivetLocalPrintOperationImpl::OnCapabilities(
-    const base::DictionaryValue* value) {
-  const base::ListValue* supported_content_types;
-  use_pdf_ = false;
+void PrivetLocalPrintOperationImpl::StartPrinting() {
+  if (has_extended_workflow_ && jobid_.empty()) {
+    DoCreatejob();
+  } else {
+    DoSubmitdoc();
+  }
+}
+
+void PrivetLocalPrintOperationImpl::FillPwgRasterSettings(
+    printing::PwgRasterSettings* transform_settings) {
+  PwgRasterConfigCapability raster_capability;
+  // If the raster capability fails to load, raster_capability will contain
+  // the default value.
+  raster_capability.LoadFrom(capabilities_);
+
+  DuplexTicketItem duplex_item;
+  DuplexType duplex_value = NO_DUPLEX;
+
+  DocumentSheetBack document_sheet_back =
+      raster_capability.value().document_sheet_back;
 
-  if (value->GetList(kPrivetCDDKeySupportedContentTypes,
-                     &supported_content_types)) {
-    for (size_t i = 0; i < supported_content_types->GetSize();
-         i++) {
-      const base::DictionaryValue* content_type_value;
-      std::string content_type;
-
-      if (supported_content_types->GetDictionary(i, &content_type_value) &&
-          content_type_value->GetString(kPrivetCDDKeyContentType,
-                                        &content_type) &&
-          content_type == kPrivetContentTypePDF) {
-        use_pdf_ = true;
+  if (duplex_item.LoadFrom(ticket_)) {
+    duplex_value = duplex_item.value();
+  }
+
+  transform_settings->odd_page_transform = printing::TRANSFORM_NORMAL;
+  switch (duplex_value) {
+    case NO_DUPLEX:
+      transform_settings->odd_page_transform = printing::TRANSFORM_NORMAL;
+      break;
+    case LONG_EDGE:
+      if (document_sheet_back == ROTATED) {
+        transform_settings->odd_page_transform = printing::TRANSFORM_ROTATE_180;
+      } else if (document_sheet_back == FLIPPED) {
+        transform_settings->odd_page_transform =
+            printing::TRANSFORM_FLIP_VERTICAL;
+      }
+      break;
+    case SHORT_EDGE:
+      if (document_sheet_back == MANUAL_TUMBLE) {
+        transform_settings->odd_page_transform = printing::TRANSFORM_ROTATE_180;
+      } else if (document_sheet_back == FLIPPED) {
+        transform_settings->odd_page_transform =
+            printing::TRANSFORM_FLIP_HORIZONTAL;
       }
-    }
   }
 
-  if (use_pdf_)
-    delegate_->OnPrivetPrintingRequestPDF(this);
-  else
-    delegate_->OnPrivetPrintingRequestPWGRaster(this);
+  transform_settings->rotate_all_pages =
+      raster_capability.value().rotate_all_pages;
+
+  transform_settings->reverse_page_order =
+      raster_capability.value().reverse_order_streaming;
+}
+
+void PrivetLocalPrintOperationImpl::StartConvertToPWG() {
+  printing::PwgRasterSettings transform_settings;
+
+  FillPwgRasterSettings(&transform_settings);
+
+  if (!pwg_raster_converter_)
+    pwg_raster_converter_ = PWGRasterConverter::CreateDefault();
+
+  double scale = dpi_;
+  scale /= printing::kPointsPerInch;
+  // Make vertical rectangle to optimize streaming to printer. Fix orientation
+  // by autorotate.
+  gfx::Rect area(std::min(page_size_.width(), page_size_.height()) * scale,
+                 std::max(page_size_.width(), page_size_.height()) * scale);
+  pwg_raster_converter_->Start(
+      data_,
+      printing::PdfRenderSettings(area, dpi_, true),
+      transform_settings,
+      base::Bind(&PrivetLocalPrintOperationImpl::OnPWGRasterConverted,
+                 base::Unretained(this)));
 }
 
 void PrivetLocalPrintOperationImpl::OnSubmitdocResponse(
+    bool has_error,
     const base::DictionaryValue* value) {
+  std::string error;
+  // This error is only relevant in the case of extended workflow:
+  // If the print job ID is invalid, retry createjob and submitdoc,
+  // rather than simply retrying the current request.
+  if (has_error && value->GetString(kPrivetKeyError, &error)) {
+    if (has_extended_workflow_ &&
+        error == kPrivetErrorInvalidPrintJob &&
+        invalid_job_retries_ < kPrivetLocalPrintMaxRetries) {
+      invalid_job_retries_++;
+
+      int timeout = kPrivetLocalPrintDefaultTimeout;
+      value->GetInteger(kPrivetKeyTimeout, &timeout);
+
+      double random_scaling_factor =
+          1 + base::RandDouble() * kPrivetMaximumTimeRandomAddition;
+
+      timeout = static_cast<int>(timeout * random_scaling_factor);
+
+      timeout = std::max(timeout, kPrivetMinimumTimeout);
+
+      base::MessageLoop::current()->PostDelayedTask(
+          FROM_HERE, base::Bind(&PrivetLocalPrintOperationImpl::DoCreatejob,
+                                weak_factory_.GetWeakPtr()),
+          base::TimeDelta::FromSeconds(timeout));
+    } else if (use_pdf_ && error == kPrivetErrorInvalidDocumentType) {
+      use_pdf_ = false;
+      StartConvertToPWG();
+    } else {
+      delegate_->OnPrivetPrintingError(this, 200);
+    }
+
+    return;
+  }
+
   // If we've gotten this far, there are no errors, so we've effectively
   // succeeded.
-
   delegate_->OnPrivetPrintingDone(this);
 }
 
+void PrivetLocalPrintOperationImpl::OnCreatejobResponse(
+    bool has_error,
+    const base::DictionaryValue* value) {
+  if (has_error) {
+    delegate_->OnPrivetPrintingError(this, 200);
+    return;
+  }
+
+  // Try to get job ID from value. If not, jobid_ will be empty and we will use
+  // simple printing.
+  value->GetString(kPrivetKeyJobID, &jobid_);
+
+  DoSubmitdoc();
+}
+
+void PrivetLocalPrintOperationImpl::OnPWGRasterConverted(
+    bool success,
+    const base::FilePath& pwg_file_path) {
+  if (!success) {
+    delegate_->OnPrivetPrintingError(this, -1);
+    return;
+  }
+
+  DCHECK(!pwg_file_path.empty());
+
+  pwg_file_path_ = pwg_file_path;
+  StartPrinting();
+}
+
 PrivetHTTPClient* PrivetLocalPrintOperationImpl::GetHTTPClient() {
   return privet_client_;
 }
@@ -531,13 +761,8 @@ void PrivetLocalPrintOperationImpl::OnParsedJson(
     PrivetURLFetcher* fetcher,
     const base::DictionaryValue* value,
     bool has_error) {
-  std::string error;
-  if (has_error) {
-    delegate_->OnPrivetPrintingError(this, -1);
-  } else {
-    DCHECK(!current_response_.is_null());
-    current_response_.Run(value);
-  }
+  DCHECK(!current_response_.is_null());
+  current_response_.Run(has_error, value);
 }
 
 void PrivetLocalPrintOperationImpl::OnNeedPrivetToken(
@@ -546,19 +771,20 @@ void PrivetLocalPrintOperationImpl::OnNeedPrivetToken(
   privet_client_->RefreshPrivetToken(callback);
 }
 
-void PrivetLocalPrintOperationImpl::SendData(const std::string& data) {
-  DCHECK(started_);
+void PrivetLocalPrintOperationImpl::SetData(base::RefCountedBytes* data) {
+  DCHECK(!started_);
   data_ = data;
-  current_request_ = base::Bind(
-      &PrivetLocalPrintOperationImpl::DoSubmitdoc,
-      base::Unretained(this));
-
-  StartCurrentRequest();
 }
 
 void PrivetLocalPrintOperationImpl::SetTicket(const std::string& ticket) {
   DCHECK(!started_);
-  ticket_ = ticket;
+  ticket_.InitFromString(ticket);
+}
+
+void PrivetLocalPrintOperationImpl::SetCapabilities(
+    const std::string& capabilities) {
+  DCHECK(!started_);
+  capabilities_.InitFromString(capabilities);
 }
 
 void PrivetLocalPrintOperationImpl::SetUsername(const std::string& user) {
@@ -576,22 +802,25 @@ void PrivetLocalPrintOperationImpl::SetOffline(bool offline) {
   offline_ = offline;
 }
 
+void PrivetLocalPrintOperationImpl::SetPageSize(const gfx::Size& page_size) {
+  DCHECK(!started_);
+  page_size_ = page_size;
+}
+
+void PrivetLocalPrintOperationImpl::SetPWGRasterConverterForTesting(
+    scoped_ptr<PWGRasterConverter> pwg_raster_converter) {
+  pwg_raster_converter_ = pwg_raster_converter.Pass();
+}
+
 PrivetHTTPClientImpl::PrivetHTTPClientImpl(
     const std::string& name,
     const net::HostPortPair& host_port,
     net::URLRequestContextGetter* request_context)
-    : name_(name),
-      fetcher_factory_(request_context),
-      host_port_(host_port) {
-}
+    : name_(name), request_context_(request_context), host_port_(host_port) {}
 
 PrivetHTTPClientImpl::~PrivetHTTPClientImpl() {
 }
 
-const base::DictionaryValue* PrivetHTTPClientImpl::GetCachedInfo() const {
-  return cached_info_.get();
-}
-
 scoped_ptr<PrivetRegisterOperation>
 PrivetHTTPClientImpl::CreateRegisterOperation(
     const std::string& user,
@@ -600,17 +829,17 @@ PrivetHTTPClientImpl::CreateRegisterOperation(
       new PrivetRegisterOperationImpl(this, user, delegate));
 }
 
-scoped_ptr<PrivetInfoOperation> PrivetHTTPClientImpl::CreateInfoOperation(
-    PrivetInfoOperation::Delegate* delegate) {
-  return scoped_ptr<PrivetInfoOperation>(
-      new PrivetInfoOperationImpl(this, delegate));
+scoped_ptr<PrivetJSONOperation> PrivetHTTPClientImpl::CreateInfoOperation(
+    const PrivetJSONOperation::ResultCallback& callback) {
+  return scoped_ptr<PrivetJSONOperation>(
+      new PrivetInfoOperationImpl(this, callback));
 }
 
-scoped_ptr<PrivetCapabilitiesOperation>
+scoped_ptr<PrivetJSONOperation>
 PrivetHTTPClientImpl::CreateCapabilitiesOperation(
-    PrivetCapabilitiesOperation::Delegate* delegate) {
-  return scoped_ptr<PrivetCapabilitiesOperation>(
-      new PrivetCapabilitiesOperationImpl(this, delegate));
+    const PrivetJSONOperation::ResultCallback& callback) {
+  return scoped_ptr<PrivetJSONOperation>(
+      new PrivetJSONOperationImpl(this, kPrivetCapabilitiesPath, "", callback));
 }
 
 scoped_ptr<PrivetLocalPrintOperation>
@@ -620,6 +849,29 @@ PrivetHTTPClientImpl::CreateLocalPrintOperation(
       new PrivetLocalPrintOperationImpl(this, delegate));
 }
 
+scoped_ptr<PrivetJSONOperation>
+PrivetHTTPClientImpl::CreateStorageListOperation(
+      const std::string& path,
+      const PrivetJSONOperation::ResultCallback& callback) {
+  std::string url_param = base::StringPrintf(kPrivetStorageParamPathFormat,
+                                            path.c_str());
+  return scoped_ptr<PrivetJSONOperation>(
+      new PrivetJSONOperationImpl(this, kPrivetStorageListPath, url_param,
+                                  callback));
+}
+
+
+scoped_ptr<PrivetDataReadOperation>
+PrivetHTTPClientImpl::CreateStorageReadOperation(
+    const std::string& path,
+    const PrivetDataReadOperation::ResultCallback& callback) {
+  std::string url_param = base::StringPrintf(kPrivetStorageParamPathFormat,
+                                             path.c_str());
+  return scoped_ptr<PrivetDataReadOperation>(
+      new PrivetDataReadOperationImpl(this, kPrivetStorageContentPath,
+                                      url_param, callback));
+}
+
 const std::string& PrivetHTTPClientImpl::GetName() {
   return name_;
 }
@@ -631,36 +883,26 @@ scoped_ptr<PrivetURLFetcher> PrivetHTTPClientImpl::CreateURLFetcher(
   replacements.SetHostStr(host_port_.host());
   std::string port(base::IntToString(host_port_.port()));  // Keep string alive.
   replacements.SetPortStr(port);
-  GURL url2 = url.ReplaceComponents(replacements);
-  return fetcher_factory_.CreateURLFetcher(url.ReplaceComponents(replacements),
-                                           request_type, delegate);
-}
-
-void PrivetHTTPClientImpl::CacheInfo(const base::DictionaryValue* cached_info) {
-  cached_info_.reset(cached_info->DeepCopy());
-  std::string token;
-  if (cached_info_->GetString(kPrivetInfoKeyToken, &token)) {
-    fetcher_factory_.set_token(token);
-  }
+  return scoped_ptr<PrivetURLFetcher>(
+      new PrivetURLFetcher(url.ReplaceComponents(replacements),
+                           request_type,
+                           request_context_.get(),
+                           delegate));
 }
 
-bool PrivetHTTPClientImpl::HasToken() const {
-  return fetcher_factory_.get_token() != "";
-};
-
 void PrivetHTTPClientImpl::RefreshPrivetToken(
     const PrivetURLFetcher::TokenCallback& callback) {
   token_callbacks_.push_back(callback);
 
   if (!info_operation_) {
-    info_operation_ = CreateInfoOperation(this);
+    info_operation_ = CreateInfoOperation(
+        base::Bind(&PrivetHTTPClientImpl::OnPrivetInfoDone,
+                   base::Unretained(this)));
     info_operation_->Start();
   }
 }
 
 void PrivetHTTPClientImpl::OnPrivetInfoDone(
-    PrivetInfoOperation* operation,
-    int http_code,
     const base::DictionaryValue* value) {
   info_operation_.reset();
   std::string token;