OLD | NEW |
| (Empty) |
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/plugin/webplugin_proxy.h" | |
6 | |
7 #include "build/build_config.h" | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/lazy_instance.h" | |
11 #include "content/child/npapi/webplugin_delegate_impl.h" | |
12 #include "content/child/plugin_messages.h" | |
13 #include "content/plugin/plugin_channel.h" | |
14 #include "content/plugin/plugin_thread.h" | |
15 #include "content/public/common/content_client.h" | |
16 #include "skia/ext/platform_canvas.h" | |
17 #include "ui/gfx/canvas.h" | |
18 #include "url/url_constants.h" | |
19 | |
20 #if defined(OS_MACOSX) | |
21 #include "base/mac/mac_util.h" | |
22 #include "base/mac/scoped_cftyperef.h" | |
23 #include "content/plugin/webplugin_accelerated_surface_proxy_mac.h" | |
24 #endif | |
25 | |
26 #if defined(OS_WIN) | |
27 #include "content/common/plugin_process_messages.h" | |
28 #include "content/public/common/sandbox_init.h" | |
29 #endif | |
30 | |
31 namespace content { | |
32 | |
33 WebPluginProxy::SharedTransportDIB::SharedTransportDIB(TransportDIB* dib) | |
34 : dib_(dib) { | |
35 } | |
36 | |
37 WebPluginProxy::SharedTransportDIB::~SharedTransportDIB() { | |
38 } | |
39 | |
40 WebPluginProxy::WebPluginProxy( | |
41 PluginChannel* channel, | |
42 int route_id, | |
43 const GURL& page_url, | |
44 int host_render_view_routing_id) | |
45 : channel_(channel), | |
46 route_id_(route_id), | |
47 delegate_(NULL), | |
48 waiting_for_paint_(false), | |
49 page_url_(page_url), | |
50 windowless_buffer_index_(0), | |
51 host_render_view_routing_id_(host_render_view_routing_id), | |
52 weak_factory_(this) { | |
53 } | |
54 | |
55 WebPluginProxy::~WebPluginProxy() { | |
56 #if defined(OS_MACOSX) | |
57 // Destroy the surface early, since it may send messages during cleanup. | |
58 if (accelerated_surface_) | |
59 accelerated_surface_.reset(); | |
60 #endif | |
61 } | |
62 | |
63 bool WebPluginProxy::Send(IPC::Message* msg) { | |
64 return channel_->Send(msg); | |
65 } | |
66 | |
67 void WebPluginProxy::Invalidate() { | |
68 gfx::Rect rect(0, 0, | |
69 delegate_->GetRect().width(), | |
70 delegate_->GetRect().height()); | |
71 InvalidateRect(rect); | |
72 } | |
73 | |
74 void WebPluginProxy::InvalidateRect(const gfx::Rect& rect) { | |
75 #if defined(OS_MACOSX) | |
76 // If this is a Core Animation plugin, all we need to do is inform the | |
77 // delegate. | |
78 if (!windowless_context()) { | |
79 delegate_->PluginDidInvalidate(); | |
80 return; | |
81 } | |
82 | |
83 // Some plugins will send invalidates larger than their own rect when | |
84 // offscreen, so constrain invalidates to the plugin rect. | |
85 gfx::Rect plugin_rect = delegate_->GetRect(); | |
86 plugin_rect.set_origin(gfx::Point(0, 0)); | |
87 plugin_rect.Intersect(rect); | |
88 const gfx::Rect invalidate_rect(plugin_rect); | |
89 #else | |
90 const gfx::Rect invalidate_rect(rect); | |
91 #endif | |
92 damaged_rect_.Union(invalidate_rect); | |
93 // Ignore NPN_InvalidateRect calls with empty rects. Also don't send an | |
94 // invalidate if it's outside the clipping region, since if we did it won't | |
95 // lead to a paint and we'll be stuck waiting forever for a DidPaint response. | |
96 // | |
97 // TODO(piman): There is a race condition here, because this test assumes | |
98 // that when the paint actually occurs, the clip rect will not have changed. | |
99 // This is not true because scrolling (or window resize) could occur and be | |
100 // handled by the renderer before it receives the InvalidateRect message, | |
101 // changing the clip rect and then not painting. | |
102 if (damaged_rect_.IsEmpty() || | |
103 !delegate_->GetClipRect().Intersects(damaged_rect_)) | |
104 return; | |
105 | |
106 // Only send a single InvalidateRect message at a time. From DidPaint we | |
107 // will dispatch an additional InvalidateRect message if necessary. | |
108 if (!waiting_for_paint_) { | |
109 waiting_for_paint_ = true; | |
110 // Invalidates caused by calls to NPN_InvalidateRect/NPN_InvalidateRgn | |
111 // need to be painted asynchronously as per the NPAPI spec. | |
112 base::MessageLoop::current()->PostTask( | |
113 FROM_HERE, | |
114 base::Bind(&WebPluginProxy::OnPaint, | |
115 weak_factory_.GetWeakPtr(), | |
116 damaged_rect_)); | |
117 damaged_rect_ = gfx::Rect(); | |
118 } | |
119 } | |
120 | |
121 bool WebPluginProxy::FindProxyForUrl(const GURL& url, std::string* proxy_list) { | |
122 bool result = false; | |
123 Send(new PluginHostMsg_ResolveProxy(route_id_, url, &result, proxy_list)); | |
124 return result; | |
125 } | |
126 | |
127 void WebPluginProxy::SetCookie(const GURL& url, | |
128 const GURL& first_party_for_cookies, | |
129 const std::string& cookie) { | |
130 Send(new PluginHostMsg_SetCookie(route_id_, url, | |
131 first_party_for_cookies, cookie)); | |
132 } | |
133 | |
134 std::string WebPluginProxy::GetCookies(const GURL& url, | |
135 const GURL& first_party_for_cookies) { | |
136 std::string cookies; | |
137 Send(new PluginHostMsg_GetCookies(route_id_, url, | |
138 first_party_for_cookies, &cookies)); | |
139 | |
140 return cookies; | |
141 } | |
142 | |
143 int WebPluginProxy::GetRendererId() { | |
144 if (channel_.get()) | |
145 return channel_->renderer_id(); | |
146 return -1; | |
147 } | |
148 | |
149 void WebPluginProxy::DidPaint() { | |
150 // If we have an accumulated damaged rect, then check to see if we need to | |
151 // send out another InvalidateRect message. | |
152 waiting_for_paint_ = false; | |
153 if (!damaged_rect_.IsEmpty()) | |
154 InvalidateRect(damaged_rect_); | |
155 } | |
156 | |
157 void WebPluginProxy::Paint(const gfx::Rect& rect) { | |
158 #if defined(OS_MACOSX) | |
159 if (!windowless_context()) | |
160 return; | |
161 #else | |
162 if (!windowless_canvas() || !windowless_canvas()->getDevice()) | |
163 return; | |
164 #endif | |
165 | |
166 // Clear the damaged area so that if the plugin doesn't paint there we won't | |
167 // end up with the old values. | |
168 gfx::Rect offset_rect = rect; | |
169 offset_rect.Offset(delegate_->GetRect().OffsetFromOrigin()); | |
170 #if defined(OS_MACOSX) | |
171 CGContextSaveGState(windowless_context()); | |
172 // It is possible for windowless_contexts_ to change during plugin painting | |
173 // (since the plugin can make a synchronous call during paint event handling), | |
174 // in which case we don't want to try to restore later. Not an owning ref | |
175 // since owning the ref without owning the shared backing memory doesn't make | |
176 // sense, so this should only be used for pointer comparisons. | |
177 CGContextRef saved_context_weak = windowless_context(); | |
178 // We also save the buffer index for the comparison because if we flip buffers | |
179 // but haven't reallocated them then we do need to restore the context because | |
180 // it is going to continue to be used. | |
181 int saved_index = windowless_buffer_index_; | |
182 | |
183 CGContextClipToRect(windowless_context(), rect.ToCGRect()); | |
184 // TODO(caryclark): This is a temporary workaround to allow the Darwin / Skia | |
185 // port to share code with the Darwin / CG port. All ports will eventually use | |
186 // the common code below. | |
187 delegate_->CGPaint(windowless_context(), rect); | |
188 if (windowless_contexts_[saved_index].get() == saved_context_weak) | |
189 CGContextRestoreGState(windowless_contexts_[saved_index]); | |
190 #else | |
191 // See above comment about windowless_context_ changing. | |
192 // http::/crbug.com/139462 | |
193 skia::RefPtr<SkCanvas> saved_canvas = windowless_canvas(); | |
194 | |
195 saved_canvas->save(); | |
196 | |
197 // The given clip rect is relative to the plugin coordinate system. | |
198 SkRect sk_rect = { SkIntToScalar(rect.x()), | |
199 SkIntToScalar(rect.y()), | |
200 SkIntToScalar(rect.right()), | |
201 SkIntToScalar(rect.bottom()) }; | |
202 saved_canvas->clipRect(sk_rect); | |
203 | |
204 // Fill a transparent value so that if the plugin supports transparency that | |
205 // will work. | |
206 saved_canvas->drawColor(SkColorSetARGB(0, 0, 0, 0), SkXfermode::kSrc_Mode); | |
207 | |
208 // Bring the windowless canvas into the window coordinate system, which is | |
209 // how the plugin expects to draw (since the windowless API was originally | |
210 // designed just for scribbling over the web page). | |
211 saved_canvas->translate(SkIntToScalar(-delegate_->GetRect().x()), | |
212 SkIntToScalar(-delegate_->GetRect().y())); | |
213 | |
214 // Before we send the invalidate, paint so that renderer uses the updated | |
215 // bitmap. | |
216 delegate_->Paint(saved_canvas.get(), offset_rect); | |
217 | |
218 saved_canvas->restore(); | |
219 #endif | |
220 } | |
221 | |
222 void WebPluginProxy::UpdateGeometry( | |
223 const gfx::Rect& window_rect, | |
224 const gfx::Rect& clip_rect, | |
225 const TransportDIB::Handle& windowless_buffer0, | |
226 const TransportDIB::Handle& windowless_buffer1, | |
227 int windowless_buffer_index) { | |
228 gfx::Rect old = delegate_->GetRect(); | |
229 gfx::Rect old_clip_rect = delegate_->GetClipRect(); | |
230 | |
231 // Update the buffers before doing anything that could call into plugin code, | |
232 // so that we don't process buffer changes out of order if plugins make | |
233 // synchronous calls that lead to nested UpdateGeometry calls. | |
234 if (TransportDIB::is_valid_handle(windowless_buffer0)) { | |
235 // The plugin's rect changed, so now we have new buffers to draw into. | |
236 SetWindowlessBuffers(windowless_buffer0, | |
237 windowless_buffer1, | |
238 window_rect); | |
239 } | |
240 | |
241 DCHECK(0 <= windowless_buffer_index && windowless_buffer_index <= 1); | |
242 windowless_buffer_index_ = windowless_buffer_index; | |
243 | |
244 #if defined(OS_MACOSX) | |
245 delegate_->UpdateGeometryAndContext( | |
246 window_rect, clip_rect, windowless_context()); | |
247 #else | |
248 delegate_->UpdateGeometry(window_rect, clip_rect); | |
249 #endif | |
250 | |
251 // Send over any pending invalidates which occured when the plugin was | |
252 // off screen. | |
253 if (!clip_rect.IsEmpty() && !damaged_rect_.IsEmpty()) { | |
254 InvalidateRect(damaged_rect_); | |
255 } | |
256 } | |
257 | |
258 #if defined(OS_WIN) | |
259 | |
260 void WebPluginProxy::CreateCanvasFromHandle( | |
261 const TransportDIB::Handle& dib_handle, | |
262 const gfx::Rect& window_rect, | |
263 skia::RefPtr<SkCanvas>* canvas) { | |
264 *canvas = skia::AdoptRef(skia::CreatePlatformCanvas( | |
265 window_rect.width(), window_rect.height(), true, dib_handle.GetHandle(), | |
266 skia::RETURN_NULL_ON_FAILURE)); | |
267 // The canvas does not own the section so we need to close it now. | |
268 dib_handle.Close(); | |
269 } | |
270 | |
271 void WebPluginProxy::SetWindowlessBuffers( | |
272 const TransportDIB::Handle& windowless_buffer0, | |
273 const TransportDIB::Handle& windowless_buffer1, | |
274 const gfx::Rect& window_rect) { | |
275 CreateCanvasFromHandle(windowless_buffer0, | |
276 window_rect, | |
277 &windowless_canvases_[0]); | |
278 if (!windowless_canvases_[0]) { | |
279 windowless_canvases_[1].clear(); | |
280 return; | |
281 } | |
282 CreateCanvasFromHandle(windowless_buffer1, | |
283 window_rect, | |
284 &windowless_canvases_[1]); | |
285 if (!windowless_canvases_[1]) { | |
286 windowless_canvases_[0].clear(); | |
287 return; | |
288 } | |
289 } | |
290 | |
291 #elif defined(OS_MACOSX) | |
292 | |
293 void WebPluginProxy::CreateDIBAndCGContextFromHandle( | |
294 const TransportDIB::Handle& dib_handle, | |
295 const gfx::Rect& window_rect, | |
296 scoped_ptr<TransportDIB>* dib_out, | |
297 base::ScopedCFTypeRef<CGContextRef>* cg_context_out) { | |
298 // Convert the shared memory handle to a handle that works in our process, | |
299 // and then use that to create a CGContextRef. | |
300 TransportDIB* dib = TransportDIB::Map(dib_handle); | |
301 CGContextRef cg_context = NULL; | |
302 if (dib) { | |
303 cg_context = CGBitmapContextCreate( | |
304 dib->memory(), | |
305 window_rect.width(), | |
306 window_rect.height(), | |
307 8, | |
308 4 * window_rect.width(), | |
309 base::mac::GetSystemColorSpace(), | |
310 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host); | |
311 CGContextTranslateCTM(cg_context, 0, window_rect.height()); | |
312 CGContextScaleCTM(cg_context, 1, -1); | |
313 } | |
314 dib_out->reset(dib); | |
315 cg_context_out->reset(cg_context); | |
316 } | |
317 | |
318 void WebPluginProxy::SetWindowlessBuffers( | |
319 const TransportDIB::Handle& windowless_buffer0, | |
320 const TransportDIB::Handle& windowless_buffer1, | |
321 const gfx::Rect& window_rect) { | |
322 CreateDIBAndCGContextFromHandle(windowless_buffer0, | |
323 window_rect, | |
324 &windowless_dibs_[0], | |
325 &windowless_contexts_[0]); | |
326 CreateDIBAndCGContextFromHandle(windowless_buffer1, | |
327 window_rect, | |
328 &windowless_dibs_[1], | |
329 &windowless_contexts_[1]); | |
330 } | |
331 | |
332 #else | |
333 | |
334 void WebPluginProxy::SetWindowlessBuffers( | |
335 const TransportDIB::Handle& windowless_buffer0, | |
336 const TransportDIB::Handle& windowless_buffer1, | |
337 const gfx::Rect& window_rect) { | |
338 NOTIMPLEMENTED(); | |
339 } | |
340 | |
341 #endif | |
342 | |
343 void WebPluginProxy::CancelDocumentLoad() { | |
344 Send(new PluginHostMsg_CancelDocumentLoad(route_id_)); | |
345 } | |
346 | |
347 void WebPluginProxy::DidStartLoading() { | |
348 Send(new PluginHostMsg_DidStartLoading(route_id_)); | |
349 } | |
350 | |
351 void WebPluginProxy::DidStopLoading() { | |
352 Send(new PluginHostMsg_DidStopLoading(route_id_)); | |
353 } | |
354 | |
355 #if defined(OS_MACOSX) | |
356 void WebPluginProxy::FocusChanged(bool focused) { | |
357 IPC::Message* msg = new PluginHostMsg_FocusChanged(route_id_, focused); | |
358 Send(msg); | |
359 } | |
360 | |
361 void WebPluginProxy::StartIme() { | |
362 IPC::Message* msg = new PluginHostMsg_StartIme(route_id_); | |
363 // This message can be sent during event-handling, and needs to be delivered | |
364 // within that context. | |
365 msg->set_unblock(true); | |
366 Send(msg); | |
367 } | |
368 | |
369 WebPluginAcceleratedSurface* WebPluginProxy::GetAcceleratedSurface( | |
370 gfx::GpuPreference gpu_preference) { | |
371 if (!accelerated_surface_) | |
372 accelerated_surface_.reset( | |
373 WebPluginAcceleratedSurfaceProxy::Create(this, gpu_preference)); | |
374 return accelerated_surface_.get(); | |
375 } | |
376 | |
377 void WebPluginProxy::AcceleratedPluginEnabledRendering() { | |
378 Send(new PluginHostMsg_AcceleratedPluginEnabledRendering(route_id_)); | |
379 } | |
380 | |
381 void WebPluginProxy::AcceleratedPluginAllocatedIOSurface(int32_t width, | |
382 int32_t height, | |
383 uint32_t surface_id) { | |
384 Send(new PluginHostMsg_AcceleratedPluginAllocatedIOSurface( | |
385 route_id_, width, height, surface_id)); | |
386 } | |
387 | |
388 void WebPluginProxy::AcceleratedPluginSwappedIOSurface() { | |
389 Send(new PluginHostMsg_AcceleratedPluginSwappedIOSurface( | |
390 route_id_)); | |
391 } | |
392 #endif | |
393 | |
394 void WebPluginProxy::OnPaint(const gfx::Rect& damaged_rect) { | |
395 GetContentClient()->SetActiveURL(page_url_); | |
396 | |
397 Paint(damaged_rect); | |
398 Send(new PluginHostMsg_InvalidateRect(route_id_, damaged_rect)); | |
399 } | |
400 | |
401 bool WebPluginProxy::IsOffTheRecord() { | |
402 return channel_->incognito(); | |
403 } | |
404 | |
405 } // namespace content | |
OLD | NEW |