• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
ShowAppInstalledAnimation(Browser * browser,const std::string & app_id)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 
ExtensionInstallUI(Profile * profile)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 
~ExtensionInstallUI()117 ExtensionInstallUI::~ExtensionInstallUI() {
118 }
119 
ConfirmInstall(Delegate * delegate,const Extension * extension)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 
ConfirmReEnable(Delegate * delegate,const Extension * extension)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 
OnInstallSuccess(const Extension * extension,SkBitmap * icon)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 
OnInstallFailure(const std::string & error)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 
SetIcon(SkBitmap * image)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 
OnImageLoaded(SkBitmap * image,const ExtensionResource & resource,int index)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 
ShowThemeInfoBar(const std::string & previous_theme_id,bool previous_use_system_theme,const Extension * new_theme,Profile * profile)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 
ShowConfirmation(PromptType prompt_type)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 
GetNewThemeInstalledInfoBarDelegate(TabContents * tab_contents,const Extension * new_theme,const std::string & previous_theme_id,bool previous_use_system_theme)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