print_preview_handler.cc revision e5d81f57cb97b3b6b7fccc9c5610d21eb81db09d
1// Copyright (c) 2012 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/ui/webui/print_preview/print_preview_handler.h"
6
7#include <ctype.h>
8
9#include <string>
10
11#include "base/base64.h"
12#include "base/bind.h"
13#include "base/bind_helpers.h"
14#include "base/command_line.h"
15#include "base/i18n/file_util_icu.h"
16#include "base/i18n/number_formatting.h"
17#include "base/json/json_reader.h"
18#include "base/lazy_instance.h"
19#include "base/memory/linked_ptr.h"
20#include "base/memory/ref_counted_memory.h"
21#include "base/metrics/histogram.h"
22#include "base/path_service.h"
23#include "base/prefs/pref_service.h"
24#include "base/strings/utf_string_conversions.h"
25#include "base/threading/thread.h"
26#include "base/threading/thread_restrictions.h"
27#include "base/values.h"
28#include "chrome/browser/browser_process.h"
29#include "chrome/browser/platform_util.h"
30#include "chrome/browser/printing/cloud_print/cloud_print_url.h"
31#include "chrome/browser/printing/print_dialog_cloud.h"
32#include "chrome/browser/printing/print_error_dialog.h"
33#include "chrome/browser/printing/print_job_manager.h"
34#include "chrome/browser/printing/print_preview_dialog_controller.h"
35#include "chrome/browser/printing/print_view_manager.h"
36#include "chrome/browser/printing/printer_manager_dialog.h"
37#include "chrome/browser/profiles/profile.h"
38#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
39#include "chrome/browser/signin/signin_manager_factory.h"
40#include "chrome/browser/ui/browser_finder.h"
41#include "chrome/browser/ui/browser_tabstrip.h"
42#include "chrome/browser/ui/chrome_select_file_policy.h"
43#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
44#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
45#include "chrome/browser/ui/webui/print_preview/sticky_settings.h"
46#include "chrome/common/chrome_paths.h"
47#include "chrome/common/chrome_switches.h"
48#include "chrome/common/cloud_print/cloud_print_constants.h"
49#include "chrome/common/crash_keys.h"
50#include "chrome/common/pref_names.h"
51#include "chrome/common/print_messages.h"
52#include "components/signin/core/browser/profile_oauth2_token_service.h"
53#include "components/signin/core/browser/signin_manager.h"
54#include "components/signin/core/browser/signin_manager_base.h"
55#include "content/public/browser/browser_context.h"
56#include "content/public/browser/browser_thread.h"
57#include "content/public/browser/navigation_controller.h"
58#include "content/public/browser/navigation_entry.h"
59#include "content/public/browser/render_view_host.h"
60#include "content/public/browser/web_contents.h"
61#include "content/public/browser/web_contents_view.h"
62#include "content/public/browser/web_ui.h"
63#include "google_apis/gaia/oauth2_token_service.h"
64#include "printing/backend/print_backend.h"
65#include "printing/backend/print_backend_consts.h"
66#include "printing/metafile.h"
67#include "printing/metafile_impl.h"
68#include "printing/pdf_render_settings.h"
69#include "printing/print_settings.h"
70#include "printing/units.h"
71#include "third_party/icu/source/i18n/unicode/ulocdata.h"
72
73#if defined(OS_CHROMEOS)
74#include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
75#include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h"
76#endif
77
78#if defined(ENABLE_SERVICE_DISCOVERY)
79#include "chrome/browser/local_discovery/privet_constants.h"
80#endif
81
82using content::BrowserThread;
83using content::RenderViewHost;
84using content::WebContents;
85
86namespace {
87
88enum UserActionBuckets {
89  PRINT_TO_PRINTER,
90  PRINT_TO_PDF,
91  CANCEL,
92  FALLBACK_TO_ADVANCED_SETTINGS_DIALOG,
93  PREVIEW_FAILED,
94  PREVIEW_STARTED,
95  INITIATOR_CRASHED,  // UNUSED
96  INITIATOR_CLOSED,
97  PRINT_WITH_CLOUD_PRINT,
98  PRINT_WITH_PRIVET,
99  USERACTION_BUCKET_BOUNDARY
100};
101
102enum PrintSettingsBuckets {
103  LANDSCAPE = 0,
104  PORTRAIT,
105  COLOR,
106  BLACK_AND_WHITE,
107  COLLATE,
108  SIMPLEX,
109  DUPLEX,
110  TOTAL,
111  HEADERS_AND_FOOTERS,
112  CSS_BACKGROUND,
113  SELECTION_ONLY,
114  PRINT_SETTINGS_BUCKET_BOUNDARY
115};
116
117enum UiBucketGroups {
118  DESTINATION_SEARCH,
119  GCP_PROMO,
120  UI_BUCKET_GROUP_BOUNDARY
121};
122
123enum PrintDestinationBuckets {
124  DESTINATION_SHOWN,
125  DESTINATION_CLOSED_CHANGED,
126  DESTINATION_CLOSED_UNCHANGED,
127  SIGNIN_PROMPT,
128  SIGNIN_TRIGGERED,
129  PRIVET_DUPLICATE_SELECTED,
130  CLOUD_DUPLICATE_SELECTED,
131  REGISTER_PROMO_SHOWN,
132  REGISTER_PROMO_SELECTED,
133  PRINT_DESTINATION_BUCKET_BOUNDARY
134};
135
136enum GcpPromoBuckets {
137  PROMO_SHOWN,
138  PROMO_CLOSED,
139  PROMO_CLICKED,
140  GCP_PROMO_BUCKET_BOUNDARY
141};
142
143void ReportUserActionHistogram(enum UserActionBuckets event) {
144  UMA_HISTOGRAM_ENUMERATION("PrintPreview.UserAction", event,
145                            USERACTION_BUCKET_BOUNDARY);
146}
147
148void ReportPrintSettingHistogram(enum PrintSettingsBuckets setting) {
149  UMA_HISTOGRAM_ENUMERATION("PrintPreview.PrintSettings", setting,
150                            PRINT_SETTINGS_BUCKET_BOUNDARY);
151}
152
153void ReportPrintDestinationHistogram(enum PrintDestinationBuckets event) {
154  UMA_HISTOGRAM_ENUMERATION("PrintPreview.DestinationAction", event,
155                            PRINT_DESTINATION_BUCKET_BOUNDARY);
156}
157
158void ReportGcpPromoHistogram(enum GcpPromoBuckets event) {
159  UMA_HISTOGRAM_ENUMERATION("PrintPreview.GcpPromo", event,
160                            GCP_PROMO_BUCKET_BOUNDARY);
161}
162
163// Name of a dictionary field holding cloud print related data;
164const char kAppState[] = "appState";
165// Name of a dictionary field holding the initiator title.
166const char kInitiatorTitle[] = "initiatorTitle";
167// Name of a dictionary field holding the measurement system according to the
168// locale.
169const char kMeasurementSystem[] = "measurementSystem";
170// Name of a dictionary field holding the number format according to the locale.
171const char kNumberFormat[] = "numberFormat";
172// Name of a dictionary field specifying whether to print automatically in
173// kiosk mode. See http://crbug.com/31395.
174const char kPrintAutomaticallyInKioskMode[] = "printAutomaticallyInKioskMode";
175#if defined(OS_WIN)
176const char kHidePrintWithSystemDialogLink[] = "hidePrintWithSystemDialogLink";
177#endif
178// Name of a dictionary field holding the state of selection for document.
179const char kDocumentHasSelection[] = "documentHasSelection";
180
181// Additional printer capability setting keys.
182const char kPrinterId[] = "printerId";
183const char kDisableColorOption[] = "disableColorOption";
184const char kSetDuplexAsDefault[] = "setDuplexAsDefault";
185const char kPrinterDefaultDuplexValue[] = "printerDefaultDuplexValue";
186#if defined(USE_CUPS)
187const char kCUPSsColorModel[] = "cupsColorModel";
188const char kCUPSsBWModel[] = "cupsBWModel";
189#endif
190
191// Get the print job settings dictionary from |args|. The caller takes
192// ownership of the returned DictionaryValue. Returns NULL on failure.
193base::DictionaryValue* GetSettingsDictionary(const base::ListValue* args) {
194  std::string json_str;
195  if (!args->GetString(0, &json_str)) {
196    NOTREACHED() << "Could not read JSON argument";
197    return NULL;
198  }
199  if (json_str.empty()) {
200    NOTREACHED() << "Empty print job settings";
201    return NULL;
202  }
203  scoped_ptr<base::DictionaryValue> settings(
204      static_cast<base::DictionaryValue*>(
205          base::JSONReader::Read(json_str)));
206  if (!settings.get() || !settings->IsType(base::Value::TYPE_DICTIONARY)) {
207    NOTREACHED() << "Print job settings must be a dictionary.";
208    return NULL;
209  }
210
211  if (settings->empty()) {
212    NOTREACHED() << "Print job settings dictionary is empty";
213    return NULL;
214  }
215
216  return settings.release();
217}
218
219// Track the popularity of print settings and report the stats.
220void ReportPrintSettingsStats(const base::DictionaryValue& settings) {
221  ReportPrintSettingHistogram(TOTAL);
222
223  bool landscape = false;
224  if (settings.GetBoolean(printing::kSettingLandscape, &landscape))
225    ReportPrintSettingHistogram(landscape ? LANDSCAPE : PORTRAIT);
226
227  bool collate = false;
228  if (settings.GetBoolean(printing::kSettingCollate, &collate) && collate)
229    ReportPrintSettingHistogram(COLLATE);
230
231  int duplex_mode = 0;
232  if (settings.GetInteger(printing::kSettingDuplexMode, &duplex_mode))
233    ReportPrintSettingHistogram(duplex_mode ? DUPLEX : SIMPLEX);
234
235  int color_mode = 0;
236  if (settings.GetInteger(printing::kSettingColor, &color_mode)) {
237    ReportPrintSettingHistogram(
238        printing::IsColorModelSelected(color_mode) ? COLOR : BLACK_AND_WHITE);
239  }
240
241  bool headers = false;
242  if (settings.GetBoolean(printing::kSettingHeaderFooterEnabled, &headers) &&
243      headers) {
244    ReportPrintSettingHistogram(HEADERS_AND_FOOTERS);
245  }
246
247  bool css_background = false;
248  if (settings.GetBoolean(printing::kSettingShouldPrintBackgrounds,
249                          &css_background) && css_background) {
250    ReportPrintSettingHistogram(CSS_BACKGROUND);
251  }
252
253  bool selection_only = false;
254  if (settings.GetBoolean(printing::kSettingShouldPrintSelectionOnly,
255                          &selection_only) && selection_only) {
256    ReportPrintSettingHistogram(SELECTION_ONLY);
257  }
258}
259
260// Callback that stores a PDF file on disk.
261void PrintToPdfCallback(printing::Metafile* metafile,
262                        const base::FilePath& path) {
263  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
264  metafile->SaveTo(path);
265  // |metafile| must be deleted on the UI thread.
266  BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, metafile);
267}
268
269std::string GetDefaultPrinterOnFileThread(
270    scoped_refptr<printing::PrintBackend> print_backend) {
271  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
272
273  std::string default_printer = print_backend->GetDefaultPrinterName();
274  VLOG(1) << "Default Printer: " << default_printer;
275  return default_printer;
276}
277
278void EnumeratePrintersOnFileThread(
279    scoped_refptr<printing::PrintBackend> print_backend,
280    base::ListValue* printers) {
281  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
282
283  VLOG(1) << "Enumerate printers start";
284  printing::PrinterList printer_list;
285  print_backend->EnumeratePrinters(&printer_list);
286
287  for (printing::PrinterList::iterator it = printer_list.begin();
288       it != printer_list.end(); ++it) {
289    base::DictionaryValue* printer_info = new base::DictionaryValue;
290    printers->Append(printer_info);
291    std::string printer_name;
292    std::string printer_description;
293#if defined(OS_MACOSX)
294    // On Mac, |it->printer_description| specifies the printer name and
295    // |it->printer_name| specifies the device name / printer queue name.
296    printer_name = it->printer_description;
297    if (!it->options[kDriverNameTagName].empty())
298      printer_description = it->options[kDriverNameTagName];
299#else
300    printer_name = it->printer_name;
301    printer_description = it->printer_description;
302#endif
303    printer_info->SetString(printing::kSettingDeviceName, it->printer_name);
304    printer_info->SetString(printing::kSettingPrinterDescription,
305                            printer_description);
306    printer_info->SetString(printing::kSettingPrinterName, printer_name);
307    VLOG(1) << "Found printer " << printer_name
308            << " with device name " << it->printer_name;
309
310    base::DictionaryValue* options = new base::DictionaryValue;
311    printer_info->Set(printing::kSettingPrinterOptions, options);
312    for (std::map<std::string, std::string>::iterator opt = it->options.begin();
313         opt != it->options.end();
314         ++opt) {
315      options->SetString(opt->first, opt->second);
316    }
317
318    VLOG(1) << "Found printer " << printer_name << " with device name "
319            << it->printer_name;
320  }
321  VLOG(1) << "Enumerate printers finished, found " << printers->GetSize()
322          << " printers";
323}
324
325typedef base::Callback<void(const base::DictionaryValue*)>
326    GetPrinterCapabilitiesSuccessCallback;
327typedef base::Callback<void(const std::string&)>
328    GetPrinterCapabilitiesFailureCallback;
329
330void GetPrinterCapabilitiesOnFileThread(
331    scoped_refptr<printing::PrintBackend> print_backend,
332    const std::string& printer_name,
333    const GetPrinterCapabilitiesSuccessCallback& success_cb,
334    const GetPrinterCapabilitiesFailureCallback& failure_cb) {
335  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
336  DCHECK(!printer_name.empty());
337
338  VLOG(1) << "Get printer capabilities start for " << printer_name;
339  crash_keys::ScopedPrinterInfo crash_key(
340      print_backend->GetPrinterDriverInfo(printer_name));
341
342  if (!print_backend->IsValidPrinter(printer_name)) {
343    // TODO(gene): Notify explicitly if printer is not valid, instead of
344    // failed to get capabilities.
345    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
346                            base::Bind(failure_cb, printer_name));
347    return;
348  }
349
350  printing::PrinterSemanticCapsAndDefaults info;
351  if (!print_backend->GetPrinterSemanticCapsAndDefaults(printer_name, &info)) {
352    LOG(WARNING) << "Failed to get capabilities for " << printer_name;
353    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
354                            base::Bind(failure_cb, printer_name));
355    return;
356  }
357
358  scoped_ptr<base::DictionaryValue> settings_info(new base::DictionaryValue);
359  settings_info->SetString(kPrinterId, printer_name);
360  settings_info->SetBoolean(kDisableColorOption, !info.color_changeable);
361  settings_info->SetBoolean(printing::kSettingSetColorAsDefault,
362                            info.color_default);
363#if defined(USE_CUPS)
364  settings_info->SetInteger(kCUPSsColorModel, info.color_model);
365  settings_info->SetInteger(kCUPSsBWModel, info.bw_model);
366#endif
367
368  // TODO(gene): Make new capabilities format for Print Preview
369  // that will suit semantic capabiltities better.
370  // Refactor pld API code below
371  bool default_duplex = info.duplex_capable ?
372      (info.duplex_default != printing::SIMPLEX) : false;
373  int duplex_value = info.duplex_capable ?
374      printing::LONG_EDGE : printing::UNKNOWN_DUPLEX_MODE;
375  settings_info->SetBoolean(kSetDuplexAsDefault, default_duplex);
376  settings_info->SetInteger(kPrinterDefaultDuplexValue, duplex_value);
377
378  BrowserThread::PostTask(
379      BrowserThread::UI, FROM_HERE,
380      base::Bind(success_cb, base::Owned(settings_info.release())));
381}
382
383base::LazyInstance<printing::StickySettings> g_sticky_settings =
384    LAZY_INSTANCE_INITIALIZER;
385
386printing::StickySettings* GetStickySettings() {
387  return g_sticky_settings.Pointer();
388}
389
390}  // namespace
391
392#if defined(USE_CUPS)
393struct PrintPreviewHandler::CUPSPrinterColorModels {
394  std::string printer_name;
395  printing::ColorModel color_model;
396  printing::ColorModel bw_model;
397};
398#endif
399
400class PrintPreviewHandler::AccessTokenService
401    : public OAuth2TokenService::Consumer {
402 public:
403  explicit AccessTokenService(PrintPreviewHandler* handler)
404      : OAuth2TokenService::Consumer("print_preview"),
405        handler_(handler) {
406  }
407
408  void RequestToken(const std::string& type) {
409    if (requests_.find(type) != requests_.end())
410      return;  // Already in progress.
411
412    OAuth2TokenService* service = NULL;
413    std::string account_id;
414    if (type == "profile") {
415      Profile* profile = Profile::FromWebUI(handler_->web_ui());
416      if (profile) {
417        ProfileOAuth2TokenService* token_service =
418            ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
419        SigninManagerBase* signin_manager =
420            SigninManagerFactory::GetInstance()->GetForProfile(profile);
421        account_id = signin_manager->GetAuthenticatedAccountId();
422        service = token_service;
423      }
424    } else if (type == "device") {
425#if defined(OS_CHROMEOS)
426      chromeos::DeviceOAuth2TokenService* token_service =
427          chromeos::DeviceOAuth2TokenServiceFactory::Get();
428      account_id = token_service->GetRobotAccountId();
429      service = token_service;
430#endif
431    }
432
433    if (service) {
434      OAuth2TokenService::ScopeSet oauth_scopes;
435      oauth_scopes.insert(cloud_print::kCloudPrintAuth);
436      scoped_ptr<OAuth2TokenService::Request> request(
437          service->StartRequest(account_id, oauth_scopes, this));
438      requests_[type].reset(request.release());
439    } else {
440      handler_->SendAccessToken(type, std::string());  // Unknown type.
441    }
442  }
443
444  virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
445                                 const std::string& access_token,
446                                 const base::Time& expiration_time) OVERRIDE {
447    OnServiceResponce(request, access_token);
448  }
449
450  virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request,
451                                 const GoogleServiceAuthError& error) OVERRIDE {
452    OnServiceResponce(request, std::string());
453  }
454
455 private:
456  void OnServiceResponce(const OAuth2TokenService::Request* request,
457                         const std::string& access_token) {
458    for (Requests::iterator i = requests_.begin(); i != requests_.end(); ++i) {
459      if (i->second == request) {
460        handler_->SendAccessToken(i->first, access_token);
461        requests_.erase(i);
462        return;
463      }
464    }
465    NOTREACHED();
466  }
467
468  typedef std::map<std::string,
469                   linked_ptr<OAuth2TokenService::Request> > Requests;
470  Requests requests_;
471  PrintPreviewHandler* handler_;
472
473  DISALLOW_COPY_AND_ASSIGN(AccessTokenService);
474};
475
476PrintPreviewHandler::PrintPreviewHandler()
477    : print_backend_(printing::PrintBackend::CreateInstance(NULL)),
478      regenerate_preview_request_count_(0),
479      manage_printers_dialog_request_count_(0),
480      manage_cloud_printers_dialog_request_count_(0),
481      reported_failed_preview_(false),
482      has_logged_printers_count_(false),
483      weak_factory_(this) {
484  ReportUserActionHistogram(PREVIEW_STARTED);
485}
486
487PrintPreviewHandler::~PrintPreviewHandler() {
488  if (select_file_dialog_.get())
489    select_file_dialog_->ListenerDestroyed();
490}
491
492void PrintPreviewHandler::RegisterMessages() {
493  web_ui()->RegisterMessageCallback("getPrinters",
494      base::Bind(&PrintPreviewHandler::HandleGetPrinters,
495                 base::Unretained(this)));
496  web_ui()->RegisterMessageCallback("getPreview",
497      base::Bind(&PrintPreviewHandler::HandleGetPreview,
498                 base::Unretained(this)));
499  web_ui()->RegisterMessageCallback("print",
500      base::Bind(&PrintPreviewHandler::HandlePrint,
501                 base::Unretained(this)));
502  web_ui()->RegisterMessageCallback("getPrinterCapabilities",
503      base::Bind(&PrintPreviewHandler::HandleGetPrinterCapabilities,
504                 base::Unretained(this)));
505  web_ui()->RegisterMessageCallback("showSystemDialog",
506      base::Bind(&PrintPreviewHandler::HandleShowSystemDialog,
507                 base::Unretained(this)));
508  web_ui()->RegisterMessageCallback("signIn",
509      base::Bind(&PrintPreviewHandler::HandleSignin,
510                 base::Unretained(this)));
511  web_ui()->RegisterMessageCallback("getAccessToken",
512      base::Bind(&PrintPreviewHandler::HandleGetAccessToken,
513                 base::Unretained(this)));
514  web_ui()->RegisterMessageCallback("manageCloudPrinters",
515      base::Bind(&PrintPreviewHandler::HandleManageCloudPrint,
516                 base::Unretained(this)));
517  web_ui()->RegisterMessageCallback("manageLocalPrinters",
518      base::Bind(&PrintPreviewHandler::HandleManagePrinters,
519                 base::Unretained(this)));
520  web_ui()->RegisterMessageCallback("closePrintPreviewDialog",
521      base::Bind(&PrintPreviewHandler::HandleClosePreviewDialog,
522                 base::Unretained(this)));
523  web_ui()->RegisterMessageCallback("hidePreview",
524      base::Bind(&PrintPreviewHandler::HandleHidePreview,
525                 base::Unretained(this)));
526  web_ui()->RegisterMessageCallback("cancelPendingPrintRequest",
527      base::Bind(&PrintPreviewHandler::HandleCancelPendingPrintRequest,
528                 base::Unretained(this)));
529  web_ui()->RegisterMessageCallback("saveAppState",
530      base::Bind(&PrintPreviewHandler::HandleSaveAppState,
531                 base::Unretained(this)));
532  web_ui()->RegisterMessageCallback("getInitialSettings",
533      base::Bind(&PrintPreviewHandler::HandleGetInitialSettings,
534                 base::Unretained(this)));
535  web_ui()->RegisterMessageCallback("reportUiEvent",
536      base::Bind(&PrintPreviewHandler::HandleReportUiEvent,
537                 base::Unretained(this)));
538  web_ui()->RegisterMessageCallback("printWithCloudPrintDialog",
539      base::Bind(&PrintPreviewHandler::HandlePrintWithCloudPrintDialog,
540                 base::Unretained(this)));
541  web_ui()->RegisterMessageCallback("forceOpenNewTab",
542      base::Bind(&PrintPreviewHandler::HandleForceOpenNewTab,
543                 base::Unretained(this)));
544  web_ui()->RegisterMessageCallback("getPrivetPrinters",
545      base::Bind(&PrintPreviewHandler::HandleGetPrivetPrinters,
546                 base::Unretained(this)));
547  web_ui()->RegisterMessageCallback("stopGetPrivetPrinters",
548      base::Bind(&PrintPreviewHandler::HandleStopGetPrivetPrinters,
549                 base::Unretained(this)));
550  web_ui()->RegisterMessageCallback("getPrivetPrinterCapabilities",
551      base::Bind(&PrintPreviewHandler::HandleGetPrivetPrinterCapabilities,
552                 base::Unretained(this)));
553}
554
555bool PrintPreviewHandler::PrivetPrintingEnabled() {
556#if defined(ENABLE_SERVICE_DISCOVERY)
557  return !CommandLine::ForCurrentProcess()->HasSwitch(
558    switches::kDisableDeviceDiscovery);
559#else
560  return false;
561#endif
562}
563
564WebContents* PrintPreviewHandler::preview_web_contents() const {
565  return web_ui()->GetWebContents();
566}
567
568void PrintPreviewHandler::HandleGetPrinters(const base::ListValue* /*args*/) {
569  base::ListValue* results = new base::ListValue;
570  BrowserThread::PostTaskAndReply(
571      BrowserThread::FILE, FROM_HERE,
572      base::Bind(&EnumeratePrintersOnFileThread, print_backend_,
573                 base::Unretained(results)),
574      base::Bind(&PrintPreviewHandler::SetupPrinterList,
575                 weak_factory_.GetWeakPtr(),
576                 base::Owned(results)));
577}
578
579void PrintPreviewHandler::HandleGetPrivetPrinters(const base::ListValue* args) {
580#if defined(ENABLE_SERVICE_DISCOVERY)
581  if (PrivetPrintingEnabled()) {
582    Profile* profile = Profile::FromWebUI(web_ui());
583    service_discovery_client_ =
584        local_discovery::ServiceDiscoverySharedClient::GetInstance();
585    printer_lister_.reset(new local_discovery::PrivetLocalPrinterLister(
586        service_discovery_client_.get(),
587        profile->GetRequestContext(),
588        this));
589    printer_lister_->Start();
590  }
591#endif
592
593  if (!PrivetPrintingEnabled()) {
594    web_ui()->CallJavascriptFunction("onPrivetPrinterSearchDone");
595  }
596}
597
598void PrintPreviewHandler::HandleStopGetPrivetPrinters(
599    const base::ListValue* args) {
600#if defined(ENABLE_SERVICE_DISCOVERY)
601  if (PrivetPrintingEnabled()) {
602    printer_lister_->Stop();
603  }
604#endif
605}
606
607void PrintPreviewHandler::HandleGetPrivetPrinterCapabilities(
608    const base::ListValue* args) {
609#if defined(ENABLE_SERVICE_DISCOVERY)
610  std::string name;
611  bool success = args->GetString(0, &name);
612  DCHECK(success);
613
614  CreatePrivetHTTP(
615      name,
616      base::Bind(&PrintPreviewHandler::PrivetCapabilitiesUpdateClient,
617                 base::Unretained(this)));
618#endif
619}
620
621void PrintPreviewHandler::HandleGetPreview(const base::ListValue* args) {
622  DCHECK_EQ(3U, args->GetSize());
623  scoped_ptr<base::DictionaryValue> settings(GetSettingsDictionary(args));
624  if (!settings.get())
625    return;
626  int request_id = -1;
627  if (!settings->GetInteger(printing::kPreviewRequestID, &request_id))
628    return;
629
630  PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(
631      web_ui()->GetController());
632  print_preview_ui->OnPrintPreviewRequest(request_id);
633  // Add an additional key in order to identify |print_preview_ui| later on
634  // when calling PrintPreviewUI::GetCurrentPrintPreviewStatus() on the IO
635  // thread.
636  settings->SetInteger(printing::kPreviewUIID,
637                       print_preview_ui->GetIDForPrintPreviewUI());
638
639  // Increment request count.
640  ++regenerate_preview_request_count_;
641
642  WebContents* initiator = GetInitiator();
643  if (!initiator) {
644    ReportUserActionHistogram(INITIATOR_CLOSED);
645    print_preview_ui->OnClosePrintPreviewDialog();
646    return;
647  }
648
649  // Retrieve the page title and url and send it to the renderer process if
650  // headers and footers are to be displayed.
651  bool display_header_footer = false;
652  if (!settings->GetBoolean(printing::kSettingHeaderFooterEnabled,
653                            &display_header_footer)) {
654    NOTREACHED();
655  }
656  if (display_header_footer) {
657    settings->SetString(printing::kSettingHeaderFooterTitle,
658                        initiator->GetTitle());
659    std::string url;
660    content::NavigationEntry* entry =
661        initiator->GetController().GetLastCommittedEntry();
662    if (entry)
663      url = entry->GetVirtualURL().spec();
664    settings->SetString(printing::kSettingHeaderFooterURL, url);
665  }
666
667  bool generate_draft_data = false;
668  bool success = settings->GetBoolean(printing::kSettingGenerateDraftData,
669                                      &generate_draft_data);
670  DCHECK(success);
671
672  if (!generate_draft_data) {
673    double draft_page_count_double = -1;
674    success = args->GetDouble(1, &draft_page_count_double);
675    DCHECK(success);
676    int draft_page_count = static_cast<int>(draft_page_count_double);
677
678    bool preview_modifiable = false;
679    success = args->GetBoolean(2, &preview_modifiable);
680    DCHECK(success);
681
682    if (draft_page_count != -1 && preview_modifiable &&
683        print_preview_ui->GetAvailableDraftPageCount() != draft_page_count) {
684      settings->SetBoolean(printing::kSettingGenerateDraftData, true);
685    }
686  }
687
688  VLOG(1) << "Print preview request start";
689  RenderViewHost* rvh = initiator->GetRenderViewHost();
690  rvh->Send(new PrintMsg_PrintPreview(rvh->GetRoutingID(), *settings));
691}
692
693void PrintPreviewHandler::HandlePrint(const base::ListValue* args) {
694  ReportStats();
695
696  // Record the number of times the user requests to regenerate preview data
697  // before printing.
698  UMA_HISTOGRAM_COUNTS("PrintPreview.RegeneratePreviewRequest.BeforePrint",
699                       regenerate_preview_request_count_);
700
701  WebContents* initiator = GetInitiator();
702  if (initiator) {
703    RenderViewHost* rvh = initiator->GetRenderViewHost();
704    rvh->Send(new PrintMsg_ResetScriptedPrintCount(rvh->GetRoutingID()));
705  }
706
707  scoped_ptr<base::DictionaryValue> settings(GetSettingsDictionary(args));
708  if (!settings.get())
709    return;
710
711  // Never try to add headers/footers here. It's already in the generated PDF.
712  settings->SetBoolean(printing::kSettingHeaderFooterEnabled, false);
713
714  bool print_to_pdf = false;
715  bool is_cloud_printer = false;
716  bool print_with_privet = false;
717
718  bool open_pdf_in_preview = false;
719#if defined(OS_MACOSX)
720  open_pdf_in_preview = settings->HasKey(printing::kSettingOpenPDFInPreview);
721#endif
722
723  if (!open_pdf_in_preview) {
724    settings->GetBoolean(printing::kSettingPrintToPDF, &print_to_pdf);
725    settings->GetBoolean(printing::kSettingPrintWithPrivet, &print_with_privet);
726    is_cloud_printer = settings->HasKey(printing::kSettingCloudPrintId);
727  }
728
729  int page_count = 0;
730  settings->GetInteger(printing::kSettingPreviewPageCount, &page_count);
731
732  if (print_to_pdf) {
733    UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToPDF", page_count);
734    ReportUserActionHistogram(PRINT_TO_PDF);
735    PrintToPdf();
736    return;
737  }
738
739#if defined(ENABLE_SERVICE_DISCOVERY)
740  if (print_with_privet && PrivetPrintingEnabled()) {
741    std::string printer_name;
742    std::string print_ticket;
743    std::string capabilities;
744    UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintWithPrivet", page_count);
745    ReportUserActionHistogram(PRINT_WITH_PRIVET);
746
747    int width = 0;
748    int height = 0;
749    if (!settings->GetString(printing::kSettingDeviceName, &printer_name) ||
750        !settings->GetString(printing::kSettingTicket, &print_ticket) ||
751        !settings->GetString(printing::kSettingCapabilities, &capabilities) ||
752        !settings->GetInteger(printing::kSettingPageWidth, &width) ||
753        !settings->GetInteger(printing::kSettingPageHeight, &height) ||
754        width <= 0 || height <= 0) {
755      NOTREACHED();
756      base::FundamentalValue http_code_value(-1);
757      web_ui()->CallJavascriptFunction("onPrivetPrintFailed", http_code_value);
758      return;
759    }
760
761    PrintToPrivetPrinter(
762        printer_name, print_ticket, capabilities, gfx::Size(width, height));
763    return;
764  }
765#endif
766
767  scoped_refptr<base::RefCountedBytes> data;
768  base::string16 title;
769  if (!GetPreviewDataAndTitle(&data, &title)) {
770    // Nothing to print, no preview available.
771    return;
772  }
773
774  if (is_cloud_printer) {
775    UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToCloudPrint",
776                         page_count);
777    ReportUserActionHistogram(PRINT_WITH_CLOUD_PRINT);
778    SendCloudPrintJob(data.get());
779  } else {
780    UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToPrinter", page_count);
781    ReportUserActionHistogram(PRINT_TO_PRINTER);
782    ReportPrintSettingsStats(*settings);
783
784    // This tries to activate the initiator as well, so do not clear the
785    // association with the initiator yet.
786    PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(
787        web_ui()->GetController());
788    print_preview_ui->OnHidePreviewDialog();
789
790    // Do this so the initiator can open a new print preview dialog, while the
791    // current print preview dialog is still handling its print job.
792    ClearInitiatorDetails();
793
794    // The PDF being printed contains only the pages that the user selected,
795    // so ignore the page range and print all pages.
796    settings->Remove(printing::kSettingPageRange, NULL);
797    // Reset selection only flag for the same reason.
798    settings->SetBoolean(printing::kSettingShouldPrintSelectionOnly, false);
799
800#if defined(USE_CUPS)
801    if (!open_pdf_in_preview)  // We can get here even for cloud printers.
802      ConvertColorSettingToCUPSColorModel(settings.get());
803#endif
804
805    // Set ID to know whether printing is for preview.
806    settings->SetInteger(printing::kPreviewUIID,
807                         print_preview_ui->GetIDForPrintPreviewUI());
808    RenderViewHost* rvh = preview_web_contents()->GetRenderViewHost();
809    rvh->Send(new PrintMsg_PrintForPrintPreview(rvh->GetRoutingID(),
810                                                *settings));
811
812    // For all other cases above, the preview dialog will stay open until the
813    // printing has finished. Then the dialog closes and PrintPreviewDone() gets
814    // called. In the case below, since the preview dialog will be hidden and
815    // not closed, we need to make this call.
816    if (initiator) {
817      printing::PrintViewManager* print_view_manager =
818          printing::PrintViewManager::FromWebContents(initiator);
819      print_view_manager->PrintPreviewDone();
820    }
821  }
822}
823
824void PrintPreviewHandler::PrintToPdf() {
825  if (!print_to_pdf_path_.empty()) {
826    // User has already selected a path, no need to show the dialog again.
827    PostPrintToPdfTask();
828  } else if (!select_file_dialog_.get() ||
829             !select_file_dialog_->IsRunning(platform_util::GetTopLevel(
830                 preview_web_contents()->GetView()->GetNativeView()))) {
831    PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(
832        web_ui()->GetController());
833    // Pre-populating select file dialog with print job title.
834    base::string16 print_job_title_utf16 = print_preview_ui->initiator_title();
835
836#if defined(OS_WIN)
837    base::FilePath::StringType print_job_title(print_job_title_utf16);
838#elif defined(OS_POSIX)
839    base::FilePath::StringType print_job_title =
840        base::UTF16ToUTF8(print_job_title_utf16);
841#endif
842
843    file_util::ReplaceIllegalCharactersInPath(&print_job_title, '_');
844    base::FilePath default_filename(print_job_title);
845    default_filename =
846        default_filename.ReplaceExtension(FILE_PATH_LITERAL("pdf"));
847
848    SelectFile(default_filename);
849  }
850}
851
852void PrintPreviewHandler::HandleHidePreview(const base::ListValue* /*args*/) {
853  PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(
854      web_ui()->GetController());
855  print_preview_ui->OnHidePreviewDialog();
856}
857
858void PrintPreviewHandler::HandleCancelPendingPrintRequest(
859    const base::ListValue* /*args*/) {
860  WebContents* initiator = GetInitiator();
861  if (initiator)
862    ClearInitiatorDetails();
863  chrome::ShowPrintErrorDialog();
864}
865
866void PrintPreviewHandler::HandleSaveAppState(const base::ListValue* args) {
867  std::string data_to_save;
868  printing::StickySettings* sticky_settings = GetStickySettings();
869  if (args->GetString(0, &data_to_save) && !data_to_save.empty())
870    sticky_settings->StoreAppState(data_to_save);
871  sticky_settings->SaveInPrefs(Profile::FromBrowserContext(
872      preview_web_contents()->GetBrowserContext())->GetPrefs());
873}
874
875void PrintPreviewHandler::HandleGetPrinterCapabilities(
876    const base::ListValue* args) {
877  std::string printer_name;
878  bool ret = args->GetString(0, &printer_name);
879  if (!ret || printer_name.empty())
880    return;
881
882  GetPrinterCapabilitiesSuccessCallback success_cb =
883      base::Bind(&PrintPreviewHandler::SendPrinterCapabilities,
884                 weak_factory_.GetWeakPtr());
885  GetPrinterCapabilitiesFailureCallback failure_cb =
886      base::Bind(&PrintPreviewHandler::SendFailedToGetPrinterCapabilities,
887                 weak_factory_.GetWeakPtr());
888  BrowserThread::PostTask(
889      BrowserThread::FILE, FROM_HERE,
890      base::Bind(&GetPrinterCapabilitiesOnFileThread,
891                 print_backend_, printer_name, success_cb, failure_cb));
892}
893
894void PrintPreviewHandler::OnSigninComplete() {
895  PrintPreviewUI* print_preview_ui =
896      static_cast<PrintPreviewUI*>(web_ui()->GetController());
897  if (print_preview_ui)
898    print_preview_ui->OnReloadPrintersList();
899}
900
901void PrintPreviewHandler::HandleSignin(const base::ListValue* args) {
902  bool add_account = false;
903  bool success = args->GetBoolean(0, &add_account);
904  DCHECK(success);
905
906  Profile* profile = Profile::FromBrowserContext(
907      preview_web_contents()->GetBrowserContext());
908  chrome::ScopedTabbedBrowserDisplayer displayer(
909      profile, chrome::GetActiveDesktop());
910  print_dialog_cloud::CreateCloudPrintSigninTab(
911      displayer.browser(),
912      add_account,
913      base::Bind(&PrintPreviewHandler::OnSigninComplete,
914                 weak_factory_.GetWeakPtr()));
915}
916
917void PrintPreviewHandler::HandleGetAccessToken(const base::ListValue* args) {
918  std::string type;
919  if (!args->GetString(0, &type))
920    return;
921  if (!token_service_)
922    token_service_.reset(new AccessTokenService(this));
923  token_service_->RequestToken(type);
924}
925
926void PrintPreviewHandler::PrintWithCloudPrintDialog() {
927  // Record the number of times the user asks to print via cloud print
928  // instead of the print preview dialog.
929  ReportStats();
930
931  scoped_refptr<base::RefCountedBytes> data;
932  base::string16 title;
933  if (!GetPreviewDataAndTitle(&data, &title)) {
934    // Nothing to print, no preview available.
935    return;
936  }
937
938  gfx::NativeWindow modal_parent = platform_util::GetTopLevel(
939      preview_web_contents()->GetView()->GetNativeView());
940  print_dialog_cloud::CreatePrintDialogForBytes(
941      preview_web_contents()->GetBrowserContext(),
942      modal_parent,
943      data.get(),
944      title,
945      base::string16(),
946      std::string("application/pdf"));
947
948  // Once the cloud print dialog comes up we're no longer in a background
949  // printing situation.  Close the print preview.
950  // TODO(abodenha@chromium.org) The flow should be changed as described in
951  // http://code.google.com/p/chromium/issues/detail?id=44093
952  ClosePreviewDialog();
953}
954
955void PrintPreviewHandler::HandleManageCloudPrint(
956    const base::ListValue* /*args*/) {
957  ++manage_cloud_printers_dialog_request_count_;
958  Profile* profile = Profile::FromBrowserContext(
959      preview_web_contents()->GetBrowserContext());
960  preview_web_contents()->OpenURL(
961      content::OpenURLParams(
962          CloudPrintURL(profile).GetCloudPrintServiceManageURL(),
963          content::Referrer(),
964          NEW_FOREGROUND_TAB,
965          content::PAGE_TRANSITION_LINK,
966          false));
967}
968
969void PrintPreviewHandler::HandleShowSystemDialog(
970    const base::ListValue* /*args*/) {
971  ReportStats();
972  ReportUserActionHistogram(FALLBACK_TO_ADVANCED_SETTINGS_DIALOG);
973
974  WebContents* initiator = GetInitiator();
975  if (!initiator)
976    return;
977
978  printing::PrintViewManager* print_view_manager =
979      printing::PrintViewManager::FromWebContents(initiator);
980  print_view_manager->set_observer(this);
981  print_view_manager->PrintForSystemDialogNow();
982
983  // Cancel the pending preview request if exists.
984  PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(
985      web_ui()->GetController());
986  print_preview_ui->OnCancelPendingPreviewRequest();
987}
988
989void PrintPreviewHandler::HandleManagePrinters(
990    const base::ListValue* /*args*/) {
991  ++manage_printers_dialog_request_count_;
992  printing::PrinterManagerDialog::ShowPrinterManagerDialog();
993}
994
995void PrintPreviewHandler::HandlePrintWithCloudPrintDialog(
996    const base::ListValue* args) {
997  int page_count = 0;
998  if (!args || !args->GetInteger(0, &page_count))
999    return;
1000  UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToCloudPrintWebDialog",
1001                       page_count);
1002
1003  PrintWithCloudPrintDialog();
1004}
1005
1006void PrintPreviewHandler::HandleClosePreviewDialog(
1007    const base::ListValue* /*args*/) {
1008  ReportStats();
1009  ReportUserActionHistogram(CANCEL);
1010
1011  // Record the number of times the user requests to regenerate preview data
1012  // before cancelling.
1013  UMA_HISTOGRAM_COUNTS("PrintPreview.RegeneratePreviewRequest.BeforeCancel",
1014                       regenerate_preview_request_count_);
1015}
1016
1017void PrintPreviewHandler::ReportStats() {
1018  UMA_HISTOGRAM_COUNTS("PrintPreview.ManagePrinters",
1019                       manage_printers_dialog_request_count_);
1020  UMA_HISTOGRAM_COUNTS("PrintPreview.ManageCloudPrinters",
1021                       manage_cloud_printers_dialog_request_count_);
1022}
1023
1024void PrintPreviewHandler::GetNumberFormatAndMeasurementSystem(
1025    base::DictionaryValue* settings) {
1026
1027  // Getting the measurement system based on the locale.
1028  UErrorCode errorCode = U_ZERO_ERROR;
1029  const char* locale = g_browser_process->GetApplicationLocale().c_str();
1030  UMeasurementSystem system = ulocdata_getMeasurementSystem(locale, &errorCode);
1031  if (errorCode > U_ZERO_ERROR || system == UMS_LIMIT)
1032    system = UMS_SI;
1033
1034  // Getting the number formatting based on the locale and writing to
1035  // dictionary.
1036  settings->SetString(kNumberFormat, base::FormatDouble(123456.78, 2));
1037  settings->SetInteger(kMeasurementSystem, system);
1038}
1039
1040void PrintPreviewHandler::HandleGetInitialSettings(
1041    const base::ListValue* /*args*/) {
1042  // Send before SendInitialSettings to allow cloud printer auto select.
1043  SendCloudPrintEnabled();
1044  BrowserThread::PostTaskAndReplyWithResult(
1045      BrowserThread::FILE, FROM_HERE,
1046      base::Bind(&GetDefaultPrinterOnFileThread, print_backend_),
1047      base::Bind(&PrintPreviewHandler::SendInitialSettings,
1048                 weak_factory_.GetWeakPtr()));
1049}
1050
1051void PrintPreviewHandler::HandleReportUiEvent(const base::ListValue* args) {
1052  int event_group, event_number;
1053  if (!args->GetInteger(0, &event_group) || !args->GetInteger(1, &event_number))
1054    return;
1055
1056  enum UiBucketGroups ui_bucket_group =
1057      static_cast<enum UiBucketGroups>(event_group);
1058  if (ui_bucket_group >= UI_BUCKET_GROUP_BOUNDARY)
1059    return;
1060
1061  switch (ui_bucket_group) {
1062    case DESTINATION_SEARCH: {
1063      enum PrintDestinationBuckets event =
1064          static_cast<enum PrintDestinationBuckets>(event_number);
1065      if (event >= PRINT_DESTINATION_BUCKET_BOUNDARY)
1066        return;
1067      ReportPrintDestinationHistogram(event);
1068      break;
1069    }
1070    case GCP_PROMO: {
1071      enum GcpPromoBuckets event =
1072          static_cast<enum GcpPromoBuckets>(event_number);
1073      if (event >= GCP_PROMO_BUCKET_BOUNDARY)
1074        return;
1075      ReportGcpPromoHistogram(event);
1076      break;
1077    }
1078    default:
1079      break;
1080  }
1081}
1082
1083void PrintPreviewHandler::HandleForceOpenNewTab(const base::ListValue* args) {
1084  std::string url;
1085  if (!args->GetString(0, &url))
1086    return;
1087  Browser* browser = chrome::FindBrowserWithWebContents(GetInitiator());
1088  if (!browser)
1089    return;
1090  chrome::AddSelectedTabWithURL(browser,
1091                                GURL(url),
1092                                content::PAGE_TRANSITION_LINK);
1093}
1094
1095void PrintPreviewHandler::SendInitialSettings(
1096    const std::string& default_printer) {
1097  PrintPreviewUI* print_preview_ui =
1098      static_cast<PrintPreviewUI*>(web_ui()->GetController());
1099
1100  base::DictionaryValue initial_settings;
1101  initial_settings.SetString(kInitiatorTitle,
1102                             print_preview_ui->initiator_title());
1103  initial_settings.SetBoolean(printing::kSettingPreviewModifiable,
1104                              print_preview_ui->source_is_modifiable());
1105  initial_settings.SetString(printing::kSettingPrinterName, default_printer);
1106  initial_settings.SetBoolean(kDocumentHasSelection,
1107                              print_preview_ui->source_has_selection());
1108  initial_settings.SetBoolean(printing::kSettingShouldPrintSelectionOnly,
1109                              print_preview_ui->print_selection_only());
1110  printing::StickySettings* sticky_settings = GetStickySettings();
1111  sticky_settings->RestoreFromPrefs(Profile::FromBrowserContext(
1112      preview_web_contents()->GetBrowserContext())->GetPrefs());
1113  if (sticky_settings->printer_app_state())
1114    initial_settings.SetString(kAppState,
1115                               *sticky_settings->printer_app_state());
1116
1117  CommandLine* cmdline = CommandLine::ForCurrentProcess();
1118  initial_settings.SetBoolean(kPrintAutomaticallyInKioskMode,
1119                              cmdline->HasSwitch(switches::kKioskModePrinting));
1120#if defined(OS_WIN)
1121  // In Win8 metro, the system print dialog can only open on the desktop.  Doing
1122  // so will cause the browser to appear hung, so we don't show the link in
1123  // metro.
1124  bool is_ash = (chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_ASH);
1125  initial_settings.SetBoolean(kHidePrintWithSystemDialogLink, is_ash);
1126#endif
1127
1128  if (print_preview_ui->source_is_modifiable())
1129    GetNumberFormatAndMeasurementSystem(&initial_settings);
1130  web_ui()->CallJavascriptFunction("setInitialSettings", initial_settings);
1131}
1132
1133void PrintPreviewHandler::ClosePreviewDialog() {
1134  PrintPreviewUI* print_preview_ui =
1135      static_cast<PrintPreviewUI*>(web_ui()->GetController());
1136  print_preview_ui->OnClosePrintPreviewDialog();
1137}
1138
1139void PrintPreviewHandler::SendAccessToken(const std::string& type,
1140                                          const std::string& access_token) {
1141  VLOG(1) << "Get getAccessToken finished";
1142  web_ui()->CallJavascriptFunction("onDidGetAccessToken",
1143                                   base::StringValue(type),
1144                                   base::StringValue(access_token));
1145}
1146
1147void PrintPreviewHandler::SendPrinterCapabilities(
1148    const base::DictionaryValue* settings_info) {
1149  VLOG(1) << "Get printer capabilities finished";
1150
1151#if defined(USE_CUPS)
1152  SaveCUPSColorSetting(settings_info);
1153#endif
1154
1155  web_ui()->CallJavascriptFunction("updateWithPrinterCapabilities",
1156                                   *settings_info);
1157}
1158
1159void PrintPreviewHandler::SendFailedToGetPrinterCapabilities(
1160    const std::string& printer_name) {
1161  VLOG(1) << "Get printer capabilities failed";
1162  base::StringValue printer_name_value(printer_name);
1163  web_ui()->CallJavascriptFunction("failedToGetPrinterCapabilities",
1164                                   printer_name_value);
1165}
1166
1167void PrintPreviewHandler::SetupPrinterList(const base::ListValue* printers) {
1168  if (!has_logged_printers_count_) {
1169    UMA_HISTOGRAM_COUNTS("PrintPreview.NumberOfPrinters", printers->GetSize());
1170    has_logged_printers_count_ = true;
1171  }
1172
1173  web_ui()->CallJavascriptFunction("setPrinters", *printers);
1174}
1175
1176void PrintPreviewHandler::SendCloudPrintEnabled() {
1177  Profile* profile = Profile::FromBrowserContext(
1178      preview_web_contents()->GetBrowserContext());
1179  PrefService* prefs = profile->GetPrefs();
1180  if (prefs->GetBoolean(prefs::kCloudPrintSubmitEnabled)) {
1181    GURL gcp_url(CloudPrintURL(profile).GetCloudPrintServiceURL());
1182    base::StringValue gcp_url_value(gcp_url.spec());
1183    web_ui()->CallJavascriptFunction("setUseCloudPrint", gcp_url_value);
1184  }
1185}
1186
1187void PrintPreviewHandler::SendCloudPrintJob(const base::RefCountedBytes* data) {
1188  // BASE64 encode the job data.
1189  std::string raw_data(reinterpret_cast<const char*>(data->front()),
1190                       data->size());
1191  std::string base64_data;
1192  base::Base64Encode(raw_data, &base64_data);
1193  base::StringValue data_value(base64_data);
1194
1195  web_ui()->CallJavascriptFunction("printToCloud", data_value);
1196}
1197
1198WebContents* PrintPreviewHandler::GetInitiator() const {
1199  printing::PrintPreviewDialogController* dialog_controller =
1200      printing::PrintPreviewDialogController::GetInstance();
1201  if (!dialog_controller)
1202    return NULL;
1203  return dialog_controller->GetInitiator(preview_web_contents());
1204}
1205
1206void PrintPreviewHandler::OnPrintDialogShown() {
1207  ClosePreviewDialog();
1208}
1209
1210void PrintPreviewHandler::SelectFile(const base::FilePath& default_filename) {
1211  ui::SelectFileDialog::FileTypeInfo file_type_info;
1212  file_type_info.extensions.resize(1);
1213  file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("pdf"));
1214
1215  // Initializing |save_path_| if it is not already initialized.
1216  printing::StickySettings* sticky_settings = GetStickySettings();
1217  if (!sticky_settings->save_path()) {
1218    // Allowing IO operation temporarily. It is ok to do so here because
1219    // the select file dialog performs IO anyway in order to display the
1220    // folders and also it is modal.
1221    base::ThreadRestrictions::ScopedAllowIO allow_io;
1222    base::FilePath file_path;
1223    PathService::Get(chrome::DIR_USER_DOCUMENTS, &file_path);
1224    sticky_settings->StoreSavePath(file_path);
1225    sticky_settings->SaveInPrefs(Profile::FromBrowserContext(
1226        preview_web_contents()->GetBrowserContext())->GetPrefs());
1227  }
1228
1229  select_file_dialog_ = ui::SelectFileDialog::Create(
1230      this, new ChromeSelectFilePolicy(preview_web_contents())),
1231  select_file_dialog_->SelectFile(
1232      ui::SelectFileDialog::SELECT_SAVEAS_FILE,
1233      base::string16(),
1234      sticky_settings->save_path()->Append(default_filename),
1235      &file_type_info,
1236      0,
1237      base::FilePath::StringType(),
1238      platform_util::GetTopLevel(
1239          preview_web_contents()->GetView()->GetNativeView()),
1240      NULL);
1241}
1242
1243void PrintPreviewHandler::OnPrintPreviewDialogDestroyed() {
1244  WebContents* initiator = GetInitiator();
1245  if (!initiator)
1246    return;
1247
1248  printing::PrintViewManager* print_view_manager =
1249      printing::PrintViewManager::FromWebContents(initiator);
1250  print_view_manager->set_observer(NULL);
1251}
1252
1253void PrintPreviewHandler::OnPrintPreviewFailed() {
1254  if (reported_failed_preview_)
1255    return;
1256  reported_failed_preview_ = true;
1257  ReportUserActionHistogram(PREVIEW_FAILED);
1258}
1259
1260void PrintPreviewHandler::ShowSystemDialog() {
1261  HandleShowSystemDialog(NULL);
1262}
1263
1264void PrintPreviewHandler::FileSelected(const base::FilePath& path,
1265                                       int index, void* params) {
1266  // Updating |save_path_| to the newly selected folder.
1267  printing::StickySettings* sticky_settings = GetStickySettings();
1268  sticky_settings->StoreSavePath(path.DirName());
1269  sticky_settings->SaveInPrefs(Profile::FromBrowserContext(
1270      preview_web_contents()->GetBrowserContext())->GetPrefs());
1271  web_ui()->CallJavascriptFunction("fileSelectionCompleted");
1272  print_to_pdf_path_ = path;
1273  PostPrintToPdfTask();
1274}
1275
1276void PrintPreviewHandler::PostPrintToPdfTask() {
1277  scoped_refptr<base::RefCountedBytes> data;
1278  base::string16 title;
1279  if (!GetPreviewDataAndTitle(&data, &title)) {
1280    NOTREACHED() << "Preview data was checked before file dialog.";
1281    return;
1282  }
1283  scoped_ptr<printing::PreviewMetafile> metafile(new printing::PreviewMetafile);
1284  metafile->InitFromData(static_cast<const void*>(data->front()), data->size());
1285  BrowserThread::PostTask(
1286      BrowserThread::FILE, FROM_HERE,
1287      base::Bind(&PrintToPdfCallback, metafile.release(), print_to_pdf_path_));
1288  print_to_pdf_path_ = base::FilePath();
1289  ClosePreviewDialog();
1290}
1291
1292void PrintPreviewHandler::FileSelectionCanceled(void* params) {
1293  PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(
1294      web_ui()->GetController());
1295  print_preview_ui->OnFileSelectionCancelled();
1296}
1297
1298void PrintPreviewHandler::ClearInitiatorDetails() {
1299  WebContents* initiator = GetInitiator();
1300  if (!initiator)
1301    return;
1302
1303  // We no longer require the initiator details. Remove those details associated
1304  // with the preview dialog to allow the initiator to create another preview
1305  // dialog.
1306  printing::PrintPreviewDialogController* dialog_controller =
1307      printing::PrintPreviewDialogController::GetInstance();
1308  if (dialog_controller)
1309    dialog_controller->EraseInitiatorInfo(preview_web_contents());
1310}
1311
1312bool PrintPreviewHandler::GetPreviewDataAndTitle(
1313    scoped_refptr<base::RefCountedBytes>* data,
1314    base::string16* title) const {
1315  PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(
1316      web_ui()->GetController());
1317  scoped_refptr<base::RefCountedBytes> tmp_data;
1318  print_preview_ui->GetPrintPreviewDataForIndex(
1319      printing::COMPLETE_PREVIEW_DOCUMENT_INDEX, &tmp_data);
1320
1321  if (!tmp_data.get()) {
1322    // Nothing to print, no preview available.
1323    return false;
1324  }
1325  DCHECK(tmp_data->size() && tmp_data->front());
1326
1327  *data = tmp_data;
1328  *title = print_preview_ui->initiator_title();
1329  return true;
1330}
1331
1332#if defined(USE_CUPS)
1333void PrintPreviewHandler::SaveCUPSColorSetting(
1334    const base::DictionaryValue* settings) {
1335  cups_printer_color_models_.reset(new CUPSPrinterColorModels);
1336  settings->GetString(kPrinterId, &cups_printer_color_models_->printer_name);
1337  settings->GetInteger(
1338      kCUPSsColorModel,
1339      reinterpret_cast<int*>(&cups_printer_color_models_->color_model));
1340  settings->GetInteger(
1341      kCUPSsBWModel,
1342      reinterpret_cast<int*>(&cups_printer_color_models_->bw_model));
1343}
1344
1345void PrintPreviewHandler::ConvertColorSettingToCUPSColorModel(
1346    base::DictionaryValue* settings) const {
1347  if (!cups_printer_color_models_)
1348    return;
1349
1350  // Sanity check the printer name.
1351  std::string printer_name;
1352  if (!settings->GetString(printing::kSettingDeviceName, &printer_name) ||
1353      printer_name != cups_printer_color_models_->printer_name) {
1354    NOTREACHED();
1355    return;
1356  }
1357
1358  int color;
1359  if (!settings->GetInteger(printing::kSettingColor, &color)) {
1360    NOTREACHED();
1361    return;
1362  }
1363
1364  if (color == printing::GRAY) {
1365    if (cups_printer_color_models_->bw_model != printing::UNKNOWN_COLOR_MODEL) {
1366      settings->SetInteger(printing::kSettingColor,
1367                           cups_printer_color_models_->bw_model);
1368    }
1369    return;
1370  }
1371
1372  printing::ColorModel color_model = cups_printer_color_models_->color_model;
1373  if (color_model != printing::UNKNOWN_COLOR_MODEL)
1374    settings->SetInteger(printing::kSettingColor, color_model);
1375}
1376
1377#endif
1378
1379#if defined(ENABLE_SERVICE_DISCOVERY)
1380void PrintPreviewHandler::LocalPrinterChanged(
1381    bool added,
1382    const std::string& name,
1383    bool has_local_printing,
1384    const local_discovery::DeviceDescription& description) {
1385  CommandLine* command_line = CommandLine::ForCurrentProcess();
1386  if (has_local_printing ||
1387      command_line->HasSwitch(switches::kEnablePrintPreviewRegisterPromos)) {
1388    base::DictionaryValue info;
1389    FillPrinterDescription(name, description, has_local_printing, &info);
1390    web_ui()->CallJavascriptFunction("onPrivetPrinterChanged", info);
1391  }
1392}
1393
1394void PrintPreviewHandler::LocalPrinterRemoved(const std::string& name) {
1395}
1396
1397void PrintPreviewHandler::LocalPrinterCacheFlushed() {
1398}
1399
1400void PrintPreviewHandler::PrivetCapabilitiesUpdateClient(
1401    scoped_ptr<local_discovery::PrivetHTTPClient> http_client) {
1402  if (!PrivetUpdateClient(http_client.Pass()))
1403    return;
1404
1405  privet_capabilities_operation_ =
1406      privet_http_client_->CreateCapabilitiesOperation(
1407          base::Bind(&PrintPreviewHandler::OnPrivetCapabilities,
1408                     base::Unretained(this)));
1409  privet_capabilities_operation_->Start();
1410}
1411
1412bool PrintPreviewHandler::PrivetUpdateClient(
1413    scoped_ptr<local_discovery::PrivetHTTPClient> http_client) {
1414  if (!http_client) {
1415    SendPrivetCapabilitiesError(privet_http_resolution_->GetName());
1416    privet_http_resolution_.reset();
1417    return false;
1418  }
1419
1420  privet_local_print_operation_.reset();
1421  privet_capabilities_operation_.reset();
1422  privet_http_client_ = http_client.Pass();
1423
1424  privet_http_resolution_.reset();
1425
1426  return true;
1427}
1428
1429void PrintPreviewHandler::PrivetLocalPrintUpdateClient(
1430    std::string print_ticket,
1431    std::string capabilities,
1432    gfx::Size page_size,
1433    scoped_ptr<local_discovery::PrivetHTTPClient> http_client) {
1434  if (!PrivetUpdateClient(http_client.Pass()))
1435    return;
1436
1437  StartPrivetLocalPrint(print_ticket, capabilities, page_size);
1438}
1439
1440void PrintPreviewHandler::StartPrivetLocalPrint(const std::string& print_ticket,
1441                                                const std::string& capabilities,
1442                                                const gfx::Size& page_size) {
1443  privet_local_print_operation_ =
1444      privet_http_client_->CreateLocalPrintOperation(this);
1445
1446  privet_local_print_operation_->SetTicket(print_ticket);
1447  privet_local_print_operation_->SetCapabilities(capabilities);
1448
1449  scoped_refptr<base::RefCountedBytes> data;
1450  base::string16 title;
1451
1452  if (!GetPreviewDataAndTitle(&data, &title)) {
1453    base::FundamentalValue http_code_value(-1);
1454    web_ui()->CallJavascriptFunction("onPrivetPrintFailed", http_code_value);
1455    return;
1456  }
1457
1458  privet_local_print_operation_->SetJobname(base::UTF16ToUTF8(title));
1459  privet_local_print_operation_->SetPageSize(page_size);
1460  privet_local_print_operation_->SetData(data);
1461
1462  Profile* profile = Profile::FromWebUI(web_ui());
1463  SigninManagerBase* signin_manager =
1464      SigninManagerFactory::GetForProfileIfExists(profile);
1465
1466  if (signin_manager) {
1467    privet_local_print_operation_->SetUsername(
1468        signin_manager->GetAuthenticatedUsername());
1469  }
1470
1471  privet_local_print_operation_->Start();
1472}
1473
1474
1475void PrintPreviewHandler::OnPrivetCapabilities(
1476    const base::DictionaryValue* capabilities) {
1477  std::string name = privet_capabilities_operation_->GetHTTPClient()->GetName();
1478
1479  if (!capabilities || capabilities->HasKey(local_discovery::kPrivetKeyError)) {
1480    SendPrivetCapabilitiesError(name);
1481    return;
1482  }
1483
1484  base::DictionaryValue printer_info;
1485  const local_discovery::DeviceDescription* description =
1486      printer_lister_->GetDeviceDescription(name);
1487
1488  if (!description) {
1489    SendPrivetCapabilitiesError(name);
1490    return;
1491  }
1492
1493  FillPrinterDescription(name, *description, true, &printer_info);
1494
1495  web_ui()->CallJavascriptFunction(
1496      "onPrivetCapabilitiesSet",
1497      printer_info,
1498      *capabilities);
1499
1500  privet_capabilities_operation_.reset();
1501}
1502
1503void PrintPreviewHandler::SendPrivetCapabilitiesError(
1504    const std::string& device_name) {
1505  base::StringValue name_value(device_name);
1506  web_ui()->CallJavascriptFunction(
1507      "failedToGetPrivetPrinterCapabilities",
1508      name_value);
1509}
1510
1511void PrintPreviewHandler::PrintToPrivetPrinter(const std::string& device_name,
1512                                               const std::string& ticket,
1513                                               const std::string& capabilities,
1514                                               const gfx::Size& page_size) {
1515  CreatePrivetHTTP(
1516      device_name,
1517      base::Bind(&PrintPreviewHandler::PrivetLocalPrintUpdateClient,
1518                 base::Unretained(this),
1519                 ticket,
1520                 capabilities,
1521                 page_size));
1522}
1523
1524bool PrintPreviewHandler::CreatePrivetHTTP(
1525    const std::string& name,
1526    const local_discovery::PrivetHTTPAsynchronousFactory::ResultCallback&
1527    callback) {
1528  const local_discovery::DeviceDescription* device_description =
1529      printer_lister_->GetDeviceDescription(name);
1530
1531  if (!device_description) {
1532    SendPrivetCapabilitiesError(name);
1533    return false;
1534  }
1535
1536  privet_http_factory_ =
1537      local_discovery::PrivetHTTPAsynchronousFactory::CreateInstance(
1538      service_discovery_client_,
1539      Profile::FromWebUI(web_ui())->GetRequestContext());
1540  privet_http_resolution_ = privet_http_factory_->CreatePrivetHTTP(
1541      name,
1542      device_description->address,
1543      callback);
1544  privet_http_resolution_->Start();
1545
1546  return true;
1547}
1548
1549void PrintPreviewHandler::OnPrivetPrintingDone(
1550    const local_discovery::PrivetLocalPrintOperation* print_operation) {
1551  ClosePreviewDialog();
1552}
1553
1554void PrintPreviewHandler::OnPrivetPrintingError(
1555    const local_discovery::PrivetLocalPrintOperation* print_operation,
1556    int http_code) {
1557  base::FundamentalValue http_code_value(http_code);
1558  web_ui()->CallJavascriptFunction("onPrivetPrintFailed", http_code_value);
1559}
1560
1561void PrintPreviewHandler::FillPrinterDescription(
1562    const std::string& name,
1563    const local_discovery::DeviceDescription& description,
1564    bool has_local_printing,
1565    base::DictionaryValue* printer_value) {
1566  CommandLine* command_line = CommandLine::ForCurrentProcess();
1567
1568  printer_value->SetString("serviceName", name);
1569  printer_value->SetString("name", description.name);
1570  printer_value->SetBoolean("hasLocalPrinting", has_local_printing);
1571  printer_value->SetBoolean(
1572      "isUnregistered",
1573      description.id.empty() &&
1574      command_line->HasSwitch(switches::kEnablePrintPreviewRegisterPromos));
1575  printer_value->SetString("cloudID", description.id);
1576}
1577
1578#endif
1579