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.
5 #include "chrome/browser/extensions/extension_uninstall_dialog.h"
7 #include "base/basictypes.h"
8 #include "base/compiler_specific.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/ui/app_list/app_list_service.h"
13 #include "chrome/browser/ui/native_window_tracker.h"
14 #include "chrome/grit/generated_resources.h"
15 #include "components/constrained_window/constrained_window_views.h"
16 #include "extensions/common/extension.h"
17 #include "ui/base/l10n/l10n_util.h"
18 #include "ui/compositor/compositor.h"
19 #include "ui/compositor/layer.h"
20 #include "ui/views/controls/button/checkbox.h"
21 #include "ui/views/controls/image_view.h"
22 #include "ui/views/controls/label.h"
23 #include "ui/views/layout/layout_constants.h"
24 #include "ui/views/view.h"
25 #include "ui/views/widget/widget.h"
26 #include "ui/views/window/dialog_delegate.h"
30 const int kRightColumnWidth
= 210;
31 const int kIconSize
= 64;
33 class ExtensionUninstallDialogDelegateView
;
35 // Views implementation of the uninstall dialog.
36 class ExtensionUninstallDialogViews
37 : public extensions::ExtensionUninstallDialog
{
39 ExtensionUninstallDialogViews(
41 gfx::NativeWindow parent
,
42 extensions::ExtensionUninstallDialog::Delegate
* delegate
);
43 ~ExtensionUninstallDialogViews() override
;
45 // Called when the ExtensionUninstallDialogDelegate has been destroyed to make
46 // sure we invalidate pointers.
47 void DialogDelegateDestroyed() { view_
= NULL
; }
49 // Forwards the accept and cancels to the delegate.
50 void DialogAccepted(bool handle_report_abuse
);
51 void DialogCanceled();
56 ExtensionUninstallDialogDelegateView
* view_
;
58 // The dialog's parent window.
59 gfx::NativeWindow parent_
;
61 // Tracks whether |parent_| got destroyed.
62 scoped_ptr
<NativeWindowTracker
> parent_window_tracker_
;
64 DISALLOW_COPY_AND_ASSIGN(ExtensionUninstallDialogViews
);
67 // The dialog's view, owned by the views framework.
68 class ExtensionUninstallDialogDelegateView
: public views::DialogDelegateView
{
70 ExtensionUninstallDialogDelegateView(
71 ExtensionUninstallDialogViews
* dialog_view
,
72 bool triggered_by_extension
,
73 const gfx::ImageSkia
* image
);
74 ~ExtensionUninstallDialogDelegateView() override
;
76 // Called when the ExtensionUninstallDialog has been destroyed to make sure
77 // we invalidate pointers.
78 void DialogDestroyed() { dialog_
= NULL
; }
81 // views::DialogDelegate:
82 views::View
* CreateExtraView() override
;
83 bool GetExtraViewPadding(int* padding
) override
;
84 base::string16
GetDialogButtonLabel(ui::DialogButton button
) const override
;
85 int GetDefaultDialogButton() const override
{
86 // Default to accept when triggered via chrome://extensions page.
87 return triggered_by_extension_
?
88 ui::DIALOG_BUTTON_CANCEL
: ui::DIALOG_BUTTON_OK
;
90 bool Accept() override
;
91 bool Cancel() override
;
93 // views::WidgetDelegate:
94 ui::ModalType
GetModalType() const override
{ return ui::MODAL_TYPE_WINDOW
; }
95 base::string16
GetWindowTitle() const override
;
98 gfx::Size
GetPreferredSize() const override
;
100 void Layout() override
;
102 ExtensionUninstallDialogViews
* dialog_
;
104 views::ImageView
* icon_
;
105 views::Label
* heading_
;
106 bool triggered_by_extension_
;
107 views::Checkbox
* report_abuse_checkbox_
;
109 DISALLOW_COPY_AND_ASSIGN(ExtensionUninstallDialogDelegateView
);
112 ExtensionUninstallDialogViews::ExtensionUninstallDialogViews(
114 gfx::NativeWindow parent
,
115 extensions::ExtensionUninstallDialog::Delegate
* delegate
)
116 : extensions::ExtensionUninstallDialog(profile
, delegate
),
120 parent_window_tracker_
= NativeWindowTracker::Create(parent_
);
123 ExtensionUninstallDialogViews::~ExtensionUninstallDialogViews() {
124 // Close the widget (the views framework will delete view_).
126 view_
->DialogDestroyed();
127 view_
->GetWidget()->CloseNow();
131 void ExtensionUninstallDialogViews::Show() {
132 if (parent_
&& parent_window_tracker_
->WasNativeWindowClosed()) {
133 OnDialogClosed(CLOSE_ACTION_CANCELED
);
137 view_
= new ExtensionUninstallDialogDelegateView(
138 this, triggering_extension() != nullptr, &icon());
139 constrained_window::CreateBrowserModalDialogViews(view_
, parent_
)->Show();
142 void ExtensionUninstallDialogViews::DialogAccepted(bool report_abuse_checked
) {
143 // The widget gets destroyed when the dialog is accepted.
144 view_
->DialogDestroyed();
146 OnDialogClosed(report_abuse_checked
?
147 CLOSE_ACTION_UNINSTALL_AND_REPORT_ABUSE
: CLOSE_ACTION_UNINSTALL
);
150 void ExtensionUninstallDialogViews::DialogCanceled() {
151 // The widget gets destroyed when the dialog is canceled.
152 view_
->DialogDestroyed();
154 OnDialogClosed(CLOSE_ACTION_CANCELED
);
157 ExtensionUninstallDialogDelegateView::ExtensionUninstallDialogDelegateView(
158 ExtensionUninstallDialogViews
* dialog_view
,
159 bool triggered_by_extension
,
160 const gfx::ImageSkia
* image
)
161 : dialog_(dialog_view
),
162 triggered_by_extension_(triggered_by_extension
),
163 report_abuse_checkbox_(nullptr) {
164 // Scale down to icon size, but allow smaller icons (don't scale up).
165 gfx::Size
size(image
->width(), image
->height());
166 if (size
.width() > kIconSize
|| size
.height() > kIconSize
)
167 size
= gfx::Size(kIconSize
, kIconSize
);
168 icon_
= new views::ImageView();
169 icon_
->SetImageSize(size
);
170 icon_
->SetImage(*image
);
173 heading_
= new views::Label(base::UTF8ToUTF16(dialog_
->GetHeadingText()));
174 heading_
->SetMultiLine(true);
175 heading_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
176 heading_
->SetAllowCharacterBreak(true);
177 AddChildView(heading_
);
180 ExtensionUninstallDialogDelegateView::~ExtensionUninstallDialogDelegateView() {
181 // If we're here, 2 things could have happened. Either the user closed the
182 // dialog nicely and one of the installed/canceled methods has been called
183 // (in which case dialog_ will be null), *or* neither of them have been
184 // called and we are being forced closed by our parent widget. In this case,
185 // we need to make sure to notify dialog_ not to call us again, since we're
186 // about to be freed by the Widget framework.
188 dialog_
->DialogDelegateDestroyed();
191 views::View
* ExtensionUninstallDialogDelegateView::CreateExtraView() {
192 if (dialog_
->ShouldShowReportAbuseCheckbox()) {
193 report_abuse_checkbox_
= new views::Checkbox(
194 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_UNINSTALL_REPORT_ABUSE
));
196 return report_abuse_checkbox_
;
199 bool ExtensionUninstallDialogDelegateView::GetExtraViewPadding(int* padding
) {
200 // We want a little more padding between the "report abuse" checkbox and the
202 *padding
= views::kUnrelatedControlLargeHorizontalSpacing
;
206 base::string16
ExtensionUninstallDialogDelegateView::GetDialogButtonLabel(
207 ui::DialogButton button
) const {
208 return l10n_util::GetStringUTF16((button
== ui::DIALOG_BUTTON_OK
) ?
209 IDS_EXTENSION_PROMPT_UNINSTALL_BUTTON
: IDS_CANCEL
);
212 bool ExtensionUninstallDialogDelegateView::Accept() {
214 dialog_
->DialogAccepted(report_abuse_checkbox_
&&
215 report_abuse_checkbox_
->checked());
220 bool ExtensionUninstallDialogDelegateView::Cancel() {
222 dialog_
->DialogCanceled();
226 base::string16
ExtensionUninstallDialogDelegateView::GetWindowTitle() const {
227 return l10n_util::GetStringUTF16(IDS_EXTENSION_UNINSTALL_PROMPT_TITLE
);
230 gfx::Size
ExtensionUninstallDialogDelegateView::GetPreferredSize() const {
231 int width
= kRightColumnWidth
;
233 width
+= views::kButtonHEdgeMarginNew
* 2;
234 width
+= views::kRelatedControlHorizontalSpacing
;
236 int height
= views::kPanelVertMargin
* 2;
237 height
+= heading_
->GetHeightForWidth(kRightColumnWidth
);
239 return gfx::Size(width
,
240 std::max(height
, kIconSize
+ views::kPanelVertMargin
* 2));
243 void ExtensionUninstallDialogDelegateView::Layout() {
244 int x
= views::kButtonHEdgeMarginNew
;
245 int y
= views::kPanelVertMargin
;
247 heading_
->SizeToFit(kRightColumnWidth
);
249 if (heading_
->height() <= kIconSize
) {
250 icon_
->SetBounds(x
, y
, kIconSize
, kIconSize
);
252 x
+= views::kRelatedControlHorizontalSpacing
;
255 heading_
->SetY(y
+ (kIconSize
- heading_
->height()) / 2);
258 y
+ (heading_
->height() - kIconSize
) / 2,
262 x
+= views::kRelatedControlHorizontalSpacing
;
272 extensions::ExtensionUninstallDialog
*
273 extensions::ExtensionUninstallDialog::Create(Profile
* profile
,
274 gfx::NativeWindow parent
,
275 Delegate
* delegate
) {
276 return new ExtensionUninstallDialogViews(profile
, parent
, delegate
);