Home | History | Annotate | Download | only in extensions
      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