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/render_process_host.h" | |
18 #include "content/public/browser/resource_request_info.h" | |
19 #include "content/public/browser/web_contents.h" | |
20 #include "content/public/browser/web_contents_observer.h" | |
21 #include "net/http/http_request_headers.h" | |
22 #include "net/url_request/url_request.h" | |
23 | |
24 namespace content { | |
25 | |
26 namespace { | |
27 const char kDevToolsURLRequestInterceptorKeyName[] = | |
28 "DevToolsURLRequestInterceptor"; | |
29 | |
30 class DevToolsURLRequestInterceptorUserData | |
31 : public base::SupportsUserData::Data { | |
32 public: | |
33 explicit DevToolsURLRequestInterceptorUserData( | |
34 DevToolsURLRequestInterceptor* devtools_url_request_interceptor) | |
35 : devtools_url_request_interceptor_(devtools_url_request_interceptor) {} | |
36 | |
37 DevToolsURLRequestInterceptor* devtools_url_request_interceptor() const { | |
38 return devtools_url_request_interceptor_; | |
39 } | |
40 | |
41 private: | |
42 DevToolsURLRequestInterceptor* devtools_url_request_interceptor_; | |
43 | |
44 DISALLOW_COPY_AND_ASSIGN(DevToolsURLRequestInterceptorUserData); | |
45 }; | |
46 | |
47 } // namespace | |
48 | |
49 DevToolsURLRequestInterceptor::DevToolsURLRequestInterceptor( | |
50 BrowserContext* browser_context) | |
51 : browser_context_(browser_context), state_(new State()) { | |
52 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
53 browser_context_->SetUserData( | |
54 kDevToolsURLRequestInterceptorKeyName, | |
55 base::MakeUnique<DevToolsURLRequestInterceptorUserData>(this)); | |
56 } | |
57 | |
58 DevToolsURLRequestInterceptor::~DevToolsURLRequestInterceptor() { | |
59 // The BrowserContext owns us, so we don't need to unregister | |
60 // DevToolsURLRequestInterceptorUserData explicitly. | |
61 } | |
62 | |
63 net::URLRequestJob* DevToolsURLRequestInterceptor::MaybeInterceptRequest( | |
64 net::URLRequest* request, | |
65 net::NetworkDelegate* network_delegate) const { | |
66 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
67 | |
68 const InterceptedPage* intercepted_page; | |
69 if (!state()->ShouldInterceptRequest(request, &intercepted_page)) | |
70 return nullptr; | |
71 | |
72 return state()->CreateDevToolsURLInterceptorRequestJob( | |
dgozman
2017/05/30 21:44:27
Perhaps, it makes sense to combine ShouldIntercept
alex clarke (OOO till 29th)
2017/05/31 15:52:02
Done.
| |
73 request, network_delegate, intercepted_page); | |
74 } | |
75 | |
76 net::URLRequestJob* DevToolsURLRequestInterceptor::MaybeInterceptRedirect( | |
77 net::URLRequest* request, | |
78 net::NetworkDelegate* network_delegate, | |
79 const GURL& location) const { | |
80 return nullptr; | |
81 } | |
82 | |
83 net::URLRequestJob* DevToolsURLRequestInterceptor::MaybeInterceptResponse( | |
84 net::URLRequest* request, | |
85 net::NetworkDelegate* network_delegate) const { | |
86 return nullptr; | |
87 } | |
88 | |
89 DevToolsURLRequestInterceptor::State::State() : next_id_(0) {} | |
90 | |
91 DevToolsURLRequestInterceptor::State::~State() {} | |
92 | |
93 void DevToolsURLRequestInterceptor::State::ContinueInterceptedRequest( | |
94 std::string interception_id, | |
95 std::unique_ptr<Modifications> modifications, | |
96 CommandCallback callback) { | |
97 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
98 DevToolsURLInterceptorRequestJob* job = GetJob(interception_id); | |
99 if (!job) { | |
100 std::move(callback).Run(CommandStatus::UnknownInterceptionId); | |
101 } else { | |
102 std::move(callback).Run( | |
103 job->ContinueInterceptedRequest(std::move(modifications))); | |
104 } | |
105 } | |
106 | |
107 bool DevToolsURLRequestInterceptor::State::ShouldInterceptRequest( | |
108 const net::URLRequest* request, | |
109 const InterceptedPage** intercepted_page) const { | |
110 // Bail out if we're not intercepting anything. | |
111 if (intercepted_render_frames_.empty()) { | |
112 DCHECK(intercepted_frame_tree_nodes_.empty()); | |
113 return false; | |
114 } | |
115 const ResourceRequestInfo* resource_request_info = | |
116 ResourceRequestInfo::ForRequest(request); | |
117 if (!resource_request_info) | |
118 return false; | |
119 int child_id = resource_request_info->GetChildID(); | |
120 int frame_tree_node_id = resource_request_info->GetFrameTreeNodeId(); | |
121 if (frame_tree_node_id == -1) { | |
122 // |frame_tree_node_id| is not set for renderer side requests, fall back to | |
123 // the RenderFrameID. | |
124 int render_frame_id = resource_request_info->GetRenderFrameID(); | |
125 const auto find_it = intercepted_render_frames_.find( | |
126 std::make_pair(render_frame_id, child_id)); | |
127 if (find_it == intercepted_render_frames_.end()) | |
128 return false; | |
129 *intercepted_page = &find_it->second; | |
130 } else { | |
131 // |frame_tree_node_id| is set for browser side navigations, so use that | |
132 // because the RenderFrameID isn't known (neither is the ChildID). | |
133 const auto find_it = intercepted_frame_tree_nodes_.find(frame_tree_node_id); | |
134 if (find_it == intercepted_frame_tree_nodes_.end()) | |
135 return false; | |
136 *intercepted_page = &find_it->second; | |
137 } | |
138 | |
139 // We don't want to intercept our own sub requests. | |
140 return sub_requests_.find(request) == sub_requests_.end(); | |
141 } | |
142 | |
143 DevToolsURLInterceptorRequestJob* | |
144 DevToolsURLRequestInterceptor::State::CreateDevToolsURLInterceptorRequestJob( | |
145 net::URLRequest* request, | |
146 net::NetworkDelegate* network_delegate, | |
147 const InterceptedPage* intercepted_page) { | |
148 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
149 bool is_redirect; | |
150 std::string interception_id = GetIdForRequest(request, &is_redirect); | |
151 DevToolsURLInterceptorRequestJob* job = new DevToolsURLInterceptorRequestJob( | |
152 this, interception_id, request, network_delegate, | |
153 intercepted_page->web_contents, intercepted_page->network_handler, | |
154 is_redirect); | |
155 interception_id_to_job_map_[interception_id] = job; | |
156 return job; | |
157 } | |
158 | |
159 class DevToolsURLRequestInterceptor::State::InterceptedWebContentsObserver | |
160 : public WebContentsObserver { | |
161 public: | |
162 InterceptedWebContentsObserver( | |
163 WebContents* web_contents, | |
164 scoped_refptr<DevToolsURLRequestInterceptor::State> state, | |
165 base::WeakPtr<protocol::NetworkHandler> network_handler) | |
166 : WebContentsObserver(web_contents), | |
167 state_(state), | |
168 network_handler_(network_handler) { | |
169 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
170 } | |
171 | |
172 void RenderFrameCreated(RenderFrameHost* render_frame_host) override { | |
173 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
174 BrowserThread::PostTask( | |
175 BrowserThread::IO, FROM_HERE, | |
176 base::BindOnce(&DevToolsURLRequestInterceptor::State:: | |
177 StartInterceptingRequestsInternal, | |
178 state_, render_frame_host->GetRoutingID(), | |
179 render_frame_host->GetFrameTreeNodeId(), | |
180 render_frame_host->GetProcess()->GetID(), web_contents(), | |
181 network_handler_)); | |
182 } | |
183 | |
184 void RenderFrameDeleted(RenderFrameHost* render_frame_host) override { | |
185 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
186 BrowserThread::PostTask( | |
187 BrowserThread::IO, FROM_HERE, | |
188 base::BindOnce(&DevToolsURLRequestInterceptor::State:: | |
189 StopInterceptingRequestsInternal, | |
190 state_, render_frame_host->GetRoutingID(), | |
191 render_frame_host->GetFrameTreeNodeId(), | |
192 render_frame_host->GetProcess()->GetID())); | |
193 } | |
194 | |
195 private: | |
196 scoped_refptr<DevToolsURLRequestInterceptor::State> state_; | |
197 base::WeakPtr<protocol::NetworkHandler> network_handler_; | |
198 }; | |
199 | |
200 void DevToolsURLRequestInterceptor::State::StartInterceptingRequestsInternal( | |
201 int render_frame_id, | |
202 int frame_tree_node_id, | |
203 int process_id, | |
204 WebContents* web_contents, | |
205 base::WeakPtr<protocol::NetworkHandler> network_handler) { | |
206 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
207 intercepted_render_frames_[std::make_pair(render_frame_id, process_id)] = | |
208 InterceptedPage(web_contents, network_handler); | |
209 intercepted_frame_tree_nodes_[frame_tree_node_id] = | |
210 InterceptedPage(web_contents, network_handler); | |
211 } | |
212 | |
213 void DevToolsURLRequestInterceptor::State::StopInterceptingRequestsInternal( | |
214 int render_frame_id, | |
215 int frame_tree_node_id, | |
216 int process_id) { | |
217 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
218 intercepted_render_frames_.erase(std::make_pair(render_frame_id, process_id)); | |
219 intercepted_frame_tree_nodes_.erase(frame_tree_node_id); | |
220 } | |
221 | |
222 void DevToolsURLRequestInterceptor::State::StartInterceptingRequests( | |
223 WebContents* web_contents, | |
224 base::WeakPtr<protocol::NetworkHandler> network_handler) { | |
225 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
226 // WebContents methods are UI thread only. | |
227 for (RenderFrameHost* render_frame_host : web_contents->GetAllFrames()) { | |
228 BrowserThread::PostTask( | |
229 BrowserThread::IO, FROM_HERE, | |
230 base::BindOnce(&DevToolsURLRequestInterceptor::State:: | |
231 StartInterceptingRequestsInternal, | |
232 this, render_frame_host->GetRoutingID(), | |
233 render_frame_host->GetFrameTreeNodeId(), | |
234 render_frame_host->GetProcess()->GetID(), web_contents, | |
235 network_handler)); | |
236 } | |
237 | |
238 // Listen for future updates. | |
239 observers_.emplace(web_contents, | |
240 base::MakeUnique<InterceptedWebContentsObserver>( | |
241 web_contents, this, network_handler)); | |
242 } | |
243 | |
244 void DevToolsURLRequestInterceptor::State::StopInterceptingRequests( | |
245 WebContents* web_contents) { | |
246 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
247 observers_.erase(web_contents); | |
248 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
249 base::BindOnce(&DevToolsURLRequestInterceptor::State:: | |
250 StopInterceptingRequestsOnIoThread, | |
251 this, web_contents)); | |
252 } | |
253 | |
254 void DevToolsURLRequestInterceptor::State::StopInterceptingRequestsOnIoThread( | |
255 WebContents* web_contents) { | |
256 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
257 // Remove any intercepted render frames associated with |web_contents|. | |
258 base::flat_map<std::pair<int, int>, InterceptedPage> | |
259 remaining_intercepted_render_frames; | |
260 for (const auto pair : intercepted_render_frames_) { | |
261 if (pair.second.web_contents == web_contents) | |
262 continue; | |
263 remaining_intercepted_render_frames.insert(pair); | |
264 } | |
265 std::swap(remaining_intercepted_render_frames, intercepted_render_frames_); | |
266 | |
267 // Remove any intercepted frame tree nodes associated with |web_contents|. | |
268 base::flat_map<int, InterceptedPage> remaining_intercepted_frame_tree_nodes; | |
269 for (const auto pair : intercepted_frame_tree_nodes_) { | |
270 if (pair.second.web_contents == web_contents) | |
271 continue; | |
272 remaining_intercepted_frame_tree_nodes.insert(pair); | |
273 } | |
274 std::swap(remaining_intercepted_frame_tree_nodes, | |
275 intercepted_frame_tree_nodes_); | |
276 | |
277 // Tell any jobs associated with |web_contents| to stop intercepting. | |
278 for (const auto pair : interception_id_to_job_map_) { | |
279 if (pair.second->web_contents() == web_contents) | |
280 pair.second->StopIntercepting(); | |
281 } | |
282 } | |
283 | |
284 void DevToolsURLRequestInterceptor::State::RegisterSubRequest( | |
285 const net::URLRequest* sub_request) { | |
286 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
287 DCHECK(sub_requests_.find(sub_request) == sub_requests_.end()); | |
288 sub_requests_.insert(sub_request); | |
289 } | |
290 | |
291 void DevToolsURLRequestInterceptor::State::UnregisterSubRequest( | |
292 const net::URLRequest* sub_request) { | |
293 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
294 DCHECK(sub_requests_.find(sub_request) != sub_requests_.end()); | |
295 sub_requests_.erase(sub_request); | |
296 } | |
297 | |
298 void DevToolsURLRequestInterceptor::State::ExpectRequestAfterRedirect( | |
299 const net::URLRequest* request, | |
300 std::string id) { | |
301 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
302 expected_redirects_[request] = id; | |
303 } | |
304 | |
305 std::string DevToolsURLRequestInterceptor::State::GetIdForRequest( | |
306 const net::URLRequest* request, | |
307 bool* is_redirect) { | |
308 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
309 auto find_it = expected_redirects_.find(request); | |
310 if (find_it == expected_redirects_.end()) { | |
311 *is_redirect = false; | |
312 return base::StringPrintf("id-%zu", ++next_id_); | |
313 } | |
314 *is_redirect = true; | |
315 std::string id = find_it->second; | |
316 expected_redirects_.erase(find_it); | |
317 return id; | |
318 } | |
319 | |
320 DevToolsURLInterceptorRequestJob* DevToolsURLRequestInterceptor::State::GetJob( | |
321 const std::string& interception_id) const { | |
322 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
323 const auto it = interception_id_to_job_map_.find(interception_id); | |
324 if (it == interception_id_to_job_map_.end()) | |
325 return nullptr; | |
326 return it->second; | |
327 } | |
328 | |
329 void DevToolsURLRequestInterceptor::State::JobFinished( | |
330 const std::string& interception_id) { | |
331 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
332 interception_id_to_job_map_.erase(interception_id); | |
333 } | |
334 | |
335 // static | |
336 DevToolsURLRequestInterceptor* | |
337 DevToolsURLRequestInterceptor::FromBrowserContext(BrowserContext* context) { | |
338 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
339 return static_cast<DevToolsURLRequestInterceptorUserData*>( | |
340 context->GetUserData(kDevToolsURLRequestInterceptorKeyName)) | |
341 ->devtools_url_request_interceptor(); | |
342 } | |
343 | |
344 DevToolsURLRequestInterceptor::Modifications::Modifications( | |
345 base::Optional<net::Error> error_reason, | |
346 base::Optional<std::string> raw_response, | |
347 protocol::Maybe<std::string> modified_url, | |
348 protocol::Maybe<std::string> modified_method, | |
349 protocol::Maybe<std::string> modified_post_data, | |
350 protocol::Maybe<protocol::Network::Headers> modified_headers) | |
351 : error_reason(std::move(error_reason)), | |
352 raw_response(std::move(raw_response)), | |
353 modified_url(std::move(modified_url)), | |
354 modified_method(std::move(modified_method)), | |
355 modified_post_data(std::move(modified_post_data)), | |
356 modified_headers(std::move(modified_headers)) {} | |
357 | |
358 DevToolsURLRequestInterceptor::Modifications::~Modifications() {} | |
359 | |
360 DevToolsURLRequestInterceptor::InterceptedPage::InterceptedPage() | |
361 : web_contents(nullptr) {} | |
362 | |
363 DevToolsURLRequestInterceptor::InterceptedPage::InterceptedPage( | |
364 const InterceptedPage& other) | |
365 : web_contents(other.web_contents), | |
366 network_handler(other.network_handler) {} | |
367 | |
368 DevToolsURLRequestInterceptor::InterceptedPage::InterceptedPage( | |
369 WebContents* web_contents, | |
370 base::WeakPtr<protocol::NetworkHandler> network_handler) | |
371 : web_contents(web_contents), network_handler(network_handler) {} | |
372 | |
373 DevToolsURLRequestInterceptor::InterceptedPage::~InterceptedPage() {} | |
374 | |
375 } // namespace content | |
OLD | NEW |