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