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 |