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