Add ICU message format support
[chromium-blink-merge.git] / chrome / browser / extensions / extension_install_prompt.cc
blob41a8e45cc725301f9396d97181f8f49fc42a2501
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"
7 #include <map>
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::CoalescedPermissionMessage;
56 using extensions::CoalescedPermissionMessages;
57 using extensions::PermissionSet;
59 namespace {
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] = {
113 IDS_CANCEL,
114 IDS_CANCEL,
115 IDS_CANCEL,
116 IDS_CANCEL,
117 IDS_EXTENSION_PROMPT_PERMISSIONS_ABORT_BUTTON,
118 IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ABORT_BUTTON,
119 IDS_CLOSE,
120 IDS_CANCEL,
121 IDS_CANCEL,
122 IDS_CANCEL,
123 IDS_CANCEL,
124 IDS_CANCEL,
126 static const int
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:
157 return false;
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(
163 FROM_HERE,
164 base::Bind(&ExtensionInstallPrompt::Delegate::InstallUIProceed,
165 base::Unretained(delegate)));
166 return true;
167 case extensions::ScopedTestDialogAutoConfirm::CANCEL:
168 base::ThreadTaskRunnerHandle::Get()->PostTask(
169 FROM_HERE,
170 base::Bind(&ExtensionInstallPrompt::Delegate::InstallUIAbort,
171 base::Unretained(delegate),
172 true));
173 return true;
176 NOTREACHED();
177 return false;
180 Profile* ProfileForWebContents(content::WebContents* web_contents) {
181 if (!web_contents)
182 return NULL;
183 return Profile::FromBrowserContext(web_contents->GetBrowserContext());
186 } // namespace
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) {
201 switch (type) {
202 case ExtensionInstallPrompt::INSTALL_PROMPT:
203 return "INSTALL_PROMPT";
204 case ExtensionInstallPrompt::INLINE_INSTALL_PROMPT:
205 return "INLINE_INSTALL_PROMPT";
206 case ExtensionInstallPrompt::BUNDLE_INSTALL_PROMPT:
207 return "BUNDLE_INSTALL_PROMPT";
208 case ExtensionInstallPrompt::RE_ENABLE_PROMPT:
209 return "RE_ENABLE_PROMPT";
210 case ExtensionInstallPrompt::PERMISSIONS_PROMPT:
211 return "PERMISSIONS_PROMPT";
212 case ExtensionInstallPrompt::EXTERNAL_INSTALL_PROMPT:
213 return "EXTERNAL_INSTALL_PROMPT";
214 case ExtensionInstallPrompt::POST_INSTALL_PERMISSIONS_PROMPT:
215 return "POST_INSTALL_PERMISSIONS_PROMPT";
216 case ExtensionInstallPrompt::LAUNCH_PROMPT:
217 return "LAUNCH_PROMPT";
218 case ExtensionInstallPrompt::REMOTE_INSTALL_PROMPT:
219 return "REMOTE_INSTALL_PROMPT";
220 case ExtensionInstallPrompt::REPAIR_PROMPT:
221 return "REPAIR_PROMPT";
222 case ExtensionInstallPrompt::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:
228 break;
230 return "OTHER";
233 ExtensionInstallPrompt::Prompt::Prompt(PromptType type)
234 : type_(type),
235 is_showing_details_for_retained_files_(false),
236 is_showing_details_for_retained_devices_(false),
237 extension_(NULL),
238 bundle_(NULL),
239 average_rating_(0.0),
240 rating_count_(0),
241 show_user_count_(false),
242 has_webstore_data_(false) {
245 ExtensionInstallPrompt::Prompt::~Prompt() {
248 void ExtensionInstallPrompt::Prompt::SetPermissions(
249 const CoalescedPermissionMessages& 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 CoalescedPermissionMessage& msg : permissions) {
259 install_permissions.permissions.push_back(msg.message());
260 // Add a dash to the front of each permission detail.
261 base::string16 details;
262 if (!msg.submessages().empty()) {
263 std::vector<base::string16> detail_lines_with_bullets;
264 for (const auto& detail_line : msg.submessages()) {
265 detail_lines_with_bullets.push_back(base::ASCIIToUTF16("- ") +
266 detail_line);
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(
278 DetailsType type,
279 size_t index,
280 bool is_showing_details) {
281 switch (type) {
282 case PERMISSIONS_DETAILS:
283 prompt_permissions_.is_showing_details[index] = is_showing_details;
284 break;
285 case WITHHELD_PERMISSIONS_DETAILS:
286 withheld_prompt_permissions_.is_showing_details[index] =
287 is_showing_details;
288 break;
289 case RETAINED_FILES_DETAILS:
290 is_showing_details_for_retained_files_ = is_showing_details;
291 break;
292 case RETAINED_DEVICES_DETAILS:
293 is_showing_details_for_retained_devices_ = is_showing_details;
294 break;
298 void ExtensionInstallPrompt::Prompt::SetWebstoreData(
299 const std::string& localized_user_count,
300 bool show_user_count,
301 double average_rating,
302 int rating_count) {
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;
326 else
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;
349 else
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;
356 else
357 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION;
358 } else if (type_ == POST_INSTALL_PERMISSIONS_PROMPT) {
359 if (GetRetainedFileCount() && GetRetainedDeviceCount()) {
360 id =
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;
372 else
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;
377 else
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:
395 default:
396 NOTREACHED();
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()
407 const {
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 {
419 CHECK(appender);
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) {
425 rating_integer++;
428 if (rating_fractional < 0.33 || rating_fractional > 0.66) {
429 rating_fractional = 0;
432 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
433 int i;
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);
439 i++;
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();
472 default:
473 NOTREACHED();
474 return 0u;
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();
488 default:
489 NOTREACHED();
490 return 0u;
494 base::string16 ExtensionInstallPrompt::Prompt::GetPermission(
495 size_t index,
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(
504 size_t index,
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 {
514 switch (type) {
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_;
526 return false;
529 size_t ExtensionInstallPrompt::Prompt::GetRetainedFileCount() const {
530 return retained_files_.size();
533 base::string16 ExtensionInstallPrompt::Prompt::GetRetainedFile(size_t index)
534 const {
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();
573 // static
574 scoped_refptr<Extension>
575 ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
576 const base::DictionaryValue* manifest,
577 int flags,
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,
587 localized_name);
589 if (!localized_description.empty()) {
590 localized_manifest->SetString(extensions::manifest_keys::kDescription,
591 localized_description);
595 return Extension::Create(
596 base::FilePath(),
597 Manifest::INTERNAL,
598 localized_manifest.get() ? *localized_manifest.get() : *manifest,
599 flags,
601 error);
604 ExtensionInstallPrompt::ExtensionInstallPrompt(content::WebContents* contents)
605 : profile_(ProfileForWebContents(contents)),
606 ui_loop_(base::MessageLoop::current()),
607 extension_(NULL),
608 bundle_(NULL),
609 install_ui_(extensions::CreateExtensionInstallUI(
610 ProfileForWebContents(contents))),
611 show_params_(new ExtensionInstallPromptShowParams(contents)),
612 delegate_(NULL) {
615 ExtensionInstallPrompt::ExtensionInstallPrompt(Profile* profile,
616 gfx::NativeWindow native_window)
617 : profile_(profile),
618 ui_loop_(base::MessageLoop::current()),
619 extension_(NULL),
620 bundle_(NULL),
621 install_ui_(extensions::CreateExtensionInstallUI(profile)),
622 show_params_(
623 new ExtensionInstallPromptShowParams(profile, native_window)),
624 delegate_(NULL) {
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());
635 bundle_ = bundle;
636 custom_permissions_ = permissions;
637 delegate_ = bundle;
638 prompt_ = new Prompt(BUNDLE_INSTALL_PROMPT);
640 SetIcon(icon);
641 ShowConfirmation();
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());
650 bundle_ = bundle;
651 delegated_username_ = delegated_username;
652 custom_permissions_ = permissions;
653 delegate_ = bundle;
654 prompt_ = new Prompt(DELEGATED_BUNDLE_PERMISSIONS_PROMPT);
656 SetIcon(icon);
657 ShowConfirmation();
660 void ExtensionInstallPrompt::ConfirmStandaloneInstall(
661 Delegate* delegate,
662 const Extension* extension,
663 SkBitmap* icon,
664 scoped_refptr<Prompt> prompt) {
665 DCHECK(ui_loop_ == base::MessageLoop::current());
666 extension_ = extension;
667 delegate_ = delegate;
668 prompt_ = prompt;
670 SetIcon(icon);
671 ShowConfirmation();
674 void ExtensionInstallPrompt::ConfirmWebstoreInstall(
675 Delegate* delegate,
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
680 // remaining fields.
681 extension_ = extension;
682 SetIcon(icon);
683 ConfirmInstall(delegate, extension, show_dialog_callback);
686 void ExtensionInstallPrompt::ConfirmInstall(
687 Delegate* delegate,
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();
707 return;
711 LoadImageIfNeeded();
714 void ExtensionInstallPrompt::ConfirmPermissionsForDelegatedInstall(
715 Delegate* delegate,
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;
723 SetIcon(icon);
724 prompt_ = new Prompt(DELEGATED_PERMISSIONS_PROMPT);
725 ShowConfirmation();
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 =
734 profile_ &&
735 extensions::ExtensionPrefs::Get(profile_)->HasDisableReason(
736 extension->id(), extensions::Extension::DISABLE_REMOTE_INSTALL);
737 bool is_ephemeral =
738 extensions::util::IsEphemeralApp(extension->id(), profile_);
740 PromptType type = UNSET_PROMPT_TYPE;
741 if (is_ephemeral)
742 type = LAUNCH_PROMPT;
743 else if (is_remote_install)
744 type = REMOTE_INSTALL_PROMPT;
745 else
746 type = RE_ENABLE_PROMPT;
747 prompt_ = new Prompt(type);
749 LoadImageIfNeeded();
752 void ExtensionInstallPrompt::ConfirmExternalInstall(
753 Delegate* delegate,
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;
760 prompt_ = prompt;
761 show_dialog_callback_ = show_dialog_callback;
763 LoadImageIfNeeded();
766 void ExtensionInstallPrompt::ConfirmPermissions(
767 Delegate* delegate,
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);
776 LoadImageIfNeeded();
779 void ExtensionInstallPrompt::ReviewPermissions(
780 Delegate* delegate,
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;
791 LoadImageIfNeeded();
794 void ExtensionInstallPrompt::OnInstallSuccess(const Extension* extension,
795 SkBitmap* icon) {
796 extension_ = extension;
797 SetIcon(icon);
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) {
808 if (image)
809 icon_ = *image;
810 else
811 icon_ = SkBitmap();
812 if (icon_.empty()) {
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());
823 ShowConfirmation();
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_) {
830 ShowConfirmation();
831 return;
834 extensions::ExtensionResource image = extensions::IconsInfo::GetIconResource(
835 extension_,
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(
844 image,
845 extensions::ImageLoader::ImageRepresentation::NEVER_RESIZE,
846 gfx::Size(),
847 ui::SCALE_FACTOR_100P));
848 loader->LoadImagesAsync(
849 extension_,
850 images_list,
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() &&
879 (!extension_ ||
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->GetPermissionMessages(
888 message_provider->GetAllPermissionIDs(
889 permissions_to_display.get(), type)),
890 REGULAR_PERMISSIONS);
892 scoped_refptr<const extensions::PermissionSet> withheld =
893 extension_ ? extension_->permissions_data()->withheld_permissions()
894 : nullptr;
895 if (withheld && !withheld->IsEmpty()) {
896 prompt_->SetPermissions(
897 message_provider->GetPermissionMessages(
898 message_provider->GetAllPermissionIDs(withheld.get(), type)),
899 WITHHELD_PERMISSIONS);
903 switch (prompt_->type()) {
904 case PERMISSIONS_PROMPT:
905 case RE_ENABLE_PROMPT:
906 case INLINE_INSTALL_PROMPT:
907 case EXTERNAL_INSTALL_PROMPT:
908 case INSTALL_PROMPT:
909 case LAUNCH_PROMPT:
910 case POST_INSTALL_PERMISSIONS_PROMPT:
911 case REMOTE_INSTALL_PROMPT:
912 case REPAIR_PROMPT:
913 case DELEGATED_PERMISSIONS_PROMPT: {
914 prompt_->set_extension(extension_);
915 break;
917 case BUNDLE_INSTALL_PROMPT:
918 case DELEGATED_BUNDLE_PERMISSIONS_PROMPT: {
919 prompt_->set_bundle(bundle_);
920 break;
922 default:
923 NOTREACHED() << "Unknown message";
924 return;
926 prompt_->set_delegated_username(delegated_username_);
927 prompt_->set_icon(gfx::Image::CreateFrom1xBitmap(icon_));
929 g_last_prompt_type_for_tests = prompt_->type();
931 if (AutoConfirmPrompt(delegate_))
932 return;
934 if (show_params_->WasParentDestroyed()) {
935 delegate_->InstallUIAbort(false);
936 return;
939 if (show_dialog_callback_.is_null())
940 GetDefaultShowDialogCallback().Run(show_params_.get(), delegate_, prompt_);
941 else
942 show_dialog_callback_.Run(show_params_.get(), delegate_, prompt_);