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