1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/browsing_instance.h"
6
7 #include "base/check_op.h"
8 #include "base/command_line.h"
9 #include "content/browser/child_process_security_policy_impl.h"
10 #include "content/browser/site_instance_impl.h"
11 #include "content/public/browser/browser_context.h"
12 #include "content/public/browser/browser_or_resource_context.h"
13 #include "content/public/browser/content_browser_client.h"
14 #include "content/public/browser/site_isolation_policy.h"
15 #include "content/public/common/content_features.h"
16 #include "content/public/common/content_switches.h"
17 #include "content/public/common/url_constants.h"
18
19 namespace content {
20
21 // Start the BrowsingInstance ID counter from 1 to avoid a conflict with the
22 // invalid BrowsingInstanceId value, which is 0 in its underlying IdType32.
23 int BrowsingInstance::next_browsing_instance_id_ = 1;
24
BrowsingInstance(BrowserContext * browser_context,const CoopCoepCrossOriginIsolatedInfo & cross_origin_isolated_info)25 BrowsingInstance::BrowsingInstance(
26 BrowserContext* browser_context,
27 const CoopCoepCrossOriginIsolatedInfo& cross_origin_isolated_info)
28 : isolation_context_(
29 BrowsingInstanceId::FromUnsafeValue(next_browsing_instance_id_++),
30 BrowserOrResourceContext(browser_context)),
31 active_contents_count_(0u),
32 default_process_(nullptr),
33 default_site_instance_(nullptr),
34 cross_origin_isolated_info_(cross_origin_isolated_info) {
35 DCHECK(browser_context);
36 }
37
RenderProcessHostDestroyed(RenderProcessHost * host)38 void BrowsingInstance::RenderProcessHostDestroyed(RenderProcessHost* host) {
39 DCHECK_EQ(default_process_, host);
40 // Only clear the default process if the RenderProcessHost object goes away,
41 // not if the renderer process goes away while the RenderProcessHost remains.
42 default_process_->RemoveObserver(this);
43 default_process_ = nullptr;
44 }
45
GetBrowserContext() const46 BrowserContext* BrowsingInstance::GetBrowserContext() const {
47 return isolation_context_.browser_or_resource_context().ToBrowserContext();
48 }
49
SetDefaultProcess(RenderProcessHost * default_process)50 void BrowsingInstance::SetDefaultProcess(RenderProcessHost* default_process) {
51 DCHECK(!default_process_);
52 DCHECK(!default_site_instance_);
53 default_process_ = default_process;
54 default_process_->AddObserver(this);
55 }
56
IsDefaultSiteInstance(const SiteInstanceImpl * site_instance) const57 bool BrowsingInstance::IsDefaultSiteInstance(
58 const SiteInstanceImpl* site_instance) const {
59 return site_instance != nullptr && site_instance == default_site_instance_;
60 }
61
IsSiteInDefaultSiteInstance(const GURL & site_url) const62 bool BrowsingInstance::IsSiteInDefaultSiteInstance(const GURL& site_url) const {
63 return site_url_set_.find(site_url) != site_url_set_.end();
64 }
65
HasSiteInstance(const SiteInfo & site_info)66 bool BrowsingInstance::HasSiteInstance(const SiteInfo& site_info) {
67 return site_instance_map_.find(site_info) != site_instance_map_.end();
68 }
69
GetSiteInstanceForURL(const UrlInfo & url_info,bool allow_default_instance)70 scoped_refptr<SiteInstanceImpl> BrowsingInstance::GetSiteInstanceForURL(
71 const UrlInfo& url_info,
72 bool allow_default_instance) {
73 scoped_refptr<SiteInstanceImpl> site_instance =
74 GetSiteInstanceForURLHelper(url_info, allow_default_instance);
75
76 if (site_instance)
77 return site_instance;
78
79 // No current SiteInstance for this site, so let's create one.
80 scoped_refptr<SiteInstanceImpl> instance = new SiteInstanceImpl(this);
81
82 // Set the site of this new SiteInstance, which will register it with us,
83 // unless this URL should leave the SiteInstance's site unassigned.
84 if (SiteInstance::ShouldAssignSiteForURL(url_info.url))
85 instance->SetSite(url_info);
86 return instance;
87 }
88
GetSiteInfoForURL(const UrlInfo & url_info,bool allow_default_instance)89 SiteInfo BrowsingInstance::GetSiteInfoForURL(const UrlInfo& url_info,
90 bool allow_default_instance) {
91 scoped_refptr<SiteInstanceImpl> site_instance =
92 GetSiteInstanceForURLHelper(url_info, allow_default_instance);
93
94 if (site_instance)
95 return site_instance->GetSiteInfo();
96
97 return ComputeSiteInfoForURL(url_info);
98 }
99
TrySettingDefaultSiteInstance(SiteInstanceImpl * site_instance,const UrlInfo & url_info)100 bool BrowsingInstance::TrySettingDefaultSiteInstance(
101 SiteInstanceImpl* site_instance,
102 const UrlInfo& url_info) {
103 DCHECK(!site_instance->HasSite());
104 const SiteInfo site_info = ComputeSiteInfoForURL(url_info);
105 if (default_site_instance_ ||
106 !SiteInstanceImpl::CanBePlacedInDefaultSiteInstance(
107 isolation_context_, url_info.url, site_info)) {
108 return false;
109 }
110
111 // Note: |default_site_instance_| must be set before SetSite() call to
112 // properly trigger default SiteInstance behavior inside that method.
113 default_site_instance_ = site_instance;
114 site_instance->SetSiteInfoToDefault();
115 site_url_set_.insert(site_info.site_url());
116 return true;
117 }
118
GetSiteInstanceForURLHelper(const UrlInfo & url_info,bool allow_default_instance)119 scoped_refptr<SiteInstanceImpl> BrowsingInstance::GetSiteInstanceForURLHelper(
120 const UrlInfo& url_info,
121 bool allow_default_instance) {
122 const SiteInfo site_info = ComputeSiteInfoForURL(url_info);
123 auto i = site_instance_map_.find(site_info);
124 if (i != site_instance_map_.end())
125 return i->second;
126
127 // Check to see if we can use the default SiteInstance for sites that don't
128 // need to be isolated in their own process.
129 if (allow_default_instance &&
130 SiteInstanceImpl::CanBePlacedInDefaultSiteInstance(
131 isolation_context_, url_info.url, site_info)) {
132 DCHECK(!default_process_);
133 scoped_refptr<SiteInstanceImpl> site_instance = default_site_instance_;
134 if (!site_instance) {
135 site_instance = new SiteInstanceImpl(this);
136
137 // Keep a copy of the pointer so it can be used for other URLs. This is
138 // safe because the SiteInstanceImpl destructor will call
139 // UnregisterSiteInstance() to clear this copy when the last
140 // reference to |site_instance| is destroyed.
141 // Note: This assignment MUST happen before the SetSite() call to ensure
142 // this instance is not added to |site_instance_map_| when SetSite()
143 // calls RegisterSiteInstance().
144 default_site_instance_ = site_instance.get();
145
146 site_instance->SetSiteInfoToDefault();
147 }
148
149 // Add |site_url| to the set so we can keep track of all the sites the
150 // the default SiteInstance has been returned for.
151 site_url_set_.insert(site_info.site_url());
152 return site_instance;
153 }
154
155 return nullptr;
156 }
157
RegisterSiteInstance(SiteInstanceImpl * site_instance)158 void BrowsingInstance::RegisterSiteInstance(SiteInstanceImpl* site_instance) {
159 DCHECK(site_instance->browsing_instance_.get() == this);
160 DCHECK(site_instance->HasSite());
161
162 // Explicitly prevent the |default_site_instance_| from being added since
163 // the map is only supposed to contain instances that map to a single site.
164 if (site_instance == default_site_instance_)
165 return;
166
167 const SiteInfo& site_info = site_instance->GetSiteInfo();
168
169 // Only register if we don't have a SiteInstance for this site already.
170 // It's possible to have two SiteInstances point to the same site if two
171 // tabs are navigated there at the same time. (We don't call SetSite or
172 // register them until DidNavigate.) If there is a previously existing
173 // SiteInstance for this site, we just won't register the new one.
174 auto i = site_instance_map_.find(site_info);
175 if (i == site_instance_map_.end()) {
176 // Not previously registered, so register it.
177 site_instance_map_[site_info] = site_instance;
178 }
179 }
180
UnregisterSiteInstance(SiteInstanceImpl * site_instance)181 void BrowsingInstance::UnregisterSiteInstance(SiteInstanceImpl* site_instance) {
182 DCHECK(site_instance->browsing_instance_.get() == this);
183 DCHECK(site_instance->HasSite());
184
185 if (site_instance == default_site_instance_) {
186 // The last reference to the default SiteInstance is being destroyed.
187 default_site_instance_ = nullptr;
188 }
189
190 // Only unregister the SiteInstance if it is the same one that is registered
191 // for the site. (It might have been an unregistered SiteInstance. See the
192 // comments in RegisterSiteInstance.)
193 auto i = site_instance_map_.find(site_instance->GetSiteInfo());
194 if (i != site_instance_map_.end() && i->second == site_instance) {
195 // Matches, so erase it.
196 site_instance_map_.erase(i);
197 }
198 }
199
200 // static
NextBrowsingInstanceId()201 BrowsingInstanceId BrowsingInstance::NextBrowsingInstanceId() {
202 return BrowsingInstanceId::FromUnsafeValue(next_browsing_instance_id_);
203 }
204
~BrowsingInstance()205 BrowsingInstance::~BrowsingInstance() {
206 // We should only be deleted when all of the SiteInstances that refer to
207 // us are gone.
208 DCHECK(site_instance_map_.empty());
209 DCHECK_EQ(0u, active_contents_count_);
210 DCHECK(!default_site_instance_);
211 if (default_process_)
212 default_process_->RemoveObserver(this);
213
214 // Remove any origin isolation opt-ins related to this instance.
215 ChildProcessSecurityPolicyImpl* policy =
216 ChildProcessSecurityPolicyImpl::GetInstance();
217 policy->RemoveOptInIsolatedOriginsForBrowsingInstance(
218 isolation_context_.browsing_instance_id());
219 }
220
ComputeSiteInfoForURL(const UrlInfo & url_info) const221 SiteInfo BrowsingInstance::ComputeSiteInfoForURL(
222 const UrlInfo& url_info) const {
223 return SiteInstanceImpl::ComputeSiteInfo(isolation_context_, url_info,
224 cross_origin_isolated_info_);
225 }
226
227 } // namespace content
228