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.
5 #include "content/renderer/webplugin_delegate_proxy.h"
7 #if defined(TOOLKIT_GTK)
10 #include <cairo/cairo.h>
15 #include "base/auto_reset.h"
16 #include "base/basictypes.h"
17 #include "base/command_line.h"
18 #include "base/file_util.h"
19 #include "base/logging.h"
20 #include "base/memory/ref_counted.h"
21 #include "base/memory/scoped_ptr.h"
22 #include "base/process.h"
23 #include "base/string_split.h"
24 #include "base/string_util.h"
25 #include "base/utf_string_conversions.h"
26 #include "base/version.h"
27 #include "content/common/child_process.h"
28 #include "content/common/npobject_proxy.h"
29 #include "content/common/npobject_stub.h"
30 #include "content/common/npobject_util.h"
31 #include "content/common/plugin_messages.h"
32 #include "content/common/view_messages.h"
33 #include "content/public/renderer/content_renderer_client.h"
34 #include "content/renderer/plugin_channel_host.h"
35 #include "content/renderer/render_thread_impl.h"
36 #include "content/renderer/render_view_impl.h"
37 #include "ipc/ipc_channel_handle.h"
38 #include "net/base/mime_util.h"
39 #include "skia/ext/platform_canvas.h"
40 #include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h"
41 #include "third_party/WebKit/Source/WebKit/chromium/public/WebCursorInfo.h"
42 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
43 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
44 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
45 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebDragData.h"
46 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
47 #include "ui/gfx/blit.h"
48 #include "ui/gfx/canvas.h"
49 #include "ui/gfx/native_widget_types.h"
50 #include "ui/gfx/size.h"
51 #include "ui/gfx/skia_util.h"
52 #include "webkit/glue/webkit_glue.h"
53 #include "webkit/plugins/npapi/webplugin.h"
54 #include "webkit/plugins/plugin_constants.h"
55 #include "webkit/plugins/sad_plugin.h"
58 #include "ipc/ipc_channel_posix.h"
61 #if defined(OS_MACOSX)
62 #include "base/mac/mac_util.h"
66 #include "content/public/common/sandbox_init.h"
69 using WebKit::WebBindings
;
70 using WebKit::WebCursorInfo
;
71 using WebKit::WebDragData
;
72 using WebKit::WebInputEvent
;
73 using WebKit::WebString
;
74 using WebKit::WebView
;
80 class ScopedLogLevel
{
82 explicit ScopedLogLevel(int level
);
88 DISALLOW_COPY_AND_ASSIGN(ScopedLogLevel
);
91 ScopedLogLevel::ScopedLogLevel(int level
)
92 : old_level_(logging::GetMinLogLevel()) {
93 logging::SetMinLogLevel(level
);
96 ScopedLogLevel::~ScopedLogLevel() {
97 logging::SetMinLogLevel(old_level_
);
100 // Proxy for WebPluginResourceClient. The object owns itself after creation,
101 // deleting itself after its callback has been called.
102 class ResourceClientProxy
: public webkit::npapi::WebPluginResourceClient
{
104 ResourceClientProxy(PluginChannelHost
* channel
, int instance_id
)
105 : channel_(channel
), instance_id_(instance_id
), resource_id_(0),
106 multibyte_response_expected_(false) {
109 ~ResourceClientProxy() {
112 void Initialize(unsigned long resource_id
, const GURL
& url
, int notify_id
) {
113 resource_id_
= resource_id
;
114 channel_
->Send(new PluginMsg_HandleURLRequestReply(
115 instance_id_
, resource_id
, url
, notify_id
));
118 void InitializeForSeekableStream(unsigned long resource_id
,
119 int range_request_id
) {
120 resource_id_
= resource_id
;
121 multibyte_response_expected_
= true;
122 channel_
->Send(new PluginMsg_HTTPRangeRequestReply(
123 instance_id_
, resource_id
, range_request_id
));
126 // PluginResourceClient implementation:
127 void WillSendRequest(const GURL
& url
, int http_status_code
) {
128 DCHECK(channel_
!= NULL
);
129 channel_
->Send(new PluginMsg_WillSendRequest(instance_id_
, resource_id_
,
130 url
, http_status_code
));
133 void DidReceiveResponse(const std::string
& mime_type
,
134 const std::string
& headers
,
135 uint32 expected_length
,
136 uint32 last_modified
,
137 bool request_is_seekable
) {
138 DCHECK(channel_
!= NULL
);
139 PluginMsg_DidReceiveResponseParams params
;
140 params
.id
= resource_id_
;
141 params
.mime_type
= mime_type
;
142 params
.headers
= headers
;
143 params
.expected_length
= expected_length
;
144 params
.last_modified
= last_modified
;
145 params
.request_is_seekable
= request_is_seekable
;
146 // Grab a reference on the underlying channel so it does not get
147 // deleted from under us.
148 scoped_refptr
<PluginChannelHost
> channel_ref(channel_
);
149 channel_
->Send(new PluginMsg_DidReceiveResponse(instance_id_
, params
));
152 void DidReceiveData(const char* buffer
, int length
, int data_offset
) {
153 DCHECK(channel_
!= NULL
);
154 DCHECK_GT(length
, 0);
155 std::vector
<char> data
;
156 data
.resize(static_cast<size_t>(length
));
157 memcpy(&data
.front(), buffer
, length
);
158 // Grab a reference on the underlying channel so it does not get
159 // deleted from under us.
160 scoped_refptr
<PluginChannelHost
> channel_ref(channel_
);
161 channel_
->Send(new PluginMsg_DidReceiveData(instance_id_
, resource_id_
,
165 void DidFinishLoading() {
166 DCHECK(channel_
!= NULL
);
167 channel_
->Send(new PluginMsg_DidFinishLoading(instance_id_
, resource_id_
));
169 MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
173 DCHECK(channel_
!= NULL
);
174 channel_
->Send(new PluginMsg_DidFail(instance_id_
, resource_id_
));
176 MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
179 bool IsMultiByteResponseExpected() {
180 return multibyte_response_expected_
;
188 scoped_refptr
<PluginChannelHost
> channel_
;
190 unsigned long resource_id_
;
191 // Set to true if the response expected is a multibyte response.
192 // For e.g. response for a HTTP byte range request.
193 bool multibyte_response_expected_
;
198 WebPluginDelegateProxy::WebPluginDelegateProxy(
199 const std::string
& mime_type
,
200 const base::WeakPtr
<RenderViewImpl
>& render_view
)
201 : render_view_(render_view
),
203 uses_shared_bitmaps_(false),
204 #if defined(OS_MACOSX)
205 uses_compositor_(false),
206 #elif defined(OS_WIN)
207 dummy_activation_window_(NULL
),
209 window_(gfx::kNullPluginWindow
),
210 mime_type_(mime_type
),
211 instance_id_(MSG_ROUTING_NONE
),
214 invalidate_pending_(false),
215 front_buffer_index_(0),
216 page_url_(render_view_
->webview()->mainFrame()->document().url()) {
219 WebPluginDelegateProxy::~WebPluginDelegateProxy() {
221 WebBindings::releaseObject(npobject_
);
224 WebPluginDelegateProxy::SharedBitmap::SharedBitmap() {}
226 WebPluginDelegateProxy::SharedBitmap::~SharedBitmap() {}
228 void WebPluginDelegateProxy::PluginDestroyed() {
229 #if defined(OS_MACOSX) || defined(OS_WIN)
230 // Ensure that the renderer doesn't think the plugin still has focus.
232 render_view_
->PluginFocusChanged(false, instance_id_
);
236 if (dummy_activation_window_
&& render_view_
) {
237 render_view_
->Send(new ViewHostMsg_WindowlessPluginDummyWindowDestroyed(
238 render_view_
->routing_id(), dummy_activation_window_
));
240 dummy_activation_window_
= NULL
;
247 render_view_
->UnregisterPluginDelegate(this);
250 Send(new PluginMsg_DestroyInstance(instance_id_
));
252 // Must remove the route after sending the destroy message, since
253 // RemoveRoute can lead to all the outstanding NPObjects being told the
254 // channel went away if this was the last instance.
255 channel_host_
->RemoveRoute(instance_id_
);
257 // Release the channel host now. If we are is the last reference to the
258 // channel, this avoids a race where this renderer asks a new connection to
259 // the same plugin between now and the time 'this' is actually deleted.
260 // Destroying the channel host is what releases the channel name -> FD
261 // association on POSIX, and if we ask for a new connection before it is
262 // released, the plugin will give us a new FD, and we'll assert when trying
263 // to associate it with the channel name.
264 channel_host_
= NULL
;
267 if (window_script_object_
) {
268 // Release the window script object, if the plugin didn't already.
269 // If we don't do this then it will linger until the last plugin instance is
270 // destroyed. In the meantime, though, the frame that it refers to may have
271 // been destroyed by WebKit, at which point WebKit will forcibly deallocate
272 // the window script object. The window script object stub is unique to the
273 // plugin instance, so this won't affect other instances.
274 window_script_object_
->DeleteSoon();
279 MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
282 bool WebPluginDelegateProxy::Initialize(
284 const std::vector
<std::string
>& arg_names
,
285 const std::vector
<std::string
>& arg_values
,
286 webkit::npapi::WebPlugin
* plugin
,
287 bool load_manually
) {
288 // TODO(shess): Attempt to work around http://crbug.com/97285 and
289 // http://crbug.com/141055 by retrying the connection. Reports seem
290 // to indicate that the plugin hasn't crashed, and that the problem
291 // is not 100% persistent.
292 const size_t kAttempts
= 2;
295 scoped_refptr
<PluginChannelHost
> channel_host
;
298 for (size_t attempt
= 0; !result
&& attempt
< kAttempts
; attempt
++) {
299 #if defined(OS_MACOSX)
300 // TODO(shess): Debugging for http://crbug.com/97285 . See comment
301 // in plugin_channel_host.cc.
302 scoped_ptr
<base::AutoReset
<bool> > track_nested_removes(
303 new base::AutoReset
<bool>(PluginChannelHost::GetRemoveTrackingFlag(),
307 IPC::ChannelHandle channel_handle
;
308 if (!RenderThreadImpl::current()->Send(new ViewHostMsg_OpenChannelToPlugin(
309 render_view_
->routing_id(), url
, page_url_
, mime_type_
,
310 &channel_handle
, &info_
))) {
314 if (channel_handle
.name
.empty()) {
315 // We got an invalid handle. Either the plugin couldn't be found (which
316 // shouldn't happen, since if we got here the plugin should exist) or the
317 // plugin crashed on initialization.
318 if (!info_
.path
.empty()) {
319 render_view_
->PluginCrashed(info_
.path
);
320 LOG(ERROR
) << "Plug-in crashed on start";
322 // Return true so that the plugin widget is created and we can paint the
323 // crashed plugin there.
326 LOG(ERROR
) << "Plug-in couldn't be found";
331 PluginChannelHost::GetPluginChannelHost(
332 channel_handle
, ChildProcess::current()->io_message_loop_proxy());
333 if (!channel_host
.get()) {
334 LOG(ERROR
) << "Couldn't get PluginChannelHost";
337 #if defined(OS_MACOSX)
338 track_nested_removes
.reset();
342 // TODO(bauerb): Debugging for http://crbug.com/141055.
343 ScopedLogLevel
log_level(-2); // Equivalent to --v=2
344 result
= channel_host
->Send(new PluginMsg_CreateInstance(
345 mime_type_
, &instance_id
));
347 LOG(ERROR
) << "Couldn't send PluginMsg_CreateInstance";
353 // Failed too often, give up.
357 channel_host_
= channel_host
;
358 instance_id_
= instance_id
;
360 channel_host_
->AddRoute(instance_id_
, this, NULL
);
362 // Now tell the PluginInstance in the plugin process to initialize.
363 PluginMsg_Init_Params params
;
365 params
.page_url
= page_url_
;
366 params
.arg_names
= arg_names
;
367 params
.arg_values
= arg_values
;
368 params
.host_render_view_routing_id
= render_view_
->routing_id();
369 params
.load_manually
= load_manually
;
374 Send(new PluginMsg_Init(instance_id_
, params
, &result
));
377 LOG(ERROR
) << "PluginMsg_Init returned false";
379 render_view_
->RegisterPluginDelegate(this);
384 bool WebPluginDelegateProxy::Send(IPC::Message
* msg
) {
385 if (!channel_host_
) {
386 DLOG(WARNING
) << "dropping message because channel host is null";
391 return channel_host_
->Send(msg
);
394 void WebPluginDelegateProxy::SendJavaScriptStream(const GURL
& url
,
395 const std::string
& result
,
398 Send(new PluginMsg_SendJavaScriptStream(
399 instance_id_
, url
, result
, success
, notify_id
));
402 void WebPluginDelegateProxy::DidReceiveManualResponse(
403 const GURL
& url
, const std::string
& mime_type
,
404 const std::string
& headers
, uint32 expected_length
,
405 uint32 last_modified
) {
406 PluginMsg_DidReceiveResponseParams params
;
408 params
.mime_type
= mime_type
;
409 params
.headers
= headers
;
410 params
.expected_length
= expected_length
;
411 params
.last_modified
= last_modified
;
412 Send(new PluginMsg_DidReceiveManualResponse(instance_id_
, url
, params
));
415 void WebPluginDelegateProxy::DidReceiveManualData(const char* buffer
,
417 DCHECK_GT(length
, 0);
418 std::vector
<char> data
;
419 data
.resize(static_cast<size_t>(length
));
420 memcpy(&data
.front(), buffer
, length
);
421 Send(new PluginMsg_DidReceiveManualData(instance_id_
, data
));
424 void WebPluginDelegateProxy::DidFinishManualLoading() {
425 Send(new PluginMsg_DidFinishManualLoading(instance_id_
));
428 void WebPluginDelegateProxy::DidManualLoadFail() {
429 Send(new PluginMsg_DidManualLoadFail(instance_id_
));
432 bool WebPluginDelegateProxy::OnMessageReceived(const IPC::Message
& msg
) {
433 GetContentClient()->SetActiveURL(page_url_
);
436 IPC_BEGIN_MESSAGE_MAP(WebPluginDelegateProxy
, msg
)
437 IPC_MESSAGE_HANDLER(PluginHostMsg_SetWindow
, OnSetWindow
)
439 IPC_MESSAGE_HANDLER(PluginHostMsg_SetWindowlessData
, OnSetWindowlessData
)
440 IPC_MESSAGE_HANDLER(PluginHostMsg_NotifyIMEStatus
, OnNotifyIMEStatus
)
442 IPC_MESSAGE_HANDLER(PluginHostMsg_CancelResource
, OnCancelResource
)
443 IPC_MESSAGE_HANDLER(PluginHostMsg_InvalidateRect
, OnInvalidateRect
)
444 IPC_MESSAGE_HANDLER(PluginHostMsg_GetWindowScriptNPObject
,
445 OnGetWindowScriptNPObject
)
446 IPC_MESSAGE_HANDLER(PluginHostMsg_GetPluginElement
, OnGetPluginElement
)
447 IPC_MESSAGE_HANDLER(PluginHostMsg_ResolveProxy
, OnResolveProxy
)
448 IPC_MESSAGE_HANDLER(PluginHostMsg_SetCookie
, OnSetCookie
)
449 IPC_MESSAGE_HANDLER(PluginHostMsg_GetCookies
, OnGetCookies
)
450 IPC_MESSAGE_HANDLER(PluginHostMsg_URLRequest
, OnHandleURLRequest
)
451 IPC_MESSAGE_HANDLER(PluginHostMsg_CancelDocumentLoad
, OnCancelDocumentLoad
)
452 IPC_MESSAGE_HANDLER(PluginHostMsg_InitiateHTTPRangeRequest
,
453 OnInitiateHTTPRangeRequest
)
454 IPC_MESSAGE_HANDLER(PluginHostMsg_DeferResourceLoading
,
455 OnDeferResourceLoading
)
457 #if defined(OS_MACOSX)
458 IPC_MESSAGE_HANDLER(PluginHostMsg_FocusChanged
,
460 IPC_MESSAGE_HANDLER(PluginHostMsg_StartIme
,
462 IPC_MESSAGE_HANDLER(PluginHostMsg_BindFakePluginWindowHandle
,
463 OnBindFakePluginWindowHandle
);
464 // Used only on 10.6 and later.
465 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedSurfaceSetIOSurface
,
466 OnAcceleratedSurfaceSetIOSurface
)
467 // Used on 10.5 and earlier.
468 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedSurfaceSetTransportDIB
,
469 OnAcceleratedSurfaceSetTransportDIB
)
470 IPC_MESSAGE_HANDLER(PluginHostMsg_AllocTransportDIB
,
471 OnAcceleratedSurfaceAllocTransportDIB
)
472 IPC_MESSAGE_HANDLER(PluginHostMsg_FreeTransportDIB
,
473 OnAcceleratedSurfaceFreeTransportDIB
)
474 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedSurfaceBuffersSwapped
,
475 OnAcceleratedSurfaceBuffersSwapped
)
476 // Used only on 10.6 and later.
477 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginEnabledRendering
,
478 OnAcceleratedPluginEnabledRendering
)
479 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginAllocatedIOSurface
,
480 OnAcceleratedPluginAllocatedIOSurface
)
481 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginSwappedIOSurface
,
482 OnAcceleratedPluginSwappedIOSurface
)
484 IPC_MESSAGE_HANDLER(PluginHostMsg_URLRedirectResponse
,
485 OnURLRedirectResponse
)
486 IPC_MESSAGE_UNHANDLED(handled
= false)
487 IPC_END_MESSAGE_MAP()
492 void WebPluginDelegateProxy::OnChannelError() {
495 // The actual WebPluginDelegate never got a chance to tell the WebPlugin
496 // its window was going away. Do it on its behalf.
499 plugin_
->Invalidate();
501 if (!channel_host_
->expecting_shutdown())
502 render_view_
->PluginCrashed(info_
.path
);
504 #if defined(OS_MACOSX) || defined(OS_WIN)
505 // Ensure that the renderer doesn't think the plugin still has focus.
507 render_view_
->PluginFocusChanged(false, instance_id_
);
511 static void CopyTransportDIBHandleForMessage(
512 const TransportDIB::Handle
& handle_in
,
513 TransportDIB::Handle
* handle_out
,
514 base::ProcessId peer_pid
) {
515 #if defined(OS_MACOSX)
516 // On Mac, TransportDIB::Handle is typedef'ed to FileDescriptor, and
517 // FileDescriptor message fields needs to remain valid until the message is
518 // sent or else the sendmsg() call will fail.
519 if ((handle_out
->fd
= HANDLE_EINTR(dup(handle_in
.fd
))) < 0) {
520 PLOG(ERROR
) << "dup()";
523 handle_out
->auto_close
= true;
524 #elif defined(OS_WIN)
525 // On Windows we need to duplicate the handle for the plugin process.
527 BrokerDuplicateHandle(handle_in
, peer_pid
, handle_out
,
528 FILE_MAP_READ
| FILE_MAP_WRITE
, 0);
529 DCHECK(*handle_out
!= NULL
);
531 // Don't need to do anything special for other platforms.
532 *handle_out
= handle_in
;
536 void WebPluginDelegateProxy::SendUpdateGeometry(
537 bool bitmaps_changed
) {
538 PluginMsg_UpdateGeometry_Param param
;
539 param
.window_rect
= plugin_rect_
;
540 param
.clip_rect
= clip_rect_
;
541 param
.windowless_buffer0
= TransportDIB::DefaultHandleValue();
542 param
.windowless_buffer1
= TransportDIB::DefaultHandleValue();
543 param
.windowless_buffer_index
= back_buffer_index();
545 #if defined(OS_POSIX)
546 // If we're using POSIX mmap'd TransportDIBs, sending the handle across
547 // IPC establishes a new mapping rather than just sending a window ID,
548 // so only do so if we've actually changed the shared memory bitmaps.
552 if (transport_stores_
[0].dib
.get())
553 CopyTransportDIBHandleForMessage(transport_stores_
[0].dib
->handle(),
554 ¶m
.windowless_buffer0
,
555 channel_host_
->peer_pid());
557 if (transport_stores_
[1].dib
.get())
558 CopyTransportDIBHandleForMessage(transport_stores_
[1].dib
->handle(),
559 ¶m
.windowless_buffer1
,
560 channel_host_
->peer_pid());
565 if (UseSynchronousGeometryUpdates()) {
566 msg
= new PluginMsg_UpdateGeometrySync(instance_id_
, param
);
570 msg
= new PluginMsg_UpdateGeometry(instance_id_
, param
);
571 msg
->set_unblock(true);
577 void WebPluginDelegateProxy::UpdateGeometry(const gfx::Rect
& window_rect
,
578 const gfx::Rect
& clip_rect
) {
579 // window_rect becomes either a window in native windowing system
580 // coords, or a backing buffer. In either case things will go bad
581 // if the rectangle is very large.
582 if (window_rect
.width() < 0 || window_rect
.width() > kMaxPluginSideLength
||
583 window_rect
.height() < 0 || window_rect
.height() > kMaxPluginSideLength
||
584 // We know this won't overflow due to above checks.
585 static_cast<uint32
>(window_rect
.width()) *
586 static_cast<uint32
>(window_rect
.height()) > kMaxPluginSize
) {
590 plugin_rect_
= window_rect
;
591 clip_rect_
= clip_rect
;
593 bool bitmaps_changed
= false;
595 if (uses_shared_bitmaps_
) {
596 if (!front_buffer_canvas() ||
597 (window_rect
.width() != front_buffer_canvas()->getDevice()->width() ||
598 window_rect
.height() != front_buffer_canvas()->getDevice()->height()))
600 bitmaps_changed
= true;
602 // Create a shared memory section that the plugin paints into
604 ResetWindowlessBitmaps();
605 if (!window_rect
.IsEmpty()) {
606 if (!CreateSharedBitmap(&transport_stores_
[0].dib
,
607 &transport_stores_
[0].canvas
) ||
608 !CreateSharedBitmap(&transport_stores_
[1].dib
,
609 &transport_stores_
[1].canvas
)) {
611 ResetWindowlessBitmaps();
618 SendUpdateGeometry(bitmaps_changed
);
621 void WebPluginDelegateProxy::ResetWindowlessBitmaps() {
622 transport_stores_
[0].dib
.reset();
623 transport_stores_
[1].dib
.reset();
625 transport_stores_
[0].canvas
.reset();
626 transport_stores_
[1].canvas
.reset();
627 transport_store_painted_
= gfx::Rect();
628 front_buffer_diff_
= gfx::Rect();
631 static size_t BitmapSizeForPluginRect(const gfx::Rect
& plugin_rect
) {
632 const size_t stride
=
633 skia::PlatformCanvasStrideForWidth(plugin_rect
.width());
634 return stride
* plugin_rect
.height();
638 bool WebPluginDelegateProxy::CreateLocalBitmap(
639 std::vector
<uint8
>* memory
,
640 scoped_ptr
<skia::PlatformCanvas
>* canvas
) {
641 const size_t size
= BitmapSizeForPluginRect(plugin_rect_
);
642 memory
->resize(size
);
643 if (memory
->size() != size
)
645 canvas
->reset(skia::CreatePlatformCanvas(
646 plugin_rect_
.width(), plugin_rect_
.height(), true, &((*memory
)[0]),
647 skia::CRASH_ON_FAILURE
));
652 bool WebPluginDelegateProxy::CreateSharedBitmap(
653 scoped_ptr
<TransportDIB
>* memory
,
654 scoped_ptr
<skia::PlatformCanvas
>* canvas
) {
655 const size_t size
= BitmapSizeForPluginRect(plugin_rect_
);
656 #if defined(OS_POSIX) && !defined(OS_MACOSX)
657 memory
->reset(TransportDIB::Create(size
, 0));
661 #if defined(OS_MACOSX)
662 TransportDIB::Handle handle
;
663 IPC::Message
* msg
= new ViewHostMsg_AllocTransportDIB(size
, false, &handle
);
664 if (!RenderThreadImpl::current()->Send(msg
))
668 memory
->reset(TransportDIB::Map(handle
));
670 static uint32 sequence_number
= 0;
671 memory
->reset(TransportDIB::Create(size
, sequence_number
++));
673 canvas
->reset((*memory
)->GetPlatformCanvas(plugin_rect_
.width(),
674 plugin_rect_
.height()));
675 return !!canvas
->get();
678 #if defined(OS_MACOSX)
679 // Flips |rect| vertically within an enclosing rect with height |height|.
680 // Intended for converting rects between flipped and non-flipped contexts.
681 static void FlipRectVerticallyWithHeight(gfx::Rect
* rect
, int height
) {
682 rect
->set_y(height
- rect
->bottom());
686 void WebPluginDelegateProxy::Paint(WebKit::WebCanvas
* canvas
,
687 const gfx::Rect
& damaged_rect
) {
688 // Limit the damaged rectangle to whatever is contained inside the plugin
689 // rectangle, as that's the rectangle that we'll actually draw.
690 gfx::Rect rect
= gfx::IntersectRects(damaged_rect
, plugin_rect_
);
692 // If the plugin is no longer connected (channel crashed) draw a crashed
694 if (!channel_host_
|| !channel_host_
->channel_valid()) {
695 PaintSadPlugin(canvas
, rect
);
699 if (!uses_shared_bitmaps_
)
702 // We got a paint before the plugin's coordinates, so there's no buffer to
704 if (!front_buffer_canvas())
707 gfx::Rect offset_rect
= rect
;
708 offset_rect
.Offset(-plugin_rect_
.x(), -plugin_rect_
.y());
710 // transport_store_painted_ is really a bounding box, so in principle this
711 // check could falsely indicate that we don't need to paint offset_rect, but
712 // in practice it works fine.
713 if (!transport_store_painted_
.Contains(offset_rect
)) {
714 Send(new PluginMsg_Paint(instance_id_
, offset_rect
));
715 // Since the plugin is not blocked on the renderer in this context, there is
716 // a chance that it will begin repainting the back-buffer before we complete
717 // capturing the data. Buffer flipping would increase that risk because
718 // geometry update is asynchronous, so we don't want to use buffer flipping
720 UpdateFrontBuffer(offset_rect
, false);
723 const SkBitmap
& bitmap
=
724 front_buffer_canvas()->getDevice()->accessBitmap(false);
726 paint
.setXfermodeMode(SkXfermode::kSrcATop_Mode
);
727 SkIRect src_rect
= gfx::RectToSkIRect(offset_rect
);
728 canvas
->drawBitmapRect(bitmap
,
730 gfx::RectToSkRect(rect
),
733 if (invalidate_pending_
) {
734 // Only send the PaintAck message if this paint is in response to an
735 // invalidate from the plugin, since this message acts as an access token
736 // to ensure only one process is using the transport dib at a time.
737 invalidate_pending_
= false;
738 Send(new PluginMsg_DidPaint(instance_id_
));
742 NPObject
* WebPluginDelegateProxy::GetPluginScriptableObject() {
744 return WebBindings::retainObject(npobject_
);
746 int route_id
= MSG_ROUTING_NONE
;
747 Send(new PluginMsg_GetPluginScriptableObject(instance_id_
, &route_id
));
748 if (route_id
== MSG_ROUTING_NONE
)
751 npobject_
= NPObjectProxy::Create(
752 channel_host_
.get(), route_id
, 0, page_url_
);
754 return WebBindings::retainObject(npobject_
);
757 bool WebPluginDelegateProxy::GetFormValue(string16
* value
) {
758 bool success
= false;
759 Send(new PluginMsg_GetFormValue(instance_id_
, value
, &success
));
763 void WebPluginDelegateProxy::DidFinishLoadWithReason(
764 const GURL
& url
, NPReason reason
, int notify_id
) {
765 Send(new PluginMsg_DidFinishLoadWithReason(
766 instance_id_
, url
, reason
, notify_id
));
769 void WebPluginDelegateProxy::SetFocus(bool focused
) {
770 Send(new PluginMsg_SetFocus(instance_id_
, focused
));
773 render_view_
->PluginFocusChanged(focused
, instance_id_
);
777 bool WebPluginDelegateProxy::HandleInputEvent(
778 const WebInputEvent
& event
,
779 WebCursorInfo
* cursor_info
) {
782 // A windowless plugin can enter a modal loop in the context of a
783 // NPP_HandleEvent call, in which case we need to pump messages to
784 // the plugin. We pass of the corresponding event handle to the
785 // plugin process, which is set if the plugin does enter a modal loop.
786 IPC::SyncMessage
* message
= new PluginMsg_HandleInputEvent(
787 instance_id_
, &event
, &handled
, &cursor
);
788 message
->set_pump_messages_event(modal_loop_pump_messages_event_
.get());
790 cursor
.GetCursorInfo(cursor_info
);
794 int WebPluginDelegateProxy::GetProcessId() {
795 return channel_host_
->peer_pid();
798 void WebPluginDelegateProxy::SetContentAreaFocus(bool has_focus
) {
799 IPC::Message
* msg
= new PluginMsg_SetContentAreaFocus(instance_id_
,
801 // Make sure focus events are delivered in the right order relative to
802 // sync messages they might interact with (Paint, HandleEvent, etc.).
803 msg
->set_unblock(true);
808 void WebPluginDelegateProxy::ImeCompositionUpdated(
809 const string16
& text
,
810 const std::vector
<int>& clauses
,
811 const std::vector
<int>& target
,
814 // Dispatch the raw IME data if this plug-in is the focused one.
815 if (instance_id_
!= plugin_id
)
818 IPC::Message
* msg
= new PluginMsg_ImeCompositionUpdated(instance_id_
,
819 text
, clauses
, target
, cursor_position
);
820 msg
->set_unblock(true);
824 void WebPluginDelegateProxy::ImeCompositionCompleted(const string16
& text
,
826 // Dispatch the IME text if this plug-in is the focused one.
827 if (instance_id_
!= plugin_id
)
830 IPC::Message
* msg
= new PluginMsg_ImeCompositionCompleted(instance_id_
, text
);
831 msg
->set_unblock(true);
836 #if defined(OS_MACOSX)
837 void WebPluginDelegateProxy::SetWindowFocus(bool window_has_focus
) {
838 IPC::Message
* msg
= new PluginMsg_SetWindowFocus(instance_id_
,
840 // Make sure focus events are delivered in the right order relative to
841 // sync messages they might interact with (Paint, HandleEvent, etc.).
842 msg
->set_unblock(true);
846 void WebPluginDelegateProxy::SetContainerVisibility(bool is_visible
) {
849 gfx::Rect window_frame
= render_view_
->rootWindowRect();
850 gfx::Rect view_frame
= render_view_
->windowRect();
851 WebKit::WebView
* webview
= render_view_
->webview();
852 msg
= new PluginMsg_ContainerShown(instance_id_
, window_frame
, view_frame
,
853 webview
&& webview
->isActive());
855 msg
= new PluginMsg_ContainerHidden(instance_id_
);
857 // Make sure visibility events are delivered in the right order relative to
858 // sync messages they might interact with (Paint, HandleEvent, etc.).
859 msg
->set_unblock(true);
863 void WebPluginDelegateProxy::WindowFrameChanged(gfx::Rect window_frame
,
864 gfx::Rect view_frame
) {
865 IPC::Message
* msg
= new PluginMsg_WindowFrameChanged(instance_id_
,
868 // Make sure frame events are delivered in the right order relative to
869 // sync messages they might interact with (e.g., HandleEvent).
870 msg
->set_unblock(true);
873 void WebPluginDelegateProxy::ImeCompositionCompleted(const string16
& text
,
875 // If the message isn't intended for this plugin, there's nothing to do.
876 if (instance_id_
!= plugin_id
)
879 IPC::Message
* msg
= new PluginMsg_ImeCompositionCompleted(instance_id_
,
881 // Order relative to other key events is important.
882 msg
->set_unblock(true);
887 void WebPluginDelegateProxy::OnSetWindow(gfx::PluginWindowHandle window
) {
888 #if defined(OS_MACOSX)
889 uses_shared_bitmaps_
= !window
&& !uses_compositor_
;
891 uses_shared_bitmaps_
= !window
;
895 plugin_
->SetWindow(window
);
898 void WebPluginDelegateProxy::WillDestroyWindow() {
900 plugin_
->WillDestroyWindow(window_
);
901 #if defined(OS_MACOSX)
903 // This is actually a "fake" window handle only for the GPU
904 // plugin. Deallocate it on the browser side.
906 render_view_
->DestroyFakePluginWindowHandle(window_
);
909 window_
= gfx::kNullPluginWindow
;
913 void WebPluginDelegateProxy::OnSetWindowlessData(
914 HANDLE modal_loop_pump_messages_event
,
915 gfx::NativeViewId dummy_activation_window
) {
916 DCHECK(modal_loop_pump_messages_event_
== NULL
);
917 DCHECK(dummy_activation_window_
== NULL
);
919 dummy_activation_window_
= dummy_activation_window
;
920 render_view_
->Send(new ViewHostMsg_WindowlessPluginDummyWindowCreated(
921 render_view_
->routing_id(), dummy_activation_window_
));
923 // Bug 25583: this can be null because some "virus scanners" block the
924 // DuplicateHandle call in the plugin process.
925 if (!modal_loop_pump_messages_event
)
928 modal_loop_pump_messages_event_
.reset(
929 new base::WaitableEvent(modal_loop_pump_messages_event
));
932 void WebPluginDelegateProxy::OnNotifyIMEStatus(int input_type
,
933 const gfx::Rect
& caret_rect
) {
937 ViewHostMsg_TextInputState_Params params
;
938 params
.type
= static_cast<ui::TextInputType
>(input_type
);
939 params
.can_compose_inline
= true;
940 render_view_
->Send(new ViewHostMsg_TextInputStateChanged(
941 render_view_
->routing_id(),
944 render_view_
->Send(new ViewHostMsg_SelectionBoundsChanged(
945 render_view_
->routing_id(),
946 caret_rect
, WebKit::WebTextDirectionLeftToRight
,
947 caret_rect
, WebKit::WebTextDirectionLeftToRight
));
951 void WebPluginDelegateProxy::OnCancelResource(int id
) {
953 plugin_
->CancelResource(id
);
956 void WebPluginDelegateProxy::OnInvalidateRect(const gfx::Rect
& rect
) {
960 // Clip the invalidation rect to the plugin bounds; the plugin may have been
961 // resized since the invalidate message was sent.
962 gfx::Rect clipped_rect
=
963 gfx::IntersectRects(rect
, gfx::Rect(plugin_rect_
.size()));
965 invalidate_pending_
= true;
966 // The plugin is blocked on the renderer because the invalidate message it has
967 // sent us is synchronous, so we can use buffer flipping here if the caller
969 UpdateFrontBuffer(clipped_rect
, true);
970 plugin_
->InvalidateRect(clipped_rect
);
973 void WebPluginDelegateProxy::OnGetWindowScriptNPObject(
974 int route_id
, bool* success
) {
976 NPObject
* npobject
= NULL
;
978 npobject
= plugin_
->GetWindowScriptNPObject();
983 // The stub will delete itself when the proxy tells it that it's released, or
984 // otherwise when the channel is closed.
985 window_script_object_
= (new NPObjectStub(
986 npobject
, channel_host_
.get(), route_id
, 0, page_url_
))->AsWeakPtr();
990 void WebPluginDelegateProxy::OnResolveProxy(const GURL
& url
,
992 std::string
* proxy_list
) {
994 RenderThreadImpl::current()->Send(
995 new ViewHostMsg_ResolveProxy(url
, result
, proxy_list
));
998 void WebPluginDelegateProxy::OnGetPluginElement(int route_id
, bool* success
) {
1000 NPObject
* npobject
= NULL
;
1002 npobject
= plugin_
->GetPluginElement();
1006 // The stub will delete itself when the proxy tells it that it's released, or
1007 // otherwise when the channel is closed.
1009 npobject
, channel_host_
.get(), route_id
, 0, page_url_
);
1013 void WebPluginDelegateProxy::OnSetCookie(const GURL
& url
,
1014 const GURL
& first_party_for_cookies
,
1015 const std::string
& cookie
) {
1017 plugin_
->SetCookie(url
, first_party_for_cookies
, cookie
);
1020 void WebPluginDelegateProxy::OnGetCookies(const GURL
& url
,
1021 const GURL
& first_party_for_cookies
,
1022 std::string
* cookies
) {
1025 *cookies
= plugin_
->GetCookies(url
, first_party_for_cookies
);
1028 void WebPluginDelegateProxy::PaintSadPlugin(WebKit::WebCanvas
* native_context
,
1029 const gfx::Rect
& rect
) {
1030 // Lazily load the sad plugin image.
1032 sad_plugin_
= GetContentClient()->renderer()->GetSadPluginBitmap();
1034 webkit::PaintSadPlugin(native_context
, plugin_rect_
, *sad_plugin_
);
1037 void WebPluginDelegateProxy::CopyFromBackBufferToFrontBuffer(
1038 const gfx::Rect
& rect
) {
1039 #if defined(OS_MACOSX)
1040 // Blitting the bits directly is much faster than going through CG, and since
1041 // the goal is just to move the raw pixels between two bitmaps with the same
1042 // pixel format (no compositing, color correction, etc.), it's safe.
1043 const size_t stride
=
1044 skia::PlatformCanvasStrideForWidth(plugin_rect_
.width());
1045 const size_t chunk_size
= 4 * rect
.width();
1046 DCHECK(back_buffer_dib() != NULL
);
1047 uint8
* source_data
= static_cast<uint8
*>(back_buffer_dib()->memory()) +
1048 rect
.y() * stride
+ 4 * rect
.x();
1049 DCHECK(front_buffer_dib() != NULL
);
1050 uint8
* target_data
= static_cast<uint8
*>(front_buffer_dib()->memory()) +
1051 rect
.y() * stride
+ 4 * rect
.x();
1052 for (int row
= 0; row
< rect
.height(); ++row
) {
1053 memcpy(target_data
, source_data
, chunk_size
);
1054 source_data
+= stride
;
1055 target_data
+= stride
;
1058 BlitCanvasToCanvas(front_buffer_canvas(),
1060 back_buffer_canvas(),
1065 void WebPluginDelegateProxy::UpdateFrontBuffer(
1066 const gfx::Rect
& rect
,
1067 bool allow_buffer_flipping
) {
1068 if (!front_buffer_canvas()) {
1073 // If SendUpdateGeometry() would block on the plugin process then we don't
1074 // want to use buffer flipping at all since it would add extra locking.
1075 // (Alternatively we could probably safely use async updates for buffer
1076 // flipping all the time since the size is not changing.)
1077 if (UseSynchronousGeometryUpdates()) {
1078 allow_buffer_flipping
= false;
1082 // Plugin has just painted "rect" into the back-buffer, so the front-buffer
1083 // no longer holds the latest content for that rectangle.
1084 front_buffer_diff_
.Subtract(rect
);
1085 if (allow_buffer_flipping
&& front_buffer_diff_
.IsEmpty()) {
1086 // Back-buffer contains the latest content for all areas; simply flip
1088 front_buffer_index_
= back_buffer_index();
1089 SendUpdateGeometry(false);
1090 // The front-buffer now holds newer content for this region than the
1092 front_buffer_diff_
= rect
;
1094 // Back-buffer contains the latest content for "rect" but the front-buffer
1095 // contains the latest content for some other areas (or buffer flipping not
1096 // allowed); fall back to copying the data.
1097 CopyFromBackBufferToFrontBuffer(rect
);
1099 transport_store_painted_
.Union(rect
);
1102 void WebPluginDelegateProxy::OnHandleURLRequest(
1103 const PluginHostMsg_URLRequest_Params
& params
) {
1104 const char* data
= NULL
;
1105 if (params
.buffer
.size())
1106 data
= ¶ms
.buffer
[0];
1108 const char* target
= NULL
;
1109 if (params
.target
.length())
1110 target
= params
.target
.c_str();
1112 plugin_
->HandleURLRequest(
1113 params
.url
.c_str(), params
.method
.c_str(), target
, data
,
1114 static_cast<unsigned int>(params
.buffer
.size()), params
.notify_id
,
1115 params
.popups_allowed
, params
.notify_redirects
);
1118 webkit::npapi::WebPluginResourceClient
*
1119 WebPluginDelegateProxy::CreateResourceClient(
1120 unsigned long resource_id
, const GURL
& url
, int notify_id
) {
1124 ResourceClientProxy
* proxy
= new ResourceClientProxy(channel_host_
,
1126 proxy
->Initialize(resource_id
, url
, notify_id
);
1130 webkit::npapi::WebPluginResourceClient
*
1131 WebPluginDelegateProxy::CreateSeekableResourceClient(
1132 unsigned long resource_id
, int range_request_id
) {
1136 ResourceClientProxy
* proxy
= new ResourceClientProxy(channel_host_
,
1138 proxy
->InitializeForSeekableStream(resource_id
, range_request_id
);
1142 #if defined(OS_MACOSX)
1143 void WebPluginDelegateProxy::OnFocusChanged(bool focused
) {
1145 render_view_
->PluginFocusChanged(focused
, instance_id_
);
1148 void WebPluginDelegateProxy::OnStartIme() {
1150 render_view_
->StartPluginIme();
1153 void WebPluginDelegateProxy::OnBindFakePluginWindowHandle(bool opaque
) {
1154 BindFakePluginWindowHandle(opaque
);
1157 // Synthesize a fake window handle for the plug-in to identify the instance
1158 // to the browser, allowing mapping to a surface for hardware acceleration
1159 // of plug-in content. The browser generates the handle which is then set on
1160 // the plug-in. Returns true if it successfully sets the window handle on the
1162 bool WebPluginDelegateProxy::BindFakePluginWindowHandle(bool opaque
) {
1163 gfx::PluginWindowHandle fake_window
= gfx::kNullPluginWindow
;
1165 fake_window
= render_view_
->AllocateFakePluginWindowHandle(opaque
, false);
1166 // If we aren't running on 10.6, this allocation will fail.
1169 OnSetWindow(fake_window
);
1170 if (!Send(new PluginMsg_SetFakeAcceleratedSurfaceWindowHandle(instance_id_
,
1175 // Since this isn't a real window, it doesn't get initial size and location
1176 // information the way a real windowed plugin would, so we need to feed it its
1177 // starting geometry.
1178 webkit::npapi::WebPluginGeometry geom
;
1179 geom
.window
= fake_window
;
1180 geom
.window_rect
= plugin_rect_
;
1181 geom
.clip_rect
= clip_rect_
;
1182 geom
.rects_valid
= true;
1183 geom
.visible
= true;
1184 render_view_
->DidMovePlugin(geom
);
1185 // Invalidate the plugin region to ensure that the move event actually gets
1186 // dispatched (for a plugin on an otherwise static page).
1187 render_view_
->didInvalidateRect(WebKit::WebRect(plugin_rect_
.x(),
1189 plugin_rect_
.width(),
1190 plugin_rect_
.height()));
1196 gfx::PluginWindowHandle
WebPluginDelegateProxy::GetPluginWindowHandle() {
1200 void WebPluginDelegateProxy::OnCancelDocumentLoad() {
1201 plugin_
->CancelDocumentLoad();
1204 void WebPluginDelegateProxy::OnInitiateHTTPRangeRequest(
1205 const std::string
& url
,
1206 const std::string
& range_info
,
1207 int range_request_id
) {
1208 plugin_
->InitiateHTTPRangeRequest(
1209 url
.c_str(), range_info
.c_str(), range_request_id
);
1212 void WebPluginDelegateProxy::OnDeferResourceLoading(unsigned long resource_id
,
1214 plugin_
->SetDeferResourceLoading(resource_id
, defer
);
1217 #if defined(OS_MACOSX)
1218 void WebPluginDelegateProxy::OnAcceleratedSurfaceSetIOSurface(
1219 gfx::PluginWindowHandle window
,
1222 uint64 io_surface_identifier
) {
1224 render_view_
->AcceleratedSurfaceSetIOSurface(window
, width
, height
,
1225 io_surface_identifier
);
1228 void WebPluginDelegateProxy::OnAcceleratedSurfaceSetTransportDIB(
1229 gfx::PluginWindowHandle window
,
1232 TransportDIB::Handle transport_dib
) {
1234 render_view_
->AcceleratedSurfaceSetTransportDIB(window
, width
, height
,
1238 void WebPluginDelegateProxy::OnAcceleratedSurfaceAllocTransportDIB(
1240 TransportDIB::Handle
* dib_handle
) {
1242 *dib_handle
= render_view_
->AcceleratedSurfaceAllocTransportDIB(size
);
1244 *dib_handle
= TransportDIB::DefaultHandleValue();
1247 void WebPluginDelegateProxy::OnAcceleratedSurfaceFreeTransportDIB(
1248 TransportDIB::Id dib_id
) {
1250 render_view_
->AcceleratedSurfaceFreeTransportDIB(dib_id
);
1253 void WebPluginDelegateProxy::OnAcceleratedSurfaceBuffersSwapped(
1254 gfx::PluginWindowHandle window
, uint64 surface_handle
) {
1256 render_view_
->AcceleratedSurfaceBuffersSwapped(window
, surface_handle
);
1259 void WebPluginDelegateProxy::OnAcceleratedPluginEnabledRendering() {
1260 uses_compositor_
= true;
1261 OnSetWindow(gfx::kNullPluginWindow
);
1264 void WebPluginDelegateProxy::OnAcceleratedPluginAllocatedIOSurface(
1267 uint32 surface_id
) {
1269 plugin_
->AcceleratedPluginAllocatedIOSurface(width
, height
, surface_id
);
1272 void WebPluginDelegateProxy::OnAcceleratedPluginSwappedIOSurface() {
1274 plugin_
->AcceleratedPluginSwappedIOSurface();
1279 bool WebPluginDelegateProxy::UseSynchronousGeometryUpdates() {
1280 // Need to update geometry synchronously with WMP, otherwise if a site
1281 // scripts the plugin to start playing while it's in the middle of handling
1282 // an update geometry message, videos don't play. See urls in bug 20260.
1283 if (info_
.name
.find(ASCIIToUTF16("Windows Media Player")) != string16::npos
)
1286 // The move networks plugin needs to be informed of geometry updates
1288 std::vector
<webkit::WebPluginMimeType
>::iterator index
;
1289 for (index
= info_
.mime_types
.begin(); index
!= info_
.mime_types
.end();
1291 if (index
->mime_type
== "application/x-vnd.moveplayer.qm" ||
1292 index
->mime_type
== "application/x-vnd.moveplay2.qm" ||
1293 index
->mime_type
== "application/x-vnd.movenetworks.qm" ||
1294 index
->mime_type
== "application/x-vnd.mnplayer.qm") {
1302 void WebPluginDelegateProxy::OnURLRedirectResponse(bool allow
,
1307 plugin_
->URLRedirectResponse(allow
, resource_id
);
1310 } // namespace content