- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / automation / automation_resource_message_filter.cc
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
24 using content::BrowserMessageFilter;
25 using content::BrowserThread;
26
27 base::LazyInstance<AutomationResourceMessageFilter::RenderViewMap>
28     AutomationResourceMessageFilter::filtered_render_views_ =
29         LAZY_INSTANCE_INITIALIZER;
30
31 base::LazyInstance<AutomationResourceMessageFilter::CompletionCallbackMap>
32     AutomationResourceMessageFilter::completion_callback_map_ =
33         LAZY_INSTANCE_INITIALIZER;
34
35 int AutomationResourceMessageFilter::unique_request_id_ = 1;
36 int AutomationResourceMessageFilter::next_completion_callback_id_ = 0;
37
38 AutomationResourceMessageFilter::AutomationDetails::AutomationDetails()
39     : tab_handle(0),
40       ref_count(1),
41       is_pending_render_view(false) {
42 }
43
44 AutomationResourceMessageFilter::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
52 AutomationResourceMessageFilter::AutomationDetails::~AutomationDetails() {}
53
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;
60 };
61
62 AutomationResourceMessageFilter::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
74 AutomationResourceMessageFilter::~AutomationResourceMessageFilter() {
75 }
76
77 // Called on the IPC thread:
78 void AutomationResourceMessageFilter::OnFilterAdded(IPC::Channel* channel) {
79   DCHECK(!channel_);
80   channel_ = channel;
81 }
82
83 void AutomationResourceMessageFilter::OnFilterRemoved() {
84   channel_ = NULL;
85 }
86
87 // Called on the IPC thread:
88 void AutomationResourceMessageFilter::OnChannelConnected(int32 peer_pid) {
89 }
90
91 // Called on the IPC thread:
92 void 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:
122 bool 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:
163 bool 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
174 bool 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
196 void 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
212 bool 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
229 void 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
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) {
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
254 void 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
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,
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
307 void 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
333 bool 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
347 bool 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
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);
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
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();
385 }
386
387 void 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
423 void 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
441 void 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
460 void 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 }