1 // Copyright (c) 2010 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 "chrome/browser/automation/automation_resource_message_filter.h" 6 7 #include "base/path_service.h" 8 #include "base/metrics/histogram.h" 9 #include "base/stl_util-inl.h" 10 #include "chrome/browser/automation/url_request_automation_job.h" 11 #include "chrome/browser/net/url_request_failed_dns_job.h" 12 #include "chrome/browser/net/url_request_mock_http_job.h" 13 #include "chrome/browser/net/url_request_mock_util.h" 14 #include "chrome/browser/net/url_request_slow_download_job.h" 15 #include "chrome/browser/net/url_request_slow_http_job.h" 16 #include "chrome/common/automation_messages.h" 17 #include "chrome/common/chrome_paths.h" 18 #include "content/browser/browser_thread.h" 19 #include "content/browser/renderer_host/render_message_filter.h" 20 #include "googleurl/src/gurl.h" 21 #include "net/base/net_errors.h" 22 #include "net/url_request/url_request_filter.h" 23 24 base::LazyInstance<AutomationResourceMessageFilter::RenderViewMap> 25 AutomationResourceMessageFilter::filtered_render_views_( 26 base::LINKER_INITIALIZED); 27 28 base::LazyInstance<AutomationResourceMessageFilter::CompletionCallbackMap> 29 AutomationResourceMessageFilter::completion_callback_map_( 30 base::LINKER_INITIALIZED); 31 32 int AutomationResourceMessageFilter::unique_request_id_ = 1; 33 int AutomationResourceMessageFilter::next_completion_callback_id_ = 0; 34 35 // CookieStore specialization to enable fetching and setting cookies over the 36 // automation channel. This cookie store is transient i.e. it maintains cookies 37 // for the duration of the current request to set or get cookies from the 38 // renderer. 39 class AutomationCookieStore : public net::CookieStore { 40 public: 41 AutomationCookieStore(AutomationResourceMessageFilter* automation_client, 42 int tab_handle) 43 : automation_client_(automation_client), 44 tab_handle_(tab_handle) { 45 } 46 47 virtual ~AutomationCookieStore() { 48 DVLOG(1) << "In " << __FUNCTION__; 49 } 50 51 // CookieStore implementation. 52 virtual bool SetCookieWithOptions(const GURL& url, 53 const std::string& cookie_line, 54 const net::CookieOptions& options) { 55 // The cookie_string_ is available only once, i.e. once it is read by 56 // it is invalidated. 57 cookie_string_ = cookie_line; 58 return true; 59 } 60 61 virtual std::string GetCookiesWithOptions(const GURL& url, 62 const net::CookieOptions& options) { 63 return cookie_string_; 64 } 65 66 virtual void DeleteCookie(const GURL& url, 67 const std::string& cookie_name) { 68 NOTREACHED() << "Should not get called for an automation profile"; 69 } 70 71 virtual net::CookieMonster* GetCookieMonster() { 72 NOTREACHED() << "Should not get called for an automation profile"; 73 return NULL; 74 } 75 76 protected: 77 scoped_refptr<AutomationResourceMessageFilter> automation_client_; 78 int tab_handle_; 79 std::string cookie_string_; 80 81 private: 82 DISALLOW_COPY_AND_ASSIGN(AutomationCookieStore); 83 }; 84 85 AutomationResourceMessageFilter::AutomationDetails::AutomationDetails() 86 : tab_handle(0), 87 ref_count(1), 88 is_pending_render_view(false) { 89 } 90 91 AutomationResourceMessageFilter::AutomationDetails::AutomationDetails( 92 int tab, 93 AutomationResourceMessageFilter* flt, 94 bool pending_view) 95 : tab_handle(tab), ref_count(1), filter(flt), 96 is_pending_render_view(pending_view) { 97 } 98 99 AutomationResourceMessageFilter::AutomationDetails::~AutomationDetails() {} 100 101 struct AutomationResourceMessageFilter::CookieCompletionInfo { 102 net::CompletionCallback* completion_callback; 103 scoped_refptr<net::CookieStore> cookie_store; 104 }; 105 106 AutomationResourceMessageFilter::AutomationResourceMessageFilter() 107 : channel_(NULL) { 108 // Ensure that an instance of the callback map is created. 109 completion_callback_map_.Get(); 110 // Ensure that an instance of the render view map is created. 111 filtered_render_views_.Get(); 112 113 BrowserThread::PostTask( 114 BrowserThread::IO, FROM_HERE, 115 NewRunnableFunction( 116 URLRequestAutomationJob::EnsureProtocolFactoryRegistered)); 117 } 118 119 AutomationResourceMessageFilter::~AutomationResourceMessageFilter() { 120 } 121 122 // Called on the IPC thread: 123 void AutomationResourceMessageFilter::OnFilterAdded(IPC::Channel* channel) { 124 DCHECK(!channel_); 125 channel_ = channel; 126 } 127 128 void AutomationResourceMessageFilter::OnFilterRemoved() { 129 channel_ = NULL; 130 } 131 132 // Called on the IPC thread: 133 void AutomationResourceMessageFilter::OnChannelConnected(int32 peer_pid) { 134 } 135 136 // Called on the IPC thread: 137 void AutomationResourceMessageFilter::OnChannelClosing() { 138 channel_ = NULL; 139 request_map_.clear(); 140 141 // Only erase RenderViews which are associated with this 142 // AutomationResourceMessageFilter instance. 143 RenderViewMap::iterator index = filtered_render_views_.Get().begin(); 144 while (index != filtered_render_views_.Get().end()) { 145 const AutomationDetails& details = (*index).second; 146 if (details.filter.get() == this) { 147 filtered_render_views_.Get().erase(index++); 148 } else { 149 index++; 150 } 151 } 152 } 153 154 // Called on the IPC thread: 155 bool AutomationResourceMessageFilter::OnMessageReceived( 156 const IPC::Message& message) { 157 int request_id; 158 if (URLRequestAutomationJob::MayFilterMessage(message, &request_id)) { 159 RequestMap::iterator it = request_map_.find(request_id); 160 if (it != request_map_.end()) { 161 URLRequestAutomationJob* job = it->second; 162 DCHECK(job); 163 if (job) { 164 job->OnMessage(message); 165 return true; 166 } 167 } else { 168 // This could occur if the request was stopped from Chrome which would 169 // delete it from the request map. If we receive data for this request 170 // from the host we should ignore it. 171 LOG(ERROR) << "Failed to find request id:" << request_id; 172 return true; 173 } 174 } 175 176 bool handled = true; 177 IPC_BEGIN_MESSAGE_MAP(AutomationResourceMessageFilter, message) 178 IPC_MESSAGE_HANDLER(AutomationMsg_SetFilteredInet, 179 OnSetFilteredInet) 180 IPC_MESSAGE_HANDLER(AutomationMsg_GetFilteredInetHitCount, 181 OnGetFilteredInetHitCount) 182 IPC_MESSAGE_HANDLER(AutomationMsg_RecordHistograms, 183 OnRecordHistograms) 184 IPC_MESSAGE_HANDLER(AutomationMsg_GetCookiesHostResponse, 185 OnGetCookiesHostResponse) 186 IPC_MESSAGE_UNHANDLED(handled = false) 187 IPC_END_MESSAGE_MAP() 188 189 return handled; 190 } 191 192 // Called on the IPC thread: 193 bool AutomationResourceMessageFilter::Send(IPC::Message* message) { 194 // This has to be called on the IO thread. 195 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 196 if (!channel_) { 197 delete message; 198 return false; 199 } 200 201 return channel_->Send(message); 202 } 203 204 bool AutomationResourceMessageFilter::RegisterRequest( 205 URLRequestAutomationJob* job) { 206 if (!job) { 207 NOTREACHED(); 208 return false; 209 } 210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 211 212 // Register pending jobs in the pending request map for servicing later. 213 if (job->is_pending()) { 214 DCHECK(!ContainsKey(pending_request_map_, job->id())); 215 DCHECK(!ContainsKey(request_map_, job->id())); 216 pending_request_map_[job->id()] = job; 217 } else { 218 DCHECK(!ContainsKey(request_map_, job->id())); 219 DCHECK(!ContainsKey(pending_request_map_, job->id())); 220 request_map_[job->id()] = job; 221 } 222 223 return true; 224 } 225 226 void AutomationResourceMessageFilter::UnRegisterRequest( 227 URLRequestAutomationJob* job) { 228 if (!job) { 229 NOTREACHED(); 230 return; 231 } 232 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 233 234 if (job->is_pending()) { 235 DCHECK(ContainsKey(pending_request_map_, job->id())); 236 pending_request_map_.erase(job->id()); 237 } else { 238 request_map_.erase(job->id()); 239 } 240 } 241 242 bool AutomationResourceMessageFilter::RegisterRenderView( 243 int renderer_pid, int renderer_id, int tab_handle, 244 AutomationResourceMessageFilter* filter, 245 bool pending_view) { 246 if (!renderer_pid || !renderer_id || !tab_handle) { 247 NOTREACHED(); 248 return false; 249 } 250 251 BrowserThread::PostTask( 252 BrowserThread::IO, FROM_HERE, 253 NewRunnableFunction( 254 AutomationResourceMessageFilter::RegisterRenderViewInIOThread, 255 renderer_pid, 256 renderer_id, 257 tab_handle, 258 make_scoped_refptr(filter), 259 pending_view)); 260 return true; 261 } 262 263 void AutomationResourceMessageFilter::UnRegisterRenderView( 264 int renderer_pid, int renderer_id) { 265 BrowserThread::PostTask( 266 BrowserThread::IO, FROM_HERE, 267 NewRunnableFunction( 268 AutomationResourceMessageFilter::UnRegisterRenderViewInIOThread, 269 renderer_pid, renderer_id)); 270 } 271 272 bool AutomationResourceMessageFilter::ResumePendingRenderView( 273 int renderer_pid, int renderer_id, int tab_handle, 274 AutomationResourceMessageFilter* filter) { 275 if (!renderer_pid || !renderer_id || !tab_handle) { 276 NOTREACHED(); 277 return false; 278 } 279 280 BrowserThread::PostTask( 281 BrowserThread::IO, FROM_HERE, 282 NewRunnableFunction( 283 AutomationResourceMessageFilter::ResumePendingRenderViewInIOThread, 284 renderer_pid, 285 renderer_id, 286 tab_handle, 287 make_scoped_refptr(filter))); 288 return true; 289 } 290 291 void AutomationResourceMessageFilter::RegisterRenderViewInIOThread( 292 int renderer_pid, int renderer_id, 293 int tab_handle, AutomationResourceMessageFilter* filter, 294 bool pending_view) { 295 RendererId renderer_key(renderer_pid, renderer_id); 296 297 scoped_refptr<net::CookieStore> cookie_store( 298 new AutomationCookieStore(filter, tab_handle)); 299 300 RenderViewMap::iterator automation_details_iter( 301 filtered_render_views_.Get().find(renderer_key)); 302 if (automation_details_iter != filtered_render_views_.Get().end()) { 303 DCHECK(automation_details_iter->second.ref_count > 0); 304 automation_details_iter->second.ref_count++; 305 } else { 306 filtered_render_views_.Get()[renderer_key] = 307 AutomationDetails(tab_handle, filter, pending_view); 308 } 309 310 filtered_render_views_.Get()[renderer_key].set_cookie_store(cookie_store); 311 } 312 313 // static 314 void AutomationResourceMessageFilter::UnRegisterRenderViewInIOThread( 315 int renderer_pid, int renderer_id) { 316 RenderViewMap::iterator automation_details_iter( 317 filtered_render_views_.Get().find(RendererId(renderer_pid, 318 renderer_id))); 319 320 if (automation_details_iter == filtered_render_views_.Get().end()) { 321 // This is called for all RenderViewHosts, so it's fine if we don't find a 322 // match. 323 return; 324 } 325 326 automation_details_iter->second.ref_count--; 327 328 if (automation_details_iter->second.ref_count <= 0) { 329 filtered_render_views_.Get().erase(RendererId(renderer_pid, renderer_id)); 330 } 331 } 332 333 // static 334 bool AutomationResourceMessageFilter::ResumePendingRenderViewInIOThread( 335 int renderer_pid, int renderer_id, int tab_handle, 336 AutomationResourceMessageFilter* filter) { 337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 338 339 RendererId renderer_key(renderer_pid, renderer_id); 340 341 RenderViewMap::iterator automation_details_iter( 342 filtered_render_views_.Get().find(renderer_key)); 343 344 if (automation_details_iter == filtered_render_views_.Get().end()) { 345 NOTREACHED() << "Failed to find pending view for renderer pid:" 346 << renderer_pid 347 << ", render view id:" 348 << renderer_id; 349 return false; 350 } 351 352 DCHECK(automation_details_iter->second.is_pending_render_view); 353 354 scoped_refptr<net::CookieStore> cookie_store( 355 new AutomationCookieStore(filter, tab_handle)); 356 357 AutomationResourceMessageFilter* old_filter = 358 automation_details_iter->second.filter; 359 DCHECK(old_filter != NULL); 360 361 filtered_render_views_.Get()[renderer_key] = 362 AutomationDetails(tab_handle, filter, false); 363 364 filtered_render_views_.Get()[renderer_key].set_cookie_store(cookie_store); 365 366 ResumeJobsForPendingView(tab_handle, old_filter, filter); 367 return true; 368 } 369 370 bool AutomationResourceMessageFilter::LookupRegisteredRenderView( 371 int renderer_pid, int renderer_id, AutomationDetails* details) { 372 bool found = false; 373 RenderViewMap::iterator it = filtered_render_views_.Get().find(RendererId( 374 renderer_pid, renderer_id)); 375 if (it != filtered_render_views_.Get().end()) { 376 found = true; 377 if (details) 378 *details = it->second; 379 } 380 381 return found; 382 } 383 384 bool AutomationResourceMessageFilter::GetAutomationRequestId( 385 int request_id, int* automation_request_id) { 386 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 387 388 RequestMap::iterator it = request_map_.begin(); 389 while (it != request_map_.end()) { 390 URLRequestAutomationJob* job = it->second; 391 DCHECK(job); 392 if (job && job->request_id() == request_id) { 393 *automation_request_id = job->id(); 394 return true; 395 } 396 it++; 397 } 398 399 return false; 400 } 401 402 bool AutomationResourceMessageFilter::SendDownloadRequestToHost( 403 int routing_id, int tab_handle, int request_id) { 404 int automation_request_id = 0; 405 bool valid_id = GetAutomationRequestId(request_id, &automation_request_id); 406 if (!valid_id) { 407 LOG(ERROR) << "Invalid request id: " << request_id; 408 return false; 409 } 410 411 return Send(new AutomationMsg_DownloadRequestInHost(tab_handle, 412 automation_request_id)); 413 } 414 415 void AutomationResourceMessageFilter::OnSetFilteredInet(bool enable) { 416 chrome_browser_net::SetUrlRequestMocksEnabled(enable); 417 } 418 419 void AutomationResourceMessageFilter::OnGetFilteredInetHitCount( 420 int* hit_count) { 421 *hit_count = net::URLRequestFilter::GetInstance()->hit_count(); 422 } 423 424 void AutomationResourceMessageFilter::OnRecordHistograms( 425 const std::vector<std::string>& histogram_list) { 426 for (size_t index = 0; index < histogram_list.size(); ++index) { 427 base::Histogram::DeserializeHistogramInfo(histogram_list[index]); 428 } 429 } 430 431 bool AutomationResourceMessageFilter::GetCookiesForUrl( 432 const GURL& url, net::CompletionCallback* callback) { 433 GetCookiesCompletion* get_cookies_callback = 434 static_cast<GetCookiesCompletion*>(callback); 435 436 RendererId renderer_key(get_cookies_callback->render_process_id(), 437 get_cookies_callback->render_view_id()); 438 439 RenderViewMap::iterator automation_details_iter( 440 filtered_render_views_.Get().find(renderer_key)); 441 442 if (automation_details_iter == filtered_render_views_.Get().end()) { 443 return false; 444 } 445 446 DCHECK(automation_details_iter->second.filter != NULL); 447 DCHECK(automation_details_iter->second.cookie_store_.get() != NULL); 448 449 int completion_callback_id = GetNextCompletionCallbackId(); 450 DCHECK(!ContainsKey(completion_callback_map_.Get(), completion_callback_id)); 451 452 CookieCompletionInfo cookie_info; 453 cookie_info.completion_callback = callback; 454 cookie_info.cookie_store = automation_details_iter->second.cookie_store_; 455 456 completion_callback_map_.Get()[completion_callback_id] = cookie_info; 457 458 DCHECK(automation_details_iter->second.filter != NULL); 459 460 if (automation_details_iter->second.filter) { 461 automation_details_iter->second.filter->Send( 462 new AutomationMsg_GetCookiesFromHost( 463 automation_details_iter->second.tab_handle, url, 464 completion_callback_id)); 465 } 466 return true; 467 } 468 469 void AutomationResourceMessageFilter::OnGetCookiesHostResponse( 470 int tab_handle, bool success, const GURL& url, const std::string& cookies, 471 int cookie_id) { 472 CompletionCallbackMap::iterator index = 473 completion_callback_map_.Get().find(cookie_id); 474 if (index != completion_callback_map_.Get().end()) { 475 net::CompletionCallback* callback = index->second.completion_callback; 476 477 scoped_refptr<net::CookieStore> cookie_store = index->second.cookie_store; 478 479 DCHECK(callback != NULL); 480 DCHECK(cookie_store.get() != NULL); 481 482 completion_callback_map_.Get().erase(index); 483 484 OnGetCookiesHostResponseInternal(tab_handle, success, url, cookies, 485 callback, cookie_store.get()); 486 } else { 487 NOTREACHED() << "Received invalid completion callback id:" 488 << cookie_id; 489 } 490 } 491 492 void AutomationResourceMessageFilter::OnGetCookiesHostResponseInternal( 493 int tab_handle, bool success, const GURL& url, const std::string& cookies, 494 net::CompletionCallback* callback, net::CookieStore* cookie_store) { 495 DCHECK(callback); 496 DCHECK(cookie_store); 497 498 GetCookiesCompletion* get_cookies_callback = 499 static_cast<GetCookiesCompletion*>(callback); 500 501 get_cookies_callback->set_cookie_store(cookie_store); 502 503 // Set the cookie in the cookie store so that the callback can read it. 504 cookie_store->SetCookieWithOptions(url, cookies, net::CookieOptions()); 505 506 Tuple1<int> params; 507 params.a = success ? net::OK : net::ERR_ACCESS_DENIED; 508 callback->RunWithParams(params); 509 510 // The cookie for this URL is only valid until it is read by the callback. 511 cookie_store->SetCookieWithOptions(url, "", net::CookieOptions()); 512 } 513 514 bool AutomationResourceMessageFilter::SetCookiesForUrl( 515 const GURL& url, const std::string& cookie_line, 516 net::CompletionCallback* callback) { 517 SetCookieCompletion* set_cookies_callback = 518 static_cast<SetCookieCompletion*>(callback); 519 520 RenderViewMap::iterator automation_details_iter( 521 filtered_render_views_.Get().find(RendererId( 522 set_cookies_callback->render_process_id(), 523 set_cookies_callback->render_view_id()))); 524 525 if (automation_details_iter == filtered_render_views_.Get().end()) { 526 return false; 527 } 528 529 delete callback; 530 DCHECK(automation_details_iter->second.filter != NULL); 531 532 if (automation_details_iter->second.filter) { 533 automation_details_iter->second.filter->Send( 534 new AutomationMsg_SetCookieAsync( 535 automation_details_iter->second.tab_handle, url, cookie_line)); 536 } 537 538 return true; 539 } 540 541 // static 542 void AutomationResourceMessageFilter::ResumeJobsForPendingView( 543 int tab_handle, 544 AutomationResourceMessageFilter* old_filter, 545 AutomationResourceMessageFilter* new_filter) { 546 DCHECK(old_filter != NULL); 547 DCHECK(new_filter != NULL); 548 549 RequestMap pending_requests = old_filter->pending_request_map_; 550 551 for (RequestMap::iterator index = old_filter->pending_request_map_.begin(); 552 index != old_filter->pending_request_map_.end(); index++) { 553 scoped_refptr<URLRequestAutomationJob> job = (*index).second; 554 DCHECK_EQ(job->message_filter(), old_filter); 555 DCHECK(job->is_pending()); 556 // StartPendingJob will register the job with the new filter. 557 job->StartPendingJob(tab_handle, new_filter); 558 } 559 560 old_filter->pending_request_map_.clear(); 561 } 562