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 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 184 ExtensionInstallDialogView::~ExtensionInstallDialogView() { 185 } 186 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 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 273 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 287 int ExtensionInstallDialogView::GetDefaultDialogButton() const { 288 return MessageBoxFlags::DIALOGBUTTON_CANCEL; 289 } 290 291 bool ExtensionInstallDialogView::Cancel() { 292 delegate_->InstallUIAbort(); 293 return true; 294 } 295 296 bool ExtensionInstallDialogView::Accept() { 297 delegate_->InstallUIProceed(); 298 return true; 299 } 300 301 bool ExtensionInstallDialogView::IsModal() const { 302 return true; 303 } 304 305 std::wstring ExtensionInstallDialogView::GetWindowTitle() const { 306 return UTF16ToWide( 307 l10n_util::GetStringUTF16(ExtensionInstallUI::kTitleIds[type_])); 308 } 309 310 views::View* ExtensionInstallDialogView::GetContentsView() { 311 return this; 312 } 313 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