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