OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 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/devtools/devtools_url_request_interceptor.h" | |
6 | |
7 #include "base/memory/ptr_util.h" | |
8 #include "base/strings/stringprintf.h" | |
9 #include "base/supports_user_data.h" | |
10 #include "content/browser/devtools/devtools_agent_host_impl.h" | |
11 #include "content/browser/devtools/devtools_url_interceptor_request_job.h" | |
12 #include "content/browser/devtools/protocol/network_handler.h" | |
13 #include "content/public/browser/browser_context.h" | |
14 #include "content/public/browser/browser_thread.h" | |
15 #include "content/public/browser/devtools_agent_host.h" | |
16 #include "content/public/browser/render_frame_host.h" | |
17 #include "content/public/browser/resource_request_info.h" | |
18 #include "content/public/browser/web_contents.h" | |
19 #include "content/public/browser/web_contents_observer.h" | |
20 #include "net/http/http_request_headers.h" | |
21 #include "net/url_request/url_request.h" | |
22 | |
23 namespace content { | |
24 | |
25 namespace { | |
26 const char kDevToolsURLRequestInterceptorKeyName[] = | |
27 "DevToolsURLRequestInterceptor"; | |
28 | |
29 class DevToolsURLRequestInterceptorUserData | |
30 : public base::SupportsUserData::Data { | |
31 public: | |
32 explicit DevToolsURLRequestInterceptorUserData( | |
33 DevToolsURLRequestInterceptor* devtools_url_request_interceptor) | |
34 : devtools_url_request_interceptor_(devtools_url_request_interceptor) {} | |
35 | |
36 DevToolsURLRequestInterceptor* devtools_url_request_interceptor() const { | |
37 return devtools_url_request_interceptor_; | |
38 } | |
39 | |
40 private: | |
41 DevToolsURLRequestInterceptor* devtools_url_request_interceptor_; | |
42 | |
43 DISALLOW_COPY_AND_ASSIGN(DevToolsURLRequestInterceptorUserData); | |
44 }; | |
45 | |
46 } // namespace | |
47 | |
48 DevToolsURLRequestInterceptor::DevToolsURLRequestInterceptor( | |
49 BrowserContext* browser_context) | |
50 : browser_context_(browser_context), state_(new State()) { | |
51 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
52 browser_context_->SetUserData( | |
53 kDevToolsURLRequestInterceptorKeyName, | |
54 base::MakeUnique<DevToolsURLRequestInterceptorUserData>(this)); | |
55 } | |
56 | |
57 DevToolsURLRequestInterceptor::~DevToolsURLRequestInterceptor() { | |
58 // The BrowserContext owns us, so we don't need to unregister | |
59 // DevToolsURLRequestInterceptorUserData explicitly. | |
60 } | |
61 | |
62 net::URLRequestJob* DevToolsURLRequestInterceptor::MaybeInterceptRequest( | |
63 net::URLRequest* request, | |
64 net::NetworkDelegate* network_delegate) const { | |
65 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
66 | |
67 base::WeakPtr<protocol::NetworkHandler> network_handler; | |
68 if (!state().ShouldInterceptRequest(request, &network_handler)) | |
69 return nullptr; | |
70 | |
71 bool is_redirect; | |
72 std::string intercept_id = state().GetIdForRequest(request, &is_redirect); | |
73 return new DevToolsURLInterceptorRequestJob( | |
74 state().GetWeakPtr(), intercept_id, request, network_delegate, | |
75 network_handler, is_redirect); | |
76 } | |
77 | |
78 net::URLRequestJob* DevToolsURLRequestInterceptor::MaybeInterceptRedirect( | |
79 net::URLRequest* request, | |
80 net::NetworkDelegate* network_delegate, | |
81 const GURL& location) const { | |
82 return nullptr; | |
83 } | |
84 | |
85 net::URLRequestJob* DevToolsURLRequestInterceptor::MaybeInterceptResponse( | |
86 net::URLRequest* request, | |
87 net::NetworkDelegate* network_delegate) const { | |
88 return nullptr; | |
89 } | |
90 | |
91 DevToolsURLRequestInterceptor::State::State() | |
92 : next_id_(0), weak_ptr_factory_(this) {} | |
93 | |
94 DevToolsURLRequestInterceptor::State::~State() {} | |
95 | |
96 void DevToolsURLRequestInterceptor::State::AllowRequest( | |
97 std::string interception_id, | |
98 CommandCallback callback) { | |
99 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
100 DevToolsURLInterceptorRequestJob* job = GetJob(interception_id); | |
101 if (!job) { | |
102 std::move(callback).Run(CommandStatus::UnknownInterceptionId); | |
103 } else { | |
104 std::move(callback).Run(job->AllowRequest()); | |
105 } | |
106 } | |
107 | |
108 void DevToolsURLRequestInterceptor::State::BlockRequest( | |
109 std::string interception_id, | |
110 net::Error error_reason, | |
111 CommandCallback callback) { | |
112 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
113 DevToolsURLInterceptorRequestJob* job = GetJob(interception_id); | |
114 if (!job) { | |
115 std::move(callback).Run(CommandStatus::UnknownInterceptionId); | |
116 } else { | |
117 std::move(callback).Run(job->BlockRequest(error_reason)); | |
118 } | |
119 } | |
120 | |
121 void DevToolsURLRequestInterceptor::State::ModifyRequest( | |
122 std::string interception_id, | |
123 std::unique_ptr<Modifications> modifications, | |
124 CommandCallback callback) { | |
125 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
126 DevToolsURLInterceptorRequestJob* job = GetJob(interception_id); | |
127 if (!job) { | |
128 std::move(callback).Run(CommandStatus::UnknownInterceptionId); | |
129 } else { | |
130 std::move(callback).Run(job->ModifyRequest(std::move(modifications))); | |
131 } | |
132 } | |
133 | |
134 void DevToolsURLRequestInterceptor::State::MockResponse( | |
135 std::string interception_id, | |
136 std::string raw_response, | |
137 CommandCallback callback) { | |
138 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
139 DevToolsURLInterceptorRequestJob* job = GetJob(interception_id); | |
140 if (!job) { | |
141 std::move(callback).Run(CommandStatus::UnknownInterceptionId); | |
142 } else { | |
143 std::move(callback).Run(job->MockResponse(std::move(raw_response))); | |
144 } | |
145 } | |
146 | |
147 bool DevToolsURLRequestInterceptor::State::ShouldInterceptRequest( | |
148 const net::URLRequest* request, | |
149 base::WeakPtr<protocol::NetworkHandler>* network_handler) const { | |
150 const ResourceRequestInfo* resource_request_info = | |
151 ResourceRequestInfo::ForRequest(request); | |
152 if (!resource_request_info) | |
153 return false; | |
154 int frame_tree_node_id = resource_request_info->GetFrameTreeNodeId(); | |
155 if (frame_tree_node_id == -1) { | |
156 // |frame_tree_node_id| is not set for renderer side requests, fall back to | |
157 // the RenderFrameID. | |
158 int render_frame_id = resource_request_info->GetRenderFrameID(); | |
dgozman
2017/05/22 22:27:04
Note that render frame ids clash between different
alex clarke (OOO till 29th)
2017/05/23 15:46:56
There was a bug here, it needed the process id too
| |
159 const auto find_it = intercepted_render_frames_.find(render_frame_id); | |
160 if (find_it == intercepted_render_frames_.end()) | |
161 return false; | |
162 *network_handler = find_it->second; | |
163 } else { | |
164 // |frame_tree_node_id| is set for browser side navigations, so use that | |
165 // because the RenderFrameID isn't known. | |
166 const auto find_it = intercepted_frame_tree_nodes_.find(frame_tree_node_id); | |
167 if (find_it == intercepted_frame_tree_nodes_.end()) | |
168 return false; | |
169 *network_handler = find_it->second; | |
170 } | |
171 | |
172 // We don't want to intercept our own sub requests. | |
173 return sub_requests_.find(request) == sub_requests_.end(); | |
174 } | |
175 | |
176 class DevToolsURLRequestInterceptor::State::InterceptedWebContentsObserver | |
177 : public WebContentsObserver { | |
178 public: | |
179 InterceptedWebContentsObserver( | |
180 WebContents* web_contents, | |
181 base::WeakPtr<DevToolsURLRequestInterceptor::State> state, | |
182 base::WeakPtr<protocol::NetworkHandler> network_handler) | |
183 : WebContentsObserver(web_contents), | |
184 state_(state), | |
185 network_handler_(network_handler) {} | |
186 | |
187 void RenderFrameCreated(RenderFrameHost* render_frame_host) override { | |
188 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
189 BrowserThread::PostTask( | |
190 BrowserThread::IO, FROM_HERE, | |
191 base::BindOnce(&DevToolsURLRequestInterceptor::State:: | |
192 StartInterceptingRequestsInternal, | |
193 state_, render_frame_host->GetRoutingID(), | |
194 render_frame_host->GetFrameTreeNodeId(), | |
195 network_handler_)); | |
196 } | |
197 | |
198 void RenderFrameDeleted(RenderFrameHost* render_frame_host) override { | |
199 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
200 BrowserThread::PostTask( | |
201 BrowserThread::IO, FROM_HERE, | |
202 base::BindOnce(&DevToolsURLRequestInterceptor::State:: | |
203 StopInterceptingRequestsInternal, | |
204 state_, render_frame_host->GetRoutingID(), | |
205 render_frame_host->GetFrameTreeNodeId())); | |
206 } | |
207 | |
208 private: | |
209 base::WeakPtr<DevToolsURLRequestInterceptor::State> state_; | |
210 base::WeakPtr<protocol::NetworkHandler> network_handler_; | |
211 }; | |
212 | |
213 void DevToolsURLRequestInterceptor::State::StartInterceptingRequestsInternal( | |
214 int render_frame_id, | |
215 int frame_tree_node_id, | |
216 base::WeakPtr<protocol::NetworkHandler> network_handler) { | |
217 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
218 intercepted_render_frames_[render_frame_id] = network_handler; | |
219 intercepted_frame_tree_nodes_[frame_tree_node_id] = network_handler; | |
dgozman
2017/05/22 22:27:04
It's unfortunate that you have to observe and keep
alex clarke (OOO till 29th)
2017/05/23 15:46:56
Agreed, I'd rather not do that but we need to do t
| |
220 } | |
221 | |
222 void DevToolsURLRequestInterceptor::State::StopInterceptingRequestsInternal( | |
223 int render_frame_id, | |
224 int frame_tree_node_id) { | |
225 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
226 intercepted_render_frames_.erase(render_frame_id); | |
227 intercepted_frame_tree_nodes_.erase(frame_tree_node_id); | |
228 } | |
229 | |
230 void DevToolsURLRequestInterceptor::State::StartInterceptingRequests( | |
231 WebContents* web_contents, | |
232 base::WeakPtr<protocol::NetworkHandler> network_handler) { | |
233 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
234 for (RenderFrameHost* render_frame_host : web_contents->GetAllFrames()) { | |
235 intercepted_render_frames_[render_frame_host->GetRoutingID()] = | |
236 network_handler; | |
237 intercepted_frame_tree_nodes_[render_frame_host->GetFrameTreeNodeId()] = | |
238 network_handler; | |
239 } | |
240 | |
241 // Listen for future updates. | |
242 observers_.emplace( | |
243 web_contents, | |
244 base::MakeUnique<InterceptedWebContentsObserver>( | |
245 web_contents, weak_ptr_factory_.GetWeakPtr(), network_handler)); | |
246 } | |
247 | |
248 void DevToolsURLRequestInterceptor::State::StopInterceptingRequests( | |
249 WebContents* web_contents) { | |
250 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
251 for (RenderFrameHost* render_frame_host : web_contents->GetAllFrames()) { | |
252 intercepted_render_frames_.erase(render_frame_host->GetRoutingID()); | |
253 intercepted_frame_tree_nodes_.erase( | |
254 render_frame_host->GetFrameTreeNodeId()); | |
255 } | |
256 observers_.erase(web_contents); | |
257 } | |
258 | |
259 void DevToolsURLRequestInterceptor::State::RegisterSubRequest( | |
260 const net::URLRequest* sub_request) { | |
261 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
262 DCHECK(sub_requests_.find(sub_request) == sub_requests_.end()); | |
263 sub_requests_.insert(sub_request); | |
264 } | |
265 | |
266 void DevToolsURLRequestInterceptor::State::UnregisterSubRequest( | |
267 const net::URLRequest* sub_request) { | |
268 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
269 DCHECK(sub_requests_.find(sub_request) != sub_requests_.end()); | |
270 sub_requests_.erase(sub_request); | |
271 } | |
272 | |
273 void DevToolsURLRequestInterceptor::State::ExpectRequestAfterRedirect( | |
274 const net::URLRequest* request, | |
275 std::string id) { | |
276 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
277 expected_redirects_[request] = id; | |
278 } | |
279 | |
280 std::string DevToolsURLRequestInterceptor::State::GetIdForRequest( | |
281 const net::URLRequest* request, | |
282 bool* is_redirect) { | |
283 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
284 auto find_it = expected_redirects_.find(request); | |
285 if (find_it == expected_redirects_.end()) { | |
286 *is_redirect = false; | |
287 return base::StringPrintf("id-%zu", ++next_id_); | |
288 } | |
289 *is_redirect = true; | |
290 std::string id = find_it->second; | |
291 expected_redirects_.erase(find_it); | |
292 return id; | |
293 } | |
294 | |
295 DevToolsURLInterceptorRequestJob* DevToolsURLRequestInterceptor::State::GetJob( | |
296 const std::string& interception_id) const { | |
297 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
298 const auto it = interception_id_to_job_map_.find(interception_id); | |
299 if (it == interception_id_to_job_map_.end()) | |
300 return nullptr; | |
301 return it->second; | |
302 } | |
303 | |
304 void DevToolsURLRequestInterceptor::State::RegisterJob( | |
305 DevToolsURLInterceptorRequestJob* job, | |
306 const std::string& interception_id) { | |
307 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
308 interception_id_to_job_map_[interception_id] = job; | |
309 } | |
310 | |
311 void DevToolsURLRequestInterceptor::State::UnregisterJob( | |
312 const std::string& interception_id) { | |
313 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
314 interception_id_to_job_map_.erase(interception_id); | |
315 } | |
316 | |
317 // static | |
318 DevToolsURLRequestInterceptor* | |
319 DevToolsURLRequestInterceptor::FromBrowserContext(BrowserContext* context) { | |
320 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
321 return static_cast<DevToolsURLRequestInterceptorUserData*>( | |
322 context->GetUserData(kDevToolsURLRequestInterceptorKeyName)) | |
323 ->devtools_url_request_interceptor(); | |
324 } | |
325 | |
326 DevToolsURLRequestInterceptor::Modifications::Modifications( | |
327 protocol::Maybe<std::string> url, | |
328 protocol::Maybe<std::string> method, | |
329 protocol::Maybe<std::string> post_data, | |
330 protocol::Maybe<protocol::Network::Headers> headers) | |
331 : url(std::move(url)), | |
332 method(std::move(method)), | |
333 post_data(std::move(post_data)), | |
334 headers(std::move(headers)) {} | |
335 | |
336 DevToolsURLRequestInterceptor::Modifications::~Modifications() {} | |
337 | |
338 } // namespace content | |
OLD | NEW |