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(¶ms); 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