Home | History | Annotate | Download | only in extensions
      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