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_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_util.h"
18 #include "chrome/browser/extensions/permissions_updater.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/ui/extensions/extension_install_ui_factory.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "chrome/common/pref_names.h"
23 #include "chrome/grit/chromium_strings.h"
24 #include "chrome/grit/generated_resources.h"
25 #include "content/public/browser/web_contents.h"
26 #include "extensions/browser/extension_prefs.h"
27 #include "extensions/browser/extension_util.h"
28 #include "extensions/browser/image_loader.h"
29 #include "extensions/browser/install/extension_install_ui.h"
30 #include "extensions/common/constants.h"
31 #include "extensions/common/extension.h"
32 #include "extensions/common/extension_icon_set.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/manifest_handlers/icons_handler.h"
38 #include "extensions/common/permissions/permission_message_provider.h"
39 #include "extensions/common/permissions/permission_set.h"
40 #include "extensions/common/permissions/permissions_data.h"
41 #include "extensions/common/url_pattern.h"
42 #include "grit/theme_resources.h"
43 #include "ui/base/l10n/l10n_util.h"
44 #include "ui/base/resource/resource_bundle.h"
45 #include "ui/base/ui_base_types.h"
46 #include "ui/gfx/image/image.h"
48 using extensions::BundleInstaller
;
49 using extensions::Extension
;
50 using extensions::Manifest
;
51 using extensions::PermissionSet
;
55 bool AllowWebstoreData(ExtensionInstallPrompt::PromptType type
) {
56 return type
== ExtensionInstallPrompt::INLINE_INSTALL_PROMPT
||
57 type
== ExtensionInstallPrompt::EXTERNAL_INSTALL_PROMPT
||
58 type
== ExtensionInstallPrompt::REPAIR_PROMPT
;
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.
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
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
,
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 0, // Different strings depending on the files and devices retained.
105 IDS_EXTENSION_PROMPT_LAUNCH_BUTTON
,
106 IDS_EXTENSION_PROMPT_REMOTE_INSTALL_BUTTON
,
107 IDS_EXTENSION_PROMPT_REPAIR_BUTTON
,
109 static const int kAbortButtonIds
[ExtensionInstallPrompt::NUM_PROMPT_TYPES
] = {
110 0, // These all use the platform's default cancel label.
114 IDS_EXTENSION_PROMPT_PERMISSIONS_ABORT_BUTTON
,
115 IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ABORT_BUTTON
,
117 0, // Platform dependent cancel button.
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
,
135 // Returns bitmap for the default icon with size equal to the default icon's
136 // pixel size under maximal supported scale factor.
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();
145 // If auto confirm is enabled then posts a task to proceed with or cancel the
146 // install and returns true. Otherwise returns false.
147 bool AutoConfirmPrompt(ExtensionInstallPrompt::Delegate
* delegate
) {
148 switch (ExtensionInstallPrompt::g_auto_confirm_for_tests
) {
149 case ExtensionInstallPrompt::NONE
:
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(
157 base::Bind(&ExtensionInstallPrompt::Delegate::InstallUIProceed
,
158 base::Unretained(delegate
)));
160 case ExtensionInstallPrompt::CANCEL
:
161 base::MessageLoop::current()->PostTask(
163 base::Bind(&ExtensionInstallPrompt::Delegate::InstallUIAbort
,
164 base::Unretained(delegate
),
173 Profile
* ProfileForWebContents(content::WebContents
* web_contents
) {
176 return Profile::FromBrowserContext(web_contents
->GetBrowserContext());
179 gfx::NativeWindow
NativeWindowForWebContents(content::WebContents
* contents
) {
183 return contents
->GetTopLevelNativeWindow();
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 // This should match the PromptType enum.
200 std::string
ExtensionInstallPrompt::PromptTypeToString(PromptType 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
:
229 ExtensionInstallPrompt::Prompt::Prompt(PromptType type
)
231 is_showing_details_for_retained_files_(false),
232 is_showing_details_for_retained_devices_(false),
235 average_rating_(0.0),
237 show_user_count_(false),
238 has_webstore_data_(false) {
241 ExtensionInstallPrompt::Prompt::~Prompt() {
244 void ExtensionInstallPrompt::Prompt::SetPermissions(
245 const std::vector
<base::string16
>& permissions
,
246 PermissionsType permissions_type
) {
247 GetPermissionsForType(permissions_type
).permissions
= permissions
;
250 void ExtensionInstallPrompt::Prompt::SetPermissionsDetails(
251 const std::vector
<base::string16
>& details
,
252 PermissionsType permissions_type
) {
253 InstallPromptPermissions
& install_permissions
=
254 GetPermissionsForType(permissions_type
);
255 install_permissions
.details
= details
;
256 install_permissions
.is_showing_details
.clear();
257 install_permissions
.is_showing_details
.insert(
258 install_permissions
.is_showing_details
.begin(), details
.size(), false);
261 void ExtensionInstallPrompt::Prompt::SetIsShowingDetails(
264 bool is_showing_details
) {
266 case PERMISSIONS_DETAILS
:
267 prompt_permissions_
.is_showing_details
[index
] = is_showing_details
;
269 case WITHHELD_PERMISSIONS_DETAILS
:
270 withheld_prompt_permissions_
.is_showing_details
[index
] =
273 case RETAINED_FILES_DETAILS
:
274 is_showing_details_for_retained_files_
= is_showing_details
;
276 case RETAINED_DEVICES_DETAILS
:
277 is_showing_details_for_retained_devices_
= is_showing_details
;
282 void ExtensionInstallPrompt::Prompt::SetWebstoreData(
283 const std::string
& localized_user_count
,
284 bool show_user_count
,
285 double average_rating
,
287 CHECK(AllowWebstoreData(type_
));
288 localized_user_count_
= localized_user_count
;
289 show_user_count_
= show_user_count
;
290 average_rating_
= average_rating
;
291 rating_count_
= rating_count
;
292 has_webstore_data_
= true;
295 base::string16
ExtensionInstallPrompt::Prompt::GetDialogTitle() const {
296 int resource_id
= kTitleIds
[type_
];
298 if (type_
== INSTALL_PROMPT
) {
299 if (extension_
->is_app())
300 resource_id
= IDS_EXTENSION_INSTALL_APP_PROMPT_TITLE
;
301 else if (extension_
->is_theme())
302 resource_id
= IDS_EXTENSION_INSTALL_THEME_PROMPT_TITLE
;
304 resource_id
= IDS_EXTENSION_INSTALL_EXTENSION_PROMPT_TITLE
;
305 } else if (type_
== EXTERNAL_INSTALL_PROMPT
) {
306 return l10n_util::GetStringFUTF16(
307 resource_id
, base::UTF8ToUTF16(extension_
->name()));
308 } else if (type_
== REMOTE_INSTALL_PROMPT
) {
309 if (extension_
->is_app())
310 resource_id
= IDS_EXTENSION_REMOTE_INSTALL_APP_PROMPT_TITLE
;
312 resource_id
= IDS_EXTENSION_REMOTE_INSTALL_EXTENSION_PROMPT_TITLE
;
313 } else if (type_
== REPAIR_PROMPT
) {
314 if (extension_
->is_app())
315 resource_id
= IDS_EXTENSION_REPAIR_APP_PROMPT_TITLE
;
317 resource_id
= IDS_EXTENSION_REPAIR_EXTENSION_PROMPT_TITLE
;
320 return l10n_util::GetStringUTF16(resource_id
);
323 base::string16
ExtensionInstallPrompt::Prompt::GetHeading() const {
324 if (type_
== INLINE_INSTALL_PROMPT
) {
325 return base::UTF8ToUTF16(extension_
->name());
326 } else if (type_
== BUNDLE_INSTALL_PROMPT
) {
327 return bundle_
->GetHeadingTextFor(BundleInstaller::Item::STATE_PENDING
);
328 } else if (type_
== EXTERNAL_INSTALL_PROMPT
) {
329 int resource_id
= -1;
330 if (extension_
->is_app())
331 resource_id
= IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_HEADING_APP
;
332 else if (extension_
->is_theme())
333 resource_id
= IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_HEADING_THEME
;
335 resource_id
= IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_HEADING_EXTENSION
;
336 return l10n_util::GetStringUTF16(resource_id
);
338 return l10n_util::GetStringFUTF16(
339 kHeadingIds
[type_
], base::UTF8ToUTF16(extension_
->name()));
343 int ExtensionInstallPrompt::Prompt::GetDialogButtons() const {
344 if (type_
== POST_INSTALL_PERMISSIONS_PROMPT
&& ShouldDisplayRevokeButton()) {
345 return kButtons
[type_
] | ui::DIALOG_BUTTON_OK
;
348 return kButtons
[type_
];
351 bool ExtensionInstallPrompt::Prompt::ShouldShowExplanationText() const {
352 return type_
== INSTALL_PROMPT
&& extension_
->is_extension() &&
353 experiment_
.get() && experiment_
->text_only();
356 bool ExtensionInstallPrompt::Prompt::HasAcceptButtonLabel() const {
357 if (type_
== POST_INSTALL_PERMISSIONS_PROMPT
)
358 return ShouldDisplayRevokeButton();
360 if (kAcceptButtonIds
[type_
] == 0)
366 base::string16
ExtensionInstallPrompt::Prompt::GetAcceptButtonLabel() const {
367 if (type_
== EXTERNAL_INSTALL_PROMPT
) {
369 if (extension_
->is_app())
370 id
= IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_APP
;
371 else if (extension_
->is_theme())
372 id
= IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_THEME
;
374 id
= IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION
;
375 return l10n_util::GetStringUTF16(id
);
376 } else if (type_
== POST_INSTALL_PERMISSIONS_PROMPT
) {
378 if (GetRetainedFileCount() && GetRetainedDeviceCount()) {
380 IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_AND_DEVICES_BUTTON
;
381 } else if (GetRetainedFileCount()) {
382 id
= IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_BUTTON
;
384 DCHECK_LT(0U, GetRetainedDeviceCount());
385 id
= IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_DEVICES_BUTTON
;
387 return l10n_util::GetStringUTF16(id
);
389 if (ShouldShowExplanationText())
390 return experiment_
->GetOkButtonText();
391 return l10n_util::GetStringUTF16(kAcceptButtonIds
[type_
]);
394 bool ExtensionInstallPrompt::Prompt::HasAbortButtonLabel() const {
395 if (ShouldShowExplanationText())
397 return kAbortButtonIds
[type_
] > 0;
400 base::string16
ExtensionInstallPrompt::Prompt::GetAbortButtonLabel() const {
401 CHECK(HasAbortButtonLabel());
402 if (ShouldShowExplanationText())
403 return experiment_
->GetCancelButtonText();
404 return l10n_util::GetStringUTF16(kAbortButtonIds
[type_
]);
407 base::string16
ExtensionInstallPrompt::Prompt::GetPermissionsHeading(
408 PermissionsType permissions_type
) const {
409 switch (permissions_type
) {
410 case REGULAR_PERMISSIONS
:
411 return l10n_util::GetStringUTF16(kPermissionsHeaderIds
[type_
]);
412 case WITHHELD_PERMISSIONS
:
413 return l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WITHHELD
);
414 case ALL_PERMISSIONS
:
417 return base::string16();
421 base::string16
ExtensionInstallPrompt::Prompt::GetRetainedFilesHeading() const {
422 const int kRetainedFilesMessageIDs
[6] = {
423 IDS_EXTENSION_PROMPT_RETAINED_FILES_DEFAULT
,
424 IDS_EXTENSION_PROMPT_RETAINED_FILE_SINGULAR
,
425 IDS_EXTENSION_PROMPT_RETAINED_FILES_ZERO
,
426 IDS_EXTENSION_PROMPT_RETAINED_FILES_TWO
,
427 IDS_EXTENSION_PROMPT_RETAINED_FILES_FEW
,
428 IDS_EXTENSION_PROMPT_RETAINED_FILES_MANY
,
430 std::vector
<int> message_ids(
431 kRetainedFilesMessageIDs
,
432 kRetainedFilesMessageIDs
+ arraysize(kRetainedFilesMessageIDs
));
434 return l10n_util::GetPluralStringFUTF16(message_ids
, GetRetainedFileCount());
437 base::string16
ExtensionInstallPrompt::Prompt::GetRetainedDevicesHeading()
439 const int kRetainedDevicesMessageIDs
[6] = {
440 IDS_EXTENSION_PROMPT_RETAINED_DEVICES_DEFAULT
,
441 IDS_EXTENSION_PROMPT_RETAINED_DEVICE_SINGULAR
,
442 IDS_EXTENSION_PROMPT_RETAINED_DEVICES_ZERO
,
443 IDS_EXTENSION_PROMPT_RETAINED_DEVICES_TWO
,
444 IDS_EXTENSION_PROMPT_RETAINED_DEVICES_FEW
,
445 IDS_EXTENSION_PROMPT_RETAINED_DEVICES_MANY
,
447 std::vector
<int> message_ids(
448 kRetainedDevicesMessageIDs
,
449 kRetainedDevicesMessageIDs
+ arraysize(kRetainedDevicesMessageIDs
));
451 return l10n_util::GetPluralStringFUTF16(message_ids
,
452 GetRetainedDeviceCount());
455 bool ExtensionInstallPrompt::Prompt::ShouldShowPermissions() const {
456 return GetPermissionCount(ALL_PERMISSIONS
) > 0 ||
457 type_
== POST_INSTALL_PERMISSIONS_PROMPT
;
460 void ExtensionInstallPrompt::Prompt::AppendRatingStars(
461 StarAppender appender
, void* data
) const {
463 CHECK(AllowWebstoreData(type_
));
464 int rating_integer
= floor(average_rating_
);
465 double rating_fractional
= average_rating_
- rating_integer
;
467 if (rating_fractional
> 0.66) {
471 if (rating_fractional
< 0.33 || rating_fractional
> 0.66) {
472 rating_fractional
= 0;
475 ResourceBundle
& rb
= ResourceBundle::GetSharedInstance();
477 for (i
= 0; i
< rating_integer
; i
++) {
478 appender(rb
.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_ON
), data
);
480 if (rating_fractional
) {
481 appender(rb
.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_HALF_LEFT
), data
);
484 for (; i
< kMaxExtensionRating
; i
++) {
485 appender(rb
.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_OFF
), data
);
489 base::string16
ExtensionInstallPrompt::Prompt::GetRatingCount() const {
490 CHECK(AllowWebstoreData(type_
));
491 return l10n_util::GetStringFUTF16(IDS_EXTENSION_RATING_COUNT
,
492 base::IntToString16(rating_count_
));
495 base::string16
ExtensionInstallPrompt::Prompt::GetUserCount() const {
496 CHECK(AllowWebstoreData(type_
));
498 if (show_user_count_
) {
499 return l10n_util::GetStringFUTF16(IDS_EXTENSION_USER_COUNT
,
500 base::UTF8ToUTF16(localized_user_count_
));
502 return base::string16();
505 size_t ExtensionInstallPrompt::Prompt::GetPermissionCount(
506 PermissionsType permissions_type
) const {
507 switch (permissions_type
) {
508 case REGULAR_PERMISSIONS
:
509 return prompt_permissions_
.permissions
.size();
510 case WITHHELD_PERMISSIONS
:
511 return withheld_prompt_permissions_
.permissions
.size();
512 case ALL_PERMISSIONS
:
513 return prompt_permissions_
.permissions
.size() +
514 withheld_prompt_permissions_
.permissions
.size();
521 size_t ExtensionInstallPrompt::Prompt::GetPermissionsDetailsCount(
522 PermissionsType permissions_type
) const {
523 switch (permissions_type
) {
524 case REGULAR_PERMISSIONS
:
525 return prompt_permissions_
.details
.size();
526 case WITHHELD_PERMISSIONS
:
527 return withheld_prompt_permissions_
.details
.size();
528 case ALL_PERMISSIONS
:
529 return prompt_permissions_
.details
.size() +
530 withheld_prompt_permissions_
.details
.size();
537 base::string16
ExtensionInstallPrompt::Prompt::GetPermission(
539 PermissionsType permissions_type
) const {
540 const InstallPromptPermissions
& install_permissions
=
541 GetPermissionsForType(permissions_type
);
542 CHECK_LT(index
, install_permissions
.permissions
.size());
543 return install_permissions
.permissions
[index
];
546 base::string16
ExtensionInstallPrompt::Prompt::GetPermissionsDetails(
548 PermissionsType permissions_type
) const {
549 const InstallPromptPermissions
& install_permissions
=
550 GetPermissionsForType(permissions_type
);
551 CHECK_LT(index
, install_permissions
.details
.size());
552 return install_permissions
.details
[index
];
555 bool ExtensionInstallPrompt::Prompt::GetIsShowingDetails(
556 DetailsType type
, size_t index
) const {
558 case PERMISSIONS_DETAILS
:
559 CHECK_LT(index
, prompt_permissions_
.is_showing_details
.size());
560 return prompt_permissions_
.is_showing_details
[index
];
561 case WITHHELD_PERMISSIONS_DETAILS
:
562 CHECK_LT(index
, withheld_prompt_permissions_
.is_showing_details
.size());
563 return withheld_prompt_permissions_
.is_showing_details
[index
];
564 case RETAINED_FILES_DETAILS
:
565 return is_showing_details_for_retained_files_
;
566 case RETAINED_DEVICES_DETAILS
:
567 return is_showing_details_for_retained_devices_
;
572 size_t ExtensionInstallPrompt::Prompt::GetRetainedFileCount() const {
573 return retained_files_
.size();
576 base::string16
ExtensionInstallPrompt::Prompt::GetRetainedFile(size_t index
)
578 CHECK_LT(index
, retained_files_
.size());
579 return retained_files_
[index
].AsUTF16Unsafe();
582 size_t ExtensionInstallPrompt::Prompt::GetRetainedDeviceCount() const {
583 return retained_device_messages_
.size();
586 base::string16
ExtensionInstallPrompt::Prompt::GetRetainedDeviceMessageString(
587 size_t index
) const {
588 CHECK_LT(index
, retained_device_messages_
.size());
589 return retained_device_messages_
[index
];
592 bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeButton() const {
593 return !retained_files_
.empty() || !retained_device_messages_
.empty();
596 ExtensionInstallPrompt::Prompt::InstallPromptPermissions
&
597 ExtensionInstallPrompt::Prompt::GetPermissionsForType(
598 PermissionsType permissions_type
) {
599 DCHECK_NE(ALL_PERMISSIONS
, permissions_type
);
600 return permissions_type
== REGULAR_PERMISSIONS
? prompt_permissions_
601 : withheld_prompt_permissions_
;
604 const ExtensionInstallPrompt::Prompt::InstallPromptPermissions
&
605 ExtensionInstallPrompt::Prompt::GetPermissionsForType(
606 PermissionsType permissions_type
) const {
607 DCHECK_NE(ALL_PERMISSIONS
, permissions_type
);
608 return permissions_type
== REGULAR_PERMISSIONS
? prompt_permissions_
609 : withheld_prompt_permissions_
;
612 bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeFilesButton() const {
613 return !retained_files_
.empty();
616 ExtensionInstallPrompt::ShowParams::ShowParams(content::WebContents
* contents
)
617 : profile(ProfileForWebContents(contents
)),
618 parent_web_contents(contents
),
619 parent_window(NativeWindowForWebContents(contents
)) {
622 ExtensionInstallPrompt::ShowParams::ShowParams(Profile
* profile
,
623 gfx::NativeWindow window
)
625 parent_web_contents(NULL
),
626 parent_window(window
) {
630 scoped_refptr
<Extension
>
631 ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
632 const base::DictionaryValue
* manifest
,
634 const std::string
& id
,
635 const std::string
& localized_name
,
636 const std::string
& localized_description
,
637 std::string
* error
) {
638 scoped_ptr
<base::DictionaryValue
> localized_manifest
;
639 if (!localized_name
.empty() || !localized_description
.empty()) {
640 localized_manifest
.reset(manifest
->DeepCopy());
641 if (!localized_name
.empty()) {
642 localized_manifest
->SetString(extensions::manifest_keys::kName
,
645 if (!localized_description
.empty()) {
646 localized_manifest
->SetString(extensions::manifest_keys::kDescription
,
647 localized_description
);
651 return Extension::Create(
654 localized_manifest
.get() ? *localized_manifest
.get() : *manifest
,
660 ExtensionInstallPrompt::ExtensionInstallPrompt(content::WebContents
* contents
)
661 : profile_(ProfileForWebContents(contents
)),
662 ui_loop_(base::MessageLoop::current()),
665 install_ui_(extensions::CreateExtensionInstallUI(
666 ProfileForWebContents(contents
))),
667 show_params_(contents
),
671 ExtensionInstallPrompt::ExtensionInstallPrompt(Profile
* profile
,
672 gfx::NativeWindow native_window
)
674 ui_loop_(base::MessageLoop::current()),
677 install_ui_(extensions::CreateExtensionInstallUI(profile
)),
678 show_params_(profile
, native_window
),
682 ExtensionInstallPrompt::~ExtensionInstallPrompt() {
685 void ExtensionInstallPrompt::ConfirmBundleInstall(
686 extensions::BundleInstaller
* bundle
,
687 const PermissionSet
* permissions
) {
688 DCHECK(ui_loop_
== base::MessageLoop::current());
690 custom_permissions_
= permissions
;
692 prompt_
= new Prompt(BUNDLE_INSTALL_PROMPT
);
697 void ExtensionInstallPrompt::ConfirmStandaloneInstall(
699 const Extension
* extension
,
701 scoped_refptr
<Prompt
> prompt
) {
702 DCHECK(ui_loop_
== base::MessageLoop::current());
703 extension_
= extension
;
704 delegate_
= delegate
;
711 void ExtensionInstallPrompt::ConfirmWebstoreInstall(
713 const Extension
* extension
,
714 const SkBitmap
* icon
,
715 const ShowDialogCallback
& show_dialog_callback
) {
716 // SetIcon requires |extension_| to be set. ConfirmInstall will setup the
718 extension_
= extension
;
720 ConfirmInstall(delegate
, extension
, show_dialog_callback
);
723 void ExtensionInstallPrompt::ConfirmInstall(
725 const Extension
* extension
,
726 const ShowDialogCallback
& show_dialog_callback
) {
727 DCHECK(ui_loop_
== base::MessageLoop::current());
728 extension_
= extension
;
729 delegate_
= delegate
;
730 prompt_
= new Prompt(INSTALL_PROMPT
);
731 show_dialog_callback_
= show_dialog_callback
;
733 // We special-case themes to not show any confirm UI. Instead they are
734 // immediately installed, and then we show an infobar (see OnInstallSuccess)
735 // to allow the user to revert if they don't like it.
737 // We don't do this in the case where off-store extension installs are
738 // disabled because in that case, we don't show the dangerous download UI, so
739 // we need the UI confirmation.
740 if (extension
->is_theme()) {
741 if (extension
->from_webstore() ||
742 extensions::FeatureSwitch::easy_off_store_install()->IsEnabled()) {
743 delegate
->InstallUIProceed();
751 void ExtensionInstallPrompt::ConfirmReEnable(Delegate
* delegate
,
752 const Extension
* extension
) {
753 DCHECK(ui_loop_
== base::MessageLoop::current());
754 extension_
= extension
;
755 delegate_
= delegate
;
756 bool is_remote_install
=
758 extensions::ExtensionPrefs::Get(profile_
)->HasDisableReason(
759 extension
->id(), extensions::Extension::DISABLE_REMOTE_INSTALL
);
761 extensions::util::IsEphemeralApp(extension
->id(), profile_
);
763 PromptType type
= UNSET_PROMPT_TYPE
;
765 type
= LAUNCH_PROMPT
;
766 else if (is_remote_install
)
767 type
= REMOTE_INSTALL_PROMPT
;
769 type
= RE_ENABLE_PROMPT
;
770 prompt_
= new Prompt(type
);
775 void ExtensionInstallPrompt::ConfirmExternalInstall(
777 const Extension
* extension
,
778 const ShowDialogCallback
& show_dialog_callback
,
779 scoped_refptr
<Prompt
> prompt
) {
780 DCHECK(ui_loop_
== base::MessageLoop::current());
781 extension_
= extension
;
782 delegate_
= delegate
;
784 show_dialog_callback_
= show_dialog_callback
;
789 void ExtensionInstallPrompt::ConfirmPermissions(
791 const Extension
* extension
,
792 const PermissionSet
* permissions
) {
793 DCHECK(ui_loop_
== base::MessageLoop::current());
794 extension_
= extension
;
795 custom_permissions_
= permissions
;
796 delegate_
= delegate
;
797 prompt_
= new Prompt(PERMISSIONS_PROMPT
);
802 void ExtensionInstallPrompt::ReviewPermissions(
804 const Extension
* extension
,
805 const std::vector
<base::FilePath
>& retained_file_paths
,
806 const std::vector
<base::string16
>& retained_device_messages
) {
807 DCHECK(ui_loop_
== base::MessageLoop::current());
808 extension_
= extension
;
809 prompt_
= new Prompt(POST_INSTALL_PERMISSIONS_PROMPT
);
810 prompt_
->set_retained_files(retained_file_paths
);
811 prompt_
->set_retained_device_messages(retained_device_messages
);
812 delegate_
= delegate
;
817 void ExtensionInstallPrompt::OnInstallSuccess(const Extension
* extension
,
819 extension_
= extension
;
822 install_ui_
->OnInstallSuccess(extension
, &icon_
);
825 void ExtensionInstallPrompt::OnInstallFailure(
826 const extensions::CrxInstallerError
& error
) {
827 install_ui_
->OnInstallFailure(error
);
830 void ExtensionInstallPrompt::SetIcon(const SkBitmap
* image
) {
836 // Let's set default icon bitmap whose size is equal to the default icon's
837 // pixel size under maximal supported scale factor. If the bitmap is larger
838 // than the one we need, it will be scaled down by the ui code.
839 icon_
= GetDefaultIconBitmapForMaxScaleFactor(extension_
->is_app());
843 void ExtensionInstallPrompt::OnImageLoaded(const gfx::Image
& image
) {
844 SetIcon(image
.IsEmpty() ? NULL
: image
.ToSkBitmap());
848 void ExtensionInstallPrompt::LoadImageIfNeeded() {
849 // Bundle install prompts do not have an icon.
850 // Also |profile_| can be NULL in unit tests.
851 if (!icon_
.empty() || !profile_
) {
856 extensions::ExtensionResource image
= extensions::IconsInfo::GetIconResource(
858 extension_misc::EXTENSION_ICON_LARGE
,
859 ExtensionIconSet::MATCH_BIGGER
);
861 // Load the image asynchronously. The response will be sent to OnImageLoaded.
862 extensions::ImageLoader
* loader
= extensions::ImageLoader::Get(profile_
);
864 std::vector
<extensions::ImageLoader::ImageRepresentation
> images_list
;
865 images_list
.push_back(extensions::ImageLoader::ImageRepresentation(
867 extensions::ImageLoader::ImageRepresentation::NEVER_RESIZE
,
869 ui::SCALE_FACTOR_100P
));
870 loader
->LoadImagesAsync(
873 base::Bind(&ExtensionInstallPrompt::OnImageLoaded
, AsWeakPtr()));
876 void ExtensionInstallPrompt::ShowConfirmation() {
877 if (prompt_
->type() == INSTALL_PROMPT
)
878 prompt_
->set_experiment(ExtensionInstallPromptExperiment::Find());
880 prompt_
->set_experiment(ExtensionInstallPromptExperiment::ControlGroup());
882 scoped_refptr
<const PermissionSet
> permissions_to_display
;
883 if (custom_permissions_
.get()) {
884 permissions_to_display
= custom_permissions_
;
885 } else if (extension_
) {
886 // Initialize permissions if they have not already been set so that
887 // withheld permissions are displayed properly in the install prompt.
888 extensions::PermissionsUpdater(
889 profile_
, extensions::PermissionsUpdater::INIT_FLAG_TRANSIENT
)
890 .InitializePermissions(extension_
);
891 permissions_to_display
=
892 extension_
->permissions_data()->active_permissions();
895 if (permissions_to_display
.get() &&
897 !extensions::PermissionsData::ShouldSkipPermissionWarnings(
898 extension_
->id()))) {
899 Manifest::Type type
=
900 extension_
? extension_
->GetType() : Manifest::TYPE_UNKNOWN
;
901 const extensions::PermissionMessageProvider
* message_provider
=
902 extensions::PermissionMessageProvider::Get();
903 prompt_
->SetPermissions(message_provider
->GetWarningMessages(
904 permissions_to_display
.get(), type
),
905 REGULAR_PERMISSIONS
);
906 prompt_
->SetPermissionsDetails(message_provider
->GetWarningMessagesDetails(
907 permissions_to_display
.get(), type
),
908 REGULAR_PERMISSIONS
);
910 scoped_refptr
<const extensions::PermissionSet
> withheld
=
911 extension_
->permissions_data()->withheld_permissions();
912 if (!withheld
->IsEmpty()) {
913 prompt_
->SetPermissions(
914 message_provider
->GetWarningMessages(withheld
.get(), type
),
915 PermissionsType::WITHHELD_PERMISSIONS
);
916 prompt_
->SetPermissionsDetails(
917 message_provider
->GetWarningMessagesDetails(withheld
.get(), type
),
918 PermissionsType::WITHHELD_PERMISSIONS
);
922 switch (prompt_
->type()) {
923 case PERMISSIONS_PROMPT
:
924 case RE_ENABLE_PROMPT
:
925 case INLINE_INSTALL_PROMPT
:
926 case EXTERNAL_INSTALL_PROMPT
:
929 case POST_INSTALL_PERMISSIONS_PROMPT
:
930 case REMOTE_INSTALL_PROMPT
:
931 case REPAIR_PROMPT
: {
932 prompt_
->set_extension(extension_
);
933 prompt_
->set_icon(gfx::Image::CreateFrom1xBitmap(icon_
));
936 case BUNDLE_INSTALL_PROMPT
: {
937 prompt_
->set_bundle(bundle_
);
941 NOTREACHED() << "Unknown message";
945 if (AutoConfirmPrompt(delegate_
))
948 if (show_dialog_callback_
.is_null())
949 GetDefaultShowDialogCallback().Run(show_params_
, delegate_
, prompt_
);
951 show_dialog_callback_
.Run(show_params_
, delegate_
, prompt_
);