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