Home | History | Annotate | Download | only in automation
      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:
     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 
     85 AutomationResourceMessageFilter::AutomationDetails::AutomationDetails()
     86     : tab_handle(0),
     87       ref_count(1),
     88       is_pending_render_view(false) {
     89 }
     90 
     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 
     99 AutomationResourceMessageFilter::AutomationDetails::~AutomationDetails() {}
    100 
    101 struct AutomationResourceMessageFilter::CookieCompletionInfo {
    102   net::CompletionCallback* completion_callback;
    103   scoped_refptr<net::CookieStore> cookie_store;
    104 };
    105 
    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 
    119 AutomationResourceMessageFilter::~AutomationResourceMessageFilter() {
    120 }
    121 
    122 // Called on the IPC thread:
    123 void AutomationResourceMessageFilter::OnFilterAdded(IPC::Channel* channel) {
    124   DCHECK(!channel_);
    125   channel_ = channel;
    126 }
    127 
    128 void AutomationResourceMessageFilter::OnFilterRemoved() {
    129   channel_ = NULL;
    130 }
    131 
    132 // Called on the IPC thread:
    133 void AutomationResourceMessageFilter::OnChannelConnected(int32 peer_pid) {
    134 }
    135 
    136 // Called on the IPC thread:
    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:
    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:
    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 
    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 
    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 
    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 
    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 
    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 
    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
    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
    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 
    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 
    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 
    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 
    415 void AutomationResourceMessageFilter::OnSetFilteredInet(bool enable) {
    416   chrome_browser_net::SetUrlRequestMocksEnabled(enable);
    417 }
    418 
    419 void AutomationResourceMessageFilter::OnGetFilteredInetHitCount(
    420     int* hit_count) {
    421   *hit_count = net::URLRequestFilter::GetInstance()->hit_count();
    422 }
    423 
    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 
    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 
    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 
    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 
    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
    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