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 blink::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, blink::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 (IGNORE_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 (IGNORE_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