Chromium Code Reviews| 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 |