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