Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(400)

Side by Side Diff: content/browser/loader/cross_site_resource_handler.cc

Issue 464593003: Don't swap out the old RenderFrameHost until the new one commits. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Major rebase Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/loader/cross_site_resource_handler.h" 5 #include "content/browser/loader/cross_site_resource_handler.h"
6 6
7 #include <string> 7 #include <string>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/command_line.h" 10 #include "base/command_line.h"
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "content/browser/appcache/appcache_interceptor.h" 12 #include "content/browser/appcache/appcache_interceptor.h"
13 #include "content/browser/child_process_security_policy_impl.h" 13 #include "content/browser/child_process_security_policy_impl.h"
14 #include "content/browser/cross_site_request_manager.h"
15 #include "content/browser/frame_host/cross_site_transferring_request.h" 14 #include "content/browser/frame_host/cross_site_transferring_request.h"
16 #include "content/browser/frame_host/render_frame_host_impl.h" 15 #include "content/browser/frame_host/render_frame_host_impl.h"
17 #include "content/browser/loader/resource_dispatcher_host_impl.h" 16 #include "content/browser/loader/resource_dispatcher_host_impl.h"
18 #include "content/browser/loader/resource_request_info_impl.h" 17 #include "content/browser/loader/resource_request_info_impl.h"
18 #include "content/browser/site_instance_impl.h"
19 #include "content/browser/transition_request_manager.h" 19 #include "content/browser/transition_request_manager.h"
20 #include "content/public/browser/browser_thread.h" 20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/content_browser_client.h" 21 #include "content/public/browser/content_browser_client.h"
22 #include "content/public/browser/global_request_id.h" 22 #include "content/public/browser/global_request_id.h"
23 #include "content/public/browser/resource_controller.h" 23 #include "content/public/browser/resource_controller.h"
24 #include "content/public/browser/site_instance.h" 24 #include "content/public/browser/site_instance.h"
25 #include "content/public/common/content_switches.h" 25 #include "content/public/common/content_switches.h"
26 #include "content/public/common/resource_response.h" 26 #include "content/public/common/resource_response.h"
27 #include "content/public/common/url_constants.h" 27 #include "content/public/common/url_constants.h"
28 #include "net/http/http_response_headers.h" 28 #include "net/http/http_response_headers.h"
29 #include "net/url_request/url_request.h" 29 #include "net/url_request/url_request.h"
30 30
31 namespace content { 31 namespace content {
32 32
33 namespace { 33 namespace {
34 34
35 bool leak_requests_for_testing_ = false; 35 bool leak_requests_for_testing_ = false;
36 36
37 // The parameters to OnCrossSiteResponseHelper exceed the number of arguments 37 // The parameters to OnCrossSiteResponseHelper exceed the number of arguments
38 // base::Bind supports. 38 // base::Bind supports.
39 struct CrossSiteResponseParams { 39 struct CrossSiteResponseParams {
40 CrossSiteResponseParams( 40 CrossSiteResponseParams(
41 int render_frame_id, 41 int render_frame_id,
42 const GlobalRequestID& global_request_id, 42 const GlobalRequestID& global_request_id,
43 bool is_transfer,
44 const std::vector<GURL>& transfer_url_chain, 43 const std::vector<GURL>& transfer_url_chain,
45 const Referrer& referrer, 44 const Referrer& referrer,
46 PageTransition page_transition, 45 PageTransition page_transition,
47 bool should_replace_current_entry) 46 bool should_replace_current_entry)
48 : render_frame_id(render_frame_id), 47 : render_frame_id(render_frame_id),
49 global_request_id(global_request_id), 48 global_request_id(global_request_id),
50 is_transfer(is_transfer),
51 transfer_url_chain(transfer_url_chain), 49 transfer_url_chain(transfer_url_chain),
52 referrer(referrer), 50 referrer(referrer),
53 page_transition(page_transition), 51 page_transition(page_transition),
54 should_replace_current_entry(should_replace_current_entry) { 52 should_replace_current_entry(should_replace_current_entry) {
55 } 53 }
56 54
57 int render_frame_id; 55 int render_frame_id;
58 GlobalRequestID global_request_id; 56 GlobalRequestID global_request_id;
59 bool is_transfer;
60 std::vector<GURL> transfer_url_chain; 57 std::vector<GURL> transfer_url_chain;
61 Referrer referrer; 58 Referrer referrer;
62 PageTransition page_transition; 59 PageTransition page_transition;
63 bool should_replace_current_entry; 60 bool should_replace_current_entry;
64 }; 61 };
65 62
66 void OnCrossSiteResponseHelper(const CrossSiteResponseParams& params) { 63 void OnCrossSiteResponseHelper(const CrossSiteResponseParams& params) {
67 scoped_ptr<CrossSiteTransferringRequest> cross_site_transferring_request; 64 scoped_ptr<CrossSiteTransferringRequest> cross_site_transferring_request(
68 if (params.is_transfer) { 65 new CrossSiteTransferringRequest(params.global_request_id));
69 cross_site_transferring_request.reset(new CrossSiteTransferringRequest(
70 params.global_request_id));
71 }
72 66
73 RenderFrameHostImpl* rfh = 67 RenderFrameHostImpl* rfh =
74 RenderFrameHostImpl::FromID(params.global_request_id.child_id, 68 RenderFrameHostImpl::FromID(params.global_request_id.child_id,
75 params.render_frame_id); 69 params.render_frame_id);
76 if (rfh) { 70 if (rfh) {
77 rfh->OnCrossSiteResponse( 71 rfh->OnCrossSiteResponse(
78 params.global_request_id, cross_site_transferring_request.Pass(), 72 params.global_request_id, cross_site_transferring_request.Pass(),
79 params.transfer_url_chain, params.referrer, 73 params.transfer_url_chain, params.referrer,
80 params.page_transition, params.should_replace_current_entry); 74 params.page_transition, params.should_replace_current_entry);
81 } else if (leak_requests_for_testing_ && cross_site_transferring_request) { 75 } else if (leak_requests_for_testing_ && cross_site_transferring_request) {
82 // Some unit tests expect requests to be leaked in this case, so they can 76 // Some unit tests expect requests to be leaked in this case, so they can
83 // pass them along manually. 77 // pass them along manually.
84 cross_site_transferring_request->ReleaseRequest(); 78 cross_site_transferring_request->ReleaseRequest();
85 } 79 }
86 } 80 }
87 81
88 void OnDeferredAfterResponseStartedHelper( 82 void OnDeferredAfterResponseStartedHelper(
89 const GlobalRequestID& global_request_id, 83 const GlobalRequestID& global_request_id,
90 int render_frame_id, 84 int render_frame_id,
91 const TransitionLayerData& transition_data) { 85 const TransitionLayerData& transition_data) {
92 RenderFrameHostImpl* rfh = 86 RenderFrameHostImpl* rfh =
93 RenderFrameHostImpl::FromID(global_request_id.child_id, render_frame_id); 87 RenderFrameHostImpl::FromID(global_request_id.child_id, render_frame_id);
94 if (rfh) 88 if (rfh)
95 rfh->OnDeferredAfterResponseStarted(global_request_id, transition_data); 89 rfh->OnDeferredAfterResponseStarted(global_request_id, transition_data);
96 } 90 }
97 91
92 // Returns whether a transfer is needed by doing a check on the UI thread.
98 bool CheckNavigationPolicyOnUI(GURL url, int process_id, int render_frame_id) { 93 bool CheckNavigationPolicyOnUI(GURL url, int process_id, int render_frame_id) {
94 CHECK(CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess));
nasko 2014/08/19 16:45:23 Thanks for adding that!
99 RenderFrameHostImpl* rfh = 95 RenderFrameHostImpl* rfh =
100 RenderFrameHostImpl::FromID(process_id, render_frame_id); 96 RenderFrameHostImpl::FromID(process_id, render_frame_id);
101 if (!rfh) 97 if (!rfh)
102 return false; 98 return false;
103 99
104 // TODO(nasko): This check is very simplistic and is used temporarily only 100 // TODO(nasko): This check is very simplistic and is used temporarily only
105 // for --site-per-process. It should be updated to match the check performed 101 // for --site-per-process. It should be updated to match the check performed
106 // by RenderFrameHostManager::UpdateStateForNavigate. 102 // by RenderFrameHostManager::UpdateStateForNavigate.
107 return !SiteInstance::IsSameWebSite( 103 return !SiteInstance::IsSameWebSite(
108 rfh->GetSiteInstance()->GetBrowserContext(), 104 rfh->GetSiteInstance()->GetBrowserContext(),
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
169 ResourceResponse* response, 165 ResourceResponse* response,
170 bool* defer) { 166 bool* defer) {
171 // At this point, we know that the response is safe to send back to the 167 // At this point, we know that the response is safe to send back to the
172 // renderer: it is not a download, and it has passed the SSL and safe 168 // renderer: it is not a download, and it has passed the SSL and safe
173 // browsing checks. 169 // browsing checks.
174 // We should not have already started the transition before now. 170 // We should not have already started the transition before now.
175 DCHECK(!in_cross_site_transition_); 171 DCHECK(!in_cross_site_transition_);
176 172
177 ResourceRequestInfoImpl* info = GetRequestInfo(); 173 ResourceRequestInfoImpl* info = GetRequestInfo();
178 174
179 // We will need to swap processes if either (1) a redirect that requires a 175 // We only need to pause the response if a transfer to a different process is
180 // transfer occurred before we got here, or (2) a pending cross-site request 176 // required. Other cross-process navigations can proceed immediately, since
181 // was already in progress. Note that a swap may no longer be needed if we 177 // we run the unload handler at commit time.
182 // transferred back into the original process due to a redirect. 178 // Note that a process swap may no longer be necessary if we transferred back
179 // into the original process due to a redirect.
183 bool should_transfer = 180 bool should_transfer =
184 GetContentClient()->browser()->ShouldSwapProcessesForRedirect( 181 GetContentClient()->browser()->ShouldSwapProcessesForRedirect(
185 info->GetContext(), request()->original_url(), request()->url()); 182 info->GetContext(), request()->original_url(), request()->url());
186 183
187 // When the --site-per-process flag is passed, we transfer processes for 184 // When the --site-per-process flag is passed, we transfer processes for
188 // cross-site navigations. This is skipped if a transfer is already required 185 // cross-site navigations. This is skipped if a transfer is already required
189 // or for WebUI processes for now, since pages like the NTP host multiple 186 // or for WebUI processes for now, since pages like the NTP host multiple
190 // cross-site WebUI iframes. 187 // cross-site WebUI iframes.
191 if (!should_transfer && 188 if (!should_transfer &&
192 base::CommandLine::ForCurrentProcess()->HasSwitch( 189 base::CommandLine::ForCurrentProcess()->HasSwitch(
193 switches::kSitePerProcess) && 190 switches::kSitePerProcess) &&
194 !ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings( 191 !ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
195 info->GetChildID())) { 192 info->GetChildID())) {
196 return DeferForNavigationPolicyCheck(info, response, defer); 193 return DeferForNavigationPolicyCheck(info, response, defer);
197 } 194 }
198 195
199 bool swap_needed =
200 should_transfer ||
201 CrossSiteRequestManager::GetInstance()->HasPendingCrossSiteRequest(
202 info->GetChildID(), info->GetRenderFrameID());
203
204 // If this is a download, just pass the response through without doing a 196 // If this is a download, just pass the response through without doing a
205 // cross-site check. The renderer will see it is a download and abort the 197 // cross-site check. The renderer will see it is a download and abort the
206 // request. 198 // request.
207 // 199 //
208 // Similarly, HTTP 204 (No Content) responses leave us showing the previous 200 // Similarly, HTTP 204 (No Content) responses leave us showing the previous
209 // page. We should allow the navigation to finish without running the unload 201 // page. We should allow the navigation to finish without running the unload
210 // handler or swapping in the pending RenderFrameHost. 202 // handler or swapping in the pending RenderFrameHost.
211 // 203 //
212 // In both cases, any pending RenderFrameHost (if one was created for this 204 // In both cases, any pending RenderFrameHost (if one was created for this
213 // navigation) will stick around until the next cross-site navigation, since 205 // navigation) will stick around until the next cross-site navigation, since
214 // we are unable to tell when to destroy it. 206 // we are unable to tell when to destroy it.
215 // See RenderFrameHostManager::RendererAbortedProvisionalLoad. 207 // See RenderFrameHostManager::RendererAbortedProvisionalLoad.
216 // 208 //
217 // TODO(davidben): Unify IsDownload() and is_stream(). Several places need to 209 // TODO(davidben): Unify IsDownload() and is_stream(). Several places need to
218 // check for both and remembering about streams is error-prone. 210 // check for both and remembering about streams is error-prone.
219 if (!swap_needed || info->IsDownload() || info->is_stream() || 211 if (!should_transfer || info->IsDownload() || info->is_stream() ||
220 (response->head.headers.get() && 212 (response->head.headers.get() &&
221 response->head.headers->response_code() == 204)) { 213 response->head.headers->response_code() == 204)) {
222 return next_handler_->OnResponseStarted(response, defer); 214 return next_handler_->OnResponseStarted(response, defer);
223 } 215 }
224 216
225 // Now that we know a swap is needed and we have something to commit, we 217 // Now that we know a transfer is needed and we have something to commit, we
226 // pause to let the UI thread run the unload handler of the previous page 218 // pause to let the UI thread set up the transfer.
227 // and set up a transfer if needed. 219 StartCrossSiteTransition(response);
228 StartCrossSiteTransition(response, should_transfer);
229 220
230 // Defer loading until after the onunload event handler has run. 221 // Defer loading until after the new renderer process has issued a
222 // corresponding request.
231 *defer = true; 223 *defer = true;
232 OnDidDefer(); 224 OnDidDefer();
233 return true; 225 return true;
234 } 226 }
235 227
236 bool CrossSiteResourceHandler::OnNavigationTransitionResponseStarted( 228 bool CrossSiteResourceHandler::OnNavigationTransitionResponseStarted(
237 ResourceResponse* response, 229 ResourceResponse* response,
238 bool* defer, 230 bool* defer,
239 const TransitionLayerData& transition_data) { 231 const TransitionLayerData& transition_data) {
240 ResourceRequestInfoImpl* info = GetRequestInfo(); 232 ResourceRequestInfoImpl* info = GetRequestInfo();
(...skipping 18 matching lines...) Expand all
259 bool defer = false; 251 bool defer = false;
260 if (!OnNormalResponseStarted(response_, &defer)) { 252 if (!OnNormalResponseStarted(response_, &defer)) {
261 controller()->Cancel(); 253 controller()->Cancel();
262 } else if (!defer) { 254 } else if (!defer) {
263 ResumeIfDeferred(); 255 ResumeIfDeferred();
264 } 256 }
265 } 257 }
266 258
267 void CrossSiteResourceHandler::ResumeOrTransfer(bool is_transfer) { 259 void CrossSiteResourceHandler::ResumeOrTransfer(bool is_transfer) {
268 if (is_transfer) { 260 if (is_transfer) {
269 StartCrossSiteTransition(response_, is_transfer); 261 StartCrossSiteTransition(response_);
270 } else { 262 } else {
271 ResumeResponse(); 263 ResumeResponse();
272 } 264 }
273 } 265 }
274 266
275 bool CrossSiteResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { 267 bool CrossSiteResourceHandler::OnReadCompleted(int bytes_read, bool* defer) {
276 CHECK(!in_cross_site_transition_); 268 CHECK(!in_cross_site_transition_);
277 return next_handler_->OnReadCompleted(bytes_read, defer); 269 return next_handler_->OnReadCompleted(bytes_read, defer);
278 } 270 }
279 271
280 void CrossSiteResourceHandler::OnResponseCompleted( 272 void CrossSiteResourceHandler::OnResponseCompleted(
281 const net::URLRequestStatus& status, 273 const net::URLRequestStatus& status,
282 const std::string& security_info, 274 const std::string& security_info,
283 bool* defer) { 275 bool* defer) {
284 if (!in_cross_site_transition_) { 276 if (!in_cross_site_transition_) {
285 ResourceRequestInfoImpl* info = GetRequestInfo(); 277 // If we're not transferring, then we should pass this through.
286 // If we've already completed the transition, or we're canceling the 278 next_handler_->OnResponseCompleted(status, security_info, defer);
287 // request, or an error occurred with no cross-process navigation in 279 return;
288 // progress, then we should just pass this through.
289 if (has_started_response_ ||
290 status.status() != net::URLRequestStatus::FAILED ||
291 !CrossSiteRequestManager::GetInstance()->HasPendingCrossSiteRequest(
292 info->GetChildID(), info->GetRenderFrameID())) {
293 next_handler_->OnResponseCompleted(status, security_info, defer);
294 return;
295 }
296
297 // An error occurred. We should wait now for the cross-process transition,
298 // so that the error message (e.g., 404) can be displayed to the user.
299 // Also continue with the logic below to remember that we completed
300 // during the cross-site transition.
301 StartCrossSiteTransition(NULL, false);
302 } 280 }
303 281
304 // We have to buffer the call until after the transition completes. 282 // We have to buffer the call until after the transition completes.
305 completed_during_transition_ = true; 283 completed_during_transition_ = true;
306 completed_status_ = status; 284 completed_status_ = status;
307 completed_security_info_ = security_info; 285 completed_security_info_ = security_info;
308 286
309 // Defer to tell RDH not to notify the world or clean up the pending request. 287 // Defer to tell RDH not to notify the world or clean up the pending request.
310 // We will do so in ResumeResponse. 288 // We will do so in ResumeResponse.
311 *defer = true; 289 *defer = true;
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
346 ResumeIfDeferred(); 324 ResumeIfDeferred();
347 } 325 }
348 } 326 }
349 327
350 // static 328 // static
351 void CrossSiteResourceHandler::SetLeakRequestsForTesting( 329 void CrossSiteResourceHandler::SetLeakRequestsForTesting(
352 bool leak_requests_for_testing) { 330 bool leak_requests_for_testing) {
353 leak_requests_for_testing_ = leak_requests_for_testing; 331 leak_requests_for_testing_ = leak_requests_for_testing;
354 } 332 }
355 333
356 // Prepare to render the cross-site response in a new RenderFrameHost, by 334 // Prepare to transfer the response to a new RenderFrameHost.
357 // telling the old RenderFrameHost to run its onunload handler.
358 void CrossSiteResourceHandler::StartCrossSiteTransition( 335 void CrossSiteResourceHandler::StartCrossSiteTransition(
359 ResourceResponse* response, 336 ResourceResponse* response) {
360 bool should_transfer) {
361 in_cross_site_transition_ = true; 337 in_cross_site_transition_ = true;
362 response_ = response; 338 response_ = response;
363 339
364 // Store this handler on the ExtraRequestInfo, so that RDH can call our 340 // Store this handler on the ExtraRequestInfo, so that RDH can call our
365 // ResumeResponse method when we are ready to resume. 341 // ResumeResponse method when we are ready to resume.
366 ResourceRequestInfoImpl* info = GetRequestInfo(); 342 ResourceRequestInfoImpl* info = GetRequestInfo();
367 info->set_cross_site_handler(this); 343 info->set_cross_site_handler(this);
368 344
369 GlobalRequestID global_id(info->GetChildID(), info->GetRequestID()); 345 GlobalRequestID global_id(info->GetChildID(), info->GetRequestID());
370 346
371 // Tell the contents responsible for this request that a cross-site response 347 // Tell the contents responsible for this request that a cross-site response
372 // is starting, so that it can tell its old renderer to run its onunload 348 // is starting, so that it can tell its old renderer to run its onunload
373 // handler now. We will wait until the unload is finished and (if a transfer 349 // handler now. We will wait until the unload is finished and (if a transfer
374 // is needed) for the new renderer's request to arrive. 350 // is needed) for the new renderer's request to arrive.
375 // The |transfer_url_chain| contains any redirect URLs that have already 351 // The |transfer_url_chain| contains any redirect URLs that have already
376 // occurred, plus the destination URL at the end. 352 // occurred, plus the destination URL at the end.
377 std::vector<GURL> transfer_url_chain; 353 std::vector<GURL> transfer_url_chain;
378 Referrer referrer; 354 Referrer referrer;
379 int render_frame_id = info->GetRenderFrameID(); 355 int render_frame_id = info->GetRenderFrameID();
380 if (should_transfer) { 356 transfer_url_chain = request()->url_chain();
381 transfer_url_chain = request()->url_chain(); 357 referrer = Referrer(GURL(request()->referrer()), info->GetReferrerPolicy());
382 referrer = Referrer(GURL(request()->referrer()), info->GetReferrerPolicy());
383 358
384 AppCacheInterceptor::PrepareForCrossSiteTransfer( 359 AppCacheInterceptor::PrepareForCrossSiteTransfer(
385 request(), global_id.child_id); 360 request(), global_id.child_id);
386 ResourceDispatcherHostImpl::Get()->MarkAsTransferredNavigation(global_id); 361 ResourceDispatcherHostImpl::Get()->MarkAsTransferredNavigation(global_id);
387 } 362
388 BrowserThread::PostTask( 363 BrowserThread::PostTask(
389 BrowserThread::UI, 364 BrowserThread::UI,
390 FROM_HERE, 365 FROM_HERE,
391 base::Bind( 366 base::Bind(
392 &OnCrossSiteResponseHelper, 367 &OnCrossSiteResponseHelper,
393 CrossSiteResponseParams(render_frame_id, 368 CrossSiteResponseParams(render_frame_id,
394 global_id, 369 global_id,
395 should_transfer,
396 transfer_url_chain, 370 transfer_url_chain,
397 referrer, 371 referrer,
398 info->GetPageTransition(), 372 info->GetPageTransition(),
399 info->should_replace_current_entry()))); 373 info->should_replace_current_entry())));
400 } 374 }
401 375
402 bool CrossSiteResourceHandler::DeferForNavigationPolicyCheck( 376 bool CrossSiteResourceHandler::DeferForNavigationPolicyCheck(
403 ResourceRequestInfoImpl* info, 377 ResourceRequestInfoImpl* info,
404 ResourceResponse* response, 378 ResourceResponse* response,
405 bool* defer) { 379 bool* defer) {
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
438 controller()->Resume(); 412 controller()->Resume();
439 } 413 }
440 } 414 }
441 415
442 void CrossSiteResourceHandler::OnDidDefer() { 416 void CrossSiteResourceHandler::OnDidDefer() {
443 did_defer_ = true; 417 did_defer_ = true;
444 request()->LogBlockedBy("CrossSiteResourceHandler"); 418 request()->LogBlockedBy("CrossSiteResourceHandler");
445 } 419 }
446 420
447 } // namespace content 421 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698