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