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/npapi/webplugin_delegate_proxy.h"
9 #include "base/auto_reset.h"
10 #include "base/basictypes.h"
11 #include "base/command_line.h"
12 #include "base/file_util.h"
13 #include "base/logging.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/process/process.h"
17 #include "base/strings/string_split.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/version.h"
21 #include "content/child/child_process.h"
22 #include "content/child/npapi/npobject_proxy.h"
23 #include "content/child/npapi/npobject_stub.h"
24 #include "content/child/npapi/npobject_util.h"
25 #include "content/child/npapi/webplugin_resource_client.h"
26 #include "content/child/plugin_messages.h"
27 #include "content/common/content_constants_internal.h"
28 #include "content/common/cursors/webcursor.h"
29 #include "content/common/frame_messages.h"
30 #include "content/common/view_messages.h"
31 #include "content/public/renderer/content_renderer_client.h"
32 #include "content/renderer/npapi/plugin_channel_host.h"
33 #include "content/renderer/npapi/webplugin_impl.h"
34 #include "content/renderer/render_thread_impl.h"
35 #include "content/renderer/render_view_impl.h"
36 #include "content/renderer/sad_plugin.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/public/platform/WebDragData.h"
41 #include "third_party/WebKit/public/platform/WebString.h"
42 #include "third_party/WebKit/public/web/WebBindings.h"
43 #include "third_party/WebKit/public/web/WebDocument.h"
44 #include "third_party/WebKit/public/web/WebFrame.h"
45 #include "third_party/WebKit/public/web/WebView.h"
46 #include "ui/gfx/blit.h"
47 #include "ui/gfx/canvas.h"
48 #include "ui/gfx/native_widget_types.h"
49 #include "ui/gfx/size.h"
50 #include "ui/gfx/skia_util.h"
53 #include "ipc/ipc_channel_posix.h"
56 #if defined(OS_MACOSX)
57 #include "base/mac/mac_util.h"
61 #include "content/public/common/sandbox_init.h"
64 using blink::WebBindings
;
65 using blink::WebCursorInfo
;
66 using blink::WebDragData
;
67 using blink::WebInputEvent
;
68 using blink::WebString
;
75 class ScopedLogLevel
{
77 explicit ScopedLogLevel(int level
);
83 DISALLOW_COPY_AND_ASSIGN(ScopedLogLevel
);
86 ScopedLogLevel::ScopedLogLevel(int level
)
87 : old_level_(logging::GetMinLogLevel()) {
88 logging::SetMinLogLevel(level
);
91 ScopedLogLevel::~ScopedLogLevel() {
92 logging::SetMinLogLevel(old_level_
);
95 // Proxy for WebPluginResourceClient. The object owns itself after creation,
96 // deleting itself after its callback has been called.
97 class ResourceClientProxy
: public WebPluginResourceClient
{
99 ResourceClientProxy(PluginChannelHost
* channel
, int instance_id
)
100 : channel_(channel
), instance_id_(instance_id
), resource_id_(0),
101 multibyte_response_expected_(false) {
104 virtual ~ResourceClientProxy() {
107 void Initialize(unsigned long resource_id
, const GURL
& url
, int notify_id
) {
108 resource_id_
= resource_id
;
109 channel_
->Send(new PluginMsg_HandleURLRequestReply(
110 instance_id_
, resource_id
, url
, notify_id
));
113 void InitializeForSeekableStream(unsigned long resource_id
,
114 int range_request_id
) {
115 resource_id_
= resource_id
;
116 multibyte_response_expected_
= true;
117 channel_
->Send(new PluginMsg_HTTPRangeRequestReply(
118 instance_id_
, resource_id
, range_request_id
));
121 // PluginResourceClient implementation:
122 virtual void WillSendRequest(const GURL
& url
, int http_status_code
) OVERRIDE
{
123 DCHECK(channel_
.get() != NULL
);
124 channel_
->Send(new PluginMsg_WillSendRequest(
125 instance_id_
, resource_id_
, url
, http_status_code
));
128 virtual void DidReceiveResponse(const std::string
& mime_type
,
129 const std::string
& headers
,
130 uint32 expected_length
,
131 uint32 last_modified
,
132 bool request_is_seekable
) OVERRIDE
{
133 DCHECK(channel_
.get() != NULL
);
134 PluginMsg_DidReceiveResponseParams params
;
135 params
.id
= resource_id_
;
136 params
.mime_type
= mime_type
;
137 params
.headers
= headers
;
138 params
.expected_length
= expected_length
;
139 params
.last_modified
= last_modified
;
140 params
.request_is_seekable
= request_is_seekable
;
141 // Grab a reference on the underlying channel so it does not get
142 // deleted from under us.
143 scoped_refptr
<PluginChannelHost
> channel_ref(channel_
);
144 channel_
->Send(new PluginMsg_DidReceiveResponse(instance_id_
, params
));
147 virtual void DidReceiveData(const char* buffer
,
149 int data_offset
) OVERRIDE
{
150 DCHECK(channel_
.get() != NULL
);
151 DCHECK_GT(length
, 0);
152 std::vector
<char> data
;
153 data
.resize(static_cast<size_t>(length
));
154 memcpy(&data
.front(), buffer
, length
);
155 // Grab a reference on the underlying channel so it does not get
156 // deleted from under us.
157 scoped_refptr
<PluginChannelHost
> channel_ref(channel_
);
158 channel_
->Send(new PluginMsg_DidReceiveData(instance_id_
, resource_id_
,
162 virtual void DidFinishLoading(unsigned long resource_id
) OVERRIDE
{
163 DCHECK(channel_
.get() != NULL
);
164 DCHECK_EQ(resource_id
, resource_id_
);
165 channel_
->Send(new PluginMsg_DidFinishLoading(instance_id_
, resource_id_
));
167 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
170 virtual void DidFail(unsigned long resource_id
) OVERRIDE
{
171 DCHECK(channel_
.get() != NULL
);
172 DCHECK_EQ(resource_id
, resource_id_
);
173 channel_
->Send(new PluginMsg_DidFail(instance_id_
, resource_id_
));
175 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
178 virtual bool IsMultiByteResponseExpected() OVERRIDE
{
179 return multibyte_response_expected_
;
182 virtual int ResourceId() OVERRIDE
{
187 scoped_refptr
<PluginChannelHost
> channel_
;
189 unsigned long resource_id_
;
190 // Set to true if the response expected is a multibyte response.
191 // For e.g. response for a HTTP byte range request.
192 bool multibyte_response_expected_
;
197 WebPluginDelegateProxy::WebPluginDelegateProxy(
198 WebPluginImpl
* plugin
,
199 const std::string
& mime_type
,
200 const base::WeakPtr
<RenderViewImpl
>& render_view
,
201 RenderFrameImpl
* render_frame
)
202 : render_view_(render_view
),
203 render_frame_(render_frame
),
205 uses_shared_bitmaps_(false),
206 #if defined(OS_MACOSX)
207 uses_compositor_(false),
208 #elif defined(OS_WIN)
209 dummy_activation_window_(NULL
),
211 window_(gfx::kNullPluginWindow
),
212 mime_type_(mime_type
),
213 instance_id_(MSG_ROUTING_NONE
),
217 invalidate_pending_(false),
219 front_buffer_index_(0),
220 page_url_(render_view_
->webview()->mainFrame()->document().url()) {
223 WebPluginDelegateProxy::~WebPluginDelegateProxy() {
225 WebBindings::releaseObject(npobject_
);
228 WebPluginDelegateProxy::SharedBitmap::SharedBitmap() {}
230 WebPluginDelegateProxy::SharedBitmap::~SharedBitmap() {}
232 void WebPluginDelegateProxy::PluginDestroyed() {
233 #if defined(OS_MACOSX) || defined(OS_WIN)
234 // Ensure that the renderer doesn't think the plugin still has focus.
236 render_view_
->PluginFocusChanged(false, instance_id_
);
240 if (dummy_activation_window_
&& render_view_
) {
241 render_view_
->Send(new ViewHostMsg_WindowlessPluginDummyWindowDestroyed(
242 render_view_
->routing_id(), dummy_activation_window_
));
244 dummy_activation_window_
= NULL
;
250 if (render_view_
.get())
251 render_view_
->UnregisterPluginDelegate(this);
253 if (channel_host_
.get()) {
254 Send(new PluginMsg_DestroyInstance(instance_id_
));
256 // Must remove the route after sending the destroy message, rather than
257 // before, since RemoveRoute can lead to all the outstanding NPObjects
258 // being told the channel went away if this was the last instance.
259 channel_host_
->RemoveRoute(instance_id_
);
261 // Remove the mapping between our instance-Id and NPP identifiers, used by
262 // the channel to track object ownership, before releasing it.
263 channel_host_
->RemoveMappingForNPObjectOwner(instance_id_
);
265 // Release the channel host now. If we are is the last reference to the
266 // channel, this avoids a race where this renderer asks a new connection to
267 // the same plugin between now and the time 'this' is actually deleted.
268 // Destroying the channel host is what releases the channel name -> FD
269 // association on POSIX, and if we ask for a new connection before it is
270 // released, the plugin will give us a new FD, and we'll assert when trying
271 // to associate it with the channel name.
272 channel_host_
= NULL
;
277 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
280 bool WebPluginDelegateProxy::Initialize(
282 const std::vector
<std::string
>& arg_names
,
283 const std::vector
<std::string
>& arg_values
,
284 bool load_manually
) {
285 // TODO(shess): Attempt to work around http://crbug.com/97285 and
286 // http://crbug.com/141055 by retrying the connection. Reports seem
287 // to indicate that the plugin hasn't crashed, and that the problem
288 // is not 100% persistent.
289 const size_t kAttempts
= 2;
292 scoped_refptr
<PluginChannelHost
> channel_host
;
295 for (size_t attempt
= 0; !result
&& attempt
< kAttempts
; attempt
++) {
296 #if defined(OS_MACOSX)
297 // TODO(shess): Debugging for http://crbug.com/97285 . See comment
298 // in plugin_channel_host.cc.
299 scoped_ptr
<base::AutoReset
<bool> > track_nested_removes(
300 new base::AutoReset
<bool>(PluginChannelHost::GetRemoveTrackingFlag(),
304 IPC::ChannelHandle channel_handle
;
305 if (!RenderThreadImpl::current()->Send(new FrameHostMsg_OpenChannelToPlugin(
306 render_frame_
->GetRoutingID(), url
, page_url_
, mime_type_
,
307 &channel_handle
, &info_
))) {
311 if (channel_handle
.name
.empty()) {
312 // We got an invalid handle. Either the plugin couldn't be found (which
313 // shouldn't happen, since if we got here the plugin should exist) or the
314 // plugin crashed on initialization.
315 if (!info_
.path
.empty()) {
316 render_view_
->main_render_frame()->PluginCrashed(
317 info_
.path
, base::kNullProcessId
);
318 LOG(ERROR
) << "Plug-in crashed on start";
320 // Return true so that the plugin widget is created and we can paint the
321 // crashed plugin there.
324 LOG(ERROR
) << "Plug-in couldn't be found";
329 PluginChannelHost::GetPluginChannelHost(
330 channel_handle
, ChildProcess::current()->io_message_loop_proxy());
331 if (!channel_host
.get()) {
332 LOG(ERROR
) << "Couldn't get PluginChannelHost";
335 #if defined(OS_MACOSX)
336 track_nested_removes
.reset();
340 // TODO(bauerb): Debugging for http://crbug.com/141055.
341 ScopedLogLevel
log_level(-2); // Equivalent to --v=2
342 result
= channel_host
->Send(new PluginMsg_CreateInstance(
343 mime_type_
, &instance_id
));
345 LOG(ERROR
) << "Couldn't send PluginMsg_CreateInstance";
351 // Failed too often, give up.
355 channel_host_
= channel_host
;
356 instance_id_
= instance_id
;
358 channel_host_
->AddRoute(instance_id_
, this, NULL
);
360 // Inform the channel of the mapping between our instance-Id and dummy NPP
361 // identifier, for use in object ownership tracking.
362 channel_host_
->AddMappingForNPObjectOwner(instance_id_
, GetPluginNPP());
364 // Now tell the PluginInstance in the plugin process to initialize.
365 PluginMsg_Init_Params params
;
367 params
.page_url
= page_url_
;
368 params
.arg_names
= arg_names
;
369 params
.arg_values
= arg_values
;
370 params
.host_render_view_routing_id
= render_view_
->routing_id();
371 params
.load_manually
= load_manually
;
374 Send(new PluginMsg_Init(instance_id_
, params
, &transparent_
, &result
));
377 LOG(WARNING
) << "PluginMsg_Init returned false";
379 render_view_
->RegisterPluginDelegate(this);
384 bool WebPluginDelegateProxy::Send(IPC::Message
* msg
) {
385 if (!channel_host_
.get()) {
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
)
438 IPC_MESSAGE_HANDLER(PluginHostMsg_CancelResource
, OnCancelResource
)
439 IPC_MESSAGE_HANDLER(PluginHostMsg_InvalidateRect
, OnInvalidateRect
)
440 IPC_MESSAGE_HANDLER(PluginHostMsg_GetWindowScriptNPObject
,
441 OnGetWindowScriptNPObject
)
442 IPC_MESSAGE_HANDLER(PluginHostMsg_GetPluginElement
, OnGetPluginElement
)
443 IPC_MESSAGE_HANDLER(PluginHostMsg_ResolveProxy
, OnResolveProxy
)
444 IPC_MESSAGE_HANDLER(PluginHostMsg_SetCookie
, OnSetCookie
)
445 IPC_MESSAGE_HANDLER(PluginHostMsg_GetCookies
, OnGetCookies
)
446 IPC_MESSAGE_HANDLER(PluginHostMsg_URLRequest
, OnHandleURLRequest
)
447 IPC_MESSAGE_HANDLER(PluginHostMsg_CancelDocumentLoad
, OnCancelDocumentLoad
)
448 IPC_MESSAGE_HANDLER(PluginHostMsg_InitiateHTTPRangeRequest
,
449 OnInitiateHTTPRangeRequest
)
450 IPC_MESSAGE_HANDLER(PluginHostMsg_DidStartLoading
, OnDidStartLoading
)
451 IPC_MESSAGE_HANDLER(PluginHostMsg_DidStopLoading
, OnDidStopLoading
)
452 IPC_MESSAGE_HANDLER(PluginHostMsg_DeferResourceLoading
,
453 OnDeferResourceLoading
)
454 IPC_MESSAGE_HANDLER(PluginHostMsg_URLRedirectResponse
,
455 OnURLRedirectResponse
)
456 IPC_MESSAGE_HANDLER(PluginHostMsg_CheckIfRunInsecureContent
,
457 OnCheckIfRunInsecureContent
)
459 IPC_MESSAGE_HANDLER(PluginHostMsg_SetWindowlessData
, OnSetWindowlessData
)
460 IPC_MESSAGE_HANDLER(PluginHostMsg_NotifyIMEStatus
, OnNotifyIMEStatus
)
462 #if defined(OS_MACOSX)
463 IPC_MESSAGE_HANDLER(PluginHostMsg_FocusChanged
,
465 IPC_MESSAGE_HANDLER(PluginHostMsg_StartIme
,
467 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginEnabledRendering
,
468 OnAcceleratedPluginEnabledRendering
)
469 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginAllocatedIOSurface
,
470 OnAcceleratedPluginAllocatedIOSurface
)
471 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginSwappedIOSurface
,
472 OnAcceleratedPluginSwappedIOSurface
)
474 IPC_MESSAGE_UNHANDLED(handled
= false)
475 IPC_END_MESSAGE_MAP()
480 void WebPluginDelegateProxy::OnChannelError() {
483 // The actual WebPluginDelegate never got a chance to tell the WebPlugin
484 // its window was going away. Do it on its behalf.
487 plugin_
->Invalidate();
489 if (channel_host_
.get() && !channel_host_
->expecting_shutdown()) {
490 render_view_
->main_render_frame()->PluginCrashed(
491 info_
.path
, channel_host_
->peer_pid());
494 #if defined(OS_MACOSX) || defined(OS_WIN)
495 // Ensure that the renderer doesn't think the plugin still has focus.
497 render_view_
->PluginFocusChanged(false, instance_id_
);
501 static void CopyTransportDIBHandleForMessage(
502 const TransportDIB::Handle
& handle_in
,
503 TransportDIB::Handle
* handle_out
,
504 base::ProcessId peer_pid
) {
505 #if defined(OS_MACOSX)
506 // On Mac, TransportDIB::Handle is typedef'ed to FileDescriptor, and
507 // FileDescriptor message fields needs to remain valid until the message is
508 // sent or else the sendmsg() call will fail.
509 if ((handle_out
->fd
= HANDLE_EINTR(dup(handle_in
.fd
))) < 0) {
510 PLOG(ERROR
) << "dup()";
513 handle_out
->auto_close
= true;
514 #elif defined(OS_WIN)
515 // On Windows we need to duplicate the handle for the plugin process.
517 BrokerDuplicateHandle(handle_in
, peer_pid
, handle_out
,
518 FILE_MAP_READ
| FILE_MAP_WRITE
, 0);
519 DCHECK(*handle_out
!= NULL
);
521 // Don't need to do anything special for other platforms.
522 *handle_out
= handle_in
;
526 void WebPluginDelegateProxy::SendUpdateGeometry(
527 bool bitmaps_changed
) {
528 if (!channel_host_
.get())
531 PluginMsg_UpdateGeometry_Param param
;
532 param
.window_rect
= plugin_rect_
;
533 param
.clip_rect
= clip_rect_
;
534 param
.windowless_buffer0
= TransportDIB::DefaultHandleValue();
535 param
.windowless_buffer1
= TransportDIB::DefaultHandleValue();
536 param
.windowless_buffer_index
= back_buffer_index();
538 #if defined(OS_POSIX)
539 // If we're using POSIX mmap'd TransportDIBs, sending the handle across
540 // IPC establishes a new mapping rather than just sending a window ID,
541 // so only do so if we've actually changed the shared memory bitmaps.
545 if (transport_stores_
[0].dib
)
546 CopyTransportDIBHandleForMessage(transport_stores_
[0].dib
->handle(),
547 ¶m
.windowless_buffer0
,
548 channel_host_
->peer_pid());
550 if (transport_stores_
[1].dib
)
551 CopyTransportDIBHandleForMessage(transport_stores_
[1].dib
->handle(),
552 ¶m
.windowless_buffer1
,
553 channel_host_
->peer_pid());
558 if (UseSynchronousGeometryUpdates()) {
559 msg
= new PluginMsg_UpdateGeometrySync(instance_id_
, param
);
563 msg
= new PluginMsg_UpdateGeometry(instance_id_
, param
);
564 msg
->set_unblock(true);
570 void WebPluginDelegateProxy::UpdateGeometry(const gfx::Rect
& window_rect
,
571 const gfx::Rect
& clip_rect
) {
572 // window_rect becomes either a window in native windowing system
573 // coords, or a backing buffer. In either case things will go bad
574 // if the rectangle is very large.
575 if (window_rect
.width() < 0 || window_rect
.width() > kMaxPluginSideLength
||
576 window_rect
.height() < 0 || window_rect
.height() > kMaxPluginSideLength
||
577 // We know this won't overflow due to above checks.
578 static_cast<uint32
>(window_rect
.width()) *
579 static_cast<uint32
>(window_rect
.height()) > kMaxPluginSize
) {
583 plugin_rect_
= window_rect
;
584 clip_rect_
= clip_rect
;
586 bool bitmaps_changed
= false;
588 if (uses_shared_bitmaps_
) {
589 if (!front_buffer_canvas() ||
590 (window_rect
.width() != front_buffer_canvas()->getDevice()->width() ||
591 window_rect
.height() != front_buffer_canvas()->getDevice()->height()))
593 bitmaps_changed
= true;
595 // Create a shared memory section that the plugin paints into
597 ResetWindowlessBitmaps();
598 if (!window_rect
.IsEmpty()) {
599 if (!CreateSharedBitmap(&transport_stores_
[0].dib
,
600 &transport_stores_
[0].canvas
) ||
601 !CreateSharedBitmap(&transport_stores_
[1].dib
,
602 &transport_stores_
[1].canvas
)) {
604 ResetWindowlessBitmaps();
611 SendUpdateGeometry(bitmaps_changed
);
614 void WebPluginDelegateProxy::ResetWindowlessBitmaps() {
615 transport_stores_
[0].dib
.reset();
616 transport_stores_
[1].dib
.reset();
618 transport_stores_
[0].canvas
.reset();
619 transport_stores_
[1].canvas
.reset();
620 transport_store_painted_
= gfx::Rect();
621 front_buffer_diff_
= gfx::Rect();
624 static size_t BitmapSizeForPluginRect(const gfx::Rect
& plugin_rect
) {
625 const size_t stride
=
626 skia::PlatformCanvasStrideForWidth(plugin_rect
.width());
627 return stride
* plugin_rect
.height();
631 bool WebPluginDelegateProxy::CreateLocalBitmap(
632 std::vector
<uint8
>* memory
,
633 scoped_ptr
<skia::PlatformCanvas
>* canvas
) {
634 const size_t size
= BitmapSizeForPluginRect(plugin_rect_
);
635 memory
->resize(size
);
636 if (memory
->size() != size
)
638 canvas
->reset(skia::CreatePlatformCanvas(
639 plugin_rect_
.width(), plugin_rect_
.height(), true, &((*memory
)[0]),
640 skia::CRASH_ON_FAILURE
));
645 bool WebPluginDelegateProxy::CreateSharedBitmap(
646 scoped_ptr
<TransportDIB
>* memory
,
647 scoped_ptr
<skia::PlatformCanvas
>* canvas
) {
648 const size_t size
= BitmapSizeForPluginRect(plugin_rect_
);
649 #if defined(OS_POSIX) && !defined(OS_MACOSX)
650 memory
->reset(TransportDIB::Create(size
, 0));
654 #if defined(OS_POSIX) && !defined(OS_ANDROID)
655 TransportDIB::Handle handle
;
656 IPC::Message
* msg
= new ViewHostMsg_AllocTransportDIB(size
, false, &handle
);
657 if (!RenderThreadImpl::current()->Send(msg
))
661 memory
->reset(TransportDIB::Map(handle
));
663 static uint32 sequence_number
= 0;
664 memory
->reset(TransportDIB::Create(size
, sequence_number
++));
666 canvas
->reset((*memory
)->GetPlatformCanvas(plugin_rect_
.width(),
667 plugin_rect_
.height()));
668 return !!canvas
->get();
671 void WebPluginDelegateProxy::Paint(SkCanvas
* canvas
,
672 const gfx::Rect
& damaged_rect
) {
673 // Limit the damaged rectangle to whatever is contained inside the plugin
674 // rectangle, as that's the rectangle that we'll actually draw.
675 gfx::Rect rect
= gfx::IntersectRects(damaged_rect
, plugin_rect_
);
677 // If the plugin is no longer connected (channel crashed) draw a crashed
679 if (!channel_host_
.get() || !channel_host_
->channel_valid()) {
680 // Lazily load the sad plugin image.
682 sad_plugin_
= GetContentClient()->renderer()->GetSadPluginBitmap();
684 PaintSadPlugin(canvas
, plugin_rect_
, *sad_plugin_
);
688 if (!uses_shared_bitmaps_
)
691 // We got a paint before the plugin's coordinates, so there's no buffer to
693 if (!front_buffer_canvas())
696 gfx::Rect offset_rect
= rect
;
697 offset_rect
.Offset(-plugin_rect_
.x(), -plugin_rect_
.y());
699 // transport_store_painted_ is really a bounding box, so in principle this
700 // check could falsely indicate that we don't need to paint offset_rect, but
701 // in practice it works fine.
702 if (!transport_store_painted_
.Contains(offset_rect
)) {
703 Send(new PluginMsg_Paint(instance_id_
, offset_rect
));
704 // Since the plugin is not blocked on the renderer in this context, there is
705 // a chance that it will begin repainting the back-buffer before we complete
706 // capturing the data. Buffer flipping would increase that risk because
707 // geometry update is asynchronous, so we don't want to use buffer flipping
709 UpdateFrontBuffer(offset_rect
, false);
712 const SkBitmap
& bitmap
=
713 front_buffer_canvas()->getDevice()->accessBitmap(false);
715 paint
.setXfermodeMode(
716 transparent_
? SkXfermode::kSrcATop_Mode
: SkXfermode::kSrc_Mode
);
717 SkIRect src_rect
= gfx::RectToSkIRect(offset_rect
);
718 canvas
->drawBitmapRect(bitmap
,
720 gfx::RectToSkRect(rect
),
723 if (invalidate_pending_
) {
724 // Only send the PaintAck message if this paint is in response to an
725 // invalidate from the plugin, since this message acts as an access token
726 // to ensure only one process is using the transport dib at a time.
727 invalidate_pending_
= false;
728 Send(new PluginMsg_DidPaint(instance_id_
));
732 NPObject
* WebPluginDelegateProxy::GetPluginScriptableObject() {
734 return WebBindings::retainObject(npobject_
);
736 if (!channel_host_
.get())
739 int route_id
= MSG_ROUTING_NONE
;
740 Send(new PluginMsg_GetPluginScriptableObject(instance_id_
, &route_id
));
741 if (route_id
== MSG_ROUTING_NONE
)
744 npobject_
= NPObjectProxy::Create(
745 channel_host_
.get(), route_id
, 0, page_url_
, GetPluginNPP());
747 return WebBindings::retainObject(npobject_
);
750 NPP
WebPluginDelegateProxy::GetPluginNPP() {
751 // Return a dummy NPP for WebKit to use to identify this plugin.
755 bool WebPluginDelegateProxy::GetFormValue(base::string16
* value
) {
756 bool success
= false;
757 Send(new PluginMsg_GetFormValue(instance_id_
, value
, &success
));
761 void WebPluginDelegateProxy::DidFinishLoadWithReason(
762 const GURL
& url
, NPReason reason
, int notify_id
) {
763 Send(new PluginMsg_DidFinishLoadWithReason(
764 instance_id_
, url
, reason
, notify_id
));
767 void WebPluginDelegateProxy::SetFocus(bool focused
) {
768 Send(new PluginMsg_SetFocus(instance_id_
, focused
));
771 render_view_
->PluginFocusChanged(focused
, instance_id_
);
775 bool WebPluginDelegateProxy::HandleInputEvent(
776 const WebInputEvent
& event
,
777 WebCursor::CursorInfo
* cursor_info
) {
778 bool handled
= false;
780 // A windowless plugin can enter a modal loop in the context of a
781 // NPP_HandleEvent call, in which case we need to pump messages to
782 // the plugin. We pass of the corresponding event handle to the
783 // plugin process, which is set if the plugin does enter a modal loop.
784 IPC::SyncMessage
* message
= new PluginMsg_HandleInputEvent(
785 instance_id_
, &event
, &handled
, &cursor
);
786 message
->set_pump_messages_event(modal_loop_pump_messages_event_
.get());
791 int WebPluginDelegateProxy::GetProcessId() {
792 return channel_host_
->peer_pid();
795 void WebPluginDelegateProxy::SetContentAreaFocus(bool has_focus
) {
796 IPC::Message
* msg
= new PluginMsg_SetContentAreaFocus(instance_id_
,
798 // Make sure focus events are delivered in the right order relative to
799 // sync messages they might interact with (Paint, HandleEvent, etc.).
800 msg
->set_unblock(true);
805 void WebPluginDelegateProxy::ImeCompositionUpdated(
806 const base::string16
& text
,
807 const std::vector
<int>& clauses
,
808 const std::vector
<int>& target
,
811 // Dispatch the raw IME data if this plug-in is the focused one.
812 if (instance_id_
!= plugin_id
)
815 IPC::Message
* msg
= new PluginMsg_ImeCompositionUpdated(instance_id_
,
816 text
, clauses
, target
, cursor_position
);
817 msg
->set_unblock(true);
821 void WebPluginDelegateProxy::ImeCompositionCompleted(const base::string16
& text
,
823 // Dispatch the IME text if this plug-in is the focused one.
824 if (instance_id_
!= plugin_id
)
827 IPC::Message
* msg
= new PluginMsg_ImeCompositionCompleted(instance_id_
, text
);
828 msg
->set_unblock(true);
833 #if defined(OS_MACOSX)
834 void WebPluginDelegateProxy::SetWindowFocus(bool window_has_focus
) {
835 IPC::Message
* msg
= new PluginMsg_SetWindowFocus(instance_id_
,
837 // Make sure focus events are delivered in the right order relative to
838 // sync messages they might interact with (Paint, HandleEvent, etc.).
839 msg
->set_unblock(true);
843 void WebPluginDelegateProxy::SetContainerVisibility(bool is_visible
) {
846 gfx::Rect window_frame
= render_view_
->rootWindowRect();
847 gfx::Rect view_frame
= render_view_
->windowRect();
848 blink::WebView
* webview
= render_view_
->webview();
849 msg
= new PluginMsg_ContainerShown(instance_id_
, window_frame
, view_frame
,
850 webview
&& webview
->isActive());
852 msg
= new PluginMsg_ContainerHidden(instance_id_
);
854 // Make sure visibility events are delivered in the right order relative to
855 // sync messages they might interact with (Paint, HandleEvent, etc.).
856 msg
->set_unblock(true);
860 void WebPluginDelegateProxy::WindowFrameChanged(gfx::Rect window_frame
,
861 gfx::Rect view_frame
) {
862 IPC::Message
* msg
= new PluginMsg_WindowFrameChanged(instance_id_
,
865 // Make sure frame events are delivered in the right order relative to
866 // sync messages they might interact with (e.g., HandleEvent).
867 msg
->set_unblock(true);
870 void WebPluginDelegateProxy::ImeCompositionCompleted(const base::string16
& text
,
872 // If the message isn't intended for this plugin, there's nothing to do.
873 if (instance_id_
!= plugin_id
)
876 IPC::Message
* msg
= new PluginMsg_ImeCompositionCompleted(instance_id_
,
878 // Order relative to other key events is important.
879 msg
->set_unblock(true);
884 void WebPluginDelegateProxy::OnSetWindow(gfx::PluginWindowHandle window
) {
885 #if defined(OS_MACOSX)
886 uses_shared_bitmaps_
= !window
&& !uses_compositor_
;
888 uses_shared_bitmaps_
= !window
;
892 plugin_
->SetWindow(window
);
895 void WebPluginDelegateProxy::WillDestroyWindow() {
897 plugin_
->WillDestroyWindow(window_
);
898 window_
= gfx::kNullPluginWindow
;
902 void WebPluginDelegateProxy::OnSetWindowlessData(
903 HANDLE modal_loop_pump_messages_event
,
904 gfx::NativeViewId dummy_activation_window
) {
905 DCHECK(modal_loop_pump_messages_event_
== NULL
);
906 DCHECK(dummy_activation_window_
== NULL
);
908 dummy_activation_window_
= dummy_activation_window
;
909 render_view_
->Send(new ViewHostMsg_WindowlessPluginDummyWindowCreated(
910 render_view_
->routing_id(), dummy_activation_window_
));
912 // Bug 25583: this can be null because some "virus scanners" block the
913 // DuplicateHandle call in the plugin process.
914 if (!modal_loop_pump_messages_event
)
917 modal_loop_pump_messages_event_
.reset(
918 new base::WaitableEvent(modal_loop_pump_messages_event
));
921 void WebPluginDelegateProxy::OnNotifyIMEStatus(int input_type
,
922 const gfx::Rect
& caret_rect
) {
926 ViewHostMsg_TextInputState_Params p
;
927 p
.type
= static_cast<ui::TextInputType
>(input_type
);
928 p
.mode
= ui::TEXT_INPUT_MODE_DEFAULT
;
929 p
.can_compose_inline
= true;
931 render_view_
->Send(new ViewHostMsg_TextInputStateChanged(
932 render_view_
->routing_id(), p
));
934 ViewHostMsg_SelectionBounds_Params bounds_params
;
935 bounds_params
.anchor_rect
= bounds_params
.focus_rect
= caret_rect
;
936 bounds_params
.anchor_dir
= bounds_params
.focus_dir
=
937 blink::WebTextDirectionLeftToRight
;
938 bounds_params
.is_anchor_first
= true;
939 render_view_
->Send(new ViewHostMsg_SelectionBoundsChanged(
940 render_view_
->routing_id(),
945 void WebPluginDelegateProxy::OnCancelResource(int id
) {
947 plugin_
->CancelResource(id
);
950 void WebPluginDelegateProxy::OnInvalidateRect(const gfx::Rect
& rect
) {
954 // Clip the invalidation rect to the plugin bounds; the plugin may have been
955 // resized since the invalidate message was sent.
956 gfx::Rect clipped_rect
=
957 gfx::IntersectRects(rect
, gfx::Rect(plugin_rect_
.size()));
959 invalidate_pending_
= true;
960 // The plugin is blocked on the renderer because the invalidate message it has
961 // sent us is synchronous, so we can use buffer flipping here if the caller
963 UpdateFrontBuffer(clipped_rect
, true);
964 plugin_
->InvalidateRect(clipped_rect
);
967 void WebPluginDelegateProxy::OnGetWindowScriptNPObject(
968 int route_id
, bool* success
) {
970 NPObject
* npobject
= NULL
;
972 npobject
= plugin_
->GetWindowScriptNPObject();
977 // The stub will delete itself when the proxy tells it that it's released, or
978 // otherwise when the channel is closed.
979 new NPObjectStub(npobject
, channel_host_
.get(), route_id
, 0, page_url_
);
983 void WebPluginDelegateProxy::OnResolveProxy(const GURL
& url
,
985 std::string
* proxy_list
) {
986 *result
= RenderThreadImpl::current()->ResolveProxy(url
, proxy_list
);
989 void WebPluginDelegateProxy::OnGetPluginElement(int route_id
, bool* success
) {
991 NPObject
* npobject
= NULL
;
993 npobject
= plugin_
->GetPluginElement();
997 // The stub will delete itself when the proxy tells it that it's released, or
998 // otherwise when the channel is closed.
1000 npobject
, channel_host_
.get(), route_id
, 0, page_url_
);
1004 void WebPluginDelegateProxy::OnSetCookie(const GURL
& url
,
1005 const GURL
& first_party_for_cookies
,
1006 const std::string
& cookie
) {
1008 plugin_
->SetCookie(url
, first_party_for_cookies
, cookie
);
1011 void WebPluginDelegateProxy::OnGetCookies(const GURL
& url
,
1012 const GURL
& first_party_for_cookies
,
1013 std::string
* cookies
) {
1016 *cookies
= plugin_
->GetCookies(url
, first_party_for_cookies
);
1019 void WebPluginDelegateProxy::CopyFromBackBufferToFrontBuffer(
1020 const gfx::Rect
& rect
) {
1021 #if defined(OS_MACOSX)
1022 // Blitting the bits directly is much faster than going through CG, and since
1023 // the goal is just to move the raw pixels between two bitmaps with the same
1024 // pixel format (no compositing, color correction, etc.), it's safe.
1025 const size_t stride
=
1026 skia::PlatformCanvasStrideForWidth(plugin_rect_
.width());
1027 const size_t chunk_size
= 4 * rect
.width();
1028 DCHECK(back_buffer_dib() != NULL
);
1029 uint8
* source_data
= static_cast<uint8
*>(back_buffer_dib()->memory()) +
1030 rect
.y() * stride
+ 4 * rect
.x();
1031 DCHECK(front_buffer_dib() != NULL
);
1032 uint8
* target_data
= static_cast<uint8
*>(front_buffer_dib()->memory()) +
1033 rect
.y() * stride
+ 4 * rect
.x();
1034 for (int row
= 0; row
< rect
.height(); ++row
) {
1035 memcpy(target_data
, source_data
, chunk_size
);
1036 source_data
+= stride
;
1037 target_data
+= stride
;
1040 BlitCanvasToCanvas(front_buffer_canvas(),
1042 back_buffer_canvas(),
1047 void WebPluginDelegateProxy::UpdateFrontBuffer(
1048 const gfx::Rect
& rect
,
1049 bool allow_buffer_flipping
) {
1050 if (!front_buffer_canvas()) {
1055 // If SendUpdateGeometry() would block on the plugin process then we don't
1056 // want to use buffer flipping at all since it would add extra locking.
1057 // (Alternatively we could probably safely use async updates for buffer
1058 // flipping all the time since the size is not changing.)
1059 if (UseSynchronousGeometryUpdates()) {
1060 allow_buffer_flipping
= false;
1064 // Plugin has just painted "rect" into the back-buffer, so the front-buffer
1065 // no longer holds the latest content for that rectangle.
1066 front_buffer_diff_
.Subtract(rect
);
1067 if (allow_buffer_flipping
&& front_buffer_diff_
.IsEmpty()) {
1068 // Back-buffer contains the latest content for all areas; simply flip
1070 front_buffer_index_
= back_buffer_index();
1071 SendUpdateGeometry(false);
1072 // The front-buffer now holds newer content for this region than the
1074 front_buffer_diff_
= rect
;
1076 // Back-buffer contains the latest content for "rect" but the front-buffer
1077 // contains the latest content for some other areas (or buffer flipping not
1078 // allowed); fall back to copying the data.
1079 CopyFromBackBufferToFrontBuffer(rect
);
1081 transport_store_painted_
.Union(rect
);
1084 void WebPluginDelegateProxy::OnHandleURLRequest(
1085 const PluginHostMsg_URLRequest_Params
& params
) {
1086 const char* data
= NULL
;
1087 if (params
.buffer
.size())
1088 data
= ¶ms
.buffer
[0];
1090 const char* target
= NULL
;
1091 if (params
.target
.length())
1092 target
= params
.target
.c_str();
1094 plugin_
->HandleURLRequest(
1095 params
.url
.c_str(), params
.method
.c_str(), target
, data
,
1096 static_cast<unsigned int>(params
.buffer
.size()), params
.notify_id
,
1097 params
.popups_allowed
, params
.notify_redirects
);
1100 WebPluginResourceClient
* WebPluginDelegateProxy::CreateResourceClient(
1101 unsigned long resource_id
, const GURL
& url
, int notify_id
) {
1102 if (!channel_host_
.get())
1105 ResourceClientProxy
* proxy
=
1106 new ResourceClientProxy(channel_host_
.get(), instance_id_
);
1107 proxy
->Initialize(resource_id
, url
, notify_id
);
1111 WebPluginResourceClient
* WebPluginDelegateProxy::CreateSeekableResourceClient(
1112 unsigned long resource_id
, int range_request_id
) {
1113 if (!channel_host_
.get())
1116 ResourceClientProxy
* proxy
=
1117 new ResourceClientProxy(channel_host_
.get(), instance_id_
);
1118 proxy
->InitializeForSeekableStream(resource_id
, range_request_id
);
1122 void WebPluginDelegateProxy::FetchURL(unsigned long resource_id
,
1125 const GURL
& first_party_for_cookies
,
1126 const std::string
& method
,
1129 const GURL
& referrer
,
1130 bool notify_redirects
,
1131 bool is_plugin_src_load
,
1133 int render_frame_id
,
1134 int render_view_id
) {
1135 PluginMsg_FetchURL_Params params
;
1136 params
.resource_id
= resource_id
;
1137 params
.notify_id
= notify_id
;
1139 params
.first_party_for_cookies
= first_party_for_cookies
;
1140 params
.method
= method
;
1142 params
.post_data
.resize(len
);
1143 memcpy(¶ms
.post_data
.front(), buf
, len
);
1145 params
.referrer
= referrer
;
1146 params
.notify_redirect
= notify_redirects
;
1147 params
.is_plugin_src_load
= is_plugin_src_load
;
1148 params
.render_frame_id
= render_frame_id
;
1149 Send(new PluginMsg_FetchURL(instance_id_
, params
));
1152 #if defined(OS_MACOSX)
1153 void WebPluginDelegateProxy::OnFocusChanged(bool focused
) {
1155 render_view_
->PluginFocusChanged(focused
, instance_id_
);
1158 void WebPluginDelegateProxy::OnStartIme() {
1160 render_view_
->StartPluginIme();
1164 gfx::PluginWindowHandle
WebPluginDelegateProxy::GetPluginWindowHandle() {
1168 void WebPluginDelegateProxy::OnCancelDocumentLoad() {
1169 plugin_
->CancelDocumentLoad();
1172 void WebPluginDelegateProxy::OnInitiateHTTPRangeRequest(
1173 const std::string
& url
,
1174 const std::string
& range_info
,
1175 int range_request_id
) {
1176 plugin_
->InitiateHTTPRangeRequest(
1177 url
.c_str(), range_info
.c_str(), range_request_id
);
1180 void WebPluginDelegateProxy::OnDidStartLoading() {
1181 plugin_
->DidStartLoading();
1184 void WebPluginDelegateProxy::OnDidStopLoading() {
1185 plugin_
->DidStopLoading();
1188 void WebPluginDelegateProxy::OnDeferResourceLoading(unsigned long resource_id
,
1190 plugin_
->SetDeferResourceLoading(resource_id
, defer
);
1193 #if defined(OS_MACOSX)
1194 void WebPluginDelegateProxy::OnAcceleratedPluginEnabledRendering() {
1195 uses_compositor_
= true;
1196 OnSetWindow(gfx::kNullPluginWindow
);
1199 void WebPluginDelegateProxy::OnAcceleratedPluginAllocatedIOSurface(
1202 uint32 surface_id
) {
1204 plugin_
->AcceleratedPluginAllocatedIOSurface(width
, height
, surface_id
);
1207 void WebPluginDelegateProxy::OnAcceleratedPluginSwappedIOSurface() {
1209 plugin_
->AcceleratedPluginSwappedIOSurface();
1214 bool WebPluginDelegateProxy::UseSynchronousGeometryUpdates() {
1215 // Need to update geometry synchronously with WMP, otherwise if a site
1216 // scripts the plugin to start playing while it's in the middle of handling
1217 // an update geometry message, videos don't play. See urls in bug 20260.
1218 if (info_
.name
.find(base::ASCIIToUTF16("Windows Media Player")) !=
1219 base::string16::npos
)
1222 // The move networks plugin needs to be informed of geometry updates
1224 std::vector
<WebPluginMimeType
>::iterator index
;
1225 for (index
= info_
.mime_types
.begin(); index
!= info_
.mime_types
.end();
1227 if (index
->mime_type
== "application/x-vnd.moveplayer.qm" ||
1228 index
->mime_type
== "application/x-vnd.moveplay2.qm" ||
1229 index
->mime_type
== "application/x-vnd.movenetworks.qm" ||
1230 index
->mime_type
== "application/x-vnd.mnplayer.qm") {
1238 void WebPluginDelegateProxy::OnURLRedirectResponse(bool allow
,
1243 plugin_
->URLRedirectResponse(allow
, resource_id
);
1246 void WebPluginDelegateProxy::OnCheckIfRunInsecureContent(const GURL
& url
,
1248 *result
= plugin_
->CheckIfRunInsecureContent(url
);
1251 } // namespace content