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
48using extensions::BundleInstaller;
49using extensions::Extension;
50using extensions::Manifest;
51using extensions::PermissionSet;
52
53namespace {
54
55bool AllowWebstoreData(ExtensionInstallPrompt::PromptType type) {
56  return type == ExtensionInstallPrompt::INLINE_INSTALL_PROMPT ||
57         type == ExtensionInstallPrompt::EXTERNAL_INSTALL_PROMPT ||
58         type == ExtensionInstallPrompt::REPAIR_PROMPT;
59}
60
61static 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};
73static 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};
85static 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};
97static 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};
109static 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};
121static 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.
137SkBitmap 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.
147bool 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
173Profile* ProfileForWebContents(content::WebContents* web_contents) {
174  if (!web_contents)
175    return NULL;
176  return Profile::FromBrowserContext(web_contents->GetBrowserContext());
177}
178
179gfx::NativeWindow NativeWindowForWebContents(content::WebContents* contents) {
180  if (!contents)
181    return NULL;
182
183  return contents->GetTopLevelNativeWindow();
184}
185
186}  // namespace
187
188ExtensionInstallPrompt::Prompt::InstallPromptPermissions::
189    InstallPromptPermissions() {
190}
191ExtensionInstallPrompt::Prompt::InstallPromptPermissions::
192    ~InstallPromptPermissions() {
193}
194
195// static
196ExtensionInstallPrompt::AutoConfirmForTests
197ExtensionInstallPrompt::g_auto_confirm_for_tests = ExtensionInstallPrompt::NONE;
198
199// This should match the PromptType enum.
200std::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
229ExtensionInstallPrompt::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
240ExtensionInstallPrompt::Prompt::~Prompt() {
241}
242
243void ExtensionInstallPrompt::Prompt::SetPermissions(
244    const std::vector<base::string16>& permissions,
245    PermissionsType permissions_type) {
246  GetPermissionsForType(permissions_type).permissions = permissions;
247}
248
249void 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
260void 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
278void 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
291base::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
319base::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
339int 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
348bool ExtensionInstallPrompt::Prompt::ShouldShowExplanationText() const {
349  return type_ == INSTALL_PROMPT && extension_->is_extension() &&
350         experiment_.get() && experiment_->text_only();
351}
352
353bool 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
363base::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
379bool ExtensionInstallPrompt::Prompt::HasAbortButtonLabel() const {
380  if (ShouldShowExplanationText())
381    return true;
382  return kAbortButtonIds[type_] > 0;
383}
384
385base::string16 ExtensionInstallPrompt::Prompt::GetAbortButtonLabel() const {
386  CHECK(HasAbortButtonLabel());
387  if (ShouldShowExplanationText())
388    return experiment_->GetCancelButtonText();
389  return l10n_util::GetStringUTF16(kAbortButtonIds[type_]);
390}
391
392base::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
406base::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
422bool ExtensionInstallPrompt::Prompt::ShouldShowPermissions() const {
423  return GetPermissionCount(ALL_PERMISSIONS) > 0 ||
424         type_ == POST_INSTALL_PERMISSIONS_PROMPT;
425}
426
427void 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
456base::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
462base::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
472size_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
488size_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
504base::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
513base::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
522bool 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
537size_t ExtensionInstallPrompt::Prompt::GetRetainedFileCount() const {
538  return retained_files_.size();
539}
540
541base::string16 ExtensionInstallPrompt::Prompt::GetRetainedFile(size_t index)
542    const {
543  CHECK_LT(index, retained_files_.size());
544  return retained_files_[index].AsUTF16Unsafe();
545}
546
547ExtensionInstallPrompt::Prompt::InstallPromptPermissions&
548ExtensionInstallPrompt::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
555const ExtensionInstallPrompt::Prompt::InstallPromptPermissions&
556ExtensionInstallPrompt::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
563bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeFilesButton() const {
564  return !retained_files_.empty();
565}
566
567ExtensionInstallPrompt::ShowParams::ShowParams(content::WebContents* contents)
568    : parent_web_contents(contents),
569      parent_window(NativeWindowForWebContents(contents)),
570      navigator(contents) {
571}
572
573ExtensionInstallPrompt::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
582scoped_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
612ExtensionInstallPrompt::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
621ExtensionInstallPrompt::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
633ExtensionInstallPrompt::~ExtensionInstallPrompt() {
634}
635
636void 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
648void 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
662void 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
674void 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
702void 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
726void 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
740void 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
753void 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
766void ExtensionInstallPrompt::OnInstallSuccess(const Extension* extension,
767                                              SkBitmap* icon) {
768  extension_ = extension;
769  SetIcon(icon);
770
771  install_ui_->OnInstallSuccess(extension, &icon_);
772}
773
774void ExtensionInstallPrompt::OnInstallFailure(
775    const extensions::CrxInstallerError& error) {
776  install_ui_->OnInstallFailure(error);
777}
778
779void 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
792void ExtensionInstallPrompt::OnImageLoaded(const gfx::Image& image) {
793  SetIcon(image.IsEmpty() ? NULL : image.ToSkBitmap());
794  ShowConfirmation();
795}
796
797void 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
826void 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