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