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