automation_resource_message_filter.cc revision 3f50c38dc070f4bb515c1b64450dae14f316474e
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/browser_thread.h" 12#include "chrome/browser/net/url_request_failed_dns_job.h" 13#include "chrome/browser/net/url_request_mock_http_job.h" 14#include "chrome/browser/net/url_request_mock_util.h" 15#include "chrome/browser/net/url_request_slow_download_job.h" 16#include "chrome/browser/net/url_request_slow_http_job.h" 17#include "chrome/browser/renderer_host/render_message_filter.h" 18#include "chrome/common/automation_messages.h" 19#include "chrome/common/chrome_paths.h" 20#include "googleurl/src/gurl.h" 21#include "net/base/net_errors.h" 22#include "net/url_request/url_request_filter.h" 23 24base::LazyInstance<AutomationResourceMessageFilter::RenderViewMap> 25 AutomationResourceMessageFilter::filtered_render_views_( 26 base::LINKER_INITIALIZED); 27 28base::LazyInstance<AutomationResourceMessageFilter::CompletionCallbackMap> 29 AutomationResourceMessageFilter::completion_callback_map_( 30 base::LINKER_INITIALIZED); 31 32int AutomationResourceMessageFilter::unique_request_id_ = 1; 33int 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. 39class 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 85AutomationResourceMessageFilter::AutomationDetails::AutomationDetails() 86 : tab_handle(0), 87 ref_count(1), 88 is_pending_render_view(false) { 89} 90 91AutomationResourceMessageFilter::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 99AutomationResourceMessageFilter::AutomationDetails::~AutomationDetails() {} 100 101struct AutomationResourceMessageFilter::CookieCompletionInfo { 102 net::CompletionCallback* completion_callback; 103 scoped_refptr<net::CookieStore> cookie_store; 104}; 105 106AutomationResourceMessageFilter::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 119AutomationResourceMessageFilter::~AutomationResourceMessageFilter() { 120} 121 122// Called on the IPC thread: 123void AutomationResourceMessageFilter::OnFilterAdded(IPC::Channel* channel) { 124 DCHECK(channel_ == NULL); 125 channel_ = channel; 126} 127 128void AutomationResourceMessageFilter::OnFilterRemoved() { 129 channel_ = NULL; 130} 131 132// Called on the IPC thread: 133void AutomationResourceMessageFilter::OnChannelConnected(int32 peer_pid) { 134} 135 136// Called on the IPC thread: 137void 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: 155bool 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: 193bool 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 204bool 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 226void 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 242bool 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 263void 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 272bool 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 291void 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 314void 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 VLOG(1) << "UnRegisterRenderViewInIOThread: already unregistered"; 322 return; 323 } 324 325 automation_details_iter->second.ref_count--; 326 327 if (automation_details_iter->second.ref_count <= 0) { 328 filtered_render_views_.Get().erase(RendererId(renderer_pid, renderer_id)); 329 } 330} 331 332// static 333bool AutomationResourceMessageFilter::ResumePendingRenderViewInIOThread( 334 int renderer_pid, int renderer_id, int tab_handle, 335 AutomationResourceMessageFilter* filter) { 336 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 337 338 RendererId renderer_key(renderer_pid, renderer_id); 339 340 RenderViewMap::iterator automation_details_iter( 341 filtered_render_views_.Get().find(renderer_key)); 342 343 if (automation_details_iter == filtered_render_views_.Get().end()) { 344 NOTREACHED() << "Failed to find pending view for renderer pid:" 345 << renderer_pid 346 << ", render view id:" 347 << renderer_id; 348 return false; 349 } 350 351 DCHECK(automation_details_iter->second.is_pending_render_view); 352 353 scoped_refptr<net::CookieStore> cookie_store( 354 new AutomationCookieStore(filter, tab_handle)); 355 356 AutomationResourceMessageFilter* old_filter = 357 automation_details_iter->second.filter; 358 DCHECK(old_filter != NULL); 359 360 filtered_render_views_.Get()[renderer_key] = 361 AutomationDetails(tab_handle, filter, false); 362 363 filtered_render_views_.Get()[renderer_key].set_cookie_store(cookie_store); 364 365 ResumeJobsForPendingView(tab_handle, old_filter, filter); 366 return true; 367} 368 369bool AutomationResourceMessageFilter::LookupRegisteredRenderView( 370 int renderer_pid, int renderer_id, AutomationDetails* details) { 371 bool found = false; 372 RenderViewMap::iterator it = filtered_render_views_.Get().find(RendererId( 373 renderer_pid, renderer_id)); 374 if (it != filtered_render_views_.Get().end()) { 375 found = true; 376 if (details) 377 *details = it->second; 378 } 379 380 return found; 381} 382 383bool AutomationResourceMessageFilter::GetAutomationRequestId( 384 int request_id, int* automation_request_id) { 385 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 386 387 RequestMap::iterator it = request_map_.begin(); 388 while (it != request_map_.end()) { 389 URLRequestAutomationJob* job = it->second; 390 DCHECK(job); 391 if (job && job->request_id() == request_id) { 392 *automation_request_id = job->id(); 393 return true; 394 } 395 it++; 396 } 397 398 return false; 399} 400 401bool AutomationResourceMessageFilter::SendDownloadRequestToHost( 402 int routing_id, int tab_handle, int request_id) { 403 int automation_request_id = 0; 404 bool valid_id = GetAutomationRequestId(request_id, &automation_request_id); 405 if (!valid_id) { 406 NOTREACHED() << "Invalid request id: " << request_id; 407 return false; 408 } 409 410 return Send(new AutomationMsg_DownloadRequestInHost(tab_handle, 411 automation_request_id)); 412} 413 414void AutomationResourceMessageFilter::OnSetFilteredInet(bool enable) { 415 chrome_browser_net::SetUrlRequestMocksEnabled(enable); 416} 417 418void AutomationResourceMessageFilter::OnGetFilteredInetHitCount( 419 int* hit_count) { 420 *hit_count = net::URLRequestFilter::GetInstance()->hit_count(); 421} 422 423void AutomationResourceMessageFilter::OnRecordHistograms( 424 const std::vector<std::string>& histogram_list) { 425 for (size_t index = 0; index < histogram_list.size(); ++index) { 426 base::Histogram::DeserializeHistogramInfo(histogram_list[index]); 427 } 428} 429 430bool AutomationResourceMessageFilter::GetCookiesForUrl( 431 const GURL& url, net::CompletionCallback* callback) { 432 GetCookiesCompletion* get_cookies_callback = 433 static_cast<GetCookiesCompletion*>(callback); 434 435 RendererId renderer_key(get_cookies_callback->render_process_id(), 436 get_cookies_callback->render_view_id()); 437 438 RenderViewMap::iterator automation_details_iter( 439 filtered_render_views_.Get().find(renderer_key)); 440 441 if (automation_details_iter == filtered_render_views_.Get().end()) { 442 return false; 443 } 444 445 DCHECK(automation_details_iter->second.filter != NULL); 446 DCHECK(automation_details_iter->second.cookie_store_.get() != NULL); 447 448 int completion_callback_id = GetNextCompletionCallbackId(); 449 DCHECK(!ContainsKey(completion_callback_map_.Get(), completion_callback_id)); 450 451 CookieCompletionInfo cookie_info; 452 cookie_info.completion_callback = callback; 453 cookie_info.cookie_store = automation_details_iter->second.cookie_store_; 454 455 completion_callback_map_.Get()[completion_callback_id] = cookie_info; 456 457 DCHECK(automation_details_iter->second.filter != NULL); 458 459 if (automation_details_iter->second.filter) { 460 automation_details_iter->second.filter->Send( 461 new AutomationMsg_GetCookiesFromHost( 462 automation_details_iter->second.tab_handle, url, 463 completion_callback_id)); 464 } 465 return true; 466} 467 468void AutomationResourceMessageFilter::OnGetCookiesHostResponse( 469 int tab_handle, bool success, const GURL& url, const std::string& cookies, 470 int cookie_id) { 471 CompletionCallbackMap::iterator index = 472 completion_callback_map_.Get().find(cookie_id); 473 if (index != completion_callback_map_.Get().end()) { 474 net::CompletionCallback* callback = index->second.completion_callback; 475 476 scoped_refptr<net::CookieStore> cookie_store = index->second.cookie_store; 477 478 DCHECK(callback != NULL); 479 DCHECK(cookie_store.get() != NULL); 480 481 completion_callback_map_.Get().erase(index); 482 483 OnGetCookiesHostResponseInternal(tab_handle, success, url, cookies, 484 callback, cookie_store.get()); 485 } else { 486 NOTREACHED() << "Received invalid completion callback id:" 487 << cookie_id; 488 } 489} 490 491void AutomationResourceMessageFilter::OnGetCookiesHostResponseInternal( 492 int tab_handle, bool success, const GURL& url, const std::string& cookies, 493 net::CompletionCallback* callback, net::CookieStore* cookie_store) { 494 DCHECK(callback); 495 DCHECK(cookie_store); 496 497 GetCookiesCompletion* get_cookies_callback = 498 static_cast<GetCookiesCompletion*>(callback); 499 500 get_cookies_callback->set_cookie_store(cookie_store); 501 502 // Set the cookie in the cookie store so that the callback can read it. 503 cookie_store->SetCookieWithOptions(url, cookies, net::CookieOptions()); 504 505 Tuple1<int> params; 506 params.a = success ? net::OK : net::ERR_ACCESS_DENIED; 507 callback->RunWithParams(params); 508 509 // The cookie for this URL is only valid until it is read by the callback. 510 cookie_store->SetCookieWithOptions(url, "", net::CookieOptions()); 511} 512 513bool AutomationResourceMessageFilter::SetCookiesForUrl( 514 const GURL& url, const std::string& cookie_line, 515 net::CompletionCallback* callback) { 516 SetCookieCompletion* set_cookies_callback = 517 static_cast<SetCookieCompletion*>(callback); 518 519 RenderViewMap::iterator automation_details_iter( 520 filtered_render_views_.Get().find(RendererId( 521 set_cookies_callback->render_process_id(), 522 set_cookies_callback->render_view_id()))); 523 524 if (automation_details_iter == filtered_render_views_.Get().end()) { 525 return false; 526 } 527 528 DCHECK(automation_details_iter->second.filter != NULL); 529 530 if (automation_details_iter->second.filter) { 531 automation_details_iter->second.filter->Send( 532 new AutomationMsg_SetCookieAsync( 533 automation_details_iter->second.tab_handle, url, cookie_line)); 534 } 535 536 return true; 537} 538 539// static 540void AutomationResourceMessageFilter::ResumeJobsForPendingView( 541 int tab_handle, 542 AutomationResourceMessageFilter* old_filter, 543 AutomationResourceMessageFilter* new_filter) { 544 DCHECK(old_filter != NULL); 545 DCHECK(new_filter != NULL); 546 547 RequestMap pending_requests = old_filter->pending_request_map_; 548 549 for (RequestMap::iterator index = old_filter->pending_request_map_.begin(); 550 index != old_filter->pending_request_map_.end(); index++) { 551 scoped_refptr<URLRequestAutomationJob> job = (*index).second; 552 DCHECK_EQ(job->message_filter(), old_filter); 553 DCHECK(job->is_pending()); 554 // StartPendingJob will register the job with the new filter. 555 job->StartPendingJob(tab_handle, new_filter); 556 } 557 558 old_filter->pending_request_map_.clear(); 559} 560