| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 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 "chrome/browser/renderer_host/render_widget_helper.h" | |
| 6 | |
| 7 #include "base/eintr_wrapper.h" | |
| 8 #include "base/threading/thread.h" | |
| 9 #include "chrome/browser/browser_thread.h" | |
| 10 #include "chrome/browser/renderer_host/render_process_host.h" | |
| 11 #include "chrome/browser/renderer_host/render_view_host.h" | |
| 12 #include "chrome/browser/renderer_host/resource_dispatcher_host.h" | |
| 13 #include "chrome/common/render_messages_params.h" | |
| 14 | |
| 15 // A Task used with InvokeLater that we hold a pointer to in pending_paints_. | |
| 16 // Instances are deleted by MessageLoop after it calls their Run method. | |
| 17 class RenderWidgetHelper::UpdateMsgProxy : public Task { | |
| 18 public: | |
| 19 UpdateMsgProxy(RenderWidgetHelper* h, const IPC::Message& m) | |
| 20 : helper(h), | |
| 21 message(m), | |
| 22 cancelled(false) { | |
| 23 } | |
| 24 | |
| 25 ~UpdateMsgProxy() { | |
| 26 // If the paint message was never dispatched, then we need to let the | |
| 27 // helper know that we are going away. | |
| 28 if (!cancelled && helper) | |
| 29 helper->OnDiscardUpdateMsg(this); | |
| 30 } | |
| 31 | |
| 32 virtual void Run() { | |
| 33 if (!cancelled) { | |
| 34 helper->OnDispatchUpdateMsg(this); | |
| 35 helper = NULL; | |
| 36 } | |
| 37 } | |
| 38 | |
| 39 scoped_refptr<RenderWidgetHelper> helper; | |
| 40 IPC::Message message; | |
| 41 bool cancelled; // If true, then the message will not be dispatched. | |
| 42 | |
| 43 DISALLOW_COPY_AND_ASSIGN(UpdateMsgProxy); | |
| 44 }; | |
| 45 | |
| 46 RenderWidgetHelper::RenderWidgetHelper() | |
| 47 : render_process_id_(-1), | |
| 48 #if defined(OS_WIN) | |
| 49 event_(CreateEvent(NULL, FALSE /* auto-reset */, FALSE, NULL)), | |
| 50 #elif defined(OS_POSIX) | |
| 51 event_(false /* auto-reset */, false), | |
| 52 #endif | |
| 53 resource_dispatcher_host_(NULL) { | |
| 54 } | |
| 55 | |
| 56 RenderWidgetHelper::~RenderWidgetHelper() { | |
| 57 // The elements of pending_paints_ each hold an owning reference back to this | |
| 58 // object, so we should not be destroyed unless pending_paints_ is empty! | |
| 59 DCHECK(pending_paints_.empty()); | |
| 60 | |
| 61 #if defined(OS_MACOSX) | |
| 62 ClearAllocatedDIBs(); | |
| 63 #endif | |
| 64 } | |
| 65 | |
| 66 void RenderWidgetHelper::Init( | |
| 67 int render_process_id, | |
| 68 ResourceDispatcherHost* resource_dispatcher_host) { | |
| 69 render_process_id_ = render_process_id; | |
| 70 resource_dispatcher_host_ = resource_dispatcher_host; | |
| 71 } | |
| 72 | |
| 73 int RenderWidgetHelper::GetNextRoutingID() { | |
| 74 return next_routing_id_.GetNext() + 1; | |
| 75 } | |
| 76 | |
| 77 void RenderWidgetHelper::CancelResourceRequests(int render_widget_id) { | |
| 78 if (render_process_id_ == -1) | |
| 79 return; | |
| 80 | |
| 81 BrowserThread::PostTask( | |
| 82 BrowserThread::IO, FROM_HERE, | |
| 83 NewRunnableMethod(this, | |
| 84 &RenderWidgetHelper::OnCancelResourceRequests, | |
| 85 render_widget_id)); | |
| 86 } | |
| 87 | |
| 88 void RenderWidgetHelper::CrossSiteClosePageACK( | |
| 89 const ViewMsg_ClosePage_Params& params) { | |
| 90 BrowserThread::PostTask( | |
| 91 BrowserThread::IO, FROM_HERE, | |
| 92 NewRunnableMethod(this, | |
| 93 &RenderWidgetHelper::OnCrossSiteClosePageACK, | |
| 94 params)); | |
| 95 } | |
| 96 | |
| 97 bool RenderWidgetHelper::WaitForUpdateMsg(int render_widget_id, | |
| 98 const base::TimeDelta& max_delay, | |
| 99 IPC::Message* msg) { | |
| 100 base::TimeTicks time_start = base::TimeTicks::Now(); | |
| 101 | |
| 102 for (;;) { | |
| 103 UpdateMsgProxy* proxy = NULL; | |
| 104 { | |
| 105 base::AutoLock lock(pending_paints_lock_); | |
| 106 | |
| 107 UpdateMsgProxyMap::iterator it = pending_paints_.find(render_widget_id); | |
| 108 if (it != pending_paints_.end()) { | |
| 109 proxy = it->second; | |
| 110 | |
| 111 // Flag the proxy as cancelled so that when it is run as a task it will | |
| 112 // do nothing. | |
| 113 proxy->cancelled = true; | |
| 114 | |
| 115 pending_paints_.erase(it); | |
| 116 } | |
| 117 } | |
| 118 | |
| 119 if (proxy) { | |
| 120 *msg = proxy->message; | |
| 121 DCHECK(msg->routing_id() == render_widget_id); | |
| 122 return true; | |
| 123 } | |
| 124 | |
| 125 // Calculate the maximum amount of time that we are willing to sleep. | |
| 126 base::TimeDelta max_sleep_time = | |
| 127 max_delay - (base::TimeTicks::Now() - time_start); | |
| 128 if (max_sleep_time <= base::TimeDelta::FromMilliseconds(0)) | |
| 129 break; | |
| 130 | |
| 131 event_.TimedWait(max_sleep_time); | |
| 132 } | |
| 133 | |
| 134 return false; | |
| 135 } | |
| 136 | |
| 137 void RenderWidgetHelper::DidReceiveUpdateMsg(const IPC::Message& msg) { | |
| 138 int render_widget_id = msg.routing_id(); | |
| 139 | |
| 140 UpdateMsgProxy* proxy = NULL; | |
| 141 { | |
| 142 base::AutoLock lock(pending_paints_lock_); | |
| 143 | |
| 144 // Visual Studio 2010 has problems converting NULL to the null pointer for | |
| 145 // std::pair. See http://connect.microsoft.com/VisualStudio/feedback/detail
s/520043/error-converting-from-null-to-a-pointer-type-in-std-pair | |
| 146 // It will work if we pass nullptr. | |
| 147 #if defined(_MSC_VER) && _MSC_VER >= 1600 | |
| 148 RenderWidgetHelper::UpdateMsgProxy* null_proxy = nullptr; | |
| 149 #else | |
| 150 RenderWidgetHelper::UpdateMsgProxy* null_proxy = NULL; | |
| 151 #endif | |
| 152 UpdateMsgProxyMap::value_type new_value(render_widget_id, null_proxy); | |
| 153 | |
| 154 // We expect only a single PaintRect message at a time. Optimize for the | |
| 155 // case that we don't already have an entry by using the 'insert' method. | |
| 156 std::pair<UpdateMsgProxyMap::iterator, bool> result = | |
| 157 pending_paints_.insert(new_value); | |
| 158 if (!result.second) { | |
| 159 NOTREACHED() << "Unexpected PaintRect message!"; | |
| 160 return; | |
| 161 } | |
| 162 | |
| 163 result.first->second = (proxy = new UpdateMsgProxy(this, msg)); | |
| 164 } | |
| 165 | |
| 166 // Notify anyone waiting on the UI thread that there is a new entry in the | |
| 167 // proxy map. If they don't find the entry they are looking for, then they | |
| 168 // will just continue waiting. | |
| 169 event_.Signal(); | |
| 170 | |
| 171 // The proxy will be deleted when it is run as a task. | |
| 172 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, proxy); | |
| 173 } | |
| 174 | |
| 175 void RenderWidgetHelper::OnDiscardUpdateMsg(UpdateMsgProxy* proxy) { | |
| 176 const IPC::Message& msg = proxy->message; | |
| 177 | |
| 178 // Remove the proxy from the map now that we are going to handle it normally. | |
| 179 { | |
| 180 base::AutoLock lock(pending_paints_lock_); | |
| 181 | |
| 182 UpdateMsgProxyMap::iterator it = pending_paints_.find(msg.routing_id()); | |
| 183 DCHECK(it != pending_paints_.end()); | |
| 184 DCHECK(it->second == proxy); | |
| 185 | |
| 186 pending_paints_.erase(it); | |
| 187 } | |
| 188 } | |
| 189 | |
| 190 void RenderWidgetHelper::OnDispatchUpdateMsg(UpdateMsgProxy* proxy) { | |
| 191 OnDiscardUpdateMsg(proxy); | |
| 192 | |
| 193 // It is reasonable for the host to no longer exist. | |
| 194 RenderProcessHost* host = RenderProcessHost::FromID(render_process_id_); | |
| 195 if (host) | |
| 196 host->OnMessageReceived(proxy->message); | |
| 197 } | |
| 198 | |
| 199 void RenderWidgetHelper::OnCancelResourceRequests( | |
| 200 int render_widget_id) { | |
| 201 resource_dispatcher_host_->CancelRequestsForRoute( | |
| 202 render_process_id_, render_widget_id); | |
| 203 } | |
| 204 | |
| 205 void RenderWidgetHelper::OnCrossSiteClosePageACK( | |
| 206 const ViewMsg_ClosePage_Params& params) { | |
| 207 resource_dispatcher_host_->OnClosePageACK(params); | |
| 208 } | |
| 209 | |
| 210 void RenderWidgetHelper::CreateNewWindow( | |
| 211 const ViewHostMsg_CreateWindow_Params& params, | |
| 212 base::ProcessHandle render_process, | |
| 213 int* route_id) { | |
| 214 *route_id = GetNextRoutingID(); | |
| 215 // Block resource requests until the view is created, since the HWND might be | |
| 216 // needed if a response ends up creating a plugin. | |
| 217 resource_dispatcher_host_->BlockRequestsForRoute( | |
| 218 render_process_id_, *route_id); | |
| 219 | |
| 220 BrowserThread::PostTask( | |
| 221 BrowserThread::UI, FROM_HERE, | |
| 222 NewRunnableMethod( | |
| 223 this, &RenderWidgetHelper::OnCreateWindowOnUI, params, *route_id)); | |
| 224 } | |
| 225 | |
| 226 void RenderWidgetHelper::OnCreateWindowOnUI( | |
| 227 const ViewHostMsg_CreateWindow_Params& params, | |
| 228 int route_id) { | |
| 229 RenderViewHost* host = | |
| 230 RenderViewHost::FromID(render_process_id_, params.opener_id); | |
| 231 if (host) | |
| 232 host->CreateNewWindow(route_id, params); | |
| 233 | |
| 234 BrowserThread::PostTask( | |
| 235 BrowserThread::IO, FROM_HERE, | |
| 236 NewRunnableMethod(this, &RenderWidgetHelper::OnCreateWindowOnIO, | |
| 237 route_id)); | |
| 238 } | |
| 239 | |
| 240 void RenderWidgetHelper::OnCreateWindowOnIO(int route_id) { | |
| 241 resource_dispatcher_host_->ResumeBlockedRequestsForRoute( | |
| 242 render_process_id_, route_id); | |
| 243 } | |
| 244 | |
| 245 void RenderWidgetHelper::CreateNewWidget(int opener_id, | |
| 246 WebKit::WebPopupType popup_type, | |
| 247 int* route_id) { | |
| 248 *route_id = GetNextRoutingID(); | |
| 249 BrowserThread::PostTask( | |
| 250 BrowserThread::UI, FROM_HERE, | |
| 251 NewRunnableMethod( | |
| 252 this, &RenderWidgetHelper::OnCreateWidgetOnUI, opener_id, *route_id, | |
| 253 popup_type)); | |
| 254 } | |
| 255 | |
| 256 void RenderWidgetHelper::CreateNewFullscreenWidget(int opener_id, | |
| 257 int* route_id) { | |
| 258 *route_id = GetNextRoutingID(); | |
| 259 BrowserThread::PostTask( | |
| 260 BrowserThread::UI, FROM_HERE, | |
| 261 NewRunnableMethod( | |
| 262 this, &RenderWidgetHelper::OnCreateFullscreenWidgetOnUI, | |
| 263 opener_id, *route_id)); | |
| 264 } | |
| 265 | |
| 266 void RenderWidgetHelper::OnCreateWidgetOnUI( | |
| 267 int opener_id, int route_id, WebKit::WebPopupType popup_type) { | |
| 268 RenderViewHost* host = RenderViewHost::FromID(render_process_id_, opener_id); | |
| 269 if (host) | |
| 270 host->CreateNewWidget(route_id, popup_type); | |
| 271 } | |
| 272 | |
| 273 void RenderWidgetHelper::OnCreateFullscreenWidgetOnUI(int opener_id, | |
| 274 int route_id) { | |
| 275 RenderViewHost* host = RenderViewHost::FromID(render_process_id_, opener_id); | |
| 276 if (host) | |
| 277 host->CreateNewFullscreenWidget(route_id); | |
| 278 } | |
| 279 | |
| 280 #if defined(OS_MACOSX) | |
| 281 TransportDIB* RenderWidgetHelper::MapTransportDIB(TransportDIB::Id dib_id) { | |
| 282 base::AutoLock locked(allocated_dibs_lock_); | |
| 283 | |
| 284 const std::map<TransportDIB::Id, int>::iterator | |
| 285 i = allocated_dibs_.find(dib_id); | |
| 286 if (i == allocated_dibs_.end()) | |
| 287 return NULL; | |
| 288 | |
| 289 base::FileDescriptor fd(dup(i->second), true); | |
| 290 return TransportDIB::Map(fd); | |
| 291 } | |
| 292 | |
| 293 void RenderWidgetHelper::AllocTransportDIB( | |
| 294 size_t size, bool cache_in_browser, TransportDIB::Handle* result) { | |
| 295 scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory()); | |
| 296 if (!shared_memory->CreateAnonymous(size)) { | |
| 297 result->fd = -1; | |
| 298 result->auto_close = false; | |
| 299 return; | |
| 300 } | |
| 301 | |
| 302 shared_memory->GiveToProcess(0 /* pid, not needed */, result); | |
| 303 | |
| 304 if (cache_in_browser) { | |
| 305 // Keep a copy of the file descriptor around | |
| 306 base::AutoLock locked(allocated_dibs_lock_); | |
| 307 allocated_dibs_[shared_memory->id()] = dup(result->fd); | |
| 308 } | |
| 309 } | |
| 310 | |
| 311 void RenderWidgetHelper::FreeTransportDIB(TransportDIB::Id dib_id) { | |
| 312 base::AutoLock locked(allocated_dibs_lock_); | |
| 313 | |
| 314 const std::map<TransportDIB::Id, int>::iterator | |
| 315 i = allocated_dibs_.find(dib_id); | |
| 316 | |
| 317 if (i != allocated_dibs_.end()) { | |
| 318 if (HANDLE_EINTR(close(i->second)) < 0) | |
| 319 PLOG(ERROR) << "close"; | |
| 320 allocated_dibs_.erase(i); | |
| 321 } else { | |
| 322 DLOG(WARNING) << "Renderer asked us to free unknown transport DIB"; | |
| 323 } | |
| 324 } | |
| 325 | |
| 326 void RenderWidgetHelper::ClearAllocatedDIBs() { | |
| 327 for (std::map<TransportDIB::Id, int>::iterator | |
| 328 i = allocated_dibs_.begin(); i != allocated_dibs_.end(); ++i) { | |
| 329 if (HANDLE_EINTR(close(i->second)) < 0) | |
| 330 PLOG(ERROR) << "close: " << i->first; | |
| 331 } | |
| 332 | |
| 333 allocated_dibs_.clear(); | |
| 334 } | |
| 335 #endif | |
| OLD | NEW |