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