OLD | NEW |
| (Empty) |
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.h" | |
6 | |
7 #include "base/command_line.h" | |
8 #include "content/browser/browsing_instance.h" | |
9 #include "content/browser/child_process_security_policy.h" | |
10 #include "content/browser/renderer_host/render_process_host_impl.h" | |
11 #include "content/public/browser/content_browser_client.h" | |
12 #include "content/public/browser/notification_service.h" | |
13 #include "content/public/browser/notification_types.h" | |
14 #include "content/public/browser/render_process_host_factory.h" | |
15 #include "content/public/common/content_switches.h" | |
16 #include "content/public/common/url_constants.h" | |
17 #include "net/base/registry_controlled_domain.h" | |
18 | |
19 static bool IsURLSameAsAnySiteInstance(const GURL& url) { | |
20 if (!url.is_valid()) | |
21 return false; | |
22 | |
23 // We treat javascript: as the same site as any URL since it is actually | |
24 // a modifier on existing pages. | |
25 if (url.SchemeIs(chrome::kJavaScriptScheme)) | |
26 return true; | |
27 | |
28 return | |
29 content::GetContentClient()->browser()->IsURLSameAsAnySiteInstance(url); | |
30 } | |
31 | |
32 int32 SiteInstance::next_site_instance_id_ = 1; | |
33 | |
34 SiteInstance::SiteInstance(BrowsingInstance* browsing_instance) | |
35 : id_(next_site_instance_id_++), | |
36 browsing_instance_(browsing_instance), | |
37 render_process_host_factory_(NULL), | |
38 process_(NULL), | |
39 has_site_(false) { | |
40 DCHECK(browsing_instance); | |
41 | |
42 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, | |
43 content::NotificationService::AllBrowserContextsAndSources()); | |
44 } | |
45 | |
46 SiteInstance::~SiteInstance() { | |
47 content::GetContentClient()->browser()->SiteInstanceDeleting(this); | |
48 | |
49 // Now that no one is referencing us, we can safely remove ourselves from | |
50 // the BrowsingInstance. Any future visits to a page from this site | |
51 // (within the same BrowsingInstance) can safely create a new SiteInstance. | |
52 if (has_site_) | |
53 browsing_instance_->UnregisterSiteInstance(this); | |
54 } | |
55 | |
56 bool SiteInstance::HasProcess() const { | |
57 return (process_ != NULL); | |
58 } | |
59 | |
60 content::RenderProcessHost* SiteInstance::GetProcess() { | |
61 // TODO(erikkay) It would be nice to ensure that the renderer type had been | |
62 // properly set before we get here. The default tab creation case winds up | |
63 // with no site set at this point, so it will default to TYPE_NORMAL. This | |
64 // may not be correct, so we'll wind up potentially creating a process that | |
65 // we then throw away, or worse sharing a process with the wrong process type. | |
66 // See crbug.com/43448. | |
67 | |
68 // Create a new process if ours went away or was reused. | |
69 if (!process_) { | |
70 // See if we should reuse an old process | |
71 if (content::RenderProcessHost::ShouldTryToUseExistingProcessHost()) | |
72 process_ = content::RenderProcessHost::GetExistingProcessHost( | |
73 browsing_instance_->browser_context(), site_); | |
74 | |
75 // Otherwise (or if that fails), create a new one. | |
76 if (!process_) { | |
77 if (render_process_host_factory_) { | |
78 process_ = render_process_host_factory_->CreateRenderProcessHost( | |
79 browsing_instance_->browser_context()); | |
80 } else { | |
81 process_ = | |
82 new RenderProcessHostImpl(browsing_instance_->browser_context()); | |
83 } | |
84 } | |
85 | |
86 content::GetContentClient()->browser()->SiteInstanceGotProcess(this); | |
87 | |
88 if (has_site_) | |
89 LockToOrigin(); | |
90 } | |
91 DCHECK(process_); | |
92 | |
93 return process_; | |
94 } | |
95 | |
96 void SiteInstance::SetSite(const GURL& url) { | |
97 // A SiteInstance's site should not change. | |
98 // TODO(creis): When following links or script navigations, we can currently | |
99 // render pages from other sites in this SiteInstance. This will eventually | |
100 // be fixed, but until then, we should still not set the site of a | |
101 // SiteInstance more than once. | |
102 DCHECK(!has_site_); | |
103 | |
104 // Remember that this SiteInstance has been used to load a URL, even if the | |
105 // URL is invalid. | |
106 has_site_ = true; | |
107 site_ = GetSiteForURL(browsing_instance_->browser_context(), url); | |
108 | |
109 // Now that we have a site, register it with the BrowsingInstance. This | |
110 // ensures that we won't create another SiteInstance for this site within | |
111 // the same BrowsingInstance, because all same-site pages within a | |
112 // BrowsingInstance can script each other. | |
113 browsing_instance_->RegisterSiteInstance(this); | |
114 | |
115 if (process_) | |
116 LockToOrigin(); | |
117 } | |
118 | |
119 bool SiteInstance::HasRelatedSiteInstance(const GURL& url) { | |
120 return browsing_instance_->HasSiteInstance(url); | |
121 } | |
122 | |
123 SiteInstance* SiteInstance::GetRelatedSiteInstance(const GURL& url) { | |
124 return browsing_instance_->GetSiteInstanceForURL(url); | |
125 } | |
126 | |
127 bool SiteInstance::HasWrongProcessForURL(const GURL& url) const { | |
128 // Having no process isn't a problem, since we'll assign it correctly. | |
129 if (!HasProcess()) | |
130 return false; | |
131 | |
132 // If the URL to navigate to can be associated with any site instance, | |
133 // we want to keep it in the same process. | |
134 if (IsURLSameAsAnySiteInstance(url)) | |
135 return false; | |
136 | |
137 // If the site URL is an extension (e.g., for hosted apps or WebUI) but the | |
138 // process is not (or vice versa), make sure we notice and fix it. | |
139 GURL site_url = GetSiteForURL(browsing_instance_->browser_context(), url); | |
140 return !RenderProcessHostImpl::IsSuitableHost( | |
141 process_, browsing_instance_->browser_context(), site_url); | |
142 } | |
143 | |
144 content::BrowserContext* SiteInstance::GetBrowserContext() const { | |
145 return browsing_instance_->browser_context(); | |
146 } | |
147 | |
148 /*static*/ | |
149 SiteInstance* SiteInstance::CreateSiteInstance( | |
150 content::BrowserContext* browser_context) { | |
151 return new SiteInstance(new BrowsingInstance(browser_context)); | |
152 } | |
153 | |
154 /*static*/ | |
155 SiteInstance* SiteInstance::CreateSiteInstanceForURL( | |
156 content::BrowserContext* browser_context, const GURL& url) { | |
157 // This BrowsingInstance may be deleted if it returns an existing | |
158 // SiteInstance. | |
159 scoped_refptr<BrowsingInstance> instance( | |
160 new BrowsingInstance(browser_context)); | |
161 return instance->GetSiteInstanceForURL(url); | |
162 } | |
163 | |
164 /*static*/ | |
165 GURL SiteInstance::GetSiteForURL(content::BrowserContext* browser_context, | |
166 const GURL& real_url) { | |
167 GURL url = GetEffectiveURL(browser_context, real_url); | |
168 | |
169 // URLs with no host should have an empty site. | |
170 GURL site; | |
171 | |
172 // TODO(creis): For many protocols, we should just treat the scheme as the | |
173 // site, since there is no host. e.g., file:, about:, chrome: | |
174 | |
175 // If the url has a host, then determine the site. | |
176 if (url.has_host()) { | |
177 // Only keep the scheme and registered domain as given by GetOrigin. This | |
178 // may also include a port, which we need to drop. | |
179 site = url.GetOrigin(); | |
180 | |
181 // Remove port, if any. | |
182 if (site.has_port()) { | |
183 GURL::Replacements rep; | |
184 rep.ClearPort(); | |
185 site = site.ReplaceComponents(rep); | |
186 } | |
187 | |
188 // If this URL has a registered domain, we only want to remember that part. | |
189 std::string domain = | |
190 net::RegistryControlledDomainService::GetDomainAndRegistry(url); | |
191 if (!domain.empty()) { | |
192 GURL::Replacements rep; | |
193 rep.SetHostStr(domain); | |
194 site = site.ReplaceComponents(rep); | |
195 } | |
196 } | |
197 return site; | |
198 } | |
199 | |
200 /*static*/ | |
201 bool SiteInstance::IsSameWebSite(content::BrowserContext* browser_context, | |
202 const GURL& real_url1, const GURL& real_url2) { | |
203 GURL url1 = GetEffectiveURL(browser_context, real_url1); | |
204 GURL url2 = GetEffectiveURL(browser_context, real_url2); | |
205 | |
206 // We infer web site boundaries based on the registered domain name of the | |
207 // top-level page and the scheme. We do not pay attention to the port if | |
208 // one is present, because pages served from different ports can still | |
209 // access each other if they change their document.domain variable. | |
210 | |
211 // Some special URLs will match the site instance of any other URL. This is | |
212 // done before checking both of them for validity, since we want these URLs | |
213 // to have the same site instance as even an invalid one. | |
214 if (IsURLSameAsAnySiteInstance(url1) || IsURLSameAsAnySiteInstance(url2)) | |
215 return true; | |
216 | |
217 // If either URL is invalid, they aren't part of the same site. | |
218 if (!url1.is_valid() || !url2.is_valid()) | |
219 return false; | |
220 | |
221 // If the schemes differ, they aren't part of the same site. | |
222 if (url1.scheme() != url2.scheme()) | |
223 return false; | |
224 | |
225 return net::RegistryControlledDomainService::SameDomainOrHost(url1, url2); | |
226 } | |
227 | |
228 /*static*/ | |
229 GURL SiteInstance::GetEffectiveURL(content::BrowserContext* browser_context, | |
230 const GURL& url) { | |
231 return content::GetContentClient()->browser()-> | |
232 GetEffectiveURL(browser_context, url); | |
233 } | |
234 | |
235 void SiteInstance::Observe(int type, | |
236 const content::NotificationSource& source, | |
237 const content::NotificationDetails& details) { | |
238 DCHECK(type == content::NOTIFICATION_RENDERER_PROCESS_TERMINATED); | |
239 content::RenderProcessHost* rph = | |
240 content::Source<content::RenderProcessHost>(source).ptr(); | |
241 if (rph == process_) | |
242 process_ = NULL; | |
243 } | |
244 | |
245 void SiteInstance::LockToOrigin() { | |
246 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); | |
247 if (command_line.HasSwitch(switches::kEnableStrictSiteIsolation)) { | |
248 ChildProcessSecurityPolicy* policy = | |
249 ChildProcessSecurityPolicy::GetInstance(); | |
250 policy->LockToOrigin(process_->GetID(), site_); | |
251 } | |
252 } | |
253 | |
OLD | NEW |