• 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/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|
GetSizeForMaxScaleFactor(int 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.
GetDefaultIconBitmapForMaxScaleFactor(bool is_app)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.
AutoConfirmPrompt(ExtensionInstallPrompt::Delegate * delegate)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 
ProfileForWebContents(content::WebContents * web_contents)179 Profile* ProfileForWebContents(content::WebContents* web_contents) {
180   if (!web_contents)
181     return NULL;
182   return Profile::FromBrowserContext(web_contents->GetBrowserContext());
183 }
184 
NativeWindowForWebContents(content::WebContents * contents)185 gfx::NativeWindow NativeWindowForWebContents(content::WebContents* contents) {
186   if (!contents)
187     return NULL;
188 
189   return contents->GetView()->GetTopLevelNativeWindow();
190 }
191 
192 }  // namespace
193 
Prompt(PromptType type)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 }
203 
~Prompt()204 ExtensionInstallPrompt::Prompt::~Prompt() {
205 }
206 
SetPermissions(const std::vector<base::string16> & permissions)207 void ExtensionInstallPrompt::Prompt::SetPermissions(
208     const std::vector<base::string16>& permissions) {
209   permissions_ = permissions;
210 }
211 
SetPermissionsDetails(const std::vector<base::string16> & details)212 void ExtensionInstallPrompt::Prompt::SetPermissionsDetails(
213     const std::vector<base::string16>& details) {
214   details_ = details;
215   is_showing_details_for_permissions_.clear();
216   for (size_t i = 0; i < details.size(); ++i)
217     is_showing_details_for_permissions_.push_back(false);
218 }
219 
SetIsShowingDetails(DetailsType type,size_t index,bool is_showing_details)220 void ExtensionInstallPrompt::Prompt::SetIsShowingDetails(
221     DetailsType type,
222     size_t index,
223     bool is_showing_details) {
224   switch (type) {
225     case PERMISSIONS_DETAILS:
226       is_showing_details_for_permissions_[index] = is_showing_details;
227       break;
228     case OAUTH_DETAILS:
229       is_showing_details_for_oauth_[index] = is_showing_details;
230       break;
231     case RETAINED_FILES_DETAILS:
232       is_showing_details_for_retained_files_ = is_showing_details;
233       break;
234   }
235 }
236 
SetOAuthIssueAdvice(const IssueAdviceInfo & issue_advice)237 void ExtensionInstallPrompt::Prompt::SetOAuthIssueAdvice(
238     const IssueAdviceInfo& issue_advice) {
239   is_showing_details_for_oauth_.clear();
240   for (size_t i = 0; i < issue_advice.size(); ++i)
241     is_showing_details_for_oauth_.push_back(false);
242 
243   oauth_issue_advice_ = issue_advice;
244 }
245 
SetUserNameFromProfile(Profile * profile)246 void ExtensionInstallPrompt::Prompt::SetUserNameFromProfile(Profile* profile) {
247   // |profile| can be NULL in unit tests.
248   if (profile) {
249     oauth_user_name_ = UTF8ToUTF16(profile->GetPrefs()->GetString(
250         prefs::kGoogleServicesUsername));
251   } else {
252     oauth_user_name_.clear();
253   }
254 }
255 
SetInlineInstallWebstoreData(const std::string & localized_user_count,bool show_user_count,double average_rating,int rating_count)256 void ExtensionInstallPrompt::Prompt::SetInlineInstallWebstoreData(
257     const std::string& localized_user_count,
258     bool show_user_count,
259     double average_rating,
260     int rating_count) {
261   CHECK_EQ(INLINE_INSTALL_PROMPT, type_);
262   localized_user_count_ = localized_user_count;
263   show_user_count_ = show_user_count;
264   average_rating_ = average_rating;
265   rating_count_ = rating_count;
266 }
267 
GetDialogTitle() const268 base::string16 ExtensionInstallPrompt::Prompt::GetDialogTitle() const {
269   int resource_id = kTitleIds[type_];
270 
271   if (type_ == INSTALL_PROMPT) {
272     if (extension_->is_app())
273       resource_id = IDS_EXTENSION_INSTALL_APP_PROMPT_TITLE;
274     else if (extension_->is_theme())
275       resource_id = IDS_EXTENSION_INSTALL_THEME_PROMPT_TITLE;
276     else
277       resource_id = IDS_EXTENSION_INSTALL_EXTENSION_PROMPT_TITLE;
278   } else if (type_ == EXTERNAL_INSTALL_PROMPT) {
279     return l10n_util::GetStringFUTF16(
280         resource_id, UTF8ToUTF16(extension_->name()));
281   }
282 
283   return l10n_util::GetStringUTF16(resource_id);
284 }
285 
GetHeading() const286 base::string16 ExtensionInstallPrompt::Prompt::GetHeading() const {
287   if (type_ == INLINE_INSTALL_PROMPT) {
288     return UTF8ToUTF16(extension_->name());
289   } else if (type_ == BUNDLE_INSTALL_PROMPT) {
290     return bundle_->GetHeadingTextFor(BundleInstaller::Item::STATE_PENDING);
291   } else if (type_ == EXTERNAL_INSTALL_PROMPT) {
292     int resource_id = -1;
293     if (extension_->is_app())
294       resource_id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_HEADING_APP;
295     else if (extension_->is_theme())
296       resource_id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_HEADING_THEME;
297     else
298       resource_id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_HEADING_EXTENSION;
299     return l10n_util::GetStringUTF16(resource_id);
300   } else {
301     return l10n_util::GetStringFUTF16(
302         kHeadingIds[type_], UTF8ToUTF16(extension_->name()));
303   }
304 }
305 
GetDialogButtons() const306 int ExtensionInstallPrompt::Prompt::GetDialogButtons() const {
307   if (type_ == POST_INSTALL_PERMISSIONS_PROMPT &&
308       ShouldDisplayRevokeFilesButton()) {
309     return kButtons[type_] | ui::DIALOG_BUTTON_OK;
310   }
311 
312   return kButtons[type_];
313 }
314 
HasAcceptButtonLabel() const315 bool ExtensionInstallPrompt::Prompt::HasAcceptButtonLabel() const {
316   if (kAcceptButtonIds[type_] == 0)
317     return false;
318 
319   if (type_ == POST_INSTALL_PERMISSIONS_PROMPT)
320     return ShouldDisplayRevokeFilesButton();
321 
322   return true;
323 }
324 
GetAcceptButtonLabel() const325 base::string16 ExtensionInstallPrompt::Prompt::GetAcceptButtonLabel() const {
326   if (type_ == EXTERNAL_INSTALL_PROMPT) {
327     int id = -1;
328     if (extension_->is_app())
329       id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_APP;
330     else if (extension_->is_theme())
331       id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_THEME;
332     else
333       id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION;
334     return l10n_util::GetStringUTF16(id);
335   }
336   return l10n_util::GetStringUTF16(kAcceptButtonIds[type_]);
337 }
338 
HasAbortButtonLabel() const339 bool ExtensionInstallPrompt::Prompt::HasAbortButtonLabel() const {
340   return kAbortButtonIds[type_] > 0;
341 }
342 
GetAbortButtonLabel() const343 base::string16 ExtensionInstallPrompt::Prompt::GetAbortButtonLabel() const {
344   CHECK(HasAbortButtonLabel());
345   return l10n_util::GetStringUTF16(kAbortButtonIds[type_]);
346 }
347 
GetPermissionsHeading() const348 base::string16 ExtensionInstallPrompt::Prompt::GetPermissionsHeading() const {
349   return l10n_util::GetStringUTF16(kPermissionsHeaderIds[type_]);
350 }
351 
GetOAuthHeading() const352 base::string16 ExtensionInstallPrompt::Prompt::GetOAuthHeading() const {
353   return l10n_util::GetStringFUTF16(kOAuthHeaderIds[type_], oauth_user_name_);
354 }
355 
GetRetainedFilesHeading() const356 base::string16 ExtensionInstallPrompt::Prompt::GetRetainedFilesHeading() const {
357   const int kRetainedFilesMessageIDs[6] = {
358       IDS_EXTENSION_PROMPT_RETAINED_FILES_DEFAULT,
359       IDS_EXTENSION_PROMPT_RETAINED_FILE_SINGULAR,
360       IDS_EXTENSION_PROMPT_RETAINED_FILES_ZERO,
361       IDS_EXTENSION_PROMPT_RETAINED_FILES_TWO,
362       IDS_EXTENSION_PROMPT_RETAINED_FILES_FEW,
363       IDS_EXTENSION_PROMPT_RETAINED_FILES_MANY,
364   };
365   std::vector<int> message_ids;
366   for (size_t i = 0; i < arraysize(kRetainedFilesMessageIDs); i++) {
367     message_ids.push_back(kRetainedFilesMessageIDs[i]);
368   }
369   return l10n_util::GetPluralStringFUTF16(message_ids, GetRetainedFileCount());
370 }
371 
ShouldShowPermissions() const372 bool ExtensionInstallPrompt::Prompt::ShouldShowPermissions() const {
373   return GetPermissionCount() > 0 || type_ == POST_INSTALL_PERMISSIONS_PROMPT;
374 }
375 
AppendRatingStars(StarAppender appender,void * data) const376 void ExtensionInstallPrompt::Prompt::AppendRatingStars(
377     StarAppender appender, void* data) const {
378   CHECK(appender);
379   CHECK_EQ(INLINE_INSTALL_PROMPT, type_);
380   int rating_integer = floor(average_rating_);
381   double rating_fractional = average_rating_ - rating_integer;
382 
383   if (rating_fractional > 0.66) {
384     rating_integer++;
385   }
386 
387   if (rating_fractional < 0.33 || rating_fractional > 0.66) {
388     rating_fractional = 0;
389   }
390 
391   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
392   int i;
393   for (i = 0; i < rating_integer; i++) {
394     appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_ON), data);
395   }
396   if (rating_fractional) {
397     appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_HALF_LEFT), data);
398     i++;
399   }
400   for (; i < kMaxExtensionRating; i++) {
401     appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_OFF), data);
402   }
403 }
404 
GetRatingCount() const405 base::string16 ExtensionInstallPrompt::Prompt::GetRatingCount() const {
406   CHECK_EQ(INLINE_INSTALL_PROMPT, type_);
407   return l10n_util::GetStringFUTF16(IDS_EXTENSION_RATING_COUNT,
408                                     base::IntToString16(rating_count_));
409 }
410 
GetUserCount() const411 base::string16 ExtensionInstallPrompt::Prompt::GetUserCount() const {
412   CHECK_EQ(INLINE_INSTALL_PROMPT, type_);
413 
414   if (show_user_count_) {
415     return l10n_util::GetStringFUTF16(IDS_EXTENSION_USER_COUNT,
416                                       base::UTF8ToUTF16(localized_user_count_));
417   }
418   return base::string16();
419 }
420 
GetPermissionCount() const421 size_t ExtensionInstallPrompt::Prompt::GetPermissionCount() const {
422   return permissions_.size();
423 }
424 
GetPermissionsDetailsCount() const425 size_t ExtensionInstallPrompt::Prompt::GetPermissionsDetailsCount() const {
426   return details_.size();
427 }
428 
GetPermission(size_t index) const429 base::string16 ExtensionInstallPrompt::Prompt::GetPermission(size_t index)
430     const {
431   CHECK_LT(index, permissions_.size());
432   return permissions_[index];
433 }
434 
GetPermissionsDetails(size_t index) const435 base::string16 ExtensionInstallPrompt::Prompt::GetPermissionsDetails(
436     size_t index) const {
437   CHECK_LT(index, details_.size());
438   return details_[index];
439 }
440 
GetIsShowingDetails(DetailsType type,size_t index) const441 bool ExtensionInstallPrompt::Prompt::GetIsShowingDetails(
442     DetailsType type, size_t index) const {
443   switch (type) {
444     case PERMISSIONS_DETAILS:
445       CHECK_LT(index, is_showing_details_for_permissions_.size());
446       return is_showing_details_for_permissions_[index];
447     case OAUTH_DETAILS:
448       CHECK_LT(index, is_showing_details_for_oauth_.size());
449       return is_showing_details_for_oauth_[index];
450     case RETAINED_FILES_DETAILS:
451       return is_showing_details_for_retained_files_;
452   }
453   return false;
454 }
455 
GetOAuthIssueCount() const456 size_t ExtensionInstallPrompt::Prompt::GetOAuthIssueCount() const {
457   return oauth_issue_advice_.size();
458 }
459 
GetOAuthIssue(size_t index) const460 const IssueAdviceInfoEntry& ExtensionInstallPrompt::Prompt::GetOAuthIssue(
461     size_t index) const {
462   CHECK_LT(index, oauth_issue_advice_.size());
463   return oauth_issue_advice_[index];
464 }
465 
GetRetainedFileCount() const466 size_t ExtensionInstallPrompt::Prompt::GetRetainedFileCount() const {
467   return retained_files_.size();
468 }
469 
GetRetainedFile(size_t index) const470 base::string16 ExtensionInstallPrompt::Prompt::GetRetainedFile(size_t index)
471     const {
472   CHECK_LT(index, retained_files_.size());
473   return retained_files_[index].AsUTF16Unsafe();
474 }
475 
ShouldDisplayRevokeFilesButton() const476 bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeFilesButton() const {
477   return !retained_files_.empty();
478 }
479 
ShowParams(content::WebContents * contents)480 ExtensionInstallPrompt::ShowParams::ShowParams(content::WebContents* contents)
481     : parent_web_contents(contents),
482       parent_window(NativeWindowForWebContents(contents)),
483       navigator(contents) {
484 }
485 
ShowParams(gfx::NativeWindow window,content::PageNavigator * navigator)486 ExtensionInstallPrompt::ShowParams::ShowParams(
487     gfx::NativeWindow window,
488     content::PageNavigator* navigator)
489     : parent_web_contents(NULL),
490       parent_window(window),
491       navigator(navigator) {
492 }
493 
494 // static
495 scoped_refptr<Extension>
GetLocalizedExtensionForDisplay(const DictionaryValue * manifest,int flags,const std::string & id,const std::string & localized_name,const std::string & localized_description,std::string * error)496     ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
497     const DictionaryValue* manifest,
498     int flags,
499     const std::string& id,
500     const std::string& localized_name,
501     const std::string& localized_description,
502     std::string* error) {
503   scoped_ptr<DictionaryValue> localized_manifest;
504   if (!localized_name.empty() || !localized_description.empty()) {
505     localized_manifest.reset(manifest->DeepCopy());
506     if (!localized_name.empty()) {
507       localized_manifest->SetString(extensions::manifest_keys::kName,
508                                     localized_name);
509     }
510     if (!localized_description.empty()) {
511       localized_manifest->SetString(extensions::manifest_keys::kDescription,
512                                     localized_description);
513     }
514   }
515 
516   return Extension::Create(
517       base::FilePath(),
518       Manifest::INTERNAL,
519       localized_manifest.get() ? *localized_manifest.get() : *manifest,
520       flags,
521       id,
522       error);
523 }
524 
ExtensionInstallPrompt(content::WebContents * contents)525 ExtensionInstallPrompt::ExtensionInstallPrompt(content::WebContents* contents)
526     : record_oauth2_grant_(false),
527       ui_loop_(base::MessageLoop::current()),
528       extension_(NULL),
529       bundle_(NULL),
530       install_ui_(ExtensionInstallUI::Create(ProfileForWebContents(contents))),
531       show_params_(contents),
532       delegate_(NULL),
533       prompt_(UNSET_PROMPT_TYPE) {
534   prompt_.SetUserNameFromProfile(install_ui_->profile());
535 }
536 
ExtensionInstallPrompt(Profile * profile,gfx::NativeWindow native_window,content::PageNavigator * navigator)537 ExtensionInstallPrompt::ExtensionInstallPrompt(
538     Profile* profile,
539     gfx::NativeWindow native_window,
540     content::PageNavigator* navigator)
541     : record_oauth2_grant_(false),
542       ui_loop_(base::MessageLoop::current()),
543       extension_(NULL),
544       bundle_(NULL),
545       install_ui_(ExtensionInstallUI::Create(profile)),
546       show_params_(native_window, navigator),
547       delegate_(NULL),
548       prompt_(UNSET_PROMPT_TYPE) {
549   prompt_.SetUserNameFromProfile(install_ui_->profile());
550 }
551 
~ExtensionInstallPrompt()552 ExtensionInstallPrompt::~ExtensionInstallPrompt() {
553 }
554 
ConfirmBundleInstall(extensions::BundleInstaller * bundle,const PermissionSet * permissions)555 void ExtensionInstallPrompt::ConfirmBundleInstall(
556     extensions::BundleInstaller* bundle,
557     const PermissionSet* permissions) {
558   DCHECK(ui_loop_ == base::MessageLoop::current());
559   bundle_ = bundle;
560   permissions_ = permissions;
561   delegate_ = bundle;
562   prompt_.set_type(BUNDLE_INSTALL_PROMPT);
563 
564   ShowConfirmation();
565 }
566 
ConfirmStandaloneInstall(Delegate * delegate,const Extension * extension,SkBitmap * icon,const ExtensionInstallPrompt::Prompt & prompt)567 void ExtensionInstallPrompt::ConfirmStandaloneInstall(
568     Delegate* delegate,
569     const Extension* extension,
570     SkBitmap* icon,
571     const ExtensionInstallPrompt::Prompt& prompt) {
572   DCHECK(ui_loop_ == base::MessageLoop::current());
573   extension_ = extension;
574   permissions_ = extension->GetActivePermissions();
575   delegate_ = delegate;
576   prompt_ = prompt;
577 
578   SetIcon(icon);
579   ShowConfirmation();
580 }
581 
ConfirmWebstoreInstall(Delegate * delegate,const Extension * extension,const SkBitmap * icon,const ShowDialogCallback & show_dialog_callback)582 void ExtensionInstallPrompt::ConfirmWebstoreInstall(
583     Delegate* delegate,
584     const Extension* extension,
585     const SkBitmap* icon,
586     const ShowDialogCallback& show_dialog_callback) {
587   // SetIcon requires |extension_| to be set. ConfirmInstall will setup the
588   // remaining fields.
589   extension_ = extension;
590   SetIcon(icon);
591   ConfirmInstall(delegate, extension, show_dialog_callback);
592 }
593 
ConfirmInstall(Delegate * delegate,const Extension * extension,const ShowDialogCallback & show_dialog_callback)594 void ExtensionInstallPrompt::ConfirmInstall(
595     Delegate* delegate,
596     const Extension* extension,
597     const ShowDialogCallback& show_dialog_callback) {
598   DCHECK(ui_loop_ == base::MessageLoop::current());
599   extension_ = extension;
600   permissions_ = extension->GetActivePermissions();
601   delegate_ = delegate;
602   prompt_.set_type(INSTALL_PROMPT);
603   show_dialog_callback_ = show_dialog_callback;
604 
605   // We special-case themes to not show any confirm UI. Instead they are
606   // immediately installed, and then we show an infobar (see OnInstallSuccess)
607   // to allow the user to revert if they don't like it.
608   //
609   // We don't do this in the case where off-store extension installs are
610   // disabled because in that case, we don't show the dangerous download UI, so
611   // we need the UI confirmation.
612   if (extension->is_theme()) {
613     if (extension->from_webstore() ||
614         extensions::FeatureSwitch::easy_off_store_install()->IsEnabled()) {
615       delegate->InstallUIProceed();
616       return;
617     }
618   }
619 
620   LoadImageIfNeeded();
621 }
622 
ConfirmReEnable(Delegate * delegate,const Extension * extension)623 void ExtensionInstallPrompt::ConfirmReEnable(Delegate* delegate,
624                                              const Extension* extension) {
625   DCHECK(ui_loop_ == base::MessageLoop::current());
626   extension_ = extension;
627   permissions_ = extension->GetActivePermissions();
628   delegate_ = delegate;
629   prompt_.set_type(RE_ENABLE_PROMPT);
630 
631   LoadImageIfNeeded();
632 }
633 
ConfirmExternalInstall(Delegate * delegate,const Extension * extension,const ShowDialogCallback & show_dialog_callback)634 void ExtensionInstallPrompt::ConfirmExternalInstall(
635     Delegate* delegate,
636     const Extension* extension,
637     const ShowDialogCallback& show_dialog_callback) {
638   DCHECK(ui_loop_ == base::MessageLoop::current());
639   extension_ = extension;
640   permissions_ = extension->GetActivePermissions();
641   delegate_ = delegate;
642   prompt_.set_type(EXTERNAL_INSTALL_PROMPT);
643   show_dialog_callback_ = show_dialog_callback;
644 
645   LoadImageIfNeeded();
646 }
647 
ConfirmPermissions(Delegate * delegate,const Extension * extension,const PermissionSet * permissions)648 void ExtensionInstallPrompt::ConfirmPermissions(
649     Delegate* delegate,
650     const Extension* extension,
651     const PermissionSet* permissions) {
652   DCHECK(ui_loop_ == base::MessageLoop::current());
653   extension_ = extension;
654   permissions_ = permissions;
655   delegate_ = delegate;
656   prompt_.set_type(PERMISSIONS_PROMPT);
657 
658   LoadImageIfNeeded();
659 }
660 
ConfirmIssueAdvice(Delegate * delegate,const Extension * extension,const IssueAdviceInfo & issue_advice)661 void ExtensionInstallPrompt::ConfirmIssueAdvice(
662     Delegate* delegate,
663     const Extension* extension,
664     const IssueAdviceInfo& issue_advice) {
665   DCHECK(ui_loop_ == base::MessageLoop::current());
666   extension_ = extension;
667   delegate_ = delegate;
668   prompt_.set_type(PERMISSIONS_PROMPT);
669 
670   record_oauth2_grant_ = true;
671   prompt_.SetOAuthIssueAdvice(issue_advice);
672 
673   LoadImageIfNeeded();
674 }
675 
ReviewPermissions(Delegate * delegate,const Extension * extension,const std::vector<base::FilePath> & retained_file_paths)676 void ExtensionInstallPrompt::ReviewPermissions(
677     Delegate* delegate,
678     const Extension* extension,
679     const std::vector<base::FilePath>& retained_file_paths) {
680   DCHECK(ui_loop_ == base::MessageLoop::current());
681   extension_ = extension;
682   permissions_ = extension->GetActivePermissions();
683   prompt_.set_retained_files(retained_file_paths);
684   delegate_ = delegate;
685   prompt_.set_type(POST_INSTALL_PERMISSIONS_PROMPT);
686 
687   LoadImageIfNeeded();
688 }
689 
OnInstallSuccess(const Extension * extension,SkBitmap * icon)690 void ExtensionInstallPrompt::OnInstallSuccess(const Extension* extension,
691                                               SkBitmap* icon) {
692   extension_ = extension;
693   SetIcon(icon);
694 
695   install_ui_->OnInstallSuccess(extension, &icon_);
696 }
697 
OnInstallFailure(const extensions::CrxInstallerError & error)698 void ExtensionInstallPrompt::OnInstallFailure(
699     const extensions::CrxInstallerError& error) {
700   install_ui_->OnInstallFailure(error);
701 }
702 
SetIcon(const SkBitmap * image)703 void ExtensionInstallPrompt::SetIcon(const SkBitmap* image) {
704   if (image)
705     icon_ = *image;
706   else
707     icon_ = SkBitmap();
708   if (icon_.empty()) {
709     // Let's set default icon bitmap whose size is equal to the default icon's
710     // pixel size under maximal supported scale factor. If the bitmap is larger
711     // than the one we need, it will be scaled down by the ui code.
712     icon_ = GetDefaultIconBitmapForMaxScaleFactor(extension_->is_app());
713   }
714 }
715 
OnImageLoaded(const gfx::Image & image)716 void ExtensionInstallPrompt::OnImageLoaded(const gfx::Image& image) {
717   SetIcon(image.IsEmpty() ? NULL : image.ToSkBitmap());
718   ShowConfirmation();
719 }
720 
LoadImageIfNeeded()721 void ExtensionInstallPrompt::LoadImageIfNeeded() {
722   // Bundle install prompts do not have an icon.
723   // Also |install_ui_.profile()| can be NULL in unit tests.
724   if (!icon_.empty() || !install_ui_->profile()) {
725     ShowConfirmation();
726     return;
727   }
728 
729   // Load the image asynchronously. For the response, check OnImageLoaded.
730   extensions::ExtensionResource image = extensions::IconsInfo::GetIconResource(
731       extension_,
732       extension_misc::EXTENSION_ICON_LARGE,
733       ExtensionIconSet::MATCH_BIGGER);
734   // Load the icon whose pixel size is large enough to be displayed under
735   // maximal supported scale factor. UI code will scale the icon down if needed.
736   // TODO(tbarzic): We should use IconImage here and load the required bitmap
737   //     lazily.
738   int pixel_size = GetSizeForMaxScaleFactor(kIconSize);
739   extensions::ImageLoader::Get(install_ui_->profile())->LoadImageAsync(
740       extension_, image, gfx::Size(pixel_size, pixel_size),
741       base::Bind(&ExtensionInstallPrompt::OnImageLoaded, AsWeakPtr()));
742 }
743 
OnGetTokenSuccess(const OAuth2TokenService::Request * request,const std::string & access_token,const base::Time & expiration_time)744 void ExtensionInstallPrompt::OnGetTokenSuccess(
745     const OAuth2TokenService::Request* request,
746     const std::string& access_token,
747     const base::Time& expiration_time) {
748   DCHECK_EQ(login_token_request_.get(), request);
749   login_token_request_.reset();
750 
751   const extensions::OAuth2Info& oauth2_info =
752       extensions::OAuth2Info::GetOAuth2Info(extension_);
753 
754   token_flow_.reset(new OAuth2MintTokenFlow(
755       install_ui_->profile()->GetRequestContext(),
756       this,
757       OAuth2MintTokenFlow::Parameters(
758           access_token,
759           extension_->id(),
760           oauth2_info.client_id,
761           oauth2_info.scopes,
762           OAuth2MintTokenFlow::MODE_ISSUE_ADVICE)));
763   token_flow_->Start();
764 }
765 
OnGetTokenFailure(const OAuth2TokenService::Request * request,const GoogleServiceAuthError & error)766 void ExtensionInstallPrompt::OnGetTokenFailure(
767     const OAuth2TokenService::Request* request,
768     const GoogleServiceAuthError& error) {
769   DCHECK_EQ(login_token_request_.get(), request);
770   login_token_request_.reset();
771   ShowConfirmation();
772 }
773 
OnIssueAdviceSuccess(const IssueAdviceInfo & advice_info)774 void ExtensionInstallPrompt::OnIssueAdviceSuccess(
775     const IssueAdviceInfo& advice_info) {
776   prompt_.SetOAuthIssueAdvice(advice_info);
777   record_oauth2_grant_ = true;
778   ShowConfirmation();
779 }
780 
OnMintTokenFailure(const GoogleServiceAuthError & error)781 void ExtensionInstallPrompt::OnMintTokenFailure(
782     const GoogleServiceAuthError& error) {
783   ShowConfirmation();
784 }
785 
ShowConfirmation()786 void ExtensionInstallPrompt::ShowConfirmation() {
787   if (permissions_.get() &&
788       (!extension_ ||
789        !extensions::PermissionsData::ShouldSkipPermissionWarnings(
790            extension_))) {
791     Manifest::Type extension_type = extension_ ?
792         extension_->GetType() : Manifest::TYPE_UNKNOWN;
793     prompt_.SetPermissions(
794         extensions::PermissionMessageProvider::Get()->
795             GetWarningMessages(permissions_, extension_type));
796     prompt_.SetPermissionsDetails(
797         extensions::PermissionMessageProvider::Get()->
798             GetWarningMessagesDetails(permissions_, extension_type));
799   }
800 
801   switch (prompt_.type()) {
802     case PERMISSIONS_PROMPT:
803     case RE_ENABLE_PROMPT:
804     case INLINE_INSTALL_PROMPT:
805     case EXTERNAL_INSTALL_PROMPT:
806     case INSTALL_PROMPT:
807     case LAUNCH_PROMPT:
808     case POST_INSTALL_PERMISSIONS_PROMPT: {
809       prompt_.set_extension(extension_);
810       prompt_.set_icon(gfx::Image::CreateFrom1xBitmap(icon_));
811       break;
812     }
813     case BUNDLE_INSTALL_PROMPT: {
814       prompt_.set_bundle(bundle_);
815       break;
816     }
817     default:
818       NOTREACHED() << "Unknown message";
819       return;
820   }
821 
822   if (AutoConfirmPrompt(delegate_))
823     return;
824 
825   if (show_dialog_callback_.is_null())
826     GetDefaultShowDialogCallback().Run(show_params_, delegate_, prompt_);
827   else
828     show_dialog_callback_.Run(show_params_, delegate_, prompt_);
829 }
830