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