Home | History | Annotate | Download | only in extensions
      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/extensions/extension_uninstall_dialog.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "base/compiler_specific.h"
      9 #include "base/strings/string_util.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "chrome/browser/ui/app_list/app_list_service.h"
     12 #include "chrome/browser/ui/views/constrained_window_views.h"
     13 #include "chrome/grit/generated_resources.h"
     14 #include "extensions/common/extension.h"
     15 #include "ui/aura/window_tracker.h"
     16 #include "ui/base/l10n/l10n_util.h"
     17 #include "ui/compositor/compositor.h"
     18 #include "ui/compositor/layer.h"
     19 #include "ui/views/controls/image_view.h"
     20 #include "ui/views/controls/label.h"
     21 #include "ui/views/layout/layout_constants.h"
     22 #include "ui/views/view.h"
     23 #include "ui/views/widget/widget.h"
     24 #include "ui/views/window/dialog_delegate.h"
     25 
     26 namespace {
     27 
     28 const int kRightColumnWidth = 210;
     29 const int kIconSize = 64;
     30 
     31 class ExtensionUninstallDialogDelegateView;
     32 
     33 // Views implementation of the uninstall dialog.
     34 class ExtensionUninstallDialogViews
     35     : public extensions::ExtensionUninstallDialog {
     36  public:
     37   ExtensionUninstallDialogViews(
     38       Profile* profile,
     39       aura::Window* parent,
     40       extensions::ExtensionUninstallDialog::Delegate* delegate);
     41   virtual ~ExtensionUninstallDialogViews();
     42 
     43   // Called when the ExtensionUninstallDialogDelegate has been destroyed to make
     44   // sure we invalidate pointers.
     45   void DialogDelegateDestroyed() { view_ = NULL; }
     46 
     47   // Forwards the accept and cancels to the delegate.
     48   void ExtensionUninstallAccepted();
     49   void ExtensionUninstallCanceled();
     50 
     51  private:
     52   virtual void Show() OVERRIDE;
     53 
     54   ExtensionUninstallDialogDelegateView* view_;
     55 
     56   // The dialog's parent window.
     57   aura::Window* parent_;
     58 
     59   // Tracks whether |parent_| got destroyed.
     60   aura::WindowTracker parent_window_tracker_;
     61 
     62   DISALLOW_COPY_AND_ASSIGN(ExtensionUninstallDialogViews);
     63 };
     64 
     65 // The dialog's view, owned by the views framework.
     66 class ExtensionUninstallDialogDelegateView : public views::DialogDelegateView {
     67  public:
     68   ExtensionUninstallDialogDelegateView(
     69       ExtensionUninstallDialogViews* dialog_view,
     70       const extensions::Extension* extension,
     71       const extensions::Extension* triggering_extension,
     72       gfx::ImageSkia* image);
     73   virtual ~ExtensionUninstallDialogDelegateView();
     74 
     75   // Called when the ExtensionUninstallDialog has been destroyed to make sure
     76   // we invalidate pointers.
     77   void DialogDestroyed() { dialog_ = NULL; }
     78 
     79  private:
     80   // views::DialogDelegate:
     81   virtual base::string16 GetDialogButtonLabel(
     82       ui::DialogButton button) const OVERRIDE;
     83   virtual int GetDefaultDialogButton() const OVERRIDE {
     84     // Default to accept when triggered via chrome://extensions page.
     85     return triggered_by_extension_ ?
     86         ui::DIALOG_BUTTON_CANCEL : ui::DIALOG_BUTTON_OK;
     87   }
     88   virtual bool Accept() OVERRIDE;
     89   virtual bool Cancel() OVERRIDE;
     90 
     91   // views::WidgetDelegate:
     92   virtual ui::ModalType GetModalType() const OVERRIDE {
     93     return ui::MODAL_TYPE_WINDOW;
     94   }
     95   virtual base::string16 GetWindowTitle() const OVERRIDE;
     96 
     97   // views::View:
     98   virtual gfx::Size GetPreferredSize() const OVERRIDE;
     99 
    100   virtual void Layout() OVERRIDE;
    101 
    102   ExtensionUninstallDialogViews* dialog_;
    103 
    104   views::ImageView* icon_;
    105   views::Label* heading_;
    106   bool triggered_by_extension_;
    107 
    108   DISALLOW_COPY_AND_ASSIGN(ExtensionUninstallDialogDelegateView);
    109 };
    110 
    111 ExtensionUninstallDialogViews::ExtensionUninstallDialogViews(
    112     Profile* profile,
    113     aura::Window* parent,
    114     extensions::ExtensionUninstallDialog::Delegate* delegate)
    115     : extensions::ExtensionUninstallDialog(profile, delegate),
    116       view_(NULL),
    117       parent_(parent) {
    118   if (parent_)
    119     parent_window_tracker_.Add(parent_);
    120 }
    121 
    122 ExtensionUninstallDialogViews::~ExtensionUninstallDialogViews() {
    123   // Close the widget (the views framework will delete view_).
    124   if (view_) {
    125     view_->DialogDestroyed();
    126     view_->GetWidget()->CloseNow();
    127   }
    128 }
    129 
    130 void ExtensionUninstallDialogViews::Show() {
    131   if (parent_ && !parent_window_tracker_.Contains(parent_)) {
    132     delegate_->ExtensionUninstallCanceled();
    133     return;
    134   }
    135 
    136   view_ = new ExtensionUninstallDialogDelegateView(
    137       this, extension_, triggering_extension_, &icon_);
    138   CreateBrowserModalDialogViews(view_, parent_)->Show();
    139 }
    140 
    141 void ExtensionUninstallDialogViews::ExtensionUninstallAccepted() {
    142   // The widget gets destroyed when the dialog is accepted.
    143   view_->DialogDestroyed();
    144   view_ = NULL;
    145   delegate_->ExtensionUninstallAccepted();
    146 }
    147 
    148 void ExtensionUninstallDialogViews::ExtensionUninstallCanceled() {
    149   // The widget gets destroyed when the dialog is canceled.
    150   view_->DialogDestroyed();
    151   view_ = NULL;
    152   delegate_->ExtensionUninstallCanceled();
    153 }
    154 
    155 ExtensionUninstallDialogDelegateView::ExtensionUninstallDialogDelegateView(
    156     ExtensionUninstallDialogViews* dialog_view,
    157     const extensions::Extension* extension,
    158     const extensions::Extension* triggering_extension,
    159     gfx::ImageSkia* image)
    160     : dialog_(dialog_view),
    161       triggered_by_extension_(triggering_extension != NULL) {
    162   // Scale down to icon size, but allow smaller icons (don't scale up).
    163   gfx::Size size(image->width(), image->height());
    164   if (size.width() > kIconSize || size.height() > kIconSize)
    165     size = gfx::Size(kIconSize, kIconSize);
    166   icon_ = new views::ImageView();
    167   icon_->SetImageSize(size);
    168   icon_->SetImage(*image);
    169   AddChildView(icon_);
    170 
    171   heading_ = new views::Label(base::UTF8ToUTF16(dialog_->GetHeadingText()));
    172   heading_->SetMultiLine(true);
    173   heading_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    174   AddChildView(heading_);
    175 }
    176 
    177 ExtensionUninstallDialogDelegateView::~ExtensionUninstallDialogDelegateView() {
    178   // If we're here, 2 things could have happened. Either the user closed the
    179   // dialog nicely and one of ExtensionUninstallAccepted or
    180   // ExtensionUninstallCanceled has been called (in which case dialog_ will be
    181   // NULL), *or* neither of them have been called and we are being forced closed
    182   // by our parent widget. In this case, we need to make sure to notify dialog_
    183   // not to call us again, since we're about to be freed by the Widget
    184   // framework.
    185   if (dialog_)
    186     dialog_->DialogDelegateDestroyed();
    187 }
    188 
    189 base::string16 ExtensionUninstallDialogDelegateView::GetDialogButtonLabel(
    190     ui::DialogButton button) const {
    191   return l10n_util::GetStringUTF16((button == ui::DIALOG_BUTTON_OK) ?
    192       IDS_EXTENSION_PROMPT_UNINSTALL_BUTTON : IDS_CANCEL);
    193 }
    194 
    195 bool ExtensionUninstallDialogDelegateView::Accept() {
    196   if (dialog_)
    197     dialog_->ExtensionUninstallAccepted();
    198   return true;
    199 }
    200 
    201 bool ExtensionUninstallDialogDelegateView::Cancel() {
    202   if (dialog_)
    203     dialog_->ExtensionUninstallCanceled();
    204   return true;
    205 }
    206 
    207 base::string16 ExtensionUninstallDialogDelegateView::GetWindowTitle() const {
    208   return l10n_util::GetStringUTF16(IDS_EXTENSION_UNINSTALL_PROMPT_TITLE);
    209 }
    210 
    211 gfx::Size ExtensionUninstallDialogDelegateView::GetPreferredSize() const {
    212   int width = kRightColumnWidth;
    213   width += kIconSize;
    214   width += views::kButtonHEdgeMarginNew * 2;
    215   width += views::kRelatedControlHorizontalSpacing;
    216 
    217   int height = views::kPanelVertMargin * 2;
    218   height += heading_->GetHeightForWidth(kRightColumnWidth);
    219 
    220   return gfx::Size(width,
    221                    std::max(height, kIconSize + views::kPanelVertMargin * 2));
    222 }
    223 
    224 void ExtensionUninstallDialogDelegateView::Layout() {
    225   int x = views::kButtonHEdgeMarginNew;
    226   int y = views::kPanelVertMargin;
    227 
    228   heading_->SizeToFit(kRightColumnWidth);
    229 
    230   if (heading_->height() <= kIconSize) {
    231     icon_->SetBounds(x, y, kIconSize, kIconSize);
    232     x += kIconSize;
    233     x += views::kRelatedControlHorizontalSpacing;
    234 
    235     heading_->SetX(x);
    236     heading_->SetY(y + (kIconSize - heading_->height()) / 2);
    237   } else {
    238     icon_->SetBounds(x,
    239                      y + (heading_->height() - kIconSize) / 2,
    240                      kIconSize,
    241                      kIconSize);
    242     x += kIconSize;
    243     x += views::kRelatedControlHorizontalSpacing;
    244 
    245     heading_->SetX(x);
    246     heading_->SetY(y);
    247   }
    248 }
    249 
    250 }  // namespace
    251 
    252 // static
    253 extensions::ExtensionUninstallDialog*
    254 extensions::ExtensionUninstallDialog::Create(Profile* profile,
    255                                              gfx::NativeWindow parent,
    256                                              Delegate* delegate) {
    257   return new ExtensionUninstallDialogViews(profile, parent, delegate);
    258 }
    259