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