content_setting_bubble_contents.cc revision 3551c9c881056c480085172ff9840cab31610854
15738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project// Use of this source code is governed by a BSD-style license that can be 35738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project// found in the LICENSE file. 45738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 55738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "chrome/browser/ui/views/content_setting_bubble_contents.h" 65738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 75738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include <algorithm> 85738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include <set> 95738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include <string> 105738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include <vector> 115738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 125738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "base/bind.h" 135738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "base/stl_util.h" 145738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "base/strings/utf_string_conversions.h" 155738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "chrome/browser/content_settings/host_content_settings_map.h" 165738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "chrome/browser/plugins/plugin_finder.h" 175738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "chrome/browser/plugins/plugin_metadata.h" 185738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "chrome/browser/ui/content_settings/content_setting_bubble_model.h" 195738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "chrome/browser/ui/content_settings/content_setting_media_menu_model.h" 205738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "chrome/browser/ui/views/browser_dialogs.h" 215738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "content/public/browser/notification_source.h" 225738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "content/public/browser/notification_types.h" 235738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "content/public/browser/plugin_service.h" 245cd8bff2dd0337cb52bf48f312e3d2d55a8882fbMike J. Chen#include "content/public/browser/web_contents.h" 255738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "grit/generated_resources.h" 265738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "grit/theme_resources.h" 275738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "ui/base/l10n/l10n_util.h" 285738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "ui/base/models/simple_menu_model.h" 295738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "ui/views/controls/button/label_button.h" 305738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "ui/views/controls/button/menu_button.h" 315738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "ui/views/controls/button/radio_button.h" 325738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "ui/views/controls/image_view.h" 335738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "ui/views/controls/label.h" 345738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "ui/views/controls/link.h" 355738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "ui/views/controls/menu/menu.h" 365738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "ui/views/controls/menu/menu_runner.h" 375738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "ui/views/controls/separator.h" 385738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "ui/views/layout/grid_layout.h" 395738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project#include "ui/views/layout/layout_constants.h" 4066432dcffe211e317d35978283a04a96d5620bdfMatthew Xie 4166432dcffe211e317d35978283a04a96d5620bdfMatthew Xie#if defined(USE_AURA) 4266432dcffe211e317d35978283a04a96d5620bdfMatthew Xie#include "ui/base/cursor/cursor.h" 4366432dcffe211e317d35978283a04a96d5620bdfMatthew Xie#endif 4466432dcffe211e317d35978283a04a96d5620bdfMatthew Xie 455738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Projectnamespace { 465738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 475738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project// If we don't clamp the maximum width, then very long URLs and titles can make 485738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project// the bubble arbitrarily wide. 495738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Projectconst int kMaxContentsWidth = 500; 505738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 515738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project// When we have multiline labels, we should set a minimum width lest we get very 525738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project// narrow bubbles with lots of line-wrapping. 535738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Projectconst int kMinMultiLineContentsWidth = 250; 545738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 555738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project// The minimum width of the media menu buttons. 565738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Projectconst int kMinMediaMenuButtonWidth = 100; 575738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 585738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project} // namespace 595738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 605738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Projectusing content::PluginService; 615738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Projectusing content::WebContents; 625738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 635738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 645738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project// ContentSettingBubbleContents::Favicon -------------------------------------- 655738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 665738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Projectclass ContentSettingBubbleContents::Favicon : public views::ImageView { 675738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project public: 685738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project Favicon(const gfx::Image& image, 695738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project ContentSettingBubbleContents* parent, 705738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project views::Link* link); 715738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project virtual ~Favicon(); 725738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 735738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project private: 745738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project // views::View overrides: 755738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE; 765738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE; 7766432dcffe211e317d35978283a04a96d5620bdfMatthew Xie virtual gfx::NativeCursor GetCursor(const ui::MouseEvent& event) OVERRIDE; 7866432dcffe211e317d35978283a04a96d5620bdfMatthew Xie 7966432dcffe211e317d35978283a04a96d5620bdfMatthew Xie ContentSettingBubbleContents* parent_; 8066432dcffe211e317d35978283a04a96d5620bdfMatthew Xie views::Link* link_; 815738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project}; 825738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 835738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source ProjectContentSettingBubbleContents::Favicon::Favicon( 845738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project const gfx::Image& image, 8566432dcffe211e317d35978283a04a96d5620bdfMatthew Xie ContentSettingBubbleContents* parent, 8666432dcffe211e317d35978283a04a96d5620bdfMatthew Xie views::Link* link) 8766432dcffe211e317d35978283a04a96d5620bdfMatthew Xie : parent_(parent), 885738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project link_(link) { 8966432dcffe211e317d35978283a04a96d5620bdfMatthew Xie SetImage(image.AsImageSkia()); 905738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project} 91e8c3d75b75493911ebf0f99c83676359657178f7Sharvil Nanavati 9266432dcffe211e317d35978283a04a96d5620bdfMatthew XieContentSettingBubbleContents::Favicon::~Favicon() { 9366432dcffe211e317d35978283a04a96d5620bdfMatthew Xie} 9466432dcffe211e317d35978283a04a96d5620bdfMatthew Xie 9566432dcffe211e317d35978283a04a96d5620bdfMatthew Xiebool ContentSettingBubbleContents::Favicon::OnMousePressed( 9666432dcffe211e317d35978283a04a96d5620bdfMatthew Xie const ui::MouseEvent& event) { 9766432dcffe211e317d35978283a04a96d5620bdfMatthew Xie return event.IsLeftMouseButton() || event.IsMiddleMouseButton(); 9866432dcffe211e317d35978283a04a96d5620bdfMatthew Xie} 9966432dcffe211e317d35978283a04a96d5620bdfMatthew Xie 10066432dcffe211e317d35978283a04a96d5620bdfMatthew Xievoid ContentSettingBubbleContents::Favicon::OnMouseReleased( 10166432dcffe211e317d35978283a04a96d5620bdfMatthew Xie const ui::MouseEvent& event) { 10266432dcffe211e317d35978283a04a96d5620bdfMatthew Xie if ((event.IsLeftMouseButton() || event.IsMiddleMouseButton()) && 10366432dcffe211e317d35978283a04a96d5620bdfMatthew Xie HitTestPoint(event.location())) { 10466432dcffe211e317d35978283a04a96d5620bdfMatthew Xie parent_->LinkClicked(link_, event.flags()); 10566432dcffe211e317d35978283a04a96d5620bdfMatthew Xie } 10666432dcffe211e317d35978283a04a96d5620bdfMatthew Xie} 10766432dcffe211e317d35978283a04a96d5620bdfMatthew Xie 10866432dcffe211e317d35978283a04a96d5620bdfMatthew Xiegfx::NativeCursor ContentSettingBubbleContents::Favicon::GetCursor( 10966432dcffe211e317d35978283a04a96d5620bdfMatthew Xie const ui::MouseEvent& event) { 11066432dcffe211e317d35978283a04a96d5620bdfMatthew Xie#if defined(USE_AURA) 11166432dcffe211e317d35978283a04a96d5620bdfMatthew Xie return ui::kCursorHand; 11266432dcffe211e317d35978283a04a96d5620bdfMatthew Xie#elif defined(OS_WIN) 11366432dcffe211e317d35978283a04a96d5620bdfMatthew Xie static HCURSOR g_hand_cursor = LoadCursor(NULL, IDC_HAND); 11466432dcffe211e317d35978283a04a96d5620bdfMatthew Xie return g_hand_cursor; 11566432dcffe211e317d35978283a04a96d5620bdfMatthew Xie#endif 11666432dcffe211e317d35978283a04a96d5620bdfMatthew Xie} 11766432dcffe211e317d35978283a04a96d5620bdfMatthew Xie 11866432dcffe211e317d35978283a04a96d5620bdfMatthew Xie 11966432dcffe211e317d35978283a04a96d5620bdfMatthew Xie// ContentSettingBubbleContents::MediaMenuParts ------------------------------- 12066432dcffe211e317d35978283a04a96d5620bdfMatthew Xie 12166432dcffe211e317d35978283a04a96d5620bdfMatthew Xiestruct ContentSettingBubbleContents::MediaMenuParts { 12266432dcffe211e317d35978283a04a96d5620bdfMatthew Xie explicit MediaMenuParts(content::MediaStreamType type); 12366432dcffe211e317d35978283a04a96d5620bdfMatthew Xie ~MediaMenuParts(); 1245738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 1255738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project content::MediaStreamType type; 1265738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project scoped_ptr<ui::SimpleMenuModel> menu_model; 1275738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 1285738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project private: 1295738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project DISALLOW_COPY_AND_ASSIGN(MediaMenuParts); 1305738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project}; 1315738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 1325738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source ProjectContentSettingBubbleContents::MediaMenuParts::MediaMenuParts( 1335738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project content::MediaStreamType type) 1345738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project : type(type) {} 1355738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 1365738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source ProjectContentSettingBubbleContents::MediaMenuParts::~MediaMenuParts() {} 1375738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 1385738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 1395738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project// ContentSettingBubbleContents ----------------------------------------------- 1405738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 1415738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source ProjectContentSettingBubbleContents::ContentSettingBubbleContents( 142e8c3d75b75493911ebf0f99c83676359657178f7Sharvil Nanavati ContentSettingBubbleModel* content_setting_bubble_model, 1435738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project WebContents* web_contents, 1445738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project views::View* anchor_view, 1455738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project views::BubbleBorder::Arrow arrow) 1465738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project : BubbleDelegateView(anchor_view, arrow), 1475738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project content_setting_bubble_model_(content_setting_bubble_model), 1485738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project web_contents_(web_contents), 1495738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project custom_link_(NULL), 1505738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project manage_link_(NULL), 1515738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project close_button_(NULL) { 1525738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project // Compensate for built-in vertical padding in the anchor view's image. 1535738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project set_anchor_view_insets(gfx::Insets(5, 0, 5, 0)); 1545738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 1555738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, 1565738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project content::Source<WebContents>(web_contents)); 1575738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project} 158e8c3d75b75493911ebf0f99c83676359657178f7Sharvil Nanavati 1595738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source ProjectContentSettingBubbleContents::~ContentSettingBubbleContents() { 1605738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project STLDeleteValues(&media_menus_); 1615738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project} 1625738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 1635738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Projectgfx::Size ContentSettingBubbleContents::GetPreferredSize() { 1645738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project gfx::Size preferred_size(views::View::GetPreferredSize()); 1655738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project int preferred_width = 1665738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project (!content_setting_bubble_model_->bubble_content().domain_lists.empty() && 1675738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project (kMinMultiLineContentsWidth > preferred_size.width())) ? 1685738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project kMinMultiLineContentsWidth : preferred_size.width(); 1695738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project preferred_size.set_width(std::min(preferred_width, kMaxContentsWidth)); 1705738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project return preferred_size; 1715738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project} 1725738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project 1735738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Projectvoid ContentSettingBubbleContents::UpdateMenuLabel( 1745738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project content::MediaStreamType type, 1755738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project const std::string& label) { 1765cd8bff2dd0337cb52bf48f312e3d2d55a8882fbMike J. Chen for (MediaMenuPartsMap::const_iterator it = media_menus_.begin(); 177e8c3d75b75493911ebf0f99c83676359657178f7Sharvil Nanavati it != media_menus_.end(); ++it) { 1785738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project if (it->second->type == type) { 1795738f83aeb59361a0a2eda2460113f6dc919427The Android Open Source Project it->first->SetText(UTF8ToUTF16(label)); 180 return; 181 } 182 } 183 NOTREACHED(); 184} 185 186void ContentSettingBubbleContents::Init() { 187 using views::GridLayout; 188 189 GridLayout* layout = new views::GridLayout(this); 190 SetLayoutManager(layout); 191 192 const int kSingleColumnSetId = 0; 193 views::ColumnSet* column_set = layout->AddColumnSet(kSingleColumnSetId); 194 column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1, 195 GridLayout::USE_PREF, 0, 0); 196 197 const ContentSettingBubbleModel::BubbleContent& bubble_content = 198 content_setting_bubble_model_->bubble_content(); 199 bool bubble_content_empty = true; 200 201 if (!bubble_content.title.empty()) { 202 views::Label* title_label = new views::Label(UTF8ToUTF16( 203 bubble_content.title)); 204 title_label->SetMultiLine(true); 205 title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 206 layout->StartRow(0, kSingleColumnSetId); 207 layout->AddView(title_label); 208 bubble_content_empty = false; 209 } 210 211 const std::set<std::string>& plugins = bubble_content.resource_identifiers; 212 if (!plugins.empty()) { 213 if (!bubble_content_empty) 214 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 215 PluginFinder* finder = PluginFinder::GetInstance(); 216 for (std::set<std::string>::const_iterator i(plugins.begin()); 217 i != plugins.end(); ++i) { 218 string16 name = finder->FindPluginNameWithIdentifier(*i); 219 layout->StartRow(0, kSingleColumnSetId); 220 layout->AddView(new views::Label(name)); 221 bubble_content_empty = false; 222 } 223 } 224 225 if (content_setting_bubble_model_->content_type() == 226 CONTENT_SETTINGS_TYPE_POPUPS) { 227 const int kPopupColumnSetId = 2; 228 views::ColumnSet* popup_column_set = 229 layout->AddColumnSet(kPopupColumnSetId); 230 popup_column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0, 231 GridLayout::USE_PREF, 0, 0); 232 popup_column_set->AddPaddingColumn( 233 0, views::kRelatedControlHorizontalSpacing); 234 popup_column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1, 235 GridLayout::USE_PREF, 0, 0); 236 237 for (std::vector<ContentSettingBubbleModel::PopupItem>::const_iterator 238 i(bubble_content.popup_items.begin()); 239 i != bubble_content.popup_items.end(); ++i) { 240 if (!bubble_content_empty) 241 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 242 layout->StartRow(0, kPopupColumnSetId); 243 244 views::Link* link = new views::Link(UTF8ToUTF16(i->title)); 245 link->set_listener(this); 246 link->SetElideBehavior(views::Label::ELIDE_IN_MIDDLE); 247 popup_links_[link] = i - bubble_content.popup_items.begin(); 248 layout->AddView(new Favicon(i->image, this, link)); 249 layout->AddView(link); 250 bubble_content_empty = false; 251 } 252 } 253 254 const int indented_kSingleColumnSetId = 3; 255 // Insert a column set with greater indent. 256 views::ColumnSet* indented_single_column_set = 257 layout->AddColumnSet(indented_kSingleColumnSetId); 258 indented_single_column_set->AddPaddingColumn(0, views::kCheckboxIndent); 259 indented_single_column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 260 1, GridLayout::USE_PREF, 0, 0); 261 262 const ContentSettingBubbleModel::RadioGroup& radio_group = 263 bubble_content.radio_group; 264 if (!radio_group.radio_items.empty()) { 265 if (!bubble_content_empty) 266 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 267 for (ContentSettingBubbleModel::RadioItems::const_iterator i( 268 radio_group.radio_items.begin()); 269 i != radio_group.radio_items.end(); ++i) { 270 views::RadioButton* radio = new views::RadioButton(UTF8ToUTF16(*i), 0); 271 radio->SetEnabled(bubble_content.radio_group_enabled); 272 radio->set_listener(this); 273 radio_group_.push_back(radio); 274 layout->StartRow(0, indented_kSingleColumnSetId); 275 layout->AddView(radio); 276 bubble_content_empty = false; 277 } 278 DCHECK(!radio_group_.empty()); 279 // Now that the buttons have been added to the view hierarchy, it's safe 280 // to call SetChecked() on them. 281 radio_group_[radio_group.default_item]->SetChecked(true); 282 } 283 284 // Layout code for the media device menus. 285 if (content_setting_bubble_model_->content_type() == 286 CONTENT_SETTINGS_TYPE_MEDIASTREAM) { 287 const int kMediaMenuColumnSetId = 2; 288 views::ColumnSet* menu_column_set = 289 layout->AddColumnSet(kMediaMenuColumnSetId); 290 menu_column_set->AddPaddingColumn(0, views::kCheckboxIndent); 291 menu_column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0, 292 GridLayout::USE_PREF, 0, 0); 293 menu_column_set->AddPaddingColumn( 294 0, views::kRelatedControlHorizontalSpacing); 295 menu_column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1, 296 GridLayout::USE_PREF, 0, 0); 297 298 int menu_width = 0; 299 for (ContentSettingBubbleModel::MediaMenuMap::const_iterator i( 300 bubble_content.media_menus.begin()); 301 i != bubble_content.media_menus.end(); ++i) { 302 if (!bubble_content_empty) 303 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 304 layout->StartRow(0, kMediaMenuColumnSetId); 305 306 views::Label* label = new views::Label(UTF8ToUTF16(i->second.label)); 307 label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 308 309 views::MenuButton* menu_button = new views::MenuButton( 310 NULL, UTF8ToUTF16((i->second.selected_device.name)), this, true); 311 menu_button->set_alignment(views::TextButton::ALIGN_LEFT); 312 menu_button->set_border( 313 new views::TextButtonNativeThemeBorder(menu_button)); 314 menu_button->set_animate_on_state_change(false); 315 316 MediaMenuParts* menu_view = new MediaMenuParts(i->first); 317 menu_view->menu_model.reset(new ContentSettingMediaMenuModel( 318 i->first, 319 content_setting_bubble_model_.get(), 320 base::Bind(&ContentSettingBubbleContents::UpdateMenuLabel, 321 base::Unretained(this)))); 322 media_menus_[menu_button] = menu_view; 323 324 if (!menu_view->menu_model->GetItemCount()) { 325 // Show a "None available" title and grey out the menu when there is no 326 // available device. 327 menu_button->SetText( 328 l10n_util::GetStringUTF16(IDS_MEDIA_MENU_NO_DEVICE_TITLE)); 329 menu_button->SetEnabled(false); 330 } 331 332 // Use the longest width of the menus as the width of the menu buttons. 333 menu_width = std::max(menu_width, 334 GetPreferredMediaMenuWidth( 335 menu_button, menu_view->menu_model.get())); 336 337 layout->AddView(label); 338 layout->AddView(menu_button); 339 340 bubble_content_empty = false; 341 } 342 343 // Make sure the width is at least kMinMediaMenuButtonWidth. The 344 // maximum width will be clamped by kMaxContentsWidth of the view. 345 menu_width = std::max(kMinMediaMenuButtonWidth, menu_width); 346 347 // Set all the menu buttons to the width we calculated above. 348 for (MediaMenuPartsMap::const_iterator i = media_menus_.begin(); 349 i != media_menus_.end(); ++i) { 350 i->first->set_min_width(menu_width); 351 i->first->set_max_width(menu_width); 352 } 353 } 354 355 gfx::Font domain_font = 356 views::Label().font().DeriveFont(0, gfx::Font::BOLD); 357 for (std::vector<ContentSettingBubbleModel::DomainList>::const_iterator i( 358 bubble_content.domain_lists.begin()); 359 i != bubble_content.domain_lists.end(); ++i) { 360 layout->StartRow(0, kSingleColumnSetId); 361 views::Label* section_title = new views::Label(UTF8ToUTF16(i->title)); 362 section_title->SetMultiLine(true); 363 section_title->SetHorizontalAlignment(gfx::ALIGN_LEFT); 364 layout->AddView(section_title, 1, 1, GridLayout::FILL, GridLayout::LEADING); 365 for (std::set<std::string>::const_iterator j = i->hosts.begin(); 366 j != i->hosts.end(); ++j) { 367 layout->StartRow(0, indented_kSingleColumnSetId); 368 layout->AddView(new views::Label(UTF8ToUTF16(*j), domain_font)); 369 } 370 bubble_content_empty = false; 371 } 372 373 if (!bubble_content.custom_link.empty()) { 374 custom_link_ = new views::Link(UTF8ToUTF16(bubble_content.custom_link)); 375 custom_link_->SetEnabled(bubble_content.custom_link_enabled); 376 custom_link_->set_listener(this); 377 if (!bubble_content_empty) 378 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 379 layout->StartRow(0, kSingleColumnSetId); 380 layout->AddView(custom_link_); 381 bubble_content_empty = false; 382 } 383 384 if (!bubble_content_empty) { 385 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 386 layout->StartRow(0, kSingleColumnSetId); 387 layout->AddView(new views::Separator(views::Separator::HORIZONTAL), 1, 1, 388 GridLayout::FILL, GridLayout::FILL); 389 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 390 } 391 392 const int kDoubleColumnSetId = 1; 393 views::ColumnSet* double_column_set = 394 layout->AddColumnSet(kDoubleColumnSetId); 395 double_column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 1, 396 GridLayout::USE_PREF, 0, 0); 397 double_column_set->AddPaddingColumn( 398 0, views::kUnrelatedControlHorizontalSpacing); 399 double_column_set->AddColumn(GridLayout::TRAILING, GridLayout::CENTER, 0, 400 GridLayout::USE_PREF, 0, 0); 401 402 layout->StartRow(0, kDoubleColumnSetId); 403 manage_link_ = new views::Link(UTF8ToUTF16(bubble_content.manage_link)); 404 manage_link_->set_listener(this); 405 layout->AddView(manage_link_); 406 if (content_setting_bubble_model_->content_type() != 407 CONTENT_SETTINGS_TYPE_SAVE_PASSWORD) { 408 close_button_ = 409 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE)); 410 close_button_->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON); 411 layout->AddView(close_button_); 412 } 413} 414 415void ContentSettingBubbleContents::ButtonPressed(views::Button* sender, 416 const ui::Event& event) { 417 if (sender == close_button_) { 418 content_setting_bubble_model_->OnDoneClicked(); 419 StartFade(false); 420 return; 421 } 422 423 for (RadioGroup::const_iterator i(radio_group_.begin()); 424 i != radio_group_.end(); ++i) { 425 if (sender == *i) { 426 content_setting_bubble_model_->OnRadioClicked(i - radio_group_.begin()); 427 return; 428 } 429 } 430 NOTREACHED() << "unknown radio"; 431} 432 433void ContentSettingBubbleContents::LinkClicked(views::Link* source, 434 int event_flags) { 435 if (source == custom_link_) { 436 content_setting_bubble_model_->OnCustomLinkClicked(); 437 StartFade(false); 438 return; 439 } 440 if (source == manage_link_) { 441 StartFade(false); 442 content_setting_bubble_model_->OnManageLinkClicked(); 443 // CAREFUL: Showing the settings window activates it, which deactivates the 444 // info bubble, which causes it to close, which deletes us. 445 return; 446 } 447 448 PopupLinks::const_iterator i(popup_links_.find(source)); 449 DCHECK(i != popup_links_.end()); 450 content_setting_bubble_model_->OnPopupClicked(i->second); 451} 452 453void ContentSettingBubbleContents::OnMenuButtonClicked( 454 views::View* source, 455 const gfx::Point& point) { 456 MediaMenuPartsMap::iterator i(media_menus_.find( 457 static_cast<views::MenuButton*>(source))); 458 DCHECK(i != media_menus_.end()); 459 460 menu_runner_.reset(new views::MenuRunner(i->second->menu_model.get())); 461 462 gfx::Point screen_location; 463 views::View::ConvertPointToScreen(i->first, &screen_location); 464 ignore_result(menu_runner_->RunMenuAt( 465 source->GetWidget(), 466 i->first, 467 gfx::Rect(screen_location, i->first->size()), 468 views::MenuItemView::TOPLEFT, 469 ui::MENU_SOURCE_NONE, 470 views::MenuRunner::HAS_MNEMONICS)); 471} 472 473void ContentSettingBubbleContents::Observe( 474 int type, 475 const content::NotificationSource& source, 476 const content::NotificationDetails& details) { 477 DCHECK_EQ(content::NOTIFICATION_WEB_CONTENTS_DESTROYED, type); 478 DCHECK(source == content::Source<WebContents>(web_contents_)); 479 web_contents_ = NULL; 480} 481 482int ContentSettingBubbleContents::GetPreferredMediaMenuWidth( 483 views::MenuButton* button, 484 ui::SimpleMenuModel* menu_model) { 485 string16 title = button->text(); 486 487 int width = button->GetPreferredSize().width(); 488 for (int i = 0; i < menu_model->GetItemCount(); ++i) { 489 button->SetText(menu_model->GetLabelAt(i)); 490 width = std::max(width, button->GetPreferredSize().width()); 491 } 492 493 // Recover the title for the menu button. 494 button->SetText(title); 495 return width; 496} 497