Index: content/browser/loader/cross_site_resource_handler.cc |
diff --git a/content/browser/loader/cross_site_resource_handler.cc b/content/browser/loader/cross_site_resource_handler.cc |
index 6b735f6d4fd08bf7cd78b81682a69235d2239d35..d47e4f6f88fb9338a6bcd504d07bc14dbbbf520d 100644 |
--- a/content/browser/loader/cross_site_resource_handler.cc |
+++ b/content/browser/loader/cross_site_resource_handler.cc |
@@ -8,14 +8,18 @@ |
#include "base/bind.h" |
#include "base/logging.h" |
+#include "content/browser/cross_site_request_manager.h" |
+#include "content/browser/loader/resource_dispatcher_host_impl.h" |
#include "content/browser/loader/resource_request_info_impl.h" |
#include "content/browser/renderer_host/render_view_host_delegate.h" |
#include "content/browser/renderer_host/render_view_host_impl.h" |
#include "content/public/browser/browser_thread.h" |
+#include "content/public/browser/content_browser_client.h" |
#include "content/public/browser/global_request_id.h" |
#include "content/public/browser/resource_controller.h" |
#include "content/public/common/resource_response.h" |
#include "net/http/http_response_headers.h" |
+#include "net/url_request/url_request.h" |
namespace content { |
@@ -23,13 +27,20 @@ namespace { |
void OnCrossSiteResponseHelper(int render_process_id, |
int render_view_id, |
- int request_id) { |
+ const GURL& new_url, |
+ const Referrer& referrer, |
+ int64 frame_id, |
+ const GlobalRequestID& global_request_id) { |
RenderViewHostImpl* rvh = RenderViewHostImpl::FromID(render_process_id, |
render_view_id); |
- if (rvh && rvh->GetDelegate()->GetRendererManagementDelegate()) { |
- rvh->GetDelegate()->GetRendererManagementDelegate()->OnCrossSiteResponse( |
- rvh, GlobalRequestID(render_process_id, request_id)); |
- } |
+ if (!rvh) |
+ return; |
+ RenderViewHostDelegate* delegate = rvh->GetDelegate(); |
+ if (!delegate || !delegate->GetRendererManagementDelegate()) |
+ return; |
+ |
+ delegate->GetRendererManagementDelegate()->OnCrossSiteResponse( |
+ rvh, new_url, referrer, frame_id, global_request_id); |
} |
} // namespace |
@@ -81,6 +92,16 @@ bool CrossSiteResourceHandler::OnResponseStarted( |
ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(request_); |
+ // We will need to swap processes if either (1) a redirect that requires a |
+ // transfer occurred before we got here, or (2) a pending cross-site request |
+ // was already in progress. |
+ bool should_transfer = |
+ GetContentClient()->browser()->ShouldSwapProcessesForRedirect( |
+ info->GetContext(), request_->original_url(), request_->url()); |
+ bool swap_needed = should_transfer || |
+ CrossSiteRequestManager::GetInstance()-> |
+ HasPendingCrossSiteRequest(render_process_host_id_, render_view_id_); |
+ |
// If this is a download, just pass the response through without doing a |
// cross-site check. The renderer will see it is a download and abort the |
// request. |
@@ -89,16 +110,20 @@ bool CrossSiteResourceHandler::OnResponseStarted( |
// page. We should allow the navigation to finish without running the unload |
// handler or swapping in the pending RenderViewHost. |
// |
- // In both cases, the pending RenderViewHost will stick around until the next |
- // cross-site navigation, since we are unable to tell when to destroy it. |
+ // In both cases, any pending RenderViewHost (if one was created for this |
+ // navigation) will stick around until the next cross-site navigation, since |
+ // we are unable to tell when to destroy it. |
// See RenderViewHostManager::RendererAbortedProvisionalLoad. |
- if (info->is_download() || (response->head.headers.get() && |
- response->head.headers->response_code() == 204)) { |
+ if (!swap_needed || info->is_download() || |
+ (response->head.headers.get() && |
+ response->head.headers->response_code() == 204)) { |
return next_handler_->OnResponseStarted(request_id, response, defer); |
} |
- // Tell the renderer to run the onunload event handler. |
- StartCrossSiteTransition(request_id, response); |
+ // Now that we know a swap is needed and we have something to commit, we |
+ // pause to let the UI thread run the unload handler of the previous page |
+ // and set up a transfer if needed. |
+ StartCrossSiteTransition(request_id, response, should_transfer); |
// Defer loading until after the onunload event handler has run. |
did_defer_ = *defer = true; |
@@ -117,19 +142,22 @@ bool CrossSiteResourceHandler::OnResponseCompleted( |
const net::URLRequestStatus& status, |
const std::string& security_info) { |
if (!in_cross_site_transition_) { |
+ // If we've already completed the transition, or we're canceling the |
+ // request, or an error occurred with no cross-process navigation in |
+ // progress, then we should just pass this through. |
if (has_started_response_ || |
- status.status() != net::URLRequestStatus::FAILED) { |
- // We've already completed the transition or we're canceling the request, |
- // so just pass it through. |
+ status.status() != net::URLRequestStatus::FAILED || |
+ !CrossSiteRequestManager::GetInstance()->HasPendingCrossSiteRequest( |
+ render_process_host_id_, render_view_id_)) { |
return next_handler_->OnResponseCompleted(request_id, status, |
security_info); |
} |
- // An error occured, we should wait now for the cross-site transition, |
+ // An error occurred. We should wait now for the cross-process transition, |
// so that the error message (e.g., 404) can be displayed to the user. |
// Also continue with the logic below to remember that we completed |
// during the cross-site transition. |
- StartCrossSiteTransition(request_id, NULL); |
+ StartCrossSiteTransition(request_id, NULL, false); |
} |
// We have to buffer the call until after the transition completes. |
@@ -182,20 +210,39 @@ void CrossSiteResourceHandler::ResumeResponse() { |
// telling the old RenderViewHost to run its onunload handler. |
void CrossSiteResourceHandler::StartCrossSiteTransition( |
int request_id, |
- ResourceResponse* response) { |
+ ResourceResponse* response, |
+ bool should_transfer) { |
in_cross_site_transition_ = true; |
request_id_ = request_id; |
response_ = response; |
// Store this handler on the ExtraRequestInfo, so that RDH can call our |
- // ResumeResponse method when the close ACK is received. |
+ // ResumeResponse method when we are ready to resume. |
ResourceRequestInfoImpl* info = |
ResourceRequestInfoImpl::ForRequest(request_); |
info->set_cross_site_handler(this); |
+ DCHECK_EQ(render_process_host_id_, info->GetChildID()); |
+ DCHECK_EQ(request_id, info->GetRequestID()); |
+ GlobalRequestID global_id(info->GetChildID(), info->GetRequestID()); |
+ |
// Tell the contents responsible for this request that a cross-site response |
// is starting, so that it can tell its old renderer to run its onunload |
- // handler now. We will wait to hear the corresponding ClosePage_ACK. |
+ // handler now. We will wait until the unload is finished and (if a transfer |
+ // is needed) for the new renderer's request to arrive. |
+ GURL new_url; |
+ Referrer referrer; |
+ if (should_transfer) { |
+ // This will doom the handler, so we should only do this if we know a |
+ // transfer is required. |
+ // TODO(creis): Look into an alternative if we want to move the policy |
+ // check to the UI thread, in which case we might resume it without |
+ // transferring. |
+ ResourceDispatcherHostImpl::Get()->MarkAsTransferredNavigation(global_id); |
+ |
+ new_url = request_->url(); |
+ referrer = Referrer(GURL(request_->referrer()), info->GetReferrerPolicy()); |
+ } |
BrowserThread::PostTask( |
BrowserThread::UI, |
FROM_HERE, |
@@ -203,10 +250,10 @@ void CrossSiteResourceHandler::StartCrossSiteTransition( |
&OnCrossSiteResponseHelper, |
render_process_host_id_, |
render_view_id_, |
- request_id)); |
- |
- // TODO(creis): If the above call should fail, then we need to notify the IO |
- // thread to proceed anyway, using ResourceDispatcherHost::OnClosePageACK. |
+ new_url, |
+ referrer, |
+ info->GetFrameID(), |
+ global_id)); |
} |
void CrossSiteResourceHandler::ResumeIfDeferred() { |