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
24namespace {
25
26// Size of extension icon in top left of dialog.
27const 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.
32const int kPermissionBoxWidth = 270;
33
34// Width of the right column of the dialog when the extension requests no
35// permissions.
36const int kNoPermissionsRightColumnWidth = 210;
37
38// Width of the gray border around the permission box.
39const int kPermissionBoxBorderWidth = 1;
40
41// Width of the horizontal padding inside the permission box border.
42const int kPermissionBoxHorizontalPadding = 10;
43
44// Width of the vertical padding inside the permission box border.
45const int kPermissionBoxVerticalPadding = 11;
46
47// The max width of the individual permission strings inside the permission
48// box.
49const int kPermissionLabelWidth =
50    kPermissionBoxWidth -
51    kPermissionBoxBorderWidth * 2 -
52    kPermissionBoxHorizontalPadding * 2;
53
54// Heading font size correction.
55#if defined(CROS_FONTS_USING_BCI)
56const int kHeadingFontSizeDelta = 0;
57#else
58const int kHeadingFontSizeDelta = 1;
59#endif
60
61}  // namespace
62
63// Implements the extension installation dialog for TOOLKIT_VIEWS.
64class 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
123ExtensionInstallDialogView::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
184ExtensionInstallDialogView::~ExtensionInstallDialogView() {
185}
186
187gfx::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
215void 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
273std::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
287int ExtensionInstallDialogView::GetDefaultDialogButton() const {
288  return MessageBoxFlags::DIALOGBUTTON_CANCEL;
289}
290
291bool ExtensionInstallDialogView::Cancel() {
292  delegate_->InstallUIAbort();
293  return true;
294}
295
296bool ExtensionInstallDialogView::Accept() {
297  delegate_->InstallUIProceed();
298  return true;
299}
300
301bool ExtensionInstallDialogView::IsModal() const {
302  return true;
303}
304
305std::wstring ExtensionInstallDialogView::GetWindowTitle() const {
306  return UTF16ToWide(
307      l10n_util::GetStringUTF16(ExtensionInstallUI::kTitleIds[type_]));
308}
309
310views::View* ExtensionInstallDialogView::GetContentsView() {
311  return this;
312}
313
314void 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