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/location.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/thread_task_runner_handle.h"
19 #include "chrome/browser/extensions/bundle_installer.h"
20 #include "chrome/browser/extensions/extension_install_prompt_show_params.h"
21 #include "chrome/browser/extensions/extension_util.h"
22 #include "chrome/browser/extensions/permissions_updater.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/browser/ui/extensions/extension_install_ui_factory.h"
25 #include "chrome/common/chrome_switches.h"
26 #include "chrome/grit/chromium_strings.h"
27 #include "chrome/grit/generated_resources.h"
28 #include "content/public/browser/web_contents.h"
29 #include "extensions/browser/extension_dialog_auto_confirm.h"
30 #include "extensions/browser/extension_prefs.h"
31 #include "extensions/browser/extension_util.h"
32 #include "extensions/browser/image_loader.h"
33 #include "extensions/browser/install/extension_install_ui.h"
34 #include "extensions/common/constants.h"
35 #include "extensions/common/extension.h"
36 #include "extensions/common/extension_icon_set.h"
37 #include "extensions/common/extension_resource.h"
38 #include "extensions/common/feature_switch.h"
39 #include "extensions/common/manifest.h"
40 #include "extensions/common/manifest_constants.h"
41 #include "extensions/common/manifest_handlers/icons_handler.h"
42 #include "extensions/common/manifest_handlers/permissions_parser.h"
43 #include "extensions/common/permissions/permission_set.h"
44 #include "extensions/common/permissions/permissions_data.h"
45 #include "extensions/common/url_pattern.h"
46 #include "grit/theme_resources.h"
47 #include "ui/base/l10n/l10n_util.h"
48 #include "ui/base/resource/resource_bundle.h"
49 #include "ui/base/ui_base_types.h"
50 #include "ui/gfx/image/image.h"
52 using extensions::BundleInstaller
;
53 using extensions::Extension
;
54 using extensions::Manifest
;
55 using extensions::PermissionMessageString
;
56 using extensions::PermissionMessageStrings
;
57 using extensions::PermissionSet
;
61 bool AllowWebstoreData(ExtensionInstallPrompt::PromptType type
) {
62 return type
== ExtensionInstallPrompt::INLINE_INSTALL_PROMPT
||
63 type
== ExtensionInstallPrompt::EXTERNAL_INSTALL_PROMPT
||
64 type
== ExtensionInstallPrompt::REPAIR_PROMPT
;
67 static const int kTitleIds
[ExtensionInstallPrompt::NUM_PROMPT_TYPES
] = {
68 IDS_EXTENSION_INSTALL_PROMPT_TITLE
,
69 IDS_EXTENSION_INSTALL_PROMPT_TITLE
,
70 0, // Heading for bundle installs depends on the bundle contents.
71 IDS_EXTENSION_RE_ENABLE_PROMPT_TITLE
,
72 IDS_EXTENSION_PERMISSIONS_PROMPT_TITLE
,
73 0, // External installs use different strings for extensions/apps/themes.
74 IDS_EXTENSION_POST_INSTALL_PERMISSIONS_PROMPT_TITLE
,
75 IDS_EXTENSION_LAUNCH_APP_PROMPT_TITLE
,
76 IDS_EXTENSION_REMOTE_INSTALL_PROMPT_TITLE
,
77 IDS_EXTENSION_REPAIR_PROMPT_TITLE
,
78 IDS_EXTENSION_DELEGATED_INSTALL_PROMPT_TITLE
,
79 0, // Heading for delegated bundle installs depends on the bundle contents.
81 static const int kButtons
[ExtensionInstallPrompt::NUM_PROMPT_TYPES
] = {
82 ui::DIALOG_BUTTON_OK
| ui::DIALOG_BUTTON_CANCEL
,
83 ui::DIALOG_BUTTON_OK
| ui::DIALOG_BUTTON_CANCEL
,
84 ui::DIALOG_BUTTON_OK
| ui::DIALOG_BUTTON_CANCEL
,
85 ui::DIALOG_BUTTON_OK
| ui::DIALOG_BUTTON_CANCEL
,
86 ui::DIALOG_BUTTON_OK
| ui::DIALOG_BUTTON_CANCEL
,
87 ui::DIALOG_BUTTON_OK
| ui::DIALOG_BUTTON_CANCEL
,
88 // The "OK" button in the post install permissions dialog allows revoking
89 // file/device access, and is only shown if such permissions exist; see
90 // ShouldDisplayRevokeButton().
91 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_OK
| ui::DIALOG_BUTTON_CANCEL
,
96 ui::DIALOG_BUTTON_OK
| ui::DIALOG_BUTTON_CANCEL
,
98 static const int kAcceptButtonIds
[ExtensionInstallPrompt::NUM_PROMPT_TYPES
] = {
99 0, // Regular installs use different strings for extensions/apps/themes.
100 0, // Inline installs as well.
101 IDS_EXTENSION_PROMPT_INSTALL_BUTTON
,
102 IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON
,
103 IDS_EXTENSION_PROMPT_PERMISSIONS_BUTTON
,
104 0, // External installs use different strings for extensions/apps/themes.
105 0, // Different strings depending on the files and devices retained.
106 IDS_EXTENSION_PROMPT_LAUNCH_BUTTON
,
107 0, // Remote installs use different strings for extensions/apps.
108 0, // Repairs use different strings for extensions/apps.
109 0, // Delegated installs use different strings for extensions/apps/themes.
110 IDS_EXTENSION_PROMPT_INSTALL_BUTTON
,
112 static const int kAbortButtonIds
[ExtensionInstallPrompt::NUM_PROMPT_TYPES
] = {
117 IDS_EXTENSION_PROMPT_PERMISSIONS_ABORT_BUTTON
,
118 IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ABORT_BUTTON
,
127 kPermissionsHeaderIds
[ExtensionInstallPrompt::NUM_PROMPT_TYPES
] = {
128 IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO
,
129 IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO
,
130 IDS_EXTENSION_PROMPT_THESE_WILL_HAVE_ACCESS_TO
,
131 IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO
,
132 IDS_EXTENSION_PROMPT_WANTS_ACCESS_TO
,
133 IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO
,
134 IDS_EXTENSION_PROMPT_CAN_ACCESS
,
135 IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO
,
136 IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO
,
137 IDS_EXTENSION_PROMPT_CAN_ACCESS
,
138 IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO
,
139 IDS_EXTENSION_PROMPT_THESE_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 (extensions::ScopedTestDialogAutoConfirm::GetAutoConfirmValue()) {
156 case extensions::ScopedTestDialogAutoConfirm::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 extensions::ScopedTestDialogAutoConfirm::ACCEPT
:
162 base::MessageLoop::current()->PostTask(
164 base::Bind(&ExtensionInstallPrompt::Delegate::InstallUIProceed
,
165 base::Unretained(delegate
)));
167 case extensions::ScopedTestDialogAutoConfirm::CANCEL
:
168 base::ThreadTaskRunnerHandle::Get()->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() {
195 ExtensionInstallPrompt::PromptType
196 ExtensionInstallPrompt::g_last_prompt_type_for_tests
=
197 ExtensionInstallPrompt::UNSET_PROMPT_TYPE
;
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::DELEGATED_PERMISSIONS_PROMPT
:
223 return "DELEGATED_PERMISSIONS_PROMPT";
224 case ExtensionInstallPrompt::DELEGATED_BUNDLE_PERMISSIONS_PROMPT
:
225 return "DELEGATED_BUNDLE_PERMISSIONS_PROMPT";
226 case ExtensionInstallPrompt::UNSET_PROMPT_TYPE
:
227 case ExtensionInstallPrompt::NUM_PROMPT_TYPES
:
233 ExtensionInstallPrompt::Prompt::Prompt(PromptType type
)
235 is_showing_details_for_retained_files_(false),
236 is_showing_details_for_retained_devices_(false),
239 average_rating_(0.0),
241 show_user_count_(false),
242 has_webstore_data_(false) {
245 ExtensionInstallPrompt::Prompt::~Prompt() {
248 void ExtensionInstallPrompt::Prompt::SetPermissions(
249 const PermissionMessageStrings
& permissions
,
250 PermissionsType permissions_type
) {
251 InstallPromptPermissions
& install_permissions
=
252 GetPermissionsForType(permissions_type
);
254 install_permissions
.permissions
.clear();
255 install_permissions
.details
.clear();
256 install_permissions
.is_showing_details
.clear();
258 for (const PermissionMessageString
& str
: permissions
) {
259 install_permissions
.permissions
.push_back(str
.message
);
260 // Add a dash to the front of each permission detail.
261 base::string16 details
;
262 if (!str
.submessages
.empty()) {
263 std::vector
<base::string16
> detail_lines_with_bullets
;
264 for (const auto& detail_line
: str
.submessages
) {
265 detail_lines_with_bullets
.push_back(base::ASCIIToUTF16("- ") +
269 details
= base::JoinString(detail_lines_with_bullets
,
270 base::ASCIIToUTF16("\n"));
272 install_permissions
.details
.push_back(details
);
273 install_permissions
.is_showing_details
.push_back(false);
277 void ExtensionInstallPrompt::Prompt::SetIsShowingDetails(
280 bool is_showing_details
) {
282 case PERMISSIONS_DETAILS
:
283 prompt_permissions_
.is_showing_details
[index
] = is_showing_details
;
285 case WITHHELD_PERMISSIONS_DETAILS
:
286 withheld_prompt_permissions_
.is_showing_details
[index
] =
289 case RETAINED_FILES_DETAILS
:
290 is_showing_details_for_retained_files_
= is_showing_details
;
292 case RETAINED_DEVICES_DETAILS
:
293 is_showing_details_for_retained_devices_
= is_showing_details
;
298 void ExtensionInstallPrompt::Prompt::SetWebstoreData(
299 const std::string
& localized_user_count
,
300 bool show_user_count
,
301 double average_rating
,
303 CHECK(AllowWebstoreData(type_
));
304 localized_user_count_
= localized_user_count
;
305 show_user_count_
= show_user_count
;
306 average_rating_
= average_rating
;
307 rating_count_
= rating_count
;
308 has_webstore_data_
= true;
311 base::string16
ExtensionInstallPrompt::Prompt::GetDialogTitle() const {
312 int id
= kTitleIds
[type_
];
313 if (type_
== BUNDLE_INSTALL_PROMPT
||
314 type_
== DELEGATED_BUNDLE_PERMISSIONS_PROMPT
) {
315 return bundle_
->GetHeadingTextFor(BundleInstaller::Item::STATE_PENDING
);
317 if (type_
== DELEGATED_PERMISSIONS_PROMPT
) {
318 return l10n_util::GetStringFUTF16(id
, base::UTF8ToUTF16(extension_
->name()),
319 base::UTF8ToUTF16(delegated_username_
));
321 if (type_
== EXTERNAL_INSTALL_PROMPT
) {
322 if (extension_
->is_app())
323 id
= IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_APP
;
324 else if (extension_
->is_theme())
325 id
= IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_THEME
;
327 id
= IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_EXTENSION
;
329 return l10n_util::GetStringFUTF16(id
, base::UTF8ToUTF16(extension_
->name()));
332 int ExtensionInstallPrompt::Prompt::GetDialogButtons() const {
333 if (type_
== POST_INSTALL_PERMISSIONS_PROMPT
&& ShouldDisplayRevokeButton()) {
334 return kButtons
[type_
] | ui::DIALOG_BUTTON_OK
;
337 return kButtons
[type_
];
340 base::string16
ExtensionInstallPrompt::Prompt::GetAcceptButtonLabel() const {
341 int id
= kAcceptButtonIds
[type_
];
343 if (type_
== INSTALL_PROMPT
|| type_
== INLINE_INSTALL_PROMPT
||
344 type_
== DELEGATED_PERMISSIONS_PROMPT
) {
345 if (extension_
->is_app())
346 id
= IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_APP
;
347 else if (extension_
->is_theme())
348 id
= IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_THEME
;
350 id
= IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION
;
351 } else if (type_
== EXTERNAL_INSTALL_PROMPT
) {
352 if (extension_
->is_app())
353 id
= IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_APP
;
354 else if (extension_
->is_theme())
355 id
= IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_THEME
;
357 id
= IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION
;
358 } else if (type_
== POST_INSTALL_PERMISSIONS_PROMPT
) {
359 if (GetRetainedFileCount() && GetRetainedDeviceCount()) {
361 IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_AND_DEVICES_BUTTON
;
362 } else if (GetRetainedFileCount()) {
363 id
= IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_BUTTON
;
364 } else if (GetRetainedDeviceCount()) {
365 id
= IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_DEVICES_BUTTON
;
367 // If there are neither retained files nor devices, leave id 0 so there
368 // will be no "accept" button.
369 } else if (type_
== REMOTE_INSTALL_PROMPT
) {
370 if (extension_
->is_app())
371 id
= IDS_EXTENSION_PROMPT_REMOTE_INSTALL_BUTTON_APP
;
373 id
= IDS_EXTENSION_PROMPT_REMOTE_INSTALL_BUTTON_EXTENSION
;
374 } else if (type_
== REPAIR_PROMPT
) {
375 if (extension_
->is_app())
376 id
= IDS_EXTENSION_PROMPT_REPAIR_BUTTON_APP
;
378 id
= IDS_EXTENSION_PROMPT_REPAIR_BUTTON_EXTENSION
;
380 return id
? l10n_util::GetStringUTF16(id
) : base::string16();
383 base::string16
ExtensionInstallPrompt::Prompt::GetAbortButtonLabel() const {
384 return l10n_util::GetStringUTF16(kAbortButtonIds
[type_
]);
387 base::string16
ExtensionInstallPrompt::Prompt::GetPermissionsHeading(
388 PermissionsType permissions_type
) const {
389 switch (permissions_type
) {
390 case REGULAR_PERMISSIONS
:
391 return l10n_util::GetStringUTF16(kPermissionsHeaderIds
[type_
]);
392 case WITHHELD_PERMISSIONS
:
393 return l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WITHHELD
);
394 case ALL_PERMISSIONS
:
397 return base::string16();
401 base::string16
ExtensionInstallPrompt::Prompt::GetRetainedFilesHeading() const {
402 return l10n_util::GetPluralStringFUTF16(
403 IDS_EXTENSION_PROMPT_RETAINED_FILES
, GetRetainedFileCount());
406 base::string16
ExtensionInstallPrompt::Prompt::GetRetainedDevicesHeading()
408 return l10n_util::GetPluralStringFUTF16(
409 IDS_EXTENSION_PROMPT_RETAINED_DEVICES
, GetRetainedDeviceCount());
412 bool ExtensionInstallPrompt::Prompt::ShouldShowPermissions() const {
413 return GetPermissionCount(ALL_PERMISSIONS
) > 0 ||
414 type_
== POST_INSTALL_PERMISSIONS_PROMPT
;
417 void ExtensionInstallPrompt::Prompt::AppendRatingStars(
418 StarAppender appender
, void* data
) const {
420 CHECK(AllowWebstoreData(type_
));
421 int rating_integer
= floor(average_rating_
);
422 double rating_fractional
= average_rating_
- rating_integer
;
424 if (rating_fractional
> 0.66) {
428 if (rating_fractional
< 0.33 || rating_fractional
> 0.66) {
429 rating_fractional
= 0;
432 ResourceBundle
& rb
= ResourceBundle::GetSharedInstance();
434 for (i
= 0; i
< rating_integer
; i
++) {
435 appender(rb
.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_ON
), data
);
437 if (rating_fractional
) {
438 appender(rb
.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_HALF_LEFT
), data
);
441 for (; i
< kMaxExtensionRating
; i
++) {
442 appender(rb
.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_OFF
), data
);
446 base::string16
ExtensionInstallPrompt::Prompt::GetRatingCount() const {
447 CHECK(AllowWebstoreData(type_
));
448 return l10n_util::GetStringFUTF16(IDS_EXTENSION_RATING_COUNT
,
449 base::IntToString16(rating_count_
));
452 base::string16
ExtensionInstallPrompt::Prompt::GetUserCount() const {
453 CHECK(AllowWebstoreData(type_
));
455 if (show_user_count_
) {
456 return l10n_util::GetStringFUTF16(IDS_EXTENSION_USER_COUNT
,
457 base::UTF8ToUTF16(localized_user_count_
));
459 return base::string16();
462 size_t ExtensionInstallPrompt::Prompt::GetPermissionCount(
463 PermissionsType permissions_type
) const {
464 switch (permissions_type
) {
465 case REGULAR_PERMISSIONS
:
466 return prompt_permissions_
.permissions
.size();
467 case WITHHELD_PERMISSIONS
:
468 return withheld_prompt_permissions_
.permissions
.size();
469 case ALL_PERMISSIONS
:
470 return prompt_permissions_
.permissions
.size() +
471 withheld_prompt_permissions_
.permissions
.size();
478 size_t ExtensionInstallPrompt::Prompt::GetPermissionsDetailsCount(
479 PermissionsType permissions_type
) const {
480 switch (permissions_type
) {
481 case REGULAR_PERMISSIONS
:
482 return prompt_permissions_
.details
.size();
483 case WITHHELD_PERMISSIONS
:
484 return withheld_prompt_permissions_
.details
.size();
485 case ALL_PERMISSIONS
:
486 return prompt_permissions_
.details
.size() +
487 withheld_prompt_permissions_
.details
.size();
494 base::string16
ExtensionInstallPrompt::Prompt::GetPermission(
496 PermissionsType permissions_type
) const {
497 const InstallPromptPermissions
& install_permissions
=
498 GetPermissionsForType(permissions_type
);
499 CHECK_LT(index
, install_permissions
.permissions
.size());
500 return install_permissions
.permissions
[index
];
503 base::string16
ExtensionInstallPrompt::Prompt::GetPermissionsDetails(
505 PermissionsType permissions_type
) const {
506 const InstallPromptPermissions
& install_permissions
=
507 GetPermissionsForType(permissions_type
);
508 CHECK_LT(index
, install_permissions
.details
.size());
509 return install_permissions
.details
[index
];
512 bool ExtensionInstallPrompt::Prompt::GetIsShowingDetails(
513 DetailsType type
, size_t index
) const {
515 case PERMISSIONS_DETAILS
:
516 CHECK_LT(index
, prompt_permissions_
.is_showing_details
.size());
517 return prompt_permissions_
.is_showing_details
[index
];
518 case WITHHELD_PERMISSIONS_DETAILS
:
519 CHECK_LT(index
, withheld_prompt_permissions_
.is_showing_details
.size());
520 return withheld_prompt_permissions_
.is_showing_details
[index
];
521 case RETAINED_FILES_DETAILS
:
522 return is_showing_details_for_retained_files_
;
523 case RETAINED_DEVICES_DETAILS
:
524 return is_showing_details_for_retained_devices_
;
529 size_t ExtensionInstallPrompt::Prompt::GetRetainedFileCount() const {
530 return retained_files_
.size();
533 base::string16
ExtensionInstallPrompt::Prompt::GetRetainedFile(size_t index
)
535 CHECK_LT(index
, retained_files_
.size());
536 return retained_files_
[index
].AsUTF16Unsafe();
539 size_t ExtensionInstallPrompt::Prompt::GetRetainedDeviceCount() const {
540 return retained_device_messages_
.size();
543 base::string16
ExtensionInstallPrompt::Prompt::GetRetainedDeviceMessageString(
544 size_t index
) const {
545 CHECK_LT(index
, retained_device_messages_
.size());
546 return retained_device_messages_
[index
];
549 bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeButton() const {
550 return !retained_files_
.empty() || !retained_device_messages_
.empty();
553 ExtensionInstallPrompt::Prompt::InstallPromptPermissions
&
554 ExtensionInstallPrompt::Prompt::GetPermissionsForType(
555 PermissionsType permissions_type
) {
556 DCHECK_NE(ALL_PERMISSIONS
, permissions_type
);
557 return permissions_type
== REGULAR_PERMISSIONS
? prompt_permissions_
558 : withheld_prompt_permissions_
;
561 const ExtensionInstallPrompt::Prompt::InstallPromptPermissions
&
562 ExtensionInstallPrompt::Prompt::GetPermissionsForType(
563 PermissionsType permissions_type
) const {
564 DCHECK_NE(ALL_PERMISSIONS
, permissions_type
);
565 return permissions_type
== REGULAR_PERMISSIONS
? prompt_permissions_
566 : withheld_prompt_permissions_
;
569 bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeFilesButton() const {
570 return !retained_files_
.empty();
574 scoped_refptr
<Extension
>
575 ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
576 const base::DictionaryValue
* manifest
,
578 const std::string
& id
,
579 const std::string
& localized_name
,
580 const std::string
& localized_description
,
581 std::string
* error
) {
582 scoped_ptr
<base::DictionaryValue
> localized_manifest
;
583 if (!localized_name
.empty() || !localized_description
.empty()) {
584 localized_manifest
.reset(manifest
->DeepCopy());
585 if (!localized_name
.empty()) {
586 localized_manifest
->SetString(extensions::manifest_keys::kName
,
589 if (!localized_description
.empty()) {
590 localized_manifest
->SetString(extensions::manifest_keys::kDescription
,
591 localized_description
);
595 return Extension::Create(
598 localized_manifest
.get() ? *localized_manifest
.get() : *manifest
,
604 ExtensionInstallPrompt::ExtensionInstallPrompt(content::WebContents
* contents
)
605 : profile_(ProfileForWebContents(contents
)),
606 ui_loop_(base::MessageLoop::current()),
609 install_ui_(extensions::CreateExtensionInstallUI(
610 ProfileForWebContents(contents
))),
611 show_params_(new ExtensionInstallPromptShowParams(contents
)),
615 ExtensionInstallPrompt::ExtensionInstallPrompt(Profile
* profile
,
616 gfx::NativeWindow native_window
)
618 ui_loop_(base::MessageLoop::current()),
621 install_ui_(extensions::CreateExtensionInstallUI(profile
)),
623 new ExtensionInstallPromptShowParams(profile
, native_window
)),
627 ExtensionInstallPrompt::~ExtensionInstallPrompt() {
630 void ExtensionInstallPrompt::ConfirmBundleInstall(
631 extensions::BundleInstaller
* bundle
,
632 const SkBitmap
* icon
,
633 const PermissionSet
* permissions
) {
634 DCHECK(ui_loop_
== base::MessageLoop::current());
636 custom_permissions_
= permissions
;
638 prompt_
= new Prompt(BUNDLE_INSTALL_PROMPT
);
644 void ExtensionInstallPrompt::ConfirmPermissionsForDelegatedBundleInstall(
645 extensions::BundleInstaller
* bundle
,
646 const std::string
& delegated_username
,
647 const SkBitmap
* icon
,
648 const extensions::PermissionSet
* permissions
) {
649 DCHECK(ui_loop_
== base::MessageLoop::current());
651 delegated_username_
= delegated_username
;
652 custom_permissions_
= permissions
;
654 prompt_
= new Prompt(DELEGATED_BUNDLE_PERMISSIONS_PROMPT
);
660 void ExtensionInstallPrompt::ConfirmStandaloneInstall(
662 const Extension
* extension
,
664 scoped_refptr
<Prompt
> prompt
) {
665 DCHECK(ui_loop_
== base::MessageLoop::current());
666 extension_
= extension
;
667 delegate_
= delegate
;
674 void ExtensionInstallPrompt::ConfirmWebstoreInstall(
676 const Extension
* extension
,
677 const SkBitmap
* icon
,
678 const ShowDialogCallback
& show_dialog_callback
) {
679 // SetIcon requires |extension_| to be set. ConfirmInstall will setup the
681 extension_
= extension
;
683 ConfirmInstall(delegate
, extension
, show_dialog_callback
);
686 void ExtensionInstallPrompt::ConfirmInstall(
688 const Extension
* extension
,
689 const ShowDialogCallback
& show_dialog_callback
) {
690 DCHECK(ui_loop_
== base::MessageLoop::current());
691 extension_
= extension
;
692 delegate_
= delegate
;
693 prompt_
= new Prompt(INSTALL_PROMPT
);
694 show_dialog_callback_
= show_dialog_callback
;
696 // We special-case themes to not show any confirm UI. Instead they are
697 // immediately installed, and then we show an infobar (see OnInstallSuccess)
698 // to allow the user to revert if they don't like it.
700 // We don't do this in the case where off-store extension installs are
701 // disabled because in that case, we don't show the dangerous download UI, so
702 // we need the UI confirmation.
703 if (extension
->is_theme()) {
704 if (extension
->from_webstore() ||
705 extensions::FeatureSwitch::easy_off_store_install()->IsEnabled()) {
706 delegate
->InstallUIProceed();
714 void ExtensionInstallPrompt::ConfirmPermissionsForDelegatedInstall(
716 const Extension
* extension
,
717 const std::string
& delegated_username
,
718 const SkBitmap
* icon
) {
719 DCHECK(ui_loop_
== base::MessageLoop::current());
720 delegate_
= delegate
;
721 extension_
= extension
;
722 delegated_username_
= delegated_username
;
724 prompt_
= new Prompt(DELEGATED_PERMISSIONS_PROMPT
);
728 void ExtensionInstallPrompt::ConfirmReEnable(Delegate
* delegate
,
729 const Extension
* extension
) {
730 DCHECK(ui_loop_
== base::MessageLoop::current());
731 extension_
= extension
;
732 delegate_
= delegate
;
733 bool is_remote_install
=
735 extensions::ExtensionPrefs::Get(profile_
)->HasDisableReason(
736 extension
->id(), extensions::Extension::DISABLE_REMOTE_INSTALL
);
738 extensions::util::IsEphemeralApp(extension
->id(), profile_
);
740 PromptType type
= UNSET_PROMPT_TYPE
;
742 type
= LAUNCH_PROMPT
;
743 else if (is_remote_install
)
744 type
= REMOTE_INSTALL_PROMPT
;
746 type
= RE_ENABLE_PROMPT
;
747 prompt_
= new Prompt(type
);
752 void ExtensionInstallPrompt::ConfirmExternalInstall(
754 const Extension
* extension
,
755 const ShowDialogCallback
& show_dialog_callback
,
756 scoped_refptr
<Prompt
> prompt
) {
757 DCHECK(ui_loop_
== base::MessageLoop::current());
758 extension_
= extension
;
759 delegate_
= delegate
;
761 show_dialog_callback_
= show_dialog_callback
;
766 void ExtensionInstallPrompt::ConfirmPermissions(
768 const Extension
* extension
,
769 const PermissionSet
* permissions
) {
770 DCHECK(ui_loop_
== base::MessageLoop::current());
771 extension_
= extension
;
772 custom_permissions_
= permissions
;
773 delegate_
= delegate
;
774 prompt_
= new Prompt(PERMISSIONS_PROMPT
);
779 void ExtensionInstallPrompt::ReviewPermissions(
781 const Extension
* extension
,
782 const std::vector
<base::FilePath
>& retained_file_paths
,
783 const std::vector
<base::string16
>& retained_device_messages
) {
784 DCHECK(ui_loop_
== base::MessageLoop::current());
785 extension_
= extension
;
786 prompt_
= new Prompt(POST_INSTALL_PERMISSIONS_PROMPT
);
787 prompt_
->set_retained_files(retained_file_paths
);
788 prompt_
->set_retained_device_messages(retained_device_messages
);
789 delegate_
= delegate
;
794 void ExtensionInstallPrompt::OnInstallSuccess(const Extension
* extension
,
796 extension_
= extension
;
799 install_ui_
->OnInstallSuccess(extension
, &icon_
);
802 void ExtensionInstallPrompt::OnInstallFailure(
803 const extensions::CrxInstallError
& error
) {
804 install_ui_
->OnInstallFailure(error
);
807 void ExtensionInstallPrompt::SetIcon(const SkBitmap
* image
) {
813 // Let's set default icon bitmap whose size is equal to the default icon's
814 // pixel size under maximal supported scale factor. If the bitmap is larger
815 // than the one we need, it will be scaled down by the ui code.
816 icon_
= GetDefaultIconBitmapForMaxScaleFactor(
817 extension_
? extension_
->is_app() : false);
821 void ExtensionInstallPrompt::OnImageLoaded(const gfx::Image
& image
) {
822 SetIcon(image
.IsEmpty() ? NULL
: image
.ToSkBitmap());
826 void ExtensionInstallPrompt::LoadImageIfNeeded() {
827 // Bundle install prompts do not have an icon.
828 // Also |profile_| can be NULL in unit tests.
829 if (!icon_
.empty() || !profile_
) {
834 extensions::ExtensionResource image
= extensions::IconsInfo::GetIconResource(
836 extension_misc::EXTENSION_ICON_LARGE
,
837 ExtensionIconSet::MATCH_BIGGER
);
839 // Load the image asynchronously. The response will be sent to OnImageLoaded.
840 extensions::ImageLoader
* loader
= extensions::ImageLoader::Get(profile_
);
842 std::vector
<extensions::ImageLoader::ImageRepresentation
> images_list
;
843 images_list
.push_back(extensions::ImageLoader::ImageRepresentation(
845 extensions::ImageLoader::ImageRepresentation::NEVER_RESIZE
,
847 ui::SCALE_FACTOR_100P
));
848 loader
->LoadImagesAsync(
851 base::Bind(&ExtensionInstallPrompt::OnImageLoaded
, AsWeakPtr()));
854 void ExtensionInstallPrompt::ShowConfirmation() {
855 scoped_refptr
<const PermissionSet
> permissions_to_display
;
856 if (custom_permissions_
.get()) {
857 permissions_to_display
= custom_permissions_
;
858 } else if (extension_
) {
859 // Initialize permissions if they have not already been set so that
860 // withheld permissions are displayed properly in the install prompt.
861 extensions::PermissionsUpdater(
862 profile_
, extensions::PermissionsUpdater::INIT_FLAG_TRANSIENT
)
863 .InitializePermissions(extension_
);
864 permissions_to_display
=
865 extension_
->permissions_data()->active_permissions();
866 // For delegated installs, all optional permissions are pre-approved by the
867 // person who triggers the install, so add them to the list.
868 if (prompt_
->type() == DELEGATED_PERMISSIONS_PROMPT
||
869 prompt_
->type() == DELEGATED_BUNDLE_PERMISSIONS_PROMPT
) {
870 scoped_refptr
<const PermissionSet
> optional_permissions
=
871 extensions::PermissionsParser::GetOptionalPermissions(extension_
);
872 permissions_to_display
= PermissionSet::CreateUnion(
873 permissions_to_display
.get(),
874 optional_permissions
.get());
878 if (permissions_to_display
.get() &&
880 !extensions::PermissionsData::ShouldSkipPermissionWarnings(
881 extension_
->id()))) {
882 Manifest::Type type
=
883 extension_
? extension_
->GetType() : Manifest::TYPE_UNKNOWN
;
884 const extensions::PermissionMessageProvider
* message_provider
=
885 extensions::PermissionMessageProvider::Get();
887 prompt_
->SetPermissions(message_provider
->GetPermissionMessageStrings(
888 permissions_to_display
.get(), type
),
889 REGULAR_PERMISSIONS
);
891 scoped_refptr
<const extensions::PermissionSet
> withheld
=
892 extension_
? extension_
->permissions_data()->withheld_permissions()
894 if (withheld
&& !withheld
->IsEmpty()) {
895 prompt_
->SetPermissions(
896 message_provider
->GetPermissionMessageStrings(withheld
.get(), type
),
897 PermissionsType::WITHHELD_PERMISSIONS
);
901 switch (prompt_
->type()) {
902 case PERMISSIONS_PROMPT
:
903 case RE_ENABLE_PROMPT
:
904 case INLINE_INSTALL_PROMPT
:
905 case EXTERNAL_INSTALL_PROMPT
:
908 case POST_INSTALL_PERMISSIONS_PROMPT
:
909 case REMOTE_INSTALL_PROMPT
:
911 case DELEGATED_PERMISSIONS_PROMPT
: {
912 prompt_
->set_extension(extension_
);
915 case BUNDLE_INSTALL_PROMPT
:
916 case DELEGATED_BUNDLE_PERMISSIONS_PROMPT
: {
917 prompt_
->set_bundle(bundle_
);
921 NOTREACHED() << "Unknown message";
924 prompt_
->set_delegated_username(delegated_username_
);
925 prompt_
->set_icon(gfx::Image::CreateFrom1xBitmap(icon_
));
927 g_last_prompt_type_for_tests
= prompt_
->type();
929 if (AutoConfirmPrompt(delegate_
))
932 if (show_params_
->WasParentDestroyed()) {
933 delegate_
->InstallUIAbort(false);
937 if (show_dialog_callback_
.is_null())
938 GetDefaultShowDialogCallback().Run(show_params_
.get(), delegate_
, prompt_
);
940 show_dialog_callback_
.Run(show_params_
.get(), delegate_
, prompt_
);