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/site_instance_impl.h" 6 7#include "base/command_line.h" 8#include "content/browser/browsing_instance.h" 9#include "content/browser/child_process_security_policy_impl.h" 10#include "content/browser/frame_host/debug_urls.h" 11#include "content/browser/renderer_host/render_process_host_impl.h" 12#include "content/browser/storage_partition_impl.h" 13#include "content/public/browser/content_browser_client.h" 14#include "content/public/browser/render_process_host_factory.h" 15#include "content/public/browser/web_ui_controller_factory.h" 16#include "content/public/common/content_switches.h" 17#include "content/public/common/url_constants.h" 18#include "net/base/registry_controlled_domains/registry_controlled_domain.h" 19 20namespace content { 21 22const RenderProcessHostFactory* 23 SiteInstanceImpl::g_render_process_host_factory_ = NULL; 24int32 SiteInstanceImpl::next_site_instance_id_ = 1; 25 26SiteInstanceImpl::SiteInstanceImpl(BrowsingInstance* browsing_instance) 27 : id_(next_site_instance_id_++), 28 active_view_count_(0), 29 browsing_instance_(browsing_instance), 30 process_(NULL), 31 has_site_(false) { 32 DCHECK(browsing_instance); 33} 34 35SiteInstanceImpl::~SiteInstanceImpl() { 36 GetContentClient()->browser()->SiteInstanceDeleting(this); 37 38 if (process_) 39 process_->RemoveObserver(this); 40 41 // Now that no one is referencing us, we can safely remove ourselves from 42 // the BrowsingInstance. Any future visits to a page from this site 43 // (within the same BrowsingInstance) can safely create a new SiteInstance. 44 if (has_site_) 45 browsing_instance_->UnregisterSiteInstance( 46 static_cast<SiteInstance*>(this)); 47} 48 49int32 SiteInstanceImpl::GetId() { 50 return id_; 51} 52 53bool SiteInstanceImpl::HasProcess() const { 54 if (process_ != NULL) 55 return true; 56 57 // If we would use process-per-site for this site, also check if there is an 58 // existing process that we would use if GetProcess() were called. 59 BrowserContext* browser_context = 60 browsing_instance_->browser_context(); 61 if (has_site_ && 62 RenderProcessHost::ShouldUseProcessPerSite(browser_context, site_) && 63 RenderProcessHostImpl::GetProcessHostForSite(browser_context, site_)) { 64 return true; 65 } 66 67 return false; 68} 69 70RenderProcessHost* SiteInstanceImpl::GetProcess() { 71 // TODO(erikkay) It would be nice to ensure that the renderer type had been 72 // properly set before we get here. The default tab creation case winds up 73 // with no site set at this point, so it will default to TYPE_NORMAL. This 74 // may not be correct, so we'll wind up potentially creating a process that 75 // we then throw away, or worse sharing a process with the wrong process type. 76 // See crbug.com/43448. 77 78 // Create a new process if ours went away or was reused. 79 if (!process_) { 80 BrowserContext* browser_context = browsing_instance_->browser_context(); 81 82 // If we should use process-per-site mode (either in general or for the 83 // given site), then look for an existing RenderProcessHost for the site. 84 bool use_process_per_site = has_site_ && 85 RenderProcessHost::ShouldUseProcessPerSite(browser_context, site_); 86 if (use_process_per_site) { 87 process_ = RenderProcessHostImpl::GetProcessHostForSite(browser_context, 88 site_); 89 } 90 91 // If not (or if none found), see if we should reuse an existing process. 92 if (!process_ && RenderProcessHostImpl::ShouldTryToUseExistingProcessHost( 93 browser_context, site_)) { 94 process_ = RenderProcessHostImpl::GetExistingProcessHost(browser_context, 95 site_); 96 } 97 98 // Otherwise (or if that fails), create a new one. 99 if (!process_) { 100 if (g_render_process_host_factory_) { 101 process_ = g_render_process_host_factory_->CreateRenderProcessHost( 102 browser_context, this); 103 } else { 104 StoragePartitionImpl* partition = 105 static_cast<StoragePartitionImpl*>( 106 BrowserContext::GetStoragePartition(browser_context, this)); 107 process_ = new RenderProcessHostImpl(browser_context, 108 partition, 109 site_.SchemeIs(kGuestScheme)); 110 } 111 } 112 CHECK(process_); 113 process_->AddObserver(this); 114 115 // If we are using process-per-site, we need to register this process 116 // for the current site so that we can find it again. (If no site is set 117 // at this time, we will register it in SetSite().) 118 if (use_process_per_site) { 119 RenderProcessHostImpl::RegisterProcessHostForSite(browser_context, 120 process_, site_); 121 } 122 123 GetContentClient()->browser()->SiteInstanceGotProcess(this); 124 125 if (has_site_) 126 LockToOrigin(); 127 } 128 DCHECK(process_); 129 130 return process_; 131} 132 133void SiteInstanceImpl::SetSite(const GURL& url) { 134 // A SiteInstance's site should not change. 135 // TODO(creis): When following links or script navigations, we can currently 136 // render pages from other sites in this SiteInstance. This will eventually 137 // be fixed, but until then, we should still not set the site of a 138 // SiteInstance more than once. 139 DCHECK(!has_site_); 140 141 // Remember that this SiteInstance has been used to load a URL, even if the 142 // URL is invalid. 143 has_site_ = true; 144 BrowserContext* browser_context = browsing_instance_->browser_context(); 145 site_ = GetSiteForURL(browser_context, url); 146 147 // Now that we have a site, register it with the BrowsingInstance. This 148 // ensures that we won't create another SiteInstance for this site within 149 // the same BrowsingInstance, because all same-site pages within a 150 // BrowsingInstance can script each other. 151 browsing_instance_->RegisterSiteInstance(this); 152 153 if (process_) { 154 LockToOrigin(); 155 156 // Ensure the process is registered for this site if necessary. 157 if (RenderProcessHost::ShouldUseProcessPerSite(browser_context, site_)) { 158 RenderProcessHostImpl::RegisterProcessHostForSite( 159 browser_context, process_, site_); 160 } 161 } 162} 163 164const GURL& SiteInstanceImpl::GetSiteURL() const { 165 return site_; 166} 167 168bool SiteInstanceImpl::HasSite() const { 169 return has_site_; 170} 171 172bool SiteInstanceImpl::HasRelatedSiteInstance(const GURL& url) { 173 return browsing_instance_->HasSiteInstance(url); 174} 175 176SiteInstance* SiteInstanceImpl::GetRelatedSiteInstance(const GURL& url) { 177 return browsing_instance_->GetSiteInstanceForURL(url); 178} 179 180bool SiteInstanceImpl::IsRelatedSiteInstance(const SiteInstance* instance) { 181 return browsing_instance_.get() == static_cast<const SiteInstanceImpl*>( 182 instance)->browsing_instance_.get(); 183} 184 185size_t SiteInstanceImpl::GetRelatedActiveContentsCount() { 186 return browsing_instance_->active_contents_count(); 187} 188 189bool SiteInstanceImpl::HasWrongProcessForURL(const GURL& url) { 190 // Having no process isn't a problem, since we'll assign it correctly. 191 // Note that HasProcess() may return true if process_ is null, in 192 // process-per-site cases where there's an existing process available. 193 // We want to use such a process in the IsSuitableHost check, so we 194 // may end up assigning process_ in the GetProcess() call below. 195 if (!HasProcess()) 196 return false; 197 198 // If the URL to navigate to can be associated with any site instance, 199 // we want to keep it in the same process. 200 if (IsRendererDebugURL(url)) 201 return false; 202 203 // If the site URL is an extension (e.g., for hosted apps or WebUI) but the 204 // process is not (or vice versa), make sure we notice and fix it. 205 GURL site_url = GetSiteForURL(browsing_instance_->browser_context(), url); 206 return !RenderProcessHostImpl::IsSuitableHost( 207 GetProcess(), browsing_instance_->browser_context(), site_url); 208} 209 210void SiteInstanceImpl::IncrementRelatedActiveContentsCount() { 211 browsing_instance_->increment_active_contents_count(); 212} 213 214void SiteInstanceImpl::DecrementRelatedActiveContentsCount() { 215 browsing_instance_->decrement_active_contents_count(); 216} 217 218void SiteInstanceImpl::set_render_process_host_factory( 219 const RenderProcessHostFactory* rph_factory) { 220 g_render_process_host_factory_ = rph_factory; 221} 222 223BrowserContext* SiteInstanceImpl::GetBrowserContext() const { 224 return browsing_instance_->browser_context(); 225} 226 227/*static*/ 228SiteInstance* SiteInstance::Create(BrowserContext* browser_context) { 229 return new SiteInstanceImpl(new BrowsingInstance(browser_context)); 230} 231 232/*static*/ 233SiteInstance* SiteInstance::CreateForURL(BrowserContext* browser_context, 234 const GURL& url) { 235 // This BrowsingInstance may be deleted if it returns an existing 236 // SiteInstance. 237 scoped_refptr<BrowsingInstance> instance( 238 new BrowsingInstance(browser_context)); 239 return instance->GetSiteInstanceForURL(url); 240} 241 242/*static*/ 243bool SiteInstance::IsSameWebSite(BrowserContext* browser_context, 244 const GURL& real_url1, 245 const GURL& real_url2) { 246 GURL url1 = SiteInstanceImpl::GetEffectiveURL(browser_context, real_url1); 247 GURL url2 = SiteInstanceImpl::GetEffectiveURL(browser_context, real_url2); 248 249 // We infer web site boundaries based on the registered domain name of the 250 // top-level page and the scheme. We do not pay attention to the port if 251 // one is present, because pages served from different ports can still 252 // access each other if they change their document.domain variable. 253 254 // Some special URLs will match the site instance of any other URL. This is 255 // done before checking both of them for validity, since we want these URLs 256 // to have the same site instance as even an invalid one. 257 if (IsRendererDebugURL(url1) || IsRendererDebugURL(url2)) 258 return true; 259 260 // If either URL is invalid, they aren't part of the same site. 261 if (!url1.is_valid() || !url2.is_valid()) 262 return false; 263 264 // If the schemes differ, they aren't part of the same site. 265 if (url1.scheme() != url2.scheme()) 266 return false; 267 268 return net::registry_controlled_domains::SameDomainOrHost( 269 url1, 270 url2, 271 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); 272} 273 274/*static*/ 275GURL SiteInstance::GetSiteForURL(BrowserContext* browser_context, 276 const GURL& real_url) { 277 // TODO(fsamuel, creis): For some reason appID is not recognized as a host. 278 if (real_url.SchemeIs(kGuestScheme)) 279 return real_url; 280 281 GURL url = SiteInstanceImpl::GetEffectiveURL(browser_context, real_url); 282 283 // URLs with no host should have an empty site. 284 GURL site; 285 286 // TODO(creis): For many protocols, we should just treat the scheme as the 287 // site, since there is no host. e.g., file:, about:, chrome: 288 289 // If the url has a host, then determine the site. 290 if (url.has_host()) { 291 // Only keep the scheme and registered domain as given by GetOrigin. This 292 // may also include a port, which we need to drop. 293 site = url.GetOrigin(); 294 295 // Remove port, if any. 296 if (site.has_port()) { 297 GURL::Replacements rep; 298 rep.ClearPort(); 299 site = site.ReplaceComponents(rep); 300 } 301 302 // If this URL has a registered domain, we only want to remember that part. 303 std::string domain = 304 net::registry_controlled_domains::GetDomainAndRegistry( 305 url, 306 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); 307 if (!domain.empty()) { 308 GURL::Replacements rep; 309 rep.SetHostStr(domain); 310 site = site.ReplaceComponents(rep); 311 } 312 } 313 return site; 314} 315 316/*static*/ 317GURL SiteInstanceImpl::GetEffectiveURL(BrowserContext* browser_context, 318 const GURL& url) { 319 return GetContentClient()->browser()-> 320 GetEffectiveURL(browser_context, url); 321} 322 323void SiteInstanceImpl::RenderProcessHostDestroyed(RenderProcessHost* host) { 324 DCHECK_EQ(process_, host); 325 process_->RemoveObserver(this); 326 process_ = NULL; 327} 328 329void SiteInstanceImpl::LockToOrigin() { 330 // We currently only restrict this process to a particular site if the 331 // --enable-strict-site-isolation or --site-per-process flags are present. 332 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 333 if (command_line.HasSwitch(switches::kEnableStrictSiteIsolation) || 334 command_line.HasSwitch(switches::kSitePerProcess)) { 335 ChildProcessSecurityPolicyImpl* policy = 336 ChildProcessSecurityPolicyImpl::GetInstance(); 337 policy->LockToOrigin(process_->GetID(), site_); 338 } 339} 340 341} // namespace content 342