blob: 4430de11019e9b18bafa188221dbac0d63cfda03 [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.
#ifndef CHROME_BROWSER_UI_CONTENT_SETTINGS_CONTENT_SETTING_BUBBLE_MODEL_H_
#define CHROME_BROWSER_UI_CONTENT_SETTINGS_CONTENT_SETTING_BUBBLE_MODEL_H_
#include <stdint.h>
#include <array>
#include <map>
#include <string>
#include <vector>
#include "base/auto_reset.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "build/build_config.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/ui/blocked_content/framebust_block_tab_helper.h"
#include "components/blocked_content/url_list_manager.h"
#include "components/content_settings/browser/page_specific_content_settings.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/custom_handlers/protocol_handler.h"
#include "net/base/schemeful_site.h"
#include "services/device/public/cpp/geolocation/buildflags.h"
#include "third_party/blink/public/common/mediastream/media_stream_request.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/image/image.h"
#include "url/gurl.h"
#include "url/origin.h"
class ContentSettingBubbleModelDelegate;
class Profile;
namespace custom_handlers {
class ProtocolHandlerRegistry;
}
namespace content {
class Page;
class WebContents;
} // namespace content
namespace ui {
class Event;
}
// The hierarchy of bubble models:
//
// ContentSettingBubbleModel - base class
// ContentSettingSimpleBubbleModel - single content setting
// ContentSettingMixedScriptBubbleModel - mixed script
// ContentSettingRPHBubbleModel - protocol handlers
// ContentSettingPluginBubbleModel - plugins
// ContentSettingSingleRadioGroup - radio group
// ContentSettingCookiesBubbleModel - cookies
// ContentSettingPopupBubbleModel - popups
// ContentSettingFramebustBlockBubbleModel - blocked frame busting
// ContentSettingMediaStreamBubbleModel - media (camera and mic)
// ContentSettingSubresourceFilterBubbleModel - filtered subresources
// ContentSettingDownloadsBubbleModel - automatic downloads
// ContentSettingQuietRequestBubbleModel - quiet ui prompts
// ContentSettingStorageAccessBubbleModel - saa prompts
// Forward declaration necessary for downcasts.
class ContentSettingSimpleBubbleModel;
class ContentSettingMediaStreamBubbleModel;
class ContentSettingSubresourceFilterBubbleModel;
class ContentSettingDownloadsBubbleModel;
class ContentSettingFramebustBlockBubbleModel;
class ContentSettingQuietRequestBubbleModel;
// This model provides data for ContentSettingBubble, and also controls
// the action triggered when the allow / block radio buttons are triggered.
class ContentSettingBubbleModel {
public:
typedef ContentSettingBubbleModelDelegate Delegate;
struct 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);
ListItem(const ListItem& other);
ListItem& operator=(const ListItem& other);
raw_ptr<const gfx::VectorIcon> image;
std::u16string title;
std::u16string description;
bool has_link;
bool has_blocked_badge;
int32_t item_id;
};
typedef std::vector<ListItem> ListItems;
class Owner {
public:
virtual void OnListItemAdded(const ListItem& item) {}
virtual void OnListItemRemovedAt(int index) {}
virtual int GetSelectedRadioOption() = 0;
protected:
virtual ~Owner() = default;
};
typedef std::vector<std::u16string> RadioItems;
struct RadioGroup {
RadioGroup();
~RadioGroup();
GURL url;
RadioItems radio_items;
int default_item = 0;
};
typedef std::map<net::SchemefulSite, /*allowed*/ bool> SiteList;
struct MediaMenu {
MediaMenu();
MediaMenu(const MediaMenu& other);
~MediaMenu();
std::u16string label;
blink::MediaStreamDevice default_device;
blink::MediaStreamDevice selected_device;
bool disabled = false;
};
typedef std::map<blink::mojom::MediaStreamType, MediaMenu> MediaMenuMap;
enum class ManageTextStyle {
// No Manage button or checkbox is displayed.
kNone,
// Manage text is displayed as a non-prominent button.
kButton,
// Manage text is used as a checkbox title.
kCheckbox,
// Manage text is shown in a HoverButton. The "Manage" and "Done" buttons
// are hidden.
kHoverButton,
};
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class ContentSettingBubbleAction {
kOpened = 1,
kPermissionAllowed = 2,
kPermissionBlocked = 3,
kManageButtonClicked = 4,
kMaxValue = kManageButtonClicked
};
struct BubbleContent {
BubbleContent();
BubbleContent(const BubbleContent&) = delete;
BubbleContent& operator=(const BubbleContent&) = delete;
~BubbleContent();
std::u16string title;
std::u16string subtitle;
std::u16string message;
// Whether the user can modify the content of the bubble.
// False if controlled by policy, etc.
bool is_user_modifiable = true;
ListItems list_items;
RadioGroup radio_group;
SiteList site_list;
std::u16string custom_link;
bool custom_link_enabled = false;
std::u16string manage_text;
std::u16string manage_tooltip;
ManageTextStyle manage_text_style = ManageTextStyle::kButton;
bool show_learn_more = false;
std::u16string done_button_text;
std::u16string cancel_button_text;
};
static const int kAllowButtonIndex;
// Creates a bubble model for a particular |content_type|. Note that not all
// bubbles fit this description.
// TODO(msramek): Move this to ContentSettingSimpleBubbleModel or remove
// entirely.
static std::unique_ptr<ContentSettingBubbleModel>
CreateContentSettingBubbleModel(Delegate* delegate,
content::WebContents* web_contents,
ContentSettingsType content_type);
ContentSettingBubbleModel(const ContentSettingBubbleModel&) = delete;
ContentSettingBubbleModel& operator=(const ContentSettingBubbleModel&) =
delete;
virtual ~ContentSettingBubbleModel();
const BubbleContent& bubble_content() const { return bubble_content_; }
void set_owner(Owner* owner) { owner_ = owner; }
virtual void OnListItemClicked(int index, const ui::Event& event) {}
virtual void OnSiteRowClicked(const net::SchemefulSite& site,
bool is_allowed) {}
virtual void OnCustomLinkClicked() {}
virtual void OnManageButtonClicked() {}
virtual void OnManageCheckboxChecked(bool is_checked) {}
virtual void OnLearnMoreClicked() {}
virtual void OnDoneButtonClicked() {}
virtual void OnCancelButtonClicked() {}
// Called by the view code when the bubble is closed.
virtual void CommitChanges() {}
// TODO(msramek): The casting methods below are only necessary because
// ContentSettingBubbleController in the Cocoa UI needs to know the type of
// the bubble it wraps. Find a solution that does not require reflection nor
// recreating the entire hierarchy for Cocoa UI.
// Cast this bubble into ContentSettingSimpleBubbleModel if possible.
virtual ContentSettingSimpleBubbleModel* AsSimpleBubbleModel();
// Cast this bubble into ContentSettingMediaStreamBubbleModel if possible.
virtual ContentSettingMediaStreamBubbleModel* AsMediaStreamBubbleModel();
// Cast this bubble into ContentSettingSubresourceFilterBubbleModel
// if possible.
virtual ContentSettingSubresourceFilterBubbleModel*
AsSubresourceFilterBubbleModel();
// Cast this bubble into ContentSettingDownloadsBubbleModel if possible.
virtual ContentSettingDownloadsBubbleModel* AsDownloadsBubbleModel();
// Cast this bubble into ContentSettingFramebustBlockBubbleModel if possible.
virtual ContentSettingFramebustBlockBubbleModel*
AsFramebustBlockBubbleModel();
// Cast this bubble into ContentSettingQuietRequestBubbleModel if possible.
virtual ContentSettingQuietRequestBubbleModel* AsQuietRequestBubbleModel();
// Overrides the display URL used in the content bubble UI.
static base::AutoReset<std::optional<bool>>
CreateScopedDisplayURLOverrideForTesting();
bool is_UMA_for_test = false;
protected:
// |web_contents| must outlive this.
ContentSettingBubbleModel(Delegate* delegate,
content::WebContents* web_contents);
// Should always be non-nullptr.
content::WebContents* web_contents() const { return web_contents_; }
Profile* GetProfile() const;
Delegate* delegate() const { return delegate_; }
int selected_item() const { return owner_->GetSelectedRadioOption(); }
content::Page& GetPage() const { return web_contents_->GetPrimaryPage(); }
void set_title(const std::u16string& title) { bubble_content_.title = title; }
void set_subtitle(const std::u16string& subtitle) {
bubble_content_.subtitle = subtitle;
}
void set_message(const std::u16string& message) {
bubble_content_.message = message;
}
void clear_message() { bubble_content_.message.clear(); }
void AddListItem(const ListItem& item);
void RemoveListItem(int index);
void set_radio_group(const RadioGroup& radio_group) {
bubble_content_.radio_group = radio_group;
}
void set_site_list(const SiteList& site_list) {
bubble_content_.site_list = site_list;
}
void set_custom_link(const std::u16string& link) {
bubble_content_.custom_link = link;
}
void set_custom_link_enabled(bool enabled) {
bubble_content_.custom_link_enabled = enabled;
}
void set_manage_text(const std::u16string& text) {
bubble_content_.manage_text = text;
}
void set_manage_tooltip(const std::u16string& text) {
bubble_content_.manage_tooltip = text;
}
void set_manage_text_style(ManageTextStyle manage_text_style) {
bubble_content_.manage_text_style = manage_text_style;
}
void set_show_learn_more(bool show_learn_more) {
bubble_content_.show_learn_more = show_learn_more;
}
void set_done_button_text(const std::u16string& done_button_text) {
bubble_content_.done_button_text = done_button_text;
}
void set_cancel_button_text(const std::u16string& cancel_button_text) {
bubble_content_.cancel_button_text = cancel_button_text;
}
void set_is_user_modifiable(bool is_user_modifiable) {
bubble_content_.is_user_modifiable = is_user_modifiable;
}
private:
raw_ptr<content::WebContents, DanglingUntriaged> web_contents_;
raw_ptr<Owner, DanglingUntriaged> owner_;
raw_ptr<Delegate> delegate_;
BubbleContent bubble_content_;
};
// A generic bubble used for a single content setting.
class ContentSettingSimpleBubbleModel : public ContentSettingBubbleModel {
public:
ContentSettingSimpleBubbleModel(Delegate* delegate,
content::WebContents* web_contents,
ContentSettingsType content_type);
ContentSettingSimpleBubbleModel(const ContentSettingSimpleBubbleModel&) =
delete;
ContentSettingSimpleBubbleModel& operator=(
const ContentSettingSimpleBubbleModel&) = delete;
ContentSettingsType content_type() { return content_type_; }
// ContentSettingBubbleModel implementation.
ContentSettingSimpleBubbleModel* AsSimpleBubbleModel() override;
protected:
bool IsContentAllowed();
private:
FRIEND_TEST_ALL_PREFIXES(FramebustBlockBrowserTest, ManageButtonClicked);
// ContentSettingBubbleModel implementation.
void SetTitle();
void SetMessage();
void SetManageText();
void OnManageButtonClicked() override;
void SetCustomLink();
void OnCustomLinkClicked() override;
ContentSettingsType content_type_;
};
// RPH stands for Register Protocol Handler.
class ContentSettingRPHBubbleModel : public ContentSettingSimpleBubbleModel {
public:
ContentSettingRPHBubbleModel(
Delegate* delegate,
content::WebContents* web_contents,
custom_handlers::ProtocolHandlerRegistry* registry);
ContentSettingRPHBubbleModel(const ContentSettingRPHBubbleModel&) = delete;
ContentSettingRPHBubbleModel& operator=(const ContentSettingRPHBubbleModel&) =
delete;
~ContentSettingRPHBubbleModel() override;
// ContentSettingBubbleModel:
void CommitChanges() override;
private:
void RegisterProtocolHandler();
void UnregisterProtocolHandler();
void IgnoreProtocolHandler();
void ClearOrSetPreviousHandler();
void PerformActionForSelectedItem();
raw_ptr<custom_handlers::ProtocolHandlerRegistry> registry_;
custom_handlers::ProtocolHandler pending_handler_;
custom_handlers::ProtocolHandler previous_handler_;
};
// The model of the content settings bubble for media settings.
class ContentSettingMediaStreamBubbleModel : public ContentSettingBubbleModel {
public:
ContentSettingMediaStreamBubbleModel(Delegate* delegate,
content::WebContents* web_contents);
ContentSettingMediaStreamBubbleModel(
const ContentSettingMediaStreamBubbleModel&) = delete;
ContentSettingMediaStreamBubbleModel& operator=(
const ContentSettingMediaStreamBubbleModel&) = delete;
~ContentSettingMediaStreamBubbleModel() override;
// ContentSettingBubbleModel:
ContentSettingMediaStreamBubbleModel* AsMediaStreamBubbleModel() override;
void CommitChanges() override;
void OnManageButtonClicked() override;
void OnDoneButtonClicked() override;
private:
// Helper functions to check if this bubble was invoked for microphone,
// camera, or both devices.
bool MicrophoneAccessed() const;
bool CameraAccessed() const;
bool MicrophoneBlocked() const;
bool CameraBlocked() const;
void SetTitle();
void SetMessage();
void SetManageText();
// Sets the data for the radio buttons of the bubble.
void SetRadioGroup();
// Sets the data for the media menus of the bubble.
void SetMediaMenus();
// Sets the string that suggests reloading after the settings were changed.
void SetCustomLink();
// Sets whether microphone, camera, or both device permissions can be
// modified.
void SetIsUserModifiable();
// Updates the camera and microphone setting with the passed |setting|.
void UpdateSettings(ContentSetting setting);
#if BUILDFLAG(IS_MAC)
// Initialize the bubble with the elements specific to the scenario when
// camera or mic are disabled in a system (OS) level.
void InitializeSystemMediaPermissionBubble();
#endif // BUILDFLAG(IS_MAC)
// Whether or not to show the bubble UI specific to when media permissions are
// turned off in a system level.
bool ShouldShowSystemMediaPermissions();
// Updates the camera and microphone default device with the passed |type|
// and device.
void UpdateDefaultDeviceForType(blink::mojom::MediaStreamType type,
const std::string& device);
// The content settings that are associated with the individual radio
// buttons.
std::array<ContentSetting, 2> radio_item_setting_;
// The state of the microphone and camera access.
content_settings::PageSpecificContentSettings::MicrophoneCameraState state_;
};
// The model of a bubble that acts as a quiet permission request prompt for
// the notification and geolocation permissions. In contrast to other bubbles
// (which display the current permission state after the user makes the initial
// decision), this is shown before the user makes the first ever permission
// decisions.
class ContentSettingQuietRequestBubbleModel : public ContentSettingBubbleModel {
public:
ContentSettingQuietRequestBubbleModel(Delegate* delegate,
content::WebContents* web_contents);
ContentSettingQuietRequestBubbleModel(
const ContentSettingQuietRequestBubbleModel&) = delete;
ContentSettingQuietRequestBubbleModel& operator=(
const ContentSettingQuietRequestBubbleModel&) = delete;
~ContentSettingQuietRequestBubbleModel() override;
private:
void SetManageText();
// ContentSettingBubbleModel:
void OnManageButtonClicked() override;
void OnLearnMoreClicked() override;
void OnDoneButtonClicked() override;
void OnCancelButtonClicked() override;
ContentSettingQuietRequestBubbleModel* AsQuietRequestBubbleModel() override;
};
// The model for the deceptive content bubble.
class ContentSettingSubresourceFilterBubbleModel
: public ContentSettingBubbleModel {
public:
ContentSettingSubresourceFilterBubbleModel(
Delegate* delegate,
content::WebContents* web_contents);
ContentSettingSubresourceFilterBubbleModel(
const ContentSettingSubresourceFilterBubbleModel&) = delete;
ContentSettingSubresourceFilterBubbleModel& operator=(
const ContentSettingSubresourceFilterBubbleModel&) = delete;
~ContentSettingSubresourceFilterBubbleModel() override;
private:
void SetMessage();
void SetTitle();
void SetManageText();
// ContentSettingBubbleModel:
void OnManageCheckboxChecked(bool is_checked) override;
ContentSettingSubresourceFilterBubbleModel* AsSubresourceFilterBubbleModel()
override;
void OnLearnMoreClicked() override;
void CommitChanges() override;
bool is_checked_ = false;
};
// The model for automatic downloads setting.
class ContentSettingDownloadsBubbleModel : public ContentSettingBubbleModel {
public:
ContentSettingDownloadsBubbleModel(Delegate* delegate,
content::WebContents* web_contents);
ContentSettingDownloadsBubbleModel(
const ContentSettingDownloadsBubbleModel&) = delete;
ContentSettingDownloadsBubbleModel& operator=(
const ContentSettingDownloadsBubbleModel&) = delete;
~ContentSettingDownloadsBubbleModel() override;
// ContentSettingBubbleModel overrides:
ContentSettingDownloadsBubbleModel* AsDownloadsBubbleModel() override;
void CommitChanges() override;
private:
void SetRadioGroup();
void SetTitle();
void SetManageText();
// ContentSettingBubbleModel overrides:
void OnManageButtonClicked() override;
};
class ContentSettingSingleRadioGroup : public ContentSettingSimpleBubbleModel {
public:
ContentSettingSingleRadioGroup(Delegate* delegate,
content::WebContents* web_contents,
ContentSettingsType content_type);
ContentSettingSingleRadioGroup(const ContentSettingSingleRadioGroup&) =
delete;
ContentSettingSingleRadioGroup& operator=(
const ContentSettingSingleRadioGroup&) = delete;
~ContentSettingSingleRadioGroup() override;
// ContentSettingSimpleBubbleModel:
void CommitChanges() override;
protected:
bool settings_changed() const;
private:
FRIEND_TEST_ALL_PREFIXES(FramebustBlockBrowserTest, AllowRadioButtonSelected);
FRIEND_TEST_ALL_PREFIXES(FramebustBlockBrowserTest,
DisallowRadioButtonSelected);
void SetRadioGroup();
void SetNarrowestContentSetting(ContentSetting setting);
ContentSetting block_setting_;
};
// The bubble that allows users to control StorageAccess permission.
// It uses checkboxes instead of radio buttons to allow users to control
// multiple embedded sites.
class ContentSettingStorageAccessBubbleModel
: public ContentSettingBubbleModel {
public:
ContentSettingStorageAccessBubbleModel(Delegate* delegate,
content::WebContents* web_contents);
~ContentSettingStorageAccessBubbleModel() override;
ContentSettingStorageAccessBubbleModel(
const ContentSettingStorageAccessBubbleModel&) = delete;
ContentSettingStorageAccessBubbleModel& operator=(
const ContentSettingStorageAccessBubbleModel&) = delete;
// ContentSettingBubbleModel:
void OnManageButtonClicked() override;
void CommitChanges() override;
void OnSiteRowClicked(const net::SchemefulSite& site,
bool is_allowed) override;
private:
std::map<net::SchemefulSite, /*is_allowed*/ bool> changed_permissions_;
};
// The bubble that informs users that Chrome does not have access to Location
// and guides them to the system preferences to fix that problem if they wish.
class ContentSettingGeolocationBubbleModel
: public ContentSettingSingleRadioGroup {
public:
ContentSettingGeolocationBubbleModel(Delegate* delegate,
content::WebContents* web_contents);
ContentSettingGeolocationBubbleModel(
const ContentSettingGeolocationBubbleModel&) = delete;
ContentSettingGeolocationBubbleModel& operator=(
const ContentSettingGeolocationBubbleModel&) = delete;
~ContentSettingGeolocationBubbleModel() override;
// ContentSettingBubbleModel:
void OnManageButtonClicked() override;
void OnDoneButtonClicked() override;
void CommitChanges() override;
private:
// Initialize the bubble with the elements specific to the scenario when
// geolocation is disabled on the system (OS) level.
void InitializeSystemGeolocationPermissionBubble();
void SetCustomLink();
// Whether or not we are showing the bubble UI specific to when geolocation
// permissions are turned off on a system level.
bool show_system_geolocation_bubble_ = false;
};
#if BUILDFLAG(IS_MAC)
// The bubble that informs users that the app does not have access to
// Notifications and guides them to the system settings to fix that problem
// if they wish.
class ContentSettingNotificationsBubbleModel
: public ContentSettingSimpleBubbleModel {
public:
ContentSettingNotificationsBubbleModel(Delegate* delegate,
content::WebContents* web_contents);
ContentSettingNotificationsBubbleModel(
const ContentSettingNotificationsBubbleModel&) = delete;
ContentSettingNotificationsBubbleModel& operator=(
const ContentSettingNotificationsBubbleModel&) = delete;
~ContentSettingNotificationsBubbleModel() override;
// ContentSettingBubbleModel:
void OnDoneButtonClicked() override;
};
#endif
#if !BUILDFLAG(IS_ANDROID)
// The model for the blocked Framebust bubble.
class ContentSettingFramebustBlockBubbleModel
: public ContentSettingSingleRadioGroup,
public blocked_content::UrlListManager::Observer {
public:
ContentSettingFramebustBlockBubbleModel(Delegate* delegate,
content::WebContents* web_contents);
ContentSettingFramebustBlockBubbleModel(
const ContentSettingFramebustBlockBubbleModel&) = delete;
ContentSettingFramebustBlockBubbleModel& operator=(
const ContentSettingFramebustBlockBubbleModel&) = delete;
~ContentSettingFramebustBlockBubbleModel() override;
// ContentSettingBubbleModel:
void OnListItemClicked(int index, const ui::Event& event) override;
ContentSettingFramebustBlockBubbleModel* AsFramebustBlockBubbleModel()
override;
// UrlListManager::Observer:
void BlockedUrlAdded(int32_t id, const GURL& blocked_url) override;
private:
ListItem CreateListItem(const GURL& url);
base::ScopedObservation<blocked_content::UrlListManager,
blocked_content::UrlListManager::Observer>
url_list_observation_{this};
};
#endif // !BUILDFLAG(IS_ANDROID)
#endif // CHROME_BROWSER_UI_CONTENT_SETTINGS_CONTENT_SETTING_BUBBLE_MODEL_H_