Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / extension_install_prompt.cc
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/extensions/extension_install_prompt.h"
6
7 #include <map>
8
9 #include "base/command_line.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/browser/extensions/bundle_installer.h"
17 #include "chrome/browser/extensions/extension_install_ui.h"
18 #include "chrome/browser/extensions/image_loader.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/signin/profile_oauth2_token_service.h"
21 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
22 #include "chrome/browser/ui/browser.h"
23 #include "chrome/browser/ui/browser_window.h"
24 #include "chrome/common/chrome_switches.h"
25 #include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h"
26 #include "chrome/common/extensions/extension_constants.h"
27 #include "chrome/common/extensions/extension_icon_set.h"
28 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
29 #include "chrome/common/pref_names.h"
30 #include "content/public/browser/web_contents.h"
31 #include "content/public/browser/web_contents_view.h"
32 #include "extensions/common/extension.h"
33 #include "extensions/common/extension_resource.h"
34 #include "extensions/common/feature_switch.h"
35 #include "extensions/common/manifest.h"
36 #include "extensions/common/manifest_constants.h"
37 #include "extensions/common/permissions/permission_message_provider.h"
38 #include "extensions/common/permissions/permission_set.h"
39 #include "extensions/common/permissions/permissions_data.h"
40 #include "extensions/common/url_pattern.h"
41 #include "grit/chromium_strings.h"
42 #include "grit/generated_resources.h"
43 #include "grit/theme_resources.h"
44 #include "ui/base/l10n/l10n_util.h"
45 #include "ui/base/resource/resource_bundle.h"
46 #include "ui/gfx/image/image.h"
47
48 using extensions::BundleInstaller;
49 using extensions::Extension;
50 using extensions::Manifest;
51 using extensions::PermissionSet;
52
53 namespace {
54
55 static const int kTitleIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = {
56   0,  // The regular install prompt depends on what's being installed.
57   IDS_EXTENSION_INLINE_INSTALL_PROMPT_TITLE,
58   IDS_EXTENSION_INSTALL_PROMPT_TITLE,
59   IDS_EXTENSION_RE_ENABLE_PROMPT_TITLE,
60   IDS_EXTENSION_PERMISSIONS_PROMPT_TITLE,
61   IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE,
62   IDS_EXTENSION_POST_INSTALL_PERMISSIONS_PROMPT_TITLE,
63   IDS_EXTENSION_LAUNCH_APP_PROMPT_TITLE,
64 };
65 static const int kHeadingIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = {
66   IDS_EXTENSION_INSTALL_PROMPT_HEADING,
67   0,  // Inline installs use the extension name.
68   0,  // Heading for bundle installs depends on the bundle contents.
69   IDS_EXTENSION_RE_ENABLE_PROMPT_HEADING,
70   IDS_EXTENSION_PERMISSIONS_PROMPT_HEADING,
71   0,  // External installs use different strings for extensions/apps.
72   IDS_EXTENSION_POST_INSTALL_PERMISSIONS_PROMPT_HEADING,
73   IDS_EXTENSION_LAUNCH_APP_PROMPT_HEADING,
74 };
75 static const int kButtons[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = {
76   ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
77   ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
78   ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
79   ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
80   ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
81   ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
82   ui::DIALOG_BUTTON_CANCEL,
83   ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
84 };
85 static const int kAcceptButtonIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = {
86   IDS_EXTENSION_PROMPT_INSTALL_BUTTON,
87   IDS_EXTENSION_PROMPT_INSTALL_BUTTON,
88   IDS_EXTENSION_PROMPT_INSTALL_BUTTON,
89   IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON,
90   IDS_EXTENSION_PROMPT_PERMISSIONS_BUTTON,
91   0,  // External installs use different strings for extensions/apps.
92   IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_BUTTON,
93   IDS_EXTENSION_PROMPT_LAUNCH_BUTTON,
94 };
95 static const int kAbortButtonIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = {
96   0,  // These all use the platform's default cancel label.
97   0,
98   0,
99   0,
100   IDS_EXTENSION_PROMPT_PERMISSIONS_ABORT_BUTTON,
101   IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ABORT_BUTTON,
102   IDS_CLOSE,
103   0,  // Platform dependent cancel button.
104 };
105 static const int kPermissionsHeaderIds[
106     ExtensionInstallPrompt::NUM_PROMPT_TYPES] = {
107   IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO,
108   IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO,
109   IDS_EXTENSION_PROMPT_THESE_WILL_HAVE_ACCESS_TO,
110   IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO,
111   IDS_EXTENSION_PROMPT_WANTS_ACCESS_TO,
112   IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO,
113   IDS_EXTENSION_PROMPT_CAN_ACCESS,
114   IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO,
115 };
116 static const int kOAuthHeaderIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = {
117   IDS_EXTENSION_PROMPT_OAUTH_HEADER,
118   0,  // Inline installs don't show OAuth permissions.
119   0,  // Bundle installs don't show OAuth permissions.
120   IDS_EXTENSION_PROMPT_OAUTH_REENABLE_HEADER,
121   IDS_EXTENSION_PROMPT_OAUTH_PERMISSIONS_HEADER,
122   0,
123   0,
124   IDS_EXTENSION_PROMPT_OAUTH_HEADER,
125 };
126
127 // Size of extension icon in top left of dialog.
128 const int kIconSize = 69;
129
130 // Returns pixel size under maximal scale factor for the icon whose device
131 // independent size is |size_in_dip|
132 int GetSizeForMaxScaleFactor(int size_in_dip) {
133   return static_cast<int>(size_in_dip * gfx::ImageSkia::GetMaxSupportedScale());
134 }
135
136 // Returns bitmap for the default icon with size equal to the default icon's
137 // pixel size under maximal supported scale factor.
138 SkBitmap GetDefaultIconBitmapForMaxScaleFactor(bool is_app) {
139   const gfx::ImageSkia& image = is_app ?
140       extensions::IconsInfo::GetDefaultAppIcon() :
141       extensions::IconsInfo::GetDefaultExtensionIcon();
142   return image.GetRepresentation(
143       gfx::ImageSkia::GetMaxSupportedScale()).sk_bitmap();
144 }
145
146 // If auto confirm is enabled then posts a task to proceed with or cancel the
147 // install and returns true. Otherwise returns false.
148 bool AutoConfirmPrompt(ExtensionInstallPrompt::Delegate* delegate) {
149   const CommandLine* cmdline = CommandLine::ForCurrentProcess();
150   if (!cmdline->HasSwitch(switches::kAppsGalleryInstallAutoConfirmForTests))
151     return false;
152   std::string value = cmdline->GetSwitchValueASCII(
153       switches::kAppsGalleryInstallAutoConfirmForTests);
154
155   // We use PostTask instead of calling the delegate directly here, because in
156   // the real implementations it's highly likely the message loop will be
157   // pumping a few times before the user clicks accept or cancel.
158   if (value == "accept") {
159     base::MessageLoop::current()->PostTask(
160         FROM_HERE,
161         base::Bind(&ExtensionInstallPrompt::Delegate::InstallUIProceed,
162                    base::Unretained(delegate)));
163     return true;
164   }
165
166   if (value == "cancel") {
167     base::MessageLoop::current()->PostTask(
168         FROM_HERE,
169         base::Bind(&ExtensionInstallPrompt::Delegate::InstallUIAbort,
170                    base::Unretained(delegate),
171                    true));
172     return true;
173   }
174
175   NOTREACHED();
176   return false;
177 }
178
179 Profile* ProfileForWebContents(content::WebContents* web_contents) {
180   if (!web_contents)
181     return NULL;
182   return Profile::FromBrowserContext(web_contents->GetBrowserContext());
183 }
184
185 gfx::NativeWindow NativeWindowForWebContents(content::WebContents* contents) {
186   if (!contents)
187     return NULL;
188
189   return contents->GetView()->GetTopLevelNativeWindow();
190 }
191
192 }  // namespace
193
194 ExtensionInstallPrompt::Prompt::Prompt(PromptType type)
195     : type_(type),
196       is_showing_details_for_retained_files_(false),
197       extension_(NULL),
198       bundle_(NULL),
199       average_rating_(0.0),
200       rating_count_(0),
201       show_user_count_(false),
202       has_webstore_data_(false) {
203 }
204
205 ExtensionInstallPrompt::Prompt::~Prompt() {
206 }
207
208 void ExtensionInstallPrompt::Prompt::SetPermissions(
209     const std::vector<base::string16>& permissions) {
210   permissions_ = permissions;
211 }
212
213 void ExtensionInstallPrompt::Prompt::SetPermissionsDetails(
214     const std::vector<base::string16>& details) {
215   details_ = details;
216   is_showing_details_for_permissions_.clear();
217   for (size_t i = 0; i < details.size(); ++i)
218     is_showing_details_for_permissions_.push_back(false);
219 }
220
221 void ExtensionInstallPrompt::Prompt::SetIsShowingDetails(
222     DetailsType type,
223     size_t index,
224     bool is_showing_details) {
225   switch (type) {
226     case PERMISSIONS_DETAILS:
227       is_showing_details_for_permissions_[index] = is_showing_details;
228       break;
229     case OAUTH_DETAILS:
230       is_showing_details_for_oauth_[index] = is_showing_details;
231       break;
232     case RETAINED_FILES_DETAILS:
233       is_showing_details_for_retained_files_ = is_showing_details;
234       break;
235   }
236 }
237
238 void ExtensionInstallPrompt::Prompt::SetOAuthIssueAdvice(
239     const IssueAdviceInfo& issue_advice) {
240   is_showing_details_for_oauth_.clear();
241   for (size_t i = 0; i < issue_advice.size(); ++i)
242     is_showing_details_for_oauth_.push_back(false);
243
244   oauth_issue_advice_ = issue_advice;
245 }
246
247 void ExtensionInstallPrompt::Prompt::SetUserNameFromProfile(Profile* profile) {
248   // |profile| can be NULL in unit tests.
249   if (profile) {
250     oauth_user_name_ = base::UTF8ToUTF16(profile->GetPrefs()->GetString(
251         prefs::kGoogleServicesUsername));
252   } else {
253     oauth_user_name_.clear();
254   }
255 }
256
257 void ExtensionInstallPrompt::Prompt::SetWebstoreData(
258     const std::string& localized_user_count,
259     bool show_user_count,
260     double average_rating,
261     int rating_count) {
262   CHECK(type_ == INLINE_INSTALL_PROMPT || type_ == EXTERNAL_INSTALL_PROMPT);
263   localized_user_count_ = localized_user_count;
264   show_user_count_ = show_user_count;
265   average_rating_ = average_rating;
266   rating_count_ = rating_count;
267   has_webstore_data_ = true;
268 }
269
270 base::string16 ExtensionInstallPrompt::Prompt::GetDialogTitle() const {
271   int resource_id = kTitleIds[type_];
272
273   if (type_ == INSTALL_PROMPT) {
274     if (extension_->is_app())
275       resource_id = IDS_EXTENSION_INSTALL_APP_PROMPT_TITLE;
276     else if (extension_->is_theme())
277       resource_id = IDS_EXTENSION_INSTALL_THEME_PROMPT_TITLE;
278     else
279       resource_id = IDS_EXTENSION_INSTALL_EXTENSION_PROMPT_TITLE;
280   } else if (type_ == EXTERNAL_INSTALL_PROMPT) {
281     return l10n_util::GetStringFUTF16(
282         resource_id, base::UTF8ToUTF16(extension_->name()));
283   }
284
285   return l10n_util::GetStringUTF16(resource_id);
286 }
287
288 base::string16 ExtensionInstallPrompt::Prompt::GetHeading() const {
289   if (type_ == INLINE_INSTALL_PROMPT) {
290     return base::UTF8ToUTF16(extension_->name());
291   } else if (type_ == BUNDLE_INSTALL_PROMPT) {
292     return bundle_->GetHeadingTextFor(BundleInstaller::Item::STATE_PENDING);
293   } else if (type_ == EXTERNAL_INSTALL_PROMPT) {
294     int resource_id = -1;
295     if (extension_->is_app())
296       resource_id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_HEADING_APP;
297     else if (extension_->is_theme())
298       resource_id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_HEADING_THEME;
299     else
300       resource_id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_HEADING_EXTENSION;
301     return l10n_util::GetStringUTF16(resource_id);
302   } else {
303     return l10n_util::GetStringFUTF16(
304         kHeadingIds[type_], base::UTF8ToUTF16(extension_->name()));
305   }
306 }
307
308 int ExtensionInstallPrompt::Prompt::GetDialogButtons() const {
309   if (type_ == POST_INSTALL_PERMISSIONS_PROMPT &&
310       ShouldDisplayRevokeFilesButton()) {
311     return kButtons[type_] | ui::DIALOG_BUTTON_OK;
312   }
313
314   return kButtons[type_];
315 }
316
317 bool ExtensionInstallPrompt::Prompt::ShouldShowExplanationText() const {
318    return type_ == INSTALL_PROMPT &&
319        extension_->is_extension() && experiment_ && experiment_->text_only();
320 }
321
322 bool ExtensionInstallPrompt::Prompt::HasAcceptButtonLabel() const {
323   if (kAcceptButtonIds[type_] == 0)
324     return false;
325
326   if (type_ == POST_INSTALL_PERMISSIONS_PROMPT)
327     return ShouldDisplayRevokeFilesButton();
328
329   return true;
330 }
331
332 base::string16 ExtensionInstallPrompt::Prompt::GetAcceptButtonLabel() const {
333   if (type_ == EXTERNAL_INSTALL_PROMPT) {
334     int id = -1;
335     if (extension_->is_app())
336       id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_APP;
337     else if (extension_->is_theme())
338       id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_THEME;
339     else
340       id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION;
341     return l10n_util::GetStringUTF16(id);
342   }
343   if (ShouldShowExplanationText())
344     return experiment_->GetOkButtonText();
345   return l10n_util::GetStringUTF16(kAcceptButtonIds[type_]);
346 }
347
348 bool ExtensionInstallPrompt::Prompt::HasAbortButtonLabel() const {
349   if (ShouldShowExplanationText())
350     return true;
351   return kAbortButtonIds[type_] > 0;
352 }
353
354 base::string16 ExtensionInstallPrompt::Prompt::GetAbortButtonLabel() const {
355   CHECK(HasAbortButtonLabel());
356   if (ShouldShowExplanationText())
357     return experiment_->GetCancelButtonText();
358   return l10n_util::GetStringUTF16(kAbortButtonIds[type_]);
359 }
360
361 base::string16 ExtensionInstallPrompt::Prompt::GetPermissionsHeading() const {
362   return l10n_util::GetStringUTF16(kPermissionsHeaderIds[type_]);
363 }
364
365 base::string16 ExtensionInstallPrompt::Prompt::GetOAuthHeading() const {
366   return l10n_util::GetStringFUTF16(kOAuthHeaderIds[type_], oauth_user_name_);
367 }
368
369 base::string16 ExtensionInstallPrompt::Prompt::GetRetainedFilesHeading() const {
370   const int kRetainedFilesMessageIDs[6] = {
371       IDS_EXTENSION_PROMPT_RETAINED_FILES_DEFAULT,
372       IDS_EXTENSION_PROMPT_RETAINED_FILE_SINGULAR,
373       IDS_EXTENSION_PROMPT_RETAINED_FILES_ZERO,
374       IDS_EXTENSION_PROMPT_RETAINED_FILES_TWO,
375       IDS_EXTENSION_PROMPT_RETAINED_FILES_FEW,
376       IDS_EXTENSION_PROMPT_RETAINED_FILES_MANY,
377   };
378   std::vector<int> message_ids;
379   for (size_t i = 0; i < arraysize(kRetainedFilesMessageIDs); i++) {
380     message_ids.push_back(kRetainedFilesMessageIDs[i]);
381   }
382   return l10n_util::GetPluralStringFUTF16(message_ids, GetRetainedFileCount());
383 }
384
385 bool ExtensionInstallPrompt::Prompt::ShouldShowPermissions() const {
386   return GetPermissionCount() > 0 || type_ == POST_INSTALL_PERMISSIONS_PROMPT;
387 }
388
389 void ExtensionInstallPrompt::Prompt::AppendRatingStars(
390     StarAppender appender, void* data) const {
391   CHECK(appender);
392   CHECK(type_ == INLINE_INSTALL_PROMPT || type_ == EXTERNAL_INSTALL_PROMPT);
393   int rating_integer = floor(average_rating_);
394   double rating_fractional = average_rating_ - rating_integer;
395
396   if (rating_fractional > 0.66) {
397     rating_integer++;
398   }
399
400   if (rating_fractional < 0.33 || rating_fractional > 0.66) {
401     rating_fractional = 0;
402   }
403
404   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
405   int i;
406   for (i = 0; i < rating_integer; i++) {
407     appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_ON), data);
408   }
409   if (rating_fractional) {
410     appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_HALF_LEFT), data);
411     i++;
412   }
413   for (; i < kMaxExtensionRating; i++) {
414     appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_OFF), data);
415   }
416 }
417
418 base::string16 ExtensionInstallPrompt::Prompt::GetRatingCount() const {
419   CHECK(type_ == INLINE_INSTALL_PROMPT || type_ == EXTERNAL_INSTALL_PROMPT);
420   return l10n_util::GetStringFUTF16(IDS_EXTENSION_RATING_COUNT,
421                                     base::IntToString16(rating_count_));
422 }
423
424 base::string16 ExtensionInstallPrompt::Prompt::GetUserCount() const {
425   CHECK(type_ == INLINE_INSTALL_PROMPT || type_ == EXTERNAL_INSTALL_PROMPT);
426
427   if (show_user_count_) {
428     return l10n_util::GetStringFUTF16(IDS_EXTENSION_USER_COUNT,
429                                       base::UTF8ToUTF16(localized_user_count_));
430   }
431   return base::string16();
432 }
433
434 size_t ExtensionInstallPrompt::Prompt::GetPermissionCount() const {
435   return permissions_.size();
436 }
437
438 size_t ExtensionInstallPrompt::Prompt::GetPermissionsDetailsCount() const {
439   return details_.size();
440 }
441
442 base::string16 ExtensionInstallPrompt::Prompt::GetPermission(size_t index)
443     const {
444   CHECK_LT(index, permissions_.size());
445   return permissions_[index];
446 }
447
448 base::string16 ExtensionInstallPrompt::Prompt::GetPermissionsDetails(
449     size_t index) const {
450   CHECK_LT(index, details_.size());
451   return details_[index];
452 }
453
454 bool ExtensionInstallPrompt::Prompt::GetIsShowingDetails(
455     DetailsType type, size_t index) const {
456   switch (type) {
457     case PERMISSIONS_DETAILS:
458       CHECK_LT(index, is_showing_details_for_permissions_.size());
459       return is_showing_details_for_permissions_[index];
460     case OAUTH_DETAILS:
461       CHECK_LT(index, is_showing_details_for_oauth_.size());
462       return is_showing_details_for_oauth_[index];
463     case RETAINED_FILES_DETAILS:
464       return is_showing_details_for_retained_files_;
465   }
466   return false;
467 }
468
469 size_t ExtensionInstallPrompt::Prompt::GetOAuthIssueCount() const {
470   return oauth_issue_advice_.size();
471 }
472
473 const IssueAdviceInfoEntry& ExtensionInstallPrompt::Prompt::GetOAuthIssue(
474     size_t index) const {
475   CHECK_LT(index, oauth_issue_advice_.size());
476   return oauth_issue_advice_[index];
477 }
478
479 size_t ExtensionInstallPrompt::Prompt::GetRetainedFileCount() const {
480   return retained_files_.size();
481 }
482
483 base::string16 ExtensionInstallPrompt::Prompt::GetRetainedFile(size_t index)
484     const {
485   CHECK_LT(index, retained_files_.size());
486   return retained_files_[index].AsUTF16Unsafe();
487 }
488
489 bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeFilesButton() const {
490   return !retained_files_.empty();
491 }
492
493 ExtensionInstallPrompt::ShowParams::ShowParams(content::WebContents* contents)
494     : parent_web_contents(contents),
495       parent_window(NativeWindowForWebContents(contents)),
496       navigator(contents) {
497 }
498
499 ExtensionInstallPrompt::ShowParams::ShowParams(
500     gfx::NativeWindow window,
501     content::PageNavigator* navigator)
502     : parent_web_contents(NULL),
503       parent_window(window),
504       navigator(navigator) {
505 }
506
507 // static
508 scoped_refptr<Extension>
509     ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
510     const base::DictionaryValue* manifest,
511     int flags,
512     const std::string& id,
513     const std::string& localized_name,
514     const std::string& localized_description,
515     std::string* error) {
516   scoped_ptr<base::DictionaryValue> localized_manifest;
517   if (!localized_name.empty() || !localized_description.empty()) {
518     localized_manifest.reset(manifest->DeepCopy());
519     if (!localized_name.empty()) {
520       localized_manifest->SetString(extensions::manifest_keys::kName,
521                                     localized_name);
522     }
523     if (!localized_description.empty()) {
524       localized_manifest->SetString(extensions::manifest_keys::kDescription,
525                                     localized_description);
526     }
527   }
528
529   return Extension::Create(
530       base::FilePath(),
531       Manifest::INTERNAL,
532       localized_manifest.get() ? *localized_manifest.get() : *manifest,
533       flags,
534       id,
535       error);
536 }
537
538 ExtensionInstallPrompt::ExtensionInstallPrompt(content::WebContents* contents)
539     : OAuth2TokenService::Consumer("extensions_install"),
540       record_oauth2_grant_(false),
541       ui_loop_(base::MessageLoop::current()),
542       extension_(NULL),
543       bundle_(NULL),
544       install_ui_(ExtensionInstallUI::Create(ProfileForWebContents(contents))),
545       show_params_(contents),
546       delegate_(NULL),
547       prompt_(UNSET_PROMPT_TYPE) {
548   prompt_.SetUserNameFromProfile(install_ui_->profile());
549 }
550
551 ExtensionInstallPrompt::ExtensionInstallPrompt(
552     Profile* profile,
553     gfx::NativeWindow native_window,
554     content::PageNavigator* navigator)
555     : OAuth2TokenService::Consumer("extensions_install"),
556       record_oauth2_grant_(false),
557       ui_loop_(base::MessageLoop::current()),
558       extension_(NULL),
559       bundle_(NULL),
560       install_ui_(ExtensionInstallUI::Create(profile)),
561       show_params_(native_window, navigator),
562       delegate_(NULL),
563       prompt_(UNSET_PROMPT_TYPE) {
564   prompt_.SetUserNameFromProfile(install_ui_->profile());
565 }
566
567 ExtensionInstallPrompt::~ExtensionInstallPrompt() {
568 }
569
570 void ExtensionInstallPrompt::ConfirmBundleInstall(
571     extensions::BundleInstaller* bundle,
572     const PermissionSet* permissions) {
573   DCHECK(ui_loop_ == base::MessageLoop::current());
574   bundle_ = bundle;
575   permissions_ = permissions;
576   delegate_ = bundle;
577   prompt_.set_type(BUNDLE_INSTALL_PROMPT);
578
579   ShowConfirmation();
580 }
581
582 void ExtensionInstallPrompt::ConfirmStandaloneInstall(
583     Delegate* delegate,
584     const Extension* extension,
585     SkBitmap* icon,
586     const ExtensionInstallPrompt::Prompt& prompt) {
587   DCHECK(ui_loop_ == base::MessageLoop::current());
588   extension_ = extension;
589   permissions_ = extension->GetActivePermissions();
590   delegate_ = delegate;
591   prompt_ = prompt;
592
593   SetIcon(icon);
594   ShowConfirmation();
595 }
596
597 void ExtensionInstallPrompt::ConfirmWebstoreInstall(
598     Delegate* delegate,
599     const Extension* extension,
600     const SkBitmap* icon,
601     const ShowDialogCallback& show_dialog_callback) {
602   // SetIcon requires |extension_| to be set. ConfirmInstall will setup the
603   // remaining fields.
604   extension_ = extension;
605   SetIcon(icon);
606   ConfirmInstall(delegate, extension, show_dialog_callback);
607 }
608
609 void ExtensionInstallPrompt::ConfirmInstall(
610     Delegate* delegate,
611     const Extension* extension,
612     const ShowDialogCallback& show_dialog_callback) {
613   DCHECK(ui_loop_ == base::MessageLoop::current());
614   extension_ = extension;
615   permissions_ = extension->GetActivePermissions();
616   delegate_ = delegate;
617   prompt_.set_type(INSTALL_PROMPT);
618   show_dialog_callback_ = show_dialog_callback;
619
620   // We special-case themes to not show any confirm UI. Instead they are
621   // immediately installed, and then we show an infobar (see OnInstallSuccess)
622   // to allow the user to revert if they don't like it.
623   //
624   // We don't do this in the case where off-store extension installs are
625   // disabled because in that case, we don't show the dangerous download UI, so
626   // we need the UI confirmation.
627   if (extension->is_theme()) {
628     if (extension->from_webstore() ||
629         extensions::FeatureSwitch::easy_off_store_install()->IsEnabled()) {
630       delegate->InstallUIProceed();
631       return;
632     }
633   }
634
635   LoadImageIfNeeded();
636 }
637
638 void ExtensionInstallPrompt::ConfirmReEnable(Delegate* delegate,
639                                              const Extension* extension) {
640   DCHECK(ui_loop_ == base::MessageLoop::current());
641   extension_ = extension;
642   permissions_ = extension->GetActivePermissions();
643   delegate_ = delegate;
644   prompt_.set_type(extension->is_ephemeral() ? LAUNCH_PROMPT :
645                                                RE_ENABLE_PROMPT);
646
647   LoadImageIfNeeded();
648 }
649
650 void ExtensionInstallPrompt::ConfirmExternalInstall(
651     Delegate* delegate,
652     const Extension* extension,
653     const ShowDialogCallback& show_dialog_callback,
654     const Prompt& prompt) {
655   DCHECK(ui_loop_ == base::MessageLoop::current());
656   extension_ = extension;
657   permissions_ = extension->GetActivePermissions();
658   delegate_ = delegate;
659   prompt_ = prompt;
660   show_dialog_callback_ = show_dialog_callback;
661
662   LoadImageIfNeeded();
663 }
664
665 void ExtensionInstallPrompt::ConfirmPermissions(
666     Delegate* delegate,
667     const Extension* extension,
668     const PermissionSet* permissions) {
669   DCHECK(ui_loop_ == base::MessageLoop::current());
670   extension_ = extension;
671   permissions_ = permissions;
672   delegate_ = delegate;
673   prompt_.set_type(PERMISSIONS_PROMPT);
674
675   LoadImageIfNeeded();
676 }
677
678 void ExtensionInstallPrompt::ConfirmIssueAdvice(
679     Delegate* delegate,
680     const Extension* extension,
681     const IssueAdviceInfo& issue_advice) {
682   DCHECK(ui_loop_ == base::MessageLoop::current());
683   extension_ = extension;
684   delegate_ = delegate;
685   prompt_.set_type(PERMISSIONS_PROMPT);
686
687   record_oauth2_grant_ = true;
688   prompt_.SetOAuthIssueAdvice(issue_advice);
689
690   LoadImageIfNeeded();
691 }
692
693 void ExtensionInstallPrompt::ReviewPermissions(
694     Delegate* delegate,
695     const Extension* extension,
696     const std::vector<base::FilePath>& retained_file_paths) {
697   DCHECK(ui_loop_ == base::MessageLoop::current());
698   extension_ = extension;
699   permissions_ = extension->GetActivePermissions();
700   prompt_.set_retained_files(retained_file_paths);
701   delegate_ = delegate;
702   prompt_.set_type(POST_INSTALL_PERMISSIONS_PROMPT);
703
704   LoadImageIfNeeded();
705 }
706
707 void ExtensionInstallPrompt::OnInstallSuccess(const Extension* extension,
708                                               SkBitmap* icon) {
709   extension_ = extension;
710   SetIcon(icon);
711
712   install_ui_->OnInstallSuccess(extension, &icon_);
713 }
714
715 void ExtensionInstallPrompt::OnInstallFailure(
716     const extensions::CrxInstallerError& error) {
717   install_ui_->OnInstallFailure(error);
718 }
719
720 void ExtensionInstallPrompt::SetIcon(const SkBitmap* image) {
721   if (image)
722     icon_ = *image;
723   else
724     icon_ = SkBitmap();
725   if (icon_.empty()) {
726     // Let's set default icon bitmap whose size is equal to the default icon's
727     // pixel size under maximal supported scale factor. If the bitmap is larger
728     // than the one we need, it will be scaled down by the ui code.
729     icon_ = GetDefaultIconBitmapForMaxScaleFactor(extension_->is_app());
730   }
731 }
732
733 void ExtensionInstallPrompt::OnImageLoaded(const gfx::Image& image) {
734   SetIcon(image.IsEmpty() ? NULL : image.ToSkBitmap());
735   ShowConfirmation();
736 }
737
738 void ExtensionInstallPrompt::LoadImageIfNeeded() {
739   // Bundle install prompts do not have an icon.
740   // Also |install_ui_.profile()| can be NULL in unit tests.
741   if (!icon_.empty() || !install_ui_->profile()) {
742     ShowConfirmation();
743     return;
744   }
745
746   // Load the image asynchronously. For the response, check OnImageLoaded.
747   extensions::ExtensionResource image = extensions::IconsInfo::GetIconResource(
748       extension_,
749       extension_misc::EXTENSION_ICON_LARGE,
750       ExtensionIconSet::MATCH_BIGGER);
751   // Load the icon whose pixel size is large enough to be displayed under
752   // maximal supported scale factor. UI code will scale the icon down if needed.
753   // TODO(tbarzic): We should use IconImage here and load the required bitmap
754   //     lazily.
755   int pixel_size = GetSizeForMaxScaleFactor(kIconSize);
756   extensions::ImageLoader::Get(install_ui_->profile())->LoadImageAsync(
757       extension_, image, gfx::Size(pixel_size, pixel_size),
758       base::Bind(&ExtensionInstallPrompt::OnImageLoaded, AsWeakPtr()));
759 }
760
761 void ExtensionInstallPrompt::OnGetTokenSuccess(
762     const OAuth2TokenService::Request* request,
763     const std::string& access_token,
764     const base::Time& expiration_time) {
765   DCHECK_EQ(login_token_request_.get(), request);
766   login_token_request_.reset();
767
768   const extensions::OAuth2Info& oauth2_info =
769       extensions::OAuth2Info::GetOAuth2Info(extension_);
770
771   token_flow_.reset(new OAuth2MintTokenFlow(
772       install_ui_->profile()->GetRequestContext(),
773       this,
774       OAuth2MintTokenFlow::Parameters(
775           access_token,
776           extension_->id(),
777           oauth2_info.client_id,
778           oauth2_info.scopes,
779           OAuth2MintTokenFlow::MODE_ISSUE_ADVICE)));
780   token_flow_->Start();
781 }
782
783 void ExtensionInstallPrompt::OnGetTokenFailure(
784     const OAuth2TokenService::Request* request,
785     const GoogleServiceAuthError& error) {
786   DCHECK_EQ(login_token_request_.get(), request);
787   login_token_request_.reset();
788   ShowConfirmation();
789 }
790
791 void ExtensionInstallPrompt::OnIssueAdviceSuccess(
792     const IssueAdviceInfo& advice_info) {
793   prompt_.SetOAuthIssueAdvice(advice_info);
794   record_oauth2_grant_ = true;
795   ShowConfirmation();
796 }
797
798 void ExtensionInstallPrompt::OnMintTokenFailure(
799     const GoogleServiceAuthError& error) {
800   ShowConfirmation();
801 }
802
803 void ExtensionInstallPrompt::ShowConfirmation() {
804   if (prompt_.type() == INSTALL_PROMPT)
805     prompt_.set_experiment(ExtensionInstallPromptExperiment::Find());
806   else
807     prompt_.set_experiment(ExtensionInstallPromptExperiment::ControlGroup());
808
809   if (permissions_.get() &&
810       (!extension_ ||
811        !extensions::PermissionsData::ShouldSkipPermissionWarnings(
812            extension_))) {
813     Manifest::Type extension_type = extension_ ?
814         extension_->GetType() : Manifest::TYPE_UNKNOWN;
815     prompt_.SetPermissions(
816         extensions::PermissionMessageProvider::Get()->
817             GetWarningMessages(permissions_, extension_type));
818     prompt_.SetPermissionsDetails(
819         extensions::PermissionMessageProvider::Get()->
820             GetWarningMessagesDetails(permissions_, extension_type));
821   }
822
823   switch (prompt_.type()) {
824     case PERMISSIONS_PROMPT:
825     case RE_ENABLE_PROMPT:
826     case INLINE_INSTALL_PROMPT:
827     case EXTERNAL_INSTALL_PROMPT:
828     case INSTALL_PROMPT:
829     case LAUNCH_PROMPT:
830     case POST_INSTALL_PERMISSIONS_PROMPT: {
831       prompt_.set_extension(extension_);
832       prompt_.set_icon(gfx::Image::CreateFrom1xBitmap(icon_));
833       break;
834     }
835     case BUNDLE_INSTALL_PROMPT: {
836       prompt_.set_bundle(bundle_);
837       break;
838     }
839     default:
840       NOTREACHED() << "Unknown message";
841       return;
842   }
843
844   if (AutoConfirmPrompt(delegate_))
845     return;
846
847   if (show_dialog_callback_.is_null())
848     GetDefaultShowDialogCallback().Run(show_params_, delegate_, prompt_);
849   else
850     show_dialog_callback_.Run(show_params_, delegate_, prompt_);
851 }