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 <string> | |
6 | |
7 #include "content/browser/renderer_host/cross_site_resource_handler.h" | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/logging.h" | |
11 #include "content/browser/renderer_host/render_view_host_delegate.h" | |
12 #include "content/browser/renderer_host/render_view_host_impl.h" | |
13 #include "content/browser/renderer_host/resource_request_info_impl.h" | |
14 #include "content/public/browser/browser_thread.h" | |
15 #include "content/public/browser/resource_controller.h" | |
16 #include "content/public/common/resource_response.h" | |
17 #include "net/http/http_response_headers.h" | |
18 | |
19 namespace content { | |
20 | |
21 namespace { | |
22 | |
23 void OnCrossSiteResponseHelper(int render_process_id, | |
24 int render_view_id, | |
25 int request_id) { | |
26 RenderViewHostImpl* rvh = RenderViewHostImpl::FromID(render_process_id, | |
27 render_view_id); | |
28 if (rvh && rvh->GetDelegate()->GetRendererManagementDelegate()) { | |
29 rvh->GetDelegate()->GetRendererManagementDelegate()->OnCrossSiteResponse( | |
30 render_process_id, request_id); | |
31 } | |
32 } | |
33 | |
34 } // namespace | |
35 | |
36 CrossSiteResourceHandler::CrossSiteResourceHandler( | |
37 scoped_ptr<ResourceHandler> next_handler, | |
38 int render_process_host_id, | |
39 int render_view_id, | |
40 net::URLRequest* request) | |
41 : LayeredResourceHandler(next_handler.Pass()), | |
42 render_process_host_id_(render_process_host_id), | |
43 render_view_id_(render_view_id), | |
44 request_(request), | |
45 has_started_response_(false), | |
46 in_cross_site_transition_(false), | |
47 request_id_(-1), | |
48 completed_during_transition_(false), | |
49 did_defer_(false), | |
50 completed_status_(), | |
51 response_(NULL) { | |
52 } | |
53 | |
54 CrossSiteResourceHandler::~CrossSiteResourceHandler() { | |
55 // Cleanup back-pointer stored on the request info. | |
56 ResourceRequestInfoImpl::ForRequest(request_)->set_cross_site_handler(NULL); | |
57 } | |
58 | |
59 bool CrossSiteResourceHandler::OnRequestRedirected( | |
60 int request_id, | |
61 const GURL& new_url, | |
62 ResourceResponse* response, | |
63 bool* defer) { | |
64 // We should not have started the transition before being redirected. | |
65 DCHECK(!in_cross_site_transition_); | |
66 return next_handler_->OnRequestRedirected( | |
67 request_id, new_url, response, defer); | |
68 } | |
69 | |
70 bool CrossSiteResourceHandler::OnResponseStarted( | |
71 int request_id, | |
72 ResourceResponse* response, | |
73 bool* defer) { | |
74 // At this point, we know that the response is safe to send back to the | |
75 // renderer: it is not a download, and it has passed the SSL and safe | |
76 // browsing checks. | |
77 // We should not have already started the transition before now. | |
78 DCHECK(!in_cross_site_transition_); | |
79 has_started_response_ = true; | |
80 | |
81 ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(request_); | |
82 | |
83 // If this is a download, just pass the response through without doing a | |
84 // cross-site check. The renderer will see it is a download and abort the | |
85 // request. | |
86 // | |
87 // Similarly, HTTP 204 (No Content) responses leave us showing the previous | |
88 // page. We should allow the navigation to finish without running the unload | |
89 // handler or swapping in the pending RenderViewHost. | |
90 // | |
91 // In both cases, the pending RenderViewHost will stick around until the next | |
92 // cross-site navigation, since we are unable to tell when to destroy it. | |
93 // See RenderViewHostManager::RendererAbortedProvisionalLoad. | |
94 if (info->is_download() || | |
95 (response->head.headers && | |
96 response->head.headers->response_code() == 204)) { | |
97 return next_handler_->OnResponseStarted(request_id, response, defer); | |
98 } | |
99 | |
100 // Tell the renderer to run the onunload event handler. | |
101 StartCrossSiteTransition(request_id, response); | |
102 | |
103 // Defer loading until after the onunload event handler has run. | |
104 did_defer_ = *defer = true; | |
105 return true; | |
106 } | |
107 | |
108 bool CrossSiteResourceHandler::OnReadCompleted(int request_id, | |
109 int bytes_read, | |
110 bool* defer) { | |
111 CHECK(!in_cross_site_transition_); | |
112 return next_handler_->OnReadCompleted(request_id, bytes_read, defer); | |
113 } | |
114 | |
115 bool CrossSiteResourceHandler::OnResponseCompleted( | |
116 int request_id, | |
117 const net::URLRequestStatus& status, | |
118 const std::string& security_info) { | |
119 if (!in_cross_site_transition_) { | |
120 if (has_started_response_ || | |
121 status.status() != net::URLRequestStatus::FAILED) { | |
122 // We've already completed the transition or we're canceling the request, | |
123 // so just pass it through. | |
124 return next_handler_->OnResponseCompleted(request_id, status, | |
125 security_info); | |
126 } | |
127 | |
128 // An error occured, we should wait now for the cross-site transition, | |
129 // so that the error message (e.g., 404) can be displayed to the user. | |
130 // Also continue with the logic below to remember that we completed | |
131 // during the cross-site transition. | |
132 StartCrossSiteTransition(request_id, NULL); | |
133 } | |
134 | |
135 // We have to buffer the call until after the transition completes. | |
136 completed_during_transition_ = true; | |
137 completed_status_ = status; | |
138 completed_security_info_ = security_info; | |
139 | |
140 // Return false to tell RDH not to notify the world or clean up the | |
141 // pending request. We will do so in ResumeResponse. | |
142 did_defer_ = true; | |
143 return false; | |
144 } | |
145 | |
146 // We can now send the response to the new renderer, which will cause | |
147 // WebContentsImpl to swap in the new renderer and destroy the old one. | |
148 void CrossSiteResourceHandler::ResumeResponse() { | |
149 DCHECK(request_id_ != -1); | |
150 DCHECK(in_cross_site_transition_); | |
151 in_cross_site_transition_ = false; | |
152 | |
153 if (has_started_response_) { | |
154 // Send OnResponseStarted to the new renderer. | |
155 DCHECK(response_); | |
156 bool defer = false; | |
157 if (!next_handler_->OnResponseStarted(request_id_, response_, &defer)) { | |
158 controller()->Cancel(); | |
159 } else if (!defer) { | |
160 // Unpause the request to resume reading. Any further reads will be | |
161 // directed toward the new renderer. | |
162 ResumeIfDeferred(); | |
163 } | |
164 } | |
165 | |
166 // Remove ourselves from the ExtraRequestInfo. | |
167 ResourceRequestInfoImpl* info = | |
168 ResourceRequestInfoImpl::ForRequest(request_); | |
169 info->set_cross_site_handler(NULL); | |
170 | |
171 // If the response completed during the transition, notify the next | |
172 // event handler. | |
173 if (completed_during_transition_) { | |
174 if (next_handler_->OnResponseCompleted(request_id_, completed_status_, | |
175 completed_security_info_)) { | |
176 ResumeIfDeferred(); | |
177 } | |
178 } | |
179 } | |
180 | |
181 // Prepare to render the cross-site response in a new RenderViewHost, by | |
182 // telling the old RenderViewHost to run its onunload handler. | |
183 void CrossSiteResourceHandler::StartCrossSiteTransition( | |
184 int request_id, | |
185 ResourceResponse* response) { | |
186 in_cross_site_transition_ = true; | |
187 request_id_ = request_id; | |
188 response_ = response; | |
189 | |
190 // Store this handler on the ExtraRequestInfo, so that RDH can call our | |
191 // ResumeResponse method when the close ACK is received. | |
192 ResourceRequestInfoImpl* info = | |
193 ResourceRequestInfoImpl::ForRequest(request_); | |
194 info->set_cross_site_handler(this); | |
195 | |
196 // Tell the contents responsible for this request that a cross-site response | |
197 // is starting, so that it can tell its old renderer to run its onunload | |
198 // handler now. We will wait to hear the corresponding ClosePage_ACK. | |
199 BrowserThread::PostTask( | |
200 BrowserThread::UI, | |
201 FROM_HERE, | |
202 base::Bind( | |
203 &OnCrossSiteResponseHelper, | |
204 render_process_host_id_, | |
205 render_view_id_, | |
206 request_id)); | |
207 | |
208 // TODO(creis): If the above call should fail, then we need to notify the IO | |
209 // thread to proceed anyway, using ResourceDispatcherHost::OnClosePageACK. | |
210 } | |
211 | |
212 void CrossSiteResourceHandler::ResumeIfDeferred() { | |
213 if (did_defer_) { | |
214 did_defer_ = false; | |
215 controller()->Resume(); | |
216 } | |
217 } | |
218 | |
219 } // namespace content | |
OLD | NEW |