1 // Copyright (c) 2011 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/extensions/extension_install_ui.h" 6 7 #include <map> 8 9 #include "base/command_line.h" 10 #include "base/file_util.h" 11 #include "base/string_number_conversions.h" 12 #include "base/string_util.h" 13 #include "base/stringprintf.h" 14 #include "base/utf_string_conversions.h" 15 #include "chrome/browser/extensions/extension_install_dialog.h" 16 #include "chrome/browser/extensions/theme_installed_infobar_delegate.h" 17 #include "chrome/browser/platform_util.h" 18 #include "chrome/browser/profiles/profile.h" 19 #include "chrome/browser/tabs/tab_strip_model.h" 20 #include "chrome/browser/themes/theme_service_factory.h" 21 #include "chrome/browser/ui/browser.h" 22 #include "chrome/browser/ui/browser_dialogs.h" 23 #include "chrome/browser/ui/browser_list.h" 24 #include "chrome/browser/ui/browser_window.h" 25 #include "chrome/common/extensions/extension.h" 26 #include "chrome/common/extensions/extension_icon_set.h" 27 #include "chrome/common/extensions/extension_resource.h" 28 #include "chrome/common/extensions/url_pattern.h" 29 #include "chrome/common/url_constants.h" 30 #include "content/browser/tab_contents/tab_contents.h" 31 #include "content/common/notification_service.h" 32 #include "grit/chromium_strings.h" 33 #include "grit/generated_resources.h" 34 #include "grit/theme_resources.h" 35 #include "ui/base/l10n/l10n_util.h" 36 37 #if defined(TOOLKIT_GTK) 38 #include "chrome/browser/extensions/gtk_theme_installed_infobar_delegate.h" 39 #include "chrome/browser/ui/gtk/gtk_theme_service.h" 40 #endif 41 42 // static 43 const int ExtensionInstallUI::kTitleIds[NUM_PROMPT_TYPES] = { 44 IDS_EXTENSION_INSTALL_PROMPT_TITLE, 45 IDS_EXTENSION_RE_ENABLE_PROMPT_TITLE 46 }; 47 // static 48 const int ExtensionInstallUI::kHeadingIds[NUM_PROMPT_TYPES] = { 49 IDS_EXTENSION_INSTALL_PROMPT_HEADING, 50 IDS_EXTENSION_RE_ENABLE_PROMPT_HEADING 51 }; 52 // static 53 const int ExtensionInstallUI::kButtonIds[NUM_PROMPT_TYPES] = { 54 IDS_EXTENSION_PROMPT_INSTALL_BUTTON, 55 IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON 56 }; 57 // static 58 const int ExtensionInstallUI::kWarningIds[NUM_PROMPT_TYPES] = { 59 IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO, 60 IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO 61 }; 62 63 namespace { 64 65 // Size of extension icon in top left of dialog. 66 const int kIconSize = 69; 67 68 // Shows the application install animation on the new tab page for the app 69 // with |app_id|. If a NTP already exists on the active |browser|, this will 70 // select that tab and show the animation there. Otherwise, it will create 71 // a new NTP. 72 void ShowAppInstalledAnimation(Browser* browser, const std::string& app_id) { 73 // Select an already open NTP, if there is one. Existing NTPs will 74 // automatically show the install animation for any new apps. 75 for (int i = 0; i < browser->tab_count(); ++i) { 76 TabContents* tab_contents = browser->GetTabContentsAt(i); 77 GURL url = tab_contents->GetURL(); 78 if (StartsWithASCII(url.spec(), chrome::kChromeUINewTabURL, false)) { 79 browser->ActivateTabAt(i, false); 80 return; 81 } 82 } 83 84 // If there isn't an NTP, open one and pass it the ID of the installed app. 85 std::string url = base::StringPrintf( 86 "%s/#app-id=%s", chrome::kChromeUINewTabURL, app_id.c_str()); 87 browser->AddSelectedTabWithURL(GURL(url), PageTransition::TYPED); 88 } 89 90 } // namespace 91 92 ExtensionInstallUI::ExtensionInstallUI(Profile* profile) 93 : profile_(profile), 94 ui_loop_(MessageLoop::current()), 95 previous_use_system_theme_(false), 96 extension_(NULL), 97 delegate_(NULL), 98 prompt_type_(NUM_PROMPT_TYPES), 99 ALLOW_THIS_IN_INITIALIZER_LIST(tracker_(this)) { 100 // Remember the current theme in case the user presses undo. 101 if (profile_) { 102 const Extension* previous_theme = 103 ThemeServiceFactory::GetThemeForProfile(profile_); 104 if (previous_theme) 105 previous_theme_id_ = previous_theme->id(); 106 #if defined(TOOLKIT_GTK) 107 // On Linux, we also need to take the user's system settings into account 108 // to undo theme installation. 109 previous_use_system_theme_ = 110 GtkThemeService::GetFrom(profile_)->UseGtkTheme(); 111 #else 112 DCHECK(!previous_use_system_theme_); 113 #endif 114 } 115 } 116 117 ExtensionInstallUI::~ExtensionInstallUI() { 118 } 119 120 void ExtensionInstallUI::ConfirmInstall(Delegate* delegate, 121 const Extension* extension) { 122 DCHECK(ui_loop_ == MessageLoop::current()); 123 extension_ = extension; 124 delegate_ = delegate; 125 126 // We special-case themes to not show any confirm UI. Instead they are 127 // immediately installed, and then we show an infobar (see OnInstallSuccess) 128 // to allow the user to revert if they don't like it. 129 if (extension->is_theme()) { 130 delegate->InstallUIProceed(); 131 return; 132 } 133 134 ShowConfirmation(INSTALL_PROMPT); 135 } 136 137 void ExtensionInstallUI::ConfirmReEnable(Delegate* delegate, 138 const Extension* extension) { 139 DCHECK(ui_loop_ == MessageLoop::current()); 140 extension_ = extension; 141 delegate_ = delegate; 142 143 ShowConfirmation(RE_ENABLE_PROMPT); 144 } 145 146 void ExtensionInstallUI::OnInstallSuccess(const Extension* extension, 147 SkBitmap* icon) { 148 extension_ = extension; 149 SetIcon(icon); 150 151 if (extension->is_theme()) { 152 ShowThemeInfoBar(previous_theme_id_, previous_use_system_theme_, 153 extension, profile_); 154 return; 155 } 156 157 // Extensions aren't enabled by default in incognito so we confirm 158 // the install in a normal window. 159 Profile* profile = profile_->GetOriginalProfile(); 160 Browser* browser = Browser::GetOrCreateTabbedBrowser(profile); 161 if (browser->tab_count() == 0) 162 browser->AddBlankTab(true); 163 browser->window()->Show(); 164 165 if (extension->GetFullLaunchURL().is_valid()) { 166 ShowAppInstalledAnimation(browser, extension->id()); 167 return; 168 } 169 170 browser::ShowExtensionInstalledBubble(extension, browser, icon_, profile); 171 } 172 173 void ExtensionInstallUI::OnInstallFailure(const std::string& error) { 174 DCHECK(ui_loop_ == MessageLoop::current()); 175 176 Browser* browser = BrowserList::GetLastActiveWithProfile(profile_); 177 platform_util::SimpleErrorBox( 178 browser ? browser->window()->GetNativeHandle() : NULL, 179 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_FAILURE_TITLE), 180 UTF8ToUTF16(error)); 181 } 182 183 void ExtensionInstallUI::SetIcon(SkBitmap* image) { 184 if (image) 185 icon_ = *image; 186 else 187 icon_ = SkBitmap(); 188 if (icon_.empty()) 189 icon_ = Extension::GetDefaultIcon(extension_->is_app()); 190 } 191 192 void ExtensionInstallUI::OnImageLoaded( 193 SkBitmap* image, const ExtensionResource& resource, int index) { 194 SetIcon(image); 195 196 switch (prompt_type_) { 197 case RE_ENABLE_PROMPT: 198 case INSTALL_PROMPT: { 199 // TODO(jcivelli): http://crbug.com/44771 We should not show an install 200 // dialog when installing an app from the gallery. 201 NotificationService* service = NotificationService::current(); 202 service->Notify(NotificationType::EXTENSION_WILL_SHOW_CONFIRM_DIALOG, 203 Source<ExtensionInstallUI>(this), 204 NotificationService::NoDetails()); 205 206 std::vector<string16> warnings = 207 extension_->GetPermissionMessageStrings(); 208 ShowExtensionInstallDialog( 209 profile_, delegate_, extension_, &icon_, warnings, prompt_type_); 210 break; 211 } 212 default: 213 NOTREACHED() << "Unknown message"; 214 break; 215 } 216 } 217 218 void ExtensionInstallUI::ShowThemeInfoBar(const std::string& previous_theme_id, 219 bool previous_use_system_theme, 220 const Extension* new_theme, 221 Profile* profile) { 222 if (!new_theme->is_theme()) 223 return; 224 225 // Get last active normal browser of profile. 226 Browser* browser = BrowserList::FindBrowserWithType(profile, 227 Browser::TYPE_NORMAL, 228 true); 229 if (!browser) 230 return; 231 232 TabContents* tab_contents = browser->GetSelectedTabContents(); 233 if (!tab_contents) 234 return; 235 236 // First find any previous theme preview infobars. 237 InfoBarDelegate* old_delegate = NULL; 238 for (size_t i = 0; i < tab_contents->infobar_count(); ++i) { 239 InfoBarDelegate* delegate = tab_contents->GetInfoBarDelegateAt(i); 240 ThemeInstalledInfoBarDelegate* theme_infobar = 241 delegate->AsThemePreviewInfobarDelegate(); 242 if (theme_infobar) { 243 // If the user installed the same theme twice, ignore the second install 244 // and keep the first install info bar, so that they can easily undo to 245 // get back the previous theme. 246 if (theme_infobar->MatchesTheme(new_theme)) 247 return; 248 old_delegate = delegate; 249 break; 250 } 251 } 252 253 // Then either replace that old one or add a new one. 254 InfoBarDelegate* new_delegate = GetNewThemeInstalledInfoBarDelegate( 255 tab_contents, new_theme, previous_theme_id, previous_use_system_theme); 256 257 if (old_delegate) 258 tab_contents->ReplaceInfoBar(old_delegate, new_delegate); 259 else 260 tab_contents->AddInfoBar(new_delegate); 261 } 262 263 void ExtensionInstallUI::ShowConfirmation(PromptType prompt_type) { 264 // Load the image asynchronously. For the response, check OnImageLoaded. 265 prompt_type_ = prompt_type; 266 ExtensionResource image = 267 extension_->GetIconResource(Extension::EXTENSION_ICON_LARGE, 268 ExtensionIconSet::MATCH_EXACTLY); 269 tracker_.LoadImage(extension_, image, 270 gfx::Size(kIconSize, kIconSize), 271 ImageLoadingTracker::DONT_CACHE); 272 } 273 274 InfoBarDelegate* ExtensionInstallUI::GetNewThemeInstalledInfoBarDelegate( 275 TabContents* tab_contents, 276 const Extension* new_theme, 277 const std::string& previous_theme_id, 278 bool previous_use_system_theme) { 279 #if defined(TOOLKIT_GTK) 280 return new GtkThemeInstalledInfoBarDelegate(tab_contents, new_theme, 281 previous_theme_id, previous_use_system_theme); 282 #else 283 return new ThemeInstalledInfoBarDelegate(tab_contents, new_theme, 284 previous_theme_id); 285 #endif 286 } 287