#include "components/content_settings/browser/page_specific_content_settings.h"
#include <list>
#include <variant>
#include <vector>
#include "base/auto_reset.h"
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/lazy_instance.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/observer_list.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/browsing_data/content/cookie_helper.h"
#include "components/content_settings/common/content_settings_agent.mojom.h"
#include "components/content_settings/core/browser/content_settings_info.h"
#include "components/content_settings/core/browser/content_settings_registry.h"
#include "components/content_settings/core/browser/content_settings_utils.h"
#include "components/content_settings/core/browser/permission_settings_registry.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_pattern_parser.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/content_settings/core/common/content_settings_types.mojom-shared.h"
#include "components/content_settings/core/common/content_settings_utils.h"
#include "components/content_settings/core/common/features.h"
#include "components/privacy_sandbox/canonical_topic.h"
#include "components/privacy_sandbox/privacy_sandbox_features.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/cookie_access_details.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/navigation_handle_user_data.h"
#include "content/public/browser/page.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/trust_token_access_details.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
#include "content/public/common/content_constants.h"
#include "net/base/schemeful_site.h"
#include "net/device_bound_sessions/session_access.h"
#include "services/network/public/mojom/shared_dictionary_access_observer.mojom.h"
#include "third_party/abseil-cpp/absl/functional/overload.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/navigation/navigation_params.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/mojom/navigation/renderer_content_settings.mojom.h"
#include "url/gurl.h"
#include "url/origin.h"
using content::BrowserThread;
using StorageType =
content_settings::mojom::ContentSettingsManager::StorageType;
using LifecycleState = content::RenderFrameHost::LifecycleState;
namespace content_settings {
namespace {
constexpr auto kMediaIndicatorHoldAfterUseDuration = base::Seconds(1);
constexpr auto kMediaIndicatorMinimumHoldDuration = base::Seconds(5);
constexpr auto kMediaIndicatorMinimumHoldDurationPhase2 = base::Seconds(4);
constexpr auto kBlockedMediaIndicatorDismissDelay = base::Minutes(1);
constexpr auto kBlockedMediaIndicatorDismissDelayPhase2 = base::Seconds(4);
#if BUILDFLAG(IS_CHROMEOS)
constexpr auto kDeviceInUseIndicatorHideDelay = base::Seconds(15);
#endif
constexpr int kTopicsAPISampleDataTaxonomy = 1;
bool WillNavigationCreateNewPageSpecificContentSettingsOnCommit(
content::NavigationHandle* navigation_handle) {
return navigation_handle->IsInMainFrame() &&
!navigation_handle->IsSameDocument() &&
!navigation_handle->IsServedFromBackForwardCache() &&
!navigation_handle->IsPrerenderedPageActivation();
}
class InflightNavigationContentSettings
: public content::NavigationHandleUserData<
InflightNavigationContentSettings> {
public:
~InflightNavigationContentSettings() override;
std::vector<content::CookieAccessDetails> cookie_accesses;
std::vector<content::TrustTokenAccessDetails> trust_token_accesses;
std::vector<std::pair<GURL, content::AllowServiceWorkerResult>>
service_worker_accesses;
std::vector<network::mojom::SharedDictionaryAccessDetailsPtr>
shared_dictionary_accesses;
std::vector<net::device_bound_sessions::SessionAccess>
device_bound_session_accesses;
private:
explicit InflightNavigationContentSettings(
content::NavigationHandle& navigation_handle);
friend class content::NavigationHandleUserData<
InflightNavigationContentSettings>;
NAVIGATION_HANDLE_USER_DATA_KEY_DECL();
};
class WebContentsHandler
: public content::WebContentsObserver,
public content::WebContentsUserData<WebContentsHandler> {
public:
using Delegate = PageSpecificContentSettings::Delegate;
using SiteDataObserver = PageSpecificContentSettings::SiteDataObserver;
explicit WebContentsHandler(content::WebContents* web_contents,
std::unique_ptr<Delegate> delegate);
~WebContentsHandler() override;
void AddSiteDataObserver(SiteDataObserver* observer);
void RemoveSiteDataObserver(SiteDataObserver* observer);
void NotifySiteDataObservers(const AccessDetails& access_details);
void NotifyStatefulBounceObservers();
void AddPendingCommitUpdate(content::GlobalRenderFrameHostId id,
base::OnceClosure update);
Delegate* delegate() { return delegate_.get(); }
private:
friend class content::WebContentsUserData<WebContentsHandler>;
void TransferNavigationContentSettingsToCommittedDocument(
const InflightNavigationContentSettings& navigation_settings,
content::RenderFrameHost* rfh);
void ReadyToCommitNavigation(
content::NavigationHandle* navigation_handle) override;
void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override;
void OnCookiesAccessed(content::NavigationHandle* navigation,
const content::CookieAccessDetails& details) override;
void OnCookiesAccessed(content::RenderFrameHost* rfh,
const content::CookieAccessDetails& details) override;
void OnTrustTokensAccessed(
content::NavigationHandle* navigation,
const content::TrustTokenAccessDetails& details) override;
void OnTrustTokensAccessed(
content::RenderFrameHost* rfh,
const content::TrustTokenAccessDetails& details) override;
void OnServiceWorkerAccessed(
content::NavigationHandle* navigation,
const GURL& scope,
content::AllowServiceWorkerResult allowed) override;
void OnServiceWorkerAccessed(
content::RenderFrameHost* frame,
const GURL& scope,
content::AllowServiceWorkerResult allowed) override;
void OnSharedDictionaryAccessed(
content::NavigationHandle* navigation,
const network::mojom::SharedDictionaryAccessDetails& details) override;
void OnSharedDictionaryAccessed(
content::RenderFrameHost* rfh,
const network::mojom::SharedDictionaryAccessDetails& details) override;
void OnDeviceBoundSessionAccessed(
content::NavigationHandle* navigation,
const net::device_bound_sessions::SessionAccess& details) override;
void OnDeviceBoundSessionAccessed(
content::RenderFrameHost* rfh,
const net::device_bound_sessions::SessionAccess& details) override;
void WebContentsDestroyed() override;
std::unique_ptr<Delegate> delegate_;
raw_ptr<HostContentSettingsMap> map_;
base::ObserverList<SiteDataObserver>::Unchecked observer_list_;
std::map<content::GlobalRenderFrameHostId, std::vector<base::OnceClosure>>
pending_commit_updates_;
WEB_CONTENTS_USER_DATA_KEY_DECL();
};
template <typename Method, typename... Args>
bool DelayUntilCommitIfNecessary(content::RenderFrameHost* rfh,
Method method,
Args... args) {
if (!rfh)
return false;
if (rfh->GetLifecycleState() == LifecycleState::kPendingCommit) {
auto* handler = WebContentsHandler::FromWebContents(
content::WebContents::FromRenderFrameHost(rfh));
if (!handler)
return false;
handler->AddPendingCommitUpdate(rfh->GetGlobalId(),
base::BindOnce(method, args...));
return true;
}
return false;
}
bool IsThirdPartyCookieDetails(const content::CookieAccessDetails& details) {
return !net::SchemefulSite::IsSameSite(details.url,
details.first_party_url) ||
!details.site_for_cookies.IsFirstParty(details.url);
}
}
InflightNavigationContentSettings::InflightNavigationContentSettings(
content::NavigationHandle&) {}
InflightNavigationContentSettings::~InflightNavigationContentSettings() =
default;
NAVIGATION_HANDLE_USER_DATA_KEY_IMPL(InflightNavigationContentSettings);
WebContentsHandler::WebContentsHandler(content::WebContents* web_contents,
std::unique_ptr<Delegate> delegate)
: WebContentsObserver(web_contents),
content::WebContentsUserData<WebContentsHandler>(*web_contents),
delegate_(std::move(delegate)),
map_(delegate_->GetSettingsMap()) {
DCHECK(
!PageSpecificContentSettings::GetForPage(web_contents->GetPrimaryPage()));
content::PageUserData<PageSpecificContentSettings>::CreateForPage(
web_contents->GetPrimaryPage(), delegate_.get());
}
WebContentsHandler::~WebContentsHandler() = default;
void WebContentsHandler::TransferNavigationContentSettingsToCommittedDocument(
const InflightNavigationContentSettings& navigation_settings,
content::RenderFrameHost* rfh) {
for (const auto& cookie_access : navigation_settings.cookie_accesses) {
OnCookiesAccessed(rfh, cookie_access);
}
for (const auto& trust_token_access :
navigation_settings.trust_token_accesses) {
OnTrustTokensAccessed(rfh, trust_token_access);
}
for (const auto& service_worker_access :
navigation_settings.service_worker_accesses) {
OnServiceWorkerAccessed(rfh, service_worker_access.first,
service_worker_access.second);
}
for (const auto& shared_dictionary_access :
navigation_settings.shared_dictionary_accesses) {
OnSharedDictionaryAccessed(rfh, *shared_dictionary_access);
}
for (const auto& device_bound_session_access :
navigation_settings.device_bound_session_accesses) {
OnDeviceBoundSessionAccessed(rfh, device_bound_session_access);
}
}
void WebContentsHandler::OnCookiesAccessed(
content::NavigationHandle* navigation,
const content::CookieAccessDetails& details) {
if (WillNavigationCreateNewPageSpecificContentSettingsOnCommit(navigation)) {
auto* inflight_navigation_settings =
content::NavigationHandleUserData<InflightNavigationContentSettings>::
GetOrCreateForNavigationHandle(*navigation);
inflight_navigation_settings->cookie_accesses.push_back(details);
return;
}
DCHECK(navigation->GetParentFrame());
OnCookiesAccessed(navigation->GetParentFrame()->GetMainFrame(), details);
}
void WebContentsHandler::OnCookiesAccessed(
content::RenderFrameHost* rfh,
const content::CookieAccessDetails& details) {
auto* pscs = PageSpecificContentSettings::GetForPage(rfh->GetPage());
if (pscs) {
pscs->OnCookiesAccessed(details);
}
}
void WebContentsHandler::OnTrustTokensAccessed(
content::NavigationHandle* navigation,
const content::TrustTokenAccessDetails& details) {
if (WillNavigationCreateNewPageSpecificContentSettingsOnCommit(navigation)) {
auto* inflight_navigation_settings =
content::NavigationHandleUserData<InflightNavigationContentSettings>::
GetOrCreateForNavigationHandle(*navigation);
inflight_navigation_settings->trust_token_accesses.push_back(details);
return;
}
DCHECK(navigation->GetParentFrame());
OnTrustTokensAccessed(navigation->GetParentFrame()->GetMainFrame(), details);
}
void WebContentsHandler::OnTrustTokensAccessed(
content::RenderFrameHost* rfh,
const content::TrustTokenAccessDetails& details) {
auto* pscs = PageSpecificContentSettings::GetForPage(rfh->GetPage());
if (pscs) {
pscs->OnTrustTokenAccessed(details.origin, details.blocked);
}
}
void WebContentsHandler::OnServiceWorkerAccessed(
content::NavigationHandle* navigation,
const GURL& scope,
content::AllowServiceWorkerResult allowed) {
DCHECK(scope.is_valid());
if (WillNavigationCreateNewPageSpecificContentSettingsOnCommit(navigation)) {
auto* inflight_navigation_settings =
content::NavigationHandleUserData<InflightNavigationContentSettings>::
GetOrCreateForNavigationHandle(*navigation);
inflight_navigation_settings->service_worker_accesses.emplace_back(scope,
allowed);
return;
}
DCHECK(navigation->GetParentFrame());
OnServiceWorkerAccessed(navigation->GetParentFrame()->GetMainFrame(), scope,
allowed);
}
void WebContentsHandler::OnServiceWorkerAccessed(
content::RenderFrameHost* frame,
const GURL& scope,
content::AllowServiceWorkerResult allowed) {
auto* pscs = PageSpecificContentSettings::GetForPage(frame->GetPage());
if (pscs) {
pscs->OnServiceWorkerAccessed(scope, frame->GetStorageKey(), allowed);
}
}
void WebContentsHandler::OnSharedDictionaryAccessed(
content::NavigationHandle* navigation,
const network::mojom::SharedDictionaryAccessDetails& details) {
if (WillNavigationCreateNewPageSpecificContentSettingsOnCommit(navigation)) {
auto* inflight_navigation_settings =
content::NavigationHandleUserData<InflightNavigationContentSettings>::
GetOrCreateForNavigationHandle(*navigation);
inflight_navigation_settings->shared_dictionary_accesses.emplace_back(
details.Clone());
return;
}
DCHECK(navigation->GetParentFrame());
OnSharedDictionaryAccessed(navigation->GetParentFrame()->GetMainFrame(),
details);
}
void WebContentsHandler::OnSharedDictionaryAccessed(
content::RenderFrameHost* rfh,
const network::mojom::SharedDictionaryAccessDetails& details) {
PageSpecificContentSettings::BrowsingDataAccessed(
rfh, details.isolation_key,
BrowsingDataModel::StorageType::kSharedDictionary, details.is_blocked);
}
void WebContentsHandler::OnDeviceBoundSessionAccessed(
content::NavigationHandle* navigation,
const net::device_bound_sessions::SessionAccess& details) {
if (WillNavigationCreateNewPageSpecificContentSettingsOnCommit(navigation)) {
auto* inflight_navigation_settings =
content::NavigationHandleUserData<InflightNavigationContentSettings>::
GetOrCreateForNavigationHandle(*navigation);
inflight_navigation_settings->device_bound_session_accesses.emplace_back(
details);
return;
}
DCHECK(navigation->GetParentFrame());
OnDeviceBoundSessionAccessed(navigation->GetParentFrame()->GetMainFrame(),
details);
}
void WebContentsHandler::OnDeviceBoundSessionAccessed(
content::RenderFrameHost* rfh,
const net::device_bound_sessions::SessionAccess& details) {
PageSpecificContentSettings::BrowsingDataAccessed(
rfh, details.session_key,
BrowsingDataModel::StorageType::kDeviceBoundSession,
false);
}
void WebContentsHandler::ReadyToCommitNavigation(
content::NavigationHandle* navigation_handle) {
content::RenderFrameHost* rfh = navigation_handle->GetRenderFrameHost();
RendererContentSettingRules rules;
content_settings::GetRendererContentSettingRules(map_, &rules);
delegate()->SetDefaultRendererContentSettingRules(rfh, &rules);
const GURL& primary_url =
navigation_handle->GetParentFrameOrOuterDocument()
? navigation_handle->GetParentFrameOrOuterDocument()
->GetOutermostMainFrame()
->GetLastCommittedURL()
: navigation_handle->GetURL();
rules.FilterRulesByOutermostMainFrameURL(primary_url);
mojo::AssociatedRemote<content_settings::mojom::ContentSettingsAgent> agent;
rfh->GetRemoteAssociatedInterfaces()->GetInterface(&agent);
agent->SendRendererContentSettingRules(std::move(rules));
const GURL& secondary_url = navigation_handle->GetURL();
auto content_settings = blink::CreateDefaultRendererContentSettings();
content_settings->allow_script =
delegate()->IsFrameAllowlistedForJavaScript(rfh) ||
map_->GetContentSetting(primary_url, secondary_url,
ContentSettingsType::JAVASCRIPT) ==
CONTENT_SETTING_ALLOW;
content_settings->allow_popup =
map_->GetContentSetting(primary_url, secondary_url,
ContentSettingsType::POPUPS) ==
CONTENT_SETTING_ALLOW;
#if !BUILDFLAG(IS_IOS)
content_settings->allow_mixed_content =
map_->GetContentSetting(primary_url, secondary_url,
ContentSettingsType::MIXEDSCRIPT) ==
CONTENT_SETTING_ALLOW;
content_settings->allow_image =
map_->GetContentSetting(primary_url, secondary_url,
ContentSettingsType::IMAGES) ==
CONTENT_SETTING_ALLOW;
content_settings->allow_controlled_frame =
map_->GetContentSetting(primary_url, secondary_url,
ContentSettingsType::CONTROLLED_FRAME) ==
CONTENT_SETTING_ALLOW;
#endif
navigation_handle->SetContentSettings(std::move(content_settings));
}
void WebContentsHandler::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->HasCommitted())
return;
if (WillNavigationCreateNewPageSpecificContentSettingsOnCommit(
navigation_handle)) {
content::PageUserData<PageSpecificContentSettings>::CreateForPage(
navigation_handle->GetRenderFrameHost()->GetPage(), delegate_.get());
InflightNavigationContentSettings* inflight_settings =
content::NavigationHandleUserData<InflightNavigationContentSettings>::
GetForNavigationHandle(*navigation_handle);
if (inflight_settings) {
TransferNavigationContentSettingsToCommittedDocument(
*inflight_settings, navigation_handle->GetRenderFrameHost());
}
content::GlobalRenderFrameHostId rfh_id =
navigation_handle->GetRenderFrameHost()->GetGlobalId();
auto it = pending_commit_updates_.find(rfh_id);
if (it != pending_commit_updates_.end()) {
for (auto& update : it->second) {
std::move(update).Run();
}
pending_commit_updates_.erase(it);
}
}
if (navigation_handle->IsPrerenderedPageActivation()) {
auto* pscs = PageSpecificContentSettings::GetForFrame(
navigation_handle->GetRenderFrameHost());
DCHECK(pscs);
pscs->OnPrerenderingPageActivation();
}
if (navigation_handle->IsInPrimaryMainFrame())
delegate_->UpdateLocationBar();
}
void WebContentsHandler::AddSiteDataObserver(SiteDataObserver* observer) {
observer_list_.AddObserver(observer);
}
void WebContentsHandler::RemoveSiteDataObserver(SiteDataObserver* observer) {
observer_list_.RemoveObserver(observer);
}
void WebContentsHandler::NotifySiteDataObservers(
const AccessDetails& access_details) {
for (SiteDataObserver& observer : observer_list_)
observer.OnSiteDataAccessed(access_details);
}
void WebContentsHandler::NotifyStatefulBounceObservers() {
for (SiteDataObserver& observer : observer_list_) {
observer.OnStatefulBounceDetected();
}
}
void WebContentsHandler::AddPendingCommitUpdate(
content::GlobalRenderFrameHostId id,
base::OnceClosure update) {
#if DCHECK_IS_ON()
content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(id);
DCHECK(rfh);
DCHECK_EQ(rfh->GetLifecycleState(), LifecycleState::kPendingCommit);
#endif
pending_commit_updates_[id].push_back(std::move(update));
}
void WebContentsHandler::WebContentsDestroyed() {
for (SiteDataObserver& observer : observer_list_) {
observer.WebContentsDestroyed();
}
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(WebContentsHandler);
AccessDetails::AccessDetails() = default;
AccessDetails::AccessDetails(SiteDataType site_data_type,
AccessType access_type,
GURL url,
bool blocked_by_policy,
bool is_from_primary_page)
: site_data_type(site_data_type),
access_type(access_type),
url(url),
blocked_by_policy(blocked_by_policy),
is_from_primary_page(is_from_primary_page) {}
AccessDetails::~AccessDetails() = default;
bool AccessDetails::operator<(const AccessDetails& other) const {
return std::tie(site_data_type, access_type, url, blocked_by_policy,
is_from_primary_page) <
std::tie(other.site_data_type, other.access_type, other.url,
other.blocked_by_policy, other.is_from_primary_page);
}
PageSpecificContentSettings::SiteDataObserver::SiteDataObserver(
content::WebContents* web_contents)
: web_contents_(web_contents) {
auto* handler = WebContentsHandler::FromWebContents(web_contents_);
if (handler) {
handler->AddSiteDataObserver(this);
} else {
web_contents_ = nullptr;
}
}
PageSpecificContentSettings::SiteDataObserver::~SiteDataObserver() {
if (!web_contents_)
return;
auto* handler = WebContentsHandler::FromWebContents(web_contents_);
if (handler)
handler->RemoveSiteDataObserver(this);
}
void PageSpecificContentSettings::SiteDataObserver::WebContentsDestroyed() {
auto* handler = WebContentsHandler::FromWebContents(web_contents_);
if (handler) {
handler->RemoveSiteDataObserver(this);
}
web_contents_ = nullptr;
}
PageSpecificContentSettings::PendingUpdates::PendingUpdates() = default;
PageSpecificContentSettings::PendingUpdates::~PendingUpdates() = default;
PageSpecificContentSettings::PageSpecificContentSettings(content::Page& page,
Delegate* delegate)
: content::PageUserData<PageSpecificContentSettings>(page),
delegate_(delegate),
map_(delegate_->GetSettingsMap()),
allowed_browsing_data_model_(BrowsingDataModel::BuildEmpty(
GetWebContents()->GetPrimaryMainFrame()->GetStoragePartition(),
delegate_->CreateBrowsingDataModelDelegate())),
blocked_browsing_data_model_(BrowsingDataModel::BuildEmpty(
GetWebContents()->GetPrimaryMainFrame()->GetStoragePartition(),
delegate_->CreateBrowsingDataModelDelegate())) {
observation_.Observe(map_.get());
if (page.GetMainDocument().GetLifecycleState() ==
LifecycleState::kPrerendering) {
updates_queued_during_prerender_ = std::make_unique<PendingUpdates>();
}
}
PageSpecificContentSettings::~PageSpecificContentSettings() {
for (auto last_used_entry : last_used_time_) {
switch (last_used_entry.first) {
case ContentSettingsType::MEDIASTREAM_MIC:
case ContentSettingsType::MEDIASTREAM_CAMERA:
case ContentSettingsType::SMART_CARD_GUARD:
map_->UpdateLastUsedTime(media_stream_access_origin_,
media_stream_access_origin_,
last_used_entry.first, last_used_entry.second);
break;
default:
NOTREACHED();
}
}
}
void PageSpecificContentSettings::CreateForWebContents(
content::WebContents* web_contents,
std::unique_ptr<Delegate> delegate) {
WebContentsHandler::CreateForWebContents(web_contents, std::move(delegate));
}
void PageSpecificContentSettings::DeleteForWebContentsForTest(
content::WebContents* web_contents) {
PageSpecificContentSettings::DeleteForPage(web_contents->GetPrimaryPage());
web_contents->RemoveUserData(WebContentsHandler::UserDataKey());
}
PageSpecificContentSettings* PageSpecificContentSettings::GetForFrame(
const content::GlobalRenderFrameHostId& id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return GetForFrame(content::RenderFrameHost::FromID(id));
}
PageSpecificContentSettings* PageSpecificContentSettings::GetForFrame(
content::RenderFrameHost* rfh) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return rfh ? PageSpecificContentSettings::GetForPage(rfh->GetPage())
: nullptr;
}
PageSpecificContentSettings::Delegate*
PageSpecificContentSettings::GetDelegateForWebContents(
content::WebContents* web_contents) {
auto* handler = WebContentsHandler::FromWebContents(web_contents);
return handler ? handler->delegate() : nullptr;
}
void PageSpecificContentSettings::StorageAccessed(
StorageType storage_type,
std::variant<content::GlobalRenderFrameHostToken,
content::GlobalRenderFrameHostId> frame_id,
const blink::StorageKey& storage_key,
bool blocked_by_policy) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
content::RenderFrameHost* rfh = std::visit(
absl::Overload{
[](const content::GlobalRenderFrameHostToken& frame_token) {
return content::RenderFrameHost::FromFrameToken(frame_token);
},
[](const content::GlobalRenderFrameHostId& id) {
return content::RenderFrameHost::FromID(id);
},
},
frame_id);
if (DelayUntilCommitIfNecessary(
rfh, &PageSpecificContentSettings::StorageAccessed, storage_type,
frame_id, storage_key, blocked_by_policy)) {
return;
}
PageSpecificContentSettings* settings = GetForFrame(rfh);
if (settings) {
auto bdm_storage_type = ([storage_type]() {
switch (storage_type) {
case StorageType::LOCAL_STORAGE:
return BrowsingDataModel::StorageType::kLocalStorage;
case StorageType::SESSION_STORAGE:
return BrowsingDataModel::StorageType::kSessionStorage;
case StorageType::FILE_SYSTEM:
case StorageType::INDEXED_DB:
case StorageType::CACHE:
case StorageType::WEB_LOCKS:
return BrowsingDataModel::StorageType::kQuotaStorage;
}
})();
if (storage_type == StorageType::SESSION_STORAGE) {
auto* web_contents = content::WebContents::FromRenderFrameHost(rfh);
const auto& session_storage_namespace_map =
web_contents->GetController().GetSessionStorageNamespaceMap();
const auto& storage_partition_config =
web_contents->GetSiteInstance()->GetStoragePartitionConfig();
const auto& namespace_id =
session_storage_namespace_map.at(storage_partition_config);
content::SessionStorageUsageInfo session_storage_usage_info{
storage_key, namespace_id->id()};
settings->OnBrowsingDataAccessed(session_storage_usage_info,
bdm_storage_type, blocked_by_policy);
} else {
settings->OnBrowsingDataAccessed(storage_key, bdm_storage_type,
blocked_by_policy);
}
}
}
void PageSpecificContentSettings::BrowsingDataAccessed(
content::RenderFrameHost* rfh,
BrowsingDataModel::DataKey data_key,
BrowsingDataModel::StorageType storage_type,
bool blocked) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
PageSpecificContentSettings* settings = GetForFrame(rfh);
if (settings) {
settings->OnBrowsingDataAccessed(data_key, storage_type, blocked);
}
}
void PageSpecificContentSettings::ContentBlocked(
const content::GlobalRenderFrameHostToken& frame_token,
ContentSettingsType type) {
content::RenderFrameHost* rfh =
content::RenderFrameHost::FromFrameToken(frame_token);
if (DelayUntilCommitIfNecessary(rfh,
&PageSpecificContentSettings::ContentBlocked,
frame_token, type)) {
return;
}
PageSpecificContentSettings* settings = GetForFrame(rfh);
if (settings) {
settings->OnContentBlocked(type);
}
}
void PageSpecificContentSettings::SharedWorkerAccessed(
int render_process_id,
int render_frame_id,
const GURL& worker_url,
const std::string& name,
const blink::StorageKey& storage_key,
const blink::mojom::SharedWorkerSameSiteCookies same_site_cookies,
bool blocked_by_policy) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
PageSpecificContentSettings* settings = GetForFrame(
content::RenderFrameHost::FromID(render_process_id, render_frame_id));
if (settings) {
settings->OnBrowsingDataAccessed(
browsing_data::SharedWorkerInfo{worker_url, name, storage_key,
same_site_cookies},
BrowsingDataModel::StorageType::kSharedWorker, blocked_by_policy);
}
}
void PageSpecificContentSettings::InterestGroupJoined(
content::RenderFrameHost* rfh,
const url::Origin& api_origin,
bool blocked_by_policy) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
PageSpecificContentSettings* settings = GetForFrame(rfh);
if (settings) {
settings->OnInterestGroupJoined(api_origin, blocked_by_policy);
}
}
void PageSpecificContentSettings::TopicAccessed(
content::RenderFrameHost* rfh,
const url::Origin& api_origin,
bool blocked_by_policy,
privacy_sandbox::CanonicalTopic topic) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
PageSpecificContentSettings* settings = GetForFrame(rfh);
if (settings) {
settings->OnTopicAccessed(api_origin, blocked_by_policy, topic);
}
}
void PageSpecificContentSettings::NotificationsAccessed(
content::RenderFrameHost* rfh,
bool blocked) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
PageSpecificContentSettings* settings = GetForFrame(rfh);
if (settings) {
if (blocked) {
settings->OnContentBlocked(ContentSettingsType::NOTIFICATIONS);
} else {
settings->OnContentAllowed(ContentSettingsType::NOTIFICATIONS);
}
}
}
content::WebContentsObserver*
PageSpecificContentSettings::GetWebContentsObserverForTest(
content::WebContents* web_contents) {
return WebContentsHandler::FromWebContents(web_contents);
}
bool PageSpecificContentSettings::IsContentBlocked(
ContentSettingsType content_type) const {
DCHECK_NE(ContentSettingsType::AUTOMATIC_DOWNLOADS, content_type)
<< "Automatic downloads handled by DownloadRequestLimiter";
CHECK_NE(ContentSettingsType::STORAGE_ACCESS, content_type)
<< "StorageAccess handled by GetTwoOriginRequests";
if (content_type == ContentSettingsType::IMAGES ||
content_type == ContentSettingsType::JAVASCRIPT ||
content_type == ContentSettingsType::COOKIES ||
content_type == ContentSettingsType::POPUPS ||
content_type == ContentSettingsType::MIXEDSCRIPT ||
content_type == ContentSettingsType::MEDIASTREAM_MIC ||
content_type == ContentSettingsType::MEDIASTREAM_CAMERA ||
content_type == ContentSettingsType::MIDI_SYSEX ||
content_type == ContentSettingsType::ADS ||
content_type == ContentSettingsType::SOUND ||
content_type == ContentSettingsType::CLIPBOARD_READ_WRITE ||
content_type == ContentSettingsType::SENSORS ||
content_type == ContentSettingsType::GEOLOCATION ||
content_type == ContentSettingsType::GEOLOCATION_WITH_OPTIONS ||
#if BUILDFLAG(IS_WIN)
content_type == ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER ||
#endif
content_type == ContentSettingsType::NOTIFICATIONS) {
const auto& it = content_settings_status_.find(content_type);
if (it != content_settings_status_.end()) {
return it->second.blocked;
}
}
return false;
}
bool PageSpecificContentSettings::IsContentAllowed(
ContentSettingsType content_type) const {
DCHECK_NE(ContentSettingsType::AUTOMATIC_DOWNLOADS, content_type)
<< "Automatic downloads handled by DownloadRequestLimiter";
CHECK_NE(ContentSettingsType::STORAGE_ACCESS, content_type)
<< "StorageAccess handled by GetTwoOriginRequests";
if (content_type != ContentSettingsType::COOKIES &&
content_type != ContentSettingsType::MEDIASTREAM_MIC &&
content_type != ContentSettingsType::MEDIASTREAM_CAMERA &&
content_type != ContentSettingsType::MIDI_SYSEX &&
content_type != ContentSettingsType::CLIPBOARD_READ_WRITE &&
content_type != ContentSettingsType::SENSORS &&
content_type != ContentSettingsType::GEOLOCATION &&
content_type != ContentSettingsType::GEOLOCATION_WITH_OPTIONS &&
#if BUILDFLAG(IS_WIN)
content_type != ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER &&
#endif
content_type != ContentSettingsType::NOTIFICATIONS) {
return false;
}
const auto& it = content_settings_status_.find(content_type);
if (it != content_settings_status_.end()) {
return it->second.allowed;
}
return false;
}
std::map<net::SchemefulSite, bool>
PageSpecificContentSettings::GetTwoSiteRequests(
ContentSettingsType content_type) {
return content_settings_two_site_requests_[content_type];
}
void PageSpecificContentSettings::
SetNotificationsWasDeniedBecauseOfSystemPermission() {
if (notifications_was_denied_because_of_system_permission_) {
return;
}
notifications_was_denied_because_of_system_permission_ = true;
MaybeUpdateLocationBar();
}
void PageSpecificContentSettings::OnContentBlocked(ContentSettingsType type) {
DCHECK(type != ContentSettingsType::MEDIASTREAM_MIC &&
type != ContentSettingsType::MEDIASTREAM_CAMERA)
<< "Media stream settings handled by OnMediaStreamPermissionSet";
if (freeze_indicators_) {
return;
}
if (!content_settings::ContentSettingsRegistry::GetInstance()->Get(type)) {
return;
}
ContentSettingsStatus& status = content_settings_status_[type];
if (!status.blocked) {
status.blocked = true;
if (!is_updating_synced_pscs_) {
base::AutoReset<bool> auto_reset(&is_updating_synced_pscs_, true);
if (auto* synced_pccs = MaybeGetSyncedSettingsForPictureInPicture()) {
synced_pccs->OnContentBlocked(type);
}
}
MaybeUpdateLocationBar();
NotifyDelegate(&Delegate::OnContentBlocked, type);
MaybeUpdateParent(&PageSpecificContentSettings::OnContentBlocked, type);
}
}
void PageSpecificContentSettings::OnContentAllowed(ContentSettingsType type) {
DCHECK(type != ContentSettingsType::MEDIASTREAM_MIC &&
type != ContentSettingsType::MEDIASTREAM_CAMERA)
<< "Media stream settings handled by OnMediaStreamPermissionSet";
bool access_changed = false;
ContentSettingsStatus& status = content_settings_status_[type];
bool must_reset_blocked_status = false;
if (type == ContentSettingsType::SENSORS) {
must_reset_blocked_status = true;
}
auto* synced_pccs = MaybeGetSyncedSettingsForPictureInPicture();
if (synced_pccs) {
must_reset_blocked_status = true;
}
#if BUILDFLAG(IS_ANDROID)
if (type == ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER)
must_reset_blocked_status = true;
#endif
if (must_reset_blocked_status && status.blocked) {
status.blocked = false;
access_changed = true;
}
if (!status.allowed) {
status.allowed = true;
access_changed = true;
NotifyDelegate(&Delegate::OnContentAllowed, type);
}
if (access_changed) {
if (!is_updating_synced_pscs_ && synced_pccs) {
base::AutoReset<bool> auto_reset(&is_updating_synced_pscs_, true);
synced_pccs->OnContentAllowed(type);
}
MaybeUpdateLocationBar();
MaybeUpdateParent(&PageSpecificContentSettings::OnContentAllowed, type);
}
}
void PageSpecificContentSettings::OnTwoSitePermissionChanged(
ContentSettingsType type,
net::SchemefulSite requesting_site,
ContentSetting content_setting) {
bool access_changed = false;
auto& site_map = content_settings_two_site_requests_[type];
switch (content_setting) {
case CONTENT_SETTING_ASK:
case CONTENT_SETTING_DEFAULT:
if (site_map.contains(requesting_site)) {
site_map.erase(requesting_site);
access_changed = true;
}
break;
case CONTENT_SETTING_ALLOW:
case CONTENT_SETTING_BLOCK: {
bool is_allowed = content_setting == CONTENT_SETTING_ALLOW;
if (auto it = site_map.find(requesting_site);
it == site_map.end() || it->second != is_allowed) {
site_map[requesting_site] = is_allowed;
access_changed = true;
}
break;
}
default:
NOTREACHED() << content_setting;
}
if (access_changed) {
MaybeUpdateLocationBar();
}
}
void PageSpecificContentSettings::OnCookiesAccessed(
const content::CookieAccessDetails& details,
content::Page* originating_page) {
originating_page = originating_page ? originating_page : &page();
if (details.cookie_access_result_list.empty()) {
return;
}
bool blocked = details.blocked_by_policy;
auto& model =
blocked ? blocked_browsing_data_model_ : allowed_browsing_data_model_;
for (const auto& cookie_with_access_result :
details.cookie_access_result_list) {
model->AddBrowsingData(cookie_with_access_result.cookie,
BrowsingDataModel::StorageType::kCookie,
0,
1,
(blocked && IsThirdPartyCookieDetails(details)));
}
if (details.blocked_by_policy) {
OnContentBlocked(ContentSettingsType::COOKIES);
} else {
OnContentAllowed(ContentSettingsType::COOKIES);
}
MaybeUpdateParent(&PageSpecificContentSettings::OnCookiesAccessed, details,
originating_page);
AccessDetails access_details{
SiteDataType::kCookies,
details.type == network::mojom::CookieAccessDetails::Type::kChange
? AccessType::kWrite
: AccessType::kRead,
details.url, details.blocked_by_policy, originating_page->IsPrimary()};
MaybeNotifySiteDataObservers(access_details);
}
void PageSpecificContentSettings::OnServiceWorkerAccessed(
const GURL& scope,
const blink::StorageKey& storage_key,
content::AllowServiceWorkerResult allowed_result,
content::Page* originating_page) {
DCHECK(scope.is_valid());
originating_page = originating_page ? originating_page : &page();
auto& model = allowed_result ? allowed_browsing_data_model_
: blocked_browsing_data_model_;
model->AddBrowsingData(storage_key,
BrowsingDataModel::StorageType::kQuotaStorage,
0);
if (allowed_result.javascript_blocked_by_policy()) {
OnContentBlocked(ContentSettingsType::JAVASCRIPT);
} else {
OnContentAllowed(ContentSettingsType::JAVASCRIPT);
}
if (allowed_result.cookies_blocked_by_policy()) {
OnContentBlocked(ContentSettingsType::COOKIES);
} else {
OnContentAllowed(ContentSettingsType::COOKIES);
}
MaybeUpdateParent(&PageSpecificContentSettings::OnServiceWorkerAccessed,
scope, storage_key, allowed_result, originating_page);
}
void PageSpecificContentSettings::OnInterestGroupJoined(
const url::Origin& api_origin,
bool blocked_by_policy) {
if (blocked_by_policy) {
blocked_interest_group_api_.push_back(api_origin);
} else {
allowed_interest_group_api_.push_back(api_origin);
OnContentAllowed(ContentSettingsType::COOKIES);
}
MaybeUpdateParent(&PageSpecificContentSettings::OnInterestGroupJoined,
api_origin, blocked_by_policy);
AccessDetails access_details{SiteDataType::kInterestGroup, AccessType::kWrite,
api_origin.GetURL(), blocked_by_policy, false};
MaybeNotifySiteDataObservers(access_details);
}
void PageSpecificContentSettings::OnTopicAccessed(
const url::Origin& api_origin,
bool blocked_by_policy,
privacy_sandbox::CanonicalTopic topic) {
accessed_topics_.insert(topic);
MaybeUpdateParent(&PageSpecificContentSettings::OnTopicAccessed, api_origin,
blocked_by_policy, topic);
}
void PageSpecificContentSettings::OnTrustTokenAccessed(
const url::Origin& api_origin,
bool blocked) {
OnBrowsingDataAccessed(api_origin,
BrowsingDataModel::StorageType::kTrustTokens, blocked);
}
void PageSpecificContentSettings::OnBrowsingDataAccessed(
BrowsingDataModel::DataKey data_key,
BrowsingDataModel::StorageType storage_type,
bool blocked,
content::Page* originating_page) {
originating_page = originating_page ? originating_page : &page();
auto& model =
blocked ? blocked_browsing_data_model_ : allowed_browsing_data_model_;
model->AddBrowsingData(data_key, storage_type, 0);
if (blocked) {
if (model->IsStorageTypeCookieLike(storage_type)) {
OnContentBlocked(ContentSettingsType::COOKIES);
}
} else {
OnContentAllowed(ContentSettingsType::COOKIES);
}
MaybeUpdateParent(&PageSpecificContentSettings::OnBrowsingDataAccessed,
data_key, storage_type, blocked, originating_page);
GURL accessing_url =
std::holds_alternative<blink::StorageKey>(data_key)
? std::get<blink::StorageKey>(data_key).origin().GetURL()
: GURL();
if (storage_type == BrowsingDataModel::StorageType::kSessionStorage) {
accessing_url = std::get<content::SessionStorageUsageInfo>(data_key)
.storage_key.origin()
.GetURL();
}
AccessDetails access_details{SiteDataType::kStorage, AccessType::kUnknown,
accessing_url, blocked,
originating_page->IsPrimary()};
MaybeNotifySiteDataObservers(access_details);
}
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
void PageSpecificContentSettings::OnProtectedMediaIdentifierPermissionSet(
const GURL& requesting_origin,
bool allowed) {
if (allowed) {
OnContentAllowed(ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER);
} else {
OnContentBlocked(ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER);
}
}
#endif
PageSpecificContentSettings::MicrophoneCameraState
PageSpecificContentSettings::GetMicrophoneCameraState() const {
return Union(microphone_camera_state_, delegate_->GetMicrophoneCameraState());
}
bool PageSpecificContentSettings::IsMicrophoneCameraStateChanged() const {
if (microphone_camera_state_.Has(kMicrophoneAccessed) &&
(microphone_camera_state_.Has(kMicrophoneBlocked)
? !IsContentBlocked(ContentSettingsType::MEDIASTREAM_MIC)
: !IsContentAllowed(ContentSettingsType::MEDIASTREAM_MIC))) {
return true;
}
if (microphone_camera_state_.Has(kCameraAccessed) &&
(microphone_camera_state_.Has(kCameraBlocked)
? !IsContentBlocked(ContentSettingsType::MEDIASTREAM_CAMERA)
: !IsContentAllowed(ContentSettingsType::MEDIASTREAM_CAMERA))) {
return true;
}
return false;
}
void PageSpecificContentSettings::OnMediaStreamPermissionSet(
const GURL& request_origin,
MicrophoneCameraState new_microphone_camera_state) {
DCHECK(!IsEmbeddedPage());
if (freeze_indicators_ && (new_microphone_camera_state.HasAny(
{kMicrophoneBlocked, kCameraBlocked}))) {
return;
}
media_stream_access_origin_ = request_origin;
if (new_microphone_camera_state.Has(kMicrophoneAccessed)) {
bool mic_blocked = new_microphone_camera_state.Has(kMicrophoneBlocked);
ContentSettingsStatus& status =
content_settings_status_[ContentSettingsType::MEDIASTREAM_MIC];
if (!status.allowed && !mic_blocked) {
NotifyDelegate(&Delegate::OnContentAllowed,
ContentSettingsType::MEDIASTREAM_MIC);
}
status.allowed = !mic_blocked;
status.blocked = mic_blocked;
}
if (new_microphone_camera_state.Has(kCameraAccessed)) {
bool cam_blocked = new_microphone_camera_state.Has(kCameraBlocked);
ContentSettingsStatus& status =
content_settings_status_[ContentSettingsType::MEDIASTREAM_CAMERA];
if (!status.allowed && !cam_blocked) {
NotifyDelegate(&Delegate::OnContentAllowed,
ContentSettingsType::MEDIASTREAM_CAMERA);
}
status.allowed = !cam_blocked;
status.blocked = cam_blocked;
}
if (microphone_camera_state_ != new_microphone_camera_state) {
if (!is_updating_synced_pscs_) {
base::AutoReset<bool> auto_reset(&is_updating_synced_pscs_, true);
if (auto* synced_pccs = MaybeGetSyncedSettingsForPictureInPicture()) {
synced_pccs->OnMediaStreamPermissionSet(request_origin,
new_microphone_camera_state);
}
}
if (base::FeatureList::IsEnabled(
content_settings::features::kLeftHandSideActivityIndicators)) {
if (microphone_camera_state_.HasAny(
{kCameraAccessed, kMicrophoneAccessed})) {
return;
}
}
microphone_camera_state_ = new_microphone_camera_state;
MaybeUpdateLocationBar();
}
if (!delegate_->IsPiPWindow(GetWebContents())) {
if (microphone_camera_state_.Has(kMicrophoneBlocked)) {
StartBlockedIndicatorTimer(ContentSettingsType::MEDIASTREAM_MIC);
}
if (microphone_camera_state_.Has(kCameraBlocked)) {
StartBlockedIndicatorTimer(ContentSettingsType::MEDIASTREAM_CAMERA);
}
}
}
void PageSpecificContentSettings::AddPermissionUsageObserver(
PermissionUsageObserver* observer) {
permission_usage_observers_.AddObserver(observer);
}
void PageSpecificContentSettings::RemovePermissionUsageObserver(
PermissionUsageObserver* observer) {
permission_usage_observers_.RemoveObserver(observer);
}
void PageSpecificContentSettings::ClearPopupsBlocked() {
ContentSettingsStatus& status =
content_settings_status_[ContentSettingsType::POPUPS];
status.blocked = false;
if (!is_updating_synced_pscs_) {
base::AutoReset<bool> auto_reset(&is_updating_synced_pscs_, true);
if (auto* synced_pccs = MaybeGetSyncedSettingsForPictureInPicture()) {
synced_pccs->ClearPopupsBlocked();
}
}
MaybeUpdateLocationBar();
}
void PageSpecificContentSettings::OnAudioBlocked() {
OnContentBlocked(ContentSettingsType::SOUND);
}
void PageSpecificContentSettings::IncrementStatefulBounceCount() {
stateful_bounce_count_++;
WebContentsHandler::FromWebContents(GetWebContents())
->NotifyStatefulBounceObservers();
}
void PageSpecificContentSettings::OnContentSettingChanged(
const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern,
ContentSettingsType content_type) {
if (IsEmbeddedPage())
return;
const GURL current_url = page().GetMainDocument().GetLastCommittedURL();
if (content_type == ContentSettingsType::STORAGE_ACCESS) {
if (!secondary_pattern.Matches(current_url)) {
return;
}
} else {
if (!primary_pattern.Matches(current_url)) {
return;
}
}
ContentSettingsStatus& status = content_settings_status_[content_type];
auto* info = PermissionSettingsRegistry::GetInstance()->Get(content_type);
switch (content_type) {
case ContentSettingsType::MEDIASTREAM_MIC:
case ContentSettingsType::MEDIASTREAM_CAMERA: {
const GURL media_origin = media_stream_access_origin();
ContentSetting setting =
map_->GetContentSetting(media_origin, media_origin, content_type);
if (content_type == ContentSettingsType::MEDIASTREAM_MIC &&
setting == CONTENT_SETTING_ALLOW) {
mic_was_just_granted_on_site_level_ = true;
}
if (content_type == ContentSettingsType::MEDIASTREAM_CAMERA &&
setting == CONTENT_SETTING_ALLOW) {
camera_was_just_granted_on_site_level_ = true;
}
status.allowed = setting == CONTENT_SETTING_ALLOW;
status.blocked = setting == CONTENT_SETTING_BLOCK;
break;
}
case ContentSettingsType::GEOLOCATION:
case ContentSettingsType::GEOLOCATION_WITH_OPTIONS: {
PermissionSetting geolocation_setting =
map_->GetPermissionSetting(current_url, current_url, content_type);
if (info->delegate().IsAnyPermissionAllowed(geolocation_setting)) {
geolocation_was_just_granted_on_site_level_ = true;
} else if (info->delegate().IsUndecided(geolocation_setting)) {
MaybeUpdateLocationBar();
}
[[fallthrough]];
}
case ContentSettingsType::NOTIFICATIONS:
MaybeUpdateLocationBar();
[[fallthrough]];
case ContentSettingsType::IMAGES:
case ContentSettingsType::JAVASCRIPT:
case ContentSettingsType::COOKIES:
case ContentSettingsType::POPUPS:
case ContentSettingsType::MIXEDSCRIPT:
case ContentSettingsType::MIDI_SYSEX:
case ContentSettingsType::ADS:
case ContentSettingsType::SOUND:
case ContentSettingsType::CLIPBOARD_READ_WRITE:
#if BUILDFLAG(IS_WIN)
case ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER:
#endif
case ContentSettingsType::SENSORS: {
PermissionSetting setting =
map_->GetPermissionSetting(current_url, current_url, content_type);
if (info->delegate().IsBlocked(setting) && status.allowed) {
status.blocked = false;
status.allowed = false;
OnContentBlocked(content_type);
} else if (info->delegate().IsAnyPermissionAllowed(setting) &&
status.blocked) {
status.blocked = false;
status.allowed = false;
OnContentAllowed(content_type);
}
break;
}
case ContentSettingsType::STORAGE_ACCESS: {
GURL requesting_url = primary_pattern.ToRepresentativeUrl();
if (!requesting_url.is_valid()) {
return;
}
net::SchemefulSite requesting_site(requesting_url);
if (!content_settings_two_site_requests_[content_type].contains(
requesting_site)) {
return;
}
ContentSetting setting =
map_->GetContentSetting(requesting_url, current_url, content_type);
OnTwoSitePermissionChanged(content_type, requesting_site, setting);
break;
}
default:
break;
}
}
void PageSpecificContentSettings::ClearContentSettingsChangedViaPageInfo() {
content_settings_changed_via_page_info_.clear();
}
void PageSpecificContentSettings::BlockAllContentForTesting() {
content_settings::ContentSettingsRegistry* registry =
content_settings::ContentSettingsRegistry::GetInstance();
for (const content_settings::ContentSettingsInfo* info : *registry) {
ContentSettingsType type = info->website_settings_info()->type();
if (type != ContentSettingsType::MEDIASTREAM_MIC &&
type != ContentSettingsType::MEDIASTREAM_CAMERA) {
OnContentBlocked(type);
}
}
MicrophoneCameraState media_blocked{kMicrophoneAccessed, kMicrophoneBlocked,
kCameraAccessed, kCameraBlocked};
OnMediaStreamPermissionSet(page().GetMainDocument().GetLastCommittedURL(),
media_blocked);
}
void PageSpecificContentSettings::ContentSettingChangedViaPageInfo(
ContentSettingsType type) {
content_settings_changed_via_page_info_.insert(type);
}
bool PageSpecificContentSettings::HasContentSettingChangedViaPageInfo(
ContentSettingsType type) const {
return content_settings_changed_via_page_info_.find(type) !=
content_settings_changed_via_page_info_.end();
}
bool PageSpecificContentSettings::HasAccessedTopics() const {
return !GetAccessedTopics().empty();
}
std::vector<privacy_sandbox::CanonicalTopic>
PageSpecificContentSettings::GetAccessedTopics() const {
if (accessed_topics_.empty() &&
privacy_sandbox::kPrivacySandboxSettings4ShowSampleDataForTesting.Get() &&
page().GetMainDocument().GetLastCommittedURL().GetHost() ==
"example.com") {
return {privacy_sandbox::CanonicalTopic(browsing_topics::Topic(3),
kTopicsAPISampleDataTaxonomy),
privacy_sandbox::CanonicalTopic(browsing_topics::Topic(4),
kTopicsAPISampleDataTaxonomy)};
}
return {accessed_topics_.begin(), accessed_topics_.end()};
}
bool PageSpecificContentSettings::HasJoinedUserToInterestGroup() const {
return !allowed_interest_group_api_.empty();
}
bool PageSpecificContentSettings::IsPagePrerendering() const {
return !!updates_queued_during_prerender_;
}
bool PageSpecificContentSettings::IsEmbeddedPage() const {
return page().GetMainDocument().GetParentOrOuterDocument();
}
void PageSpecificContentSettings::OnPrerenderingPageActivation() {
DCHECK(updates_queued_during_prerender_);
for (auto& delegate_update :
updates_queued_during_prerender_->delegate_updates) {
std::move(delegate_update).Run();
}
if (updates_queued_during_prerender_->site_data_accessed) {
WebContentsHandler::FromWebContents(GetWebContents())
->NotifySiteDataObservers(
updates_queued_during_prerender_->access_details);
}
updates_queued_during_prerender_.reset();
}
void PageSpecificContentSettings::OnCapturingStateChanged(
ContentSettingsType type,
bool is_capturing) {
DCHECK(type == ContentSettingsType::MEDIASTREAM_MIC ||
type == ContentSettingsType::MEDIASTREAM_CAMERA);
if (indicators_hiding_delay_timer_.contains(type) && is_capturing) {
indicators_hiding_delay_timer_.erase(type);
}
if (is_capturing) {
if (media_indicator_time_ == base::TimeTicks()) {
media_indicator_time_ = base::TimeTicks::Now();
}
OnCapturingStateChangedInternal(type, is_capturing);
} else {
last_used_time_[type] = base::Time::Now();
if ((type == ContentSettingsType::MEDIASTREAM_CAMERA &&
!microphone_camera_state_.Has(kMicrophoneAccessed)) ||
(type == ContentSettingsType::MEDIASTREAM_MIC &&
!microphone_camera_state_.Has(kCameraAccessed))) {
base::TimeDelta indicator_display_time =
base::TimeTicks::Now() - media_indicator_time_;
base::TimeDelta delay;
base::TimeDelta min_delay;
if (base::FeatureList::IsEnabled(
content_settings::features::kLeftHandSideActivityIndicators)) {
min_delay = kMediaIndicatorMinimumHoldDurationPhase2;
} else {
min_delay = kMediaIndicatorMinimumHoldDuration;
}
if (indicator_display_time < min_delay) {
delay = min_delay - indicator_display_time;
delay = std::max(min_delay - indicator_display_time,
kMediaIndicatorHoldAfterUseDuration);
} else {
delay = kMediaIndicatorHoldAfterUseDuration;
}
indicators_hiding_delay_timer_[type].Start(
FROM_HERE, delay,
base::BindOnce(
&PageSpecificContentSettings::OnCapturingStateChangedInternal,
weak_factory_.GetWeakPtr(), type, false));
} else {
OnCapturingStateChangedInternal(type, false);
}
}
}
#if BUILDFLAG(IS_CHROMEOS)
void PageSpecificContentSettings::OnDeviceUsed(ContentSettingsType type) {
CHECK_EQ(ContentSettingsType::SMART_CARD_GUARD, type);
last_used_time_[type] = base::Time::Now();
if (in_use_.insert(type).second) {
MaybeUpdateLocationBar();
}
}
void PageSpecificContentSettings::OnLastDeviceConnectionLost(
ContentSettingsType type) {
CHECK_EQ(mojom::ContentSettingsType::SMART_CARD_GUARD, type);
in_use_.erase(type);
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&PageSpecificContentSettings::MaybeUpdateLocationBar,
weak_factory_.GetWeakPtr()),
kDeviceInUseIndicatorHideDelay);
}
#endif
bool PageSpecificContentSettings::IsInUse(ContentSettingsType type) const {
return in_use_.contains(type);
}
#if BUILDFLAG(IS_CHROMEOS)
bool PageSpecificContentSettings::ShouldShowDeviceInUseIndicator(
ContentSettingsType type) const {
return IsInUse(type) ||
GetLastUsedTime(type) >
base::Time::Now() - kDeviceInUseIndicatorHideDelay;
;
}
#endif
void PageSpecificContentSettings::OnCapturingStateChangedInternal(
ContentSettingsType type,
bool is_capturing) {
MicrophoneCameraStateFlags state =
type == ContentSettingsType::MEDIASTREAM_MIC ? kMicrophoneAccessed
: kCameraAccessed;
if (is_capturing) {
microphone_camera_state_.Put(state);
in_use_.insert(type);
auto t = type == ContentSettingsType::MEDIASTREAM_CAMERA
? ContentSettingsType::MEDIASTREAM_MIC
: ContentSettingsType::MEDIASTREAM_CAMERA;
if (media_blocked_indicator_timer_.contains(t) ||
delegate_->IsBlockedOnSystemLevel(t)) {
ResetMediaBlockedState(t, false);
}
} else {
microphone_camera_state_.Remove(state);
in_use_.erase(type);
}
if (!microphone_camera_state_.HasAny(
{kMicrophoneAccessed, kCameraAccessed})) {
microphone_camera_state_.Clear();
media_indicator_time_ = base::TimeTicks();
}
MaybeUpdateLocationBar();
}
const base::Time PageSpecificContentSettings::GetLastUsedTime(
ContentSettingsType type) const {
auto it = last_used_time_.find(type);
if (it != last_used_time_.end()) {
return it->second;
}
content_settings::SettingInfo info;
map_->GetPermissionSetting(GetWebContents()->GetLastCommittedURL(),
GetWebContents()->GetLastCommittedURL(), type,
&info);
return info.metadata.last_used();
}
void PageSpecificContentSettings::OnActivityIndicatorBubbleOpened(
ContentSettingsType type) {
if (auto it = indicators_hiding_delay_timer_.find(type);
it != indicators_hiding_delay_timer_.end() && it->second.IsRunning()) {
it->second.Stop();
} else if (auto jt = media_blocked_indicator_timer_.find(type);
jt != media_blocked_indicator_timer_.end() &&
jt->second.IsRunning()) {
jt->second.Stop();
}
}
void PageSpecificContentSettings::OnActivityIndicatorBubbleClosed(
ContentSettingsType type) {
if (auto it = indicators_hiding_delay_timer_.find(type);
it != indicators_hiding_delay_timer_.end()) {
it->second.Start(
FROM_HERE, kMediaIndicatorHoldAfterUseDuration,
base::BindOnce(
&PageSpecificContentSettings::OnCapturingStateChangedInternal,
weak_factory_.GetWeakPtr(), type, false));
} else if (media_blocked_indicator_timer_.contains(type)) {
StartBlockedIndicatorTimer(type);
}
}
bool PageSpecificContentSettings::IsIndicatorVisible(
ContentSettingsType type) const {
return visible_indicators_.contains(type);
}
void PageSpecificContentSettings::OnPermissionIndicatorShown(
ContentSettingsType type) {
visible_indicators_.insert(type);
}
void PageSpecificContentSettings::OnPermissionIndicatorHidden(
ContentSettingsType type) {
visible_indicators_.erase(type);
}
void PageSpecificContentSettings::StartBlockedIndicatorTimer(
ContentSettingsType type) {
base::TimeDelta blocked_indicator_delay;
if (base::FeatureList::IsEnabled(
content_settings::features::kLeftHandSideActivityIndicators)) {
blocked_indicator_delay = kBlockedMediaIndicatorDismissDelayPhase2;
} else {
blocked_indicator_delay = kBlockedMediaIndicatorDismissDelay;
}
media_blocked_indicator_timer_[type].Start(
FROM_HERE, blocked_indicator_delay,
base::BindOnce(&PageSpecificContentSettings::ResetMediaBlockedState,
weak_factory_.GetWeakPtr(), type,
true));
}
void PageSpecificContentSettings::ResetMediaBlockedState(
ContentSettingsType type,
bool update_indicators) {
media_blocked_indicator_timer_.erase(type);
if (type == ContentSettingsType::MEDIASTREAM_MIC) {
microphone_camera_state_.Remove(kMicrophoneBlocked);
microphone_camera_state_.Remove(kMicrophoneAccessed);
} else {
microphone_camera_state_.Remove(kCameraBlocked);
microphone_camera_state_.Remove(kCameraAccessed);
}
if (update_indicators) {
MaybeUpdateLocationBar();
}
}
void PageSpecificContentSettings::OnRegisteredForAutoPictureInPictureChanged() {
MaybeUpdateLocationBar();
}
void PageSpecificContentSettings::MaybeNotifySiteDataObservers(
const AccessDetails& access_details) {
if (IsEmbeddedPage())
return;
if (IsPagePrerendering()) {
updates_queued_during_prerender_->site_data_accessed = true;
updates_queued_during_prerender_->access_details = access_details;
return;
}
WebContentsHandler::FromWebContents(GetWebContents())
->NotifySiteDataObservers(access_details);
}
void PageSpecificContentSettings::MaybeUpdateLocationBar() {
if (IsEmbeddedPage())
return;
if (IsPagePrerendering())
return;
delegate_->UpdateLocationBar();
for (PermissionUsageObserver& observer : permission_usage_observers_) {
observer.OnPermissionUsageChange();
}
}
content::WebContents* PageSpecificContentSettings::GetWebContents() const {
return content::WebContents::FromRenderFrameHost(&page().GetMainDocument());
}
PageSpecificContentSettings*
PageSpecificContentSettings::MaybeGetSyncedSettingsForPictureInPicture() {
content::WebContents* web_contents =
delegate_->MaybeGetSyncedWebContentsForPictureInPicture(GetWebContents());
if (web_contents)
return GetForFrame(web_contents->GetPrimaryMainFrame());
return nullptr;
}
PAGE_USER_DATA_KEY_IMPL(PageSpecificContentSettings);
}