#include "components/content_settings/renderer/content_settings_agent_impl.h"
#include <utility>
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_number_conversions.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings.mojom.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/content_settings/core/common/content_settings_utils.h"
#include "content/public/child/child_thread.h"
#include "content/public/common/content_features.h"
#include "content/public/common/origin_util.h"
#include "content/public/common/url_constants.h"
#include "content/public/renderer/render_frame.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
#include "third_party/blink/public/platform/browser_interface_broker_proxy.h"
#include "third_party/blink/public/platform/url_conversion.h"
#include "third_party/blink/public/platform/web_security_origin.h"
#include "third_party/blink/public/platform/web_url.h"
#include "third_party/blink/public/web/web_document.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "third_party/blink/public/web/web_local_frame_client.h"
#include "third_party/blink/public/web/web_view.h"
#include "url/gurl.h"
#include "url/origin.h"
#include "url/url_constants.h"
using blink::WebDocument;
using blink::WebFrame;
using blink::WebLocalFrame;
using blink::WebSecurityOrigin;
using blink::WebString;
using blink::WebURL;
using blink::WebView;
namespace content_settings {
namespace {
bool IsFrameWithOpaqueOrigin(WebFrame* frame) {
return frame->GetSecurityOrigin().IsOpaque() ||
frame->Top()->GetSecurityOrigin().IsOpaque();
}
}
ContentSettingsAgentImpl::Delegate::~Delegate() = default;
bool ContentSettingsAgentImpl::Delegate::IsFrameAllowlistedForStorageAccess(
blink::WebFrame* frame) const {
return false;
}
bool ContentSettingsAgentImpl::Delegate::IsSchemeAllowlisted(
const std::string& scheme) {
return false;
}
bool ContentSettingsAgentImpl::Delegate::AllowReadFromClipboard() {
return false;
}
bool ContentSettingsAgentImpl::Delegate::AllowWriteToClipboard() {
return false;
}
ContentSettingsAgentImpl::ContentSettingsAgentImpl(
content::RenderFrame* render_frame,
std::unique_ptr<Delegate> delegate)
: content::RenderFrameObserver(render_frame),
content::RenderFrameObserverTracker<ContentSettingsAgentImpl>(
render_frame),
delegate_(std::move(delegate)) {
DCHECK(delegate_);
ClearBlockedContentSettings();
render_frame->GetWebFrame()->SetContentSettingsClient(this);
render_frame->GetAssociatedInterfaceRegistry()
->AddInterface<mojom::ContentSettingsAgent>(base::BindRepeating(
&ContentSettingsAgentImpl::OnContentSettingsAgentRequest,
base::Unretained(this)));
content::RenderFrame* main_frame = render_frame->GetMainRenderFrame();
if (main_frame && main_frame != render_frame) {
ContentSettingsAgentImpl* parent =
ContentSettingsAgentImpl::Get(main_frame);
allow_running_insecure_content_ = parent->allow_running_insecure_content_;
}
}
ContentSettingsAgentImpl::~ContentSettingsAgentImpl() = default;
mojom::ContentSettingsManager&
ContentSettingsAgentImpl::GetContentSettingsManager() {
if (!content_settings_manager_)
BindContentSettingsManager(&content_settings_manager_);
return *content_settings_manager_;
}
void ContentSettingsAgentImpl::DidBlockContentType(
ContentSettingsType settings_type) {
bool newly_blocked = content_blocked_.insert(settings_type).second;
if (newly_blocked)
GetContentSettingsManager().OnContentBlocked(
render_frame()->GetWebFrame()->GetLocalFrameToken(), settings_type);
}
namespace {
template <typename URL>
ContentSetting GetContentSettingFromRules(
const ContentSettingsForOneType& rules,
const URL& secondary_url) {
if (rules.size() == 1) {
DCHECK(rules[0].primary_pattern == ContentSettingsPattern::Wildcard());
DCHECK(rules[0].secondary_pattern == ContentSettingsPattern::Wildcard());
return rules[0].GetContentSetting();
}
const GURL& secondary_gurl = secondary_url;
for (const auto& rule : rules) {
if (rule.secondary_pattern.Matches(secondary_gurl)) {
return rule.GetContentSetting();
}
}
return CONTENT_SETTING_DEFAULT;
}
}
void ContentSettingsAgentImpl::BindContentSettingsManager(
mojo::Remote<mojom::ContentSettingsManager>* manager) {
DCHECK(!*manager);
content::ChildThread::Get()->BindHostReceiver(
manager->BindNewPipeAndPassReceiver());
}
void ContentSettingsAgentImpl::DidCommitProvisionalLoad(
ui::PageTransition transition) {
ClearBlockedContentSettings();
blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
if (frame->Parent())
return;
#if DCHECK_IS_ON()
GURL url = frame->GetDocument().Url();
DCHECK(frame->GetDocument().GetSecurityOrigin().ToString() == "null" ||
!url.SchemeIs(url::kDataScheme));
#endif
}
void ContentSettingsAgentImpl::OnDestruct() {
delete this;
}
void ContentSettingsAgentImpl::SetAllowRunningInsecureContent() {
allow_running_insecure_content_ = true;
blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
if (!frame->Parent())
frame->StartReload(blink::WebFrameLoadType::kReload);
}
void ContentSettingsAgentImpl::SendRendererContentSettingRules(
const RendererContentSettingRules& renderer_settings) {
content_setting_rules_ = std::make_unique<RendererContentSettingRules>(
std::move(renderer_settings));
}
void ContentSettingsAgentImpl::OnContentSettingsAgentRequest(
mojo::PendingAssociatedReceiver<mojom::ContentSettingsAgent> receiver) {
receivers_.Add(this, std::move(receiver));
}
mojom::ContentSettingsManager::StorageType
ContentSettingsAgentImpl::ConvertToMojoStorageType(StorageType storage_type) {
switch (storage_type) {
case StorageType::kIndexedDB:
return mojom::ContentSettingsManager::StorageType::INDEXED_DB;
case StorageType::kCacheStorage:
return mojom::ContentSettingsManager::StorageType::CACHE;
case StorageType::kWebLocks:
return mojom::ContentSettingsManager::StorageType::WEB_LOCKS;
case StorageType::kFileSystem:
return mojom::ContentSettingsManager::StorageType::FILE_SYSTEM;
case StorageType::kLocalStorage:
return mojom::ContentSettingsManager::StorageType::LOCAL_STORAGE;
case StorageType::kSessionStorage:
return mojom::ContentSettingsManager::StorageType::SESSION_STORAGE;
}
}
void ContentSettingsAgentImpl::AllowStorageAccess(
StorageType storage_type,
base::OnceCallback<void(bool)> callback) {
WebLocalFrame* frame = render_frame()->GetWebFrame();
if (delegate_->IsFrameAllowlistedForStorageAccess(frame)) {
std::move(callback).Run(true);
return;
}
if (IsFrameWithOpaqueOrigin(frame)) {
std::move(callback).Run(false);
return;
}
StoragePermissionsKey key(url::Origin(frame->GetSecurityOrigin()),
storage_type);
const auto permissions = cached_storage_permissions_.find(key);
if (permissions != cached_storage_permissions_.end()) {
std::move(callback).Run(permissions->second);
return;
}
base::OnceCallback<void(bool)> new_cb = base::BindOnce(
[](base::OnceCallback<void(bool)> original_cb, StoragePermissionsKey key,
base::flat_map<StoragePermissionsKey, bool>& cache_map, bool result) {
cache_map[key] = result;
std::move(original_cb).Run(result);
},
std::move(callback), key, std::ref(cached_storage_permissions_));
GetContentSettingsManager().AllowStorageAccess(
frame->GetLocalFrameToken(), ConvertToMojoStorageType(storage_type),
frame->GetSecurityOrigin(), frame->GetDocument().SiteForCookies(),
frame->GetDocument().TopFrameOrigin(), std::move(new_cb));
}
bool ContentSettingsAgentImpl::AllowStorageAccessSync(
StorageType storage_type) {
WebLocalFrame* frame = render_frame()->GetWebFrame();
if (delegate_->IsFrameAllowlistedForStorageAccess(frame)) {
return true;
}
if (IsFrameWithOpaqueOrigin(frame)) {
return false;
}
StoragePermissionsKey key(url::Origin(frame->GetSecurityOrigin()),
storage_type);
const auto permissions = cached_storage_permissions_.find(key);
if (permissions != cached_storage_permissions_.end())
return permissions->second;
SCOPED_UMA_HISTOGRAM_TIMER("ContentSettings.AllowStorageAccessSync");
bool result = false;
GetContentSettingsManager().AllowStorageAccess(
frame->GetLocalFrameToken(), ConvertToMojoStorageType(storage_type),
frame->GetSecurityOrigin(), frame->GetDocument().SiteForCookies(),
frame->GetDocument().TopFrameOrigin(), &result);
cached_storage_permissions_[key] = result;
return result;
}
bool ContentSettingsAgentImpl::AllowReadFromClipboard() {
return delegate_->AllowReadFromClipboard();
}
bool ContentSettingsAgentImpl::AllowWriteToClipboard() {
return delegate_->AllowWriteToClipboard();
}
bool ContentSettingsAgentImpl::AllowRunningInsecureContent(
bool allowed_per_settings,
const blink::WebURL& resource_url) {
if (allowed_per_settings || allow_running_insecure_content_)
return true;
if (content_setting_rules_) {
ContentSetting setting = GetContentSettingFromRules(
content_setting_rules_->mixed_content_rules, GURL());
if (setting == CONTENT_SETTING_ALLOW)
return true;
}
return false;
}
bool ContentSettingsAgentImpl::ShouldAutoupgradeMixedContent() {
if (content_setting_rules_) {
auto setting = GetContentSettingFromRules(
content_setting_rules_->mixed_content_rules, GURL());
return setting != CONTENT_SETTING_ALLOW;
}
return true;
}
RendererContentSettingRules*
ContentSettingsAgentImpl::GetRendererContentSettingRules() {
return content_setting_rules_.get();
}
void ContentSettingsAgentImpl::SetRendererContentSettingRulesForTest(
const RendererContentSettingRules& rules) {
content_setting_rules_ = std::make_unique<RendererContentSettingRules>(rules);
}
void ContentSettingsAgentImpl::DidNotAllowScript() {
DidBlockContentType(ContentSettingsType::JAVASCRIPT);
}
void ContentSettingsAgentImpl::DidNotAllowImage() {
DidBlockContentType(ContentSettingsType::IMAGES);
}
void ContentSettingsAgentImpl::ClearBlockedContentSettings() {
content_blocked_.clear();
cached_storage_permissions_.clear();
}
}