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/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 
     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.
    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.
    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 
    173 Profile* ProfileForWebContents(content::WebContents* web_contents) {
    174   if (!web_contents)
    175     return NULL;
    176   return Profile::FromBrowserContext(web_contents->GetBrowserContext());
    177 }
    178 
    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::
    189     InstallPromptPermissions() {
    190 }
    191 ExtensionInstallPrompt::Prompt::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.
    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 
    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 
    240 ExtensionInstallPrompt::Prompt::~Prompt() {
    241 }
    242 
    243 void ExtensionInstallPrompt::Prompt::SetPermissions(
    244     const std::vector<base::string16>& permissions,
    245     PermissionsType permissions_type) {
    246   GetPermissionsForType(permissions_type).permissions = permissions;
    247 }
    248 
    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 
    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 
    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 
    291 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 
    319 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 
    339 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 
    348 bool ExtensionInstallPrompt::Prompt::ShouldShowExplanationText() const {
    349   return type_ == INSTALL_PROMPT && extension_->is_extension() &&
    350          experiment_.get() && experiment_->text_only();
    351 }
    352 
    353 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 
    363 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 
    379 bool ExtensionInstallPrompt::Prompt::HasAbortButtonLabel() const {
    380   if (ShouldShowExplanationText())
    381     return true;
    382   return kAbortButtonIds[type_] > 0;
    383 }
    384 
    385 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 
    392 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 
    406 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 
    422 bool ExtensionInstallPrompt::Prompt::ShouldShowPermissions() const {
    423   return GetPermissionCount(ALL_PERMISSIONS) > 0 ||
    424          type_ == POST_INSTALL_PERMISSIONS_PROMPT;
    425 }
    426 
    427 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 
    456 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 
    462 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 
    472 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 
    488 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 
    504 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 
    513 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 
    522 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 
    537 size_t ExtensionInstallPrompt::Prompt::GetRetainedFileCount() const {
    538   return retained_files_.size();
    539 }
    540 
    541 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&
    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&
    556 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 
    563 bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeFilesButton() const {
    564   return !retained_files_.empty();
    565 }
    566 
    567 ExtensionInstallPrompt::ShowParams::ShowParams(content::WebContents* contents)
    568     : parent_web_contents(contents),
    569       parent_window(NativeWindowForWebContents(contents)),
    570       navigator(contents) {
    571 }
    572 
    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>
    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 
    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 
    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 
    633 ExtensionInstallPrompt::~ExtensionInstallPrompt() {
    634 }
    635 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    774 void ExtensionInstallPrompt::OnInstallFailure(
    775     const extensions::CrxInstallerError& error) {
    776   install_ui_->OnInstallFailure(error);
    777 }
    778 
    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 
    792 void ExtensionInstallPrompt::OnImageLoaded(const gfx::Image& image) {
    793   SetIcon(image.IsEmpty() ? NULL : image.ToSkBitmap());
    794   ShowConfirmation();
    795 }
    796 
    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 
    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