Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / views / content_setting_bubble_contents.cc
blob632e60172d5c7ddf09d51812f486876b8a556e50
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/ui/views/content_setting_bubble_contents.h"
7 #include <algorithm>
8 #include <set>
9 #include <string>
10 #include <vector>
12 #include "base/bind.h"
13 #include "base/stl_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/browser/content_settings/host_content_settings_map.h"
16 #include "chrome/browser/plugins/plugin_finder.h"
17 #include "chrome/browser/plugins/plugin_metadata.h"
18 #include "chrome/browser/ui/content_settings/content_setting_bubble_model.h"
19 #include "chrome/browser/ui/content_settings/content_setting_media_menu_model.h"
20 #include "chrome/browser/ui/views/browser_dialogs.h"
21 #include "content/public/browser/plugin_service.h"
22 #include "content/public/browser/web_contents.h"
23 #include "grit/generated_resources.h"
24 #include "grit/theme_resources.h"
25 #include "ui/base/l10n/l10n_util.h"
26 #include "ui/base/models/simple_menu_model.h"
27 #include "ui/base/resource/resource_bundle.h"
28 #include "ui/gfx/font_list.h"
29 #include "ui/views/controls/button/label_button.h"
30 #include "ui/views/controls/button/menu_button.h"
31 #include "ui/views/controls/button/radio_button.h"
32 #include "ui/views/controls/image_view.h"
33 #include "ui/views/controls/label.h"
34 #include "ui/views/controls/link.h"
35 #include "ui/views/controls/menu/menu.h"
36 #include "ui/views/controls/menu/menu_runner.h"
37 #include "ui/views/controls/separator.h"
38 #include "ui/views/layout/grid_layout.h"
39 #include "ui/views/layout/layout_constants.h"
41 #if defined(USE_AURA)
42 #include "ui/base/cursor/cursor.h"
43 #endif
45 namespace {
47 // If we don't clamp the maximum width, then very long URLs and titles can make
48 // the bubble arbitrarily wide.
49 const int kMaxContentsWidth = 500;
51 // When we have multiline labels, we should set a minimum width lest we get very
52 // narrow bubbles with lots of line-wrapping.
53 const int kMinMultiLineContentsWidth = 250;
55 // The minimum width of the media menu buttons.
56 const int kMinMediaMenuButtonWidth = 100;
58 } // namespace
60 using content::PluginService;
61 using content::WebContents;
64 // ContentSettingBubbleContents::Favicon --------------------------------------
66 class ContentSettingBubbleContents::Favicon : public views::ImageView {
67 public:
68 Favicon(const gfx::Image& image,
69 ContentSettingBubbleContents* parent,
70 views::Link* link);
71 virtual ~Favicon();
73 private:
74 // views::View overrides:
75 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE;
76 virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE;
77 virtual gfx::NativeCursor GetCursor(const ui::MouseEvent& event) OVERRIDE;
79 ContentSettingBubbleContents* parent_;
80 views::Link* link_;
83 ContentSettingBubbleContents::Favicon::Favicon(
84 const gfx::Image& image,
85 ContentSettingBubbleContents* parent,
86 views::Link* link)
87 : parent_(parent),
88 link_(link) {
89 SetImage(image.AsImageSkia());
92 ContentSettingBubbleContents::Favicon::~Favicon() {
95 bool ContentSettingBubbleContents::Favicon::OnMousePressed(
96 const ui::MouseEvent& event) {
97 return event.IsLeftMouseButton() || event.IsMiddleMouseButton();
100 void ContentSettingBubbleContents::Favicon::OnMouseReleased(
101 const ui::MouseEvent& event) {
102 if ((event.IsLeftMouseButton() || event.IsMiddleMouseButton()) &&
103 HitTestPoint(event.location())) {
104 parent_->LinkClicked(link_, event.flags());
108 gfx::NativeCursor ContentSettingBubbleContents::Favicon::GetCursor(
109 const ui::MouseEvent& event) {
110 #if defined(USE_AURA)
111 return ui::kCursorHand;
112 #elif defined(OS_WIN)
113 static HCURSOR g_hand_cursor = LoadCursor(NULL, IDC_HAND);
114 return g_hand_cursor;
115 #endif
119 // ContentSettingBubbleContents::MediaMenuParts -------------------------------
121 struct ContentSettingBubbleContents::MediaMenuParts {
122 explicit MediaMenuParts(content::MediaStreamType type);
123 ~MediaMenuParts();
125 content::MediaStreamType type;
126 scoped_ptr<ui::SimpleMenuModel> menu_model;
128 private:
129 DISALLOW_COPY_AND_ASSIGN(MediaMenuParts);
132 ContentSettingBubbleContents::MediaMenuParts::MediaMenuParts(
133 content::MediaStreamType type)
134 : type(type) {}
136 ContentSettingBubbleContents::MediaMenuParts::~MediaMenuParts() {}
138 // ContentSettingBubbleContents -----------------------------------------------
140 ContentSettingBubbleContents::ContentSettingBubbleContents(
141 ContentSettingBubbleModel* content_setting_bubble_model,
142 views::View* anchor_view,
143 views::BubbleBorder::Arrow arrow)
144 : BubbleDelegateView(anchor_view, arrow),
145 content_setting_bubble_model_(content_setting_bubble_model),
146 custom_link_(NULL),
147 manage_link_(NULL),
148 close_button_(NULL) {
149 // Compensate for built-in vertical padding in the anchor view's image.
150 set_anchor_view_insets(gfx::Insets(5, 0, 5, 0));
153 ContentSettingBubbleContents::~ContentSettingBubbleContents() {
154 STLDeleteValues(&media_menus_);
157 gfx::Size ContentSettingBubbleContents::GetPreferredSize() {
158 gfx::Size preferred_size(views::View::GetPreferredSize());
159 int preferred_width =
160 (!content_setting_bubble_model_->bubble_content().domain_lists.empty() &&
161 (kMinMultiLineContentsWidth > preferred_size.width())) ?
162 kMinMultiLineContentsWidth : preferred_size.width();
163 preferred_size.set_width(std::min(preferred_width, kMaxContentsWidth));
164 return preferred_size;
167 void ContentSettingBubbleContents::UpdateMenuLabel(
168 content::MediaStreamType type,
169 const std::string& label) {
170 for (MediaMenuPartsMap::const_iterator it = media_menus_.begin();
171 it != media_menus_.end(); ++it) {
172 if (it->second->type == type) {
173 it->first->SetText(base::UTF8ToUTF16(label));
174 return;
177 NOTREACHED();
180 void ContentSettingBubbleContents::Init() {
181 using views::GridLayout;
183 GridLayout* layout = new views::GridLayout(this);
184 SetLayoutManager(layout);
186 const int kSingleColumnSetId = 0;
187 views::ColumnSet* column_set = layout->AddColumnSet(kSingleColumnSetId);
188 column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1,
189 GridLayout::USE_PREF, 0, 0);
191 const ContentSettingBubbleModel::BubbleContent& bubble_content =
192 content_setting_bubble_model_->bubble_content();
193 bool bubble_content_empty = true;
195 if (!bubble_content.title.empty()) {
196 views::Label* title_label = new views::Label(base::UTF8ToUTF16(
197 bubble_content.title));
198 title_label->SetMultiLine(true);
199 title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
200 layout->StartRow(0, kSingleColumnSetId);
201 layout->AddView(title_label);
202 bubble_content_empty = false;
205 if (content_setting_bubble_model_->content_type() ==
206 CONTENT_SETTINGS_TYPE_POPUPS) {
207 const int kPopupColumnSetId = 2;
208 views::ColumnSet* popup_column_set =
209 layout->AddColumnSet(kPopupColumnSetId);
210 popup_column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0,
211 GridLayout::USE_PREF, 0, 0);
212 popup_column_set->AddPaddingColumn(
213 0, views::kRelatedControlHorizontalSpacing);
214 popup_column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1,
215 GridLayout::USE_PREF, 0, 0);
217 for (std::vector<ContentSettingBubbleModel::PopupItem>::const_iterator
218 i(bubble_content.popup_items.begin());
219 i != bubble_content.popup_items.end(); ++i) {
220 if (!bubble_content_empty)
221 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
222 layout->StartRow(0, kPopupColumnSetId);
224 views::Link* link = new views::Link(base::UTF8ToUTF16(i->title));
225 link->set_listener(this);
226 link->SetElideBehavior(views::Label::ELIDE_IN_MIDDLE);
227 popup_links_[link] = i - bubble_content.popup_items.begin();
228 layout->AddView(new Favicon(i->image, this, link));
229 layout->AddView(link);
230 bubble_content_empty = false;
234 const int indented_kSingleColumnSetId = 3;
235 // Insert a column set with greater indent.
236 views::ColumnSet* indented_single_column_set =
237 layout->AddColumnSet(indented_kSingleColumnSetId);
238 indented_single_column_set->AddPaddingColumn(0, views::kCheckboxIndent);
239 indented_single_column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL,
240 1, GridLayout::USE_PREF, 0, 0);
242 const ContentSettingBubbleModel::RadioGroup& radio_group =
243 bubble_content.radio_group;
244 if (!radio_group.radio_items.empty()) {
245 if (!bubble_content_empty)
246 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
247 for (ContentSettingBubbleModel::RadioItems::const_iterator i(
248 radio_group.radio_items.begin());
249 i != radio_group.radio_items.end(); ++i) {
250 views::RadioButton* radio =
251 new views::RadioButton(base::UTF8ToUTF16(*i), 0);
252 radio->SetEnabled(bubble_content.radio_group_enabled);
253 radio->set_listener(this);
254 radio_group_.push_back(radio);
255 layout->StartRow(0, indented_kSingleColumnSetId);
256 layout->AddView(radio);
257 bubble_content_empty = false;
259 DCHECK(!radio_group_.empty());
260 // Now that the buttons have been added to the view hierarchy, it's safe
261 // to call SetChecked() on them.
262 radio_group_[radio_group.default_item]->SetChecked(true);
265 // Layout code for the media device menus.
266 if (content_setting_bubble_model_->content_type() ==
267 CONTENT_SETTINGS_TYPE_MEDIASTREAM) {
268 const int kMediaMenuColumnSetId = 2;
269 views::ColumnSet* menu_column_set =
270 layout->AddColumnSet(kMediaMenuColumnSetId);
271 menu_column_set->AddPaddingColumn(0, views::kCheckboxIndent);
272 menu_column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0,
273 GridLayout::USE_PREF, 0, 0);
274 menu_column_set->AddPaddingColumn(
275 0, views::kRelatedControlHorizontalSpacing);
276 menu_column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1,
277 GridLayout::USE_PREF, 0, 0);
279 int menu_width = 0;
280 for (ContentSettingBubbleModel::MediaMenuMap::const_iterator i(
281 bubble_content.media_menus.begin());
282 i != bubble_content.media_menus.end(); ++i) {
283 if (!bubble_content_empty)
284 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
285 layout->StartRow(0, kMediaMenuColumnSetId);
287 views::Label* label =
288 new views::Label(base::UTF8ToUTF16(i->second.label));
289 label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
291 views::MenuButton* menu_button = new views::MenuButton(
292 NULL, base::UTF8ToUTF16((i->second.selected_device.name)),
293 this, true);
294 menu_button->set_alignment(views::TextButton::ALIGN_LEFT);
295 menu_button->set_border(
296 new views::TextButtonNativeThemeBorder(menu_button));
297 menu_button->set_animate_on_state_change(false);
299 MediaMenuParts* menu_view = new MediaMenuParts(i->first);
300 menu_view->menu_model.reset(new ContentSettingMediaMenuModel(
301 i->first,
302 content_setting_bubble_model_.get(),
303 base::Bind(&ContentSettingBubbleContents::UpdateMenuLabel,
304 base::Unretained(this))));
305 media_menus_[menu_button] = menu_view;
307 if (!menu_view->menu_model->GetItemCount()) {
308 // Show a "None available" title and grey out the menu when there are
309 // no available devices.
310 menu_button->SetText(
311 l10n_util::GetStringUTF16(IDS_MEDIA_MENU_NO_DEVICE_TITLE));
312 menu_button->SetEnabled(false);
315 // Disable the device selection when the website is managing the devices
316 // itself.
317 if (i->second.disabled)
318 menu_button->SetEnabled(false);
320 // Use the longest width of the menus as the width of the menu buttons.
321 menu_width = std::max(menu_width,
322 GetPreferredMediaMenuWidth(
323 menu_button, menu_view->menu_model.get()));
325 layout->AddView(label);
326 layout->AddView(menu_button);
328 bubble_content_empty = false;
331 // Make sure the width is at least kMinMediaMenuButtonWidth. The
332 // maximum width will be clamped by kMaxContentsWidth of the view.
333 menu_width = std::max(kMinMediaMenuButtonWidth, menu_width);
335 // Set all the menu buttons to the width we calculated above.
336 for (MediaMenuPartsMap::const_iterator i = media_menus_.begin();
337 i != media_menus_.end(); ++i) {
338 i->first->set_min_width(menu_width);
339 i->first->set_max_width(menu_width);
343 const gfx::FontList& domain_font =
344 ui::ResourceBundle::GetSharedInstance().GetFontList(
345 ui::ResourceBundle::BoldFont);
346 for (std::vector<ContentSettingBubbleModel::DomainList>::const_iterator i(
347 bubble_content.domain_lists.begin());
348 i != bubble_content.domain_lists.end(); ++i) {
349 layout->StartRow(0, kSingleColumnSetId);
350 views::Label* section_title = new views::Label(base::UTF8ToUTF16(i->title));
351 section_title->SetMultiLine(true);
352 section_title->SetHorizontalAlignment(gfx::ALIGN_LEFT);
353 layout->AddView(section_title, 1, 1, GridLayout::FILL, GridLayout::LEADING);
354 for (std::set<std::string>::const_iterator j = i->hosts.begin();
355 j != i->hosts.end(); ++j) {
356 layout->StartRow(0, indented_kSingleColumnSetId);
357 layout->AddView(new views::Label(base::UTF8ToUTF16(*j), domain_font));
359 bubble_content_empty = false;
362 if (!bubble_content.custom_link.empty()) {
363 custom_link_ =
364 new views::Link(base::UTF8ToUTF16(bubble_content.custom_link));
365 custom_link_->SetEnabled(bubble_content.custom_link_enabled);
366 custom_link_->set_listener(this);
367 if (!bubble_content_empty)
368 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
369 layout->StartRow(0, kSingleColumnSetId);
370 layout->AddView(custom_link_);
371 bubble_content_empty = false;
374 const int kDoubleColumnSetId = 1;
375 views::ColumnSet* double_column_set =
376 layout->AddColumnSet(kDoubleColumnSetId);
377 if (!bubble_content_empty) {
378 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
379 layout->StartRow(0, kSingleColumnSetId);
380 layout->AddView(new views::Separator(views::Separator::HORIZONTAL), 1, 1,
381 GridLayout::FILL, GridLayout::FILL);
382 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
385 double_column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 1,
386 GridLayout::USE_PREF, 0, 0);
387 double_column_set->AddPaddingColumn(
388 0, views::kUnrelatedControlHorizontalSpacing);
389 double_column_set->AddColumn(GridLayout::TRAILING, GridLayout::CENTER, 0,
390 GridLayout::USE_PREF, 0, 0);
392 layout->StartRow(0, kDoubleColumnSetId);
393 manage_link_ =
394 new views::Link(base::UTF8ToUTF16(bubble_content.manage_link));
395 manage_link_->set_listener(this);
396 layout->AddView(manage_link_);
398 close_button_ =
399 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE));
400 close_button_->SetStyle(views::Button::STYLE_BUTTON);
401 layout->AddView(close_button_);
404 void ContentSettingBubbleContents::ButtonPressed(views::Button* sender,
405 const ui::Event& event) {
406 RadioGroup::const_iterator i(
407 std::find(radio_group_.begin(), radio_group_.end(), sender));
408 if (i != radio_group_.end()) {
409 content_setting_bubble_model_->OnRadioClicked(i - radio_group_.begin());
410 return;
412 DCHECK_EQ(sender, close_button_);
413 content_setting_bubble_model_->OnDoneClicked();
414 StartFade(false);
417 void ContentSettingBubbleContents::LinkClicked(views::Link* source,
418 int event_flags) {
419 if (source == custom_link_) {
420 content_setting_bubble_model_->OnCustomLinkClicked();
421 StartFade(false);
422 return;
424 if (source == manage_link_) {
425 StartFade(false);
426 content_setting_bubble_model_->OnManageLinkClicked();
427 // CAREFUL: Showing the settings window activates it, which deactivates the
428 // info bubble, which causes it to close, which deletes us.
429 return;
432 PopupLinks::const_iterator i(popup_links_.find(source));
433 DCHECK(i != popup_links_.end());
434 content_setting_bubble_model_->OnPopupClicked(i->second);
437 void ContentSettingBubbleContents::OnMenuButtonClicked(
438 views::View* source,
439 const gfx::Point& point) {
440 MediaMenuPartsMap::iterator j(media_menus_.find(
441 static_cast<views::MenuButton*>(source)));
442 DCHECK(j != media_menus_.end());
443 menu_runner_.reset(new views::MenuRunner(j->second->menu_model.get()));
445 gfx::Point screen_location;
446 views::View::ConvertPointToScreen(j->first, &screen_location);
447 ignore_result(menu_runner_->RunMenuAt(
448 source->GetWidget(),
449 j->first,
450 gfx::Rect(screen_location, j->first->size()),
451 views::MenuItemView::TOPLEFT,
452 ui::MENU_SOURCE_NONE,
453 views::MenuRunner::HAS_MNEMONICS));
456 int ContentSettingBubbleContents::GetPreferredMediaMenuWidth(
457 views::MenuButton* button,
458 ui::SimpleMenuModel* menu_model) {
459 base::string16 title = button->text();
461 int width = button->GetPreferredSize().width();
462 for (int i = 0; i < menu_model->GetItemCount(); ++i) {
463 button->SetText(menu_model->GetLabelAt(i));
464 width = std::max(width, button->GetPreferredSize().width());
467 // Recover the title for the menu button.
468 button->SetText(title);
469 return width;