blob: b669ec04bf44a40f443674c0487d95796c99e0fe [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/content_setting_bubble_contents.h"
#include <utility>
#include <vector>
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
#include "chrome/browser/plugins/plugin_finder.h"
#include "chrome/browser/plugins/plugin_metadata.h"
#include "chrome/browser/ui/browser_dialogs.h"
#include "chrome/browser/ui/layout_constants.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/chrome_typography.h"
#include "chrome/browser/ui/views/content_setting_domain_list_view.h"
#include "chrome/common/chrome_features.h"
#include "chrome/grit/generated_resources.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/strings/grit/components_strings.h"
#include "components/vector_icons/vector_icons.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/plugin_service.h"
#include "content/public/browser/web_contents.h"
#include "ui/base/cursor/cursor.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/combobox_model.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/vector_icon_types.h"
#include "ui/native_theme/native_theme.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/button/image_button_factory.h"
#include "ui/views/controls/button/label_button_border.h"
#include "ui/views/controls/button/md_text_button.h"
#include "ui/views/controls/button/radio_button.h"
#include "ui/views/controls/combobox/combobox.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/link.h"
#include "ui/views/controls/scroll_view.h"
#include "ui/views/controls/separator.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/grid_layout.h"
#include "ui/views/native_cursor.h"
#include "ui/views/window/dialog_client_view.h"
namespace {
// Display a maximum of 4 visible items in a list before scrolling.
const int kMaxVisibleListItems = 4;
// Padding for list items and icons.
const gfx::Insets kTitleDescriptionListItemInset = gfx::Insets(3, 0, 13, 0);
// Returns a view with the given title and description.
std::unique_ptr<views::View> ConstructTitleDescriptionItemContents(
const base::string16& title,
const base::string16& description) {
auto label_container = std::make_unique<views::View>();
label_container->SetBorder(
views::CreateEmptyBorder(kTitleDescriptionListItemInset));
label_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical, gfx::Insets(), 0));
if (!title.empty()) {
auto title_label = std::make_unique<views::Label>(
title, views::style::CONTEXT_LABEL, views::style::STYLE_PRIMARY,
gfx::DirectionalityMode::DIRECTIONALITY_FROM_UI);
title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
title_label->SetAllowCharacterBreak(true);
label_container->AddChildView(title_label.release());
}
if (!description.empty()) {
auto description_label = std::make_unique<views::Label>(
description, views::style::CONTEXT_LABEL, views::style::STYLE_DISABLED,
gfx::DirectionalityMode::DIRECTIONALITY_FROM_UI);
description_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
description_label->SetAllowCharacterBreak(true);
label_container->AddChildView(description_label.release());
}
return label_container;
}
// Constructs a view for a list item containing a link.
std::unique_ptr<views::Link> ConstructLinkItemContents(
const base::string16& title,
views::LinkListener* parent_) {
auto link = std::make_unique<views::Link>(title);
link->SetElideBehavior(gfx::ELIDE_MIDDLE);
link->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
link->set_listener(parent_);
return link;
}
// Constructs an ImageView for a list item with the given icon.
std::unique_ptr<views::ImageView> ConstructItemIcon(
const gfx::VectorIcon* icon,
const gfx::VectorIcon& badge) {
auto icon_view = std::make_unique<views::ImageView>();
DCHECK(icon);
icon_view->SetBorder(
views::CreateEmptyBorder(kTitleDescriptionListItemInset));
const SkColor icon_color = views::style::GetColor(
*icon_view, CONTEXT_BODY_TEXT_SMALL, views::style::STYLE_PRIMARY);
icon_view->SetImage(CreateVectorIconWithBadge(
*icon, GetLayoutConstant(LOCATION_BAR_ICON_SIZE), icon_color, badge));
return icon_view;
} // namespace
enum class LayoutRowType {
DEFAULT,
INDENTED,
FULL_WIDTH,
};
// Represents a row containing a single view in the layout. The type determines
// the view's horizontal margins.
struct LayoutRow {
std::unique_ptr<views::View> view;
LayoutRowType type;
};
// A combobox model that builds the contents of the media capture devices menu
// in the content setting bubble.
class MediaComboboxModel : public ui::ComboboxModel {
public:
explicit MediaComboboxModel(blink::mojom::MediaStreamType type);
~MediaComboboxModel() override;
blink::mojom::MediaStreamType type() const { return type_; }
const blink::MediaStreamDevices& GetDevices() const;
int GetDeviceIndex(const blink::MediaStreamDevice& device) const;
// ui::ComboboxModel:
int GetItemCount() const override;
base::string16 GetItemAt(int index) override;
private:
blink::mojom::MediaStreamType type_;
DISALLOW_COPY_AND_ASSIGN(MediaComboboxModel);
};
// A view representing one or more rows, each containing a label and combobox
// pair, that allow the user to select a device for each media type (microphone
// and/or camera).
class MediaMenuBlock : public views::View {
public:
MediaMenuBlock(views::ComboboxListener* listener,
ContentSettingBubbleModel::MediaMenuMap media) {
const ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
views::GridLayout* layout =
SetLayoutManager(std::make_unique<views::GridLayout>());
constexpr int kColumnSetId = 0;
views::ColumnSet* column_set = layout->AddColumnSet(kColumnSetId);
column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
views::GridLayout::kFixedSize,
views::GridLayout::USE_PREF, 0, 0);
column_set->AddPaddingColumn(
views::GridLayout::kFixedSize,
provider->GetDistanceMetric(
views::DISTANCE_RELATED_CONTROL_HORIZONTAL));
column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1.0,
views::GridLayout::FIXED, 0, 0);
bool first_row = true;
for (auto i = media.cbegin(); i != media.cend(); ++i) {
if (!first_row) {
layout->AddPaddingRow(views::GridLayout::kFixedSize,
provider->GetDistanceMetric(
views::DISTANCE_RELATED_CONTROL_VERTICAL));
}
first_row = false;
layout->StartRow(views::GridLayout::kFixedSize, kColumnSetId);
blink::mojom::MediaStreamType stream_type = i->first;
const ContentSettingBubbleModel::MediaMenu& menu = i->second;
auto label = std::make_unique<views::Label>(menu.label);
label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
layout->AddView(std::move(label));
auto combobox_model = std::make_unique<MediaComboboxModel>(stream_type);
// Disable the device selection when the website is managing the devices
// itself or if there are no devices present.
const bool combobox_enabled =
!menu.disabled && !combobox_model->GetDevices().empty();
const int combobox_selected_index =
combobox_model->GetDevices().empty()
? 0
: combobox_model->GetDeviceIndex(menu.selected_device);
// The combobox takes ownership of the model.
auto combobox =
std::make_unique<views::Combobox>(std::move(combobox_model));
combobox->SetEnabled(combobox_enabled);
combobox->set_listener(listener);
combobox->SetSelectedIndex(combobox_selected_index);
layout->AddView(std::move(combobox));
}
}
private:
DISALLOW_COPY_AND_ASSIGN(MediaMenuBlock);
};
} // namespace
// MediaComboboxModel ----------------------------------------------------------
MediaComboboxModel::MediaComboboxModel(blink::mojom::MediaStreamType type)
: type_(type) {
DCHECK(type_ == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE ||
type_ == blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);
}
MediaComboboxModel::~MediaComboboxModel() {}
const blink::MediaStreamDevices& MediaComboboxModel::GetDevices() const {
MediaCaptureDevicesDispatcher* dispatcher =
MediaCaptureDevicesDispatcher::GetInstance();
return type_ == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE
? dispatcher->GetAudioCaptureDevices()
: dispatcher->GetVideoCaptureDevices();
}
int MediaComboboxModel::GetDeviceIndex(
const blink::MediaStreamDevice& device) const {
const auto& devices = GetDevices();
for (size_t i = 0; i < devices.size(); ++i) {
if (device.id == devices[i].id)
return i;
}
NOTREACHED();
return 0;
}
int MediaComboboxModel::GetItemCount() const {
return std::max(1, static_cast<int>(GetDevices().size()));
}
base::string16 MediaComboboxModel::GetItemAt(int index) {
return GetDevices().empty()
? l10n_util::GetStringUTF16(IDS_MEDIA_MENU_NO_DEVICE_TITLE)
: base::UTF8ToUTF16(GetDevices()[index].name);
}
// ContentSettingBubbleContents::ListItemContainer -----------------------------
class ContentSettingBubbleContents::ListItemContainer : public views::View {
public:
explicit ListItemContainer(ContentSettingBubbleContents* parent);
// Creates and adds child views representing |item|.
void AddItem(const ContentSettingBubbleModel::ListItem& item);
// Calling this will delete related children.
void RemoveRowAtIndex(int index);
// Returns row index of |link| among list items.
int GetRowIndexOf(const views::Link* link) const;
private:
using Row = std::pair<views::ImageView*, views::View*>;
using NewRow = std::pair<std::unique_ptr<views::ImageView>,
std::unique_ptr<views::View>>;
void ResetLayout();
void AddRowToLayout(const Row& row);
Row AddNewRowToLayout(NewRow row);
void UpdateScrollHeight(const Row& row);
ContentSettingBubbleContents* parent_;
// Our controls representing list items, so we can add or remove
// these dynamically. Each pair represents one list item.
std::vector<Row> list_item_views_;
DISALLOW_COPY_AND_ASSIGN(ListItemContainer);
};
ContentSettingBubbleContents::ListItemContainer::ListItemContainer(
ContentSettingBubbleContents* parent)
: parent_(parent) {
ResetLayout();
}
void ContentSettingBubbleContents::ListItemContainer::AddItem(
const ContentSettingBubbleModel::ListItem& item) {
std::unique_ptr<views::ImageView> item_icon =
std::make_unique<views::ImageView>();
auto item_contents =
item.has_link
? ConstructLinkItemContents(item.title, parent_)
: ConstructTitleDescriptionItemContents(item.title, item.description);
if (item.image) {
item_icon =
ConstructItemIcon(item.image, item.has_blocked_badge ? kBlockedBadgeIcon
: gfx::kNoneIcon);
}
list_item_views_.push_back(AddNewRowToLayout(
NewRow(std::move(item_icon), std::move(item_contents))));
}
void ContentSettingBubbleContents::ListItemContainer::RemoveRowAtIndex(
int index) {
auto& children = list_item_views_[index];
delete children.first;
delete children.second;
list_item_views_.erase(list_item_views_.begin() + index);
// As GridLayout can't remove rows, we have to rebuild it entirely.
ResetLayout();
for (auto& row : list_item_views_)
AddRowToLayout(row);
}
int ContentSettingBubbleContents::ListItemContainer::GetRowIndexOf(
const views::Link* link) const {
auto has_link = [link](const Row& row) { return row.second == link; };
auto iter =
std::find_if(list_item_views_.begin(), list_item_views_.end(), has_link);
return (iter == list_item_views_.end())
? -1
: std::distance(list_item_views_.begin(), iter);
}
void ContentSettingBubbleContents::ListItemContainer::ResetLayout() {
views::GridLayout* layout =
SetLayoutManager(std::make_unique<views::GridLayout>());
views::ColumnSet* item_list_column_set = layout->AddColumnSet(0);
item_list_column_set->AddColumn(
views::GridLayout::LEADING, views::GridLayout::FILL,
views::GridLayout::kFixedSize, views::GridLayout::USE_PREF, 0, 0);
const int related_control_horizontal_spacing =
ChromeLayoutProvider::Get()->GetDistanceMetric(
views::DISTANCE_RELATED_CONTROL_HORIZONTAL);
item_list_column_set->AddPaddingColumn(views::GridLayout::kFixedSize,
related_control_horizontal_spacing);
item_list_column_set->AddColumn(views::GridLayout::LEADING,
views::GridLayout::FILL, 1.0,
views::GridLayout::USE_PREF, 0, 0);
auto* scroll_view = views::ScrollView::GetScrollViewForContents(this);
// When this function is called from the constructor, the view has not yet
// been placed into a ScrollView.
if (scroll_view)
scroll_view->ClipHeightTo(-1, -1);
}
void ContentSettingBubbleContents::ListItemContainer::AddRowToLayout(
const Row& row) {
views::GridLayout* layout =
static_cast<views::GridLayout*>(GetLayoutManager());
DCHECK(layout);
layout->StartRow(views::GridLayout::kFixedSize, 0);
layout->AddExistingView(row.first);
layout->AddExistingView(row.second);
UpdateScrollHeight(row);
}
ContentSettingBubbleContents::ListItemContainer::Row
ContentSettingBubbleContents::ListItemContainer::AddNewRowToLayout(NewRow row) {
views::GridLayout* layout =
static_cast<views::GridLayout*>(GetLayoutManager());
DCHECK(layout);
Row row_result;
layout->StartRow(views::GridLayout::kFixedSize, 0);
row_result.first = layout->AddView(std::move(row.first));
row_result.second = layout->AddView(std::move(row.second));
UpdateScrollHeight(row_result);
return row_result;
}
void ContentSettingBubbleContents::ListItemContainer::UpdateScrollHeight(
const Row& row) {
auto* scroll_view = views::ScrollView::GetScrollViewForContents(this);
DCHECK(scroll_view);
if (!scroll_view->is_bounded()) {
scroll_view->ClipHeightTo(
0, std::max(row.first->GetPreferredSize().height(),
row.second->GetPreferredSize().height()) *
kMaxVisibleListItems);
}
}
// ContentSettingBubbleContents -----------------------------------------------
ContentSettingBubbleContents::ContentSettingBubbleContents(
std::unique_ptr<ContentSettingBubbleModel> content_setting_bubble_model,
content::WebContents* web_contents,
views::View* anchor_view,
views::BubbleBorder::Arrow arrow)
: content::WebContentsObserver(web_contents),
BubbleDialogDelegateView(anchor_view, arrow),
content_setting_bubble_model_(std::move(content_setting_bubble_model)) {
chrome::RecordDialogCreation(
chrome::DialogIdentifier::CONTENT_SETTING_CONTENTS);
}
ContentSettingBubbleContents::~ContentSettingBubbleContents() {
// Must remove the children here so the comboboxes get destroyed before
// their associated models.
RemoveAllChildViews(true);
}
void ContentSettingBubbleContents::WindowClosing() {
if (content_setting_bubble_model_)
content_setting_bubble_model_->CommitChanges();
}
gfx::Size ContentSettingBubbleContents::CalculatePreferredSize() const {
const int width = ChromeLayoutProvider::Get()->GetDistanceMetric(
DISTANCE_BUBBLE_PREFERRED_WIDTH) -
margins().width();
return gfx::Size(width, GetHeightForWidth(width));
}
void ContentSettingBubbleContents::OnListItemAdded(
const ContentSettingBubbleModel::ListItem& item) {
DCHECK(list_item_container_);
list_item_container_->AddItem(item);
SizeToContents();
}
void ContentSettingBubbleContents::OnListItemRemovedAt(int index) {
DCHECK(list_item_container_);
list_item_container_->RemoveRowAtIndex(index);
SizeToContents();
}
int ContentSettingBubbleContents::GetSelectedRadioOption() {
for (RadioGroup::const_iterator i(radio_group_.begin());
i != radio_group_.end(); ++i) {
if ((*i)->GetChecked())
return i - radio_group_.begin();
}
NOTREACHED();
return 0;
}
void ContentSettingBubbleContents::OnThemeChanged() {
views::BubbleDialogDelegateView::OnThemeChanged();
if (learn_more_button_)
StyleLearnMoreButton();
}
base::string16 ContentSettingBubbleContents::GetWindowTitle() const {
if (!content_setting_bubble_model_)
return base::string16();
return content_setting_bubble_model_->bubble_content().title;
}
bool ContentSettingBubbleContents::ShouldShowCloseButton() const {
return true;
}
void ContentSettingBubbleContents::Init() {
DCHECK(content_setting_bubble_model_);
const ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical, gfx::Insets(),
provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL)));
std::vector<LayoutRow> rows;
const ContentSettingBubbleModel::BubbleContent& bubble_content =
content_setting_bubble_model_->bubble_content();
if (!bubble_content.message.empty()) {
auto message_label = std::make_unique<views::Label>(
bubble_content.message, views::style::CONTEXT_LABEL,
views::style::STYLE_SECONDARY);
message_label->SetMultiLine(true);
message_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
rows.push_back({std::move(message_label), LayoutRowType::DEFAULT});
}
// Layout for the item list (blocked plugins and popups).
if (!bubble_content.list_items.empty()) {
auto list_item_container = std::make_unique<ListItemContainer>(this);
list_item_container->SetBorder(
views::CreateEmptyBorder(0, margins().left(), 0, margins().right()));
auto scroll_view = std::make_unique<views::ScrollView>();
list_item_container_ =
scroll_view->SetContents(std::move(list_item_container));
rows.push_back({std::move(scroll_view), LayoutRowType::FULL_WIDTH});
for (const ContentSettingBubbleModel::ListItem& list_item :
bubble_content.list_items) {
list_item_container_->AddItem(list_item);
}
}
const ContentSettingBubbleModel::RadioGroup& radio_group =
bubble_content.radio_group;
if (!radio_group.radio_items.empty()) {
for (auto i(radio_group.radio_items.begin());
i != radio_group.radio_items.end(); ++i) {
auto radio = std::make_unique<views::RadioButton>(*i, 0);
radio->SetEnabled(radio_group.user_managed);
radio->SetMultiLine(true);
radio_group_.push_back(radio.get());
rows.push_back({std::move(radio), LayoutRowType::INDENTED});
}
DCHECK(!radio_group_.empty());
// Now that the buttons have been added to the view hierarchy, it's safe
// to call SetChecked() on them.
radio_group_[radio_group.default_item]->SetChecked(true);
}
// Layout code for the media device menus.
if (content_setting_bubble_model_->AsMediaStreamBubbleModel()) {
rows.push_back(
{std::make_unique<MediaMenuBlock>(this, bubble_content.media_menus),
LayoutRowType::INDENTED});
}
for (auto i(bubble_content.domain_lists.begin());
i != bubble_content.domain_lists.end(); ++i) {
auto list_view =
std::make_unique<ContentSettingDomainListView>(i->title, i->hosts);
rows.push_back({std::move(list_view), LayoutRowType::DEFAULT});
}
if (!bubble_content.custom_link.empty()) {
auto custom_link =
std::make_unique<views::Link>(bubble_content.custom_link);
custom_link->SetEnabled(bubble_content.custom_link_enabled);
custom_link->SetMultiLine(true);
custom_link->set_listener(this);
custom_link->SetHorizontalAlignment(gfx::ALIGN_LEFT);
custom_link_ = custom_link.get();
rows.push_back({std::move(custom_link), LayoutRowType::DEFAULT});
}
if (bubble_content.manage_text_style ==
ContentSettingBubbleModel::ManageTextStyle::kCheckbox) {
auto manage_checkbox =
std::make_unique<views::Checkbox>(bubble_content.manage_text, this);
manage_checkbox_ = manage_checkbox.get();
rows.push_back({std::move(manage_checkbox), LayoutRowType::DEFAULT});
}
// We have to apply the left and right margins manually, because rows using
// LayoutRowType::FULL_WIDTH need to not have them applied to look correct.
const int left_margin = margins().left();
const int right_margin = margins().right();
set_margins(gfx::Insets(margins().top(), 0, margins().bottom(), 0));
for (LayoutRow& row : rows) {
if (row.type != LayoutRowType::FULL_WIDTH) {
const int row_left_margin =
left_margin + (row.type == LayoutRowType::INDENTED
? provider->GetDistanceMetric(
DISTANCE_SUBSECTION_HORIZONTAL_INDENT)
: 0);
row.view->SetBorder(
views::CreateEmptyBorder(0, row_left_margin, 0, right_margin));
}
AddChildView(std::move(row.view));
}
content_setting_bubble_model_->set_owner(this);
}
std::unique_ptr<views::View> ContentSettingBubbleContents::CreateExtraView() {
DCHECK(content_setting_bubble_model_);
const auto& bubble_content = content_setting_bubble_model_->bubble_content();
const auto* layout = ChromeLayoutProvider::Get();
std::vector<std::unique_ptr<views::View>> extra_views;
// Optionally add a help icon if the view wants to link to a help page.
if (bubble_content.show_learn_more) {
auto learn_more_button = views::CreateVectorImageButton(this);
learn_more_button->SetFocusForPlatform();
learn_more_button->SetTooltipText(
l10n_util::GetStringUTF16(IDS_LEARN_MORE));
learn_more_button_ = learn_more_button.get();
StyleLearnMoreButton();
extra_views.push_back(std::move(learn_more_button));
}
// Optionally add a "Manage" button if the view wants to use a button to
// invoke a separate management UI related to the dialog content.
if (bubble_content.manage_text_style ==
ContentSettingBubbleModel::ManageTextStyle::kButton) {
base::string16 title = bubble_content.manage_text;
if (title.empty())
title = l10n_util::GetStringUTF16(IDS_MANAGE);
auto manage_button =
views::MdTextButton::CreateSecondaryUiButton(this, title);
manage_button->SetMinSize(gfx::Size(
layout->GetDistanceMetric(views::DISTANCE_DIALOG_BUTTON_MINIMUM_WIDTH),
0));
manage_button_ = manage_button.get();
extra_views.push_back(std::move(manage_button));
}
if (extra_views.empty())
return nullptr;
if (extra_views.size() == 1)
return std::move(extra_views.front());
auto container = std::make_unique<views::View>();
container->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, gfx::Insets(),
layout->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_HORIZONTAL)));
for (auto& extra_view : extra_views)
container->AddChildView(std::move(extra_view));
return container;
}
bool ContentSettingBubbleContents::Accept() {
content_setting_bubble_model_->OnDoneButtonClicked();
return true;
}
bool ContentSettingBubbleContents::Close() {
return true;
}
int ContentSettingBubbleContents::GetDialogButtons() const {
return ui::DIALOG_BUTTON_OK;
}
base::string16 ContentSettingBubbleContents::GetDialogButtonLabel(
ui::DialogButton button) const {
if (!content_setting_bubble_model_)
return base::string16();
const base::string16& done_text =
content_setting_bubble_model_->bubble_content().done_button_text;
return done_text.empty() ? l10n_util::GetStringUTF16(IDS_DONE) : done_text;
}
void ContentSettingBubbleContents::StyleLearnMoreButton() {
DCHECK(learn_more_button_);
SkColor text_color = GetNativeTheme()->GetSystemColor(
ui::NativeTheme::kColorId_LabelEnabledColor);
views::SetImageFromVectorIcon(learn_more_button_,
vector_icons::kHelpOutlineIcon, text_color);
}
void ContentSettingBubbleContents::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInMainFrame() || !navigation_handle->HasCommitted())
return;
// Content settings are based on the main frame, so if it switches then
// close up shop.
GetWidget()->Close();
}
void ContentSettingBubbleContents::OnVisibilityChanged(
content::Visibility visibility) {
if (visibility == content::Visibility::HIDDEN)
GetWidget()->Close();
}
void ContentSettingBubbleContents::WebContentsDestroyed() {
// Destroy the bubble model to ensure that the underlying WebContents outlives
// it.
content_setting_bubble_model_->CommitChanges();
content_setting_bubble_model_.reset();
// Closing the widget should synchronously hide it (and post a task to delete
// it). Subsequent event listener methods should not be invoked on hidden
// widgets.
GetWidget()->Close();
}
void ContentSettingBubbleContents::ButtonPressed(views::Button* sender,
const ui::Event& event) {
DCHECK(content_setting_bubble_model_);
if (sender == manage_checkbox_) {
content_setting_bubble_model_->OnManageCheckboxChecked(
manage_checkbox_->GetChecked());
// Toggling the check state may change the dialog button text.
DialogModelChanged();
GetDialogClientView()->Layout();
} else if (sender == learn_more_button_) {
GetWidget()->Close();
content_setting_bubble_model_->OnLearnMoreClicked();
} else if (sender == manage_button_) {
GetWidget()->Close();
content_setting_bubble_model_->OnManageButtonClicked();
} else {
NOTREACHED();
}
}
void ContentSettingBubbleContents::LinkClicked(views::Link* source,
int event_flags) {
DCHECK(content_setting_bubble_model_);
if (source == custom_link_) {
content_setting_bubble_model_->OnCustomLinkClicked();
GetWidget()->Close();
return;
}
int row = list_item_container_->GetRowIndexOf(source);
DCHECK_NE(row, -1);
content_setting_bubble_model_->OnListItemClicked(row, event_flags);
}
void ContentSettingBubbleContents::OnPerformAction(views::Combobox* combobox) {
DCHECK(content_setting_bubble_model_);
MediaComboboxModel* model =
static_cast<MediaComboboxModel*>(combobox->model());
content_setting_bubble_model_->OnMediaMenuClicked(
model->type(), model->GetDevices()[combobox->GetSelectedIndex()].id);
}