1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/browsing_instance.h"
#include "base/check_op.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/origin_agent_cluster_isolation_state.h"
#include "content/browser/site_info.h"
#include "content/browser/site_instance_group.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 {
// Start the BrowsingInstance ID counter from 1 to avoid a conflict with the
// invalid BrowsingInstanceId value, which is 0 in its underlying IdType32.
int BrowsingInstance::next_browsing_instance_id_ = 1;
BrowsingInstance::BrowsingInstance(
BrowserContext* browser_context,
const WebExposedIsolationInfo& web_exposed_isolation_info,
bool is_guest,
bool is_fenced,
bool is_fixed_storage_partition)
: isolation_context_(
BrowsingInstanceId::FromUnsafeValue(next_browsing_instance_id_++),
BrowserOrResourceContext(browser_context),
is_guest,
is_fenced,
OriginAgentClusterIsolationState::CreateForDefaultIsolation(
browser_context)),
active_contents_count_(0u),
default_site_instance_(nullptr),
default_site_instance_group_(nullptr),
web_exposed_isolation_info_(web_exposed_isolation_info),
is_fixed_storage_partition_(is_fixed_storage_partition) {
DCHECK(browser_context);
if (is_guest) {
CHECK(is_fixed_storage_partition);
}
}
BrowserContext* BrowsingInstance::GetBrowserContext() const {
return isolation_context_.browser_or_resource_context().ToBrowserContext();
}
bool BrowsingInstance::HasSiteInstance(const SiteInfo& site_info) {
return base::Contains(site_instance_map_, site_info);
}
scoped_refptr<SiteInstanceImpl> BrowsingInstance::GetSiteInstanceForURL(
const UrlInfo& url_info,
bool allow_default_instance) {
return GetSiteInstanceForURL(url_info, /*creation_group=*/nullptr,
allow_default_instance);
}
scoped_refptr<SiteInstanceImpl> BrowsingInstance::GetSiteInstanceForURL(
const UrlInfo& url_info,
SiteInstanceGroup* creation_group,
bool allow_default_instance) {
scoped_refptr<SiteInstanceImpl> site_instance =
GetSiteInstanceForURLHelper(url_info, allow_default_instance);
if (site_instance)
return site_instance;
// No current SiteInstance for this site, so let's create one.
scoped_refptr<SiteInstanceImpl> instance = new SiteInstanceImpl(this);
// Set the site of this new SiteInstance, which will register it with us.
// Some URLs should leave the SiteInstance's site unassigned, though if
// `instance` is for a guest, we should always set the site to ensure that it
// carries guest information contained within SiteInfo.
if (SiteInstanceImpl::ShouldAssignSiteForUrlInfo(url_info) ||
isolation_context_.is_guest()) {
instance->SetSite(url_info);
}
// Add the new SiteInstance to `group`, if it exists.
if (creation_group) {
creation_group->AddSiteInstance(instance.get());
instance->SetSiteInstanceGroup(creation_group);
}
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.get();
scoped_refptr<SiteInstanceImpl> instance = new SiteInstanceImpl(this);
instance->SetSite(site_info);
return instance;
}
scoped_refptr<SiteInstanceImpl>
BrowsingInstance::GetMaybeGroupRelatedSiteInstanceForURL(
const UrlInfo& url_info,
SiteInstanceGroup* creation_group) {
CHECK(creation_group);
scoped_refptr<SiteInstanceImpl> instance = GetSiteInstanceForURL(
url_info, creation_group, /*allow_default_instance=*/false);
return 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.get();
// Check to see if we can use the default SiteInstance for sites that don't
// need to be isolated in their own process.
if (!ShouldUseDefaultSiteInstanceGroup() && allow_default_instance &&
SiteInstanceImpl::CanBePlacedInDefaultSiteInstanceOrGroup(
isolation_context_, url_info.url, site_info)) {
scoped_refptr<SiteInstanceImpl> site_instance =
default_site_instance_.get();
if (!site_instance) {
site_instance = new SiteInstanceImpl(this);
// Note: |default_site_instance_| will get set inside this call
// via RegisterSiteInstance().
site_instance->SetSiteInfoToDefault(site_info.storage_partition_config());
DCHECK_EQ(default_site_instance_, site_instance.get());
}
// Add |site_info| to the set so we can keep track of all the sites the
// the default SiteInstance has been returned for.
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());
// Verify that the SiteInstance's StoragePartitionConfig matches this
// BrowsingInstance's StoragePartitionConfig if it already has one.
const StoragePartitionConfig& storage_partition_config =
site_instance->GetSiteInfo().storage_partition_config();
if (storage_partition_config_.has_value()) {
// We should only use a single StoragePartition within a BrowsingInstance.
// If we're attempting to use multiple, something has gone wrong with the
// logic at upper layers. Similarly, whether this StoragePartition is for
// a guest should remain constant over a BrowsingInstance's lifetime.
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;
}
// Explicitly prevent the default SiteInstance from being added since
// the map is only supposed to contain instances that map to a single site.
if (site_instance->IsDefaultSiteInstance()) {
DCHECK(!ShouldUseDefaultSiteInstanceGroup());
CHECK(!default_site_instance_);
default_site_instance_ = site_instance;
return;
}
const SiteInfo& site_info = site_instance->GetSiteInfo();
// Only register if we don't have a SiteInstance for this site already.
// It's possible to have two SiteInstances point to the same site if two
// tabs are navigated there at the same time. (We don't call SetSite or
// register them until DidNavigate.) If there is a previously existing
// SiteInstance for this site, we just won't register the new one.
auto i = site_instance_map_.find(site_info);
if (i == site_instance_map_.end()) {
// Not previously registered, so register it.
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_) {
// The last reference to the default SiteInstance is being destroyed.
default_site_instance_ = nullptr;
}
// Only unregister the SiteInstance if it is the same one that is registered
// for the site. (It might have been an unregistered SiteInstance. See the
// comments in RegisterSiteInstance.)
auto i = site_instance_map_.find(site_instance->GetSiteInfo());
if (i != site_instance_map_.end() && i->second == site_instance) {
// Matches, so erase it.
site_instance_map_.erase(i);
}
}
// static
BrowsingInstanceId BrowsingInstance::NextBrowsingInstanceId() {
return BrowsingInstanceId::FromUnsafeValue(next_browsing_instance_id_);
}
BrowsingInstance::~BrowsingInstance() {
// We should only be deleted when all of the SiteInstances that refer to
// us are gone.
DCHECK(site_instance_map_.empty());
DCHECK_EQ(0u, active_contents_count_);
DCHECK(!default_site_instance_);
DCHECK(!default_site_instance_group_);
// Remove any origin isolation opt-ins related to this instance.
ChildProcessSecurityPolicyImpl* policy =
ChildProcessSecurityPolicyImpl::GetInstance();
policy->RemoveOptInIsolatedOriginsForBrowsingInstance(
isolation_context_.browsing_instance_id());
}
SiteInfo BrowsingInstance::ComputeSiteInfoForURL(
const UrlInfo& url_info) const {
// If a StoragePartitionConfig is specified in both `url_info` and this
// BrowsingInstance, make sure they match.
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());
}
// If no StoragePartitionConfig was set in `url_info`, create a new UrlInfo
// that inherit's this BrowsingInstance's StoragePartitionConfig.
UrlInfo url_info_with_partition =
url_info.storage_partition_config.has_value()
? url_info
: UrlInfo(UrlInfoInit(url_info).WithStoragePartitionConfig(
storage_partition_config_));
// The WebExposedIsolationInfos must be compatible for this function to make
// sense.
DCHECK(WebExposedIsolationInfo::AreCompatible(
url_info.web_exposed_isolation_info, web_exposed_isolation_info_));
// If the passed in UrlInfo has a null WebExposedIsolationInfo, meaning that
// it is compatible with any isolation state, we reuse the isolation state of
// the BrowsingInstance.
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;
// The following computes an estimate of how many additional processes have
// been created to deal with OriginAgentCluster (OAC) headers. When OAC
// headers forces an additional process, that corresponds to the SiteInfo's
// is_origin_keyed_ flag being set. To compute the estimate, we use the set of
// unique SiteInstances (each represented by a unique SiteInfo) in each
// BrowsingInstance as a proxy for the set of different RenderProcesses. We
// start with the total count of SiteInfos, then we create a new set of
// SiteInfos created by resetting the is_origin_keyed_ flag on each of the
// SiteInfos (along with any corresponding adjustments to the site_url_ and
// process_lock_url_ to reflect the possible conversion from origin to site).
// The assumption here is that SiteInfos that forced a new process due to OAC
// may no longer be unique once these values are reset, and as such the new
// set will have less elements than the original set, with the difference
// being the count of extra SiteInstances due to OAC. There are cases where
// ignoring the OAC header would still result in an extra process, e.g. when
// the SiteInfo's origin appears in the command-line origin isolation list.
//
// The estimate is computed using several simplifying assumptions:
// 1) We only consider HTTPS SiteInfos to compute the additional SiteInfos.
// This assumption should generally be valid, since we don't apply
// is_origin_keyed_ to non-HTTPS schemes.
// 2) We assume that SiteInfos from multiple BrowsingInstances aren't
// coalesced into a single RenderProcess. While this isn't true in general,
// it is difficult in practice to account for, so we don't try to.
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;
}
void BrowsingInstance::IncrementActiveContentsCount() {
active_contents_count_++;
}
void BrowsingInstance::DecrementActiveContentsCount() {
DCHECK_LT(0u, active_contents_count_);
active_contents_count_--;
}
} // namespace content
|