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.
5 #include "content/browser/renderer_host/render_widget_helper.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"
24 typedef std::map
<int, RenderWidgetHelper
*> WidgetHelperMap
;
25 base::LazyInstance
<WidgetHelperMap
> g_widget_helpers
=
26 LAZY_INSTANCE_INITIALIZER
;
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();
38 RenderWidgetHelper::RenderWidgetHelper()
39 : render_process_id_(-1),
40 resource_dispatcher_host_(NULL
) {
43 RenderWidgetHelper::~RenderWidgetHelper() {
44 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
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)
52 #if defined(OS_POSIX) && !defined(OS_ANDROID)
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
;
63 BrowserThread::PostTask(
64 BrowserThread::IO
, FROM_HERE
,
65 base::Bind(&AddWidgetHelper
,
66 render_process_id_
, make_scoped_refptr(this)));
69 int RenderWidgetHelper::GetNextRoutingID() {
70 return next_routing_id_
.GetNext() + 1;
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
;
82 void RenderWidgetHelper::ResumeDeferredNavigation(
83 const GlobalRequestID
& request_id
) {
84 BrowserThread::PostTask(
85 BrowserThread::IO
, FROM_HERE
,
86 base::Bind(&RenderWidgetHelper::OnResumeDeferredNavigation
,
91 void RenderWidgetHelper::ResumeResponseDeferredAtStart(
92 const GlobalRequestID
& request_id
) {
93 BrowserThread::PostTask(
96 base::Bind(&RenderWidgetHelper::OnResumeResponseDeferredAtStart
,
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
,
112 void RenderWidgetHelper::OnResumeDeferredNavigation(
113 const GlobalRequestID
& request_id
) {
114 resource_dispatcher_host_
->ResumeDeferredNavigation(request_id
);
117 void RenderWidgetHelper::OnResumeResponseDeferredAtStart(
118 const GlobalRequestID
& request_id
) {
119 resource_dispatcher_host_
->ResumeResponseDeferredAtStart(request_id
);
122 void RenderWidgetHelper::CreateNewWindow(
123 const ViewHostMsg_CreateWindow_Params
& params
,
124 bool no_javascript_access
,
125 base::ProcessHandle render_process
,
127 int* main_frame_route_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
;
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
);
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
)));
159 void RenderWidgetHelper::OnCreateWindowOnUI(
160 const ViewHostMsg_CreateWindow_Params
& params
,
162 int main_frame_route_id
,
163 SessionStorageNamespace
* session_storage_namespace
) {
164 RenderViewHostImpl
* host
=
165 RenderViewHostImpl::FromID(render_process_id_
, params
.opener_id
);
167 host
->CreateNewWindow(route_id
, main_frame_route_id
, params
,
168 session_storage_namespace
);
171 void RenderWidgetHelper::OnResumeRequestsForView(int route_id
) {
172 resource_dispatcher_host_
->ResumeBlockedRequestsForRoute(
173 render_process_id_
, route_id
);
176 void RenderWidgetHelper::CreateNewWidget(int opener_id
,
177 blink::WebPopupType popup_type
,
180 *route_id
= GetNextRoutingID();
181 *surface_id
= GpuSurfaceTracker::Get()->AddSurfaceForRenderer(
182 render_process_id_
, *route_id
);
183 BrowserThread::PostTask(
184 BrowserThread::UI
, FROM_HERE
,
186 &RenderWidgetHelper::OnCreateWidgetOnUI
, this, opener_id
, *route_id
,
190 void RenderWidgetHelper::CreateNewFullscreenWidget(int opener_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
,
199 &RenderWidgetHelper::OnCreateFullscreenWidgetOnUI
, this,
200 opener_id
, *route_id
));
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
);
208 host
->CreateNewWidget(route_id
, popup_type
);
211 void RenderWidgetHelper::OnCreateFullscreenWidgetOnUI(int opener_id
,
213 RenderViewHostImpl
* host
= RenderViewHostImpl::FromID(
214 render_process_id_
, opener_id
);
216 host
->CreateNewFullscreenWidget(route_id
);
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
)) {
226 result
->auto_close
= false;
230 shared_memory
->GiveToProcess(0 /* pid, not needed */, result
);
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
);
239 void RenderWidgetHelper::FreeTransportDIB(TransportDIB::Id dib_id
) {
240 base::AutoLock
locked(allocated_dibs_lock_
);
242 const std::map
<TransportDIB::Id
, int>::iterator
243 i
= allocated_dibs_
.find(dib_id
);
245 if (i
!= allocated_dibs_
.end()) {
246 if (IGNORE_EINTR(close(i
->second
)) < 0)
247 PLOG(ERROR
) << "close";
248 allocated_dibs_
.erase(i
);
250 DLOG(WARNING
) << "Renderer asked us to free unknown transport DIB";
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
;
261 allocated_dibs_
.clear();
265 } // namespace content