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.
5 #include "chrome/browser/automation/automation_resource_message_filter.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"
24 using content::BrowserMessageFilter;
25 using content::BrowserThread;
27 base::LazyInstance<AutomationResourceMessageFilter::RenderViewMap>
28 AutomationResourceMessageFilter::filtered_render_views_ =
29 LAZY_INSTANCE_INITIALIZER;
31 base::LazyInstance<AutomationResourceMessageFilter::CompletionCallbackMap>
32 AutomationResourceMessageFilter::completion_callback_map_ =
33 LAZY_INSTANCE_INITIALIZER;
35 int AutomationResourceMessageFilter::unique_request_id_ = 1;
36 int AutomationResourceMessageFilter::next_completion_callback_id_ = 0;
38 AutomationResourceMessageFilter::AutomationDetails::AutomationDetails()
41 is_pending_render_view(false) {
44 AutomationResourceMessageFilter::AutomationDetails::AutomationDetails(
46 AutomationResourceMessageFilter* flt,
48 : tab_handle(tab), ref_count(1), filter(flt),
49 is_pending_render_view(pending_view) {
52 AutomationResourceMessageFilter::AutomationDetails::~AutomationDetails() {}
54 struct 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;
62 AutomationResourceMessageFilter::AutomationResourceMessageFilter()
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();
69 BrowserThread::PostTask(
70 BrowserThread::IO, FROM_HERE,
71 base::Bind(&URLRequestAutomationJob::EnsureProtocolFactoryRegistered));
74 AutomationResourceMessageFilter::~AutomationResourceMessageFilter() {
77 // Called on the IPC thread:
78 void AutomationResourceMessageFilter::OnFilterAdded(IPC::Channel* channel) {
83 void AutomationResourceMessageFilter::OnFilterRemoved() {
87 // Called on the IPC thread:
88 void AutomationResourceMessageFilter::OnChannelConnected(int32 peer_pid) {
91 // Called on the IPC thread:
92 void AutomationResourceMessageFilter::OnChannelClosing() {
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++);
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++);
121 // Called on the IPC thread:
122 bool AutomationResourceMessageFilter::OnMessageReceived(
123 const IPC::Message& message) {
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;
131 job->OnMessage(message);
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;
144 bool deserialize_success = true;
145 IPC_BEGIN_MESSAGE_MAP_EX(AutomationResourceMessageFilter,
148 IPC_MESSAGE_HANDLER(AutomationMsg_GetCookiesHostResponse,
149 OnGetCookiesHostResponse)
150 IPC_MESSAGE_UNHANDLED(handled = false)
151 IPC_END_MESSAGE_MAP_EX()
153 if (!deserialize_success) {
154 LOG(ERROR) << "Failed to deserialize IPC message. "
155 << "Closing the automation channel.";
162 // Called on the IPC thread:
163 bool AutomationResourceMessageFilter::Send(IPC::Message* message) {
164 // This has to be called on the IO thread.
165 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
171 return channel_->Send(message);
174 bool AutomationResourceMessageFilter::RegisterRequest(
175 URLRequestAutomationJob* job) {
180 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
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;
188 DCHECK(!ContainsKey(request_map_, job->id()));
189 DCHECK(!ContainsKey(pending_request_map_, job->id()));
190 request_map_[job->id()] = job;
196 void AutomationResourceMessageFilter::UnRegisterRequest(
197 URLRequestAutomationJob* job) {
202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
204 if (job->is_pending()) {
205 DCHECK(ContainsKey(pending_request_map_, job->id()));
206 pending_request_map_.erase(job->id());
208 request_map_.erase(job->id());
212 bool AutomationResourceMessageFilter::RegisterRenderView(
213 int renderer_pid, int renderer_id, int tab_handle,
214 AutomationResourceMessageFilter* filter,
216 if (!renderer_pid || !renderer_id || !tab_handle) {
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));
229 void AutomationResourceMessageFilter::UnRegisterRenderView(
230 int renderer_pid, int renderer_id) {
231 BrowserThread::PostTask(
232 BrowserThread::IO, FROM_HERE,
234 &AutomationResourceMessageFilter::UnRegisterRenderViewInIOThread,
235 renderer_pid, renderer_id));
238 bool AutomationResourceMessageFilter::ResumePendingRenderView(
239 int renderer_pid, int renderer_id, int tab_handle,
240 AutomationResourceMessageFilter* filter) {
241 if (!renderer_pid || !renderer_id || !tab_handle) {
246 BrowserThread::PostTask(
247 BrowserThread::IO, FROM_HERE,
249 &AutomationResourceMessageFilter::ResumePendingRenderViewInIOThread,
250 renderer_pid, renderer_id, tab_handle, make_scoped_refptr(filter)));
254 void AutomationResourceMessageFilter::RegisterRenderViewInIOThread(
255 int renderer_pid, int renderer_id,
256 int tab_handle, AutomationResourceMessageFilter* filter,
258 RendererId renderer_key(renderer_pid, renderer_id);
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
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;
281 filtered_render_views_.Get()[renderer_key] =
282 AutomationDetails(tab_handle, filter, pending_view);
287 void AutomationResourceMessageFilter::UnRegisterRenderViewInIOThread(
288 int renderer_pid, int renderer_id) {
289 RenderViewMap::iterator automation_details_iter(
290 filtered_render_views_.Get().find(RendererId(renderer_pid,
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
299 automation_details_iter->second.ref_count--;
301 if (automation_details_iter->second.ref_count <= 0) {
302 filtered_render_views_.Get().erase(RendererId(renderer_pid, renderer_id));
307 void AutomationResourceMessageFilter::ResumePendingRenderViewInIOThread(
308 int renderer_pid, int renderer_id, int tab_handle,
309 AutomationResourceMessageFilter* filter) {
310 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
312 RendererId renderer_key(renderer_pid, renderer_id);
314 RenderViewMap::iterator automation_details_iter(
315 filtered_render_views_.Get().find(renderer_key));
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;
321 DCHECK(automation_details_iter->second.is_pending_render_view);
323 AutomationResourceMessageFilter* old_filter =
324 automation_details_iter->second.filter.get();
325 DCHECK(old_filter != NULL);
327 filtered_render_views_.Get()[renderer_key] =
328 AutomationDetails(tab_handle, filter, false);
330 ResumeJobsForPendingView(tab_handle, old_filter, filter);
333 bool AutomationResourceMessageFilter::LookupRegisteredRenderView(
334 int renderer_pid, int renderer_id, AutomationDetails* details) {
336 RenderViewMap::iterator it = filtered_render_views_.Get().find(RendererId(
337 renderer_pid, renderer_id));
338 if (it != filtered_render_views_.Get().end()) {
341 *details = it->second;
347 bool AutomationResourceMessageFilter::GetAutomationRequestId(
348 int request_id, int* automation_request_id) {
349 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
351 RequestMap::iterator it = request_map_.begin();
352 while (it != request_map_.end()) {
353 URLRequestAutomationJob* job = it->second;
355 if (job && job->request_id() == request_id) {
356 *automation_request_id = job->id();
365 bool 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);
370 LOG(ERROR) << "Invalid request id: " << request_id;
374 return Send(new AutomationMsg_DownloadRequestInHost(tab_handle,
375 automation_request_id));
378 bool 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();
387 void AutomationResourceMessageFilter::GetCookiesForUrl(
388 BrowserMessageFilter* filter, net::URLRequestContext* context,
389 int render_process_id, IPC::Message* reply_msg, const GURL& url) {
391 RendererId renderer_key(render_process_id, reply_msg->routing_id());
393 RenderViewMap::iterator automation_details_iter(
394 filtered_render_views_.Get().find(renderer_key));
396 DCHECK(automation_details_iter != filtered_render_views_.Get().end());
397 DCHECK(automation_details_iter->second.filter.get() != NULL);
399 int completion_callback_id = GetNextCompletionCallbackId();
400 DCHECK(!ContainsKey(completion_callback_map_.Get(), completion_callback_id));
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;
410 completion_callback_map_.Get()[completion_callback_id] = cookie_info;
412 DCHECK(automation_details_iter->second.filter.get() != NULL);
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,
419 completion_callback_id));
423 void AutomationResourceMessageFilter::OnGetCookiesHostResponse(
424 int tab_handle, bool success, const GURL& url, const std::string& cookies,
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:"
434 ChromeViewHostMsg_GetCookies::WriteReplyParams(index->second.reply_msg,
436 index->second.filter->Send(index->second.reply_msg);
438 completion_callback_map_.Get().erase(index);
441 void AutomationResourceMessageFilter::SetCookiesForUrl(
442 int render_process_id,
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);
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));
460 void AutomationResourceMessageFilter::ResumeJobsForPendingView(
462 AutomationResourceMessageFilter* old_filter,
463 AutomationResourceMessageFilter* new_filter) {
464 DCHECK(old_filter != NULL);
465 DCHECK(new_filter != NULL);
467 RequestMap pending_requests = old_filter->pending_request_map_;
468 old_filter->pending_request_map_.clear();
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);