site_instance_impl.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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
21namespace content {
22
23static 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(kJavaScriptScheme))
30    return true;
31
32  return url == GURL(kChromeUICrashURL) ||
33         url == GURL(kChromeUIKillURL) ||
34         url == GURL(kChromeUIHangURL) ||
35         url == GURL(kChromeUIShorthangURL);
36}
37
38const RenderProcessHostFactory*
39    SiteInstanceImpl::g_render_process_host_factory_ = NULL;
40int32 SiteInstanceImpl::next_site_instance_id_ = 1;
41
42SiteInstanceImpl::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
54SiteInstanceImpl::~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
65int32 SiteInstanceImpl::GetId() {
66  return id_;
67}
68
69bool 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
86RenderProcessHost* 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_ = new RenderProcessHostImpl(browser_context,
126                                             partition,
127                                             supports_browser_plugin,
128                                             site_.SchemeIs(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
151void 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
182const GURL& SiteInstanceImpl::GetSiteURL() const {
183  return site_;
184}
185
186bool SiteInstanceImpl::HasSite() const {
187  return has_site_;
188}
189
190bool SiteInstanceImpl::HasRelatedSiteInstance(const GURL& url) {
191  return browsing_instance_->HasSiteInstance(url);
192}
193
194SiteInstance* SiteInstanceImpl::GetRelatedSiteInstance(const GURL& url) {
195  return browsing_instance_->GetSiteInstanceForURL(url);
196}
197
198bool SiteInstanceImpl::IsRelatedSiteInstance(const SiteInstance* instance) {
199  return browsing_instance_.get() == static_cast<const SiteInstanceImpl*>(
200                                         instance)->browsing_instance_.get();
201}
202
203bool 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
224void SiteInstanceImpl::set_render_process_host_factory(
225    const RenderProcessHostFactory* rph_factory) {
226  g_render_process_host_factory_ = rph_factory;
227}
228
229BrowserContext* SiteInstanceImpl::GetBrowserContext() const {
230  return browsing_instance_->browser_context();
231}
232
233/*static*/
234SiteInstance* SiteInstance::Create(BrowserContext* browser_context) {
235  return new SiteInstanceImpl(new BrowsingInstance(browser_context));
236}
237
238/*static*/
239SiteInstance* 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*/
249bool 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*/
281GURL 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(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*/
323GURL SiteInstanceImpl::GetEffectiveURL(BrowserContext* browser_context,
324                                       const GURL& url) {
325  return GetContentClient()->browser()->
326      GetEffectiveURL(browser_context, url);
327}
328
329void 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
338void 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