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