| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/automation/automation_resource_message_filter.h" |
| |
| #include "base/path_service.h" |
| #include "base/metrics/histogram.h" |
| #include "base/stl_util.h" |
| #include "chrome/browser/automation/url_request_automation_job.h" |
| #include "chrome/browser/content_settings/tab_specific_content_settings.h" |
| #include "chrome/browser/net/url_request_mock_util.h" |
| #include "chrome/common/automation_messages.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "content/browser/browser_message_filter.h" |
| #include "content/browser/browser_thread.h" |
| #include "content/common/view_messages.h" |
| #include "googleurl/src/gurl.h" |
| #include "net/base/net_errors.h" |
| #include "net/url_request/url_request_context.h" |
| #include "net/url_request/url_request_filter.h" |
| |
| base::LazyInstance<AutomationResourceMessageFilter::RenderViewMap> |
| AutomationResourceMessageFilter::filtered_render_views_( |
| base::LINKER_INITIALIZED); |
| |
| base::LazyInstance<AutomationResourceMessageFilter::CompletionCallbackMap> |
| AutomationResourceMessageFilter::completion_callback_map_( |
| base::LINKER_INITIALIZED); |
| |
| int AutomationResourceMessageFilter::unique_request_id_ = 1; |
| int AutomationResourceMessageFilter::next_completion_callback_id_ = 0; |
| |
| AutomationResourceMessageFilter::AutomationDetails::AutomationDetails() |
| : tab_handle(0), |
| ref_count(1), |
| is_pending_render_view(false) { |
| } |
| |
| AutomationResourceMessageFilter::AutomationDetails::AutomationDetails( |
| int tab, |
| AutomationResourceMessageFilter* flt, |
| bool pending_view) |
| : tab_handle(tab), ref_count(1), filter(flt), |
| is_pending_render_view(pending_view) { |
| } |
| |
| AutomationResourceMessageFilter::AutomationDetails::~AutomationDetails() {} |
| |
| struct AutomationResourceMessageFilter::CookieCompletionInfo { |
| scoped_refptr<BrowserMessageFilter> filter; |
| scoped_refptr<net::URLRequestContext> context; |
| int render_process_id; |
| IPC::Message* reply_msg; |
| scoped_refptr<AutomationResourceMessageFilter> automation_message_filter; |
| }; |
| |
| AutomationResourceMessageFilter::AutomationResourceMessageFilter() |
| : channel_(NULL) { |
| // Ensure that an instance of the callback map is created. |
| completion_callback_map_.Get(); |
| // Ensure that an instance of the render view map is created. |
| filtered_render_views_.Get(); |
| |
| BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| NewRunnableFunction( |
| URLRequestAutomationJob::EnsureProtocolFactoryRegistered)); |
| } |
| |
| AutomationResourceMessageFilter::~AutomationResourceMessageFilter() { |
| } |
| |
| // Called on the IPC thread: |
| void AutomationResourceMessageFilter::OnFilterAdded(IPC::Channel* channel) { |
| DCHECK(!channel_); |
| channel_ = channel; |
| } |
| |
| void AutomationResourceMessageFilter::OnFilterRemoved() { |
| channel_ = NULL; |
| } |
| |
| // Called on the IPC thread: |
| void AutomationResourceMessageFilter::OnChannelConnected(int32 peer_pid) { |
| } |
| |
| // Called on the IPC thread: |
| void AutomationResourceMessageFilter::OnChannelClosing() { |
| channel_ = NULL; |
| request_map_.clear(); |
| |
| // Only erase RenderViews which are associated with this |
| // AutomationResourceMessageFilter instance. |
| RenderViewMap::iterator index = filtered_render_views_.Get().begin(); |
| while (index != filtered_render_views_.Get().end()) { |
| const AutomationDetails& details = (*index).second; |
| if (details.filter.get() == this) { |
| filtered_render_views_.Get().erase(index++); |
| } else { |
| index++; |
| } |
| } |
| |
| CompletionCallbackMap::iterator callback_index = |
| completion_callback_map_.Get().begin(); |
| while (callback_index != completion_callback_map_.Get().end()) { |
| const CookieCompletionInfo& cookie_completion_info = |
| (*callback_index).second; |
| if (cookie_completion_info.automation_message_filter.get() == this) { |
| completion_callback_map_.Get().erase(callback_index++); |
| } else { |
| callback_index++; |
| } |
| } |
| } |
| |
| // Called on the IPC thread: |
| bool AutomationResourceMessageFilter::OnMessageReceived( |
| const IPC::Message& message) { |
| int request_id; |
| if (URLRequestAutomationJob::MayFilterMessage(message, &request_id)) { |
| RequestMap::iterator it = request_map_.find(request_id); |
| if (it != request_map_.end()) { |
| URLRequestAutomationJob* job = it->second; |
| DCHECK(job); |
| if (job) { |
| job->OnMessage(message); |
| return true; |
| } |
| } else { |
| // This could occur if the request was stopped from Chrome which would |
| // delete it from the request map. If we receive data for this request |
| // from the host we should ignore it. |
| LOG(ERROR) << "Failed to find request id:" << request_id; |
| return true; |
| } |
| } |
| |
| bool handled = true; |
| IPC_BEGIN_MESSAGE_MAP(AutomationResourceMessageFilter, message) |
| IPC_MESSAGE_HANDLER(AutomationMsg_SetFilteredInet, |
| OnSetFilteredInet) |
| IPC_MESSAGE_HANDLER(AutomationMsg_GetFilteredInetHitCount, |
| OnGetFilteredInetHitCount) |
| IPC_MESSAGE_HANDLER(AutomationMsg_RecordHistograms, |
| OnRecordHistograms) |
| IPC_MESSAGE_HANDLER(AutomationMsg_GetCookiesHostResponse, |
| OnGetCookiesHostResponse) |
| IPC_MESSAGE_UNHANDLED(handled = false) |
| IPC_END_MESSAGE_MAP() |
| |
| return handled; |
| } |
| |
| // Called on the IPC thread: |
| bool AutomationResourceMessageFilter::Send(IPC::Message* message) { |
| // This has to be called on the IO thread. |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| if (!channel_) { |
| delete message; |
| return false; |
| } |
| |
| return channel_->Send(message); |
| } |
| |
| bool AutomationResourceMessageFilter::RegisterRequest( |
| URLRequestAutomationJob* job) { |
| if (!job) { |
| NOTREACHED(); |
| return false; |
| } |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| |
| // Register pending jobs in the pending request map for servicing later. |
| if (job->is_pending()) { |
| DCHECK(!ContainsKey(pending_request_map_, job->id())); |
| DCHECK(!ContainsKey(request_map_, job->id())); |
| pending_request_map_[job->id()] = job; |
| } else { |
| DCHECK(!ContainsKey(request_map_, job->id())); |
| DCHECK(!ContainsKey(pending_request_map_, job->id())); |
| request_map_[job->id()] = job; |
| } |
| |
| return true; |
| } |
| |
| void AutomationResourceMessageFilter::UnRegisterRequest( |
| URLRequestAutomationJob* job) { |
| if (!job) { |
| NOTREACHED(); |
| return; |
| } |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| |
| if (job->is_pending()) { |
| DCHECK(ContainsKey(pending_request_map_, job->id())); |
| pending_request_map_.erase(job->id()); |
| } else { |
| request_map_.erase(job->id()); |
| } |
| } |
| |
| bool AutomationResourceMessageFilter::RegisterRenderView( |
| int renderer_pid, int renderer_id, int tab_handle, |
| AutomationResourceMessageFilter* filter, |
| bool pending_view) { |
| if (!renderer_pid || !renderer_id || !tab_handle) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| NewRunnableFunction( |
| AutomationResourceMessageFilter::RegisterRenderViewInIOThread, |
| renderer_pid, |
| renderer_id, |
| tab_handle, |
| make_scoped_refptr(filter), |
| pending_view)); |
| return true; |
| } |
| |
| void AutomationResourceMessageFilter::UnRegisterRenderView( |
| int renderer_pid, int renderer_id) { |
| BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| NewRunnableFunction( |
| AutomationResourceMessageFilter::UnRegisterRenderViewInIOThread, |
| renderer_pid, renderer_id)); |
| } |
| |
| bool AutomationResourceMessageFilter::ResumePendingRenderView( |
| int renderer_pid, int renderer_id, int tab_handle, |
| AutomationResourceMessageFilter* filter) { |
| if (!renderer_pid || !renderer_id || !tab_handle) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| NewRunnableFunction( |
| AutomationResourceMessageFilter::ResumePendingRenderViewInIOThread, |
| renderer_pid, |
| renderer_id, |
| tab_handle, |
| make_scoped_refptr(filter))); |
| return true; |
| } |
| |
| void AutomationResourceMessageFilter::RegisterRenderViewInIOThread( |
| int renderer_pid, int renderer_id, |
| int tab_handle, AutomationResourceMessageFilter* filter, |
| bool pending_view) { |
| RendererId renderer_key(renderer_pid, renderer_id); |
| |
| RenderViewMap::iterator automation_details_iter( |
| filtered_render_views_.Get().find(renderer_key)); |
| if (automation_details_iter != filtered_render_views_.Get().end()) { |
| DCHECK_GT(automation_details_iter->second.ref_count, 0); |
| automation_details_iter->second.ref_count++; |
| } else { |
| filtered_render_views_.Get()[renderer_key] = |
| AutomationDetails(tab_handle, filter, pending_view); |
| } |
| } |
| |
| // static |
| void AutomationResourceMessageFilter::UnRegisterRenderViewInIOThread( |
| int renderer_pid, int renderer_id) { |
| RenderViewMap::iterator automation_details_iter( |
| filtered_render_views_.Get().find(RendererId(renderer_pid, |
| renderer_id))); |
| |
| if (automation_details_iter == filtered_render_views_.Get().end()) { |
| // This is called for all RenderViewHosts, so it's fine if we don't find a |
| // match. |
| return; |
| } |
| |
| automation_details_iter->second.ref_count--; |
| |
| if (automation_details_iter->second.ref_count <= 0) { |
| filtered_render_views_.Get().erase(RendererId(renderer_pid, renderer_id)); |
| } |
| } |
| |
| // static |
| bool AutomationResourceMessageFilter::ResumePendingRenderViewInIOThread( |
| int renderer_pid, int renderer_id, int tab_handle, |
| AutomationResourceMessageFilter* filter) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| |
| RendererId renderer_key(renderer_pid, renderer_id); |
| |
| RenderViewMap::iterator automation_details_iter( |
| filtered_render_views_.Get().find(renderer_key)); |
| |
| if (automation_details_iter == filtered_render_views_.Get().end()) { |
| NOTREACHED() << "Failed to find pending view for renderer pid:" |
| << renderer_pid |
| << ", render view id:" |
| << renderer_id; |
| return false; |
| } |
| |
| DCHECK(automation_details_iter->second.is_pending_render_view); |
| |
| AutomationResourceMessageFilter* old_filter = |
| automation_details_iter->second.filter; |
| DCHECK(old_filter != NULL); |
| |
| filtered_render_views_.Get()[renderer_key] = |
| AutomationDetails(tab_handle, filter, false); |
| |
| ResumeJobsForPendingView(tab_handle, old_filter, filter); |
| return true; |
| } |
| |
| bool AutomationResourceMessageFilter::LookupRegisteredRenderView( |
| int renderer_pid, int renderer_id, AutomationDetails* details) { |
| bool found = false; |
| RenderViewMap::iterator it = filtered_render_views_.Get().find(RendererId( |
| renderer_pid, renderer_id)); |
| if (it != filtered_render_views_.Get().end()) { |
| found = true; |
| if (details) |
| *details = it->second; |
| } |
| |
| return found; |
| } |
| |
| bool AutomationResourceMessageFilter::GetAutomationRequestId( |
| int request_id, int* automation_request_id) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| |
| RequestMap::iterator it = request_map_.begin(); |
| while (it != request_map_.end()) { |
| URLRequestAutomationJob* job = it->second; |
| DCHECK(job); |
| if (job && job->request_id() == request_id) { |
| *automation_request_id = job->id(); |
| return true; |
| } |
| it++; |
| } |
| |
| return false; |
| } |
| |
| bool AutomationResourceMessageFilter::SendDownloadRequestToHost( |
| int routing_id, int tab_handle, int request_id) { |
| int automation_request_id = 0; |
| bool valid_id = GetAutomationRequestId(request_id, &automation_request_id); |
| if (!valid_id) { |
| LOG(ERROR) << "Invalid request id: " << request_id; |
| return false; |
| } |
| |
| return Send(new AutomationMsg_DownloadRequestInHost(tab_handle, |
| automation_request_id)); |
| } |
| |
| void AutomationResourceMessageFilter::OnSetFilteredInet(bool enable) { |
| chrome_browser_net::SetUrlRequestMocksEnabled(enable); |
| } |
| |
| void AutomationResourceMessageFilter::OnGetFilteredInetHitCount( |
| int* hit_count) { |
| *hit_count = net::URLRequestFilter::GetInstance()->hit_count(); |
| } |
| |
| void AutomationResourceMessageFilter::OnRecordHistograms( |
| const std::vector<std::string>& histogram_list) { |
| for (size_t index = 0; index < histogram_list.size(); ++index) { |
| base::Histogram::DeserializeHistogramInfo(histogram_list[index]); |
| } |
| } |
| |
| bool AutomationResourceMessageFilter::ShouldFilterCookieMessages( |
| int render_process_id, int render_view_id) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| RendererId renderer_key(render_process_id, render_view_id); |
| RenderViewMap::iterator automation_details_iter( |
| filtered_render_views_.Get().find(renderer_key)); |
| return automation_details_iter != filtered_render_views_.Get().end(); |
| } |
| |
| void AutomationResourceMessageFilter::GetCookiesForUrl( |
| BrowserMessageFilter* filter, net::URLRequestContext* context, |
| int render_process_id, IPC::Message* reply_msg, const GURL& url) { |
| |
| RendererId renderer_key(render_process_id, reply_msg->routing_id()); |
| |
| RenderViewMap::iterator automation_details_iter( |
| filtered_render_views_.Get().find(renderer_key)); |
| |
| DCHECK(automation_details_iter != filtered_render_views_.Get().end()); |
| DCHECK(automation_details_iter->second.filter != NULL); |
| |
| int completion_callback_id = GetNextCompletionCallbackId(); |
| DCHECK(!ContainsKey(completion_callback_map_.Get(), completion_callback_id)); |
| |
| CookieCompletionInfo cookie_info; |
| cookie_info.filter = filter; |
| cookie_info.context = context; |
| cookie_info.render_process_id = render_process_id; |
| cookie_info.reply_msg = reply_msg; |
| cookie_info.automation_message_filter = |
| automation_details_iter->second.filter; |
| |
| completion_callback_map_.Get()[completion_callback_id] = cookie_info; |
| |
| DCHECK(automation_details_iter->second.filter != NULL); |
| |
| if (automation_details_iter->second.filter) { |
| automation_details_iter->second.filter->Send( |
| new AutomationMsg_GetCookiesFromHost( |
| automation_details_iter->second.tab_handle, url, |
| completion_callback_id)); |
| } |
| } |
| |
| void AutomationResourceMessageFilter::OnGetCookiesHostResponse( |
| int tab_handle, bool success, const GURL& url, const std::string& cookies, |
| int cookie_id) { |
| CompletionCallbackMap::iterator index = |
| completion_callback_map_.Get().find(cookie_id); |
| if (index == completion_callback_map_.Get().end()) { |
| NOTREACHED() << "Received invalid completion callback id:" |
| << cookie_id; |
| return; |
| } |
| |
| ViewHostMsg_GetCookies::WriteReplyParams(index->second.reply_msg, cookies); |
| index->second.filter->Send(index->second.reply_msg); |
| |
| completion_callback_map_.Get().erase(index); |
| } |
| |
| void AutomationResourceMessageFilter::SetCookiesForUrl( |
| int render_process_id, |
| int render_view_id, |
| const GURL& url, |
| const std::string& cookie_line) { |
| RenderViewMap::iterator automation_details_iter( |
| filtered_render_views_.Get().find(RendererId( |
| render_process_id, render_view_id))); |
| DCHECK(automation_details_iter != filtered_render_views_.Get().end()); |
| DCHECK(automation_details_iter->second.filter != NULL); |
| |
| if (automation_details_iter->second.filter) { |
| automation_details_iter->second.filter->Send( |
| new AutomationMsg_SetCookieAsync( |
| automation_details_iter->second.tab_handle, url, cookie_line)); |
| } |
| } |
| |
| // static |
| void AutomationResourceMessageFilter::ResumeJobsForPendingView( |
| int tab_handle, |
| AutomationResourceMessageFilter* old_filter, |
| AutomationResourceMessageFilter* new_filter) { |
| DCHECK(old_filter != NULL); |
| DCHECK(new_filter != NULL); |
| |
| RequestMap pending_requests = old_filter->pending_request_map_; |
| |
| for (RequestMap::iterator index = old_filter->pending_request_map_.begin(); |
| index != old_filter->pending_request_map_.end(); index++) { |
| URLRequestAutomationJob* job = (*index).second; |
| DCHECK_EQ(job->message_filter(), old_filter); |
| DCHECK(job->is_pending()); |
| // StartPendingJob will register the job with the new filter. |
| job->StartPendingJob(tab_handle, new_filter); |
| } |
| |
| old_filter->pending_request_map_.clear(); |
| } |