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_process_host_ui_shim.h"
     14 #include "content/browser/gpu/gpu_surface_tracker.h"
     15 #include "content/browser/loader/resource_dispatcher_host_impl.h"
     16 #include "content/browser/renderer_host/render_process_host_impl.h"
     17 #include "content/browser/renderer_host/render_view_host_impl.h"
     18 #include "content/browser/dom_storage/session_storage_namespace_impl.h"
     19 #include "content/common/view_messages.h"
     20 
     21 namespace content {
     22 namespace {
     23 
     24 typedef std::map<int, RenderWidgetHelper*> WidgetHelperMap;
     25 base::LazyInstance<WidgetHelperMap> g_widget_helpers =
     26     LAZY_INSTANCE_INITIALIZER;
     27 
     28 void AddWidgetHelper(int render_process_id,
     29                      const scoped_refptr<RenderWidgetHelper>& widget_helper) {
     30   DCHECK_CURRENTLY_ON(BrowserThread::IO);
     31   // We don't care if RenderWidgetHelpers overwrite an existing process_id. Just
     32   // want this to be up to date.
     33   g_widget_helpers.Get()[render_process_id] = widget_helper.get();
     34 }
     35 
     36 }  // namespace
     37 
     38 RenderWidgetHelper::RenderWidgetHelper()
     39     : render_process_id_(-1),
     40       resource_dispatcher_host_(NULL) {
     41 }
     42 
     43 RenderWidgetHelper::~RenderWidgetHelper() {
     44   DCHECK_CURRENTLY_ON(BrowserThread::IO);
     45 
     46   // Delete this RWH from the map if it is found.
     47   WidgetHelperMap& widget_map = g_widget_helpers.Get();
     48   WidgetHelperMap::iterator it = widget_map.find(render_process_id_);
     49   if (it != widget_map.end() && it->second == this)
     50     widget_map.erase(it);
     51 
     52 #if defined(OS_POSIX) && !defined(OS_ANDROID)
     53   ClearAllocatedDIBs();
     54 #endif
     55 }
     56 
     57 void RenderWidgetHelper::Init(
     58     int render_process_id,
     59     ResourceDispatcherHostImpl* resource_dispatcher_host) {
     60   render_process_id_ = render_process_id;
     61   resource_dispatcher_host_ = resource_dispatcher_host;
     62 
     63   BrowserThread::PostTask(
     64       BrowserThread::IO, FROM_HERE,
     65       base::Bind(&AddWidgetHelper,
     66                  render_process_id_, make_scoped_refptr(this)));
     67 }
     68 
     69 int RenderWidgetHelper::GetNextRoutingID() {
     70   return next_routing_id_.GetNext() + 1;
     71 }
     72 
     73 // static
     74 RenderWidgetHelper* RenderWidgetHelper::FromProcessHostID(
     75     int render_process_host_id) {
     76   DCHECK_CURRENTLY_ON(BrowserThread::IO);
     77   WidgetHelperMap::const_iterator ci = g_widget_helpers.Get().find(
     78       render_process_host_id);
     79   return (ci == g_widget_helpers.Get().end())? NULL : ci->second;
     80 }
     81 
     82 void RenderWidgetHelper::ResumeDeferredNavigation(
     83     const GlobalRequestID& request_id) {
     84   BrowserThread::PostTask(
     85       BrowserThread::IO, FROM_HERE,
     86       base::Bind(&RenderWidgetHelper::OnResumeDeferredNavigation,
     87                  this,
     88                  request_id));
     89 }
     90 
     91 void RenderWidgetHelper::ResumeResponseDeferredAtStart(
     92     const GlobalRequestID& request_id) {
     93   BrowserThread::PostTask(
     94       BrowserThread::IO,
     95       FROM_HERE,
     96       base::Bind(&RenderWidgetHelper::OnResumeResponseDeferredAtStart,
     97                  this,
     98                  request_id));
     99 }
    100 
    101 void RenderWidgetHelper::ResumeRequestsForView(int route_id) {
    102   // We only need to resume blocked requests if we used a valid route_id.
    103   // See CreateNewWindow.
    104   if (route_id != MSG_ROUTING_NONE) {
    105     BrowserThread::PostTask(
    106         BrowserThread::IO, FROM_HERE,
    107         base::Bind(&RenderWidgetHelper::OnResumeRequestsForView,
    108             this, route_id));
    109   }
    110 }
    111 
    112 void RenderWidgetHelper::OnResumeDeferredNavigation(
    113     const GlobalRequestID& request_id) {
    114   resource_dispatcher_host_->ResumeDeferredNavigation(request_id);
    115 }
    116 
    117 void RenderWidgetHelper::OnResumeResponseDeferredAtStart(
    118     const GlobalRequestID& request_id) {
    119   resource_dispatcher_host_->ResumeResponseDeferredAtStart(request_id);
    120 }
    121 
    122 void RenderWidgetHelper::CreateNewWindow(
    123     const ViewHostMsg_CreateWindow_Params& params,
    124     bool no_javascript_access,
    125     base::ProcessHandle render_process,
    126     int* route_id,
    127     int* main_frame_route_id,
    128     int* surface_id,
    129     SessionStorageNamespace* session_storage_namespace) {
    130   if (params.opener_suppressed || no_javascript_access) {
    131     // If the opener is supppressed or script access is disallowed, we should
    132     // open the window in a new BrowsingInstance, and thus a new process. That
    133     // means the current renderer process will not be able to route messages to
    134     // it. Because of this, we will immediately show and navigate the window
    135     // in OnCreateWindowOnUI, using the params provided here.
    136     *route_id = MSG_ROUTING_NONE;
    137     *main_frame_route_id = MSG_ROUTING_NONE;
    138     *surface_id = 0;
    139   } else {
    140     *route_id = GetNextRoutingID();
    141     *main_frame_route_id = GetNextRoutingID();
    142     *surface_id = GpuSurfaceTracker::Get()->AddSurfaceForRenderer(
    143         render_process_id_, *route_id);
    144     // Block resource requests until the view is created, since the HWND might
    145     // be needed if a response ends up creating a plugin.
    146     resource_dispatcher_host_->BlockRequestsForRoute(
    147         render_process_id_, *route_id);
    148     resource_dispatcher_host_->BlockRequestsForRoute(
    149         render_process_id_, *main_frame_route_id);
    150   }
    151 
    152   BrowserThread::PostTask(
    153       BrowserThread::UI, FROM_HERE,
    154       base::Bind(&RenderWidgetHelper::OnCreateWindowOnUI,
    155                  this, params, *route_id, *main_frame_route_id,
    156                  make_scoped_refptr(session_storage_namespace)));
    157 }
    158 
    159 void RenderWidgetHelper::OnCreateWindowOnUI(
    160     const ViewHostMsg_CreateWindow_Params& params,
    161     int route_id,
    162     int main_frame_route_id,
    163     SessionStorageNamespace* session_storage_namespace) {
    164   RenderViewHostImpl* host =
    165       RenderViewHostImpl::FromID(render_process_id_, params.opener_id);
    166   if (host)
    167     host->CreateNewWindow(route_id, main_frame_route_id, params,
    168         session_storage_namespace);
    169 }
    170 
    171 void RenderWidgetHelper::OnResumeRequestsForView(int route_id) {
    172   resource_dispatcher_host_->ResumeBlockedRequestsForRoute(
    173       render_process_id_, route_id);
    174 }
    175 
    176 void RenderWidgetHelper::CreateNewWidget(int opener_id,
    177                                          blink::WebPopupType popup_type,
    178                                          int* route_id,
    179                                          int* surface_id) {
    180   *route_id = GetNextRoutingID();
    181   *surface_id = GpuSurfaceTracker::Get()->AddSurfaceForRenderer(
    182       render_process_id_, *route_id);
    183   BrowserThread::PostTask(
    184       BrowserThread::UI, FROM_HERE,
    185       base::Bind(
    186           &RenderWidgetHelper::OnCreateWidgetOnUI, this, opener_id, *route_id,
    187           popup_type));
    188 }
    189 
    190 void RenderWidgetHelper::CreateNewFullscreenWidget(int opener_id,
    191                                                    int* route_id,
    192                                                    int* surface_id) {
    193   *route_id = GetNextRoutingID();
    194   *surface_id = GpuSurfaceTracker::Get()->AddSurfaceForRenderer(
    195       render_process_id_, *route_id);
    196   BrowserThread::PostTask(
    197       BrowserThread::UI, FROM_HERE,
    198       base::Bind(
    199           &RenderWidgetHelper::OnCreateFullscreenWidgetOnUI, this,
    200           opener_id, *route_id));
    201 }
    202 
    203 void RenderWidgetHelper::OnCreateWidgetOnUI(
    204     int opener_id, int route_id, blink::WebPopupType popup_type) {
    205   RenderViewHostImpl* host = RenderViewHostImpl::FromID(
    206       render_process_id_, opener_id);
    207   if (host)
    208     host->CreateNewWidget(route_id, popup_type);
    209 }
    210 
    211 void RenderWidgetHelper::OnCreateFullscreenWidgetOnUI(int opener_id,
    212                                                       int route_id) {
    213   RenderViewHostImpl* host = RenderViewHostImpl::FromID(
    214       render_process_id_, opener_id);
    215   if (host)
    216     host->CreateNewFullscreenWidget(route_id);
    217 }
    218 
    219 #if defined(OS_POSIX) && !defined(OS_ANDROID)
    220 void RenderWidgetHelper::AllocTransportDIB(uint32 size,
    221                                            bool cache_in_browser,
    222                                            TransportDIB::Handle* result) {
    223   scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory());
    224   if (!shared_memory->CreateAnonymous(size)) {
    225     result->fd = -1;
    226     result->auto_close = false;
    227     return;
    228   }
    229 
    230   shared_memory->GiveToProcess(0 /* pid, not needed */, result);
    231 
    232   if (cache_in_browser) {
    233     // Keep a copy of the file descriptor around
    234     base::AutoLock locked(allocated_dibs_lock_);
    235     allocated_dibs_[shared_memory->id()] = dup(result->fd);
    236   }
    237 }
    238 
    239 void RenderWidgetHelper::FreeTransportDIB(TransportDIB::Id dib_id) {
    240   base::AutoLock locked(allocated_dibs_lock_);
    241 
    242   const std::map<TransportDIB::Id, int>::iterator
    243     i = allocated_dibs_.find(dib_id);
    244 
    245   if (i != allocated_dibs_.end()) {
    246     if (IGNORE_EINTR(close(i->second)) < 0)
    247       PLOG(ERROR) << "close";
    248     allocated_dibs_.erase(i);
    249   } else {
    250     DLOG(WARNING) << "Renderer asked us to free unknown transport DIB";
    251   }
    252 }
    253 
    254 void RenderWidgetHelper::ClearAllocatedDIBs() {
    255   for (std::map<TransportDIB::Id, int>::iterator
    256        i = allocated_dibs_.begin(); i != allocated_dibs_.end(); ++i) {
    257     if (IGNORE_EINTR(close(i->second)) < 0)
    258       PLOG(ERROR) << "close: " << i->first;
    259   }
    260 
    261   allocated_dibs_.clear();
    262 }
    263 #endif
    264 
    265 }  // namespace content
    266