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/image_loader.h" 19 #include "chrome/browser/profiles/profile.h" 20 #include "chrome/browser/signin/profile_oauth2_token_service.h" 21 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" 22 #include "chrome/browser/ui/browser.h" 23 #include "chrome/browser/ui/browser_window.h" 24 #include "chrome/common/chrome_switches.h" 25 #include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h" 26 #include "chrome/common/extensions/extension_constants.h" 27 #include "chrome/common/extensions/extension_icon_set.h" 28 #include "chrome/common/extensions/manifest_handlers/icons_handler.h" 29 #include "chrome/common/pref_names.h" 30 #include "content/public/browser/web_contents.h" 31 #include "content/public/browser/web_contents_view.h" 32 #include "extensions/common/extension.h" 33 #include "extensions/common/extension_resource.h" 34 #include "extensions/common/feature_switch.h" 35 #include "extensions/common/manifest.h" 36 #include "extensions/common/manifest_constants.h" 37 #include "extensions/common/permissions/permission_message_provider.h" 38 #include "extensions/common/permissions/permission_set.h" 39 #include "extensions/common/permissions/permissions_data.h" 40 #include "extensions/common/url_pattern.h" 41 #include "grit/chromium_strings.h" 42 #include "grit/generated_resources.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 55 static const int kTitleIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { 56 0, // The regular install prompt depends on what's being installed. 57 IDS_EXTENSION_INLINE_INSTALL_PROMPT_TITLE, 58 IDS_EXTENSION_INSTALL_PROMPT_TITLE, 59 IDS_EXTENSION_RE_ENABLE_PROMPT_TITLE, 60 IDS_EXTENSION_PERMISSIONS_PROMPT_TITLE, 61 IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE, 62 IDS_EXTENSION_POST_INSTALL_PERMISSIONS_PROMPT_TITLE, 63 IDS_EXTENSION_LAUNCH_APP_PROMPT_TITLE, 64 }; 65 static const int kHeadingIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { 66 IDS_EXTENSION_INSTALL_PROMPT_HEADING, 67 0, // Inline installs use the extension name. 68 0, // Heading for bundle installs depends on the bundle contents. 69 IDS_EXTENSION_RE_ENABLE_PROMPT_HEADING, 70 IDS_EXTENSION_PERMISSIONS_PROMPT_HEADING, 71 0, // External installs use different strings for extensions/apps. 72 IDS_EXTENSION_POST_INSTALL_PERMISSIONS_PROMPT_HEADING, 73 IDS_EXTENSION_LAUNCH_APP_PROMPT_HEADING, 74 }; 75 static const int kButtons[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { 76 ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL, 77 ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL, 78 ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL, 79 ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL, 80 ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL, 81 ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL, 82 ui::DIALOG_BUTTON_CANCEL, 83 ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL, 84 }; 85 static const int kAcceptButtonIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { 86 IDS_EXTENSION_PROMPT_INSTALL_BUTTON, 87 IDS_EXTENSION_PROMPT_INSTALL_BUTTON, 88 IDS_EXTENSION_PROMPT_INSTALL_BUTTON, 89 IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON, 90 IDS_EXTENSION_PROMPT_PERMISSIONS_BUTTON, 91 0, // External installs use different strings for extensions/apps. 92 IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_BUTTON, 93 IDS_EXTENSION_PROMPT_LAUNCH_BUTTON, 94 }; 95 static const int kAbortButtonIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { 96 0, // These all use the platform's default cancel label. 97 0, 98 0, 99 0, 100 IDS_EXTENSION_PROMPT_PERMISSIONS_ABORT_BUTTON, 101 IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ABORT_BUTTON, 102 IDS_CLOSE, 103 0, // Platform dependent cancel button. 104 }; 105 static const int kPermissionsHeaderIds[ 106 ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { 107 IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO, 108 IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO, 109 IDS_EXTENSION_PROMPT_THESE_WILL_HAVE_ACCESS_TO, 110 IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO, 111 IDS_EXTENSION_PROMPT_WANTS_ACCESS_TO, 112 IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO, 113 IDS_EXTENSION_PROMPT_CAN_ACCESS, 114 IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO, 115 }; 116 static const int kOAuthHeaderIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { 117 IDS_EXTENSION_PROMPT_OAUTH_HEADER, 118 0, // Inline installs don't show OAuth permissions. 119 0, // Bundle installs don't show OAuth permissions. 120 IDS_EXTENSION_PROMPT_OAUTH_REENABLE_HEADER, 121 IDS_EXTENSION_PROMPT_OAUTH_PERMISSIONS_HEADER, 122 0, 123 0, 124 IDS_EXTENSION_PROMPT_OAUTH_HEADER, 125 }; 126 127 // Size of extension icon in top left of dialog. 128 const int kIconSize = 69; 129 130 // Returns pixel size under maximal scale factor for the icon whose device 131 // independent size is |size_in_dip| 132 int GetSizeForMaxScaleFactor(int size_in_dip) { 133 return static_cast<int>(size_in_dip * gfx::ImageSkia::GetMaxSupportedScale()); 134 } 135 136 // Returns bitmap for the default icon with size equal to the default icon's 137 // pixel size under maximal supported scale factor. 138 SkBitmap GetDefaultIconBitmapForMaxScaleFactor(bool is_app) { 139 const gfx::ImageSkia& image = is_app ? 140 extensions::IconsInfo::GetDefaultAppIcon() : 141 extensions::IconsInfo::GetDefaultExtensionIcon(); 142 return image.GetRepresentation( 143 gfx::ImageSkia::GetMaxSupportedScale()).sk_bitmap(); 144 } 145 146 // If auto confirm is enabled then posts a task to proceed with or cancel the 147 // install and returns true. Otherwise returns false. 148 bool AutoConfirmPrompt(ExtensionInstallPrompt::Delegate* delegate) { 149 const CommandLine* cmdline = CommandLine::ForCurrentProcess(); 150 if (!cmdline->HasSwitch(switches::kAppsGalleryInstallAutoConfirmForTests)) 151 return false; 152 std::string value = cmdline->GetSwitchValueASCII( 153 switches::kAppsGalleryInstallAutoConfirmForTests); 154 155 // We use PostTask instead of calling the delegate directly here, because in 156 // the real implementations it's highly likely the message loop will be 157 // pumping a few times before the user clicks accept or cancel. 158 if (value == "accept") { 159 base::MessageLoop::current()->PostTask( 160 FROM_HERE, 161 base::Bind(&ExtensionInstallPrompt::Delegate::InstallUIProceed, 162 base::Unretained(delegate))); 163 return true; 164 } 165 166 if (value == "cancel") { 167 base::MessageLoop::current()->PostTask( 168 FROM_HERE, 169 base::Bind(&ExtensionInstallPrompt::Delegate::InstallUIAbort, 170 base::Unretained(delegate), 171 true)); 172 return true; 173 } 174 175 NOTREACHED(); 176 return false; 177 } 178 179 Profile* ProfileForWebContents(content::WebContents* web_contents) { 180 if (!web_contents) 181 return NULL; 182 return Profile::FromBrowserContext(web_contents->GetBrowserContext()); 183 } 184 185 gfx::NativeWindow NativeWindowForWebContents(content::WebContents* contents) { 186 if (!contents) 187 return NULL; 188 189 return contents->GetView()->GetTopLevelNativeWindow(); 190 } 191 192 } // namespace 193 194 ExtensionInstallPrompt::Prompt::Prompt(PromptType type) 195 : type_(type), 196 is_showing_details_for_retained_files_(false), 197 extension_(NULL), 198 bundle_(NULL), 199 average_rating_(0.0), 200 rating_count_(0), 201 show_user_count_(false) { 202 } 203 204 ExtensionInstallPrompt::Prompt::~Prompt() { 205 } 206 207 void ExtensionInstallPrompt::Prompt::SetPermissions( 208 const std::vector<base::string16>& permissions) { 209 permissions_ = permissions; 210 } 211 212 void ExtensionInstallPrompt::Prompt::SetPermissionsDetails( 213 const std::vector<base::string16>& details) { 214 details_ = details; 215 is_showing_details_for_permissions_.clear(); 216 for (size_t i = 0; i < details.size(); ++i) 217 is_showing_details_for_permissions_.push_back(false); 218 } 219 220 void ExtensionInstallPrompt::Prompt::SetIsShowingDetails( 221 DetailsType type, 222 size_t index, 223 bool is_showing_details) { 224 switch (type) { 225 case PERMISSIONS_DETAILS: 226 is_showing_details_for_permissions_[index] = is_showing_details; 227 break; 228 case OAUTH_DETAILS: 229 is_showing_details_for_oauth_[index] = is_showing_details; 230 break; 231 case RETAINED_FILES_DETAILS: 232 is_showing_details_for_retained_files_ = is_showing_details; 233 break; 234 } 235 } 236 237 void ExtensionInstallPrompt::Prompt::SetOAuthIssueAdvice( 238 const IssueAdviceInfo& issue_advice) { 239 is_showing_details_for_oauth_.clear(); 240 for (size_t i = 0; i < issue_advice.size(); ++i) 241 is_showing_details_for_oauth_.push_back(false); 242 243 oauth_issue_advice_ = issue_advice; 244 } 245 246 void ExtensionInstallPrompt::Prompt::SetUserNameFromProfile(Profile* profile) { 247 // |profile| can be NULL in unit tests. 248 if (profile) { 249 oauth_user_name_ = UTF8ToUTF16(profile->GetPrefs()->GetString( 250 prefs::kGoogleServicesUsername)); 251 } else { 252 oauth_user_name_.clear(); 253 } 254 } 255 256 void ExtensionInstallPrompt::Prompt::SetInlineInstallWebstoreData( 257 const std::string& localized_user_count, 258 bool show_user_count, 259 double average_rating, 260 int rating_count) { 261 CHECK_EQ(INLINE_INSTALL_PROMPT, type_); 262 localized_user_count_ = localized_user_count; 263 show_user_count_ = show_user_count; 264 average_rating_ = average_rating; 265 rating_count_ = rating_count; 266 } 267 268 base::string16 ExtensionInstallPrompt::Prompt::GetDialogTitle() const { 269 int resource_id = kTitleIds[type_]; 270 271 if (type_ == INSTALL_PROMPT) { 272 if (extension_->is_app()) 273 resource_id = IDS_EXTENSION_INSTALL_APP_PROMPT_TITLE; 274 else if (extension_->is_theme()) 275 resource_id = IDS_EXTENSION_INSTALL_THEME_PROMPT_TITLE; 276 else 277 resource_id = IDS_EXTENSION_INSTALL_EXTENSION_PROMPT_TITLE; 278 } else if (type_ == EXTERNAL_INSTALL_PROMPT) { 279 return l10n_util::GetStringFUTF16( 280 resource_id, UTF8ToUTF16(extension_->name())); 281 } 282 283 return l10n_util::GetStringUTF16(resource_id); 284 } 285 286 base::string16 ExtensionInstallPrompt::Prompt::GetHeading() const { 287 if (type_ == INLINE_INSTALL_PROMPT) { 288 return UTF8ToUTF16(extension_->name()); 289 } else if (type_ == BUNDLE_INSTALL_PROMPT) { 290 return bundle_->GetHeadingTextFor(BundleInstaller::Item::STATE_PENDING); 291 } else if (type_ == EXTERNAL_INSTALL_PROMPT) { 292 int resource_id = -1; 293 if (extension_->is_app()) 294 resource_id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_HEADING_APP; 295 else if (extension_->is_theme()) 296 resource_id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_HEADING_THEME; 297 else 298 resource_id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_HEADING_EXTENSION; 299 return l10n_util::GetStringUTF16(resource_id); 300 } else { 301 return l10n_util::GetStringFUTF16( 302 kHeadingIds[type_], UTF8ToUTF16(extension_->name())); 303 } 304 } 305 306 int ExtensionInstallPrompt::Prompt::GetDialogButtons() const { 307 if (type_ == POST_INSTALL_PERMISSIONS_PROMPT && 308 ShouldDisplayRevokeFilesButton()) { 309 return kButtons[type_] | ui::DIALOG_BUTTON_OK; 310 } 311 312 return kButtons[type_]; 313 } 314 315 bool ExtensionInstallPrompt::Prompt::HasAcceptButtonLabel() const { 316 if (kAcceptButtonIds[type_] == 0) 317 return false; 318 319 if (type_ == POST_INSTALL_PERMISSIONS_PROMPT) 320 return ShouldDisplayRevokeFilesButton(); 321 322 return true; 323 } 324 325 base::string16 ExtensionInstallPrompt::Prompt::GetAcceptButtonLabel() const { 326 if (type_ == EXTERNAL_INSTALL_PROMPT) { 327 int id = -1; 328 if (extension_->is_app()) 329 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_APP; 330 else if (extension_->is_theme()) 331 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_THEME; 332 else 333 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION; 334 return l10n_util::GetStringUTF16(id); 335 } 336 return l10n_util::GetStringUTF16(kAcceptButtonIds[type_]); 337 } 338 339 bool ExtensionInstallPrompt::Prompt::HasAbortButtonLabel() const { 340 return kAbortButtonIds[type_] > 0; 341 } 342 343 base::string16 ExtensionInstallPrompt::Prompt::GetAbortButtonLabel() const { 344 CHECK(HasAbortButtonLabel()); 345 return l10n_util::GetStringUTF16(kAbortButtonIds[type_]); 346 } 347 348 base::string16 ExtensionInstallPrompt::Prompt::GetPermissionsHeading() const { 349 return l10n_util::GetStringUTF16(kPermissionsHeaderIds[type_]); 350 } 351 352 base::string16 ExtensionInstallPrompt::Prompt::GetOAuthHeading() const { 353 return l10n_util::GetStringFUTF16(kOAuthHeaderIds[type_], oauth_user_name_); 354 } 355 356 base::string16 ExtensionInstallPrompt::Prompt::GetRetainedFilesHeading() const { 357 const int kRetainedFilesMessageIDs[6] = { 358 IDS_EXTENSION_PROMPT_RETAINED_FILES_DEFAULT, 359 IDS_EXTENSION_PROMPT_RETAINED_FILE_SINGULAR, 360 IDS_EXTENSION_PROMPT_RETAINED_FILES_ZERO, 361 IDS_EXTENSION_PROMPT_RETAINED_FILES_TWO, 362 IDS_EXTENSION_PROMPT_RETAINED_FILES_FEW, 363 IDS_EXTENSION_PROMPT_RETAINED_FILES_MANY, 364 }; 365 std::vector<int> message_ids; 366 for (size_t i = 0; i < arraysize(kRetainedFilesMessageIDs); i++) { 367 message_ids.push_back(kRetainedFilesMessageIDs[i]); 368 } 369 return l10n_util::GetPluralStringFUTF16(message_ids, GetRetainedFileCount()); 370 } 371 372 bool ExtensionInstallPrompt::Prompt::ShouldShowPermissions() const { 373 return GetPermissionCount() > 0 || type_ == POST_INSTALL_PERMISSIONS_PROMPT; 374 } 375 376 void ExtensionInstallPrompt::Prompt::AppendRatingStars( 377 StarAppender appender, void* data) const { 378 CHECK(appender); 379 CHECK_EQ(INLINE_INSTALL_PROMPT, type_); 380 int rating_integer = floor(average_rating_); 381 double rating_fractional = average_rating_ - rating_integer; 382 383 if (rating_fractional > 0.66) { 384 rating_integer++; 385 } 386 387 if (rating_fractional < 0.33 || rating_fractional > 0.66) { 388 rating_fractional = 0; 389 } 390 391 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 392 int i; 393 for (i = 0; i < rating_integer; i++) { 394 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_ON), data); 395 } 396 if (rating_fractional) { 397 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_HALF_LEFT), data); 398 i++; 399 } 400 for (; i < kMaxExtensionRating; i++) { 401 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_OFF), data); 402 } 403 } 404 405 base::string16 ExtensionInstallPrompt::Prompt::GetRatingCount() const { 406 CHECK_EQ(INLINE_INSTALL_PROMPT, type_); 407 return l10n_util::GetStringFUTF16(IDS_EXTENSION_RATING_COUNT, 408 base::IntToString16(rating_count_)); 409 } 410 411 base::string16 ExtensionInstallPrompt::Prompt::GetUserCount() const { 412 CHECK_EQ(INLINE_INSTALL_PROMPT, type_); 413 414 if (show_user_count_) { 415 return l10n_util::GetStringFUTF16(IDS_EXTENSION_USER_COUNT, 416 base::UTF8ToUTF16(localized_user_count_)); 417 } 418 return base::string16(); 419 } 420 421 size_t ExtensionInstallPrompt::Prompt::GetPermissionCount() const { 422 return permissions_.size(); 423 } 424 425 size_t ExtensionInstallPrompt::Prompt::GetPermissionsDetailsCount() const { 426 return details_.size(); 427 } 428 429 base::string16 ExtensionInstallPrompt::Prompt::GetPermission(size_t index) 430 const { 431 CHECK_LT(index, permissions_.size()); 432 return permissions_[index]; 433 } 434 435 base::string16 ExtensionInstallPrompt::Prompt::GetPermissionsDetails( 436 size_t index) const { 437 CHECK_LT(index, details_.size()); 438 return details_[index]; 439 } 440 441 bool ExtensionInstallPrompt::Prompt::GetIsShowingDetails( 442 DetailsType type, size_t index) const { 443 switch (type) { 444 case PERMISSIONS_DETAILS: 445 CHECK_LT(index, is_showing_details_for_permissions_.size()); 446 return is_showing_details_for_permissions_[index]; 447 case OAUTH_DETAILS: 448 CHECK_LT(index, is_showing_details_for_oauth_.size()); 449 return is_showing_details_for_oauth_[index]; 450 case RETAINED_FILES_DETAILS: 451 return is_showing_details_for_retained_files_; 452 } 453 return false; 454 } 455 456 size_t ExtensionInstallPrompt::Prompt::GetOAuthIssueCount() const { 457 return oauth_issue_advice_.size(); 458 } 459 460 const IssueAdviceInfoEntry& ExtensionInstallPrompt::Prompt::GetOAuthIssue( 461 size_t index) const { 462 CHECK_LT(index, oauth_issue_advice_.size()); 463 return oauth_issue_advice_[index]; 464 } 465 466 size_t ExtensionInstallPrompt::Prompt::GetRetainedFileCount() const { 467 return retained_files_.size(); 468 } 469 470 base::string16 ExtensionInstallPrompt::Prompt::GetRetainedFile(size_t index) 471 const { 472 CHECK_LT(index, retained_files_.size()); 473 return retained_files_[index].AsUTF16Unsafe(); 474 } 475 476 bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeFilesButton() const { 477 return !retained_files_.empty(); 478 } 479 480 ExtensionInstallPrompt::ShowParams::ShowParams(content::WebContents* contents) 481 : parent_web_contents(contents), 482 parent_window(NativeWindowForWebContents(contents)), 483 navigator(contents) { 484 } 485 486 ExtensionInstallPrompt::ShowParams::ShowParams( 487 gfx::NativeWindow window, 488 content::PageNavigator* navigator) 489 : parent_web_contents(NULL), 490 parent_window(window), 491 navigator(navigator) { 492 } 493 494 // static 495 scoped_refptr<Extension> 496 ExtensionInstallPrompt::GetLocalizedExtensionForDisplay( 497 const DictionaryValue* manifest, 498 int flags, 499 const std::string& id, 500 const std::string& localized_name, 501 const std::string& localized_description, 502 std::string* error) { 503 scoped_ptr<DictionaryValue> localized_manifest; 504 if (!localized_name.empty() || !localized_description.empty()) { 505 localized_manifest.reset(manifest->DeepCopy()); 506 if (!localized_name.empty()) { 507 localized_manifest->SetString(extensions::manifest_keys::kName, 508 localized_name); 509 } 510 if (!localized_description.empty()) { 511 localized_manifest->SetString(extensions::manifest_keys::kDescription, 512 localized_description); 513 } 514 } 515 516 return Extension::Create( 517 base::FilePath(), 518 Manifest::INTERNAL, 519 localized_manifest.get() ? *localized_manifest.get() : *manifest, 520 flags, 521 id, 522 error); 523 } 524 525 ExtensionInstallPrompt::ExtensionInstallPrompt(content::WebContents* contents) 526 : record_oauth2_grant_(false), 527 ui_loop_(base::MessageLoop::current()), 528 extension_(NULL), 529 bundle_(NULL), 530 install_ui_(ExtensionInstallUI::Create(ProfileForWebContents(contents))), 531 show_params_(contents), 532 delegate_(NULL), 533 prompt_(UNSET_PROMPT_TYPE) { 534 prompt_.SetUserNameFromProfile(install_ui_->profile()); 535 } 536 537 ExtensionInstallPrompt::ExtensionInstallPrompt( 538 Profile* profile, 539 gfx::NativeWindow native_window, 540 content::PageNavigator* navigator) 541 : record_oauth2_grant_(false), 542 ui_loop_(base::MessageLoop::current()), 543 extension_(NULL), 544 bundle_(NULL), 545 install_ui_(ExtensionInstallUI::Create(profile)), 546 show_params_(native_window, navigator), 547 delegate_(NULL), 548 prompt_(UNSET_PROMPT_TYPE) { 549 prompt_.SetUserNameFromProfile(install_ui_->profile()); 550 } 551 552 ExtensionInstallPrompt::~ExtensionInstallPrompt() { 553 } 554 555 void ExtensionInstallPrompt::ConfirmBundleInstall( 556 extensions::BundleInstaller* bundle, 557 const PermissionSet* permissions) { 558 DCHECK(ui_loop_ == base::MessageLoop::current()); 559 bundle_ = bundle; 560 permissions_ = permissions; 561 delegate_ = bundle; 562 prompt_.set_type(BUNDLE_INSTALL_PROMPT); 563 564 ShowConfirmation(); 565 } 566 567 void ExtensionInstallPrompt::ConfirmStandaloneInstall( 568 Delegate* delegate, 569 const Extension* extension, 570 SkBitmap* icon, 571 const ExtensionInstallPrompt::Prompt& prompt) { 572 DCHECK(ui_loop_ == base::MessageLoop::current()); 573 extension_ = extension; 574 permissions_ = extension->GetActivePermissions(); 575 delegate_ = delegate; 576 prompt_ = prompt; 577 578 SetIcon(icon); 579 ShowConfirmation(); 580 } 581 582 void ExtensionInstallPrompt::ConfirmWebstoreInstall( 583 Delegate* delegate, 584 const Extension* extension, 585 const SkBitmap* icon, 586 const ShowDialogCallback& show_dialog_callback) { 587 // SetIcon requires |extension_| to be set. ConfirmInstall will setup the 588 // remaining fields. 589 extension_ = extension; 590 SetIcon(icon); 591 ConfirmInstall(delegate, extension, show_dialog_callback); 592 } 593 594 void ExtensionInstallPrompt::ConfirmInstall( 595 Delegate* delegate, 596 const Extension* extension, 597 const ShowDialogCallback& show_dialog_callback) { 598 DCHECK(ui_loop_ == base::MessageLoop::current()); 599 extension_ = extension; 600 permissions_ = extension->GetActivePermissions(); 601 delegate_ = delegate; 602 prompt_.set_type(INSTALL_PROMPT); 603 show_dialog_callback_ = show_dialog_callback; 604 605 // We special-case themes to not show any confirm UI. Instead they are 606 // immediately installed, and then we show an infobar (see OnInstallSuccess) 607 // to allow the user to revert if they don't like it. 608 // 609 // We don't do this in the case where off-store extension installs are 610 // disabled because in that case, we don't show the dangerous download UI, so 611 // we need the UI confirmation. 612 if (extension->is_theme()) { 613 if (extension->from_webstore() || 614 extensions::FeatureSwitch::easy_off_store_install()->IsEnabled()) { 615 delegate->InstallUIProceed(); 616 return; 617 } 618 } 619 620 LoadImageIfNeeded(); 621 } 622 623 void ExtensionInstallPrompt::ConfirmReEnable(Delegate* delegate, 624 const Extension* extension) { 625 DCHECK(ui_loop_ == base::MessageLoop::current()); 626 extension_ = extension; 627 permissions_ = extension->GetActivePermissions(); 628 delegate_ = delegate; 629 prompt_.set_type(RE_ENABLE_PROMPT); 630 631 LoadImageIfNeeded(); 632 } 633 634 void ExtensionInstallPrompt::ConfirmExternalInstall( 635 Delegate* delegate, 636 const Extension* extension, 637 const ShowDialogCallback& show_dialog_callback) { 638 DCHECK(ui_loop_ == base::MessageLoop::current()); 639 extension_ = extension; 640 permissions_ = extension->GetActivePermissions(); 641 delegate_ = delegate; 642 prompt_.set_type(EXTERNAL_INSTALL_PROMPT); 643 show_dialog_callback_ = show_dialog_callback; 644 645 LoadImageIfNeeded(); 646 } 647 648 void ExtensionInstallPrompt::ConfirmPermissions( 649 Delegate* delegate, 650 const Extension* extension, 651 const PermissionSet* permissions) { 652 DCHECK(ui_loop_ == base::MessageLoop::current()); 653 extension_ = extension; 654 permissions_ = permissions; 655 delegate_ = delegate; 656 prompt_.set_type(PERMISSIONS_PROMPT); 657 658 LoadImageIfNeeded(); 659 } 660 661 void ExtensionInstallPrompt::ConfirmIssueAdvice( 662 Delegate* delegate, 663 const Extension* extension, 664 const IssueAdviceInfo& issue_advice) { 665 DCHECK(ui_loop_ == base::MessageLoop::current()); 666 extension_ = extension; 667 delegate_ = delegate; 668 prompt_.set_type(PERMISSIONS_PROMPT); 669 670 record_oauth2_grant_ = true; 671 prompt_.SetOAuthIssueAdvice(issue_advice); 672 673 LoadImageIfNeeded(); 674 } 675 676 void ExtensionInstallPrompt::ReviewPermissions( 677 Delegate* delegate, 678 const Extension* extension, 679 const std::vector<base::FilePath>& retained_file_paths) { 680 DCHECK(ui_loop_ == base::MessageLoop::current()); 681 extension_ = extension; 682 permissions_ = extension->GetActivePermissions(); 683 prompt_.set_retained_files(retained_file_paths); 684 delegate_ = delegate; 685 prompt_.set_type(POST_INSTALL_PERMISSIONS_PROMPT); 686 687 LoadImageIfNeeded(); 688 } 689 690 void ExtensionInstallPrompt::OnInstallSuccess(const Extension* extension, 691 SkBitmap* icon) { 692 extension_ = extension; 693 SetIcon(icon); 694 695 install_ui_->OnInstallSuccess(extension, &icon_); 696 } 697 698 void ExtensionInstallPrompt::OnInstallFailure( 699 const extensions::CrxInstallerError& error) { 700 install_ui_->OnInstallFailure(error); 701 } 702 703 void ExtensionInstallPrompt::SetIcon(const SkBitmap* image) { 704 if (image) 705 icon_ = *image; 706 else 707 icon_ = SkBitmap(); 708 if (icon_.empty()) { 709 // Let's set default icon bitmap whose size is equal to the default icon's 710 // pixel size under maximal supported scale factor. If the bitmap is larger 711 // than the one we need, it will be scaled down by the ui code. 712 icon_ = GetDefaultIconBitmapForMaxScaleFactor(extension_->is_app()); 713 } 714 } 715 716 void ExtensionInstallPrompt::OnImageLoaded(const gfx::Image& image) { 717 SetIcon(image.IsEmpty() ? NULL : image.ToSkBitmap()); 718 ShowConfirmation(); 719 } 720 721 void ExtensionInstallPrompt::LoadImageIfNeeded() { 722 // Bundle install prompts do not have an icon. 723 // Also |install_ui_.profile()| can be NULL in unit tests. 724 if (!icon_.empty() || !install_ui_->profile()) { 725 ShowConfirmation(); 726 return; 727 } 728 729 // Load the image asynchronously. For the response, check OnImageLoaded. 730 extensions::ExtensionResource image = extensions::IconsInfo::GetIconResource( 731 extension_, 732 extension_misc::EXTENSION_ICON_LARGE, 733 ExtensionIconSet::MATCH_BIGGER); 734 // Load the icon whose pixel size is large enough to be displayed under 735 // maximal supported scale factor. UI code will scale the icon down if needed. 736 // TODO(tbarzic): We should use IconImage here and load the required bitmap 737 // lazily. 738 int pixel_size = GetSizeForMaxScaleFactor(kIconSize); 739 extensions::ImageLoader::Get(install_ui_->profile())->LoadImageAsync( 740 extension_, image, gfx::Size(pixel_size, pixel_size), 741 base::Bind(&ExtensionInstallPrompt::OnImageLoaded, AsWeakPtr())); 742 } 743 744 void ExtensionInstallPrompt::OnGetTokenSuccess( 745 const OAuth2TokenService::Request* request, 746 const std::string& access_token, 747 const base::Time& expiration_time) { 748 DCHECK_EQ(login_token_request_.get(), request); 749 login_token_request_.reset(); 750 751 const extensions::OAuth2Info& oauth2_info = 752 extensions::OAuth2Info::GetOAuth2Info(extension_); 753 754 token_flow_.reset(new OAuth2MintTokenFlow( 755 install_ui_->profile()->GetRequestContext(), 756 this, 757 OAuth2MintTokenFlow::Parameters( 758 access_token, 759 extension_->id(), 760 oauth2_info.client_id, 761 oauth2_info.scopes, 762 OAuth2MintTokenFlow::MODE_ISSUE_ADVICE))); 763 token_flow_->Start(); 764 } 765 766 void ExtensionInstallPrompt::OnGetTokenFailure( 767 const OAuth2TokenService::Request* request, 768 const GoogleServiceAuthError& error) { 769 DCHECK_EQ(login_token_request_.get(), request); 770 login_token_request_.reset(); 771 ShowConfirmation(); 772 } 773 774 void ExtensionInstallPrompt::OnIssueAdviceSuccess( 775 const IssueAdviceInfo& advice_info) { 776 prompt_.SetOAuthIssueAdvice(advice_info); 777 record_oauth2_grant_ = true; 778 ShowConfirmation(); 779 } 780 781 void ExtensionInstallPrompt::OnMintTokenFailure( 782 const GoogleServiceAuthError& error) { 783 ShowConfirmation(); 784 } 785 786 void ExtensionInstallPrompt::ShowConfirmation() { 787 if (permissions_.get() && 788 (!extension_ || 789 !extensions::PermissionsData::ShouldSkipPermissionWarnings( 790 extension_))) { 791 Manifest::Type extension_type = extension_ ? 792 extension_->GetType() : Manifest::TYPE_UNKNOWN; 793 prompt_.SetPermissions( 794 extensions::PermissionMessageProvider::Get()-> 795 GetWarningMessages(permissions_, extension_type)); 796 prompt_.SetPermissionsDetails( 797 extensions::PermissionMessageProvider::Get()-> 798 GetWarningMessagesDetails(permissions_, extension_type)); 799 } 800 801 switch (prompt_.type()) { 802 case PERMISSIONS_PROMPT: 803 case RE_ENABLE_PROMPT: 804 case INLINE_INSTALL_PROMPT: 805 case EXTERNAL_INSTALL_PROMPT: 806 case INSTALL_PROMPT: 807 case LAUNCH_PROMPT: 808 case POST_INSTALL_PERMISSIONS_PROMPT: { 809 prompt_.set_extension(extension_); 810 prompt_.set_icon(gfx::Image::CreateFrom1xBitmap(icon_)); 811 break; 812 } 813 case BUNDLE_INSTALL_PROMPT: { 814 prompt_.set_bundle(bundle_); 815 break; 816 } 817 default: 818 NOTREACHED() << "Unknown message"; 819 return; 820 } 821 822 if (AutoConfirmPrompt(delegate_)) 823 return; 824 825 if (show_dialog_callback_.is_null()) 826 GetDefaultShowDialogCallback().Run(show_params_, delegate_, prompt_); 827 else 828 show_dialog_callback_.Run(show_params_, delegate_, prompt_); 829 } 830