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/ui/extensions/extension_install_ui_default.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/strings/utf_string_conversions.h"
      9 #include "chrome/browser/chrome_notification_types.h"
     10 #include "chrome/browser/extensions/extension_install_prompt.h"
     11 #include "chrome/browser/extensions/theme_installed_infobar_delegate.h"
     12 #include "chrome/browser/infobars/infobar_service.h"
     13 #include "chrome/browser/prefs/incognito_mode_prefs.h"
     14 #include "chrome/browser/profiles/profile.h"
     15 #include "chrome/browser/themes/theme_service.h"
     16 #include "chrome/browser/themes/theme_service_factory.h"
     17 #include "chrome/browser/ui/app_list/app_list_service.h"
     18 #include "chrome/browser/ui/app_list/app_list_util.h"
     19 #include "chrome/browser/ui/browser.h"
     20 #include "chrome/browser/ui/browser_dialogs.h"
     21 #include "chrome/browser/ui/browser_finder.h"
     22 #include "chrome/browser/ui/browser_navigator.h"
     23 #include "chrome/browser/ui/browser_tabstrip.h"
     24 #include "chrome/browser/ui/browser_window.h"
     25 #include "chrome/browser/ui/host_desktop.h"
     26 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
     27 #include "chrome/browser/ui/simple_message_box.h"
     28 #include "chrome/browser/ui/singleton_tabs.h"
     29 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     30 #include "chrome/common/url_constants.h"
     31 #include "chrome/grit/generated_resources.h"
     32 #include "components/infobars/core/confirm_infobar_delegate.h"
     33 #include "components/infobars/core/infobar.h"
     34 #include "components/search/search.h"
     35 #include "content/public/browser/browser_thread.h"
     36 #include "content/public/browser/notification_service.h"
     37 #include "content/public/browser/web_contents.h"
     38 #include "extensions/common/extension.h"
     39 #include "grit/components_strings.h"
     40 #include "ui/base/l10n/l10n_util.h"
     41 
     42 #if defined(USE_ASH)
     43 #include "ash/shell.h"
     44 #endif
     45 
     46 using content::BrowserThread;
     47 using content::WebContents;
     48 using extensions::Extension;
     49 
     50 namespace {
     51 
     52 // Helpers --------------------------------------------------------------------
     53 
     54 Browser* FindOrCreateVisibleBrowser(Profile* profile) {
     55   // TODO(mpcomplete): remove this workaround for http://crbug.com/244246
     56   // after fixing http://crbug.com/38676.
     57   if (!IncognitoModePrefs::CanOpenBrowser(profile))
     58     return NULL;
     59   chrome::ScopedTabbedBrowserDisplayer displayer(
     60       profile, chrome::GetActiveDesktop());
     61   Browser* browser = displayer.browser();
     62   if (browser->tab_strip_model()->count() == 0)
     63     chrome::AddTabAt(browser, GURL(), -1, true);
     64   return browser;
     65 }
     66 
     67 void ShowExtensionInstalledBubble(const extensions::Extension* extension,
     68                                   Profile* profile,
     69                                   const SkBitmap& icon) {
     70   Browser* browser = FindOrCreateVisibleBrowser(profile);
     71   if (browser)
     72     chrome::ShowExtensionInstalledBubble(extension, browser, icon);
     73 }
     74 
     75 
     76 // ErrorInfoBarDelegate -------------------------------------------------------
     77 
     78 // Helper class to put up an infobar when installation fails.
     79 class ErrorInfoBarDelegate : public ConfirmInfoBarDelegate {
     80  public:
     81   // Creates an error infobar and delegate and adds the infobar to
     82   // |infobar_service|.
     83   static void Create(InfoBarService* infobar_service,
     84                      const extensions::CrxInstallerError& error);
     85 
     86  private:
     87   explicit ErrorInfoBarDelegate(const extensions::CrxInstallerError& error);
     88   virtual ~ErrorInfoBarDelegate();
     89 
     90   // ConfirmInfoBarDelegate:
     91   virtual base::string16 GetMessageText() const OVERRIDE;
     92   virtual int GetButtons() const OVERRIDE;
     93   virtual base::string16 GetLinkText() const OVERRIDE;
     94   virtual bool LinkClicked(WindowOpenDisposition disposition) OVERRIDE;
     95 
     96   extensions::CrxInstallerError error_;
     97 
     98   DISALLOW_COPY_AND_ASSIGN(ErrorInfoBarDelegate);
     99 };
    100 
    101 // static
    102 void ErrorInfoBarDelegate::Create(InfoBarService* infobar_service,
    103                                   const extensions::CrxInstallerError& error) {
    104   infobar_service->AddInfoBar(ConfirmInfoBarDelegate::CreateInfoBar(
    105       scoped_ptr<ConfirmInfoBarDelegate>(new ErrorInfoBarDelegate(error))));
    106 }
    107 
    108 ErrorInfoBarDelegate::ErrorInfoBarDelegate(
    109     const extensions::CrxInstallerError& error)
    110     : ConfirmInfoBarDelegate(),
    111       error_(error) {
    112 }
    113 
    114 ErrorInfoBarDelegate::~ErrorInfoBarDelegate() {
    115 }
    116 
    117 base::string16 ErrorInfoBarDelegate::GetMessageText() const {
    118   return error_.message();
    119 }
    120 
    121 int ErrorInfoBarDelegate::GetButtons() const {
    122   return BUTTON_OK;
    123 }
    124 
    125 base::string16 ErrorInfoBarDelegate::GetLinkText() const {
    126   return (error_.type() == extensions::CrxInstallerError::ERROR_OFF_STORE) ?
    127       l10n_util::GetStringUTF16(IDS_LEARN_MORE) : base::string16();
    128 }
    129 
    130 bool ErrorInfoBarDelegate::LinkClicked(WindowOpenDisposition disposition) {
    131   InfoBarService::WebContentsFromInfoBar(infobar())->OpenURL(
    132       content::OpenURLParams(
    133           GURL("http://support.google.com/chrome_webstore/?p=crx_warning"),
    134           content::Referrer(),
    135           (disposition == CURRENT_TAB) ? NEW_FOREGROUND_TAB : disposition,
    136           ui::PAGE_TRANSITION_LINK, false));
    137   return false;
    138 }
    139 
    140 }  // namespace
    141 
    142 
    143 // ExtensionInstallUI ---------------------------------------------------------
    144 
    145 // static
    146 ExtensionInstallUI* ExtensionInstallUI::Create(Profile* profile) {
    147   return new ExtensionInstallUIDefault(profile);
    148 }
    149 
    150 // static
    151 void ExtensionInstallUI::OpenAppInstalledUI(Profile* profile,
    152                                             const std::string& app_id) {
    153 #if defined(OS_CHROMEOS)
    154   AppListService::Get(chrome::HOST_DESKTOP_TYPE_ASH)->
    155       ShowForProfile(profile);
    156 
    157   content::NotificationService::current()->Notify(
    158       chrome::NOTIFICATION_APP_INSTALLED_TO_APPLIST,
    159       content::Source<Profile>(profile),
    160       content::Details<const std::string>(&app_id));
    161 #else
    162   Browser* browser = FindOrCreateVisibleBrowser(profile);
    163   if (browser) {
    164     GURL url(chrome::IsInstantExtendedAPIEnabled() ?
    165              chrome::kChromeUIAppsURL : chrome::kChromeUINewTabURL);
    166     chrome::NavigateParams params(
    167         chrome::GetSingletonTabNavigateParams(browser, url));
    168     chrome::Navigate(&params);
    169 
    170     content::NotificationService::current()->Notify(
    171         chrome::NOTIFICATION_APP_INSTALLED_TO_NTP,
    172         content::Source<WebContents>(params.target_contents),
    173         content::Details<const std::string>(&app_id));
    174   }
    175 #endif
    176 }
    177 
    178 // static
    179 ExtensionInstallPrompt* ExtensionInstallUI::CreateInstallPromptWithBrowser(
    180     Browser* browser) {
    181   content::WebContents* web_contents = NULL;
    182   if (browser)
    183     web_contents = browser->tab_strip_model()->GetActiveWebContents();
    184   return new ExtensionInstallPrompt(web_contents);
    185 }
    186 
    187 // static
    188 ExtensionInstallPrompt* ExtensionInstallUI::CreateInstallPromptWithProfile(
    189     Profile* profile) {
    190   Browser* browser = chrome::FindLastActiveWithProfile(profile,
    191       chrome::GetActiveDesktop());
    192   if (browser)
    193     return CreateInstallPromptWithBrowser(browser);
    194   // No browser window is open yet. Create a free-standing dialog associated
    195   // with |profile|.
    196   return new ExtensionInstallPrompt(profile, NULL, NULL);
    197 }
    198 
    199 
    200 // ExtensionInstallUIDefault --------------------------------------------------
    201 
    202 ExtensionInstallUIDefault::ExtensionInstallUIDefault(Profile* profile)
    203     : ExtensionInstallUI(profile),
    204       previous_using_system_theme_(false),
    205       use_app_installed_bubble_(false) {
    206   // |profile| can be NULL during tests.
    207   if (profile) {
    208     // Remember the current theme in case the user presses undo.
    209     const Extension* previous_theme =
    210         ThemeServiceFactory::GetThemeForProfile(profile);
    211     if (previous_theme)
    212       previous_theme_id_ = previous_theme->id();
    213     previous_using_system_theme_ =
    214         ThemeServiceFactory::GetForProfile(profile)->UsingSystemTheme();
    215   }
    216 }
    217 
    218 ExtensionInstallUIDefault::~ExtensionInstallUIDefault() {}
    219 
    220 void ExtensionInstallUIDefault::OnInstallSuccess(const Extension* extension,
    221                                                  const SkBitmap* icon) {
    222   if (skip_post_install_ui())
    223     return;
    224 
    225   if (!profile()) {
    226     // TODO(zelidrag): Figure out what exact conditions cause crash
    227     // http://crbug.com/159437 and write browser test to cover it.
    228     NOTREACHED();
    229     return;
    230   }
    231 
    232   if (extension->is_theme()) {
    233     ThemeInstalledInfoBarDelegate::Create(
    234         extension, profile(), previous_theme_id_, previous_using_system_theme_);
    235     return;
    236   }
    237 
    238   // Extensions aren't enabled by default in incognito so we confirm
    239   // the install in a normal window.
    240   Profile* current_profile = profile()->GetOriginalProfile();
    241   if (extension->is_app()) {
    242     bool use_bubble = false;
    243 
    244 #if defined(TOOLKIT_VIEWS)  || defined(OS_MACOSX)
    245     use_bubble = use_app_installed_bubble_;
    246 #endif
    247 
    248     if (IsAppLauncherEnabled()) {
    249       // TODO(tapted): ExtensionInstallUI should retain the desktop type from
    250       // the browser used to initiate the flow. http://crbug.com/308360.
    251       AppListService::Get(chrome::GetActiveDesktop())->
    252           ShowForProfile(current_profile);
    253 
    254       content::NotificationService::current()->Notify(
    255           chrome::NOTIFICATION_APP_INSTALLED_TO_APPLIST,
    256           content::Source<Profile>(current_profile),
    257           content::Details<const std::string>(&extension->id()));
    258       return;
    259     }
    260 
    261     if (use_bubble) {
    262       ShowExtensionInstalledBubble(extension, current_profile, *icon);
    263       return;
    264     }
    265 
    266     ExtensionInstallUI::OpenAppInstalledUI(current_profile, extension->id());
    267     return;
    268   }
    269 
    270   ShowExtensionInstalledBubble(extension, current_profile, *icon);
    271 }
    272 
    273 void ExtensionInstallUIDefault::OnInstallFailure(
    274     const extensions::CrxInstallerError& error) {
    275   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    276   if (disable_failure_ui_for_tests() || skip_post_install_ui())
    277     return;
    278 
    279   Browser* browser =
    280       chrome::FindLastActiveWithProfile(profile(), chrome::GetActiveDesktop());
    281   if (!browser)  // Can be NULL in unittests.
    282     return;
    283   WebContents* web_contents =
    284       browser->tab_strip_model()->GetActiveWebContents();
    285   if (!web_contents)
    286     return;
    287   ErrorInfoBarDelegate::Create(InfoBarService::FromWebContents(web_contents),
    288                                error);
    289 }
    290 
    291 void ExtensionInstallUIDefault::SetUseAppInstalledBubble(bool use_bubble) {
    292   use_app_installed_bubble_ = use_bubble;
    293 }
    294