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