#include "content/browser/browsing_instance.h"
#include "base/check_op.h"
#include "base/command_line.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/coop_related_group.h"
#include "content/browser/site_info.h"
#include "content/browser/site_instance_impl.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_or_resource_context.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/site_isolation_policy.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/url_constants.h"
namespace content {
int BrowsingInstance::next_browsing_instance_id_ = 1;
BrowsingInstance::BrowsingInstance(
BrowserContext* browser_context,
const WebExposedIsolationInfo& web_exposed_isolation_info,
bool is_guest,
bool is_fenced,
const scoped_refptr<CoopRelatedGroup>& coop_related_group,
absl::optional<url::Origin> common_coop_origin)
: isolation_context_(
BrowsingInstanceId::FromUnsafeValue(next_browsing_instance_id_++),
BrowserOrResourceContext(browser_context),
is_guest,
is_fenced),
active_contents_count_(0u),
default_site_instance_(nullptr),
web_exposed_isolation_info_(web_exposed_isolation_info),
coop_related_group_(coop_related_group),
common_coop_origin_(common_coop_origin) {
DCHECK(browser_context);
if (!coop_related_group_) {
coop_related_group_ = base::WrapRefCounted<CoopRelatedGroup>(
new CoopRelatedGroup(browser_context, is_guest, is_fenced));
}
DCHECK(coop_related_group_);
coop_related_group_->RegisterBrowsingInstance(this);
}
BrowserContext* BrowsingInstance::GetBrowserContext() const {
return isolation_context_.browser_or_resource_context().ToBrowserContext();
}
bool BrowsingInstance::HasSiteInstance(const SiteInfo& site_info) {
return site_instance_map_.find(site_info) != site_instance_map_.end();
}
scoped_refptr<SiteInstanceImpl> BrowsingInstance::GetSiteInstanceForURL(
const UrlInfo& url_info,
bool allow_default_instance) {
scoped_refptr<SiteInstanceImpl> site_instance =
GetSiteInstanceForURLHelper(url_info, allow_default_instance);
if (site_instance)
return site_instance;
scoped_refptr<SiteInstanceImpl> instance = new SiteInstanceImpl(this);
if (SiteInstanceImpl::ShouldAssignSiteForUrlInfo(url_info) ||
isolation_context_.is_guest()) {
instance->SetSite(url_info);
}
return instance;
}
SiteInfo BrowsingInstance::GetSiteInfoForURL(const UrlInfo& url_info,
bool allow_default_instance) {
scoped_refptr<SiteInstanceImpl> site_instance =
GetSiteInstanceForURLHelper(url_info, allow_default_instance);
if (site_instance)
return site_instance->GetSiteInfo();
return ComputeSiteInfoForURL(url_info);
}
scoped_refptr<SiteInstanceImpl> BrowsingInstance::GetSiteInstanceForSiteInfo(
const SiteInfo& site_info) {
auto i = site_instance_map_.find(site_info);
if (i != site_instance_map_.end())
return i->second;
scoped_refptr<SiteInstanceImpl> instance = new SiteInstanceImpl(this);
instance->SetSite(site_info);
return instance;
}
scoped_refptr<SiteInstanceImpl>
BrowsingInstance::GetCoopRelatedSiteInstanceForURL(
const UrlInfo& url_info,
bool allow_default_instance) {
return coop_related_group_->GetCoopRelatedSiteInstanceForURL(
url_info, allow_default_instance);
}
scoped_refptr<SiteInstanceImpl> BrowsingInstance::GetSiteInstanceForURLHelper(
const UrlInfo& url_info,
bool allow_default_instance) {
const SiteInfo site_info = ComputeSiteInfoForURL(url_info);
auto i = site_instance_map_.find(site_info);
if (i != site_instance_map_.end())
return i->second;
if (allow_default_instance &&
SiteInstanceImpl::CanBePlacedInDefaultSiteInstance(
isolation_context_, url_info.url, site_info)) {
scoped_refptr<SiteInstanceImpl> site_instance =
default_site_instance_.get();
if (!site_instance) {
site_instance = new SiteInstanceImpl(this);
site_instance->SetSiteInfoToDefault(site_info.storage_partition_config());
DCHECK_EQ(default_site_instance_, site_instance.get());
}
site_instance->AddSiteInfoToDefault(site_info);
return site_instance;
}
return nullptr;
}
void BrowsingInstance::RegisterSiteInstance(SiteInstanceImpl* site_instance) {
DCHECK(site_instance->browsing_instance_.get() == this);
DCHECK(site_instance->HasSite());
const StoragePartitionConfig& storage_partition_config =
site_instance->GetSiteInfo().storage_partition_config();
if (storage_partition_config_.has_value()) {
CHECK_EQ(storage_partition_config_.value(), storage_partition_config);
CHECK_EQ(isolation_context_.is_guest(), site_instance->IsGuest());
} else {
storage_partition_config_ = storage_partition_config;
}
if (site_instance->IsDefaultSiteInstance()) {
CHECK(!default_site_instance_);
default_site_instance_ = site_instance;
return;
}
const SiteInfo& site_info = site_instance->GetSiteInfo();
auto i = site_instance_map_.find(site_info);
if (i == site_instance_map_.end()) {
site_instance_map_[site_info] = site_instance;
}
}
void BrowsingInstance::UnregisterSiteInstance(SiteInstanceImpl* site_instance) {
DCHECK(site_instance->browsing_instance_.get() == this);
DCHECK(site_instance->HasSite());
if (site_instance == default_site_instance_) {
default_site_instance_ = nullptr;
}
auto i = site_instance_map_.find(site_instance->GetSiteInfo());
if (i != site_instance_map_.end() && i->second == site_instance) {
site_instance_map_.erase(i);
}
}
BrowsingInstanceId BrowsingInstance::NextBrowsingInstanceId() {
return BrowsingInstanceId::FromUnsafeValue(next_browsing_instance_id_);
}
BrowsingInstance::~BrowsingInstance() {
DCHECK(site_instance_map_.empty());
DCHECK_EQ(0u, active_contents_count_);
DCHECK(!default_site_instance_);
ChildProcessSecurityPolicyImpl* policy =
ChildProcessSecurityPolicyImpl::GetInstance();
policy->RemoveOptInIsolatedOriginsForBrowsingInstance(
isolation_context_.browsing_instance_id());
coop_related_group_->UnregisterBrowsingInstance(this);
}
SiteInfo BrowsingInstance::ComputeSiteInfoForURL(
const UrlInfo& url_info) const {
if (url_info.storage_partition_config.has_value() &&
storage_partition_config_.has_value()) {
CHECK_EQ(storage_partition_config_.value(),
url_info.storage_partition_config.value());
}
UrlInfo url_info_with_partition =
url_info.storage_partition_config.has_value()
? url_info
: UrlInfo(UrlInfoInit(url_info).WithStoragePartitionConfig(
storage_partition_config_));
DCHECK(WebExposedIsolationInfo::AreCompatible(
url_info.web_exposed_isolation_info, web_exposed_isolation_info_));
url_info_with_partition.web_exposed_isolation_info =
url_info.web_exposed_isolation_info.value_or(web_exposed_isolation_info_);
return SiteInfo::Create(isolation_context_, url_info_with_partition);
}
int BrowsingInstance::EstimateOriginAgentClusterOverhead() {
DCHECK(SiteIsolationPolicy::IsProcessIsolationForOriginAgentClusterEnabled());
std::set<SiteInfo> site_info_set;
std::set<SiteInfo> site_info_set_no_oac;
for (auto& entry : site_instance_map_) {
const SiteInfo& site_info = entry.first;
GURL process_lock_url = site_info.process_lock_url();
if (!process_lock_url.SchemeIs(url::kHttpsScheme))
continue;
site_info_set.insert(site_info);
site_info_set_no_oac.insert(
site_info.GetNonOriginKeyedEquivalentForMetrics(isolation_context_));
}
DCHECK_GE(site_info_set.size(), site_info_set_no_oac.size());
int result = site_info_set.size() - site_info_set_no_oac.size();
return result;
}
CoopRelatedGroupId BrowsingInstance::GetCoopRelatedGroupId() {
return coop_related_group_->GetId();
}
size_t BrowsingInstance::GetCoopRelatedGroupActiveContentsCount() {
return coop_related_group_->active_contents_count();
}
void BrowsingInstance::IncrementActiveContentsCount() {
active_contents_count_++;
coop_related_group_->increment_active_contents_count();
}
void BrowsingInstance::DecrementActiveContentsCount() {
DCHECK_LT(0u, active_contents_count_);
active_contents_count_--;
coop_related_group_->decrement_active_contents_count();
}
}