| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "content/browser/renderer_host/render_widget_helper.h" | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/bind_helpers.h" | 
 | #include "base/lazy_instance.h" | 
 | #include "base/posix/eintr_wrapper.h" | 
 | #include "base/threading/thread.h" | 
 | #include "base/threading/thread_restrictions.h" | 
 | #include "content/browser/gpu/gpu_process_host_ui_shim.h" | 
 | #include "content/browser/gpu/gpu_surface_tracker.h" | 
 | #include "content/browser/loader/resource_dispatcher_host_impl.h" | 
 | #include "content/browser/renderer_host/render_process_host_impl.h" | 
 | #include "content/browser/renderer_host/render_view_host_impl.h" | 
 | #include "content/browser/dom_storage/session_storage_namespace_impl.h" | 
 | #include "content/common/view_messages.h" | 
 |  | 
 | namespace content { | 
 | namespace { | 
 |  | 
 | typedef std::map<int, RenderWidgetHelper*> WidgetHelperMap; | 
 | base::LazyInstance<WidgetHelperMap> g_widget_helpers = | 
 |     LAZY_INSTANCE_INITIALIZER; | 
 |  | 
 | void AddWidgetHelper(int render_process_id, | 
 |                      const scoped_refptr<RenderWidgetHelper>& widget_helper) { | 
 |   DCHECK_CURRENTLY_ON(BrowserThread::IO); | 
 |   // We don't care if RenderWidgetHelpers overwrite an existing process_id. Just | 
 |   // want this to be up to date. | 
 |   g_widget_helpers.Get()[render_process_id] = widget_helper.get(); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | RenderWidgetHelper::RenderWidgetHelper() | 
 |     : render_process_id_(-1), | 
 |       resource_dispatcher_host_(NULL) { | 
 | } | 
 |  | 
 | RenderWidgetHelper::~RenderWidgetHelper() { | 
 |   DCHECK_CURRENTLY_ON(BrowserThread::IO); | 
 |  | 
 |   // Delete this RWH from the map if it is found. | 
 |   WidgetHelperMap& widget_map = g_widget_helpers.Get(); | 
 |   WidgetHelperMap::iterator it = widget_map.find(render_process_id_); | 
 |   if (it != widget_map.end() && it->second == this) | 
 |     widget_map.erase(it); | 
 |  | 
 | #if defined(OS_POSIX) && !defined(OS_ANDROID) | 
 |   ClearAllocatedDIBs(); | 
 | #endif | 
 | } | 
 |  | 
 | void RenderWidgetHelper::Init( | 
 |     int render_process_id, | 
 |     ResourceDispatcherHostImpl* resource_dispatcher_host) { | 
 |   render_process_id_ = render_process_id; | 
 |   resource_dispatcher_host_ = resource_dispatcher_host; | 
 |  | 
 |   BrowserThread::PostTask( | 
 |       BrowserThread::IO, FROM_HERE, | 
 |       base::Bind(&AddWidgetHelper, | 
 |                  render_process_id_, make_scoped_refptr(this))); | 
 | } | 
 |  | 
 | int RenderWidgetHelper::GetNextRoutingID() { | 
 |   return next_routing_id_.GetNext() + 1; | 
 | } | 
 |  | 
 | // static | 
 | RenderWidgetHelper* RenderWidgetHelper::FromProcessHostID( | 
 |     int render_process_host_id) { | 
 |   DCHECK_CURRENTLY_ON(BrowserThread::IO); | 
 |   WidgetHelperMap::const_iterator ci = g_widget_helpers.Get().find( | 
 |       render_process_host_id); | 
 |   return (ci == g_widget_helpers.Get().end())? NULL : ci->second; | 
 | } | 
 |  | 
 | void RenderWidgetHelper::ResumeDeferredNavigation( | 
 |     const GlobalRequestID& request_id) { | 
 |   BrowserThread::PostTask( | 
 |       BrowserThread::IO, FROM_HERE, | 
 |       base::Bind(&RenderWidgetHelper::OnResumeDeferredNavigation, | 
 |                  this, | 
 |                  request_id)); | 
 | } | 
 |  | 
 | void RenderWidgetHelper::ResumeResponseDeferredAtStart( | 
 |     const GlobalRequestID& request_id) { | 
 |   BrowserThread::PostTask( | 
 |       BrowserThread::IO, | 
 |       FROM_HERE, | 
 |       base::Bind(&RenderWidgetHelper::OnResumeResponseDeferredAtStart, | 
 |                  this, | 
 |                  request_id)); | 
 | } | 
 |  | 
 | void RenderWidgetHelper::ResumeRequestsForView(int route_id) { | 
 |   // We only need to resume blocked requests if we used a valid route_id. | 
 |   // See CreateNewWindow. | 
 |   if (route_id != MSG_ROUTING_NONE) { | 
 |     BrowserThread::PostTask( | 
 |         BrowserThread::IO, FROM_HERE, | 
 |         base::Bind(&RenderWidgetHelper::OnResumeRequestsForView, | 
 |             this, route_id)); | 
 |   } | 
 | } | 
 |  | 
 | void RenderWidgetHelper::OnResumeDeferredNavigation( | 
 |     const GlobalRequestID& request_id) { | 
 |   resource_dispatcher_host_->ResumeDeferredNavigation(request_id); | 
 | } | 
 |  | 
 | void RenderWidgetHelper::OnResumeResponseDeferredAtStart( | 
 |     const GlobalRequestID& request_id) { | 
 |   resource_dispatcher_host_->ResumeResponseDeferredAtStart(request_id); | 
 | } | 
 |  | 
 | void RenderWidgetHelper::CreateNewWindow( | 
 |     const ViewHostMsg_CreateWindow_Params& params, | 
 |     bool no_javascript_access, | 
 |     base::ProcessHandle render_process, | 
 |     int* route_id, | 
 |     int* main_frame_route_id, | 
 |     int* surface_id, | 
 |     SessionStorageNamespace* session_storage_namespace) { | 
 |   if (params.opener_suppressed || no_javascript_access) { | 
 |     // If the opener is supppressed or script access is disallowed, we should | 
 |     // open the window in a new BrowsingInstance, and thus a new process. That | 
 |     // means the current renderer process will not be able to route messages to | 
 |     // it. Because of this, we will immediately show and navigate the window | 
 |     // in OnCreateWindowOnUI, using the params provided here. | 
 |     *route_id = MSG_ROUTING_NONE; | 
 |     *main_frame_route_id = MSG_ROUTING_NONE; | 
 |     *surface_id = 0; | 
 |   } else { | 
 |     *route_id = GetNextRoutingID(); | 
 |     *main_frame_route_id = GetNextRoutingID(); | 
 |     *surface_id = GpuSurfaceTracker::Get()->AddSurfaceForRenderer( | 
 |         render_process_id_, *route_id); | 
 |     // Block resource requests until the view is created, since the HWND might | 
 |     // be needed if a response ends up creating a plugin. | 
 |     resource_dispatcher_host_->BlockRequestsForRoute( | 
 |         render_process_id_, *route_id); | 
 |     resource_dispatcher_host_->BlockRequestsForRoute( | 
 |         render_process_id_, *main_frame_route_id); | 
 |   } | 
 |  | 
 |   BrowserThread::PostTask( | 
 |       BrowserThread::UI, FROM_HERE, | 
 |       base::Bind(&RenderWidgetHelper::OnCreateWindowOnUI, | 
 |                  this, params, *route_id, *main_frame_route_id, | 
 |                  make_scoped_refptr(session_storage_namespace))); | 
 | } | 
 |  | 
 | void RenderWidgetHelper::OnCreateWindowOnUI( | 
 |     const ViewHostMsg_CreateWindow_Params& params, | 
 |     int route_id, | 
 |     int main_frame_route_id, | 
 |     SessionStorageNamespace* session_storage_namespace) { | 
 |   RenderViewHostImpl* host = | 
 |       RenderViewHostImpl::FromID(render_process_id_, params.opener_id); | 
 |   if (host) | 
 |     host->CreateNewWindow(route_id, main_frame_route_id, params, | 
 |         session_storage_namespace); | 
 | } | 
 |  | 
 | void RenderWidgetHelper::OnResumeRequestsForView(int route_id) { | 
 |   resource_dispatcher_host_->ResumeBlockedRequestsForRoute( | 
 |       render_process_id_, route_id); | 
 | } | 
 |  | 
 | void RenderWidgetHelper::CreateNewWidget(int opener_id, | 
 |                                          blink::WebPopupType popup_type, | 
 |                                          int* route_id, | 
 |                                          int* surface_id) { | 
 |   *route_id = GetNextRoutingID(); | 
 |   *surface_id = GpuSurfaceTracker::Get()->AddSurfaceForRenderer( | 
 |       render_process_id_, *route_id); | 
 |   BrowserThread::PostTask( | 
 |       BrowserThread::UI, FROM_HERE, | 
 |       base::Bind( | 
 |           &RenderWidgetHelper::OnCreateWidgetOnUI, this, opener_id, *route_id, | 
 |           popup_type)); | 
 | } | 
 |  | 
 | void RenderWidgetHelper::CreateNewFullscreenWidget(int opener_id, | 
 |                                                    int* route_id, | 
 |                                                    int* surface_id) { | 
 |   *route_id = GetNextRoutingID(); | 
 |   *surface_id = GpuSurfaceTracker::Get()->AddSurfaceForRenderer( | 
 |       render_process_id_, *route_id); | 
 |   BrowserThread::PostTask( | 
 |       BrowserThread::UI, FROM_HERE, | 
 |       base::Bind( | 
 |           &RenderWidgetHelper::OnCreateFullscreenWidgetOnUI, this, | 
 |           opener_id, *route_id)); | 
 | } | 
 |  | 
 | void RenderWidgetHelper::OnCreateWidgetOnUI( | 
 |     int opener_id, int route_id, blink::WebPopupType popup_type) { | 
 |   RenderViewHostImpl* host = RenderViewHostImpl::FromID( | 
 |       render_process_id_, opener_id); | 
 |   if (host) | 
 |     host->CreateNewWidget(route_id, popup_type); | 
 | } | 
 |  | 
 | void RenderWidgetHelper::OnCreateFullscreenWidgetOnUI(int opener_id, | 
 |                                                       int route_id) { | 
 |   RenderViewHostImpl* host = RenderViewHostImpl::FromID( | 
 |       render_process_id_, opener_id); | 
 |   if (host) | 
 |     host->CreateNewFullscreenWidget(route_id); | 
 | } | 
 |  | 
 | #if defined(OS_POSIX) && !defined(OS_ANDROID) | 
 | void RenderWidgetHelper::AllocTransportDIB(uint32 size, | 
 |                                            bool cache_in_browser, | 
 |                                            TransportDIB::Handle* result) { | 
 |   scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory()); | 
 |   if (!shared_memory->CreateAnonymous(size)) { | 
 |     result->fd = -1; | 
 |     result->auto_close = false; | 
 |     return; | 
 |   } | 
 |  | 
 |   shared_memory->GiveToProcess(0 /* pid, not needed */, result); | 
 |  | 
 |   if (cache_in_browser) { | 
 |     // Keep a copy of the file descriptor around | 
 |     base::AutoLock locked(allocated_dibs_lock_); | 
 |     allocated_dibs_[shared_memory->id()] = dup(result->fd); | 
 |   } | 
 | } | 
 |  | 
 | void RenderWidgetHelper::FreeTransportDIB(TransportDIB::Id dib_id) { | 
 |   base::AutoLock locked(allocated_dibs_lock_); | 
 |  | 
 |   const std::map<TransportDIB::Id, int>::iterator | 
 |     i = allocated_dibs_.find(dib_id); | 
 |  | 
 |   if (i != allocated_dibs_.end()) { | 
 |     if (IGNORE_EINTR(close(i->second)) < 0) | 
 |       PLOG(ERROR) << "close"; | 
 |     allocated_dibs_.erase(i); | 
 |   } else { | 
 |     DLOG(WARNING) << "Renderer asked us to free unknown transport DIB"; | 
 |   } | 
 | } | 
 |  | 
 | void RenderWidgetHelper::ClearAllocatedDIBs() { | 
 |   for (std::map<TransportDIB::Id, int>::iterator | 
 |        i = allocated_dibs_.begin(); i != allocated_dibs_.end(); ++i) { | 
 |     if (IGNORE_EINTR(close(i->second)) < 0) | 
 |       PLOG(ERROR) << "close: " << i->first; | 
 |   } | 
 |  | 
 |   allocated_dibs_.clear(); | 
 | } | 
 | #endif | 
 |  | 
 | }  // namespace content |