1// Copyright (c) 2012 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/bind.h" 8#include "base/metrics/histogram.h" 9#include "base/path_service.h" 10#include "base/stl_util.h" 11#include "chrome/browser/automation/url_request_automation_job.h" 12#include "chrome/browser/content_settings/tab_specific_content_settings.h" 13#include "chrome/browser/net/url_request_mock_util.h" 14#include "chrome/common/automation_messages.h" 15#include "chrome/common/chrome_paths.h" 16#include "chrome/common/render_messages.h" 17#include "content/public/browser/browser_message_filter.h" 18#include "content/public/browser/browser_thread.h" 19#include "net/base/net_errors.h" 20#include "net/url_request/url_request_context.h" 21#include "net/url_request/url_request_filter.h" 22#include "url/gurl.h" 23 24using content::BrowserMessageFilter; 25using content::BrowserThread; 26 27base::LazyInstance<AutomationResourceMessageFilter::RenderViewMap> 28 AutomationResourceMessageFilter::filtered_render_views_ = 29 LAZY_INSTANCE_INITIALIZER; 30 31base::LazyInstance<AutomationResourceMessageFilter::CompletionCallbackMap> 32 AutomationResourceMessageFilter::completion_callback_map_ = 33 LAZY_INSTANCE_INITIALIZER; 34 35int AutomationResourceMessageFilter::unique_request_id_ = 1; 36int AutomationResourceMessageFilter::next_completion_callback_id_ = 0; 37 38AutomationResourceMessageFilter::AutomationDetails::AutomationDetails() 39 : tab_handle(0), 40 ref_count(1), 41 is_pending_render_view(false) { 42} 43 44AutomationResourceMessageFilter::AutomationDetails::AutomationDetails( 45 int tab, 46 AutomationResourceMessageFilter* flt, 47 bool pending_view) 48 : tab_handle(tab), ref_count(1), filter(flt), 49 is_pending_render_view(pending_view) { 50} 51 52AutomationResourceMessageFilter::AutomationDetails::~AutomationDetails() {} 53 54struct AutomationResourceMessageFilter::CookieCompletionInfo { 55 scoped_refptr<BrowserMessageFilter> filter; 56 net::URLRequestContext* context; 57 int render_process_id; 58 IPC::Message* reply_msg; 59 scoped_refptr<AutomationResourceMessageFilter> automation_message_filter; 60}; 61 62AutomationResourceMessageFilter::AutomationResourceMessageFilter() 63 : channel_(NULL) { 64 // Ensure that an instance of the callback map is created. 65 completion_callback_map_.Get(); 66 // Ensure that an instance of the render view map is created. 67 filtered_render_views_.Get(); 68 69 BrowserThread::PostTask( 70 BrowserThread::IO, FROM_HERE, 71 base::Bind(&URLRequestAutomationJob::EnsureProtocolFactoryRegistered)); 72} 73 74AutomationResourceMessageFilter::~AutomationResourceMessageFilter() { 75} 76 77// Called on the IPC thread: 78void AutomationResourceMessageFilter::OnFilterAdded(IPC::Channel* channel) { 79 DCHECK(!channel_); 80 channel_ = channel; 81} 82 83void AutomationResourceMessageFilter::OnFilterRemoved() { 84 channel_ = NULL; 85} 86 87// Called on the IPC thread: 88void AutomationResourceMessageFilter::OnChannelConnected(int32 peer_pid) { 89} 90 91// Called on the IPC thread: 92void AutomationResourceMessageFilter::OnChannelClosing() { 93 channel_ = NULL; 94 request_map_.clear(); 95 96 // Only erase RenderViews which are associated with this 97 // AutomationResourceMessageFilter instance. 98 RenderViewMap::iterator index = filtered_render_views_.Get().begin(); 99 while (index != filtered_render_views_.Get().end()) { 100 const AutomationDetails& details = (*index).second; 101 if (details.filter.get() == this) { 102 filtered_render_views_.Get().erase(index++); 103 } else { 104 index++; 105 } 106 } 107 108 CompletionCallbackMap::iterator callback_index = 109 completion_callback_map_.Get().begin(); 110 while (callback_index != completion_callback_map_.Get().end()) { 111 const CookieCompletionInfo& cookie_completion_info = 112 (*callback_index).second; 113 if (cookie_completion_info.automation_message_filter.get() == this) { 114 completion_callback_map_.Get().erase(callback_index++); 115 } else { 116 callback_index++; 117 } 118 } 119} 120 121// Called on the IPC thread: 122bool AutomationResourceMessageFilter::OnMessageReceived( 123 const IPC::Message& message) { 124 int request_id; 125 if (URLRequestAutomationJob::MayFilterMessage(message, &request_id)) { 126 RequestMap::iterator it = request_map_.find(request_id); 127 if (it != request_map_.end()) { 128 URLRequestAutomationJob* job = it->second; 129 DCHECK(job); 130 if (job) { 131 job->OnMessage(message); 132 return true; 133 } 134 } else { 135 // This could occur if the request was stopped from Chrome which would 136 // delete it from the request map. If we receive data for this request 137 // from the host we should ignore it. 138 LOG(ERROR) << "Failed to find request id:" << request_id; 139 return true; 140 } 141 } 142 143 bool handled = true; 144 bool deserialize_success = true; 145 IPC_BEGIN_MESSAGE_MAP_EX(AutomationResourceMessageFilter, 146 message, 147 deserialize_success) 148 IPC_MESSAGE_HANDLER(AutomationMsg_GetCookiesHostResponse, 149 OnGetCookiesHostResponse) 150 IPC_MESSAGE_UNHANDLED(handled = false) 151 IPC_END_MESSAGE_MAP_EX() 152 153 if (!deserialize_success) { 154 LOG(ERROR) << "Failed to deserialize IPC message. " 155 << "Closing the automation channel."; 156 channel_->Close(); 157 } 158 159 return handled; 160} 161 162// Called on the IPC thread: 163bool AutomationResourceMessageFilter::Send(IPC::Message* message) { 164 // This has to be called on the IO thread. 165 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 166 if (!channel_) { 167 delete message; 168 return false; 169 } 170 171 return channel_->Send(message); 172} 173 174bool AutomationResourceMessageFilter::RegisterRequest( 175 URLRequestAutomationJob* job) { 176 if (!job) { 177 NOTREACHED(); 178 return false; 179 } 180 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 181 182 // Register pending jobs in the pending request map for servicing later. 183 if (job->is_pending()) { 184 DCHECK(!ContainsKey(pending_request_map_, job->id())); 185 DCHECK(!ContainsKey(request_map_, job->id())); 186 pending_request_map_[job->id()] = job; 187 } else { 188 DCHECK(!ContainsKey(request_map_, job->id())); 189 DCHECK(!ContainsKey(pending_request_map_, job->id())); 190 request_map_[job->id()] = job; 191 } 192 193 return true; 194} 195 196void AutomationResourceMessageFilter::UnRegisterRequest( 197 URLRequestAutomationJob* job) { 198 if (!job) { 199 NOTREACHED(); 200 return; 201 } 202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 203 204 if (job->is_pending()) { 205 DCHECK(ContainsKey(pending_request_map_, job->id())); 206 pending_request_map_.erase(job->id()); 207 } else { 208 request_map_.erase(job->id()); 209 } 210} 211 212bool AutomationResourceMessageFilter::RegisterRenderView( 213 int renderer_pid, int renderer_id, int tab_handle, 214 AutomationResourceMessageFilter* filter, 215 bool pending_view) { 216 if (!renderer_pid || !renderer_id || !tab_handle) { 217 NOTREACHED(); 218 return false; 219 } 220 221 BrowserThread::PostTask( 222 BrowserThread::IO, FROM_HERE, 223 base::Bind(&AutomationResourceMessageFilter::RegisterRenderViewInIOThread, 224 renderer_pid, renderer_id, tab_handle, 225 make_scoped_refptr(filter), pending_view)); 226 return true; 227} 228 229void AutomationResourceMessageFilter::UnRegisterRenderView( 230 int renderer_pid, int renderer_id) { 231 BrowserThread::PostTask( 232 BrowserThread::IO, FROM_HERE, 233 base::Bind( 234 &AutomationResourceMessageFilter::UnRegisterRenderViewInIOThread, 235 renderer_pid, renderer_id)); 236} 237 238bool AutomationResourceMessageFilter::ResumePendingRenderView( 239 int renderer_pid, int renderer_id, int tab_handle, 240 AutomationResourceMessageFilter* filter) { 241 if (!renderer_pid || !renderer_id || !tab_handle) { 242 NOTREACHED(); 243 return false; 244 } 245 246 BrowserThread::PostTask( 247 BrowserThread::IO, FROM_HERE, 248 base::Bind( 249 &AutomationResourceMessageFilter::ResumePendingRenderViewInIOThread, 250 renderer_pid, renderer_id, tab_handle, make_scoped_refptr(filter))); 251 return true; 252} 253 254void AutomationResourceMessageFilter::RegisterRenderViewInIOThread( 255 int renderer_pid, int renderer_id, 256 int tab_handle, AutomationResourceMessageFilter* filter, 257 bool pending_view) { 258 RendererId renderer_key(renderer_pid, renderer_id); 259 260 RenderViewMap::iterator automation_details_iter( 261 filtered_render_views_.Get().find(renderer_key)); 262 // We need to match the renderer key and the AutomationResourceMessageFilter 263 // instances. If the filter instances are different it means that a new 264 // automation channel (External host process) was created for this tab. 265 if (automation_details_iter != filtered_render_views_.Get().end() && 266 automation_details_iter->second.filter.get() == filter) { 267 DCHECK_GT(automation_details_iter->second.ref_count, 0); 268 automation_details_iter->second.ref_count++; 269 // The tab handle and the pending status may have changed:- 270 // 1.A external tab container is being destroyed and a new one is being 271 // created. 272 // 2.The external tab container being destroyed receives a RVH created 273 // notification for the new RVH created to host the newly created tab. 274 // In this case the tab handle in the AutomationDetails structure would 275 // be invalid as it points to a destroyed tab. 276 // We need to replace the handle of the external tab being destroyed with 277 // the new one that is being created." 278 automation_details_iter->second.tab_handle = tab_handle; 279 automation_details_iter->second.is_pending_render_view = pending_view; 280 } else { 281 filtered_render_views_.Get()[renderer_key] = 282 AutomationDetails(tab_handle, filter, pending_view); 283 } 284} 285 286// static 287void AutomationResourceMessageFilter::UnRegisterRenderViewInIOThread( 288 int renderer_pid, int renderer_id) { 289 RenderViewMap::iterator automation_details_iter( 290 filtered_render_views_.Get().find(RendererId(renderer_pid, 291 renderer_id))); 292 293 if (automation_details_iter == filtered_render_views_.Get().end()) { 294 // This is called for all RenderViewHosts, so it's fine if we don't find a 295 // match. 296 return; 297 } 298 299 automation_details_iter->second.ref_count--; 300 301 if (automation_details_iter->second.ref_count <= 0) { 302 filtered_render_views_.Get().erase(RendererId(renderer_pid, renderer_id)); 303 } 304} 305 306// static 307void AutomationResourceMessageFilter::ResumePendingRenderViewInIOThread( 308 int renderer_pid, int renderer_id, int tab_handle, 309 AutomationResourceMessageFilter* filter) { 310 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 311 312 RendererId renderer_key(renderer_pid, renderer_id); 313 314 RenderViewMap::iterator automation_details_iter( 315 filtered_render_views_.Get().find(renderer_key)); 316 317 DCHECK(automation_details_iter != filtered_render_views_.Get().end()) 318 << "Failed to find pending view for renderer pid:" 319 << renderer_pid << ", render view id:" << renderer_id; 320 321 DCHECK(automation_details_iter->second.is_pending_render_view); 322 323 AutomationResourceMessageFilter* old_filter = 324 automation_details_iter->second.filter.get(); 325 DCHECK(old_filter != NULL); 326 327 filtered_render_views_.Get()[renderer_key] = 328 AutomationDetails(tab_handle, filter, false); 329 330 ResumeJobsForPendingView(tab_handle, old_filter, filter); 331} 332 333bool AutomationResourceMessageFilter::LookupRegisteredRenderView( 334 int renderer_pid, int renderer_id, AutomationDetails* details) { 335 bool found = false; 336 RenderViewMap::iterator it = filtered_render_views_.Get().find(RendererId( 337 renderer_pid, renderer_id)); 338 if (it != filtered_render_views_.Get().end()) { 339 found = true; 340 if (details) 341 *details = it->second; 342 } 343 344 return found; 345} 346 347bool AutomationResourceMessageFilter::GetAutomationRequestId( 348 int request_id, int* automation_request_id) { 349 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 350 351 RequestMap::iterator it = request_map_.begin(); 352 while (it != request_map_.end()) { 353 URLRequestAutomationJob* job = it->second; 354 DCHECK(job); 355 if (job && job->request_id() == request_id) { 356 *automation_request_id = job->id(); 357 return true; 358 } 359 it++; 360 } 361 362 return false; 363} 364 365bool AutomationResourceMessageFilter::SendDownloadRequestToHost( 366 int routing_id, int tab_handle, int request_id) { 367 int automation_request_id = 0; 368 bool valid_id = GetAutomationRequestId(request_id, &automation_request_id); 369 if (!valid_id) { 370 LOG(ERROR) << "Invalid request id: " << request_id; 371 return false; 372 } 373 374 return Send(new AutomationMsg_DownloadRequestInHost(tab_handle, 375 automation_request_id)); 376} 377 378bool AutomationResourceMessageFilter::ShouldFilterCookieMessages( 379 int render_process_id, int render_view_id) { 380 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 381 RendererId renderer_key(render_process_id, render_view_id); 382 RenderViewMap::iterator automation_details_iter( 383 filtered_render_views_.Get().find(renderer_key)); 384 return automation_details_iter != filtered_render_views_.Get().end(); 385} 386 387void AutomationResourceMessageFilter::GetCookiesForUrl( 388 BrowserMessageFilter* filter, net::URLRequestContext* context, 389 int render_process_id, IPC::Message* reply_msg, const GURL& url) { 390 391 RendererId renderer_key(render_process_id, reply_msg->routing_id()); 392 393 RenderViewMap::iterator automation_details_iter( 394 filtered_render_views_.Get().find(renderer_key)); 395 396 DCHECK(automation_details_iter != filtered_render_views_.Get().end()); 397 DCHECK(automation_details_iter->second.filter.get() != NULL); 398 399 int completion_callback_id = GetNextCompletionCallbackId(); 400 DCHECK(!ContainsKey(completion_callback_map_.Get(), completion_callback_id)); 401 402 CookieCompletionInfo cookie_info; 403 cookie_info.filter = filter; 404 cookie_info.context = context; 405 cookie_info.render_process_id = render_process_id; 406 cookie_info.reply_msg = reply_msg; 407 cookie_info.automation_message_filter = 408 automation_details_iter->second.filter; 409 410 completion_callback_map_.Get()[completion_callback_id] = cookie_info; 411 412 DCHECK(automation_details_iter->second.filter.get() != NULL); 413 414 if (automation_details_iter->second.filter.get()) { 415 automation_details_iter->second.filter 416 ->Send(new AutomationMsg_GetCookiesFromHost( 417 automation_details_iter->second.tab_handle, 418 url, 419 completion_callback_id)); 420 } 421} 422 423void AutomationResourceMessageFilter::OnGetCookiesHostResponse( 424 int tab_handle, bool success, const GURL& url, const std::string& cookies, 425 int cookie_id) { 426 CompletionCallbackMap::iterator index = 427 completion_callback_map_.Get().find(cookie_id); 428 if (index == completion_callback_map_.Get().end()) { 429 NOTREACHED() << "Received invalid completion callback id:" 430 << cookie_id; 431 return; 432 } 433 434 ChromeViewHostMsg_GetCookies::WriteReplyParams(index->second.reply_msg, 435 cookies); 436 index->second.filter->Send(index->second.reply_msg); 437 438 completion_callback_map_.Get().erase(index); 439} 440 441void AutomationResourceMessageFilter::SetCookiesForUrl( 442 int render_process_id, 443 int render_view_id, 444 const GURL& url, 445 const std::string& cookie_line) { 446 RenderViewMap::iterator automation_details_iter( 447 filtered_render_views_.Get().find(RendererId( 448 render_process_id, render_view_id))); 449 DCHECK(automation_details_iter != filtered_render_views_.Get().end()); 450 DCHECK(automation_details_iter->second.filter.get() != NULL); 451 452 if (automation_details_iter->second.filter.get()) { 453 automation_details_iter->second.filter 454 ->Send(new AutomationMsg_SetCookieAsync( 455 automation_details_iter->second.tab_handle, url, cookie_line)); 456 } 457} 458 459// static 460void AutomationResourceMessageFilter::ResumeJobsForPendingView( 461 int tab_handle, 462 AutomationResourceMessageFilter* old_filter, 463 AutomationResourceMessageFilter* new_filter) { 464 DCHECK(old_filter != NULL); 465 DCHECK(new_filter != NULL); 466 467 RequestMap pending_requests = old_filter->pending_request_map_; 468 old_filter->pending_request_map_.clear(); 469 470 for (RequestMap::iterator index = pending_requests.begin(); 471 index != pending_requests.end(); index++) { 472 URLRequestAutomationJob* job = (*index).second; 473 DCHECK_EQ(job->message_filter(), old_filter); 474 DCHECK(job->is_pending()); 475 // StartPendingJob will register the job with the new filter. 476 job->StartPendingJob(tab_handle, new_filter); 477 } 478} 479