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