blob: 949c080ac7ccc5c863d43c2286ddb55402f2d37c [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/content_settings/content_setting_bubble_model.h"
#include <stddef.h>
#include <memory>
#include <string>
#include <utility>
#include "base/auto_reset.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/lazy_instance.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#include "base/notreached.h"
#include "base/scoped_observation.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/content_settings/chrome_content_settings_utils.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/content_settings/mixed_content_settings_tab_helper.h"
#include "chrome/browser/content_settings/page_specific_content_settings_delegate.h"
#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
#include "chrome/browser/download/download_request_limiter.h"
#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
#include "chrome/browser/media/webrtc/permission_bubble_media_access_handler.h"
#include "chrome/browser/permissions/permission_actions_history_factory.h"
#include "chrome/browser/permissions/permission_decision_auto_blocker_factory.h"
#include "chrome/browser/permissions/quiet_notification_permission_ui_config.h"
#include "chrome/browser/permissions/system/system_permission_settings.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/collected_cookies_infobar_delegate.h"
#include "chrome/browser/ui/content_settings/content_setting_bubble_model_delegate.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/url_identity.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/grit/theme_resources.h"
#include "components/blocked_content/popup_blocker_tab_helper.h"
#include "components/content_settings/browser/page_specific_content_settings.h"
#include "components/content_settings/common/content_settings_agent.mojom.h"
#include "components/content_settings/core/browser/content_settings_utils.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_constraints.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/content_settings/core/common/content_settings_utils.h"
#include "components/custom_handlers/protocol_handler_registry.h"
#include "components/infobars/content/content_infobar_manager.h"
#include "components/permissions/constants.h"
#include "components/permissions/features.h"
#include "components/permissions/permission_decision_auto_blocker.h"
#include "components/permissions/permission_request_manager.h"
#include "components/permissions/permission_uma_util.h"
#include "components/permissions/permission_util.h"
#include "components/permissions/permissions_client.h"
#include "components/strings/grit/components_strings.h"
#include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
#include "components/subresource_filter/core/browser/subresource_filter_constants.h"
#include "components/subresource_filter/core/browser/subresource_filter_features.h"
#include "components/url_formatter/elide_url.h"
#include "components/vector_icons/vector_icons.h"
#include "content/public/browser/page.h"
#include "content/public/browser/permission_controller.h"
#include "content/public/browser/permission_descriptor_util.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/weak_document_ptr.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "services/device/public/cpp/device_features.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/loader/network_utils.h"
#include "third_party/blink/public/common/permissions/permission_utils.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/window_open_disposition.h"
#include "ui/base/window_open_disposition_utils.h"
#include "ui/events/event.h"
#include "ui/gfx/vector_icon_types.h"
#include "ui/resources/grit/ui_resources.h"
#if BUILDFLAG(IS_MAC)
#include "base/apple/bundle_locations.h"
#include "base/mac/mac_util.h"
#include "chrome/browser/permissions/system/system_media_capture_permissions_mac.h"
#include "chrome/browser/web_applications/os_integration/mac/web_app_shortcut_mac.h"
#include "chrome/browser/web_applications/web_app_tab_helper.h"
#endif
using base::UserMetricsAction;
using content::WebContents;
using content_settings::PageSpecificContentSettings;
using content_settings::SettingInfo;
using content_settings::SettingSource;
using content_settings::mojom::SessionModel;
namespace {
using ContentSettingBubbleAction =
ContentSettingBubbleModel::ContentSettingBubbleAction;
void RecordActionHistogram(ContentSettingsType type,
ContentSettingBubbleAction action) {
switch (type) {
case ContentSettingsType::STORAGE_ACCESS:
base::UmaHistogramEnumeration(
"ContentSettings.Bubble.StorageAccess.Action", action);
break;
default:
// Currently only defined and implemented for StorageAccess.
NOTREACHED();
}
}
using QuietUiReason = permissions::PermissionRequestManager::QuietUiReason;
const std::u16string& GetDefaultDisplayURLForTesting() {
static const base::NoDestructor<std::u16string> kDefaultDisplayURL(
u"http://www.example.com");
return *kDefaultDisplayURL;
}
// An override display URL in content setting bubble UI for testing.
std::optional<bool> g_display_url_override_for_testing = std::nullopt;
// Returns a boolean indicating whether the setting should be managed by the
// user (i.e. it is not controlled by policy). Also takes a (nullable) out-param
// which is populated by the actual setting for the given URL.
bool GetSettingManagedByUser(const GURL& url,
ContentSettingsType type,
Profile* profile,
ContentSetting* out_setting) {
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(profile);
SettingInfo info;
ContentSetting setting;
if (type == ContentSettingsType::COOKIES) {
// TODO(crbug.com/40247160): Consider whether the following check should
// somehow determine real CookieSettingOverrides rather than default to
// none.
setting = CookieSettingsFactory::GetForProfile(profile)->GetCookieSetting(
url, net::SiteForCookies::FromUrl(url), url,
net::CookieSettingOverrides(), &info);
} else {
setting = map->GetContentSetting(url, url, type, &info);
}
if (out_setting) {
*out_setting = setting;
}
// Prevent creation of content settings for illegal urls like about:blank by
// disallowing user management.
return info.source == SettingSource::kUser &&
map->CanSetNarrowestContentSetting(url, url, type);
}
ContentSettingBubbleModel::ListItem CreateUrlListItem(int32_t id,
const GURL& url) {
// Empty URLs should get a placeholder.
// TODO(csharrison): See if we can DCHECK that the URL will be valid here.
std::u16string title = url.spec().empty()
? l10n_util::GetStringUTF16(IDS_TAB_LOADING_TITLE)
: base::UTF8ToUTF16(url.spec());
// Format the title to include the unicode single dot bullet code-point
// \u2022 and two spaces.
title = l10n_util::GetStringFUTF16(IDS_LIST_BULLET, title);
return ContentSettingBubbleModel::ListItem(nullptr, title, std::u16string(),
true /* has_link */,
false /* has_blocked_badge */, id);
}
struct ContentSettingsTypeIdEntry {
ContentSettingsType type;
int id;
};
int GetIdForContentType(base::span<const ContentSettingsTypeIdEntry> entries,
ContentSettingsType type) {
if (auto found =
std::ranges::find(entries, type, &ContentSettingsTypeIdEntry::type);
found != entries.end()) {
return found->id;
}
return 0;
}
void SetAllowRunningInsecureContent(
MixedContentSettingsTabHelper* mixed_content_settings,
content::RenderFrameHost* frame) {
// Set Insecure Content flag only if running insecure content is allowed
if (mixed_content_settings &&
!mixed_content_settings->IsRunningInsecureContentAllowed(*frame)) {
return;
}
mojo::AssociatedRemote<content_settings::mojom::ContentSettingsAgent> agent;
frame->GetRemoteAssociatedInterfaces()->GetInterface(&agent);
agent->SetAllowRunningInsecureContent();
}
constexpr UrlIdentity::TypeSet allowed_types = {
UrlIdentity::Type::kDefault, UrlIdentity::Type::kFile,
UrlIdentity::Type::kIsolatedWebApp};
constexpr UrlIdentity::FormatOptions options;
std::u16string GetUrlForDisplay(Profile* profile, const GURL& url) {
if (g_display_url_override_for_testing.value_or(false)) {
return GetDefaultDisplayURLForTesting(); // IN-TEST
}
UrlIdentity identity =
UrlIdentity::CreateFromUrl(profile, url, allowed_types, options);
return identity.name;
}
// Permissions eligible for auto-revocation by Safety Hub need to track
// `last_visited` timestamp for the site. Automation uses it to decide if the
// site is considered "unused". Unused site permissions are then auto-revoked.
content_settings::ContentSettingConstraints CreateConstraintsForAutoRevocation(
ContentSettingsType content_type,
ContentSetting setting) {
content_settings::ContentSettingConstraints constraints;
if (base::FeatureList::IsEnabled(
permissions::features::
kSafetyHubUnusedPermissionRevocationForAllSurfaces) &&
content_settings::CanBeAutoRevokedAsUnusedPermission(
content_type, content_settings::ContentSettingToValue(setting))) {
constraints.set_track_last_visit_for_autoexpiration(true);
}
return constraints;
}
} // namespace
// static
base::AutoReset<std::optional<bool>>
ContentSettingBubbleModel::CreateScopedDisplayURLOverrideForTesting() {
return base::AutoReset<std::optional<bool>>(
&g_display_url_override_for_testing, true);
}
// ContentSettingSimpleBubbleModel ---------------------------------------------
ContentSettingBubbleModel::ListItem::ListItem(const gfx::VectorIcon* image,
const std::u16string& title,
const std::u16string& description,
bool has_link,
bool has_blocked_badge,
int32_t item_id)
: image(image),
title(title),
description(description),
has_link(has_link),
has_blocked_badge(has_blocked_badge),
item_id(item_id) {}
ContentSettingBubbleModel::ListItem::ListItem(const ListItem& other) = default;
ContentSettingBubbleModel::ListItem&
ContentSettingBubbleModel::ListItem::operator=(const ListItem& other) = default;
ContentSettingSimpleBubbleModel::ContentSettingSimpleBubbleModel(
Delegate* delegate,
WebContents* web_contents,
ContentSettingsType content_type)
: ContentSettingBubbleModel(delegate, web_contents),
content_type_(content_type) {
SetTitle();
SetMessage();
SetManageText();
SetCustomLink();
}
ContentSettingSimpleBubbleModel*
ContentSettingSimpleBubbleModel::AsSimpleBubbleModel() {
return this;
}
bool ContentSettingSimpleBubbleModel::IsContentAllowed() {
PageSpecificContentSettings* content_settings =
PageSpecificContentSettings::GetForFrame(&GetPage().GetMainDocument());
if (content_type() == ContentSettingsType::COOKIES) {
ContentSetting setting;
GetSettingManagedByUser(web_contents()->GetLastCommittedURL(),
content_type(), GetProfile(), &setting);
// We check the content setting here as well because 3PC access influences
// the allowed/blocked status even though the icon is meant for 1PC control.
return content_settings->IsContentAllowed(content_type()) &&
setting == CONTENT_SETTING_ALLOW;
}
return content_settings->IsContentAllowed(content_type()) &&
!content_settings->IsContentBlocked(content_type());
}
void ContentSettingSimpleBubbleModel::SetTitle() {
static const ContentSettingsTypeIdEntry kBlockedTitleIDs[] = {
{ContentSettingsType::COOKIES, IDS_BLOCKED_ON_DEVICE_SITE_DATA_TITLE},
{ContentSettingsType::IMAGES, IDS_BLOCKED_IMAGES_TITLE},
{ContentSettingsType::JAVASCRIPT, IDS_BLOCKED_JAVASCRIPT_TITLE},
{ContentSettingsType::MIXEDSCRIPT,
IDS_BLOCKED_DISPLAYING_INSECURE_CONTENT_TITLE},
{ContentSettingsType::SOUND, IDS_BLOCKED_SOUND_TITLE},
{ContentSettingsType::CLIPBOARD_READ_WRITE, IDS_BLOCKED_CLIPBOARD_TITLE},
{ContentSettingsType::GEOLOCATION, IDS_BLOCKED_GEOLOCATION_TITLE},
{ContentSettingsType::MIDI_SYSEX, IDS_BLOCKED_MIDI_SYSEX_TITLE},
{ContentSettingsType::SENSORS, IDS_BLOCKED_SENSORS_TITLE},
#if BUILDFLAG(IS_WIN)
{ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER,
IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_TITLE},
#endif
};
// Fields as for kBlockedTitleIDs, above.
static const ContentSettingsTypeIdEntry kAccessedTitleIDs[] = {
{ContentSettingsType::COOKIES, IDS_ACCESSED_ON_DEVICE_SITE_DATA_TITLE},
{ContentSettingsType::CLIPBOARD_READ_WRITE, IDS_ALLOWED_CLIPBOARD_TITLE},
{ContentSettingsType::GEOLOCATION, IDS_ALLOWED_GEOLOCATION_TITLE},
{ContentSettingsType::MIDI_SYSEX, IDS_ALLOWED_MIDI_SYSEX_TITLE},
{ContentSettingsType::SENSORS, IDS_ALLOWED_SENSORS_TITLE},
#if BUILDFLAG(IS_WIN)
{ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER,
IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_TITLE},
#endif
};
int title_id = [&]() {
#if BUILDFLAG(IS_CHROMEOS)
if (content_type() == ContentSettingsType::SMART_CARD_GUARD) {
return IDS_ACCESSED_SMART_CARD_READER_TITLE;
}
#endif
if (IsContentAllowed()) {
return GetIdForContentType(kAccessedTitleIDs, content_type());
}
return GetIdForContentType(kBlockedTitleIDs, content_type());
}();
if (title_id) {
set_title(l10n_util::GetStringUTF16(title_id));
}
}
void ContentSettingSimpleBubbleModel::SetMessage() {
// TODO(crbug.com/40633805): Make the two arrays below static again once
// we no longer need to check base::FeatureList.
const ContentSettingsTypeIdEntry kBlockedMessageIDs[] = {
{ContentSettingsType::COOKIES, IDS_BLOCKED_ON_DEVICE_SITE_DATA_MESSAGE},
{ContentSettingsType::IMAGES, IDS_BLOCKED_IMAGES_MESSAGE},
{ContentSettingsType::JAVASCRIPT, IDS_BLOCKED_JAVASCRIPT_MESSAGE},
// {ContentSettingsType::POPUPS, No message. intentionally left out},
{ContentSettingsType::MIXEDSCRIPT,
IDS_BLOCKED_DISPLAYING_INSECURE_CONTENT},
{ContentSettingsType::GEOLOCATION, IDS_BLOCKED_GEOLOCATION_MESSAGE},
{ContentSettingsType::MIDI_SYSEX, IDS_BLOCKED_MIDI_SYSEX_MESSAGE},
{ContentSettingsType::CLIPBOARD_READ_WRITE,
IDS_BLOCKED_CLIPBOARD_MESSAGE},
{ContentSettingsType::SENSORS,
base::FeatureList::IsEnabled(features::kGenericSensorExtraClasses)
? IDS_BLOCKED_SENSORS_MESSAGE
: IDS_BLOCKED_MOTION_SENSORS_MESSAGE},
#if BUILDFLAG(IS_WIN)
{ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER,
IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_MESSAGE},
#endif
};
// Fields as for kBlockedMessageIDs, above.
const ContentSettingsTypeIdEntry kAccessedMessageIDs[] = {
{ContentSettingsType::COOKIES, IDS_ACCESSED_ON_DEVICE_SITE_DATA_MESSAGE},
{ContentSettingsType::GEOLOCATION, IDS_ALLOWED_GEOLOCATION_MESSAGE},
{ContentSettingsType::MIDI_SYSEX, IDS_ALLOWED_MIDI_SYSEX_MESSAGE},
{ContentSettingsType::CLIPBOARD_READ_WRITE,
IDS_ALLOWED_CLIPBOARD_MESSAGE},
{ContentSettingsType::SENSORS,
base::FeatureList::IsEnabled(features::kGenericSensorExtraClasses)
? IDS_ALLOWED_SENSORS_MESSAGE
: IDS_ALLOWED_MOTION_SENSORS_MESSAGE},
#if BUILDFLAG(IS_WIN)
{ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER,
IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_MESSAGE},
#endif
};
int message_id = [&]() {
#if BUILDFLAG(IS_CHROMEOS)
if (content_type() == ContentSettingsType::SMART_CARD_GUARD) {
return IDS_ACCESSED_SMART_CARD_READER_BODY;
}
#endif
if (IsContentAllowed()) {
return GetIdForContentType(kAccessedMessageIDs, content_type());
}
return GetIdForContentType(kBlockedMessageIDs, content_type());
}();
if (message_id) {
set_message(l10n_util::GetStringUTF16(message_id));
}
}
void ContentSettingSimpleBubbleModel::SetManageText() {
set_manage_text(l10n_util::GetStringUTF16(IDS_MANAGE));
}
void ContentSettingSimpleBubbleModel::OnManageButtonClicked() {
if (delegate()) {
delegate()->ShowContentSettingsPage(content_type());
}
if (content_type() == ContentSettingsType::POPUPS) {
content_settings::RecordPopupsAction(
content_settings::POPUPS_ACTION_CLICKED_MANAGE_POPUPS_BLOCKING);
}
}
void ContentSettingSimpleBubbleModel::SetCustomLink() {
static const ContentSettingsTypeIdEntry kCustomIDs[] = {
{ContentSettingsType::MIXEDSCRIPT, IDS_ALLOW_INSECURE_CONTENT_BUTTON},
};
int custom_link_id = GetIdForContentType(kCustomIDs, content_type());
if (custom_link_id) {
set_custom_link(l10n_util::GetStringUTF16(custom_link_id));
}
}
void ContentSettingSimpleBubbleModel::OnCustomLinkClicked() {}
// ContentSettingMixedScriptBubbleModel ----------------------------------------
class ContentSettingMixedScriptBubbleModel
: public ContentSettingSimpleBubbleModel {
public:
ContentSettingMixedScriptBubbleModel(Delegate* delegate,
WebContents* web_contents);
ContentSettingMixedScriptBubbleModel(
const ContentSettingMixedScriptBubbleModel&) = delete;
ContentSettingMixedScriptBubbleModel& operator=(
const ContentSettingMixedScriptBubbleModel&) = delete;
~ContentSettingMixedScriptBubbleModel() override = default;
private:
void SetManageText();
// ContentSettingBubbleModel:
void OnLearnMoreClicked() override;
void OnCustomLinkClicked() override;
};
ContentSettingMixedScriptBubbleModel::ContentSettingMixedScriptBubbleModel(
Delegate* delegate,
WebContents* web_contents)
: ContentSettingSimpleBubbleModel(delegate,
web_contents,
ContentSettingsType::MIXEDSCRIPT) {
set_custom_link_enabled(true);
set_show_learn_more(true);
SetManageText();
}
void ContentSettingMixedScriptBubbleModel::OnLearnMoreClicked() {
if (delegate()) {
delegate()->ShowLearnMorePage(content_type());
}
}
void ContentSettingMixedScriptBubbleModel::OnCustomLinkClicked() {
MixedContentSettingsTabHelper* mixed_content_settings =
MixedContentSettingsTabHelper::FromWebContents(web_contents());
if (mixed_content_settings) {
// Update browser side settings to allow active mixed content.
mixed_content_settings->AllowRunningOfInsecureContent(
GetPage().GetMainDocument());
}
// Update renderer side settings to allow active mixed content.
GetPage().GetMainDocument().ForEachRenderFrameHostWithAction(
[mixed_content_settings](content::RenderFrameHost* frame) {
// Stop the child frame enumeration if we have reached a fenced frame.
// This is correct since fence frames should ignore InsecureContent
// setting.
if (frame->IsFencedFrameRoot()) {
return content::RenderFrameHost::FrameIterationAction::kSkipChildren;
}
SetAllowRunningInsecureContent(mixed_content_settings, frame);
return content::RenderFrameHost::FrameIterationAction::kContinue;
});
}
// Don't set any manage text since none is displayed.
void ContentSettingMixedScriptBubbleModel::SetManageText() {
set_manage_text_style(ContentSettingBubbleModel::ManageTextStyle::kNone);
}
// ContentSettingRPHBubbleModel ------------------------------------------------
namespace {
// These states must match the order of appearance of the radio buttons
// in the XIB file for the Mac port.
enum RPHState {
RPH_ALLOW = 0,
RPH_BLOCK,
RPH_IGNORE,
};
} // namespace
ContentSettingRPHBubbleModel::ContentSettingRPHBubbleModel(
Delegate* delegate,
WebContents* web_contents,
custom_handlers::ProtocolHandlerRegistry* registry)
: ContentSettingSimpleBubbleModel(delegate,
web_contents,
ContentSettingsType::PROTOCOL_HANDLERS),
registry_(registry),
pending_handler_(
custom_handlers::ProtocolHandler::EmptyProtocolHandler()),
previous_handler_(
custom_handlers::ProtocolHandler::EmptyProtocolHandler()) {
auto* content_settings =
PageSpecificContentSettingsDelegate::FromWebContents(web_contents);
pending_handler_ = content_settings->pending_protocol_handler();
previous_handler_ = content_settings->previous_protocol_handler();
std::u16string protocol = pending_handler_.GetProtocolDisplayName();
// Note that we ignore the |title| parameter.
if (previous_handler_.IsEmpty()) {
set_title(l10n_util::GetStringFUTF16(
IDS_REGISTER_PROTOCOL_HANDLER_CONFIRM,
base::UTF8ToUTF16(pending_handler_.url().GetHost()), protocol));
} else {
set_title(l10n_util::GetStringFUTF16(
IDS_REGISTER_PROTOCOL_HANDLER_CONFIRM_REPLACE,
base::UTF8ToUTF16(pending_handler_.url().GetHost()), protocol,
base::UTF8ToUTF16(previous_handler_.url().GetHost())));
}
std::u16string radio_allow_label =
l10n_util::GetStringUTF16(IDS_REGISTER_PROTOCOL_HANDLER_ACCEPT);
std::u16string radio_deny_label =
l10n_util::GetStringUTF16(IDS_REGISTER_PROTOCOL_HANDLER_DENY);
std::u16string radio_ignore_label =
l10n_util::GetStringUTF16(IDS_REGISTER_PROTOCOL_HANDLER_IGNORE);
const GURL& url = web_contents->GetLastCommittedURL();
RadioGroup radio_group;
radio_group.url = url;
radio_group.radio_items = {radio_allow_label, radio_deny_label,
radio_ignore_label};
ContentSetting setting = content_settings->pending_protocol_handler_setting();
if (setting == CONTENT_SETTING_ALLOW) {
radio_group.default_item = RPH_ALLOW;
} else if (setting == CONTENT_SETTING_BLOCK) {
radio_group.default_item = RPH_BLOCK;
} else {
radio_group.default_item = RPH_IGNORE;
}
set_radio_group(radio_group);
}
ContentSettingRPHBubbleModel::~ContentSettingRPHBubbleModel() = default;
void ContentSettingRPHBubbleModel::CommitChanges() {
PerformActionForSelectedItem();
// The user has one chance to deal with the RPH content setting UI,
// then we remove it.
PageSpecificContentSettingsDelegate::FromWebContents(web_contents())
->ClearPendingProtocolHandler();
content_settings::UpdateLocationBarUiForWebContents(web_contents());
}
void ContentSettingRPHBubbleModel::RegisterProtocolHandler() {
// A no-op if the handler hasn't been ignored, but needed in case the user
// selects sequences like register/ignore/register.
registry_->RemoveIgnoredHandler(pending_handler_);
registry_->OnAcceptRegisterProtocolHandler(pending_handler_);
PageSpecificContentSettingsDelegate::FromWebContents(web_contents())
->set_pending_protocol_handler_setting(CONTENT_SETTING_ALLOW);
}
void ContentSettingRPHBubbleModel::UnregisterProtocolHandler() {
registry_->OnDenyRegisterProtocolHandler(pending_handler_);
PageSpecificContentSettingsDelegate::FromWebContents(web_contents())
->set_pending_protocol_handler_setting(CONTENT_SETTING_BLOCK);
ClearOrSetPreviousHandler();
}
void ContentSettingRPHBubbleModel::IgnoreProtocolHandler() {
registry_->OnIgnoreRegisterProtocolHandler(pending_handler_);
PageSpecificContentSettingsDelegate::FromWebContents(web_contents())
->set_pending_protocol_handler_setting(CONTENT_SETTING_DEFAULT);
ClearOrSetPreviousHandler();
}
void ContentSettingRPHBubbleModel::ClearOrSetPreviousHandler() {
if (previous_handler_.IsEmpty()) {
registry_->ClearDefault(pending_handler_.protocol());
} else {
registry_->OnAcceptRegisterProtocolHandler(previous_handler_);
}
}
void ContentSettingRPHBubbleModel::PerformActionForSelectedItem() {
if (selected_item() == RPH_ALLOW) {
RegisterProtocolHandler();
} else if (selected_item() == RPH_BLOCK) {
UnregisterProtocolHandler();
} else if (selected_item() == RPH_IGNORE) {
IgnoreProtocolHandler();
} else {
NOTREACHED();
}
}
// ContentSettingSingleRadioGroup ----------------------------------------------
ContentSettingSingleRadioGroup::ContentSettingSingleRadioGroup(
Delegate* delegate,
WebContents* web_contents,
ContentSettingsType content_type)
: ContentSettingSimpleBubbleModel(delegate, web_contents, content_type),
block_setting_(CONTENT_SETTING_BLOCK) {
SetRadioGroup();
set_is_user_modifiable(GetSettingManagedByUser(
web_contents->GetURL(), content_type, GetProfile(), nullptr));
}
ContentSettingSingleRadioGroup::~ContentSettingSingleRadioGroup() = default;
void ContentSettingSingleRadioGroup::CommitChanges() {
if (settings_changed()) {
ContentSetting setting = selected_item() == kAllowButtonIndex
? CONTENT_SETTING_ALLOW
: block_setting_;
permissions::PermissionUmaUtil::ScopedRevocationReporter
scoped_revocation_reporter(
GetProfile(), bubble_content().radio_group.url, GURL(),
content_type(), permissions::PermissionSourceUI::PAGE_ACTION);
SetNarrowestContentSetting(setting);
}
}
bool ContentSettingSingleRadioGroup::settings_changed() const {
return selected_item() != bubble_content().radio_group.default_item;
}
// Initialize the radio group by setting the appropriate labels for the
// content type and setting the default value based on the content setting.
void ContentSettingSingleRadioGroup::SetRadioGroup() {
const GURL& url = web_contents()->GetURL();
const std::u16string& display_url = GetUrlForDisplay(GetProfile(), url);
bool allowed = IsContentAllowed();
// For the frame busting case the content is blocked but its content type is
// popup, and the popup PageSpecificContentSettings is unaware of the frame
// busting block. Since the popup bubble won't happen without blocking, it's
// safe to manually set this.
if (content_type() == ContentSettingsType::POPUPS) {
allowed = false;
}
DCHECK(!allowed ||
PageSpecificContentSettings::GetForFrame(&GetPage().GetMainDocument())
->IsContentAllowed(content_type()));
RadioGroup radio_group;
radio_group.url = url;
static const ContentSettingsTypeIdEntry kBlockedAllowIDs[] = {
{ContentSettingsType::COOKIES, IDS_BLOCKED_ON_DEVICE_SITE_DATA_UNBLOCK},
{ContentSettingsType::IMAGES, IDS_BLOCKED_IMAGES_UNBLOCK},
{ContentSettingsType::JAVASCRIPT, IDS_BLOCKED_JAVASCRIPT_UNBLOCK},
{ContentSettingsType::POPUPS, IDS_BLOCKED_POPUPS_REDIRECTS_UNBLOCK},
{ContentSettingsType::SOUND, IDS_BLOCKED_SOUND_UNBLOCK},
{ContentSettingsType::GEOLOCATION, IDS_BLOCKED_GEOLOCATION_UNBLOCK},
{ContentSettingsType::MIDI_SYSEX, IDS_BLOCKED_MIDI_SYSEX_UNBLOCK},
{ContentSettingsType::CLIPBOARD_READ_WRITE,
IDS_BLOCKED_CLIPBOARD_UNBLOCK},
{ContentSettingsType::SENSORS, IDS_BLOCKED_SENSORS_UNBLOCK},
#if BUILDFLAG(IS_WIN)
{ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER,
IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_UNBLOCK},
#endif
};
// Fields as for kBlockedAllowIDs, above.
static const ContentSettingsTypeIdEntry kAllowedAllowIDs[] = {
{ContentSettingsType::COOKIES, IDS_ALLOWED_ON_DEVICE_SITE_DATA_NO_ACTION},
{ContentSettingsType::GEOLOCATION, IDS_ALLOWED_GEOLOCATION_NO_ACTION},
{ContentSettingsType::MIDI_SYSEX, IDS_ALLOWED_MIDI_SYSEX_NO_ACTION},
{ContentSettingsType::CLIPBOARD_READ_WRITE,
IDS_ALLOWED_CLIPBOARD_NO_ACTION},
{ContentSettingsType::SENSORS, IDS_ALLOWED_SENSORS_NO_ACTION},
#if BUILDFLAG(IS_WIN)
{ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER,
IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_NO_ACTION},
#endif
};
std::u16string radio_allow_label;
if (allowed) {
int resource_id = GetIdForContentType(kAllowedAllowIDs, content_type());
radio_allow_label = l10n_util::GetStringUTF16(resource_id);
} else {
int resource_id = GetIdForContentType(kBlockedAllowIDs, content_type());
if (content_type() == ContentSettingsType::COOKIES) {
radio_allow_label = l10n_util::GetStringUTF16(resource_id);
} else {
radio_allow_label = l10n_util::GetStringFUTF16(resource_id, display_url);
}
}
static const ContentSettingsTypeIdEntry kBlockedBlockIDs[] = {
{ContentSettingsType::COOKIES, IDS_BLOCKED_ON_DEVICE_SITE_DATA_NO_ACTION},
{ContentSettingsType::IMAGES, IDS_BLOCKED_IMAGES_NO_ACTION},
{ContentSettingsType::JAVASCRIPT, IDS_BLOCKED_JAVASCRIPT_NO_ACTION},
{ContentSettingsType::POPUPS, IDS_BLOCKED_POPUPS_REDIRECTS_NO_ACTION},
{ContentSettingsType::SOUND, IDS_BLOCKED_SOUND_NO_ACTION},
{ContentSettingsType::GEOLOCATION, IDS_BLOCKED_GEOLOCATION_NO_ACTION},
{ContentSettingsType::MIDI_SYSEX, IDS_BLOCKED_MIDI_SYSEX_NO_ACTION},
{ContentSettingsType::CLIPBOARD_READ_WRITE,
IDS_BLOCKED_CLIPBOARD_NO_ACTION},
{ContentSettingsType::SENSORS, IDS_BLOCKED_SENSORS_NO_ACTION},
#if BUILDFLAG(IS_WIN)
{ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER,
IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_NO_ACTION},
#endif
};
static const ContentSettingsTypeIdEntry kAllowedBlockIDs[] = {
{ContentSettingsType::COOKIES, IDS_ALLOWED_ON_DEVICE_SITE_DATA_BLOCK},
{ContentSettingsType::GEOLOCATION, IDS_ALLOWED_GEOLOCATION_BLOCK},
{ContentSettingsType::MIDI_SYSEX, IDS_ALLOWED_MIDI_SYSEX_BLOCK},
{ContentSettingsType::CLIPBOARD_READ_WRITE, IDS_ALLOWED_CLIPBOARD_BLOCK},
{ContentSettingsType::SENSORS, IDS_ALLOWED_SENSORS_BLOCK},
#if BUILDFLAG(IS_WIN)
{ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER,
IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_BLOCK},
#endif
};
std::u16string radio_block_label;
if (allowed) {
int resource_id = GetIdForContentType(kAllowedBlockIDs, content_type());
if (content_type() == ContentSettingsType::COOKIES) {
radio_block_label = l10n_util::GetStringUTF16(resource_id);
} else {
radio_block_label = l10n_util::GetStringFUTF16(resource_id, display_url);
}
} else {
radio_block_label = l10n_util::GetStringUTF16(
GetIdForContentType(kBlockedBlockIDs, content_type()));
}
radio_group.radio_items = {radio_allow_label, radio_block_label};
ContentSetting setting;
GetSettingManagedByUser(url, content_type(), GetProfile(), &setting);
if (setting == CONTENT_SETTING_ALLOW) {
radio_group.default_item = kAllowButtonIndex;
// |block_setting_| is already set to |CONTENT_SETTING_BLOCK|.
} else {
radio_group.default_item = 1;
block_setting_ = setting;
}
set_radio_group(radio_group);
}
void ContentSettingSingleRadioGroup::SetNarrowestContentSetting(
ContentSetting setting) {
if (!GetProfile()) {
return;
}
auto* map = HostContentSettingsMapFactory::GetForProfile(GetProfile());
map->SetNarrowestContentSetting(
bubble_content().radio_group.url, bubble_content().radio_group.url,
content_type(), setting,
CreateConstraintsForAutoRevocation(content_type(), setting));
}
// ContentSettingStorageAccessBubbleModel --------------------------------------
ContentSettingStorageAccessBubbleModel::ContentSettingStorageAccessBubbleModel(
Delegate* delegate,
WebContents* web_contents)
: ContentSettingBubbleModel(delegate, web_contents) {
RecordActionHistogram(ContentSettingsType::STORAGE_ACCESS,
ContentSettingBubbleAction::kOpened);
set_title(l10n_util::GetStringUTF16(IDS_SITE_SETTINGS_TYPE_STORAGE_ACCESS));
// TODO(crbug.com/40064079): Consider to add subtitles to all permissions.
set_subtitle(url_formatter::FormatUrlForSecurityDisplay(
web_contents->GetURL(),
url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC));
set_message(l10n_util::GetStringFUTF16(
IDS_STORAGE_ACCESS_PERMISSION_BUBBLE_MESSAGE,
url_formatter::FormatUrlForSecurityDisplay(
web_contents->GetURL(),
url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC)));
auto* page_content_settings =
PageSpecificContentSettings::GetForFrame(&GetPage().GetMainDocument());
set_site_list(page_content_settings->GetTwoSiteRequests(
ContentSettingsType::STORAGE_ACCESS));
set_manage_text_style(ManageTextStyle::kHoverButton);
set_manage_text(l10n_util::GetStringUTF16(IDS_STORAGE_ACCESS_MANAGE_TEXT));
set_manage_tooltip(
l10n_util::GetStringUTF16(IDS_STORAGE_ACCESS_MANAGE_TOOLTIP));
}
ContentSettingStorageAccessBubbleModel::
~ContentSettingStorageAccessBubbleModel() = default;
void ContentSettingStorageAccessBubbleModel::CommitChanges() {
if (!GetProfile()) {
return;
}
for (const auto& entry : changed_permissions_) {
GURL primary = entry.first.GetURL();
const GURL& secondary = web_contents()->GetURL();
ContentSetting setting =
entry.second ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK;
permissions::PermissionUmaUtil::ScopedRevocationReporter
scoped_revocation_reporter(
GetProfile(), primary, secondary,
ContentSettingsType::STORAGE_ACCESS,
permissions::PermissionSourceUI::PAGE_ACTION);
auto* map = HostContentSettingsMapFactory::GetForProfile(GetProfile());
content_settings::ContentSettingConstraints constraints;
constraints.set_lifetime(
permissions::kStorageAccessAPIExplicitPermissionLifetime);
map->SetNarrowestContentSetting(primary, secondary,
ContentSettingsType::STORAGE_ACCESS,
setting, constraints);
}
}
void ContentSettingStorageAccessBubbleModel::OnSiteRowClicked(
const net::SchemefulSite& site,
bool is_allowed) {
RecordActionHistogram(ContentSettingsType::STORAGE_ACCESS,
is_allowed
? ContentSettingBubbleAction::kPermissionAllowed
: ContentSettingBubbleAction::kPermissionBlocked);
changed_permissions_[site] = is_allowed;
}
void ContentSettingStorageAccessBubbleModel::OnManageButtonClicked() {
if (!delegate()) {
return;
}
RecordActionHistogram(ContentSettingsType::STORAGE_ACCESS,
ContentSettingBubbleAction::kManageButtonClicked);
delegate()->ShowContentSettingsPage(ContentSettingsType::STORAGE_ACCESS);
}
// ContentSettingCookiesBubbleModel --------------------------------------------
class ContentSettingCookiesBubbleModel : public ContentSettingSingleRadioGroup {
public:
ContentSettingCookiesBubbleModel(Delegate* delegate,
WebContents* web_contents);
ContentSettingCookiesBubbleModel(const ContentSettingCookiesBubbleModel&) =
delete;
ContentSettingCookiesBubbleModel& operator=(
const ContentSettingCookiesBubbleModel&) = delete;
~ContentSettingCookiesBubbleModel() override;
// ContentSettingBubbleModel:
void CommitChanges() override;
void OnManageButtonClicked() override;
};
ContentSettingCookiesBubbleModel::ContentSettingCookiesBubbleModel(
Delegate* delegate,
WebContents* web_contents)
: ContentSettingSingleRadioGroup(delegate,
web_contents,
ContentSettingsType::COOKIES) {}
ContentSettingCookiesBubbleModel::~ContentSettingCookiesBubbleModel() = default;
void ContentSettingCookiesBubbleModel::CommitChanges() {
// On some plattforms e.g. MacOS X it is possible to close a tab while the
// cookies settings bubble is open. This resets the web contents to NULL.
if (settings_changed()) {
CollectedCookiesInfoBarDelegate::Create(
infobars::ContentInfoBarManager::FromWebContents(web_contents()));
}
ContentSettingSingleRadioGroup::CommitChanges();
}
void ContentSettingCookiesBubbleModel::OnManageButtonClicked() {
delegate()->ShowCollectedCookiesDialog(web_contents());
}
// ContentSettingPopupBubbleModel ----------------------------------------------
class ContentSettingPopupBubbleModel
: public ContentSettingSingleRadioGroup,
public blocked_content::UrlListManager::Observer {
public:
ContentSettingPopupBubbleModel(Delegate* delegate, WebContents* web_contents);
ContentSettingPopupBubbleModel(const ContentSettingPopupBubbleModel&) =
delete;
ContentSettingPopupBubbleModel& operator=(
const ContentSettingPopupBubbleModel&) = delete;
~ContentSettingPopupBubbleModel() override;
// ContentSettingBubbleModel:
void CommitChanges() override;
// PopupBlockerTabHelper::Observer:
void BlockedUrlAdded(int32_t id, const GURL& url) override;
private:
void OnListItemClicked(int index, const ui::Event& event) override;
int32_t item_id_from_item_index(int index) const {
return bubble_content().list_items[index].item_id;
}
base::ScopedObservation<blocked_content::UrlListManager,
blocked_content::UrlListManager::Observer>
url_list_observation_{this};
};
ContentSettingPopupBubbleModel::ContentSettingPopupBubbleModel(
Delegate* delegate,
WebContents* web_contents)
: ContentSettingSingleRadioGroup(delegate,
web_contents,
ContentSettingsType::POPUPS) {
set_title(l10n_util::GetStringUTF16(IDS_BLOCKED_POPUPS_TITLE));
// Build blocked popup list.
auto* helper =
blocked_content::PopupBlockerTabHelper::FromWebContents(web_contents);
std::map<int32_t, GURL> blocked_popups = helper->GetBlockedPopupRequests();
for (const auto& blocked_popup : blocked_popups) {
AddListItem(CreateUrlListItem(blocked_popup.first, blocked_popup.second));
}
url_list_observation_.Observe(helper->manager());
content_settings::RecordPopupsAction(
content_settings::POPUPS_ACTION_DISPLAYED_BUBBLE);
}
void ContentSettingPopupBubbleModel::BlockedUrlAdded(int32_t id,
const GURL& url) {
AddListItem(CreateUrlListItem(id, url));
}
void ContentSettingPopupBubbleModel::OnListItemClicked(int index,
const ui::Event& event) {
auto* helper =
blocked_content::PopupBlockerTabHelper::FromWebContents(web_contents());
helper->ShowBlockedPopup(item_id_from_item_index(index),
ui::DispositionFromEventFlags(event.flags()));
RemoveListItem(index);
content_settings::RecordPopupsAction(
content_settings::POPUPS_ACTION_CLICKED_LIST_ITEM_CLICKED);
}
void ContentSettingPopupBubbleModel::CommitChanges() {
// User selected to always allow pop-ups from.
if (settings_changed() && selected_item() == kAllowButtonIndex) {
// Increases the counter.
content_settings::RecordPopupsAction(
content_settings::POPUPS_ACTION_SELECTED_ALWAYS_ALLOW_POPUPS_FROM);
}
ContentSettingSingleRadioGroup::CommitChanges();
}
ContentSettingPopupBubbleModel::~ContentSettingPopupBubbleModel() = default;
// ContentSettingMediaStreamBubbleModel ----------------------------------------
ContentSettingMediaStreamBubbleModel::ContentSettingMediaStreamBubbleModel(
Delegate* delegate,
WebContents* web_contents)
: ContentSettingBubbleModel(delegate, web_contents) {
// TODO(msramek): The media bubble has three states - mic only, camera only,
// and both. There is a lot of duplicated code which does the same thing
// for camera and microphone separately. Consider refactoring it to avoid
// duplication.
// Initialize the content settings associated with the individual radio
// buttons.
radio_item_setting_[0] = CONTENT_SETTING_ASK;
radio_item_setting_[1] = CONTENT_SETTING_BLOCK;
PageSpecificContentSettings* content_settings =
PageSpecificContentSettings::GetForFrame(&GetPage().GetMainDocument());
state_ = content_settings->GetMicrophoneCameraState();
CHECK(CameraAccessed() || MicrophoneAccessed());
if (CameraAccessed()) {
content_settings->OnActivityIndicatorBubbleOpened(
ContentSettingsType::MEDIASTREAM_CAMERA);
}
if (MicrophoneAccessed()) {
content_settings->OnActivityIndicatorBubbleOpened(
ContentSettingsType::MEDIASTREAM_MIC);
}
// If the permission is turned off in MacOS system preferences, overwrite
// the bubble to enable the user to trigger the system dialog.
if (ShouldShowSystemMediaPermissions()) {
#if BUILDFLAG(IS_MAC)
InitializeSystemMediaPermissionBubble();
return;
#endif // BUILDFLAG(IS_MAC)
}
SetTitle();
SetMessage();
SetRadioGroup();
SetManageText();
SetCustomLink();
SetIsUserModifiable();
}
ContentSettingMediaStreamBubbleModel::~ContentSettingMediaStreamBubbleModel() =
default;
void ContentSettingMediaStreamBubbleModel::CommitChanges() {
PageSpecificContentSettings* content_settings =
PageSpecificContentSettings::GetForFrame(&GetPage().GetMainDocument());
if (CameraAccessed()) {
content_settings->OnActivityIndicatorBubbleClosed(
ContentSettingsType::MEDIASTREAM_CAMERA);
}
if (MicrophoneAccessed()) {
content_settings->OnActivityIndicatorBubbleClosed(
ContentSettingsType::MEDIASTREAM_MIC);
}
if (content_settings->media_stream_access_origin().is_empty()) {
return;
}
// No need for radio group in the bubble UI shown when permission is blocked
// on a system level.
if (!ShouldShowSystemMediaPermissions()) {
// Update the media settings if the radio button selection was changed.
if (selected_item() != bubble_content().radio_group.default_item) {
UpdateSettings(radio_item_setting_[selected_item()]);
}
}
}
ContentSettingMediaStreamBubbleModel*
ContentSettingMediaStreamBubbleModel::AsMediaStreamBubbleModel() {
return this;
}
void ContentSettingMediaStreamBubbleModel::OnManageButtonClicked() {
CHECK(CameraAccessed() || MicrophoneAccessed());
if (!delegate()) {
return;
}
CommitChanges();
if (MicrophoneAccessed() && CameraAccessed()) {
delegate()->ShowMediaSettingsPage();
} else {
delegate()->ShowContentSettingsPage(
CameraAccessed() ? ContentSettingsType::MEDIASTREAM_CAMERA
: ContentSettingsType::MEDIASTREAM_MIC);
}
}
void ContentSettingMediaStreamBubbleModel::OnDoneButtonClicked() {
if (ShouldShowSystemMediaPermissions()) {
#if BUILDFLAG(IS_MAC)
DCHECK(CameraAccessed() || MicrophoneAccessed());
base::RecordAction(UserMetricsAction("Media.OpenPreferencesClicked"));
DCHECK(ShouldShowSystemMediaPermissions());
if (CameraAccessed()) {
base::mac::OpenSystemSettingsPane(
base::mac::SystemSettingsPane::kPrivacySecurity_Camera);
} else if (MicrophoneAccessed()) {
base::mac::OpenSystemSettingsPane(
base::mac::SystemSettingsPane::kPrivacySecurity_Microphone);
}
return;
#endif // BUILDFLAG(IS_MAC)
}
}
bool ContentSettingMediaStreamBubbleModel::MicrophoneAccessed() const {
return state_.Has(PageSpecificContentSettings::kMicrophoneAccessed);
}
bool ContentSettingMediaStreamBubbleModel::CameraAccessed() const {
return state_.Has(PageSpecificContentSettings::kCameraAccessed);
}
bool ContentSettingMediaStreamBubbleModel::MicrophoneBlocked() const {
return state_.Has(PageSpecificContentSettings::kMicrophoneBlocked);
}
bool ContentSettingMediaStreamBubbleModel::CameraBlocked() const {
return state_.Has(PageSpecificContentSettings::kCameraBlocked);
}
void ContentSettingMediaStreamBubbleModel::SetIsUserModifiable() {
CHECK(CameraAccessed() || MicrophoneAccessed());
PageSpecificContentSettings* page_content_settings =
PageSpecificContentSettings::GetForFrame(&GetPage().GetMainDocument());
bool is_camera_modifiable = GetSettingManagedByUser(
page_content_settings->media_stream_access_origin(),
ContentSettingsType::MEDIASTREAM_CAMERA, GetProfile(), nullptr);
bool is_mic_modifiable = GetSettingManagedByUser(
page_content_settings->media_stream_access_origin(),
ContentSettingsType::MEDIASTREAM_MIC, GetProfile(), nullptr);
set_is_user_modifiable((MicrophoneAccessed() && is_mic_modifiable) ||
(CameraAccessed() && is_camera_modifiable));
}
void ContentSettingMediaStreamBubbleModel::SetTitle() {
CHECK(CameraAccessed() || MicrophoneAccessed());
int title_id = 0;
if (MicrophoneBlocked() && CameraBlocked()) {
title_id = IDS_MICROPHONE_CAMERA_BLOCKED_TITLE;
} else if (MicrophoneBlocked()) {
title_id = IDS_MICROPHONE_BLOCKED_TITLE;
} else if (CameraBlocked()) {
title_id = IDS_CAMERA_BLOCKED_TITLE;
} else if (MicrophoneAccessed() && CameraAccessed()) {
title_id = IDS_MICROPHONE_CAMERA_ALLOWED_TITLE;
} else if (MicrophoneAccessed()) {
title_id = IDS_MICROPHONE_ACCESSED_TITLE;
} else if (CameraAccessed()) {
title_id = IDS_CAMERA_ACCESSED_TITLE;
} else {
NOTREACHED();
}
set_title(l10n_util::GetStringUTF16(title_id));
}
void ContentSettingMediaStreamBubbleModel::SetMessage() {
CHECK(CameraAccessed() || MicrophoneAccessed());
int message_id = 0;
if (MicrophoneBlocked() && CameraBlocked()) {
message_id = IDS_MICROPHONE_CAMERA_BLOCKED;
} else if (MicrophoneBlocked()) {
message_id = IDS_MICROPHONE_BLOCKED;
} else if (CameraBlocked()) {
message_id = IDS_CAMERA_BLOCKED;
} else if (MicrophoneAccessed() && CameraAccessed()) {
message_id = IDS_MICROPHONE_CAMERA_ALLOWED;
} else if (MicrophoneAccessed()) {
message_id = IDS_MICROPHONE_ACCESSED;
} else if (CameraAccessed()) {
message_id = IDS_CAMERA_ACCESSED;
} else {
NOTREACHED();
}
set_message(l10n_util::GetStringUTF16(message_id));
}
void ContentSettingMediaStreamBubbleModel::SetRadioGroup() {
PageSpecificContentSettings* content_settings =
PageSpecificContentSettings::GetForFrame(&GetPage().GetMainDocument());
GURL url = content_settings->media_stream_access_origin();
RadioGroup radio_group;
radio_group.url = url;
const std::u16string& display_url = GetUrlForDisplay(GetProfile(), url);
CHECK(CameraAccessed() || MicrophoneAccessed());
int radio_allow_label_id = 0;
int radio_block_label_id = 0;
if (state_.Has(PageSpecificContentSettings::kMicrophoneBlocked) ||
state_.Has(PageSpecificContentSettings::kCameraBlocked)) {
if (network::IsUrlPotentiallyTrustworthy(url)) {
radio_item_setting_[0] = CONTENT_SETTING_ALLOW;
radio_allow_label_id = IDS_BLOCKED_MEDIASTREAM_CAMERA_ALLOW;
if (MicrophoneAccessed()) {
radio_allow_label_id =
CameraAccessed() ? IDS_BLOCKED_MEDIASTREAM_MIC_AND_CAMERA_ALLOW
: IDS_BLOCKED_MEDIASTREAM_MIC_ALLOW;
}
} else {
radio_allow_label_id = IDS_BLOCKED_MEDIASTREAM_CAMERA_ASK;
if (MicrophoneAccessed()) {
radio_allow_label_id = CameraAccessed()
? IDS_BLOCKED_MEDIASTREAM_MIC_AND_CAMERA_ASK
: IDS_BLOCKED_MEDIASTREAM_MIC_ASK;
}
}
radio_block_label_id = IDS_BLOCKED_MEDIASTREAM_CAMERA_NO_ACTION;
if (MicrophoneAccessed()) {
radio_block_label_id =
CameraAccessed() ? IDS_BLOCKED_MEDIASTREAM_MIC_AND_CAMERA_NO_ACTION
: IDS_BLOCKED_MEDIASTREAM_MIC_NO_ACTION;
}
} else {
bool has_pan_tilt_zoom_permission_granted =
web_contents()
->GetBrowserContext()
->GetPermissionController()
->GetPermissionStatusForCurrentDocument(
content::PermissionDescriptorUtil::
CreatePermissionDescriptorForPermissionType(
blink::PermissionType::CAMERA_PAN_TILT_ZOOM),
&GetPage().GetMainDocument()) ==
blink::mojom::PermissionStatus::GRANTED;
if (MicrophoneAccessed() && CameraAccessed()) {
radio_allow_label_id =
has_pan_tilt_zoom_permission_granted
? IDS_ALLOWED_MEDIASTREAM_MIC_AND_CAMERA_PAN_TILT_ZOOM_NO_ACTION
: IDS_ALLOWED_MEDIASTREAM_MIC_AND_CAMERA_NO_ACTION;
radio_block_label_id = IDS_ALLOWED_MEDIASTREAM_MIC_AND_CAMERA_BLOCK;
} else if (MicrophoneAccessed()) {
radio_allow_label_id = IDS_ALLOWED_MEDIASTREAM_MIC_NO_ACTION;
radio_block_label_id = IDS_ALLOWED_MEDIASTREAM_MIC_BLOCK;
} else {
radio_allow_label_id = has_pan_tilt_zoom_permission_granted
? IDS_ALLOWED_CAMERA_PAN_TILT_ZOOM_NO_ACTION
: IDS_ALLOWED_MEDIASTREAM_CAMERA_NO_ACTION;
radio_block_label_id = IDS_ALLOWED_MEDIASTREAM_CAMERA_BLOCK;
}
}
std::u16string radio_allow_label =
l10n_util::GetStringFUTF16(radio_allow_label_id, display_url);
std::u16string radio_block_label =
l10n_util::GetStringUTF16(radio_block_label_id);
radio_group.default_item =
(MicrophoneAccessed() && content_settings->IsContentBlocked(
ContentSettingsType::MEDIASTREAM_MIC)) ||
(CameraAccessed() && content_settings->IsContentBlocked(
ContentSettingsType::MEDIASTREAM_CAMERA))
? 1
: 0;
radio_group.radio_items = {radio_allow_label, radio_block_label};
set_radio_group(radio_group);
}
void ContentSettingMediaStreamBubbleModel::UpdateSettings(
ContentSetting setting) {
PageSpecificContentSettings* page_content_settings =
PageSpecificContentSettings::GetForFrame(&GetPage().GetMainDocument());
// The same urls must be used as in other places (e.g. the infobar) in
// order to override the existing rule. Otherwise a new rule is created.
// TODO(markusheintz): Extract to a helper so that there is only a single
// place to touch.
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(GetProfile());
if (MicrophoneAccessed()) {
permissions::PermissionUmaUtil::ScopedRevocationReporter
scoped_revocation_reporter(
GetProfile(), page_content_settings->media_stream_access_origin(),
GURL(), ContentSettingsType::MEDIASTREAM_MIC,
permissions::PermissionSourceUI::PAGE_ACTION);
map->SetContentSettingDefaultScope(
page_content_settings->media_stream_access_origin(), GURL(),
ContentSettingsType::MEDIASTREAM_MIC, setting,
CreateConstraintsForAutoRevocation(ContentSettingsType::MEDIASTREAM_MIC,
setting));
}
if (CameraAccessed()) {
permissions::PermissionUmaUtil::ScopedRevocationReporter
scoped_revocation_reporter(
GetProfile(), page_content_settings->media_stream_access_origin(),
GURL(), ContentSettingsType::MEDIASTREAM_CAMERA,
permissions::PermissionSourceUI::PAGE_ACTION);
map->SetContentSettingDefaultScope(
page_content_settings->media_stream_access_origin(), GURL(),
ContentSettingsType::MEDIASTREAM_CAMERA, setting,
CreateConstraintsForAutoRevocation(
ContentSettingsType::MEDIASTREAM_CAMERA, setting));
}
}
#if BUILDFLAG(IS_MAC)
void ContentSettingMediaStreamBubbleModel::
InitializeSystemMediaPermissionBubble() {
DCHECK(CameraAccessed() || MicrophoneAccessed());
base::RecordAction(
base::UserMetricsAction("Media.ShowSystemMediaPermissionBubble"));
int title_id = 0;
if (MicrophoneAccessed() && CameraAccessed() &&
(system_permission_settings::CheckSystemVideoCapturePermission() ==
system_permission_settings::SystemPermission::kDenied ||
system_permission_settings::CheckSystemAudioCapturePermission() ==
system_permission_settings::SystemPermission::kDenied)) {
title_id = IDS_CAMERA_MIC_TURNED_OFF_IN_MACOS;
AddListItem(ContentSettingBubbleModel::ListItem(
&vector_icons::kVideocamIcon, l10n_util::GetStringUTF16(IDS_CAMERA),
l10n_util::GetStringUTF16(IDS_TURNED_OFF), false, true, 0));
AddListItem(ContentSettingBubbleModel::ListItem(
&vector_icons::kMicIcon, l10n_util::GetStringUTF16(IDS_MIC),
l10n_util::GetStringUTF16(IDS_TURNED_OFF), false, true, 1));
} else if (CameraAccessed() &&
system_permission_settings::CheckSystemVideoCapturePermission() ==
system_permission_settings::SystemPermission::kDenied) {
title_id = IDS_CAMERA_TURNED_OFF_IN_MACOS;
AddListItem(ContentSettingBubbleModel::ListItem(
&vector_icons::kVideocamIcon, l10n_util::GetStringUTF16(IDS_CAMERA),
l10n_util::GetStringUTF16(IDS_TURNED_OFF), false, true, 0));
} else if (MicrophoneAccessed() &&
system_permission_settings::CheckSystemAudioCapturePermission() ==
system_permission_settings::SystemPermission::kDenied) {
title_id = IDS_MIC_TURNED_OFF_IN_MACOS;
AddListItem(ContentSettingBubbleModel::ListItem(
&vector_icons::kMicIcon, l10n_util::GetStringUTF16(IDS_MIC),
l10n_util::GetStringUTF16(IDS_TURNED_OFF), false, true, 1));
}
set_title(l10n_util::GetStringUTF16(title_id));
set_manage_text_style(ContentSettingBubbleModel::ManageTextStyle::kNone);
SetCustomLink();
set_done_button_text(l10n_util::GetStringUTF16(IDS_OPEN_SETTINGS_LINK));
}
#endif // BUILDFLAG(IS_MAC)
bool ContentSettingMediaStreamBubbleModel::ShouldShowSystemMediaPermissions() {
#if BUILDFLAG(IS_MAC)
return (((system_permission_settings::CheckSystemVideoCapturePermission() ==
system_permission_settings::SystemPermission::kDenied &&
CameraAccessed() && !CameraBlocked()) ||
(system_permission_settings::CheckSystemAudioCapturePermission() ==
system_permission_settings::SystemPermission::kDenied &&
MicrophoneAccessed() && !MicrophoneBlocked())) &&
!(CameraAccessed() && CameraBlocked()) &&
!(MicrophoneAccessed() && MicrophoneBlocked()));
#else
return false;
#endif // BUILDFLAG(IS_MAC)
}
void ContentSettingMediaStreamBubbleModel::SetManageText() {
CHECK(CameraAccessed() || MicrophoneAccessed());
set_manage_text(l10n_util::GetStringUTF16(IDS_MANAGE));
}
void ContentSettingMediaStreamBubbleModel::SetCustomLink() {
PageSpecificContentSettings* content_settings =
PageSpecificContentSettings::GetForFrame(&GetPage().GetMainDocument());
if (content_settings->IsMicrophoneCameraStateChanged()) {
set_custom_link(
l10n_util::GetStringUTF16(IDS_MEDIASTREAM_SETTING_CHANGED_MESSAGE));
}
}
// ContentSettingGeolocationBubbleModel --------------------------------------
ContentSettingGeolocationBubbleModel::ContentSettingGeolocationBubbleModel(
Delegate* delegate,
content::WebContents* web_contents)
: ContentSettingSingleRadioGroup(delegate,
web_contents,
ContentSettingsType::GEOLOCATION) {
SetCustomLink();
// Get the stored geolocation content setting and the system permission
// state to determine whether geolocation is blocked by a system permission.
//
// The content setting must be read from HostContentSettingsMap.
// PageSpecificContentSettings cannot be used because it combines the
// site-level and system-level permissions, indicating the feature is
// blocked if either the site-level or system-level permission is not
// granted. We need to distinguish these cases to ensure the bubble that
// launches the system dialog is not shown if the site-level permission was
// not granted.
const GURL& url = web_contents->GetPrimaryMainFrame()->GetLastCommittedURL();
ContentSetting content_setting =
HostContentSettingsMapFactory::GetForProfile(GetProfile())
->GetContentSetting(url, url, ContentSettingsType::GEOLOCATION);
if (content_setting == CONTENT_SETTING_ALLOW &&
!system_permission_settings::IsAllowed(
ContentSettingsType::GEOLOCATION)) {
// If the permission is turned off in system preferences, overwrite the
// bubble to enable the user to trigger the system dialog.
InitializeSystemGeolocationPermissionBubble();
}
}
ContentSettingGeolocationBubbleModel::~ContentSettingGeolocationBubbleModel() =
default;
void ContentSettingGeolocationBubbleModel::OnDoneButtonClicked() {
if (show_system_geolocation_bubble_) {
base::RecordAction(UserMetricsAction(
"ContentSettings.GeolocationDialog.OpenPreferencesClicked"));
system_permission_settings::OpenSystemSettings(
web_contents(), ContentSettingsType::GEOLOCATION);
}
}
void ContentSettingGeolocationBubbleModel::OnManageButtonClicked() {
if (delegate()) {
delegate()->ShowContentSettingsPage(ContentSettingsType::GEOLOCATION);
}
}
void ContentSettingGeolocationBubbleModel::CommitChanges() {
if (show_system_geolocation_bubble_) {
return;
}
ContentSettingSingleRadioGroup::CommitChanges();
}
void ContentSettingGeolocationBubbleModel::
InitializeSystemGeolocationPermissionBubble() {
#if BUILDFLAG(OS_LEVEL_GEOLOCATION_PERMISSION_SUPPORTED)
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS)
set_title(l10n_util::GetStringUTF16(IDS_GEOLOCATION_TURNED_OFF_IN_OS));
#else
// The system-level location permission is not supported on Linux.
NOTREACHED();
#endif
clear_message();
AddListItem(ContentSettingBubbleModel::ListItem(
&vector_icons::kLocationOnIcon,
l10n_util::GetStringUTF16(IDS_GEOLOCATION),
l10n_util::GetStringUTF16(IDS_TURNED_OFF), false, true, 0));
set_manage_text_style(ContentSettingBubbleModel::ManageTextStyle::kNone);
set_done_button_text(l10n_util::GetStringUTF16(IDS_OPEN_SETTINGS_LINK));
set_radio_group(RadioGroup());
show_system_geolocation_bubble_ = true;
#endif // BUILDFLAG(OS_LEVEL_GEOLOCATION_PERMISSION_SUPPORTED)
}
void ContentSettingGeolocationBubbleModel::SetCustomLink() {
auto* map = HostContentSettingsMapFactory::GetForProfile(
web_contents()->GetBrowserContext());
SettingInfo info;
const GURL url =
GetPage().GetMainDocument().GetLastCommittedOrigin().GetURL();
map->GetWebsiteSetting(url, url, ContentSettingsType::GEOLOCATION, &info);
if (info.metadata.session_model() == SessionModel::ONE_TIME) {
set_custom_link(l10n_util::GetStringUTF16(IDS_GEOLOCATION_WILL_ASK_AGAIN));
}
}
// ContentSettingNotificationsBubbleModel ------------------------------------
#if BUILDFLAG(IS_MAC)
ContentSettingNotificationsBubbleModel::ContentSettingNotificationsBubbleModel(
Delegate* delegate,
content::WebContents* web_contents)
: ContentSettingSimpleBubbleModel(delegate,
web_contents,
ContentSettingsType::NOTIFICATIONS) {
set_title(l10n_util::GetStringUTF16(IDS_NOTIFICATIONS_TURNED_OFF_IN_MACOS));
AddListItem(ContentSettingBubbleModel::ListItem(
&vector_icons::kNotificationsOffChromeRefreshIcon,
l10n_util::GetStringUTF16(IDS_NOTIFICATIONS),
l10n_util::GetStringUTF16(IDS_TURNED_OFF), /*has_link=*/false,
/*has_blocked_badge=*/false, 0));
set_manage_text_style(ContentSettingBubbleModel::ManageTextStyle::kNone);
set_done_button_text(l10n_util::GetStringUTF16(IDS_OPEN_SETTINGS_LINK));
}
ContentSettingNotificationsBubbleModel::
~ContentSettingNotificationsBubbleModel() = default;
void ContentSettingNotificationsBubbleModel::OnDoneButtonClicked() {
std::string bundle_identifier = base::apple::MainBundleIdentifier();
if (std::optional<webapps::AppId> app_id =
web_app::WebAppTabHelper::GetAppIdForNotificationAttribution(
web_contents());
app_id.has_value()) {
bundle_identifier = web_app::GetBundleIdentifierForShim(*app_id);
}
base::mac::OpenSystemSettingsPane(
base::mac::SystemSettingsPane::kNotifications, bundle_identifier);
}
#endif
// ContentSettingSubresourceFilterBubbleModel ----------------------------------
ContentSettingSubresourceFilterBubbleModel::
ContentSettingSubresourceFilterBubbleModel(Delegate* delegate,
WebContents* web_contents)
: ContentSettingBubbleModel(delegate, web_contents) {
SetTitle();
SetMessage();
SetManageText();
set_done_button_text(l10n_util::GetStringUTF16(IDS_OK));
set_show_learn_more(true);
subresource_filter::ContentSubresourceFilterThrottleManager::LogAction(
subresource_filter::SubresourceFilterAction::kDetailsShown);
}
ContentSettingSubresourceFilterBubbleModel::
~ContentSettingSubresourceFilterBubbleModel() = default;
void ContentSettingSubresourceFilterBubbleModel::SetTitle() {
set_title(l10n_util::GetStringUTF16(IDS_BLOCKED_ADS_PROMPT_TITLE));
}
void ContentSettingSubresourceFilterBubbleModel::SetManageText() {
set_manage_text(l10n_util::GetStringUTF16(IDS_ALWAYS_ALLOW_ADS));
set_manage_text_style(ContentSettingBubbleModel::ManageTextStyle::kCheckbox);
}
void ContentSettingSubresourceFilterBubbleModel::SetMessage() {
set_message(l10n_util::GetStringUTF16(IDS_BLOCKED_ADS_PROMPT_EXPLANATION));
}
void ContentSettingSubresourceFilterBubbleModel::OnManageCheckboxChecked(
bool is_checked) {
set_done_button_text(
l10n_util::GetStringUTF16(is_checked ? IDS_APP_MENU_RELOAD : IDS_OK));
is_checked_ = is_checked;
}
void ContentSettingSubresourceFilterBubbleModel::OnLearnMoreClicked() {
DCHECK(delegate());
subresource_filter::ContentSubresourceFilterThrottleManager::LogAction(
subresource_filter::SubresourceFilterAction::kClickedLearnMore);
delegate()->ShowLearnMorePage(ContentSettingsType::ADS);
}
void ContentSettingSubresourceFilterBubbleModel::CommitChanges() {
if (is_checked_) {
subresource_filter::ContentSubresourceFilterThrottleManager::FromPage(
web_contents()->GetPrimaryPage())
->OnReloadRequested();
}
}
ContentSettingSubresourceFilterBubbleModel*
ContentSettingSubresourceFilterBubbleModel::AsSubresourceFilterBubbleModel() {
return this;
}
// ContentSettingDownloadsBubbleModel ------------------------------------------
ContentSettingDownloadsBubbleModel::ContentSettingDownloadsBubbleModel(
Delegate* delegate,
WebContents* web_contents)
: ContentSettingBubbleModel(delegate, web_contents) {
SetTitle();
SetManageText();
SetRadioGroup();
DownloadRequestLimiter* download_request_limiter =
g_browser_process->download_request_limiter();
set_is_user_modifiable(GetSettingManagedByUser(
download_request_limiter->GetDownloadOrigin(web_contents),
ContentSettingsType::AUTOMATIC_DOWNLOADS, GetProfile(), nullptr));
}
ContentSettingDownloadsBubbleModel::~ContentSettingDownloadsBubbleModel() =
default;
void ContentSettingDownloadsBubbleModel::CommitChanges() {
if (selected_item() != bubble_content().radio_group.default_item) {
permissions::PermissionUmaUtil::ScopedRevocationReporter
scoped_revocation_reporter(
GetProfile(), bubble_content().radio_group.url, GURL(),
ContentSettingsType::AUTOMATIC_DOWNLOADS,
permissions::PermissionSourceUI::PAGE_ACTION);
ContentSetting setting = selected_item() == kAllowButtonIndex
? CONTENT_SETTING_ALLOW
: CONTENT_SETTING_BLOCK;
auto* map = HostContentSettingsMapFactory::GetForProfile(GetProfile());
map->SetNarrowestContentSetting(
bubble_content().radio_group.url, bubble_content().radio_group.url,
ContentSettingsType::AUTOMATIC_DOWNLOADS, setting,
CreateConstraintsForAutoRevocation(
ContentSettingsType::AUTOMATIC_DOWNLOADS, setting));
}
}
ContentSettingDownloadsBubbleModel*
ContentSettingDownloadsBubbleModel::AsDownloadsBubbleModel() {
return this;
}
// Initialize the radio group by setting the appropriate labels for the
// content type and setting the default value based on the content setting.
void ContentSettingDownloadsBubbleModel::SetRadioGroup() {
DownloadRequestLimiter* download_request_limiter =
g_browser_process->download_request_limiter();
const GURL& download_origin =
download_request_limiter->GetDownloadOrigin(web_contents());
const std::u16string& display_url =
GetUrlForDisplay(GetProfile(), download_origin);
DCHECK(download_request_limiter);
RadioGroup radio_group;
radio_group.url = download_origin;
switch (download_request_limiter->GetDownloadUiStatus(web_contents())) {
case DownloadRequestLimiter::DOWNLOAD_UI_ALLOWED:
radio_group.radio_items = {
l10n_util::GetStringUTF16(IDS_ALLOWED_DOWNLOAD_NO_ACTION),
l10n_util::GetStringFUTF16(IDS_ALLOWED_DOWNLOAD_BLOCK, display_url)};
radio_group.default_item = kAllowButtonIndex;
break;
case DownloadRequestLimiter::DOWNLOAD_UI_BLOCKED:
radio_group.radio_items = {
l10n_util::GetStringFUTF16(IDS_BLOCKED_DOWNLOAD_UNBLOCK, display_url),
l10n_util::GetStringUTF16(IDS_BLOCKED_DOWNLOAD_NO_ACTION)};
radio_group.default_item = 1;
break;
case DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT:
DUMP_WILL_BE_NOTREACHED();
return;
}
set_radio_group(radio_group);
}
void ContentSettingDownloadsBubbleModel::SetTitle() {
DownloadRequestLimiter* download_request_limiter =
g_browser_process->download_request_limiter();
DCHECK(download_request_limiter);
switch (download_request_limiter->GetDownloadUiStatus(web_contents())) {
case DownloadRequestLimiter::DOWNLOAD_UI_ALLOWED:
set_title(l10n_util::GetStringUTF16(IDS_ALLOWED_DOWNLOAD_TITLE));
return;
case DownloadRequestLimiter::DOWNLOAD_UI_BLOCKED:
set_title(l10n_util::GetStringUTF16(IDS_BLOCKED_DOWNLOAD_TITLE));
return;
case DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT:
// No title otherwise.
return;
}
}
void ContentSettingDownloadsBubbleModel::SetManageText() {
set_manage_text(l10n_util::GetStringUTF16(IDS_MANAGE));
}
void ContentSettingDownloadsBubbleModel::OnManageButtonClicked() {
if (delegate()) {
delegate()->ShowContentSettingsPage(
ContentSettingsType::AUTOMATIC_DOWNLOADS);
}
}
// ContentSettingFramebustBlockBubbleModel -------------------------------------
ContentSettingFramebustBlockBubbleModel::
ContentSettingFramebustBlockBubbleModel(Delegate* delegate,
WebContents* web_contents)
: ContentSettingSingleRadioGroup(delegate,
web_contents,
ContentSettingsType::POPUPS) {
set_title(l10n_util::GetStringUTF16(IDS_REDIRECT_BLOCKED_MESSAGE));
auto* helper = FramebustBlockTabHelper::FromWebContents(web_contents);
// Build the blocked urls list.
for (const auto& blocked_url : helper->blocked_urls()) {
AddListItem(CreateUrlListItem(0 /* id */, blocked_url));
}
url_list_observation_.Observe(helper->manager());
}
ContentSettingFramebustBlockBubbleModel::
~ContentSettingFramebustBlockBubbleModel() = default;
void ContentSettingFramebustBlockBubbleModel::OnListItemClicked(
int index,
const ui::Event& event) {
FramebustBlockTabHelper::FromWebContents(web_contents())
->OnBlockedUrlClicked(index);
}
ContentSettingFramebustBlockBubbleModel*
ContentSettingFramebustBlockBubbleModel::AsFramebustBlockBubbleModel() {
return this;
}
void ContentSettingFramebustBlockBubbleModel::BlockedUrlAdded(
int32_t id,
const GURL& blocked_url) {
AddListItem(CreateUrlListItem(0 /* id */, blocked_url));
}
// ContentSettingQuietRequestBubbleModel ----------------------------------
ContentSettingQuietRequestBubbleModel::ContentSettingQuietRequestBubbleModel(
Delegate* delegate,
WebContents* web_contents)
: ContentSettingBubbleModel(delegate, web_contents) {
// TODO(crbug.com/40110076): This block is more defensive than it needs to be
// because ContentSettingImageModelBrowserTest exercises it without setting up
// the correct PermissionRequestManager state. Fix that.
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(web_contents);
auto quiet_ui_reason = manager->ReasonForUsingQuietUi();
if (!quiet_ui_reason) {
return;
}
CHECK_GT(manager->Requests().size(), 0u);
DCHECK_EQ(manager->Requests().size(), 1u);
const permissions::RequestType request_type =
manager->Requests()[0]->request_type();
int bubble_title_string_id = 0;
switch (request_type) {
case permissions::RequestType::kNotifications:
bubble_title_string_id = IDS_NOTIFICATIONS_QUIET_PERMISSION_BUBBLE_TITLE;
break;
case permissions::RequestType::kGeolocation:
bubble_title_string_id = IDS_GEOLOCATION_QUIET_PERMISSION_BUBBLE_TITLE;
break;
default:
NOTREACHED();
}
set_title(l10n_util::GetStringUTF16(bubble_title_string_id));
switch (*quiet_ui_reason) {
case QuietUiReason::kEnabledInPrefs:
DCHECK(request_type == permissions::RequestType::kNotifications ||
request_type == permissions::RequestType::kGeolocation);
set_message(l10n_util::GetStringUTF16(
request_type == permissions::RequestType::kNotifications
? IDS_NOTIFICATIONS_QUIET_PERMISSION_BUBBLE_DESCRIPTION
: IDS_GEOLOCATION_QUIET_PERMISSION_BUBBLE_DESCRIPTION));
set_done_button_text(l10n_util::GetStringUTF16(
IDS_NOTIFICATIONS_QUIET_PERMISSION_BUBBLE_ALLOW_BUTTON));
set_show_learn_more(false);
base::RecordAction(
base::UserMetricsAction("Notifications.Quiet.AnimatedIconClicked"));
break;
case QuietUiReason::kTriggeredByCrowdDeny:
DCHECK_EQ(request_type, permissions::RequestType::kNotifications);
set_message(l10n_util::GetStringUTF16(
IDS_NOTIFICATIONS_QUIET_PERMISSION_BUBBLE_CROWD_DENY_DESCRIPTION));
set_done_button_text(l10n_util::GetStringUTF16(
IDS_NOTIFICATIONS_QUIET_PERMISSION_BUBBLE_ALLOW_BUTTON));
set_show_learn_more(false);
base::RecordAction(
base::UserMetricsAction("Notifications.Quiet.StaticIconClicked"));
break;
case QuietUiReason::kTriggeredDueToAbusiveRequests:
case QuietUiReason::kTriggeredDueToAbusiveContent:
DCHECK_EQ(request_type, permissions::RequestType::kNotifications);
set_message(l10n_util::GetStringUTF16(
IDS_NOTIFICATIONS_QUIET_PERMISSION_BUBBLE_ABUSIVE_DESCRIPTION));
// TODO(crbug.com/40131070): It is rather confusing to have the `Cancel`
// button allow the permission, but we want the primary to block.
set_cancel_button_text(l10n_util::GetStringUTF16(
IDS_NOTIFICATIONS_QUIET_PERMISSION_BUBBLE_COMPACT_ALLOW_BUTTON));
set_done_button_text(l10n_util::GetStringUTF16(
IDS_NOTIFICATIONS_QUIET_PERMISSION_BUBBLE_CONTINUE_BLOCKING_BUTTON));
set_show_learn_more(true);
set_manage_text_style(ManageTextStyle::kNone);
base::RecordAction(
base::UserMetricsAction("Notifications.Quiet.StaticIconClicked"));
break;
case QuietUiReason::kTriggeredDueToDisruptiveBehavior:
DCHECK_EQ(request_type, permissions::RequestType::kNotifications);
set_message(l10n_util::GetStringUTF16(
IDS_NOTIFICATIONS_QUIET_PERMISSION_BUBBLE_DISRUPTIVE_DESCRIPTION));
set_cancel_button_text(l10n_util::GetStringUTF16(
IDS_NOTIFICATIONS_QUIET_PERMISSION_BUBBLE_COMPACT_ALLOW_BUTTON));
set_done_button_text(l10n_util::GetStringUTF16(
IDS_NOTIFICATIONS_QUIET_PERMISSION_BUBBLE_CONTINUE_BLOCKING_BUTTON));
set_show_learn_more(true);
set_manage_text_style(ManageTextStyle::kNone);
base::RecordAction(
base::UserMetricsAction("Notifications.Quiet.StaticIconClicked"));
break;
case QuietUiReason::kServicePredictedVeryUnlikelyGrant:
case QuietUiReason::kOnDevicePredictedVeryUnlikelyGrant:
int bubble_message_string_id = 0;
int bubble_done_button_string_id = 0;
switch (request_type) {
case permissions::RequestType::kNotifications:
bubble_message_string_id =
IDS_NOTIFICATIONS_QUIET_PERMISSION_BUBBLE_PREDICTION_SERVICE_DESCRIPTION;
bubble_done_button_string_id =
IDS_NOTIFICATIONS_QUIET_PERMISSION_BUBBLE_ALLOW_BUTTON;
break;
case permissions::RequestType::kGeolocation:
bubble_message_string_id =
IDS_GEOLOCATION_QUIET_PERMISSION_BUBBLE_PREDICTION_SERVICE_DESCRIPTION;
bubble_done_button_string_id =
IDS_GEOLOCATION_QUIET_PERMISSION_BUBBLE_ALLOW_BUTTON;
break;
default:
NOTREACHED();
}
set_message(l10n_util::GetStringUTF16(bubble_message_string_id));
set_done_button_text(
l10n_util::GetStringUTF16(bubble_done_button_string_id));
set_show_learn_more(false);
base::RecordAction(
base::UserMetricsAction("Notifications.Quiet.AnimatedIconClicked"));
break;
}
}
ContentSettingQuietRequestBubbleModel::
~ContentSettingQuietRequestBubbleModel() = default;
ContentSettingQuietRequestBubbleModel*
ContentSettingQuietRequestBubbleModel::AsQuietRequestBubbleModel() {
return this;
}
void ContentSettingQuietRequestBubbleModel::OnManageButtonClicked() {
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(web_contents());
CHECK_GT(manager->Requests().size(), 0u);
DCHECK_EQ(manager->Requests().size(), 1u);
manager->set_manage_clicked();
if (is_UMA_for_test) {
// `delegate()->ShowContentSettingsPage` opens a new tab. It is not needed
// for UMA tests.
return;
}
const permissions::RequestType request_type =
manager->Requests()[0]->request_type();
if (delegate()) {
switch (request_type) {
case permissions::RequestType::kNotifications:
delegate()->ShowContentSettingsPage(ContentSettingsType::NOTIFICATIONS);
break;
case permissions::RequestType::kGeolocation:
delegate()->ShowContentSettingsPage(ContentSettingsType::GEOLOCATION);
break;
default:
NOTREACHED();
}
}
}
void ContentSettingQuietRequestBubbleModel::OnLearnMoreClicked() {
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(web_contents());
manager->set_learn_more_clicked();
if (is_UMA_for_test) {
// `delegate()->ShowLearnMorePage` opens a new tab. It is not needed for UMA
// tests.
return;
}
if (delegate()) {
// We only show learn more button for Notification quiet ui dialog when it
// is triggered due to abusive requests or contents. We don't have any learn
// more button for the geolocation quiet ui dialogs.
DCHECK_EQ(
permissions::PermissionRequestManager::FromWebContents(web_contents())
->Requests()[0]
->request_type(),
permissions::RequestType::kNotifications);
delegate()->ShowLearnMorePage(ContentSettingsType::NOTIFICATIONS);
}
}
void ContentSettingQuietRequestBubbleModel::OnDoneButtonClicked() {
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(web_contents());
CHECK_GT(manager->Requests().size(), 0u);
DCHECK_EQ(manager->Requests().size(), 1u);
auto quiet_ui_reason = manager->ReasonForUsingQuietUi();
const permissions::RequestType request_type =
manager->Requests()[0]->request_type();
DCHECK(quiet_ui_reason);
DCHECK(request_type == permissions::RequestType::kNotifications ||
request_type == permissions::RequestType::kGeolocation);
switch (*quiet_ui_reason) {
case QuietUiReason::kEnabledInPrefs:
case QuietUiReason::kTriggeredByCrowdDeny:
case QuietUiReason::kServicePredictedVeryUnlikelyGrant:
case QuietUiReason::kOnDevicePredictedVeryUnlikelyGrant:
manager->Accept();
break;
case QuietUiReason::kTriggeredDueToAbusiveRequests:
case QuietUiReason::kTriggeredDueToAbusiveContent:
case QuietUiReason::kTriggeredDueToDisruptiveBehavior:
manager->Deny();
base::RecordAction(base::UserMetricsAction(
"Notifications.Quiet.ContinueBlockingClicked"));
break;
}
}
void ContentSettingQuietRequestBubbleModel::OnCancelButtonClicked() {
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(web_contents());
auto quiet_ui_reason = manager->ReasonForUsingQuietUi();
if (!quiet_ui_reason) {
return;
}
switch (*quiet_ui_reason) {
case QuietUiReason::kEnabledInPrefs:
case QuietUiReason::kTriggeredByCrowdDeny:
case QuietUiReason::kServicePredictedVeryUnlikelyGrant:
case QuietUiReason::kOnDevicePredictedVeryUnlikelyGrant:
// No-op.
break;
case QuietUiReason::kTriggeredDueToAbusiveRequests:
case QuietUiReason::kTriggeredDueToAbusiveContent:
case QuietUiReason::kTriggeredDueToDisruptiveBehavior:
manager->Accept();
base::RecordAction(
base::UserMetricsAction("Notifications.Quiet.ShowForSiteClicked"));
break;
}
}
// ContentSettingBubbleModel ---------------------------------------------------
// This class must be placed last because it needs the definition of the other
// classes declared in this file.
const int ContentSettingBubbleModel::kAllowButtonIndex = 0;
// static
std::unique_ptr<ContentSettingBubbleModel>
ContentSettingBubbleModel::CreateContentSettingBubbleModel(
Delegate* delegate,
WebContents* web_contents,
ContentSettingsType content_type) {
DCHECK(web_contents);
switch (content_type) {
case ContentSettingsType::COOKIES:
return std::make_unique<ContentSettingCookiesBubbleModel>(delegate,
web_contents);
case ContentSettingsType::POPUPS:
return std::make_unique<ContentSettingPopupBubbleModel>(delegate,
web_contents);
case ContentSettingsType::MIXEDSCRIPT:
return std::make_unique<ContentSettingMixedScriptBubbleModel>(
delegate, web_contents);
case ContentSettingsType::PROTOCOL_HANDLERS: {
custom_handlers::ProtocolHandlerRegistry* registry =
ProtocolHandlerRegistryFactory::GetForBrowserContext(
web_contents->GetBrowserContext());
return std::make_unique<ContentSettingRPHBubbleModel>(
delegate, web_contents, registry);
}
case ContentSettingsType::AUTOMATIC_DOWNLOADS:
return std::make_unique<ContentSettingDownloadsBubbleModel>(delegate,
web_contents);
case ContentSettingsType::ADS:
return std::make_unique<ContentSettingSubresourceFilterBubbleModel>(
delegate, web_contents);
case ContentSettingsType::IMAGES:
case ContentSettingsType::JAVASCRIPT:
case ContentSettingsType::SOUND:
case ContentSettingsType::CLIPBOARD_READ_WRITE:
case ContentSettingsType::MIDI_SYSEX:
case ContentSettingsType::SENSORS:
#if BUILDFLAG(IS_WIN)
case ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER:
#endif
return std::make_unique<ContentSettingSingleRadioGroup>(
delegate, web_contents, content_type);
case ContentSettingsType::STORAGE_ACCESS:
return std::make_unique<ContentSettingStorageAccessBubbleModel>(
delegate, web_contents);
#if BUILDFLAG(IS_CHROMEOS)
case ContentSettingsType::SMART_CARD_GUARD:
return std::make_unique<ContentSettingSimpleBubbleModel>(
delegate, web_contents, content_type);
#endif
default:
NOTREACHED() << "No bubble for the content type "
<< static_cast<int32_t>(content_type) << ".";
}
}
ContentSettingBubbleModel::ContentSettingBubbleModel(Delegate* delegate,
WebContents* web_contents)
: web_contents_(web_contents), owner_(nullptr), delegate_(delegate) {
DCHECK(web_contents_);
}
ContentSettingBubbleModel::~ContentSettingBubbleModel() = default;
ContentSettingBubbleModel::RadioGroup::RadioGroup() = default;
ContentSettingBubbleModel::RadioGroup::~RadioGroup() = default;
ContentSettingBubbleModel::MediaMenu::MediaMenu() = default;
ContentSettingBubbleModel::MediaMenu::MediaMenu(const MediaMenu& other) =
default;
ContentSettingBubbleModel::MediaMenu::~MediaMenu() = default;
ContentSettingBubbleModel::BubbleContent::BubbleContent() = default;
ContentSettingBubbleModel::BubbleContent::~BubbleContent() = default;
ContentSettingSimpleBubbleModel*
ContentSettingBubbleModel::AsSimpleBubbleModel() {
// In general, bubble models might not inherit from the simple bubble model.
return nullptr;
}
ContentSettingMediaStreamBubbleModel*
ContentSettingBubbleModel::AsMediaStreamBubbleModel() {
// In general, bubble models might not inherit from the media bubble model.
return nullptr;
}
ContentSettingQuietRequestBubbleModel*
ContentSettingBubbleModel::AsQuietRequestBubbleModel() {
return nullptr;
}
ContentSettingSubresourceFilterBubbleModel*
ContentSettingBubbleModel::AsSubresourceFilterBubbleModel() {
return nullptr;
}
ContentSettingDownloadsBubbleModel*
ContentSettingBubbleModel::AsDownloadsBubbleModel() {
return nullptr;
}
ContentSettingFramebustBlockBubbleModel*
ContentSettingBubbleModel::AsFramebustBlockBubbleModel() {
return nullptr;
}
Profile* ContentSettingBubbleModel::GetProfile() const {
return Profile::FromBrowserContext(web_contents_->GetBrowserContext());
}
void ContentSettingBubbleModel::AddListItem(const ListItem& item) {
bubble_content_.list_items.push_back(item);
if (owner_) {
owner_->OnListItemAdded(item);
}
}
void ContentSettingBubbleModel::RemoveListItem(int index) {
if (owner_) {
owner_->OnListItemRemovedAt(index);
}
bubble_content_.list_items.erase(bubble_content_.list_items.begin() + index);
}