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