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 "base/basictypes.h"
6 #include "base/compiler_specific.h"
7 #include "base/string_util.h"
8 #include "base/utf_string_conversions.h"
9 #include "chrome/browser/extensions/extension_install_dialog.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/browser/ui/browser_list.h"
12 #include "chrome/browser/ui/browser_window.h"
13 #include "chrome/browser/ui/views/window.h"
14 #include "chrome/common/extensions/extension.h"
15 #include "grit/generated_resources.h"
16 #include "ui/base/l10n/l10n_util.h"
17 #include "views/controls/image_view.h"
18 #include "views/controls/label.h"
19 #include "views/layout/layout_constants.h"
20 #include "views/view.h"
21 #include "views/window/dialog_delegate.h"
22 #include "views/window/window.h"
23
24 namespace {
25
26 // Size of extension icon in top left of dialog.
27 const int kIconSize = 69;
28
29 // Width of the white permission box. This also is the max width of all
30 // elements in the right column of the dialog in the case where the extension
31 // requests permissions.
32 const int kPermissionBoxWidth = 270;
33
34 // Width of the right column of the dialog when the extension requests no
35 // permissions.
36 const int kNoPermissionsRightColumnWidth = 210;
37
38 // Width of the gray border around the permission box.
39 const int kPermissionBoxBorderWidth = 1;
40
41 // Width of the horizontal padding inside the permission box border.
42 const int kPermissionBoxHorizontalPadding = 10;
43
44 // Width of the vertical padding inside the permission box border.
45 const int kPermissionBoxVerticalPadding = 11;
46
47 // The max width of the individual permission strings inside the permission
48 // box.
49 const int kPermissionLabelWidth =
50 kPermissionBoxWidth -
51 kPermissionBoxBorderWidth * 2 -
52 kPermissionBoxHorizontalPadding * 2;
53
54 // Heading font size correction.
55 #if defined(CROS_FONTS_USING_BCI)
56 const int kHeadingFontSizeDelta = 0;
57 #else
58 const int kHeadingFontSizeDelta = 1;
59 #endif
60
61 } // namespace
62
63 // Implements the extension installation dialog for TOOLKIT_VIEWS.
64 class ExtensionInstallDialogView : public views::View,
65 public views::DialogDelegate {
66 public:
67 ExtensionInstallDialogView(ExtensionInstallUI::Delegate* delegate,
68 const Extension* extension,
69 SkBitmap* icon,
70 const std::vector<string16>& permissions,
71 ExtensionInstallUI::PromptType type);
72 virtual ~ExtensionInstallDialogView();
73
74 private:
75 // views::View:
76 virtual gfx::Size GetPreferredSize() OVERRIDE;
77 virtual void Layout() OVERRIDE;
78
79 // views::DialogDelegate:
80 virtual std::wstring GetDialogButtonLabel(
81 MessageBoxFlags::DialogButton button) const OVERRIDE;
82 virtual int GetDefaultDialogButton() const OVERRIDE;
83 virtual bool Cancel() OVERRIDE;
84 virtual bool Accept() OVERRIDE;
85
86 // views::WindowDelegate:
87 virtual bool IsModal() const OVERRIDE;
88 virtual std::wstring GetWindowTitle() const OVERRIDE;
89 virtual views::View* GetContentsView() OVERRIDE;
90
91 // The delegate that we will call back to when the user accepts or rejects
92 // the installation.
93 ExtensionInstallUI::Delegate* delegate_;
94
95 // Displays the extension's icon.
96 views::ImageView* icon_;
97
98 // Displays the main heading "Install FooBar?".
99 views::Label* heading_;
100
101 // Displays the permission box header "The extension will have access to:".
102 views::Label* will_have_access_to_;
103
104 // The white box containing the list of permissions the extension requires.
105 // This can be NULL if the extension requires no permissions.
106 views::View* permission_box_;
107
108 // The labels describing each of the permissions the extension requires.
109 std::vector<views::Label*> permissions_;
110
111 // The width of the right column of the dialog. Will be either
112 // kPermissionBoxWidth or kNoPermissionsRightColumnWidth, depending on
113 // whether the extension requires any permissions.
114 int right_column_width_;
115
116 // The type of install dialog, which must be INSTALL_PROMPT or
117 // RE_ENABLE_PROMPT.
118 ExtensionInstallUI::PromptType type_;
119
120 DISALLOW_COPY_AND_ASSIGN(ExtensionInstallDialogView);
121 };
122
ExtensionInstallDialogView(ExtensionInstallUI::Delegate * delegate,const Extension * extension,SkBitmap * icon,const std::vector<string16> & permissions,ExtensionInstallUI::PromptType type)123 ExtensionInstallDialogView::ExtensionInstallDialogView(
124 ExtensionInstallUI::Delegate* delegate,
125 const Extension* extension,
126 SkBitmap* icon,
127 const std::vector<string16>& permissions,
128 ExtensionInstallUI::PromptType type)
129 : delegate_(delegate),
130 icon_(NULL),
131 heading_(NULL),
132 will_have_access_to_(NULL),
133 permission_box_(NULL),
134 right_column_width_(0),
135 type_(type) {
136 // Scale down to icon size, but allow smaller icons (don't scale up).
137 gfx::Size size(icon->width(), icon->height());
138 if (size.width() > kIconSize || size.height() > kIconSize)
139 size = gfx::Size(kIconSize, kIconSize);
140 icon_ = new views::ImageView();
141 icon_->SetImageSize(size);
142 icon_->SetImage(*icon);
143 icon_->SetHorizontalAlignment(views::ImageView::CENTER);
144 icon_->SetVerticalAlignment(views::ImageView::CENTER);
145 AddChildView(icon_);
146
147 heading_ = new views::Label(UTF16ToWide(
148 l10n_util::GetStringFUTF16(ExtensionInstallUI::kHeadingIds[type_],
149 UTF8ToUTF16(extension->name()))));
150 heading_->SetFont(heading_->font().DeriveFont(kHeadingFontSizeDelta,
151 gfx::Font::BOLD));
152 heading_->SetMultiLine(true);
153 heading_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
154 AddChildView(heading_);
155
156 if (permissions.empty()) {
157 right_column_width_ = kNoPermissionsRightColumnWidth;
158 } else {
159 right_column_width_ = kPermissionBoxWidth;
160 will_have_access_to_ = new views::Label(UTF16ToWide(
161 l10n_util::GetStringUTF16(ExtensionInstallUI::kWarningIds[type_])));
162 will_have_access_to_->SetMultiLine(true);
163 will_have_access_to_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
164 AddChildView(will_have_access_to_);
165
166 permission_box_ = new views::View();
167 permission_box_->set_background(
168 views::Background::CreateSolidBackground(SK_ColorWHITE));
169 permission_box_->set_border(
170 views::Border::CreateSolidBorder(kPermissionBoxBorderWidth,
171 SK_ColorLTGRAY));
172 AddChildView(permission_box_);
173 }
174
175 for (size_t i = 0; i < permissions.size(); ++i) {
176 views::Label* label = new views::Label(UTF16ToWide(permissions[i]));
177 label->SetMultiLine(true);
178 label->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
179 permission_box_->AddChildView(label);
180 permissions_.push_back(label);
181 }
182 }
183
~ExtensionInstallDialogView()184 ExtensionInstallDialogView::~ExtensionInstallDialogView() {
185 }
186
GetPreferredSize()187 gfx::Size ExtensionInstallDialogView::GetPreferredSize() {
188 int width = views::kPanelHorizMargin * 2;
189 width += kIconSize;
190 width += views::kPanelHorizMargin; // Gutter.
191 width += right_column_width_;
192
193 int height = views::kPanelVertMargin * 2;
194 height += heading_->GetHeightForWidth(right_column_width_);
195
196 if (permission_box_) {
197 height += views::kRelatedControlVerticalSpacing;
198 height += will_have_access_to_->GetHeightForWidth(right_column_width_);
199
200 height += views::kRelatedControlVerticalSpacing;
201 height += kPermissionBoxBorderWidth * 2;
202 height += kPermissionBoxVerticalPadding * 2;
203
204 for (size_t i = 0; i < permissions_.size(); ++i) {
205 if (i > 0)
206 height += views::kRelatedControlVerticalSpacing;
207 height += permissions_[0]->GetHeightForWidth(kPermissionLabelWidth);
208 }
209 }
210
211 return gfx::Size(width,
212 std::max(height, kIconSize + views::kPanelVertMargin * 2));
213 }
214
Layout()215 void ExtensionInstallDialogView::Layout() {
216 int x = views::kPanelHorizMargin;
217 int y = views::kPanelVertMargin;
218
219 icon_->SetBounds(x, y, kIconSize, kIconSize);
220 x += kIconSize;
221 x += views::kPanelHorizMargin;
222
223 heading_->SizeToFit(right_column_width_);
224 heading_->SetX(x);
225
226 // If there's no special permissions, we do a slightly different layout with
227 // the heading centered vertically wrt the icon.
228 if (!permission_box_) {
229 heading_->SetY((GetPreferredSize().height() - heading_->height()) / 2);
230 return;
231 }
232
233 // Otherwise, do the layout with the permission box.
234 heading_->SetY(y);
235 y += heading_->height();
236
237 y += views::kRelatedControlVerticalSpacing;
238 will_have_access_to_->SizeToFit(right_column_width_);
239 will_have_access_to_->SetX(x);
240 will_have_access_to_->SetY(y);
241 y += will_have_access_to_->height();
242
243 y += views::kRelatedControlVerticalSpacing;
244 permission_box_->SetX(x);
245 permission_box_->SetY(y);
246
247 // First we layout the labels inside the permission box, so that we know how
248 // big the box will have to be.
249 int label_x = kPermissionBoxBorderWidth + kPermissionBoxHorizontalPadding;
250 int label_y = kPermissionBoxBorderWidth + kPermissionBoxVerticalPadding;
251 int permission_box_height = kPermissionBoxBorderWidth * 2;
252 permission_box_height += kPermissionBoxVerticalPadding * 2;
253
254 for (size_t i = 0; i < permissions_.size(); ++i) {
255 if (i > 0) {
256 label_y += views::kRelatedControlVerticalSpacing;
257 permission_box_height += views::kPanelVertMargin;
258 }
259
260 permissions_[i]->SizeToFit(kPermissionLabelWidth);
261 permissions_[i]->SetX(label_x);
262 permissions_[i]->SetY(label_y);
263
264 label_y += permissions_[i]->height();
265 permission_box_height += permissions_[i]->height();
266 }
267
268 // Now finally we can size the permission box itself.
269 permission_box_->SetBounds(permission_box_->x(), permission_box_->y(),
270 right_column_width_, permission_box_height);
271 }
272
GetDialogButtonLabel(MessageBoxFlags::DialogButton button) const273 std::wstring ExtensionInstallDialogView::GetDialogButtonLabel(
274 MessageBoxFlags::DialogButton button) const {
275 switch (button) {
276 case MessageBoxFlags::DIALOGBUTTON_OK:
277 return UTF16ToWide(
278 l10n_util::GetStringUTF16(ExtensionInstallUI::kButtonIds[type_]));
279 case MessageBoxFlags::DIALOGBUTTON_CANCEL:
280 return UTF16ToWide(l10n_util::GetStringUTF16(IDS_CANCEL));
281 default:
282 NOTREACHED();
283 return std::wstring();
284 }
285 }
286
GetDefaultDialogButton() const287 int ExtensionInstallDialogView::GetDefaultDialogButton() const {
288 return MessageBoxFlags::DIALOGBUTTON_CANCEL;
289 }
290
Cancel()291 bool ExtensionInstallDialogView::Cancel() {
292 delegate_->InstallUIAbort();
293 return true;
294 }
295
Accept()296 bool ExtensionInstallDialogView::Accept() {
297 delegate_->InstallUIProceed();
298 return true;
299 }
300
IsModal() const301 bool ExtensionInstallDialogView::IsModal() const {
302 return true;
303 }
304
GetWindowTitle() const305 std::wstring ExtensionInstallDialogView::GetWindowTitle() const {
306 return UTF16ToWide(
307 l10n_util::GetStringUTF16(ExtensionInstallUI::kTitleIds[type_]));
308 }
309
GetContentsView()310 views::View* ExtensionInstallDialogView::GetContentsView() {
311 return this;
312 }
313
ShowExtensionInstallDialog(Profile * profile,ExtensionInstallUI::Delegate * delegate,const Extension * extension,SkBitmap * icon,const std::vector<string16> & permissions,ExtensionInstallUI::PromptType type)314 void ShowExtensionInstallDialog(
315 Profile* profile,
316 ExtensionInstallUI::Delegate* delegate,
317 const Extension* extension,
318 SkBitmap* icon,
319 const std::vector<string16>& permissions,
320 ExtensionInstallUI::PromptType type) {
321 #if defined(OS_CHROMEOS)
322 // Use a normal browser window as parent on ChromeOS.
323 Browser* browser = BrowserList::FindBrowserWithType(profile,
324 Browser::TYPE_NORMAL,
325 true);
326 #else
327 Browser* browser = BrowserList::GetLastActiveWithProfile(profile);
328 #endif
329 if (!browser) {
330 delegate->InstallUIAbort();
331 return;
332 }
333
334 BrowserWindow* browser_window = browser->window();
335 if (!browser_window) {
336 delegate->InstallUIAbort();
337 return;
338 }
339
340 ExtensionInstallDialogView* dialog = new ExtensionInstallDialogView(
341 delegate, extension, icon, permissions, type);
342
343 views::Window* window = browser::CreateViewsWindow(
344 browser_window->GetNativeHandle(), gfx::Rect(), dialog);
345
346 window->Show();
347 }
348