Home | History | Annotate | Download | only in renderer_host
      1 // Copyright (c) 2012 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 "content/browser/renderer_host/render_widget_helper.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/lazy_instance.h"
     10 #include "base/posix/eintr_wrapper.h"
     11 #include "base/threading/thread.h"
     12 #include "base/threading/thread_restrictions.h"
     13 #include "content/browser/gpu/gpu_surface_tracker.h"
     14 #include "content/browser/loader/resource_dispatcher_host_impl.h"
     15 #include "content/browser/renderer_host/render_process_host_impl.h"
     16 #include "content/browser/renderer_host/render_view_host_impl.h"
     17 #include "content/browser/dom_storage/session_storage_namespace_impl.h"
     18 #include "content/common/view_messages.h"
     19 
     20 namespace content {
     21 namespace {
     22 
     23 typedef std::map<int, RenderWidgetHelper*> WidgetHelperMap;
     24 base::LazyInstance<WidgetHelperMap> g_widget_helpers =
     25     LAZY_INSTANCE_INITIALIZER;
     26 
     27 void AddWidgetHelper(int render_process_id,
     28                      const scoped_refptr<RenderWidgetHelper>& widget_helper) {
     29   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     30   // We don't care if RenderWidgetHelpers overwrite an existing process_id. Just
     31   // want this to be up to date.
     32   g_widget_helpers.Get()[render_process_id] = widget_helper.get();
     33 }
     34 
     35 }  // namespace
     36 
     37 // A helper used with DidReceiveBackingStoreMsg that we hold a pointer to in
     38 // pending_paints_.
     39 class RenderWidgetHelper::BackingStoreMsgProxy {
     40  public:
     41   BackingStoreMsgProxy(RenderWidgetHelper* h, const IPC::Message& m);
     42   ~BackingStoreMsgProxy();
     43   void Run();
     44   void Cancel() { cancelled_ = true; }
     45 
     46   const IPC::Message& message() const { return message_; }
     47 
     48  private:
     49   scoped_refptr<RenderWidgetHelper> helper_;
     50   IPC::Message message_;
     51   bool cancelled_;  // If true, then the message will not be dispatched.
     52 
     53   DISALLOW_COPY_AND_ASSIGN(BackingStoreMsgProxy);
     54 };
     55 
     56 RenderWidgetHelper::BackingStoreMsgProxy::BackingStoreMsgProxy(
     57     RenderWidgetHelper* h, const IPC::Message& m)
     58     : helper_(h),
     59       message_(m),
     60       cancelled_(false) {
     61 }
     62 
     63 RenderWidgetHelper::BackingStoreMsgProxy::~BackingStoreMsgProxy() {
     64   // If the paint message was never dispatched, then we need to let the
     65   // helper know that we are going away.
     66   if (!cancelled_ && helper_.get())
     67     helper_->OnDiscardBackingStoreMsg(this);
     68 }
     69 
     70 void RenderWidgetHelper::BackingStoreMsgProxy::Run() {
     71   if (!cancelled_) {
     72     helper_->OnDispatchBackingStoreMsg(this);
     73     helper_ = NULL;
     74   }
     75 }
     76 
     77 RenderWidgetHelper::RenderWidgetHelper()
     78     : render_process_id_(-1),
     79 #if defined(OS_WIN)
     80       event_(CreateEvent(NULL, FALSE /* auto-reset */, FALSE, NULL)),
     81 #elif defined(OS_POSIX)
     82       event_(false /* auto-reset */, false),
     83 #endif
     84       resource_dispatcher_host_(NULL) {
     85 }
     86 
     87 RenderWidgetHelper::~RenderWidgetHelper() {
     88   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     89 
     90   // Delete this RWH from the map if it is found.
     91   WidgetHelperMap& widget_map = g_widget_helpers.Get();
     92   WidgetHelperMap::iterator it = widget_map.find(render_process_id_);
     93   if (it != widget_map.end() && it->second == this)
     94     widget_map.erase(it);
     95 
     96   // The elements of pending_paints_ each hold an owning reference back to this
     97   // object, so we should not be destroyed unless pending_paints_ is empty!
     98   DCHECK(pending_paints_.empty());
     99 
    100 #if defined(OS_POSIX) && !defined(TOOLKIT_GTK) && !defined(OS_ANDROID)
    101   ClearAllocatedDIBs();
    102 #endif
    103 }
    104 
    105 void RenderWidgetHelper::Init(
    106     int render_process_id,
    107     ResourceDispatcherHostImpl* resource_dispatcher_host) {
    108   render_process_id_ = render_process_id;
    109   resource_dispatcher_host_ = resource_dispatcher_host;
    110 
    111   BrowserThread::PostTask(
    112       BrowserThread::IO, FROM_HERE,
    113       base::Bind(&AddWidgetHelper,
    114                  render_process_id_, make_scoped_refptr(this)));
    115 }
    116 
    117 int RenderWidgetHelper::GetNextRoutingID() {
    118   return next_routing_id_.GetNext() + 1;
    119 }
    120 
    121 // static
    122 RenderWidgetHelper* RenderWidgetHelper::FromProcessHostID(
    123     int render_process_host_id) {
    124   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    125   WidgetHelperMap::const_iterator ci = g_widget_helpers.Get().find(
    126       render_process_host_id);
    127   return (ci == g_widget_helpers.Get().end())? NULL : ci->second;
    128 }
    129 
    130 void RenderWidgetHelper::ResumeDeferredNavigation(
    131     const GlobalRequestID& request_id) {
    132   BrowserThread::PostTask(
    133       BrowserThread::IO, FROM_HERE,
    134       base::Bind(&RenderWidgetHelper::OnResumeDeferredNavigation,
    135                  this,
    136                  request_id));
    137 }
    138 
    139 bool RenderWidgetHelper::WaitForBackingStoreMsg(
    140     int render_widget_id, const base::TimeDelta& max_delay, IPC::Message* msg) {
    141   base::TimeTicks time_start = base::TimeTicks::Now();
    142 
    143   for (;;) {
    144     BackingStoreMsgProxy* proxy = NULL;
    145     {
    146       base::AutoLock lock(pending_paints_lock_);
    147 
    148       BackingStoreMsgProxyMap::iterator it =
    149           pending_paints_.find(render_widget_id);
    150       if (it != pending_paints_.end()) {
    151         BackingStoreMsgProxyQueue &queue = it->second;
    152         DCHECK(!queue.empty());
    153         proxy = queue.front();
    154 
    155         // Flag the proxy as cancelled so that when it is run as a task it will
    156         // do nothing.
    157         proxy->Cancel();
    158 
    159         queue.pop_front();
    160         if (queue.empty())
    161           pending_paints_.erase(it);
    162       }
    163     }
    164 
    165     if (proxy) {
    166       *msg = proxy->message();
    167       DCHECK(msg->routing_id() == render_widget_id);
    168       return true;
    169     }
    170 
    171     // Calculate the maximum amount of time that we are willing to sleep.
    172     base::TimeDelta max_sleep_time =
    173         max_delay - (base::TimeTicks::Now() - time_start);
    174     if (max_sleep_time <= base::TimeDelta::FromMilliseconds(0))
    175       break;
    176 
    177     base::ThreadRestrictions::ScopedAllowWait allow_wait;
    178     event_.TimedWait(max_sleep_time);
    179   }
    180 
    181   return false;
    182 }
    183 
    184 void RenderWidgetHelper::ResumeRequestsForView(int route_id) {
    185   // We only need to resume blocked requests if we used a valid route_id.
    186   // See CreateNewWindow.
    187   if (route_id != MSG_ROUTING_NONE) {
    188     BrowserThread::PostTask(
    189         BrowserThread::IO, FROM_HERE,
    190         base::Bind(&RenderWidgetHelper::OnResumeRequestsForView,
    191             this, route_id));
    192   }
    193 }
    194 
    195 void RenderWidgetHelper::DidReceiveBackingStoreMsg(const IPC::Message& msg) {
    196   int render_widget_id = msg.routing_id();
    197 
    198   BackingStoreMsgProxy* proxy = new BackingStoreMsgProxy(this, msg);
    199   {
    200     base::AutoLock lock(pending_paints_lock_);
    201 
    202     pending_paints_[render_widget_id].push_back(proxy);
    203   }
    204 
    205   // Notify anyone waiting on the UI thread that there is a new entry in the
    206   // proxy map.  If they don't find the entry they are looking for, then they
    207   // will just continue waiting.
    208   event_.Signal();
    209 
    210   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    211       base::Bind(&BackingStoreMsgProxy::Run, base::Owned(proxy)));
    212 }
    213 
    214 void RenderWidgetHelper::OnDiscardBackingStoreMsg(BackingStoreMsgProxy* proxy) {
    215   const IPC::Message& msg = proxy->message();
    216 
    217   // Remove the proxy from the map now that we are going to handle it normally.
    218   {
    219     base::AutoLock lock(pending_paints_lock_);
    220 
    221     BackingStoreMsgProxyMap::iterator it =
    222         pending_paints_.find(msg.routing_id());
    223     DCHECK(it != pending_paints_.end());
    224     BackingStoreMsgProxyQueue &queue = it->second;
    225     DCHECK(queue.front() == proxy);
    226 
    227     queue.pop_front();
    228     if (queue.empty())
    229       pending_paints_.erase(it);
    230   }
    231 }
    232 
    233 void RenderWidgetHelper::OnDispatchBackingStoreMsg(
    234     BackingStoreMsgProxy* proxy) {
    235   OnDiscardBackingStoreMsg(proxy);
    236 
    237   // It is reasonable for the host to no longer exist.
    238   RenderProcessHost* host = RenderProcessHost::FromID(render_process_id_);
    239   if (host)
    240     host->OnMessageReceived(proxy->message());
    241 }
    242 
    243 void RenderWidgetHelper::OnResumeDeferredNavigation(
    244     const GlobalRequestID& request_id) {
    245   resource_dispatcher_host_->ResumeDeferredNavigation(request_id);
    246 }
    247 
    248 void RenderWidgetHelper::CreateNewWindow(
    249     const ViewHostMsg_CreateWindow_Params& params,
    250     bool no_javascript_access,
    251     base::ProcessHandle render_process,
    252     int* route_id,
    253     int* main_frame_route_id,
    254     int* surface_id,
    255     SessionStorageNamespace* session_storage_namespace) {
    256   if (params.opener_suppressed || no_javascript_access) {
    257     // If the opener is supppressed or script access is disallowed, we should
    258     // open the window in a new BrowsingInstance, and thus a new process. That
    259     // means the current renderer process will not be able to route messages to
    260     // it. Because of this, we will immediately show and navigate the window
    261     // in OnCreateWindowOnUI, using the params provided here.
    262     *route_id = MSG_ROUTING_NONE;
    263     *main_frame_route_id = MSG_ROUTING_NONE;
    264     *surface_id = 0;
    265   } else {
    266     *route_id = GetNextRoutingID();
    267     *main_frame_route_id = GetNextRoutingID();
    268     *surface_id = GpuSurfaceTracker::Get()->AddSurfaceForRenderer(
    269         render_process_id_, *route_id);
    270     // Block resource requests until the view is created, since the HWND might
    271     // be needed if a response ends up creating a plugin.
    272     resource_dispatcher_host_->BlockRequestsForRoute(
    273         render_process_id_, *route_id);
    274     resource_dispatcher_host_->BlockRequestsForRoute(
    275         render_process_id_, *main_frame_route_id);
    276   }
    277 
    278   BrowserThread::PostTask(
    279       BrowserThread::UI, FROM_HERE,
    280       base::Bind(&RenderWidgetHelper::OnCreateWindowOnUI,
    281                  this, params, *route_id, *main_frame_route_id,
    282                  make_scoped_refptr(session_storage_namespace)));
    283 }
    284 
    285 void RenderWidgetHelper::OnCreateWindowOnUI(
    286     const ViewHostMsg_CreateWindow_Params& params,
    287     int route_id,
    288     int main_frame_route_id,
    289     SessionStorageNamespace* session_storage_namespace) {
    290   RenderViewHostImpl* host =
    291       RenderViewHostImpl::FromID(render_process_id_, params.opener_id);
    292   if (host)
    293     host->CreateNewWindow(route_id, main_frame_route_id, params,
    294         session_storage_namespace);
    295 }
    296 
    297 void RenderWidgetHelper::OnResumeRequestsForView(int route_id) {
    298   resource_dispatcher_host_->ResumeBlockedRequestsForRoute(
    299       render_process_id_, route_id);
    300 }
    301 
    302 void RenderWidgetHelper::CreateNewWidget(int opener_id,
    303                                          WebKit::WebPopupType popup_type,
    304                                          int* route_id,
    305                                          int* surface_id) {
    306   *route_id = GetNextRoutingID();
    307   *surface_id = GpuSurfaceTracker::Get()->AddSurfaceForRenderer(
    308       render_process_id_, *route_id);
    309   BrowserThread::PostTask(
    310       BrowserThread::UI, FROM_HERE,
    311       base::Bind(
    312           &RenderWidgetHelper::OnCreateWidgetOnUI, this, opener_id, *route_id,
    313           popup_type));
    314 }
    315 
    316 void RenderWidgetHelper::CreateNewFullscreenWidget(int opener_id,
    317                                                    int* route_id,
    318                                                    int* surface_id) {
    319   *route_id = GetNextRoutingID();
    320   *surface_id = GpuSurfaceTracker::Get()->AddSurfaceForRenderer(
    321       render_process_id_, *route_id);
    322   BrowserThread::PostTask(
    323       BrowserThread::UI, FROM_HERE,
    324       base::Bind(
    325           &RenderWidgetHelper::OnCreateFullscreenWidgetOnUI, this,
    326           opener_id, *route_id));
    327 }
    328 
    329 void RenderWidgetHelper::OnCreateWidgetOnUI(
    330     int opener_id, int route_id, WebKit::WebPopupType popup_type) {
    331   RenderViewHostImpl* host = RenderViewHostImpl::FromID(
    332       render_process_id_, opener_id);
    333   if (host)
    334     host->CreateNewWidget(route_id, popup_type);
    335 }
    336 
    337 void RenderWidgetHelper::OnCreateFullscreenWidgetOnUI(int opener_id,
    338                                                       int route_id) {
    339   RenderViewHostImpl* host = RenderViewHostImpl::FromID(
    340       render_process_id_, opener_id);
    341   if (host)
    342     host->CreateNewFullscreenWidget(route_id);
    343 }
    344 
    345 #if defined(OS_POSIX) && !defined(TOOLKIT_GTK) && !defined(OS_ANDROID)
    346 TransportDIB* RenderWidgetHelper::MapTransportDIB(TransportDIB::Id dib_id) {
    347   base::AutoLock locked(allocated_dibs_lock_);
    348 
    349   const std::map<TransportDIB::Id, int>::iterator
    350       i = allocated_dibs_.find(dib_id);
    351   if (i == allocated_dibs_.end())
    352     return NULL;
    353 
    354   base::FileDescriptor fd(dup(i->second), true);
    355   return TransportDIB::Map(fd);
    356 }
    357 
    358 void RenderWidgetHelper::AllocTransportDIB(uint32 size,
    359                                            bool cache_in_browser,
    360                                            TransportDIB::Handle* result) {
    361   scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory());
    362   if (!shared_memory->CreateAnonymous(size)) {
    363     result->fd = -1;
    364     result->auto_close = false;
    365     return;
    366   }
    367 
    368   shared_memory->GiveToProcess(0 /* pid, not needed */, result);
    369 
    370   if (cache_in_browser) {
    371     // Keep a copy of the file descriptor around
    372     base::AutoLock locked(allocated_dibs_lock_);
    373     allocated_dibs_[shared_memory->id()] = dup(result->fd);
    374   }
    375 }
    376 
    377 void RenderWidgetHelper::FreeTransportDIB(TransportDIB::Id dib_id) {
    378   base::AutoLock locked(allocated_dibs_lock_);
    379 
    380   const std::map<TransportDIB::Id, int>::iterator
    381     i = allocated_dibs_.find(dib_id);
    382 
    383   if (i != allocated_dibs_.end()) {
    384     if (HANDLE_EINTR(close(i->second)) < 0)
    385       PLOG(ERROR) << "close";
    386     allocated_dibs_.erase(i);
    387   } else {
    388     DLOG(WARNING) << "Renderer asked us to free unknown transport DIB";
    389   }
    390 }
    391 
    392 void RenderWidgetHelper::ClearAllocatedDIBs() {
    393   for (std::map<TransportDIB::Id, int>::iterator
    394        i = allocated_dibs_.begin(); i != allocated_dibs_.end(); ++i) {
    395     if (HANDLE_EINTR(close(i->second)) < 0)
    396       PLOG(ERROR) << "close: " << i->first;
    397   }
    398 
    399   allocated_dibs_.clear();
    400 }
    401 #endif
    402 
    403 }  // namespace content
    404