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.
5 #include "chrome/browser/extensions/extension_install_prompt.h"
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_split.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "chrome/browser/extensions/bundle_installer.h"
18 #include "chrome/browser/extensions/extension_install_prompt_show_params.h"
19 #include "chrome/browser/extensions/extension_util.h"
20 #include "chrome/browser/extensions/permissions_updater.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/ui/extensions/extension_install_ui_factory.h"
23 #include "chrome/common/chrome_switches.h"
24 #include "chrome/grit/chromium_strings.h"
25 #include "chrome/grit/generated_resources.h"
26 #include "content/public/browser/web_contents.h"
27 #include "extensions/browser/extension_prefs.h"
28 #include "extensions/browser/extension_util.h"
29 #include "extensions/browser/image_loader.h"
30 #include "extensions/browser/install/extension_install_ui.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/base/ui_base_types.h"
47 #include "ui/gfx/image/image.h"
49 using extensions::BundleInstaller
;
50 using extensions::Extension
;
51 using extensions::Manifest
;
52 using extensions::PermissionSet
;
56 bool AllowWebstoreData(ExtensionInstallPrompt::PromptType type
) {
57 return type
== ExtensionInstallPrompt::INLINE_INSTALL_PROMPT
||
58 type
== ExtensionInstallPrompt::EXTERNAL_INSTALL_PROMPT
||
59 type
== ExtensionInstallPrompt::REPAIR_PROMPT
;
62 static const int kTitleIds
[ExtensionInstallPrompt::NUM_PROMPT_TYPES
] = {
63 0, // The regular install prompt depends on what's being installed.
64 IDS_EXTENSION_INLINE_INSTALL_PROMPT_TITLE
,
65 IDS_EXTENSION_INSTALL_PROMPT_TITLE
,
66 IDS_EXTENSION_RE_ENABLE_PROMPT_TITLE
,
67 IDS_EXTENSION_PERMISSIONS_PROMPT_TITLE
,
68 IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE
,
69 IDS_EXTENSION_POST_INSTALL_PERMISSIONS_PROMPT_TITLE
,
70 IDS_EXTENSION_LAUNCH_APP_PROMPT_TITLE
,
71 0, // The remote install prompt depends on what's being installed.
72 0, // The repair install prompt depends on what's being installed.
73 0, // The delegated install prompt depends on what's being installed.
75 static const int kHeadingIds
[ExtensionInstallPrompt::NUM_PROMPT_TYPES
] = {
76 IDS_EXTENSION_INSTALL_PROMPT_HEADING
,
77 0, // Inline installs use the extension name.
78 0, // Heading for bundle installs depends on the bundle contents.
79 IDS_EXTENSION_RE_ENABLE_PROMPT_HEADING
,
80 IDS_EXTENSION_PERMISSIONS_PROMPT_HEADING
,
81 0, // External installs use different strings for extensions/apps.
82 IDS_EXTENSION_POST_INSTALL_PERMISSIONS_PROMPT_HEADING
,
83 IDS_EXTENSION_LAUNCH_APP_PROMPT_HEADING
,
84 IDS_EXTENSION_REMOTE_INSTALL_PROMPT_HEADING
,
85 IDS_EXTENSION_REPAIR_PROMPT_HEADING
,
86 IDS_EXTENSION_INSTALL_PROMPT_HEADING
,
88 static const int kButtons
[ExtensionInstallPrompt::NUM_PROMPT_TYPES
] = {
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_OK
| 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_CANCEL
,
96 ui::DIALOG_BUTTON_OK
| ui::DIALOG_BUTTON_CANCEL
,
97 ui::DIALOG_BUTTON_OK
| ui::DIALOG_BUTTON_CANCEL
,
98 ui::DIALOG_BUTTON_OK
| ui::DIALOG_BUTTON_CANCEL
,
99 ui::DIALOG_BUTTON_OK
| ui::DIALOG_BUTTON_CANCEL
,
101 static const int kAcceptButtonIds
[ExtensionInstallPrompt::NUM_PROMPT_TYPES
] = {
102 IDS_EXTENSION_PROMPT_INSTALL_BUTTON
,
103 IDS_EXTENSION_PROMPT_INSTALL_BUTTON
,
104 IDS_EXTENSION_PROMPT_INSTALL_BUTTON
,
105 IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON
,
106 IDS_EXTENSION_PROMPT_PERMISSIONS_BUTTON
,
107 0, // External installs use different strings for extensions/apps.
108 0, // Different strings depending on the files and devices retained.
109 IDS_EXTENSION_PROMPT_LAUNCH_BUTTON
,
110 IDS_EXTENSION_PROMPT_REMOTE_INSTALL_BUTTON
,
111 IDS_EXTENSION_PROMPT_REPAIR_BUTTON
,
112 IDS_EXTENSION_PROMPT_PERMISSIONS_BUTTON
,
114 static const int kAbortButtonIds
[ExtensionInstallPrompt::NUM_PROMPT_TYPES
] = {
115 0, // These all use the platform's default cancel label.
119 IDS_EXTENSION_PROMPT_PERMISSIONS_ABORT_BUTTON
,
120 IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ABORT_BUTTON
,
122 0, // Platform dependent cancel button.
125 IDS_EXTENSION_PROMPT_PERMISSIONS_ABORT_BUTTON
,
128 kPermissionsHeaderIds
[ExtensionInstallPrompt::NUM_PROMPT_TYPES
] = {
129 IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO
,
130 IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO
,
131 IDS_EXTENSION_PROMPT_THESE_WILL_HAVE_ACCESS_TO
,
132 IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO
,
133 IDS_EXTENSION_PROMPT_WANTS_ACCESS_TO
,
134 IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO
,
135 IDS_EXTENSION_PROMPT_CAN_ACCESS
,
136 IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO
,
137 IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO
,
138 IDS_EXTENSION_PROMPT_CAN_ACCESS
,
139 IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO
,
142 // Returns bitmap for the default icon with size equal to the default icon's
143 // pixel size under maximal supported scale factor.
144 SkBitmap
GetDefaultIconBitmapForMaxScaleFactor(bool is_app
) {
145 const gfx::ImageSkia
& image
= is_app
?
146 extensions::util::GetDefaultAppIcon() :
147 extensions::util::GetDefaultExtensionIcon();
148 return image
.GetRepresentation(
149 gfx::ImageSkia::GetMaxSupportedScale()).sk_bitmap();
152 // If auto confirm is enabled then posts a task to proceed with or cancel the
153 // install and returns true. Otherwise returns false.
154 bool AutoConfirmPrompt(ExtensionInstallPrompt::Delegate
* delegate
) {
155 switch (ExtensionInstallPrompt::g_auto_confirm_for_tests
) {
156 case ExtensionInstallPrompt::NONE
:
158 // We use PostTask instead of calling the delegate directly here, because in
159 // the real implementations it's highly likely the message loop will be
160 // pumping a few times before the user clicks accept or cancel.
161 case ExtensionInstallPrompt::ACCEPT
:
162 base::MessageLoop::current()->PostTask(
164 base::Bind(&ExtensionInstallPrompt::Delegate::InstallUIProceed
,
165 base::Unretained(delegate
)));
167 case ExtensionInstallPrompt::CANCEL
:
168 base::MessageLoop::current()->PostTask(
170 base::Bind(&ExtensionInstallPrompt::Delegate::InstallUIAbort
,
171 base::Unretained(delegate
),
180 Profile
* ProfileForWebContents(content::WebContents
* web_contents
) {
183 return Profile::FromBrowserContext(web_contents
->GetBrowserContext());
188 ExtensionInstallPrompt::Prompt::InstallPromptPermissions::
189 InstallPromptPermissions() {
191 ExtensionInstallPrompt::Prompt::InstallPromptPermissions::
192 ~InstallPromptPermissions() {
196 ExtensionInstallPrompt::AutoConfirmForTests
197 ExtensionInstallPrompt::g_auto_confirm_for_tests
= ExtensionInstallPrompt::NONE
;
199 ExtensionInstallPrompt::PromptType
200 ExtensionInstallPrompt::g_last_prompt_type_for_tests
=
201 ExtensionInstallPrompt::UNSET_PROMPT_TYPE
;
203 // This should match the PromptType enum.
204 std::string
ExtensionInstallPrompt::PromptTypeToString(PromptType type
) {
206 case ExtensionInstallPrompt::INSTALL_PROMPT
:
207 return "INSTALL_PROMPT";
208 case ExtensionInstallPrompt::INLINE_INSTALL_PROMPT
:
209 return "INLINE_INSTALL_PROMPT";
210 case ExtensionInstallPrompt::BUNDLE_INSTALL_PROMPT
:
211 return "BUNDLE_INSTALL_PROMPT";
212 case ExtensionInstallPrompt::RE_ENABLE_PROMPT
:
213 return "RE_ENABLE_PROMPT";
214 case ExtensionInstallPrompt::PERMISSIONS_PROMPT
:
215 return "PERMISSIONS_PROMPT";
216 case ExtensionInstallPrompt::EXTERNAL_INSTALL_PROMPT
:
217 return "EXTERNAL_INSTALL_PROMPT";
218 case ExtensionInstallPrompt::POST_INSTALL_PERMISSIONS_PROMPT
:
219 return "POST_INSTALL_PERMISSIONS_PROMPT";
220 case ExtensionInstallPrompt::LAUNCH_PROMPT
:
221 return "LAUNCH_PROMPT";
222 case ExtensionInstallPrompt::REMOTE_INSTALL_PROMPT
:
223 return "REMOTE_INSTALL_PROMPT";
224 case ExtensionInstallPrompt::REPAIR_PROMPT
:
225 return "REPAIR_PROMPT";
226 case ExtensionInstallPrompt::DELEGATED_PERMISSIONS_PROMPT
:
227 return "DELEGATED_PERMISSIONS_PROMPT";
228 case ExtensionInstallPrompt::UNSET_PROMPT_TYPE
:
229 case ExtensionInstallPrompt::NUM_PROMPT_TYPES
:
235 ExtensionInstallPrompt::Prompt::Prompt(PromptType type
)
237 is_showing_details_for_retained_files_(false),
238 is_showing_details_for_retained_devices_(false),
241 average_rating_(0.0),
243 show_user_count_(false),
244 has_webstore_data_(false) {
247 ExtensionInstallPrompt::Prompt::~Prompt() {
250 void ExtensionInstallPrompt::Prompt::SetPermissions(
251 const std::vector
<base::string16
>& permissions
,
252 PermissionsType permissions_type
) {
253 GetPermissionsForType(permissions_type
).permissions
= permissions
;
256 void ExtensionInstallPrompt::Prompt::SetPermissionsDetails(
257 const std::vector
<base::string16
>& details
,
258 PermissionsType permissions_type
) {
259 InstallPromptPermissions
& install_permissions
=
260 GetPermissionsForType(permissions_type
);
262 // Add a dash to the front of each permission detail.
263 for (const auto& details_entry
: details
) {
264 if (!details_entry
.empty()) {
265 std::vector
<base::string16
> detail_lines
;
266 base::SplitString(details_entry
, base::char16('\n'), &detail_lines
);
268 std::vector
<base::string16
> detail_lines_with_bullets
;
269 for (const auto& detail_line
: detail_lines
)
270 detail_lines_with_bullets
.push_back(base::ASCIIToUTF16("- ") +
273 install_permissions
.details
.push_back(
274 JoinString(detail_lines_with_bullets
, '\n'));
276 install_permissions
.details
.push_back(details_entry
);
280 install_permissions
.is_showing_details
.clear();
281 install_permissions
.is_showing_details
.insert(
282 install_permissions
.is_showing_details
.begin(), details
.size(), false);
285 void ExtensionInstallPrompt::Prompt::SetIsShowingDetails(
288 bool is_showing_details
) {
290 case PERMISSIONS_DETAILS
:
291 prompt_permissions_
.is_showing_details
[index
] = is_showing_details
;
293 case WITHHELD_PERMISSIONS_DETAILS
:
294 withheld_prompt_permissions_
.is_showing_details
[index
] =
297 case RETAINED_FILES_DETAILS
:
298 is_showing_details_for_retained_files_
= is_showing_details
;
300 case RETAINED_DEVICES_DETAILS
:
301 is_showing_details_for_retained_devices_
= is_showing_details
;
306 void ExtensionInstallPrompt::Prompt::SetWebstoreData(
307 const std::string
& localized_user_count
,
308 bool show_user_count
,
309 double average_rating
,
311 CHECK(AllowWebstoreData(type_
));
312 localized_user_count_
= localized_user_count
;
313 show_user_count_
= show_user_count
;
314 average_rating_
= average_rating
;
315 rating_count_
= rating_count
;
316 has_webstore_data_
= true;
319 base::string16
ExtensionInstallPrompt::Prompt::GetDialogTitle() const {
320 int resource_id
= kTitleIds
[type_
];
322 if (type_
== INSTALL_PROMPT
) {
323 if (extension_
->is_app())
324 resource_id
= IDS_EXTENSION_INSTALL_APP_PROMPT_TITLE
;
325 else if (extension_
->is_theme())
326 resource_id
= IDS_EXTENSION_INSTALL_THEME_PROMPT_TITLE
;
328 resource_id
= IDS_EXTENSION_INSTALL_EXTENSION_PROMPT_TITLE
;
329 } else if (type_
== EXTERNAL_INSTALL_PROMPT
) {
330 return l10n_util::GetStringFUTF16(
331 resource_id
, base::UTF8ToUTF16(extension_
->name()));
332 } else if (type_
== REMOTE_INSTALL_PROMPT
) {
333 if (extension_
->is_app())
334 resource_id
= IDS_EXTENSION_REMOTE_INSTALL_APP_PROMPT_TITLE
;
336 resource_id
= IDS_EXTENSION_REMOTE_INSTALL_EXTENSION_PROMPT_TITLE
;
337 } else if (type_
== REPAIR_PROMPT
) {
338 if (extension_
->is_app())
339 resource_id
= IDS_EXTENSION_REPAIR_APP_PROMPT_TITLE
;
341 resource_id
= IDS_EXTENSION_REPAIR_EXTENSION_PROMPT_TITLE
;
342 } else if (type_
== DELEGATED_PERMISSIONS_PROMPT
) {
343 DCHECK(!delegated_username_
.empty());
344 if (extension_
->is_app())
345 resource_id
= IDS_EXTENSION_DELEGATED_INSTALL_APP_PROMPT_TITLE
;
347 resource_id
= IDS_EXTENSION_DELEGATED_INSTALL_EXTENSION_PROMPT_TITLE
;
348 return l10n_util::GetStringFUTF16(
349 resource_id
, base::UTF8ToUTF16(delegated_username_
));
352 return l10n_util::GetStringUTF16(resource_id
);
355 base::string16
ExtensionInstallPrompt::Prompt::GetHeading() const {
356 if (type_
== INLINE_INSTALL_PROMPT
) {
357 return base::UTF8ToUTF16(extension_
->name());
358 } else if (type_
== BUNDLE_INSTALL_PROMPT
) {
359 return bundle_
->GetHeadingTextFor(BundleInstaller::Item::STATE_PENDING
);
360 } else if (type_
== EXTERNAL_INSTALL_PROMPT
) {
361 int resource_id
= -1;
362 if (extension_
->is_app())
363 resource_id
= IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_HEADING_APP
;
364 else if (extension_
->is_theme())
365 resource_id
= IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_HEADING_THEME
;
367 resource_id
= IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_HEADING_EXTENSION
;
368 return l10n_util::GetStringUTF16(resource_id
);
370 return l10n_util::GetStringFUTF16(
371 kHeadingIds
[type_
], base::UTF8ToUTF16(extension_
->name()));
375 int ExtensionInstallPrompt::Prompt::GetDialogButtons() const {
376 if (type_
== POST_INSTALL_PERMISSIONS_PROMPT
&& ShouldDisplayRevokeButton()) {
377 return kButtons
[type_
] | ui::DIALOG_BUTTON_OK
;
380 return kButtons
[type_
];
383 bool ExtensionInstallPrompt::Prompt::ShouldShowExplanationText() const {
384 return type_
== INSTALL_PROMPT
&& extension_
->is_extension() &&
385 experiment_
.get() && experiment_
->text_only();
388 bool ExtensionInstallPrompt::Prompt::HasAcceptButtonLabel() const {
389 if (type_
== POST_INSTALL_PERMISSIONS_PROMPT
)
390 return ShouldDisplayRevokeButton();
392 if (kAcceptButtonIds
[type_
] == 0)
398 base::string16
ExtensionInstallPrompt::Prompt::GetAcceptButtonLabel() const {
399 if (type_
== EXTERNAL_INSTALL_PROMPT
) {
401 if (extension_
->is_app())
402 id
= IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_APP
;
403 else if (extension_
->is_theme())
404 id
= IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_THEME
;
406 id
= IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION
;
407 return l10n_util::GetStringUTF16(id
);
408 } else if (type_
== POST_INSTALL_PERMISSIONS_PROMPT
) {
410 if (GetRetainedFileCount() && GetRetainedDeviceCount()) {
412 IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_AND_DEVICES_BUTTON
;
413 } else if (GetRetainedFileCount()) {
414 id
= IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_BUTTON
;
416 DCHECK_LT(0U, GetRetainedDeviceCount());
417 id
= IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_DEVICES_BUTTON
;
419 return l10n_util::GetStringUTF16(id
);
421 if (ShouldShowExplanationText())
422 return experiment_
->GetOkButtonText();
423 return l10n_util::GetStringUTF16(kAcceptButtonIds
[type_
]);
426 bool ExtensionInstallPrompt::Prompt::HasAbortButtonLabel() const {
427 if (ShouldShowExplanationText())
429 return kAbortButtonIds
[type_
] > 0;
432 base::string16
ExtensionInstallPrompt::Prompt::GetAbortButtonLabel() const {
433 CHECK(HasAbortButtonLabel());
434 if (ShouldShowExplanationText())
435 return experiment_
->GetCancelButtonText();
436 return l10n_util::GetStringUTF16(kAbortButtonIds
[type_
]);
439 base::string16
ExtensionInstallPrompt::Prompt::GetPermissionsHeading(
440 PermissionsType permissions_type
) const {
441 switch (permissions_type
) {
442 case REGULAR_PERMISSIONS
:
443 return l10n_util::GetStringUTF16(kPermissionsHeaderIds
[type_
]);
444 case WITHHELD_PERMISSIONS
:
445 return l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WITHHELD
);
446 case ALL_PERMISSIONS
:
449 return base::string16();
453 base::string16
ExtensionInstallPrompt::Prompt::GetRetainedFilesHeading() const {
454 const int kRetainedFilesMessageIDs
[6] = {
455 IDS_EXTENSION_PROMPT_RETAINED_FILES_DEFAULT
,
456 IDS_EXTENSION_PROMPT_RETAINED_FILE_SINGULAR
,
457 IDS_EXTENSION_PROMPT_RETAINED_FILES_ZERO
,
458 IDS_EXTENSION_PROMPT_RETAINED_FILES_TWO
,
459 IDS_EXTENSION_PROMPT_RETAINED_FILES_FEW
,
460 IDS_EXTENSION_PROMPT_RETAINED_FILES_MANY
,
462 std::vector
<int> message_ids(
463 kRetainedFilesMessageIDs
,
464 kRetainedFilesMessageIDs
+ arraysize(kRetainedFilesMessageIDs
));
466 return l10n_util::GetPluralStringFUTF16(message_ids
, GetRetainedFileCount());
469 base::string16
ExtensionInstallPrompt::Prompt::GetRetainedDevicesHeading()
471 const int kRetainedDevicesMessageIDs
[6] = {
472 IDS_EXTENSION_PROMPT_RETAINED_DEVICES_DEFAULT
,
473 IDS_EXTENSION_PROMPT_RETAINED_DEVICE_SINGULAR
,
474 IDS_EXTENSION_PROMPT_RETAINED_DEVICES_ZERO
,
475 IDS_EXTENSION_PROMPT_RETAINED_DEVICES_TWO
,
476 IDS_EXTENSION_PROMPT_RETAINED_DEVICES_FEW
,
477 IDS_EXTENSION_PROMPT_RETAINED_DEVICES_MANY
,
479 std::vector
<int> message_ids(
480 kRetainedDevicesMessageIDs
,
481 kRetainedDevicesMessageIDs
+ arraysize(kRetainedDevicesMessageIDs
));
483 return l10n_util::GetPluralStringFUTF16(message_ids
,
484 GetRetainedDeviceCount());
487 bool ExtensionInstallPrompt::Prompt::ShouldShowPermissions() const {
488 return GetPermissionCount(ALL_PERMISSIONS
) > 0 ||
489 type_
== POST_INSTALL_PERMISSIONS_PROMPT
;
492 void ExtensionInstallPrompt::Prompt::AppendRatingStars(
493 StarAppender appender
, void* data
) const {
495 CHECK(AllowWebstoreData(type_
));
496 int rating_integer
= floor(average_rating_
);
497 double rating_fractional
= average_rating_
- rating_integer
;
499 if (rating_fractional
> 0.66) {
503 if (rating_fractional
< 0.33 || rating_fractional
> 0.66) {
504 rating_fractional
= 0;
507 ResourceBundle
& rb
= ResourceBundle::GetSharedInstance();
509 for (i
= 0; i
< rating_integer
; i
++) {
510 appender(rb
.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_ON
), data
);
512 if (rating_fractional
) {
513 appender(rb
.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_HALF_LEFT
), data
);
516 for (; i
< kMaxExtensionRating
; i
++) {
517 appender(rb
.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_OFF
), data
);
521 base::string16
ExtensionInstallPrompt::Prompt::GetRatingCount() const {
522 CHECK(AllowWebstoreData(type_
));
523 return l10n_util::GetStringFUTF16(IDS_EXTENSION_RATING_COUNT
,
524 base::IntToString16(rating_count_
));
527 base::string16
ExtensionInstallPrompt::Prompt::GetUserCount() const {
528 CHECK(AllowWebstoreData(type_
));
530 if (show_user_count_
) {
531 return l10n_util::GetStringFUTF16(IDS_EXTENSION_USER_COUNT
,
532 base::UTF8ToUTF16(localized_user_count_
));
534 return base::string16();
537 size_t ExtensionInstallPrompt::Prompt::GetPermissionCount(
538 PermissionsType permissions_type
) const {
539 switch (permissions_type
) {
540 case REGULAR_PERMISSIONS
:
541 return prompt_permissions_
.permissions
.size();
542 case WITHHELD_PERMISSIONS
:
543 return withheld_prompt_permissions_
.permissions
.size();
544 case ALL_PERMISSIONS
:
545 return prompt_permissions_
.permissions
.size() +
546 withheld_prompt_permissions_
.permissions
.size();
553 size_t ExtensionInstallPrompt::Prompt::GetPermissionsDetailsCount(
554 PermissionsType permissions_type
) const {
555 switch (permissions_type
) {
556 case REGULAR_PERMISSIONS
:
557 return prompt_permissions_
.details
.size();
558 case WITHHELD_PERMISSIONS
:
559 return withheld_prompt_permissions_
.details
.size();
560 case ALL_PERMISSIONS
:
561 return prompt_permissions_
.details
.size() +
562 withheld_prompt_permissions_
.details
.size();
569 base::string16
ExtensionInstallPrompt::Prompt::GetPermission(
571 PermissionsType permissions_type
) const {
572 const InstallPromptPermissions
& install_permissions
=
573 GetPermissionsForType(permissions_type
);
574 CHECK_LT(index
, install_permissions
.permissions
.size());
575 return install_permissions
.permissions
[index
];
578 base::string16
ExtensionInstallPrompt::Prompt::GetPermissionsDetails(
580 PermissionsType permissions_type
) const {
581 const InstallPromptPermissions
& install_permissions
=
582 GetPermissionsForType(permissions_type
);
583 CHECK_LT(index
, install_permissions
.details
.size());
584 return install_permissions
.details
[index
];
587 bool ExtensionInstallPrompt::Prompt::GetIsShowingDetails(
588 DetailsType type
, size_t index
) const {
590 case PERMISSIONS_DETAILS
:
591 CHECK_LT(index
, prompt_permissions_
.is_showing_details
.size());
592 return prompt_permissions_
.is_showing_details
[index
];
593 case WITHHELD_PERMISSIONS_DETAILS
:
594 CHECK_LT(index
, withheld_prompt_permissions_
.is_showing_details
.size());
595 return withheld_prompt_permissions_
.is_showing_details
[index
];
596 case RETAINED_FILES_DETAILS
:
597 return is_showing_details_for_retained_files_
;
598 case RETAINED_DEVICES_DETAILS
:
599 return is_showing_details_for_retained_devices_
;
604 size_t ExtensionInstallPrompt::Prompt::GetRetainedFileCount() const {
605 return retained_files_
.size();
608 base::string16
ExtensionInstallPrompt::Prompt::GetRetainedFile(size_t index
)
610 CHECK_LT(index
, retained_files_
.size());
611 return retained_files_
[index
].AsUTF16Unsafe();
614 size_t ExtensionInstallPrompt::Prompt::GetRetainedDeviceCount() const {
615 return retained_device_messages_
.size();
618 base::string16
ExtensionInstallPrompt::Prompt::GetRetainedDeviceMessageString(
619 size_t index
) const {
620 CHECK_LT(index
, retained_device_messages_
.size());
621 return retained_device_messages_
[index
];
624 bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeButton() const {
625 return !retained_files_
.empty() || !retained_device_messages_
.empty();
628 ExtensionInstallPrompt::Prompt::InstallPromptPermissions
&
629 ExtensionInstallPrompt::Prompt::GetPermissionsForType(
630 PermissionsType permissions_type
) {
631 DCHECK_NE(ALL_PERMISSIONS
, permissions_type
);
632 return permissions_type
== REGULAR_PERMISSIONS
? prompt_permissions_
633 : withheld_prompt_permissions_
;
636 const ExtensionInstallPrompt::Prompt::InstallPromptPermissions
&
637 ExtensionInstallPrompt::Prompt::GetPermissionsForType(
638 PermissionsType permissions_type
) const {
639 DCHECK_NE(ALL_PERMISSIONS
, permissions_type
);
640 return permissions_type
== REGULAR_PERMISSIONS
? prompt_permissions_
641 : withheld_prompt_permissions_
;
644 bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeFilesButton() const {
645 return !retained_files_
.empty();
649 scoped_refptr
<Extension
>
650 ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
651 const base::DictionaryValue
* manifest
,
653 const std::string
& id
,
654 const std::string
& localized_name
,
655 const std::string
& localized_description
,
656 std::string
* error
) {
657 scoped_ptr
<base::DictionaryValue
> localized_manifest
;
658 if (!localized_name
.empty() || !localized_description
.empty()) {
659 localized_manifest
.reset(manifest
->DeepCopy());
660 if (!localized_name
.empty()) {
661 localized_manifest
->SetString(extensions::manifest_keys::kName
,
664 if (!localized_description
.empty()) {
665 localized_manifest
->SetString(extensions::manifest_keys::kDescription
,
666 localized_description
);
670 return Extension::Create(
673 localized_manifest
.get() ? *localized_manifest
.get() : *manifest
,
679 ExtensionInstallPrompt::ExtensionInstallPrompt(content::WebContents
* contents
)
680 : profile_(ProfileForWebContents(contents
)),
681 ui_loop_(base::MessageLoop::current()),
684 install_ui_(extensions::CreateExtensionInstallUI(
685 ProfileForWebContents(contents
))),
686 show_params_(new ExtensionInstallPromptShowParams(contents
)),
690 ExtensionInstallPrompt::ExtensionInstallPrompt(Profile
* profile
,
691 gfx::NativeWindow native_window
)
693 ui_loop_(base::MessageLoop::current()),
696 install_ui_(extensions::CreateExtensionInstallUI(profile
)),
698 new ExtensionInstallPromptShowParams(profile
, native_window
)),
702 ExtensionInstallPrompt::~ExtensionInstallPrompt() {
705 void ExtensionInstallPrompt::ConfirmBundleInstall(
706 extensions::BundleInstaller
* bundle
,
707 const PermissionSet
* permissions
) {
708 DCHECK(ui_loop_
== base::MessageLoop::current());
710 custom_permissions_
= permissions
;
712 prompt_
= new Prompt(BUNDLE_INSTALL_PROMPT
);
717 void ExtensionInstallPrompt::ConfirmStandaloneInstall(
719 const Extension
* extension
,
721 scoped_refptr
<Prompt
> prompt
) {
722 DCHECK(ui_loop_
== base::MessageLoop::current());
723 extension_
= extension
;
724 delegate_
= delegate
;
731 void ExtensionInstallPrompt::ConfirmWebstoreInstall(
733 const Extension
* extension
,
734 const SkBitmap
* icon
,
735 const ShowDialogCallback
& show_dialog_callback
) {
736 // SetIcon requires |extension_| to be set. ConfirmInstall will setup the
738 extension_
= extension
;
740 ConfirmInstall(delegate
, extension
, show_dialog_callback
);
743 void ExtensionInstallPrompt::ConfirmInstall(
745 const Extension
* extension
,
746 const ShowDialogCallback
& show_dialog_callback
) {
747 DCHECK(ui_loop_
== base::MessageLoop::current());
748 extension_
= extension
;
749 delegate_
= delegate
;
750 prompt_
= new Prompt(INSTALL_PROMPT
);
751 show_dialog_callback_
= show_dialog_callback
;
753 // We special-case themes to not show any confirm UI. Instead they are
754 // immediately installed, and then we show an infobar (see OnInstallSuccess)
755 // to allow the user to revert if they don't like it.
757 // We don't do this in the case where off-store extension installs are
758 // disabled because in that case, we don't show the dangerous download UI, so
759 // we need the UI confirmation.
760 if (extension
->is_theme()) {
761 if (extension
->from_webstore() ||
762 extensions::FeatureSwitch::easy_off_store_install()->IsEnabled()) {
763 delegate
->InstallUIProceed();
771 void ExtensionInstallPrompt::ConfirmPermissionsForDelegatedInstall(
773 const Extension
* extension
,
774 const std::string
& delegated_username
,
775 const SkBitmap
* icon
) {
776 DCHECK(ui_loop_
== base::MessageLoop::current());
777 delegate_
= delegate
;
778 extension_
= extension
;
779 delegated_username_
= delegated_username
;
781 prompt_
= new Prompt(DELEGATED_PERMISSIONS_PROMPT
);
785 void ExtensionInstallPrompt::ConfirmReEnable(Delegate
* delegate
,
786 const Extension
* extension
) {
787 DCHECK(ui_loop_
== base::MessageLoop::current());
788 extension_
= extension
;
789 delegate_
= delegate
;
790 bool is_remote_install
=
792 extensions::ExtensionPrefs::Get(profile_
)->HasDisableReason(
793 extension
->id(), extensions::Extension::DISABLE_REMOTE_INSTALL
);
795 extensions::util::IsEphemeralApp(extension
->id(), profile_
);
797 PromptType type
= UNSET_PROMPT_TYPE
;
799 type
= LAUNCH_PROMPT
;
800 else if (is_remote_install
)
801 type
= REMOTE_INSTALL_PROMPT
;
803 type
= RE_ENABLE_PROMPT
;
804 prompt_
= new Prompt(type
);
809 void ExtensionInstallPrompt::ConfirmExternalInstall(
811 const Extension
* extension
,
812 const ShowDialogCallback
& show_dialog_callback
,
813 scoped_refptr
<Prompt
> prompt
) {
814 DCHECK(ui_loop_
== base::MessageLoop::current());
815 extension_
= extension
;
816 delegate_
= delegate
;
818 show_dialog_callback_
= show_dialog_callback
;
823 void ExtensionInstallPrompt::ConfirmPermissions(
825 const Extension
* extension
,
826 const PermissionSet
* permissions
) {
827 DCHECK(ui_loop_
== base::MessageLoop::current());
828 extension_
= extension
;
829 custom_permissions_
= permissions
;
830 delegate_
= delegate
;
831 prompt_
= new Prompt(PERMISSIONS_PROMPT
);
836 void ExtensionInstallPrompt::ReviewPermissions(
838 const Extension
* extension
,
839 const std::vector
<base::FilePath
>& retained_file_paths
,
840 const std::vector
<base::string16
>& retained_device_messages
) {
841 DCHECK(ui_loop_
== base::MessageLoop::current());
842 extension_
= extension
;
843 prompt_
= new Prompt(POST_INSTALL_PERMISSIONS_PROMPT
);
844 prompt_
->set_retained_files(retained_file_paths
);
845 prompt_
->set_retained_device_messages(retained_device_messages
);
846 delegate_
= delegate
;
851 void ExtensionInstallPrompt::OnInstallSuccess(const Extension
* extension
,
853 extension_
= extension
;
856 install_ui_
->OnInstallSuccess(extension
, &icon_
);
859 void ExtensionInstallPrompt::OnInstallFailure(
860 const extensions::CrxInstallError
& error
) {
861 install_ui_
->OnInstallFailure(error
);
864 void ExtensionInstallPrompt::SetIcon(const SkBitmap
* image
) {
870 // Let's set default icon bitmap whose size is equal to the default icon's
871 // pixel size under maximal supported scale factor. If the bitmap is larger
872 // than the one we need, it will be scaled down by the ui code.
873 icon_
= GetDefaultIconBitmapForMaxScaleFactor(extension_
->is_app());
877 void ExtensionInstallPrompt::OnImageLoaded(const gfx::Image
& image
) {
878 SetIcon(image
.IsEmpty() ? NULL
: image
.ToSkBitmap());
882 void ExtensionInstallPrompt::LoadImageIfNeeded() {
883 // Bundle install prompts do not have an icon.
884 // Also |profile_| can be NULL in unit tests.
885 if (!icon_
.empty() || !profile_
) {
890 extensions::ExtensionResource image
= extensions::IconsInfo::GetIconResource(
892 extension_misc::EXTENSION_ICON_LARGE
,
893 ExtensionIconSet::MATCH_BIGGER
);
895 // Load the image asynchronously. The response will be sent to OnImageLoaded.
896 extensions::ImageLoader
* loader
= extensions::ImageLoader::Get(profile_
);
898 std::vector
<extensions::ImageLoader::ImageRepresentation
> images_list
;
899 images_list
.push_back(extensions::ImageLoader::ImageRepresentation(
901 extensions::ImageLoader::ImageRepresentation::NEVER_RESIZE
,
903 ui::SCALE_FACTOR_100P
));
904 loader
->LoadImagesAsync(
907 base::Bind(&ExtensionInstallPrompt::OnImageLoaded
, AsWeakPtr()));
910 void ExtensionInstallPrompt::ShowConfirmation() {
911 if (prompt_
->type() == INSTALL_PROMPT
)
912 prompt_
->set_experiment(ExtensionInstallPromptExperiment::Find());
914 prompt_
->set_experiment(ExtensionInstallPromptExperiment::ControlGroup());
916 scoped_refptr
<const PermissionSet
> permissions_to_display
;
917 if (custom_permissions_
.get()) {
918 permissions_to_display
= custom_permissions_
;
919 } else if (extension_
) {
920 // Initialize permissions if they have not already been set so that
921 // withheld permissions are displayed properly in the install prompt.
922 extensions::PermissionsUpdater(
923 profile_
, extensions::PermissionsUpdater::INIT_FLAG_TRANSIENT
)
924 .InitializePermissions(extension_
);
925 permissions_to_display
=
926 extension_
->permissions_data()->active_permissions();
929 if (permissions_to_display
.get() &&
931 !extensions::PermissionsData::ShouldSkipPermissionWarnings(
932 extension_
->id()))) {
933 Manifest::Type type
=
934 extension_
? extension_
->GetType() : Manifest::TYPE_UNKNOWN
;
935 const extensions::PermissionMessageProvider
* message_provider
=
936 extensions::PermissionMessageProvider::Get();
937 prompt_
->SetPermissions(message_provider
->GetWarningMessages(
938 permissions_to_display
.get(), type
),
939 REGULAR_PERMISSIONS
);
940 prompt_
->SetPermissionsDetails(message_provider
->GetWarningMessagesDetails(
941 permissions_to_display
.get(), type
),
942 REGULAR_PERMISSIONS
);
944 scoped_refptr
<const extensions::PermissionSet
> withheld
=
945 extension_
->permissions_data()->withheld_permissions();
946 if (!withheld
->IsEmpty()) {
947 prompt_
->SetPermissions(
948 message_provider
->GetWarningMessages(withheld
.get(), type
),
949 PermissionsType::WITHHELD_PERMISSIONS
);
950 prompt_
->SetPermissionsDetails(
951 message_provider
->GetWarningMessagesDetails(withheld
.get(), type
),
952 PermissionsType::WITHHELD_PERMISSIONS
);
956 switch (prompt_
->type()) {
957 case PERMISSIONS_PROMPT
:
958 case RE_ENABLE_PROMPT
:
959 case INLINE_INSTALL_PROMPT
:
960 case EXTERNAL_INSTALL_PROMPT
:
963 case POST_INSTALL_PERMISSIONS_PROMPT
:
964 case REMOTE_INSTALL_PROMPT
:
966 case DELEGATED_PERMISSIONS_PROMPT
: {
967 prompt_
->set_extension(extension_
);
968 prompt_
->set_icon(gfx::Image::CreateFrom1xBitmap(icon_
));
969 prompt_
->set_delegated_username(delegated_username_
);
972 case BUNDLE_INSTALL_PROMPT
: {
973 prompt_
->set_bundle(bundle_
);
977 NOTREACHED() << "Unknown message";
981 g_last_prompt_type_for_tests
= prompt_
->type();
983 if (AutoConfirmPrompt(delegate_
))
986 if (show_params_
->WasParentDestroyed()) {
987 delegate_
->InstallUIAbort(false);
991 if (show_dialog_callback_
.is_null())
992 GetDefaultShowDialogCallback().Run(show_params_
.get(), delegate_
, prompt_
);
994 show_dialog_callback_
.Run(show_params_
.get(), delegate_
, prompt_
);