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 20 namespace content { 21 22 const RenderProcessHostFactory* 23 SiteInstanceImpl::g_render_process_host_factory_ = NULL; 24 int32 SiteInstanceImpl::next_site_instance_id_ = 1; 25 26 SiteInstanceImpl::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 35 SiteInstanceImpl::~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 49 int32 SiteInstanceImpl::GetId() { 50 return id_; 51 } 52 53 bool 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 70 RenderProcessHost* 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 bool supports_browser_plugin = GetContentClient()->browser()-> 108 SupportsBrowserPlugin(browser_context, site_); 109 process_ = new RenderProcessHostImpl(browser_context, 110 partition, 111 supports_browser_plugin, 112 site_.SchemeIs(kGuestScheme)); 113 } 114 } 115 CHECK(process_); 116 process_->AddObserver(this); 117 118 // If we are using process-per-site, we need to register this process 119 // for the current site so that we can find it again. (If no site is set 120 // at this time, we will register it in SetSite().) 121 if (use_process_per_site) { 122 RenderProcessHostImpl::RegisterProcessHostForSite(browser_context, 123 process_, site_); 124 } 125 126 GetContentClient()->browser()->SiteInstanceGotProcess(this); 127 128 if (has_site_) 129 LockToOrigin(); 130 } 131 DCHECK(process_); 132 133 return process_; 134 } 135 136 void SiteInstanceImpl::SetSite(const GURL& url) { 137 // A SiteInstance's site should not change. 138 // TODO(creis): When following links or script navigations, we can currently 139 // render pages from other sites in this SiteInstance. This will eventually 140 // be fixed, but until then, we should still not set the site of a 141 // SiteInstance more than once. 142 DCHECK(!has_site_); 143 144 // Remember that this SiteInstance has been used to load a URL, even if the 145 // URL is invalid. 146 has_site_ = true; 147 BrowserContext* browser_context = browsing_instance_->browser_context(); 148 site_ = GetSiteForURL(browser_context, url); 149 150 // Now that we have a site, register it with the BrowsingInstance. This 151 // ensures that we won't create another SiteInstance for this site within 152 // the same BrowsingInstance, because all same-site pages within a 153 // BrowsingInstance can script each other. 154 browsing_instance_->RegisterSiteInstance(this); 155 156 if (process_) { 157 LockToOrigin(); 158 159 // Ensure the process is registered for this site if necessary. 160 if (RenderProcessHost::ShouldUseProcessPerSite(browser_context, site_)) { 161 RenderProcessHostImpl::RegisterProcessHostForSite( 162 browser_context, process_, site_); 163 } 164 } 165 } 166 167 const GURL& SiteInstanceImpl::GetSiteURL() const { 168 return site_; 169 } 170 171 bool SiteInstanceImpl::HasSite() const { 172 return has_site_; 173 } 174 175 bool SiteInstanceImpl::HasRelatedSiteInstance(const GURL& url) { 176 return browsing_instance_->HasSiteInstance(url); 177 } 178 179 SiteInstance* SiteInstanceImpl::GetRelatedSiteInstance(const GURL& url) { 180 return browsing_instance_->GetSiteInstanceForURL(url); 181 } 182 183 bool SiteInstanceImpl::IsRelatedSiteInstance(const SiteInstance* instance) { 184 return browsing_instance_.get() == static_cast<const SiteInstanceImpl*>( 185 instance)->browsing_instance_.get(); 186 } 187 188 bool SiteInstanceImpl::HasWrongProcessForURL(const GURL& url) { 189 // Having no process isn't a problem, since we'll assign it correctly. 190 // Note that HasProcess() may return true if process_ is null, in 191 // process-per-site cases where there's an existing process available. 192 // We want to use such a process in the IsSuitableHost check, so we 193 // may end up assigning process_ in the GetProcess() call below. 194 if (!HasProcess()) 195 return false; 196 197 // If the URL to navigate to can be associated with any site instance, 198 // we want to keep it in the same process. 199 if (IsRendererDebugURL(url)) 200 return false; 201 202 // If the site URL is an extension (e.g., for hosted apps or WebUI) but the 203 // process is not (or vice versa), make sure we notice and fix it. 204 GURL site_url = GetSiteForURL(browsing_instance_->browser_context(), url); 205 return !RenderProcessHostImpl::IsSuitableHost( 206 GetProcess(), browsing_instance_->browser_context(), site_url); 207 } 208 209 void SiteInstanceImpl::set_render_process_host_factory( 210 const RenderProcessHostFactory* rph_factory) { 211 g_render_process_host_factory_ = rph_factory; 212 } 213 214 BrowserContext* SiteInstanceImpl::GetBrowserContext() const { 215 return browsing_instance_->browser_context(); 216 } 217 218 /*static*/ 219 SiteInstance* SiteInstance::Create(BrowserContext* browser_context) { 220 return new SiteInstanceImpl(new BrowsingInstance(browser_context)); 221 } 222 223 /*static*/ 224 SiteInstance* SiteInstance::CreateForURL(BrowserContext* browser_context, 225 const GURL& url) { 226 // This BrowsingInstance may be deleted if it returns an existing 227 // SiteInstance. 228 scoped_refptr<BrowsingInstance> instance( 229 new BrowsingInstance(browser_context)); 230 return instance->GetSiteInstanceForURL(url); 231 } 232 233 /*static*/ 234 bool SiteInstance::IsSameWebSite(BrowserContext* browser_context, 235 const GURL& real_url1, 236 const GURL& real_url2) { 237 GURL url1 = SiteInstanceImpl::GetEffectiveURL(browser_context, real_url1); 238 GURL url2 = SiteInstanceImpl::GetEffectiveURL(browser_context, real_url2); 239 240 // We infer web site boundaries based on the registered domain name of the 241 // top-level page and the scheme. We do not pay attention to the port if 242 // one is present, because pages served from different ports can still 243 // access each other if they change their document.domain variable. 244 245 // Some special URLs will match the site instance of any other URL. This is 246 // done before checking both of them for validity, since we want these URLs 247 // to have the same site instance as even an invalid one. 248 if (IsRendererDebugURL(url1) || IsRendererDebugURL(url2)) 249 return true; 250 251 // If either URL is invalid, they aren't part of the same site. 252 if (!url1.is_valid() || !url2.is_valid()) 253 return false; 254 255 // If the schemes differ, they aren't part of the same site. 256 if (url1.scheme() != url2.scheme()) 257 return false; 258 259 return net::registry_controlled_domains::SameDomainOrHost( 260 url1, 261 url2, 262 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); 263 } 264 265 /*static*/ 266 GURL SiteInstance::GetSiteForURL(BrowserContext* browser_context, 267 const GURL& real_url) { 268 // TODO(fsamuel, creis): For some reason appID is not recognized as a host. 269 if (real_url.SchemeIs(kGuestScheme)) 270 return real_url; 271 272 GURL url = SiteInstanceImpl::GetEffectiveURL(browser_context, real_url); 273 274 // URLs with no host should have an empty site. 275 GURL site; 276 277 // TODO(creis): For many protocols, we should just treat the scheme as the 278 // site, since there is no host. e.g., file:, about:, chrome: 279 280 // If the url has a host, then determine the site. 281 if (url.has_host()) { 282 // Only keep the scheme and registered domain as given by GetOrigin. This 283 // may also include a port, which we need to drop. 284 site = url.GetOrigin(); 285 286 // Remove port, if any. 287 if (site.has_port()) { 288 GURL::Replacements rep; 289 rep.ClearPort(); 290 site = site.ReplaceComponents(rep); 291 } 292 293 // If this URL has a registered domain, we only want to remember that part. 294 std::string domain = 295 net::registry_controlled_domains::GetDomainAndRegistry( 296 url, 297 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); 298 if (!domain.empty()) { 299 GURL::Replacements rep; 300 rep.SetHostStr(domain); 301 site = site.ReplaceComponents(rep); 302 } 303 } 304 return site; 305 } 306 307 /*static*/ 308 GURL SiteInstanceImpl::GetEffectiveURL(BrowserContext* browser_context, 309 const GURL& url) { 310 return GetContentClient()->browser()-> 311 GetEffectiveURL(browser_context, url); 312 } 313 314 void SiteInstanceImpl::RenderProcessHostDestroyed(RenderProcessHost* host) { 315 DCHECK_EQ(process_, host); 316 process_->RemoveObserver(this); 317 process_ = NULL; 318 } 319 320 void SiteInstanceImpl::LockToOrigin() { 321 // We currently only restrict this process to a particular site if the 322 // --enable-strict-site-isolation or --site-per-process flags are present. 323 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 324 if (command_line.HasSwitch(switches::kEnableStrictSiteIsolation) || 325 command_line.HasSwitch(switches::kSitePerProcess)) { 326 ChildProcessSecurityPolicyImpl* policy = 327 ChildProcessSecurityPolicyImpl::GetInstance(); 328 policy->LockToOrigin(process_->GetID(), site_); 329 } 330 } 331 332 } // namespace content 333