automation_resource_message_filter.cc revision c407dc5cd9bdc5668497f21b26b09d988ab439de
1// Copyright (c) 2006-2009 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/histogram.h"
8#include "base/path_service.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/chrome_paths.h"
17#include "chrome/browser/chrome_thread.h"
18#include "chrome/test/automation/automation_messages.h"
19#include "googleurl/src/gurl.h"
20#include "net/base/cookie_store.h"
21#include "net/base/net_errors.h"
22#include "net/url_request/url_request_filter.h"
23
24AutomationResourceMessageFilter::RenderViewMap
25    AutomationResourceMessageFilter::filtered_render_views_;
26
27int AutomationResourceMessageFilter::unique_request_id_ = 1;
28
29AutomationResourceMessageFilter::AutomationResourceMessageFilter()
30    : channel_(NULL) {
31  ChromeThread::PostTask(
32      ChromeThread::IO, FROM_HERE,
33      NewRunnableFunction(
34          URLRequestAutomationJob::EnsureProtocolFactoryRegistered));
35}
36
37AutomationResourceMessageFilter::~AutomationResourceMessageFilter() {
38}
39
40// Called on the IPC thread:
41void AutomationResourceMessageFilter::OnFilterAdded(IPC::Channel* channel) {
42  DCHECK(channel_ == NULL);
43  channel_ = channel;
44}
45
46void AutomationResourceMessageFilter::OnFilterRemoved() {
47  channel_ = NULL;
48}
49
50// Called on the IPC thread:
51void AutomationResourceMessageFilter::OnChannelConnected(int32 peer_pid) {
52}
53
54// Called on the IPC thread:
55void AutomationResourceMessageFilter::OnChannelClosing() {
56  channel_ = NULL;
57  request_map_.clear();
58
59  // Only erase RenderViews which are associated with this
60  // AutomationResourceMessageFilter instance.
61  RenderViewMap::iterator index = filtered_render_views_.begin();
62  while (index != filtered_render_views_.end()) {
63    const AutomationDetails& details = (*index).second;
64    if (details.filter.get() == this) {
65      filtered_render_views_.erase(index++);
66    } else {
67      index++;
68    }
69  }
70}
71
72// Called on the IPC thread:
73bool AutomationResourceMessageFilter::OnMessageReceived(
74    const IPC::Message& message) {
75  int request_id;
76  if (URLRequestAutomationJob::MayFilterMessage(message, &request_id)) {
77    RequestMap::iterator it = request_map_.find(request_id);
78    if (it != request_map_.end()) {
79      URLRequestAutomationJob* job = it->second;
80      DCHECK(job);
81      if (job) {
82        job->OnMessage(message);
83        return true;
84      }
85    }
86  }
87
88  bool handled = true;
89  IPC_BEGIN_MESSAGE_MAP(AutomationResourceMessageFilter, message)
90    IPC_MESSAGE_HANDLER(AutomationMsg_SetFilteredInet,
91                        OnSetFilteredInet)
92    IPC_MESSAGE_HANDLER(AutomationMsg_GetFilteredInetHitCount,
93                        OnGetFilteredInetHitCount)
94    IPC_MESSAGE_HANDLER(AutomationMsg_RecordHistograms,
95                        OnRecordHistograms)
96    IPC_MESSAGE_HANDLER(AutomationMsg_GetCookiesHostResponse,
97                        OnGetCookiesHostResponse)
98    IPC_MESSAGE_UNHANDLED(handled = false)
99  IPC_END_MESSAGE_MAP()
100
101  return handled;
102}
103
104// Called on the IPC thread:
105bool AutomationResourceMessageFilter::Send(IPC::Message* message) {
106  // This has to be called on the IO thread.
107  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
108  if (!channel_) {
109    delete message;
110    return false;
111  }
112
113  return channel_->Send(message);
114}
115
116bool AutomationResourceMessageFilter::RegisterRequest(
117    URLRequestAutomationJob* job) {
118  if (!job) {
119    NOTREACHED();
120    return false;
121  }
122  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
123
124  // Register pending jobs in the pending request map for servicing later.
125  if (job->is_pending()) {
126    DCHECK(!ContainsKey(pending_request_map_, job->id()));
127    DCHECK(!ContainsKey(request_map_, job->id()));
128    pending_request_map_[job->id()] = job;
129  } else {
130    DCHECK(!ContainsKey(request_map_, job->id()));
131    DCHECK(!ContainsKey(pending_request_map_, job->id()));
132    request_map_[job->id()] = job;
133  }
134
135  return true;
136}
137
138void AutomationResourceMessageFilter::UnRegisterRequest(
139    URLRequestAutomationJob* job) {
140  if (!job) {
141    NOTREACHED();
142    return;
143  }
144  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
145
146  if (job->is_pending()) {
147    DCHECK(ContainsKey(pending_request_map_, job->id()));
148    pending_request_map_.erase(job->id());
149  } else {
150    DCHECK(ContainsKey(request_map_, job->id()));
151    request_map_.erase(job->id());
152  }
153}
154
155bool AutomationResourceMessageFilter::RegisterRenderView(
156    int renderer_pid, int renderer_id, int tab_handle,
157    AutomationResourceMessageFilter* filter,
158    bool pending_view) {
159  if (!renderer_pid || !renderer_id || !tab_handle) {
160    NOTREACHED();
161    return false;
162  }
163
164  ChromeThread::PostTask(
165      ChromeThread::IO, FROM_HERE,
166      NewRunnableFunction(
167          AutomationResourceMessageFilter::RegisterRenderViewInIOThread,
168          renderer_pid, renderer_id, tab_handle, filter, pending_view));
169  return true;
170}
171
172void AutomationResourceMessageFilter::UnRegisterRenderView(
173    int renderer_pid, int renderer_id) {
174  ChromeThread::PostTask(
175      ChromeThread::IO, FROM_HERE,
176      NewRunnableFunction(
177          AutomationResourceMessageFilter::UnRegisterRenderViewInIOThread,
178          renderer_pid, renderer_id));
179}
180
181bool AutomationResourceMessageFilter::ResumePendingRenderView(
182    int renderer_pid, int renderer_id, int tab_handle,
183    AutomationResourceMessageFilter* filter) {
184  if (!renderer_pid || !renderer_id || !tab_handle) {
185    NOTREACHED();
186    return false;
187  }
188
189  ChromeThread::PostTask(
190      ChromeThread::IO, FROM_HERE,
191      NewRunnableFunction(
192          AutomationResourceMessageFilter::ResumePendingRenderViewInIOThread,
193          renderer_pid, renderer_id, tab_handle, filter));
194  return true;
195}
196
197void AutomationResourceMessageFilter::RegisterRenderViewInIOThread(
198    int renderer_pid, int renderer_id,
199    int tab_handle, AutomationResourceMessageFilter* filter,
200    bool pending_view) {
201  RenderViewMap::iterator automation_details_iter(
202      filtered_render_views_.find(RendererId(renderer_pid, renderer_id)));
203  if (automation_details_iter != filtered_render_views_.end()) {
204    DCHECK(automation_details_iter->second.ref_count > 0);
205    automation_details_iter->second.ref_count++;
206  } else {
207    filtered_render_views_[RendererId(renderer_pid, renderer_id)] =
208        AutomationDetails(tab_handle, filter, pending_view);
209  }
210}
211
212// static
213void AutomationResourceMessageFilter::UnRegisterRenderViewInIOThread(
214    int renderer_pid, int renderer_id) {
215  RenderViewMap::iterator automation_details_iter(
216      filtered_render_views_.find(RendererId(renderer_pid, renderer_id)));
217
218  if (automation_details_iter == filtered_render_views_.end()) {
219    LOG(INFO) << "UnRegisterRenderViewInIOThread: already unregistered";
220    return;
221  }
222
223  automation_details_iter->second.ref_count--;
224
225  if (automation_details_iter->second.ref_count <= 0) {
226    filtered_render_views_.erase(RendererId(renderer_pid, renderer_id));
227  }
228}
229
230// static
231bool AutomationResourceMessageFilter::ResumePendingRenderViewInIOThread(
232    int renderer_pid, int renderer_id, int tab_handle,
233    AutomationResourceMessageFilter* filter) {
234  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
235
236  RenderViewMap::iterator automation_details_iter(
237      filtered_render_views_.find(RendererId(renderer_pid, renderer_id)));
238
239  if (automation_details_iter == filtered_render_views_.end()) {
240    NOTREACHED() << "Failed to find pending view for renderer pid:"
241                 << renderer_pid
242                 << ", render view id:"
243                 << renderer_id;
244    return false;
245  }
246
247  DCHECK(automation_details_iter->second.is_pending_render_view);
248
249  AutomationResourceMessageFilter* old_filter =
250      automation_details_iter->second.filter;
251  DCHECK(old_filter != NULL);
252
253  filtered_render_views_[RendererId(renderer_pid, renderer_id)] =
254      AutomationDetails(tab_handle, filter, false);
255
256  ResumeJobsForPendingView(tab_handle, old_filter, filter);
257  return true;
258}
259
260bool AutomationResourceMessageFilter::LookupRegisteredRenderView(
261    int renderer_pid, int renderer_id, AutomationDetails* details) {
262  bool found = false;
263  RenderViewMap::iterator it = filtered_render_views_.find(RendererId(
264      renderer_pid, renderer_id));
265  if (it != filtered_render_views_.end()) {
266    found = true;
267    if (details)
268      *details = it->second;
269  }
270
271  return found;
272}
273
274bool AutomationResourceMessageFilter::GetAutomationRequestId(
275    int request_id, int* automation_request_id) {
276  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
277
278  RequestMap::iterator it = request_map_.begin();
279  while (it != request_map_.end()) {
280    URLRequestAutomationJob* job = it->second;
281    DCHECK(job);
282    if (job && job->request_id() == request_id) {
283      *automation_request_id = job->id();
284      return true;
285    }
286    it++;
287  }
288
289  return false;
290}
291
292bool AutomationResourceMessageFilter::SendDownloadRequestToHost(
293    int routing_id, int tab_handle, int request_id) {
294  int automation_request_id = 0;
295  bool valid_id = GetAutomationRequestId(request_id, &automation_request_id);
296  if (!valid_id) {
297    NOTREACHED() << "Invalid request id: " << request_id;
298    return false;
299  }
300
301  return Send(new AutomationMsg_DownloadRequestInHost(0, tab_handle,
302                                                      automation_request_id));
303}
304
305void AutomationResourceMessageFilter::OnSetFilteredInet(bool enable) {
306  chrome_browser_net::SetUrlRequestMocksEnabled(enable);
307}
308
309void AutomationResourceMessageFilter::OnGetFilteredInetHitCount(
310    int* hit_count) {
311  *hit_count = URLRequestFilter::GetInstance()->hit_count();
312}
313
314void AutomationResourceMessageFilter::OnRecordHistograms(
315    const std::vector<std::string>& histogram_list) {
316  for (size_t index = 0; index < histogram_list.size(); ++index) {
317    Histogram::DeserializeHistogramInfo(histogram_list[index]);
318  }
319}
320
321void AutomationResourceMessageFilter::GetCookiesForUrl(
322    int tab_handle, const GURL& url, net::CompletionCallback* callback,
323    net::CookieStore* cookie_store) {
324  DCHECK(callback != NULL);
325  DCHECK(cookie_store != NULL);
326
327  int completion_callback_id = GetNextCompletionCallbackId();
328  DCHECK(!ContainsKey(completion_callback_map_, completion_callback_id));
329
330  CookieCompletionInfo cookie_info;
331  cookie_info.completion_callback = callback;
332  cookie_info.cookie_store = cookie_store;
333
334  completion_callback_map_[completion_callback_id] = cookie_info;
335
336  Send(new AutomationMsg_GetCookiesFromHost(0, tab_handle, url,
337                                            completion_callback_id));
338}
339
340void AutomationResourceMessageFilter::OnGetCookiesHostResponse(
341    int tab_handle, bool success, const GURL& url, const std::string& cookies,
342    int cookie_id) {
343  CompletionCallbackMap::iterator index =
344      completion_callback_map_.find(cookie_id);
345  if (index != completion_callback_map_.end()) {
346    net::CompletionCallback* callback = index->second.completion_callback;
347    scoped_refptr<net::CookieStore> cookie_store = index->second.cookie_store;
348
349    DCHECK(callback != NULL);
350    DCHECK(cookie_store.get() != NULL);
351
352    completion_callback_map_.erase(index);
353
354    // Set the cookie in the cookie store so that the callback can read it.
355    cookie_store->SetCookieWithOptions(url, cookies, net::CookieOptions());
356
357    Tuple1<int> params;
358    params.a = success ? net::OK : net::ERR_ACCESS_DENIED;
359    callback->RunWithParams(params);
360
361    // The cookie for this URL is only valid until it is read by the callback.
362    cookie_store->SetCookieWithOptions(url, "", net::CookieOptions());
363  } else {
364    NOTREACHED() << "Received invalid completion callback id:"
365                 << cookie_id;
366  }
367}
368
369// static
370void AutomationResourceMessageFilter::ResumeJobsForPendingView(
371    int tab_handle,
372    AutomationResourceMessageFilter* old_filter,
373    AutomationResourceMessageFilter* new_filter) {
374  DCHECK(old_filter != NULL);
375  DCHECK(new_filter != NULL);
376
377  RequestMap pending_requests = old_filter->pending_request_map_;
378
379  for (RequestMap::iterator index = old_filter->pending_request_map_.begin();
380          index != old_filter->pending_request_map_.end(); index++) {
381    scoped_refptr<URLRequestAutomationJob> job = (*index).second;
382    DCHECK_EQ(job->message_filter(), old_filter);
383    DCHECK(job->is_pending());
384    // StartPendingJob will register the job with the new filter.
385    job->StartPendingJob(tab_handle, new_filter);
386  }
387
388  old_filter->pending_request_map_.clear();
389}
390