automation_resource_message_filter.cc revision 3f50c38dc070f4bb515c1b64450dae14f316474e
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/browser_thread.h"
12#include "chrome/browser/net/url_request_failed_dns_job.h"
13#include "chrome/browser/net/url_request_mock_http_job.h"
14#include "chrome/browser/net/url_request_mock_util.h"
15#include "chrome/browser/net/url_request_slow_download_job.h"
16#include "chrome/browser/net/url_request_slow_http_job.h"
17#include "chrome/browser/renderer_host/render_message_filter.h"
18#include "chrome/common/automation_messages.h"
19#include "chrome/common/chrome_paths.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_ == NULL);
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    VLOG(1) << "UnRegisterRenderViewInIOThread: already unregistered";
322    return;
323  }
324
325  automation_details_iter->second.ref_count--;
326
327  if (automation_details_iter->second.ref_count <= 0) {
328    filtered_render_views_.Get().erase(RendererId(renderer_pid, renderer_id));
329  }
330}
331
332// static
333bool AutomationResourceMessageFilter::ResumePendingRenderViewInIOThread(
334    int renderer_pid, int renderer_id, int tab_handle,
335    AutomationResourceMessageFilter* filter) {
336  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
337
338  RendererId renderer_key(renderer_pid, renderer_id);
339
340  RenderViewMap::iterator automation_details_iter(
341      filtered_render_views_.Get().find(renderer_key));
342
343  if (automation_details_iter == filtered_render_views_.Get().end()) {
344    NOTREACHED() << "Failed to find pending view for renderer pid:"
345                 << renderer_pid
346                 << ", render view id:"
347                 << renderer_id;
348    return false;
349  }
350
351  DCHECK(automation_details_iter->second.is_pending_render_view);
352
353  scoped_refptr<net::CookieStore> cookie_store(
354      new AutomationCookieStore(filter, tab_handle));
355
356  AutomationResourceMessageFilter* old_filter =
357      automation_details_iter->second.filter;
358  DCHECK(old_filter != NULL);
359
360  filtered_render_views_.Get()[renderer_key] =
361      AutomationDetails(tab_handle, filter, false);
362
363  filtered_render_views_.Get()[renderer_key].set_cookie_store(cookie_store);
364
365  ResumeJobsForPendingView(tab_handle, old_filter, filter);
366  return true;
367}
368
369bool AutomationResourceMessageFilter::LookupRegisteredRenderView(
370    int renderer_pid, int renderer_id, AutomationDetails* details) {
371  bool found = false;
372  RenderViewMap::iterator it = filtered_render_views_.Get().find(RendererId(
373      renderer_pid, renderer_id));
374  if (it != filtered_render_views_.Get().end()) {
375    found = true;
376    if (details)
377      *details = it->second;
378  }
379
380  return found;
381}
382
383bool AutomationResourceMessageFilter::GetAutomationRequestId(
384    int request_id, int* automation_request_id) {
385  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
386
387  RequestMap::iterator it = request_map_.begin();
388  while (it != request_map_.end()) {
389    URLRequestAutomationJob* job = it->second;
390    DCHECK(job);
391    if (job && job->request_id() == request_id) {
392      *automation_request_id = job->id();
393      return true;
394    }
395    it++;
396  }
397
398  return false;
399}
400
401bool AutomationResourceMessageFilter::SendDownloadRequestToHost(
402    int routing_id, int tab_handle, int request_id) {
403  int automation_request_id = 0;
404  bool valid_id = GetAutomationRequestId(request_id, &automation_request_id);
405  if (!valid_id) {
406    NOTREACHED() << "Invalid request id: " << request_id;
407    return false;
408  }
409
410  return Send(new AutomationMsg_DownloadRequestInHost(tab_handle,
411                                                      automation_request_id));
412}
413
414void AutomationResourceMessageFilter::OnSetFilteredInet(bool enable) {
415  chrome_browser_net::SetUrlRequestMocksEnabled(enable);
416}
417
418void AutomationResourceMessageFilter::OnGetFilteredInetHitCount(
419    int* hit_count) {
420  *hit_count = net::URLRequestFilter::GetInstance()->hit_count();
421}
422
423void AutomationResourceMessageFilter::OnRecordHistograms(
424    const std::vector<std::string>& histogram_list) {
425  for (size_t index = 0; index < histogram_list.size(); ++index) {
426    base::Histogram::DeserializeHistogramInfo(histogram_list[index]);
427  }
428}
429
430bool AutomationResourceMessageFilter::GetCookiesForUrl(
431    const GURL& url, net::CompletionCallback* callback) {
432  GetCookiesCompletion* get_cookies_callback =
433      static_cast<GetCookiesCompletion*>(callback);
434
435  RendererId renderer_key(get_cookies_callback->render_process_id(),
436      get_cookies_callback->render_view_id());
437
438  RenderViewMap::iterator automation_details_iter(
439        filtered_render_views_.Get().find(renderer_key));
440
441  if (automation_details_iter == filtered_render_views_.Get().end()) {
442    return false;
443  }
444
445  DCHECK(automation_details_iter->second.filter != NULL);
446  DCHECK(automation_details_iter->second.cookie_store_.get() != NULL);
447
448  int completion_callback_id = GetNextCompletionCallbackId();
449  DCHECK(!ContainsKey(completion_callback_map_.Get(), completion_callback_id));
450
451  CookieCompletionInfo cookie_info;
452  cookie_info.completion_callback = callback;
453  cookie_info.cookie_store = automation_details_iter->second.cookie_store_;
454
455  completion_callback_map_.Get()[completion_callback_id] = cookie_info;
456
457  DCHECK(automation_details_iter->second.filter != NULL);
458
459  if (automation_details_iter->second.filter) {
460    automation_details_iter->second.filter->Send(
461        new AutomationMsg_GetCookiesFromHost(
462            automation_details_iter->second.tab_handle, url,
463            completion_callback_id));
464  }
465  return true;
466}
467
468void AutomationResourceMessageFilter::OnGetCookiesHostResponse(
469    int tab_handle, bool success, const GURL& url, const std::string& cookies,
470    int cookie_id) {
471  CompletionCallbackMap::iterator index =
472      completion_callback_map_.Get().find(cookie_id);
473  if (index != completion_callback_map_.Get().end()) {
474    net::CompletionCallback* callback = index->second.completion_callback;
475
476    scoped_refptr<net::CookieStore> cookie_store = index->second.cookie_store;
477
478    DCHECK(callback != NULL);
479    DCHECK(cookie_store.get() != NULL);
480
481    completion_callback_map_.Get().erase(index);
482
483    OnGetCookiesHostResponseInternal(tab_handle, success, url, cookies,
484                                     callback, cookie_store.get());
485  } else {
486    NOTREACHED() << "Received invalid completion callback id:"
487                 << cookie_id;
488  }
489}
490
491void AutomationResourceMessageFilter::OnGetCookiesHostResponseInternal(
492    int tab_handle, bool success, const GURL& url, const std::string& cookies,
493  net::CompletionCallback* callback, net::CookieStore* cookie_store) {
494  DCHECK(callback);
495  DCHECK(cookie_store);
496
497  GetCookiesCompletion* get_cookies_callback =
498      static_cast<GetCookiesCompletion*>(callback);
499
500  get_cookies_callback->set_cookie_store(cookie_store);
501
502  // Set the cookie in the cookie store so that the callback can read it.
503  cookie_store->SetCookieWithOptions(url, cookies, net::CookieOptions());
504
505  Tuple1<int> params;
506  params.a = success ? net::OK : net::ERR_ACCESS_DENIED;
507  callback->RunWithParams(params);
508
509  // The cookie for this URL is only valid until it is read by the callback.
510  cookie_store->SetCookieWithOptions(url, "", net::CookieOptions());
511}
512
513bool AutomationResourceMessageFilter::SetCookiesForUrl(
514    const GURL& url, const std::string& cookie_line,
515    net::CompletionCallback* callback) {
516  SetCookieCompletion* set_cookies_callback =
517      static_cast<SetCookieCompletion*>(callback);
518
519  RenderViewMap::iterator automation_details_iter(
520        filtered_render_views_.Get().find(RendererId(
521            set_cookies_callback->render_process_id(),
522            set_cookies_callback->render_view_id())));
523
524  if (automation_details_iter == filtered_render_views_.Get().end()) {
525    return false;
526  }
527
528  DCHECK(automation_details_iter->second.filter != NULL);
529
530  if (automation_details_iter->second.filter) {
531    automation_details_iter->second.filter->Send(
532        new AutomationMsg_SetCookieAsync(
533            automation_details_iter->second.tab_handle, url, cookie_line));
534  }
535
536  return true;
537}
538
539// static
540void AutomationResourceMessageFilter::ResumeJobsForPendingView(
541    int tab_handle,
542    AutomationResourceMessageFilter* old_filter,
543    AutomationResourceMessageFilter* new_filter) {
544  DCHECK(old_filter != NULL);
545  DCHECK(new_filter != NULL);
546
547  RequestMap pending_requests = old_filter->pending_request_map_;
548
549  for (RequestMap::iterator index = old_filter->pending_request_map_.begin();
550          index != old_filter->pending_request_map_.end(); index++) {
551    scoped_refptr<URLRequestAutomationJob> job = (*index).second;
552    DCHECK_EQ(job->message_filter(), old_filter);
553    DCHECK(job->is_pending());
554    // StartPendingJob will register the job with the new filter.
555    job->StartPendingJob(tab_handle, new_filter);
556  }
557
558  old_filter->pending_request_map_.clear();
559}
560