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/renderer/npapi/webplugin_delegate_proxy.h" | |
6 | |
7 #include <stddef.h> | |
8 | |
9 #include <algorithm> | |
10 #include <utility> | |
11 | |
12 #include "base/auto_reset.h" | |
13 #include "base/command_line.h" | |
14 #include "base/files/file_util.h" | |
15 #include "base/logging.h" | |
16 #include "base/macros.h" | |
17 #include "base/memory/ref_counted.h" | |
18 #include "base/memory/scoped_ptr.h" | |
19 #include "base/process/process.h" | |
20 #include "base/strings/string_split.h" | |
21 #include "base/strings/string_util.h" | |
22 #include "base/strings/utf_string_conversions.h" | |
23 #include "base/version.h" | |
24 #include "build/build_config.h" | |
25 #include "cc/resources/shared_bitmap.h" | |
26 #include "content/child/child_process.h" | |
27 #include "content/child/child_shared_bitmap_manager.h" | |
28 #include "content/child/npapi/webplugin_resource_client.h" | |
29 #include "content/child/plugin_messages.h" | |
30 #include "content/common/content_constants_internal.h" | |
31 #include "content/common/cursors/webcursor.h" | |
32 #include "content/common/frame_messages.h" | |
33 #include "content/common/view_messages.h" | |
34 #include "content/public/renderer/content_renderer_client.h" | |
35 #include "content/renderer/npapi/plugin_channel_host.h" | |
36 #include "content/renderer/npapi/webplugin_impl.h" | |
37 #include "content/renderer/render_thread_impl.h" | |
38 #include "content/renderer/render_view_impl.h" | |
39 #include "content/renderer/sad_plugin.h" | |
40 #include "ipc/ipc_channel_handle.h" | |
41 #include "net/base/mime_util.h" | |
42 #include "skia/ext/platform_canvas.h" | |
43 #include "third_party/WebKit/public/platform/WebDragData.h" | |
44 #include "third_party/WebKit/public/platform/WebString.h" | |
45 #include "third_party/WebKit/public/web/WebDocument.h" | |
46 #include "third_party/WebKit/public/web/WebFrame.h" | |
47 #include "third_party/WebKit/public/web/WebView.h" | |
48 #include "ui/gfx/blit.h" | |
49 #include "ui/gfx/canvas.h" | |
50 #include "ui/gfx/geometry/size.h" | |
51 #include "ui/gfx/native_widget_types.h" | |
52 #include "ui/gfx/skia_util.h" | |
53 | |
54 #if defined(OS_POSIX) | |
55 #include "ipc/ipc_channel_posix.h" | |
56 #endif | |
57 | |
58 #if defined(OS_WIN) | |
59 #include "base/win/scoped_handle.h" | |
60 #include "content/public/common/sandbox_init.h" | |
61 #endif | |
62 | |
63 using blink::WebCursorInfo; | |
64 using blink::WebDragData; | |
65 using blink::WebInputEvent; | |
66 using blink::WebString; | |
67 using blink::WebView; | |
68 | |
69 namespace content { | |
70 | |
71 namespace { | |
72 | |
73 class ScopedLogLevel { | |
74 public: | |
75 explicit ScopedLogLevel(int level); | |
76 ~ScopedLogLevel(); | |
77 | |
78 private: | |
79 int old_level_; | |
80 | |
81 DISALLOW_COPY_AND_ASSIGN(ScopedLogLevel); | |
82 }; | |
83 | |
84 ScopedLogLevel::ScopedLogLevel(int level) | |
85 : old_level_(logging::GetMinLogLevel()) { | |
86 logging::SetMinLogLevel(level); | |
87 } | |
88 | |
89 ScopedLogLevel::~ScopedLogLevel() { | |
90 logging::SetMinLogLevel(old_level_); | |
91 } | |
92 | |
93 } // namespace | |
94 | |
95 WebPluginDelegateProxy::WebPluginDelegateProxy( | |
96 WebPluginImpl* plugin, | |
97 const std::string& mime_type, | |
98 const base::WeakPtr<RenderViewImpl>& render_view, | |
99 RenderFrameImpl* render_frame) | |
100 : render_view_(render_view), | |
101 render_frame_(render_frame), | |
102 plugin_(plugin), | |
103 uses_shared_bitmaps_(true), | |
104 #if defined(OS_MACOSX) | |
105 uses_compositor_(false), | |
106 #endif | |
107 mime_type_(mime_type), | |
108 instance_id_(MSG_ROUTING_NONE), | |
109 sad_plugin_(NULL), | |
110 invalidate_pending_(false), | |
111 transparent_(false), | |
112 front_buffer_index_(0), | |
113 page_url_(render_view_->webview()->mainFrame()->document().url()) { | |
114 } | |
115 | |
116 WebPluginDelegateProxy::~WebPluginDelegateProxy() { | |
117 } | |
118 | |
119 WebPluginDelegateProxy::SharedBitmap::SharedBitmap() {} | |
120 | |
121 WebPluginDelegateProxy::SharedBitmap::~SharedBitmap() {} | |
122 | |
123 void WebPluginDelegateProxy::PluginDestroyed() { | |
124 #if defined(OS_MACOSX) | |
125 // Ensure that the renderer doesn't think the plugin still has focus. | |
126 if (render_view_) | |
127 render_view_->PluginFocusChanged(false, instance_id_); | |
128 #endif | |
129 | |
130 if (render_view_.get()) | |
131 render_view_->UnregisterPluginDelegate(this); | |
132 | |
133 if (channel_host_.get()) { | |
134 Send(new PluginMsg_DestroyInstance(instance_id_)); | |
135 | |
136 // Must remove the route after sending the destroy message, rather than | |
137 // before, since RemoveRoute can lead to all the outstanding NPObjects | |
138 // being told the channel went away if this was the last instance. | |
139 channel_host_->RemoveRoute(instance_id_); | |
140 | |
141 // Release the channel host now. If we are is the last reference to the | |
142 // channel, this avoids a race where this renderer asks a new connection to | |
143 // the same plugin between now and the time 'this' is actually deleted. | |
144 // Destroying the channel host is what releases the channel name -> FD | |
145 // association on POSIX, and if we ask for a new connection before it is | |
146 // released, the plugin will give us a new FD, and we'll assert when trying | |
147 // to associate it with the channel name. | |
148 channel_host_ = NULL; | |
149 } | |
150 | |
151 plugin_ = NULL; | |
152 | |
153 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); | |
154 } | |
155 | |
156 bool WebPluginDelegateProxy::Initialize( | |
157 const GURL& url, | |
158 const std::vector<std::string>& arg_names, | |
159 const std::vector<std::string>& arg_values, | |
160 bool load_manually) { | |
161 // TODO(shess): Attempt to work around http://crbug.com/97285 and | |
162 // http://crbug.com/141055 by retrying the connection. Reports seem | |
163 // to indicate that the plugin hasn't crashed, and that the problem | |
164 // is not 100% persistent. | |
165 const size_t kAttempts = 2; | |
166 | |
167 bool result = false; | |
168 scoped_refptr<PluginChannelHost> channel_host; | |
169 int instance_id = 0; | |
170 | |
171 for (size_t attempt = 0; !result && attempt < kAttempts; attempt++) { | |
172 #if defined(OS_MACOSX) | |
173 // TODO(shess): Debugging for http://crbug.com/97285 . See comment | |
174 // in plugin_channel_host.cc. | |
175 scoped_ptr<base::AutoReset<bool> > track_nested_removes( | |
176 new base::AutoReset<bool>(PluginChannelHost::GetRemoveTrackingFlag(), | |
177 true)); | |
178 #endif | |
179 | |
180 IPC::ChannelHandle channel_handle; | |
181 if (!RenderThreadImpl::current()->Send(new FrameHostMsg_OpenChannelToPlugin( | |
182 render_frame_->GetRoutingID(), url, page_url_, mime_type_, | |
183 &channel_handle, &info_))) { | |
184 continue; | |
185 } | |
186 | |
187 if (channel_handle.name.empty()) { | |
188 // We got an invalid handle. Either the plugin couldn't be found (which | |
189 // shouldn't happen, since if we got here the plugin should exist) or the | |
190 // plugin crashed on initialization. | |
191 if (!info_.path.empty()) { | |
192 render_view_->GetMainRenderFrame()->PluginCrashed( | |
193 info_.path, base::kNullProcessId); | |
194 LOG(ERROR) << "Plugin crashed on start"; | |
195 | |
196 // Return true so that the plugin widget is created and we can paint the | |
197 // crashed plugin there. | |
198 return true; | |
199 } | |
200 LOG(ERROR) << "Plugin couldn't be found"; | |
201 return false; | |
202 } | |
203 | |
204 channel_host = PluginChannelHost::GetPluginChannelHost( | |
205 channel_handle, ChildProcess::current()->io_task_runner()); | |
206 if (!channel_host.get()) { | |
207 LOG(ERROR) << "Couldn't get PluginChannelHost"; | |
208 continue; | |
209 } | |
210 #if defined(OS_MACOSX) | |
211 track_nested_removes.reset(); | |
212 #endif | |
213 | |
214 { | |
215 // TODO(bauerb): Debugging for http://crbug.com/141055. | |
216 ScopedLogLevel log_level(-2); // Equivalent to --v=2 | |
217 result = channel_host->Send(new PluginMsg_CreateInstance( | |
218 mime_type_, &instance_id)); | |
219 if (!result) { | |
220 LOG(ERROR) << "Couldn't send PluginMsg_CreateInstance"; | |
221 continue; | |
222 } | |
223 } | |
224 } | |
225 | |
226 // Failed too often, give up. | |
227 if (!result) | |
228 return false; | |
229 | |
230 channel_host_ = channel_host; | |
231 instance_id_ = instance_id; | |
232 | |
233 channel_host_->AddRoute(instance_id_, this); | |
234 | |
235 // Now tell the PluginInstance in the plugin process to initialize. | |
236 PluginMsg_Init_Params params; | |
237 params.url = url; | |
238 params.page_url = page_url_; | |
239 params.arg_names = arg_names; | |
240 params.arg_values = arg_values; | |
241 params.host_render_view_routing_id = render_view_->GetRoutingID(); | |
242 params.load_manually = load_manually; | |
243 | |
244 result = false; | |
245 Send(new PluginMsg_Init(instance_id_, params, &transparent_, &result)); | |
246 | |
247 if (!result) | |
248 LOG(WARNING) << "PluginMsg_Init returned false"; | |
249 | |
250 render_view_->RegisterPluginDelegate(this); | |
251 | |
252 return result; | |
253 } | |
254 | |
255 bool WebPluginDelegateProxy::Send(IPC::Message* msg) { | |
256 if (!channel_host_.get()) { | |
257 DLOG(WARNING) << "dropping message because channel host is null"; | |
258 delete msg; | |
259 return false; | |
260 } | |
261 | |
262 return channel_host_->Send(msg); | |
263 } | |
264 | |
265 bool WebPluginDelegateProxy::OnMessageReceived(const IPC::Message& msg) { | |
266 GetContentClient()->SetActiveURL(page_url_); | |
267 | |
268 bool handled = true; | |
269 IPC_BEGIN_MESSAGE_MAP(WebPluginDelegateProxy, msg) | |
270 IPC_MESSAGE_HANDLER(PluginHostMsg_InvalidateRect, OnInvalidateRect) | |
271 IPC_MESSAGE_HANDLER(PluginHostMsg_ResolveProxy, OnResolveProxy) | |
272 IPC_MESSAGE_HANDLER(PluginHostMsg_SetCookie, OnSetCookie) | |
273 IPC_MESSAGE_HANDLER(PluginHostMsg_GetCookies, OnGetCookies) | |
274 IPC_MESSAGE_HANDLER(PluginHostMsg_CancelDocumentLoad, OnCancelDocumentLoad) | |
275 IPC_MESSAGE_HANDLER(PluginHostMsg_DidStartLoading, OnDidStartLoading) | |
276 IPC_MESSAGE_HANDLER(PluginHostMsg_DidStopLoading, OnDidStopLoading) | |
277 #if defined(OS_MACOSX) | |
278 IPC_MESSAGE_HANDLER(PluginHostMsg_FocusChanged, | |
279 OnFocusChanged); | |
280 IPC_MESSAGE_HANDLER(PluginHostMsg_StartIme, | |
281 OnStartIme); | |
282 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginEnabledRendering, | |
283 OnAcceleratedPluginEnabledRendering) | |
284 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginAllocatedIOSurface, | |
285 OnAcceleratedPluginAllocatedIOSurface) | |
286 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginSwappedIOSurface, | |
287 OnAcceleratedPluginSwappedIOSurface) | |
288 #endif | |
289 IPC_MESSAGE_UNHANDLED(handled = false) | |
290 IPC_END_MESSAGE_MAP() | |
291 DCHECK(handled); | |
292 return handled; | |
293 } | |
294 | |
295 void WebPluginDelegateProxy::OnChannelError() { | |
296 if (plugin_) { | |
297 plugin_->Invalidate(); | |
298 } | |
299 if (channel_host_.get() && !channel_host_->expecting_shutdown()) { | |
300 render_view_->GetMainRenderFrame()->PluginCrashed( | |
301 info_.path, channel_host_->peer_pid()); | |
302 } | |
303 | |
304 #if defined(OS_MACOSX) | |
305 // Ensure that the renderer doesn't think the plugin still has focus. | |
306 if (render_view_) | |
307 render_view_->PluginFocusChanged(false, instance_id_); | |
308 #endif | |
309 } | |
310 | |
311 static void CopySharedMemoryHandleForMessage( | |
312 const base::SharedMemoryHandle& handle_in, | |
313 base::SharedMemoryHandle* handle_out, | |
314 base::ProcessId peer_pid) { | |
315 #if defined(OS_POSIX) || defined(OS_WIN) | |
316 *handle_out = base::SharedMemory::DuplicateHandle(handle_in); | |
317 #else | |
318 #error Shared memory copy not implemented. | |
319 #endif | |
320 } | |
321 | |
322 void WebPluginDelegateProxy::SendUpdateGeometry( | |
323 bool bitmaps_changed) { | |
324 if (!channel_host_.get()) | |
325 return; | |
326 | |
327 PluginMsg_UpdateGeometry_Param param; | |
328 param.window_rect = plugin_rect_; | |
329 param.clip_rect = clip_rect_; | |
330 param.windowless_buffer0 = base::SharedMemory::NULLHandle(); | |
331 param.windowless_buffer1 = base::SharedMemory::NULLHandle(); | |
332 param.windowless_buffer_index = back_buffer_index(); | |
333 | |
334 #if defined(OS_POSIX) | |
335 // If we're using POSIX mmap'd TransportDIBs, sending the handle across | |
336 // IPC establishes a new mapping rather than just sending a window ID, | |
337 // so only do so if we've actually changed the shared memory bitmaps. | |
338 if (bitmaps_changed) | |
339 #endif | |
340 { | |
341 if (transport_stores_[0].bitmap) | |
342 CopySharedMemoryHandleForMessage( | |
343 transport_stores_[0].bitmap->shared_memory()->handle(), | |
344 ¶m.windowless_buffer0, channel_host_->peer_pid()); | |
345 | |
346 if (transport_stores_[1].bitmap) | |
347 CopySharedMemoryHandleForMessage( | |
348 transport_stores_[1].bitmap->shared_memory()->handle(), | |
349 ¶m.windowless_buffer1, channel_host_->peer_pid()); | |
350 } | |
351 | |
352 IPC::Message* msg; | |
353 #if defined(OS_WIN) | |
354 if (UseSynchronousGeometryUpdates()) { | |
355 msg = new PluginMsg_UpdateGeometrySync(instance_id_, param); | |
356 } else // NOLINT | |
357 #endif | |
358 { | |
359 msg = new PluginMsg_UpdateGeometry(instance_id_, param); | |
360 msg->set_unblock(true); | |
361 } | |
362 | |
363 Send(msg); | |
364 } | |
365 | |
366 void WebPluginDelegateProxy::UpdateGeometry(const gfx::Rect& window_rect, | |
367 const gfx::Rect& clip_rect) { | |
368 // window_rect becomes either a window in native windowing system | |
369 // coords, or a backing buffer. In either case things will go bad | |
370 // if the rectangle is very large. | |
371 if (window_rect.width() < 0 || window_rect.width() > kMaxPluginSideLength || | |
372 window_rect.height() < 0 || window_rect.height() > kMaxPluginSideLength || | |
373 // We know this won't overflow due to above checks. | |
374 static_cast<uint32_t>(window_rect.width()) * | |
375 static_cast<uint32_t>(window_rect.height()) > | |
376 kMaxPluginSize) { | |
377 return; | |
378 } | |
379 | |
380 plugin_rect_ = window_rect; | |
381 clip_rect_ = clip_rect; | |
382 | |
383 bool bitmaps_changed = false; | |
384 | |
385 if (uses_shared_bitmaps_) { | |
386 if (!front_buffer_canvas() || | |
387 (window_rect.width() != | |
388 front_buffer_canvas()->getBaseLayerSize().width() || | |
389 window_rect.height() != | |
390 front_buffer_canvas()->getBaseLayerSize().height())) | |
391 { | |
392 bitmaps_changed = true; | |
393 | |
394 // Create a shared memory section that the plugin paints into | |
395 // asynchronously. | |
396 ResetWindowlessBitmaps(); | |
397 if (!window_rect.IsEmpty()) { | |
398 if (!CreateSharedBitmap(&transport_stores_[0].bitmap, | |
399 &transport_stores_[0].canvas) || | |
400 !CreateSharedBitmap(&transport_stores_[1].bitmap, | |
401 &transport_stores_[1].canvas)) { | |
402 DCHECK(false); | |
403 ResetWindowlessBitmaps(); | |
404 return; | |
405 } | |
406 } | |
407 } | |
408 } | |
409 | |
410 SendUpdateGeometry(bitmaps_changed); | |
411 } | |
412 | |
413 void WebPluginDelegateProxy::ResetWindowlessBitmaps() { | |
414 transport_stores_[0].bitmap.reset(); | |
415 transport_stores_[1].bitmap.reset(); | |
416 | |
417 transport_stores_[0].canvas.clear(); | |
418 transport_stores_[1].canvas.clear(); | |
419 transport_store_painted_ = gfx::Rect(); | |
420 front_buffer_diff_ = gfx::Rect(); | |
421 } | |
422 | |
423 #if !defined(OS_WIN) | |
424 static size_t BitmapSizeForPluginRect(const gfx::Rect& plugin_rect) { | |
425 const size_t stride = | |
426 skia::PlatformCanvasStrideForWidth(plugin_rect.width()); | |
427 return stride * plugin_rect.height(); | |
428 } | |
429 | |
430 bool WebPluginDelegateProxy::CreateLocalBitmap( | |
431 std::vector<uint8_t>* memory, | |
432 skia::RefPtr<SkCanvas>* canvas) { | |
433 const size_t size = BitmapSizeForPluginRect(plugin_rect_); | |
434 memory->resize(size); | |
435 if (memory->size() != size) | |
436 return false; | |
437 *canvas = skia::AdoptRef(skia::CreatePlatformCanvas( | |
438 plugin_rect_.width(), plugin_rect_.height(), true, &((*memory)[0]), | |
439 skia::CRASH_ON_FAILURE)); | |
440 return true; | |
441 } | |
442 #endif | |
443 | |
444 bool WebPluginDelegateProxy::CreateSharedBitmap( | |
445 scoped_ptr<SharedMemoryBitmap>* memory, | |
446 skia::RefPtr<SkCanvas>* canvas) { | |
447 *memory = ChildThreadImpl::current() | |
448 ->shared_bitmap_manager() | |
449 ->AllocateSharedMemoryBitmap(plugin_rect_.size()); | |
450 if (!memory->get()) | |
451 return false; | |
452 DCHECK((*memory)->shared_memory()); | |
453 #if defined(OS_POSIX) | |
454 *canvas = skia::AdoptRef(skia::CreatePlatformCanvas( | |
455 plugin_rect_.width(), plugin_rect_.height(), true, (*memory)->pixels(), | |
456 skia::RETURN_NULL_ON_FAILURE)); | |
457 #else | |
458 *canvas = skia::AdoptRef(skia::CreatePlatformCanvas( | |
459 plugin_rect_.width(), plugin_rect_.height(), true, | |
460 (*memory)->shared_memory()->handle().GetHandle(), | |
461 skia::RETURN_NULL_ON_FAILURE)); | |
462 #endif | |
463 return !!canvas->get(); | |
464 } | |
465 | |
466 void WebPluginDelegateProxy::Paint(SkCanvas* canvas, | |
467 const gfx::Rect& damaged_rect) { | |
468 // Limit the damaged rectangle to whatever is contained inside the plugin | |
469 // rectangle, as that's the rectangle that we'll actually draw. | |
470 gfx::Rect rect = gfx::IntersectRects(damaged_rect, plugin_rect_); | |
471 | |
472 // If the plugin is no longer connected (channel crashed) draw a crashed | |
473 // plugin bitmap | |
474 if (!channel_host_.get() || !channel_host_->channel_valid()) { | |
475 // Lazily load the sad plugin image. | |
476 if (!sad_plugin_) | |
477 sad_plugin_ = GetContentClient()->renderer()->GetSadPluginBitmap(); | |
478 if (sad_plugin_) | |
479 PaintSadPlugin(canvas, plugin_rect_, *sad_plugin_); | |
480 return; | |
481 } | |
482 | |
483 if (!uses_shared_bitmaps_) | |
484 return; | |
485 | |
486 // We got a paint before the plugin's coordinates, so there's no buffer to | |
487 // copy from. | |
488 if (!front_buffer_canvas()) | |
489 return; | |
490 | |
491 gfx::Rect offset_rect = rect; | |
492 offset_rect.Offset(-plugin_rect_.x(), -plugin_rect_.y()); | |
493 | |
494 // transport_store_painted_ is really a bounding box, so in principle this | |
495 // check could falsely indicate that we don't need to paint offset_rect, but | |
496 // in practice it works fine. | |
497 if (!transport_store_painted_.Contains(offset_rect)) { | |
498 Send(new PluginMsg_Paint(instance_id_, offset_rect)); | |
499 // Since the plugin is not blocked on the renderer in this context, there is | |
500 // a chance that it will begin repainting the back-buffer before we complete | |
501 // capturing the data. Buffer flipping would increase that risk because | |
502 // geometry update is asynchronous, so we don't want to use buffer flipping | |
503 // here. | |
504 UpdateFrontBuffer(offset_rect, false); | |
505 } | |
506 | |
507 const SkBitmap bitmap = skia::ReadPixels(front_buffer_canvas()); | |
508 SkPaint paint; | |
509 paint.setXfermodeMode( | |
510 transparent_ ? SkXfermode::kSrcATop_Mode : SkXfermode::kSrc_Mode); | |
511 SkRect src_rect = gfx::RectToSkRect(offset_rect); | |
512 canvas->drawBitmapRect(bitmap, | |
513 src_rect, | |
514 gfx::RectToSkRect(rect), | |
515 &paint); | |
516 | |
517 if (invalidate_pending_) { | |
518 // Only send the PaintAck message if this paint is in response to an | |
519 // invalidate from the plugin, since this message acts as an access token | |
520 // to ensure only one process is using the shared bitmap at a time. | |
521 invalidate_pending_ = false; | |
522 Send(new PluginMsg_DidPaint(instance_id_)); | |
523 } | |
524 } | |
525 | |
526 void WebPluginDelegateProxy::SetFocus(bool focused) { | |
527 Send(new PluginMsg_SetFocus(instance_id_, focused)); | |
528 } | |
529 | |
530 bool WebPluginDelegateProxy::HandleInputEvent( | |
531 const WebInputEvent& event, | |
532 WebCursor::CursorInfo* cursor_info) { | |
533 bool handled = false; | |
534 WebCursor cursor; | |
535 Send(new PluginMsg_HandleInputEvent(instance_id_, &event, &handled, &cursor)); | |
536 return handled; | |
537 } | |
538 | |
539 int WebPluginDelegateProxy::GetProcessId() { | |
540 return channel_host_->peer_pid(); | |
541 } | |
542 | |
543 void WebPluginDelegateProxy::SetContentAreaFocus(bool has_focus) { | |
544 IPC::Message* msg = new PluginMsg_SetContentAreaFocus(instance_id_, | |
545 has_focus); | |
546 // Make sure focus events are delivered in the right order relative to | |
547 // sync messages they might interact with (Paint, HandleEvent, etc.). | |
548 msg->set_unblock(true); | |
549 Send(msg); | |
550 } | |
551 | |
552 #if defined(OS_MACOSX) | |
553 void WebPluginDelegateProxy::SetWindowFocus(bool window_has_focus) { | |
554 IPC::Message* msg = new PluginMsg_SetWindowFocus(instance_id_, | |
555 window_has_focus); | |
556 // Make sure focus events are delivered in the right order relative to | |
557 // sync messages they might interact with (Paint, HandleEvent, etc.). | |
558 msg->set_unblock(true); | |
559 Send(msg); | |
560 } | |
561 | |
562 void WebPluginDelegateProxy::SetContainerVisibility(bool is_visible) { | |
563 IPC::Message* msg; | |
564 if (is_visible) { | |
565 gfx::Rect window_frame = render_view_->GetWidget()->rootWindowRect(); | |
566 gfx::Rect view_frame = render_view_->GetWidget()->windowRect(); | |
567 blink::WebView* webview = render_view_->webview(); | |
568 msg = new PluginMsg_ContainerShown(instance_id_, window_frame, view_frame, | |
569 webview && webview->isActive()); | |
570 } else { | |
571 msg = new PluginMsg_ContainerHidden(instance_id_); | |
572 } | |
573 // Make sure visibility events are delivered in the right order relative to | |
574 // sync messages they might interact with (Paint, HandleEvent, etc.). | |
575 msg->set_unblock(true); | |
576 Send(msg); | |
577 } | |
578 | |
579 void WebPluginDelegateProxy::WindowFrameChanged(gfx::Rect window_frame, | |
580 gfx::Rect view_frame) { | |
581 IPC::Message* msg = new PluginMsg_WindowFrameChanged(instance_id_, | |
582 window_frame, | |
583 view_frame); | |
584 // Make sure frame events are delivered in the right order relative to | |
585 // sync messages they might interact with (e.g., HandleEvent). | |
586 msg->set_unblock(true); | |
587 Send(msg); | |
588 } | |
589 void WebPluginDelegateProxy::ImeCompositionCompleted(const base::string16& text, | |
590 int plugin_id) { | |
591 // If the message isn't intended for this plugin, there's nothing to do. | |
592 if (instance_id_ != plugin_id) | |
593 return; | |
594 | |
595 IPC::Message* msg = new PluginMsg_ImeCompositionCompleted(instance_id_, | |
596 text); | |
597 // Order relative to other key events is important. | |
598 msg->set_unblock(true); | |
599 Send(msg); | |
600 } | |
601 #endif // OS_MACOSX | |
602 | |
603 void WebPluginDelegateProxy::OnInvalidateRect(const gfx::Rect& rect) { | |
604 if (!plugin_) | |
605 return; | |
606 | |
607 // Clip the invalidation rect to the plugin bounds; the plugin may have been | |
608 // resized since the invalidate message was sent. | |
609 gfx::Rect clipped_rect = | |
610 gfx::IntersectRects(rect, gfx::Rect(plugin_rect_.size())); | |
611 | |
612 invalidate_pending_ = true; | |
613 // The plugin is blocked on the renderer because the invalidate message it has | |
614 // sent us is synchronous, so we can use buffer flipping here if the caller | |
615 // allows it. | |
616 UpdateFrontBuffer(clipped_rect, true); | |
617 plugin_->InvalidateRect(clipped_rect); | |
618 } | |
619 | |
620 void WebPluginDelegateProxy::OnResolveProxy(const GURL& url, | |
621 bool* result, | |
622 std::string* proxy_list) { | |
623 *result = RenderThreadImpl::current()->ResolveProxy(url, proxy_list); | |
624 } | |
625 | |
626 void WebPluginDelegateProxy::OnSetCookie(const GURL& url, | |
627 const GURL& first_party_for_cookies, | |
628 const std::string& cookie) { | |
629 if (plugin_) | |
630 plugin_->SetCookie(url, first_party_for_cookies, cookie); | |
631 } | |
632 | |
633 void WebPluginDelegateProxy::OnGetCookies(const GURL& url, | |
634 const GURL& first_party_for_cookies, | |
635 std::string* cookies) { | |
636 DCHECK(cookies); | |
637 if (plugin_) | |
638 *cookies = plugin_->GetCookies(url, first_party_for_cookies); | |
639 } | |
640 | |
641 void WebPluginDelegateProxy::CopyFromBackBufferToFrontBuffer( | |
642 const gfx::Rect& rect) { | |
643 // Blitting the bits directly is much faster than going through CG, and since | |
644 // the goal is just to move the raw pixels between two bitmaps with the same | |
645 // pixel format (no compositing, color correction, etc.), it's safe. | |
646 const size_t stride = | |
647 skia::PlatformCanvasStrideForWidth(plugin_rect_.width()); | |
648 const size_t chunk_size = 4 * rect.width(); | |
649 DCHECK(back_buffer_bitmap() != NULL); | |
650 uint8_t* source_data = | |
651 back_buffer_bitmap()->pixels() + rect.y() * stride + 4 * rect.x(); | |
652 DCHECK(front_buffer_bitmap() != NULL); | |
653 uint8_t* target_data = | |
654 front_buffer_bitmap()->pixels() + rect.y() * stride + 4 * rect.x(); | |
655 for (int row = 0; row < rect.height(); ++row) { | |
656 memcpy(target_data, source_data, chunk_size); | |
657 source_data += stride; | |
658 target_data += stride; | |
659 } | |
660 } | |
661 | |
662 void WebPluginDelegateProxy::UpdateFrontBuffer( | |
663 const gfx::Rect& rect, | |
664 bool allow_buffer_flipping) { | |
665 if (!front_buffer_canvas()) { | |
666 return; | |
667 } | |
668 | |
669 #if defined(OS_WIN) | |
670 // If SendUpdateGeometry() would block on the plugin process then we don't | |
671 // want to use buffer flipping at all since it would add extra locking. | |
672 // (Alternatively we could probably safely use async updates for buffer | |
673 // flipping all the time since the size is not changing.) | |
674 if (UseSynchronousGeometryUpdates()) { | |
675 allow_buffer_flipping = false; | |
676 } | |
677 #endif | |
678 | |
679 // Plugin has just painted "rect" into the back-buffer, so the front-buffer | |
680 // no longer holds the latest content for that rectangle. | |
681 front_buffer_diff_.Subtract(rect); | |
682 if (allow_buffer_flipping && front_buffer_diff_.IsEmpty()) { | |
683 // Back-buffer contains the latest content for all areas; simply flip | |
684 // the buffers. | |
685 front_buffer_index_ = back_buffer_index(); | |
686 SendUpdateGeometry(false); | |
687 // The front-buffer now holds newer content for this region than the | |
688 // back-buffer. | |
689 front_buffer_diff_ = rect; | |
690 } else { | |
691 // Back-buffer contains the latest content for "rect" but the front-buffer | |
692 // contains the latest content for some other areas (or buffer flipping not | |
693 // allowed); fall back to copying the data. | |
694 CopyFromBackBufferToFrontBuffer(rect); | |
695 } | |
696 transport_store_painted_.Union(rect); | |
697 } | |
698 | |
699 #if defined(OS_MACOSX) | |
700 void WebPluginDelegateProxy::OnFocusChanged(bool focused) { | |
701 if (render_view_) | |
702 render_view_->PluginFocusChanged(focused, instance_id_); | |
703 } | |
704 | |
705 void WebPluginDelegateProxy::OnStartIme() { | |
706 if (render_view_) | |
707 render_view_->StartPluginIme(); | |
708 } | |
709 #endif | |
710 | |
711 void WebPluginDelegateProxy::OnCancelDocumentLoad() { | |
712 plugin_->CancelDocumentLoad(); | |
713 } | |
714 | |
715 void WebPluginDelegateProxy::OnDidStartLoading() { | |
716 plugin_->DidStartLoading(); | |
717 } | |
718 | |
719 void WebPluginDelegateProxy::OnDidStopLoading() { | |
720 plugin_->DidStopLoading(); | |
721 } | |
722 | |
723 #if defined(OS_MACOSX) | |
724 void WebPluginDelegateProxy::OnAcceleratedPluginEnabledRendering() { | |
725 uses_compositor_ = true; | |
726 uses_shared_bitmaps_ = false; | |
727 } | |
728 | |
729 void WebPluginDelegateProxy::OnAcceleratedPluginAllocatedIOSurface( | |
730 int32_t width, | |
731 int32_t height, | |
732 uint32_t surface_id) { | |
733 if (plugin_) | |
734 plugin_->AcceleratedPluginAllocatedIOSurface(width, height, surface_id); | |
735 } | |
736 | |
737 void WebPluginDelegateProxy::OnAcceleratedPluginSwappedIOSurface() { | |
738 if (plugin_) | |
739 plugin_->AcceleratedPluginSwappedIOSurface(); | |
740 } | |
741 #endif | |
742 | |
743 #if defined(OS_WIN) | |
744 bool WebPluginDelegateProxy::UseSynchronousGeometryUpdates() { | |
745 // Need to update geometry synchronously with WMP, otherwise if a site | |
746 // scripts the plugin to start playing while it's in the middle of handling | |
747 // an update geometry message, videos don't play. See urls in bug 20260. | |
748 if (info_.name.find(base::ASCIIToUTF16("Windows Media Player")) != | |
749 base::string16::npos) | |
750 return true; | |
751 | |
752 // The move networks plugin needs to be informed of geometry updates | |
753 // synchronously. | |
754 std::vector<WebPluginMimeType>::iterator index; | |
755 for (index = info_.mime_types.begin(); index != info_.mime_types.end(); | |
756 index++) { | |
757 if (index->mime_type == "application/x-vnd.moveplayer.qm" || | |
758 index->mime_type == "application/x-vnd.moveplay2.qm" || | |
759 index->mime_type == "application/x-vnd.movenetworks.qm" || | |
760 index->mime_type == "application/x-vnd.mnplayer.qm") { | |
761 return true; | |
762 } | |
763 } | |
764 return false; | |
765 } | |
766 #endif | |
767 | |
768 } // namespace content | |
OLD | NEW |