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
24base::LazyInstance<AutomationResourceMessageFilter::RenderViewMap>
25    AutomationResourceMessageFilter::filtered_render_views_(
26        base::LINKER_INITIALIZED);
27
28base::LazyInstance<AutomationResourceMessageFilter::CompletionCallbackMap>
29    AutomationResourceMessageFilter::completion_callback_map_(
30        base::LINKER_INITIALIZED);
31
32int AutomationResourceMessageFilter::unique_request_id_ = 1;
33int 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.
39class AutomationCookieStore : public net::CookieStore {
40 public:
41  AutomationCookieStore(AutomationResourceMessageFilter* automation_client,
42                        int tab_handle)
43      : automation_client_(automation_client),
44        tab_handle_(tab_handle) {
45  }
46
47  virtual ~AutomationCookieStore() {
48    DVLOG(1) << "In " << __FUNCTION__;
49  }
50
51  // CookieStore implementation.
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
61  virtual std::string GetCookiesWithOptions(const GURL& url,
62                                            const net::CookieOptions& options) {
63    return cookie_string_;
64  }
65
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
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
85AutomationResourceMessageFilter::AutomationDetails::AutomationDetails()
86    : tab_handle(0),
87      ref_count(1),
88      is_pending_render_view(false) {
89}
90
91AutomationResourceMessageFilter::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
99AutomationResourceMessageFilter::AutomationDetails::~AutomationDetails() {}
100
101struct AutomationResourceMessageFilter::CookieCompletionInfo {
102  net::CompletionCallback* completion_callback;
103  scoped_refptr<net::CookieStore> cookie_store;
104};
105
106AutomationResourceMessageFilter::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
119AutomationResourceMessageFilter::~AutomationResourceMessageFilter() {
120}
121
122// Called on the IPC thread:
123void AutomationResourceMessageFilter::OnFilterAdded(IPC::Channel* channel) {
124  DCHECK(!channel_);
125  channel_ = channel;
126}
127
128void AutomationResourceMessageFilter::OnFilterRemoved() {
129  channel_ = NULL;
130}
131
132// Called on the IPC thread:
133void AutomationResourceMessageFilter::OnChannelConnected(int32 peer_pid) {
134}
135
136// Called on the IPC thread:
137void 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:
155bool 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:
193bool 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
204bool 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
226void 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
242bool 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
263void 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
272bool 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
291void 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
314void 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
334bool 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
370bool 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
384bool 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
402bool 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
415void AutomationResourceMessageFilter::OnSetFilteredInet(bool enable) {
416  chrome_browser_net::SetUrlRequestMocksEnabled(enable);
417}
418
419void AutomationResourceMessageFilter::OnGetFilteredInetHitCount(
420    int* hit_count) {
421  *hit_count = net::URLRequestFilter::GetInstance()->hit_count();
422}
423
424void 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
431bool 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
469void 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
492void 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
514bool 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
542void 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