Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / local_discovery / privet_http_impl.cc
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/local_discovery/privet_http_impl.h"
6
7 #include <algorithm>
8 #include <vector>
9
10 #include "base/bind.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/rand_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/browser/local_discovery/privet_constants.h"
17 #include "components/cloud_devices/common/printer_description.h"
18 #include "net/base/url_util.h"
19 #include "printing/pwg_raster_settings.h"
20 #include "printing/units.h"
21 #include "ui/gfx/text_elider.h"
22 #include "url/gurl.h"
23
24 #if defined(ENABLE_FULL_PRINTING)
25 #include "chrome/browser/local_discovery/pwg_raster_converter.h"
26 #endif  // ENABLE_FULL_PRINTING
27
28 using namespace cloud_devices::printer;
29
30 namespace cloud_print {
31 extern const char kContentTypeJSON[];
32 }
33
34 namespace local_discovery {
35
36 namespace {
37 const char kUrlPlaceHolder[] = "http://host/";
38 const char kPrivetRegisterActionArgName[] = "action";
39 const char kPrivetRegisterUserArgName[] = "user";
40
41 const int kPrivetCancelationTimeoutSeconds = 3;
42
43 #if defined(ENABLE_FULL_PRINTING)
44 const char kPrivetURLKeyUserName[] = "user_name";
45 const char kPrivetURLKeyClientName[] = "client_name";
46 const char kPrivetURLKeyJobname[] = "job_name";
47 const char kPrivetURLKeyOffline[] = "offline";
48 const char kPrivetURLValueOffline[] = "1";
49 const char kPrivetURLValueClientName[] = "Chrome";
50
51 const char kPrivetContentTypePDF[] = "application/pdf";
52 const char kPrivetContentTypePWGRaster[] = "image/pwg-raster";
53 const char kPrivetContentTypeAny[] = "*/*";
54
55 const char kPrivetKeyJobID[] = "job_id";
56
57 const int kPrivetLocalPrintMaxRetries = 2;
58 const int kPrivetLocalPrintDefaultTimeout = 5;
59
60 const size_t kPrivetLocalPrintMaxJobNameLength = 64;
61 #endif  // ENABLE_FULL_PRINTING
62
63 GURL CreatePrivetURL(const std::string& path) {
64   GURL url(kUrlPlaceHolder);
65   GURL::Replacements replacements;
66   replacements.SetPathStr(path);
67   return url.ReplaceComponents(replacements);
68 }
69
70 GURL CreatePrivetRegisterURL(const std::string& action,
71                              const std::string& user) {
72   GURL url = CreatePrivetURL(kPrivetRegisterPath);
73   url = net::AppendQueryParameter(url, kPrivetRegisterActionArgName, action);
74   return net::AppendQueryParameter(url, kPrivetRegisterUserArgName, user);
75 }
76
77 GURL CreatePrivetParamURL(const std::string& path,
78                           const std::string& query_params) {
79   GURL url(kUrlPlaceHolder);
80   GURL::Replacements replacements;
81   replacements.SetPathStr(path);
82   if (!query_params.empty()) {
83     replacements.SetQueryStr(query_params);
84   }
85   return url.ReplaceComponents(replacements);
86 }
87
88 }  // namespace
89
90 PrivetInfoOperationImpl::PrivetInfoOperationImpl(
91     PrivetHTTPClient* privet_client,
92     const PrivetJSONOperation::ResultCallback& callback)
93     : privet_client_(privet_client), callback_(callback) {
94 }
95
96 PrivetInfoOperationImpl::~PrivetInfoOperationImpl() {
97 }
98
99 void PrivetInfoOperationImpl::Start() {
100   url_fetcher_ = privet_client_->CreateURLFetcher(
101       CreatePrivetURL(kPrivetInfoPath), net::URLFetcher::GET, this);
102
103   url_fetcher_->DoNotRetryOnTransientError();
104   url_fetcher_->SendEmptyPrivetToken();
105
106   url_fetcher_->Start();
107 }
108
109 PrivetHTTPClient* PrivetInfoOperationImpl::GetHTTPClient() {
110   return privet_client_;
111 }
112
113 void PrivetInfoOperationImpl::OnError(PrivetURLFetcher* fetcher,
114                                       PrivetURLFetcher::ErrorType error) {
115   callback_.Run(NULL);
116 }
117
118 void PrivetInfoOperationImpl::OnParsedJson(PrivetURLFetcher* fetcher,
119                                            const base::DictionaryValue& value,
120                                            bool has_error) {
121   callback_.Run(&value);
122 }
123
124 PrivetRegisterOperationImpl::PrivetRegisterOperationImpl(
125     PrivetHTTPClient* privet_client,
126     const std::string& user,
127     PrivetRegisterOperation::Delegate* delegate)
128     : user_(user),
129       delegate_(delegate),
130       privet_client_(privet_client),
131       ongoing_(false) {
132 }
133
134 PrivetRegisterOperationImpl::~PrivetRegisterOperationImpl() {
135 }
136
137 void PrivetRegisterOperationImpl::Start() {
138   ongoing_ = true;
139   next_response_handler_ =
140       base::Bind(&PrivetRegisterOperationImpl::StartResponse,
141                  base::Unretained(this));
142   SendRequest(kPrivetActionStart);
143 }
144
145 void PrivetRegisterOperationImpl::Cancel() {
146   url_fetcher_.reset();
147
148   if (ongoing_) {
149     // Owned by the message loop.
150     Cancelation* cancelation = new Cancelation(privet_client_, user_);
151
152     base::MessageLoop::current()->PostDelayedTask(
153         FROM_HERE,
154         base::Bind(&PrivetRegisterOperationImpl::Cancelation::Cleanup,
155                    base::Owned(cancelation)),
156         base::TimeDelta::FromSeconds(kPrivetCancelationTimeoutSeconds));
157
158     ongoing_ = false;
159   }
160 }
161
162 void PrivetRegisterOperationImpl::CompleteRegistration() {
163   next_response_handler_ =
164       base::Bind(&PrivetRegisterOperationImpl::CompleteResponse,
165                  base::Unretained(this));
166   SendRequest(kPrivetActionComplete);
167 }
168
169 PrivetHTTPClient* PrivetRegisterOperationImpl::GetHTTPClient() {
170   return privet_client_;
171 }
172
173 void PrivetRegisterOperationImpl::OnError(PrivetURLFetcher* fetcher,
174                                           PrivetURLFetcher::ErrorType error) {
175   ongoing_ = false;
176   int visible_http_code = -1;
177   FailureReason reason = FAILURE_NETWORK;
178
179   if (error == PrivetURLFetcher::RESPONSE_CODE_ERROR) {
180     visible_http_code = fetcher->response_code();
181     reason = FAILURE_HTTP_ERROR;
182   } else if (error == PrivetURLFetcher::JSON_PARSE_ERROR) {
183     reason = FAILURE_MALFORMED_RESPONSE;
184   } else if (error == PrivetURLFetcher::TOKEN_ERROR) {
185     reason = FAILURE_TOKEN;
186   } else if (error == PrivetURLFetcher::RETRY_ERROR) {
187     reason = FAILURE_RETRY;
188   }
189
190   delegate_->OnPrivetRegisterError(this,
191                                    current_action_,
192                                    reason,
193                                    visible_http_code,
194                                    NULL);
195 }
196
197 void PrivetRegisterOperationImpl::OnParsedJson(
198     PrivetURLFetcher* fetcher,
199     const base::DictionaryValue& value,
200     bool has_error) {
201   if (has_error) {
202     std::string error;
203     value.GetString(kPrivetKeyError, &error);
204
205     ongoing_ = false;
206     delegate_->OnPrivetRegisterError(this,
207                                      current_action_,
208                                      FAILURE_JSON_ERROR,
209                                      fetcher->response_code(),
210                                      &value);
211     return;
212   }
213
214   // TODO(noamsml): Match the user&action with the user&action in the object,
215   // and fail if different.
216
217   next_response_handler_.Run(value);
218 }
219
220 void PrivetRegisterOperationImpl::OnNeedPrivetToken(
221     PrivetURLFetcher* fetcher,
222     const PrivetURLFetcher::TokenCallback& callback) {
223   privet_client_->RefreshPrivetToken(callback);
224 }
225
226 void PrivetRegisterOperationImpl::SendRequest(const std::string& action) {
227   current_action_ = action;
228   url_fetcher_ = privet_client_->CreateURLFetcher(
229       CreatePrivetRegisterURL(action, user_), net::URLFetcher::POST, this);
230   url_fetcher_->Start();
231 }
232
233 void PrivetRegisterOperationImpl::StartResponse(
234     const base::DictionaryValue& value) {
235   next_response_handler_ =
236       base::Bind(&PrivetRegisterOperationImpl::GetClaimTokenResponse,
237                  base::Unretained(this));
238
239   SendRequest(kPrivetActionGetClaimToken);
240 }
241
242 void PrivetRegisterOperationImpl::GetClaimTokenResponse(
243     const base::DictionaryValue& value) {
244   std::string claimUrl;
245   std::string claimToken;
246   bool got_url = value.GetString(kPrivetKeyClaimURL, &claimUrl);
247   bool got_token = value.GetString(kPrivetKeyClaimToken, &claimToken);
248   if (got_url || got_token) {
249     delegate_->OnPrivetRegisterClaimToken(this, claimToken, GURL(claimUrl));
250   } else {
251     delegate_->OnPrivetRegisterError(this,
252                                      current_action_,
253                                      FAILURE_MALFORMED_RESPONSE,
254                                      -1,
255                                      NULL);
256   }
257 }
258
259 void PrivetRegisterOperationImpl::CompleteResponse(
260     const base::DictionaryValue& value) {
261   std::string id;
262   value.GetString(kPrivetKeyDeviceID, &id);
263   ongoing_ = false;
264   expected_id_ = id;
265   StartInfoOperation();
266 }
267
268 void PrivetRegisterOperationImpl::OnPrivetInfoDone(
269     const base::DictionaryValue* value) {
270   // TODO(noamsml): Simplify error case and depracate HTTP error value in
271   // OnPrivetRegisterError.
272   if (!value) {
273     delegate_->OnPrivetRegisterError(this,
274                                      kPrivetActionNameInfo,
275                                      FAILURE_NETWORK,
276                                      -1,
277                                      NULL);
278     return;
279   }
280
281   if (!value->HasKey(kPrivetInfoKeyID)) {
282     if (value->HasKey(kPrivetKeyError)) {
283       delegate_->OnPrivetRegisterError(this,
284                                        kPrivetActionNameInfo,
285                                         FAILURE_JSON_ERROR,
286                                        -1,
287                                        value);
288     } else {
289       delegate_->OnPrivetRegisterError(this,
290                                        kPrivetActionNameInfo,
291                                        FAILURE_MALFORMED_RESPONSE,
292                                        -1,
293                                        NULL);
294     }
295     return;
296   }
297
298   std::string id;
299
300   if (!value->GetString(kPrivetInfoKeyID, &id) ||
301       id != expected_id_) {
302     delegate_->OnPrivetRegisterError(this,
303                                      kPrivetActionNameInfo,
304                                      FAILURE_MALFORMED_RESPONSE,
305                                      -1,
306                                      NULL);
307   } else {
308     delegate_->OnPrivetRegisterDone(this, id);
309   }
310 }
311
312 void PrivetRegisterOperationImpl::StartInfoOperation() {
313   info_operation_ = privet_client_->CreateInfoOperation(
314       base::Bind(&PrivetRegisterOperationImpl::OnPrivetInfoDone,
315                  base::Unretained(this)));
316   info_operation_->Start();
317 }
318
319 PrivetRegisterOperationImpl::Cancelation::Cancelation(
320     PrivetHTTPClient* privet_client,
321     const std::string& user) {
322   url_fetcher_ =
323       privet_client->CreateURLFetcher(
324           CreatePrivetRegisterURL(kPrivetActionCancel, user),
325           net::URLFetcher::POST, this);
326   url_fetcher_->DoNotRetryOnTransientError();
327   url_fetcher_->Start();
328 }
329
330 PrivetRegisterOperationImpl::Cancelation::~Cancelation() {
331 }
332
333 void PrivetRegisterOperationImpl::Cancelation::OnError(
334     PrivetURLFetcher* fetcher,
335     PrivetURLFetcher::ErrorType error) {
336 }
337
338 void PrivetRegisterOperationImpl::Cancelation::OnParsedJson(
339     PrivetURLFetcher* fetcher,
340     const base::DictionaryValue& value,
341     bool has_error) {
342 }
343
344 void PrivetRegisterOperationImpl::Cancelation::Cleanup() {
345   // Nothing needs to be done, as base::Owned will delete this object,
346   // this callback is just here to pass ownership of the Cancelation to
347   // the message loop.
348 }
349
350 PrivetJSONOperationImpl::PrivetJSONOperationImpl(
351     PrivetHTTPClient* privet_client,
352     const std::string& path,
353     const std::string& query_params,
354     const PrivetJSONOperation::ResultCallback& callback)
355     : privet_client_(privet_client),
356       path_(path),
357       query_params_(query_params),
358       callback_(callback) {
359 }
360
361 PrivetJSONOperationImpl::~PrivetJSONOperationImpl() {
362 }
363
364 void PrivetJSONOperationImpl::Start() {
365   url_fetcher_ = privet_client_->CreateURLFetcher(
366       CreatePrivetParamURL(path_, query_params_), net::URLFetcher::GET, this);
367   url_fetcher_->DoNotRetryOnTransientError();
368   url_fetcher_->Start();
369 }
370
371 PrivetHTTPClient* PrivetJSONOperationImpl::GetHTTPClient() {
372   return privet_client_;
373 }
374
375 void PrivetJSONOperationImpl::OnError(
376     PrivetURLFetcher* fetcher,
377     PrivetURLFetcher::ErrorType error) {
378   callback_.Run(NULL);
379 }
380
381 void PrivetJSONOperationImpl::OnParsedJson(PrivetURLFetcher* fetcher,
382                                            const base::DictionaryValue& value,
383                                            bool has_error) {
384   callback_.Run(&value);
385 }
386
387 void PrivetJSONOperationImpl::OnNeedPrivetToken(
388     PrivetURLFetcher* fetcher,
389     const PrivetURLFetcher::TokenCallback& callback) {
390   privet_client_->RefreshPrivetToken(callback);
391 }
392
393 PrivetDataReadOperationImpl::PrivetDataReadOperationImpl(
394     PrivetHTTPClient* privet_client,
395     const std::string& path,
396     const std::string& query_params,
397     const PrivetDataReadOperation::ResultCallback& callback)
398     : privet_client_(privet_client),
399       path_(path),
400       query_params_(query_params),
401       callback_(callback),
402       has_range_(false),
403       save_to_file_(false) {
404 }
405
406 PrivetDataReadOperationImpl::~PrivetDataReadOperationImpl() {
407 }
408
409
410 void PrivetDataReadOperationImpl::Start() {
411   url_fetcher_ = privet_client_->CreateURLFetcher(
412       CreatePrivetParamURL(path_, query_params_), net::URLFetcher::GET, this);
413   url_fetcher_->DoNotRetryOnTransientError();
414
415   if (has_range_) {
416     url_fetcher_->SetByteRange(range_start_, range_end_);
417   }
418
419   if (save_to_file_) {
420     url_fetcher_->SaveResponseToFile();
421   }
422
423   url_fetcher_->Start();
424 }
425
426 void PrivetDataReadOperationImpl::SetDataRange(int range_start, int range_end) {
427   has_range_ = true;
428   range_start_ = range_start;
429   range_end_ = range_end;
430 }
431
432 void PrivetDataReadOperationImpl::SaveDataToFile() {
433   save_to_file_ = false;
434 }
435
436 PrivetHTTPClient* PrivetDataReadOperationImpl::GetHTTPClient() {
437   return privet_client_;
438 }
439
440 void PrivetDataReadOperationImpl::OnError(
441     PrivetURLFetcher* fetcher,
442     PrivetURLFetcher::ErrorType error) {
443   callback_.Run(RESPONSE_TYPE_ERROR, std::string(), base::FilePath());
444 }
445
446 void PrivetDataReadOperationImpl::OnParsedJson(
447     PrivetURLFetcher* fetcher,
448     const base::DictionaryValue& value,
449     bool has_error) {
450   NOTREACHED();
451 }
452
453 void PrivetDataReadOperationImpl::OnNeedPrivetToken(
454     PrivetURLFetcher* fetcher,
455     const PrivetURLFetcher::TokenCallback& callback) {
456   privet_client_->RefreshPrivetToken(callback);
457 }
458
459 bool PrivetDataReadOperationImpl::OnRawData(PrivetURLFetcher* fetcher,
460                                             bool is_file,
461                                             const std::string& data_str,
462                                             const base::FilePath& file_path) {
463   ResponseType type = (is_file) ? RESPONSE_TYPE_FILE : RESPONSE_TYPE_STRING;
464   callback_.Run(type, data_str, file_path);
465   return true;
466 }
467
468 #if defined(ENABLE_FULL_PRINTING)
469 PrivetLocalPrintOperationImpl::PrivetLocalPrintOperationImpl(
470     PrivetHTTPClient* privet_client,
471     PrivetLocalPrintOperation::Delegate* delegate)
472     : privet_client_(privet_client),
473       delegate_(delegate),
474       use_pdf_(false),
475       has_extended_workflow_(false),
476       started_(false),
477       offline_(false),
478       dpi_(printing::kDefaultPdfDpi),
479       invalid_job_retries_(0),
480       weak_factory_(this) {
481 }
482
483 PrivetLocalPrintOperationImpl::~PrivetLocalPrintOperationImpl() {
484 }
485
486 void PrivetLocalPrintOperationImpl::Start() {
487   DCHECK(!started_);
488
489   // We need to get the /info response so we can know which APIs are available.
490   // TODO(noamsml): Use cached info when available.
491   info_operation_ = privet_client_->CreateInfoOperation(
492       base::Bind(&PrivetLocalPrintOperationImpl::OnPrivetInfoDone,
493                  base::Unretained(this)));
494   info_operation_->Start();
495
496   started_ = true;
497 }
498
499 void PrivetLocalPrintOperationImpl::OnPrivetInfoDone(
500     const base::DictionaryValue* value) {
501   if (value && !value->HasKey(kPrivetKeyError)) {
502     has_extended_workflow_ = false;
503     bool has_printing = false;
504
505     const base::ListValue* api_list;
506     if (value->GetList(kPrivetInfoKeyAPIList, &api_list)) {
507       for (size_t i = 0; i < api_list->GetSize(); i++) {
508         std::string api;
509         api_list->GetString(i, &api);
510         if (api == kPrivetSubmitdocPath) {
511           has_printing = true;
512         } else if (api == kPrivetCreatejobPath) {
513           has_extended_workflow_ = true;
514         }
515       }
516     }
517
518     if (!has_printing) {
519       delegate_->OnPrivetPrintingError(this, -1);
520       return;
521     }
522
523     StartInitialRequest();
524   } else {
525     delegate_->OnPrivetPrintingError(this, -1);
526   }
527 }
528
529 void PrivetLocalPrintOperationImpl::StartInitialRequest() {
530   use_pdf_ = false;
531   ContentTypesCapability content_types;
532   if (content_types.LoadFrom(capabilities_)) {
533     use_pdf_ = content_types.Contains(kPrivetContentTypePDF) ||
534                content_types.Contains(kPrivetContentTypeAny);
535   }
536
537   if (use_pdf_) {
538     StartPrinting();
539   } else {
540     DpiCapability dpis;
541     if (dpis.LoadFrom(capabilities_)) {
542       dpi_ = std::max(dpis.GetDefault().horizontal, dpis.GetDefault().vertical);
543     }
544     StartConvertToPWG();
545   }
546 }
547
548 void PrivetLocalPrintOperationImpl::DoCreatejob() {
549   current_response_ = base::Bind(
550       &PrivetLocalPrintOperationImpl::OnCreatejobResponse,
551       base::Unretained(this));
552
553   url_fetcher_= privet_client_->CreateURLFetcher(
554       CreatePrivetURL(kPrivetCreatejobPath), net::URLFetcher::POST, this);
555   url_fetcher_->SetUploadData(cloud_print::kContentTypeJSON,
556                               ticket_.ToString());
557
558   url_fetcher_->Start();
559 }
560
561 void PrivetLocalPrintOperationImpl::DoSubmitdoc() {
562   current_response_ = base::Bind(
563       &PrivetLocalPrintOperationImpl::OnSubmitdocResponse,
564       base::Unretained(this));
565
566   GURL url = CreatePrivetURL(kPrivetSubmitdocPath);
567
568   url = net::AppendQueryParameter(url,
569                                   kPrivetURLKeyClientName,
570                                   kPrivetURLValueClientName);
571
572   if (!user_.empty()) {
573     url = net::AppendQueryParameter(url,
574                                     kPrivetURLKeyUserName,
575                                     user_);
576   }
577
578   base::string16 shortened_jobname;
579
580   gfx::ElideString(base::UTF8ToUTF16(jobname_),
581                    kPrivetLocalPrintMaxJobNameLength,
582                    &shortened_jobname);
583
584   if (!jobname_.empty()) {
585     url = net::AppendQueryParameter(
586         url, kPrivetURLKeyJobname, base::UTF16ToUTF8(shortened_jobname));
587   }
588
589   if (!jobid_.empty()) {
590     url = net::AppendQueryParameter(url,
591                                     kPrivetKeyJobID,
592                                     jobid_);
593   }
594
595   if (offline_) {
596     url = net::AppendQueryParameter(url,
597                                     kPrivetURLKeyOffline,
598                                     kPrivetURLValueOffline);
599   }
600
601   url_fetcher_= privet_client_->CreateURLFetcher(
602       url, net::URLFetcher::POST, this);
603
604   if (!use_pdf_) {
605     url_fetcher_->SetUploadFilePath(kPrivetContentTypePWGRaster,
606                                     pwg_file_path_);
607   } else {
608     // TODO(noamsml): Move to file-based upload data?
609     std::string data_str((const char*)data_->front(), data_->size());
610     url_fetcher_->SetUploadData(kPrivetContentTypePDF, data_str);
611   }
612
613   url_fetcher_->Start();
614 }
615
616 void PrivetLocalPrintOperationImpl::StartPrinting() {
617   if (has_extended_workflow_ && jobid_.empty()) {
618     DoCreatejob();
619   } else {
620     DoSubmitdoc();
621   }
622 }
623
624 void PrivetLocalPrintOperationImpl::FillPwgRasterSettings(
625     printing::PwgRasterSettings* transform_settings) {
626   PwgRasterConfigCapability raster_capability;
627   // If the raster capability fails to load, raster_capability will contain
628   // the default value.
629   raster_capability.LoadFrom(capabilities_);
630
631   DuplexTicketItem duplex_item;
632   DuplexType duplex_value = NO_DUPLEX;
633
634   DocumentSheetBack document_sheet_back =
635       raster_capability.value().document_sheet_back;
636
637   if (duplex_item.LoadFrom(ticket_)) {
638     duplex_value = duplex_item.value();
639   }
640
641   transform_settings->odd_page_transform = printing::TRANSFORM_NORMAL;
642   switch (duplex_value) {
643     case NO_DUPLEX:
644       transform_settings->odd_page_transform = printing::TRANSFORM_NORMAL;
645       break;
646     case LONG_EDGE:
647       if (document_sheet_back == ROTATED) {
648         transform_settings->odd_page_transform = printing::TRANSFORM_ROTATE_180;
649       } else if (document_sheet_back == FLIPPED) {
650         transform_settings->odd_page_transform =
651             printing::TRANSFORM_FLIP_VERTICAL;
652       }
653       break;
654     case SHORT_EDGE:
655       if (document_sheet_back == MANUAL_TUMBLE) {
656         transform_settings->odd_page_transform = printing::TRANSFORM_ROTATE_180;
657       } else if (document_sheet_back == FLIPPED) {
658         transform_settings->odd_page_transform =
659             printing::TRANSFORM_FLIP_HORIZONTAL;
660       }
661   }
662
663   transform_settings->rotate_all_pages =
664       raster_capability.value().rotate_all_pages;
665
666   transform_settings->reverse_page_order =
667       raster_capability.value().reverse_order_streaming;
668 }
669
670 void PrivetLocalPrintOperationImpl::StartConvertToPWG() {
671   printing::PwgRasterSettings transform_settings;
672
673   FillPwgRasterSettings(&transform_settings);
674
675   if (!pwg_raster_converter_)
676     pwg_raster_converter_ = PWGRasterConverter::CreateDefault();
677
678   double scale = dpi_;
679   scale /= printing::kPointsPerInch;
680   // Make vertical rectangle to optimize streaming to printer. Fix orientation
681   // by autorotate.
682   gfx::Rect area(std::min(page_size_.width(), page_size_.height()) * scale,
683                  std::max(page_size_.width(), page_size_.height()) * scale);
684   pwg_raster_converter_->Start(
685       data_.get(),
686       printing::PdfRenderSettings(area, dpi_, true),
687       transform_settings,
688       base::Bind(&PrivetLocalPrintOperationImpl::OnPWGRasterConverted,
689                  base::Unretained(this)));
690 }
691
692 void PrivetLocalPrintOperationImpl::OnSubmitdocResponse(
693     bool has_error,
694     const base::DictionaryValue* value) {
695   std::string error;
696   // This error is only relevant in the case of extended workflow:
697   // If the print job ID is invalid, retry createjob and submitdoc,
698   // rather than simply retrying the current request.
699   if (has_error && value->GetString(kPrivetKeyError, &error)) {
700     if (has_extended_workflow_ &&
701         error == kPrivetErrorInvalidPrintJob &&
702         invalid_job_retries_ < kPrivetLocalPrintMaxRetries) {
703       invalid_job_retries_++;
704
705       int timeout = kPrivetLocalPrintDefaultTimeout;
706       value->GetInteger(kPrivetKeyTimeout, &timeout);
707
708       double random_scaling_factor =
709           1 + base::RandDouble() * kPrivetMaximumTimeRandomAddition;
710
711       timeout = static_cast<int>(timeout * random_scaling_factor);
712
713       timeout = std::max(timeout, kPrivetMinimumTimeout);
714
715       base::MessageLoop::current()->PostDelayedTask(
716           FROM_HERE, base::Bind(&PrivetLocalPrintOperationImpl::DoCreatejob,
717                                 weak_factory_.GetWeakPtr()),
718           base::TimeDelta::FromSeconds(timeout));
719     } else if (use_pdf_ && error == kPrivetErrorInvalidDocumentType) {
720       use_pdf_ = false;
721       StartConvertToPWG();
722     } else {
723       delegate_->OnPrivetPrintingError(this, 200);
724     }
725
726     return;
727   }
728
729   // If we've gotten this far, there are no errors, so we've effectively
730   // succeeded.
731   delegate_->OnPrivetPrintingDone(this);
732 }
733
734 void PrivetLocalPrintOperationImpl::OnCreatejobResponse(
735     bool has_error,
736     const base::DictionaryValue* value) {
737   if (has_error) {
738     delegate_->OnPrivetPrintingError(this, 200);
739     return;
740   }
741
742   // Try to get job ID from value. If not, jobid_ will be empty and we will use
743   // simple printing.
744   value->GetString(kPrivetKeyJobID, &jobid_);
745
746   DoSubmitdoc();
747 }
748
749 void PrivetLocalPrintOperationImpl::OnPWGRasterConverted(
750     bool success,
751     const base::FilePath& pwg_file_path) {
752   if (!success) {
753     delegate_->OnPrivetPrintingError(this, -1);
754     return;
755   }
756
757   DCHECK(!pwg_file_path.empty());
758
759   pwg_file_path_ = pwg_file_path;
760   StartPrinting();
761 }
762
763 PrivetHTTPClient* PrivetLocalPrintOperationImpl::GetHTTPClient() {
764   return privet_client_;
765 }
766
767 void PrivetLocalPrintOperationImpl::OnError(
768     PrivetURLFetcher* fetcher,
769     PrivetURLFetcher::ErrorType error) {
770   delegate_->OnPrivetPrintingError(this, -1);
771 }
772
773 void PrivetLocalPrintOperationImpl::OnParsedJson(
774     PrivetURLFetcher* fetcher,
775     const base::DictionaryValue& value,
776     bool has_error) {
777   DCHECK(!current_response_.is_null());
778   current_response_.Run(has_error, &value);
779 }
780
781 void PrivetLocalPrintOperationImpl::OnNeedPrivetToken(
782     PrivetURLFetcher* fetcher,
783     const PrivetURLFetcher::TokenCallback& callback) {
784   privet_client_->RefreshPrivetToken(callback);
785 }
786
787 void PrivetLocalPrintOperationImpl::SetData(
788     const scoped_refptr<base::RefCountedBytes>& data) {
789   DCHECK(!started_);
790   data_ = data;
791 }
792
793 void PrivetLocalPrintOperationImpl::SetTicket(const std::string& ticket) {
794   DCHECK(!started_);
795   ticket_.InitFromString(ticket);
796 }
797
798 void PrivetLocalPrintOperationImpl::SetCapabilities(
799     const std::string& capabilities) {
800   DCHECK(!started_);
801   capabilities_.InitFromString(capabilities);
802 }
803
804 void PrivetLocalPrintOperationImpl::SetUsername(const std::string& user) {
805   DCHECK(!started_);
806   user_= user;
807 }
808
809 void PrivetLocalPrintOperationImpl::SetJobname(const std::string& jobname) {
810   DCHECK(!started_);
811   jobname_ = jobname;
812 }
813
814 void PrivetLocalPrintOperationImpl::SetOffline(bool offline) {
815   DCHECK(!started_);
816   offline_ = offline;
817 }
818
819 void PrivetLocalPrintOperationImpl::SetPageSize(const gfx::Size& page_size) {
820   DCHECK(!started_);
821   page_size_ = page_size;
822 }
823
824 void PrivetLocalPrintOperationImpl::SetPWGRasterConverterForTesting(
825     scoped_ptr<PWGRasterConverter> pwg_raster_converter) {
826   pwg_raster_converter_ = pwg_raster_converter.Pass();
827 }
828 #endif  // ENABLE_FULL_PRINTING
829
830 PrivetHTTPClientImpl::PrivetHTTPClientImpl(
831     const std::string& name,
832     const net::HostPortPair& host_port,
833     net::URLRequestContextGetter* request_context)
834     : name_(name), request_context_(request_context), host_port_(host_port) {}
835
836 PrivetHTTPClientImpl::~PrivetHTTPClientImpl() {
837 }
838
839 const std::string& PrivetHTTPClientImpl::GetName() {
840   return name_;
841 }
842
843 scoped_ptr<PrivetJSONOperation> PrivetHTTPClientImpl::CreateInfoOperation(
844     const PrivetJSONOperation::ResultCallback& callback) {
845   return scoped_ptr<PrivetJSONOperation>(
846       new PrivetInfoOperationImpl(this, callback));
847 }
848
849 scoped_ptr<PrivetURLFetcher> PrivetHTTPClientImpl::CreateURLFetcher(
850     const GURL& url,
851     net::URLFetcher::RequestType request_type,
852     PrivetURLFetcher::Delegate* delegate) {
853   GURL::Replacements replacements;
854   replacements.SetHostStr(host_port_.host());
855   std::string port(base::IntToString(host_port_.port()));  // Keep string alive.
856   replacements.SetPortStr(port);
857   return scoped_ptr<PrivetURLFetcher>(
858       new PrivetURLFetcher(url.ReplaceComponents(replacements),
859                            request_type,
860                            request_context_.get(),
861                            delegate));
862 }
863
864 void PrivetHTTPClientImpl::RefreshPrivetToken(
865     const PrivetURLFetcher::TokenCallback& callback) {
866   token_callbacks_.push_back(callback);
867
868   if (!info_operation_) {
869     info_operation_ = CreateInfoOperation(
870         base::Bind(&PrivetHTTPClientImpl::OnPrivetInfoDone,
871                    base::Unretained(this)));
872     info_operation_->Start();
873   }
874 }
875
876 void PrivetHTTPClientImpl::OnPrivetInfoDone(
877     const base::DictionaryValue* value) {
878   info_operation_.reset();
879   std::string token;
880
881   // If this does not succeed, token will be empty, and an empty string
882   // is our sentinel value, since empty X-Privet-Tokens are not allowed.
883   if (value) {
884     value->GetString(kPrivetInfoKeyToken, &token);
885   }
886
887   TokenCallbackVector token_callbacks;
888   token_callbacks_.swap(token_callbacks);
889
890   for (TokenCallbackVector::iterator i = token_callbacks.begin();
891        i != token_callbacks.end(); i++) {
892     i->Run(token);
893   }
894 }
895
896 PrivetV1HTTPClientImpl::PrivetV1HTTPClientImpl(
897     scoped_ptr<PrivetHTTPClient> info_client)
898     : info_client_(info_client.Pass()) {
899 }
900
901 PrivetV1HTTPClientImpl::~PrivetV1HTTPClientImpl() {
902 }
903
904 const std::string& PrivetV1HTTPClientImpl::GetName() {
905   return info_client()->GetName();
906 }
907
908 scoped_ptr<PrivetJSONOperation> PrivetV1HTTPClientImpl::CreateInfoOperation(
909     const PrivetJSONOperation::ResultCallback& callback) {
910   return info_client()->CreateInfoOperation(callback);
911 }
912
913 scoped_ptr<PrivetRegisterOperation>
914 PrivetV1HTTPClientImpl::CreateRegisterOperation(
915     const std::string& user,
916     PrivetRegisterOperation::Delegate* delegate) {
917   return scoped_ptr<PrivetRegisterOperation>(
918       new PrivetRegisterOperationImpl(info_client(), user, delegate));
919 }
920
921 scoped_ptr<PrivetJSONOperation>
922 PrivetV1HTTPClientImpl::CreateCapabilitiesOperation(
923     const PrivetJSONOperation::ResultCallback& callback) {
924   return scoped_ptr<PrivetJSONOperation>(new PrivetJSONOperationImpl(
925       info_client(), kPrivetCapabilitiesPath, "", callback));
926 }
927
928 scoped_ptr<PrivetLocalPrintOperation>
929 PrivetV1HTTPClientImpl::CreateLocalPrintOperation(
930     PrivetLocalPrintOperation::Delegate* delegate) {
931 #if defined(ENABLE_FULL_PRINTING)
932   return scoped_ptr<PrivetLocalPrintOperation>(
933       new PrivetLocalPrintOperationImpl(info_client(), delegate));
934 #else
935   return scoped_ptr<PrivetLocalPrintOperation>();
936 #endif  // ENABLE_FULL_PRINTING
937 }
938
939 }  // namespace local_discovery