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/files/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 ~ResourceClientProxy() override
{}
106 void Initialize(unsigned long resource_id
, const GURL
& url
, int notify_id
) {
107 resource_id_
= resource_id
;
108 channel_
->Send(new PluginMsg_HandleURLRequestReply(
109 instance_id_
, resource_id
, url
, notify_id
));
112 void InitializeForSeekableStream(unsigned long resource_id
,
113 int range_request_id
) {
114 resource_id_
= resource_id
;
115 multibyte_response_expected_
= true;
116 channel_
->Send(new PluginMsg_HTTPRangeRequestReply(
117 instance_id_
, resource_id
, range_request_id
));
120 // PluginResourceClient implementation:
121 void WillSendRequest(const GURL
& url
, int http_status_code
) override
{
122 DCHECK(channel_
.get() != NULL
);
123 channel_
->Send(new PluginMsg_WillSendRequest(
124 instance_id_
, resource_id_
, url
, http_status_code
));
127 void DidReceiveResponse(const std::string
& mime_type
,
128 const std::string
& headers
,
129 uint32 expected_length
,
130 uint32 last_modified
,
131 bool request_is_seekable
) override
{
132 DCHECK(channel_
.get() != NULL
);
133 PluginMsg_DidReceiveResponseParams params
;
134 params
.id
= resource_id_
;
135 params
.mime_type
= mime_type
;
136 params
.headers
= headers
;
137 params
.expected_length
= expected_length
;
138 params
.last_modified
= last_modified
;
139 params
.request_is_seekable
= request_is_seekable
;
140 // Grab a reference on the underlying channel so it does not get
141 // deleted from under us.
142 scoped_refptr
<PluginChannelHost
> channel_ref(channel_
);
143 channel_
->Send(new PluginMsg_DidReceiveResponse(instance_id_
, params
));
146 void DidReceiveData(const char* buffer
,
148 int data_offset
) override
{
149 DCHECK(channel_
.get() != NULL
);
150 DCHECK_GT(length
, 0);
151 std::vector
<char> data
;
152 data
.resize(static_cast<size_t>(length
));
153 memcpy(&data
.front(), buffer
, length
);
154 // Grab a reference on the underlying channel so it does not get
155 // deleted from under us.
156 scoped_refptr
<PluginChannelHost
> channel_ref(channel_
);
157 channel_
->Send(new PluginMsg_DidReceiveData(instance_id_
, resource_id_
,
161 void DidFinishLoading(unsigned long resource_id
) override
{
162 DCHECK(channel_
.get() != NULL
);
163 DCHECK_EQ(resource_id
, resource_id_
);
164 channel_
->Send(new PluginMsg_DidFinishLoading(instance_id_
, resource_id_
));
166 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
169 void DidFail(unsigned long resource_id
) override
{
170 DCHECK(channel_
.get() != NULL
);
171 DCHECK_EQ(resource_id
, resource_id_
);
172 channel_
->Send(new PluginMsg_DidFail(instance_id_
, resource_id_
));
174 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
177 bool IsMultiByteResponseExpected() override
{
178 return multibyte_response_expected_
;
181 int ResourceId() override
{ return resource_id_
; }
184 scoped_refptr
<PluginChannelHost
> channel_
;
186 unsigned long resource_id_
;
187 // Set to true if the response expected is a multibyte response.
188 // For e.g. response for a HTTP byte range request.
189 bool multibyte_response_expected_
;
194 WebPluginDelegateProxy::WebPluginDelegateProxy(
195 WebPluginImpl
* plugin
,
196 const std::string
& mime_type
,
197 const base::WeakPtr
<RenderViewImpl
>& render_view
,
198 RenderFrameImpl
* render_frame
)
199 : render_view_(render_view
),
200 render_frame_(render_frame
),
202 uses_shared_bitmaps_(false),
203 #if defined(OS_MACOSX)
204 uses_compositor_(false),
205 #elif defined(OS_WIN)
206 dummy_activation_window_(NULL
),
208 window_(gfx::kNullPluginWindow
),
209 mime_type_(mime_type
),
210 instance_id_(MSG_ROUTING_NONE
),
214 invalidate_pending_(false),
216 front_buffer_index_(0),
217 page_url_(render_view_
->webview()->mainFrame()->document().url()) {
220 WebPluginDelegateProxy::~WebPluginDelegateProxy() {
222 WebBindings::releaseObject(npobject_
);
225 WebPluginDelegateProxy::SharedBitmap::SharedBitmap() {}
227 WebPluginDelegateProxy::SharedBitmap::~SharedBitmap() {}
229 void WebPluginDelegateProxy::PluginDestroyed() {
230 #if defined(OS_MACOSX) || defined(OS_WIN)
231 // Ensure that the renderer doesn't think the plugin still has focus.
233 render_view_
->PluginFocusChanged(false, instance_id_
);
237 if (dummy_activation_window_
&& render_view_
) {
238 render_view_
->Send(new ViewHostMsg_WindowlessPluginDummyWindowDestroyed(
239 render_view_
->routing_id(), dummy_activation_window_
));
241 dummy_activation_window_
= NULL
;
247 if (render_view_
.get())
248 render_view_
->UnregisterPluginDelegate(this);
250 if (channel_host_
.get()) {
251 Send(new PluginMsg_DestroyInstance(instance_id_
));
253 // Must remove the route after sending the destroy message, rather than
254 // before, since RemoveRoute can lead to all the outstanding NPObjects
255 // being told the channel went away if this was the last instance.
256 channel_host_
->RemoveRoute(instance_id_
);
258 // Remove the mapping between our instance-Id and NPP identifiers, used by
259 // the channel to track object ownership, before releasing it.
260 channel_host_
->RemoveMappingForNPObjectOwner(instance_id_
);
262 // Release the channel host now. If we are is the last reference to the
263 // channel, this avoids a race where this renderer asks a new connection to
264 // the same plugin between now and the time 'this' is actually deleted.
265 // Destroying the channel host is what releases the channel name -> FD
266 // association on POSIX, and if we ask for a new connection before it is
267 // released, the plugin will give us a new FD, and we'll assert when trying
268 // to associate it with the channel name.
269 channel_host_
= NULL
;
274 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
277 bool WebPluginDelegateProxy::Initialize(
279 const std::vector
<std::string
>& arg_names
,
280 const std::vector
<std::string
>& arg_values
,
281 bool load_manually
) {
282 // TODO(shess): Attempt to work around http://crbug.com/97285 and
283 // http://crbug.com/141055 by retrying the connection. Reports seem
284 // to indicate that the plugin hasn't crashed, and that the problem
285 // is not 100% persistent.
286 const size_t kAttempts
= 2;
289 scoped_refptr
<PluginChannelHost
> channel_host
;
292 for (size_t attempt
= 0; !result
&& attempt
< kAttempts
; attempt
++) {
293 #if defined(OS_MACOSX)
294 // TODO(shess): Debugging for http://crbug.com/97285 . See comment
295 // in plugin_channel_host.cc.
296 scoped_ptr
<base::AutoReset
<bool> > track_nested_removes(
297 new base::AutoReset
<bool>(PluginChannelHost::GetRemoveTrackingFlag(),
301 IPC::ChannelHandle channel_handle
;
302 if (!RenderThreadImpl::current()->Send(new FrameHostMsg_OpenChannelToPlugin(
303 render_frame_
->GetRoutingID(), url
, page_url_
, mime_type_
,
304 &channel_handle
, &info_
))) {
308 if (channel_handle
.name
.empty()) {
309 // We got an invalid handle. Either the plugin couldn't be found (which
310 // shouldn't happen, since if we got here the plugin should exist) or the
311 // plugin crashed on initialization.
312 if (!info_
.path
.empty()) {
313 render_view_
->GetMainRenderFrame()->PluginCrashed(
314 info_
.path
, base::kNullProcessId
);
315 LOG(ERROR
) << "Plug-in crashed on start";
317 // Return true so that the plugin widget is created and we can paint the
318 // crashed plugin there.
321 LOG(ERROR
) << "Plug-in couldn't be found";
326 PluginChannelHost::GetPluginChannelHost(
327 channel_handle
, ChildProcess::current()->io_message_loop_proxy());
328 if (!channel_host
.get()) {
329 LOG(ERROR
) << "Couldn't get PluginChannelHost";
332 #if defined(OS_MACOSX)
333 track_nested_removes
.reset();
337 // TODO(bauerb): Debugging for http://crbug.com/141055.
338 ScopedLogLevel
log_level(-2); // Equivalent to --v=2
339 result
= channel_host
->Send(new PluginMsg_CreateInstance(
340 mime_type_
, &instance_id
));
342 LOG(ERROR
) << "Couldn't send PluginMsg_CreateInstance";
348 // Failed too often, give up.
352 channel_host_
= channel_host
;
353 instance_id_
= instance_id
;
355 channel_host_
->AddRoute(instance_id_
, this, NULL
);
357 // Inform the channel of the mapping between our instance-Id and dummy NPP
358 // identifier, for use in object ownership tracking.
359 channel_host_
->AddMappingForNPObjectOwner(instance_id_
, GetPluginNPP());
361 // Now tell the PluginInstance in the plugin process to initialize.
362 PluginMsg_Init_Params params
;
364 params
.page_url
= page_url_
;
365 params
.arg_names
= arg_names
;
366 params
.arg_values
= arg_values
;
367 params
.host_render_view_routing_id
= render_view_
->routing_id();
368 params
.load_manually
= load_manually
;
371 Send(new PluginMsg_Init(instance_id_
, params
, &transparent_
, &result
));
374 LOG(WARNING
) << "PluginMsg_Init returned false";
376 render_view_
->RegisterPluginDelegate(this);
381 bool WebPluginDelegateProxy::Send(IPC::Message
* msg
) {
382 if (!channel_host_
.get()) {
383 DLOG(WARNING
) << "dropping message because channel host is null";
388 return channel_host_
->Send(msg
);
391 void WebPluginDelegateProxy::SendJavaScriptStream(const GURL
& url
,
392 const std::string
& result
,
395 Send(new PluginMsg_SendJavaScriptStream(
396 instance_id_
, url
, result
, success
, notify_id
));
399 void WebPluginDelegateProxy::DidReceiveManualResponse(
400 const GURL
& url
, const std::string
& mime_type
,
401 const std::string
& headers
, uint32 expected_length
,
402 uint32 last_modified
) {
403 PluginMsg_DidReceiveResponseParams params
;
405 params
.mime_type
= mime_type
;
406 params
.headers
= headers
;
407 params
.expected_length
= expected_length
;
408 params
.last_modified
= last_modified
;
409 Send(new PluginMsg_DidReceiveManualResponse(instance_id_
, url
, params
));
412 void WebPluginDelegateProxy::DidReceiveManualData(const char* buffer
,
414 DCHECK_GT(length
, 0);
415 std::vector
<char> data
;
416 data
.resize(static_cast<size_t>(length
));
417 memcpy(&data
.front(), buffer
, length
);
418 Send(new PluginMsg_DidReceiveManualData(instance_id_
, data
));
421 void WebPluginDelegateProxy::DidFinishManualLoading() {
422 Send(new PluginMsg_DidFinishManualLoading(instance_id_
));
425 void WebPluginDelegateProxy::DidManualLoadFail() {
426 Send(new PluginMsg_DidManualLoadFail(instance_id_
));
429 bool WebPluginDelegateProxy::OnMessageReceived(const IPC::Message
& msg
) {
430 GetContentClient()->SetActiveURL(page_url_
);
433 IPC_BEGIN_MESSAGE_MAP(WebPluginDelegateProxy
, msg
)
434 IPC_MESSAGE_HANDLER(PluginHostMsg_SetWindow
, OnSetWindow
)
435 IPC_MESSAGE_HANDLER(PluginHostMsg_CancelResource
, OnCancelResource
)
436 IPC_MESSAGE_HANDLER(PluginHostMsg_InvalidateRect
, OnInvalidateRect
)
437 IPC_MESSAGE_HANDLER(PluginHostMsg_GetWindowScriptNPObject
,
438 OnGetWindowScriptNPObject
)
439 IPC_MESSAGE_HANDLER(PluginHostMsg_GetPluginElement
, OnGetPluginElement
)
440 IPC_MESSAGE_HANDLER(PluginHostMsg_ResolveProxy
, OnResolveProxy
)
441 IPC_MESSAGE_HANDLER(PluginHostMsg_SetCookie
, OnSetCookie
)
442 IPC_MESSAGE_HANDLER(PluginHostMsg_GetCookies
, OnGetCookies
)
443 IPC_MESSAGE_HANDLER(PluginHostMsg_URLRequest
, OnHandleURLRequest
)
444 IPC_MESSAGE_HANDLER(PluginHostMsg_CancelDocumentLoad
, OnCancelDocumentLoad
)
445 IPC_MESSAGE_HANDLER(PluginHostMsg_InitiateHTTPRangeRequest
,
446 OnInitiateHTTPRangeRequest
)
447 IPC_MESSAGE_HANDLER(PluginHostMsg_DidStartLoading
, OnDidStartLoading
)
448 IPC_MESSAGE_HANDLER(PluginHostMsg_DidStopLoading
, OnDidStopLoading
)
449 IPC_MESSAGE_HANDLER(PluginHostMsg_DeferResourceLoading
,
450 OnDeferResourceLoading
)
451 IPC_MESSAGE_HANDLER(PluginHostMsg_URLRedirectResponse
,
452 OnURLRedirectResponse
)
453 IPC_MESSAGE_HANDLER(PluginHostMsg_CheckIfRunInsecureContent
,
454 OnCheckIfRunInsecureContent
)
456 IPC_MESSAGE_HANDLER(PluginHostMsg_SetWindowlessData
, OnSetWindowlessData
)
457 IPC_MESSAGE_HANDLER(PluginHostMsg_NotifyIMEStatus
, OnNotifyIMEStatus
)
459 #if defined(OS_MACOSX)
460 IPC_MESSAGE_HANDLER(PluginHostMsg_FocusChanged
,
462 IPC_MESSAGE_HANDLER(PluginHostMsg_StartIme
,
464 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginEnabledRendering
,
465 OnAcceleratedPluginEnabledRendering
)
466 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginAllocatedIOSurface
,
467 OnAcceleratedPluginAllocatedIOSurface
)
468 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginSwappedIOSurface
,
469 OnAcceleratedPluginSwappedIOSurface
)
471 IPC_MESSAGE_UNHANDLED(handled
= false)
472 IPC_END_MESSAGE_MAP()
477 void WebPluginDelegateProxy::OnChannelError() {
480 // The actual WebPluginDelegate never got a chance to tell the WebPlugin
481 // its window was going away. Do it on its behalf.
484 plugin_
->Invalidate();
486 if (channel_host_
.get() && !channel_host_
->expecting_shutdown()) {
487 render_view_
->GetMainRenderFrame()->PluginCrashed(
488 info_
.path
, channel_host_
->peer_pid());
491 #if defined(OS_MACOSX) || defined(OS_WIN)
492 // Ensure that the renderer doesn't think the plugin still has focus.
494 render_view_
->PluginFocusChanged(false, instance_id_
);
498 static void CopyTransportDIBHandleForMessage(
499 const TransportDIB::Handle
& handle_in
,
500 TransportDIB::Handle
* handle_out
,
501 base::ProcessId peer_pid
) {
502 #if defined(OS_MACOSX)
503 // On Mac, TransportDIB::Handle is typedef'ed to FileDescriptor, and
504 // FileDescriptor message fields needs to remain valid until the message is
505 // sent or else the sendmsg() call will fail.
506 if ((handle_out
->fd
= HANDLE_EINTR(dup(handle_in
.fd
))) < 0) {
507 PLOG(ERROR
) << "dup()";
510 handle_out
->auto_close
= true;
511 #elif defined(OS_WIN)
512 // On Windows we need to duplicate the handle for the plugin process.
514 BrokerDuplicateHandle(handle_in
, peer_pid
, handle_out
,
515 FILE_MAP_READ
| FILE_MAP_WRITE
, 0);
516 DCHECK(*handle_out
!= NULL
);
518 // Don't need to do anything special for other platforms.
519 *handle_out
= handle_in
;
523 void WebPluginDelegateProxy::SendUpdateGeometry(
524 bool bitmaps_changed
) {
525 if (!channel_host_
.get())
528 PluginMsg_UpdateGeometry_Param param
;
529 param
.window_rect
= plugin_rect_
;
530 param
.clip_rect
= clip_rect_
;
531 param
.windowless_buffer0
= TransportDIB::DefaultHandleValue();
532 param
.windowless_buffer1
= TransportDIB::DefaultHandleValue();
533 param
.windowless_buffer_index
= back_buffer_index();
535 #if defined(OS_POSIX)
536 // If we're using POSIX mmap'd TransportDIBs, sending the handle across
537 // IPC establishes a new mapping rather than just sending a window ID,
538 // so only do so if we've actually changed the shared memory bitmaps.
542 if (transport_stores_
[0].dib
)
543 CopyTransportDIBHandleForMessage(transport_stores_
[0].dib
->handle(),
544 ¶m
.windowless_buffer0
,
545 channel_host_
->peer_pid());
547 if (transport_stores_
[1].dib
)
548 CopyTransportDIBHandleForMessage(transport_stores_
[1].dib
->handle(),
549 ¶m
.windowless_buffer1
,
550 channel_host_
->peer_pid());
555 if (UseSynchronousGeometryUpdates()) {
556 msg
= new PluginMsg_UpdateGeometrySync(instance_id_
, param
);
560 msg
= new PluginMsg_UpdateGeometry(instance_id_
, param
);
561 msg
->set_unblock(true);
567 void WebPluginDelegateProxy::UpdateGeometry(const gfx::Rect
& window_rect
,
568 const gfx::Rect
& clip_rect
) {
569 // window_rect becomes either a window in native windowing system
570 // coords, or a backing buffer. In either case things will go bad
571 // if the rectangle is very large.
572 if (window_rect
.width() < 0 || window_rect
.width() > kMaxPluginSideLength
||
573 window_rect
.height() < 0 || window_rect
.height() > kMaxPluginSideLength
||
574 // We know this won't overflow due to above checks.
575 static_cast<uint32
>(window_rect
.width()) *
576 static_cast<uint32
>(window_rect
.height()) > kMaxPluginSize
) {
580 plugin_rect_
= window_rect
;
581 clip_rect_
= clip_rect
;
583 bool bitmaps_changed
= false;
585 if (uses_shared_bitmaps_
) {
586 if (!front_buffer_canvas() ||
587 (window_rect
.width() != front_buffer_canvas()->getDevice()->width() ||
588 window_rect
.height() != front_buffer_canvas()->getDevice()->height()))
590 bitmaps_changed
= true;
592 // Create a shared memory section that the plugin paints into
594 ResetWindowlessBitmaps();
595 if (!window_rect
.IsEmpty()) {
596 if (!CreateSharedBitmap(&transport_stores_
[0].dib
,
597 &transport_stores_
[0].canvas
) ||
598 !CreateSharedBitmap(&transport_stores_
[1].dib
,
599 &transport_stores_
[1].canvas
)) {
601 ResetWindowlessBitmaps();
608 SendUpdateGeometry(bitmaps_changed
);
611 void WebPluginDelegateProxy::ResetWindowlessBitmaps() {
612 transport_stores_
[0].dib
.reset();
613 transport_stores_
[1].dib
.reset();
615 transport_stores_
[0].canvas
.reset();
616 transport_stores_
[1].canvas
.reset();
617 transport_store_painted_
= gfx::Rect();
618 front_buffer_diff_
= gfx::Rect();
621 static size_t BitmapSizeForPluginRect(const gfx::Rect
& plugin_rect
) {
622 const size_t stride
=
623 skia::PlatformCanvasStrideForWidth(plugin_rect
.width());
624 return stride
* plugin_rect
.height();
628 bool WebPluginDelegateProxy::CreateLocalBitmap(
629 std::vector
<uint8
>* memory
,
630 scoped_ptr
<skia::PlatformCanvas
>* canvas
) {
631 const size_t size
= BitmapSizeForPluginRect(plugin_rect_
);
632 memory
->resize(size
);
633 if (memory
->size() != size
)
635 canvas
->reset(skia::CreatePlatformCanvas(
636 plugin_rect_
.width(), plugin_rect_
.height(), true, &((*memory
)[0]),
637 skia::CRASH_ON_FAILURE
));
642 bool WebPluginDelegateProxy::CreateSharedBitmap(
643 scoped_ptr
<TransportDIB
>* memory
,
644 scoped_ptr
<skia::PlatformCanvas
>* canvas
) {
645 const size_t size
= BitmapSizeForPluginRect(plugin_rect_
);
646 #if defined(OS_POSIX) && !defined(OS_MACOSX)
647 memory
->reset(TransportDIB::Create(size
, 0));
651 #if defined(OS_POSIX) && !defined(OS_ANDROID)
652 TransportDIB::Handle handle
;
653 IPC::Message
* msg
= new ViewHostMsg_AllocTransportDIB(size
, false, &handle
);
654 if (!RenderThreadImpl::current()->Send(msg
))
658 memory
->reset(TransportDIB::Map(handle
));
660 static uint32 sequence_number
= 0;
661 memory
->reset(TransportDIB::Create(size
, sequence_number
++));
663 canvas
->reset((*memory
)->GetPlatformCanvas(plugin_rect_
.width(),
664 plugin_rect_
.height()));
665 return !!canvas
->get();
668 void WebPluginDelegateProxy::Paint(SkCanvas
* canvas
,
669 const gfx::Rect
& damaged_rect
) {
670 // Limit the damaged rectangle to whatever is contained inside the plugin
671 // rectangle, as that's the rectangle that we'll actually draw.
672 gfx::Rect rect
= gfx::IntersectRects(damaged_rect
, plugin_rect_
);
674 // If the plugin is no longer connected (channel crashed) draw a crashed
676 if (!channel_host_
.get() || !channel_host_
->channel_valid()) {
677 // Lazily load the sad plugin image.
679 sad_plugin_
= GetContentClient()->renderer()->GetSadPluginBitmap();
681 PaintSadPlugin(canvas
, plugin_rect_
, *sad_plugin_
);
685 if (!uses_shared_bitmaps_
)
688 // We got a paint before the plugin's coordinates, so there's no buffer to
690 if (!front_buffer_canvas())
693 gfx::Rect offset_rect
= rect
;
694 offset_rect
.Offset(-plugin_rect_
.x(), -plugin_rect_
.y());
696 // transport_store_painted_ is really a bounding box, so in principle this
697 // check could falsely indicate that we don't need to paint offset_rect, but
698 // in practice it works fine.
699 if (!transport_store_painted_
.Contains(offset_rect
)) {
700 Send(new PluginMsg_Paint(instance_id_
, offset_rect
));
701 // Since the plugin is not blocked on the renderer in this context, there is
702 // a chance that it will begin repainting the back-buffer before we complete
703 // capturing the data. Buffer flipping would increase that risk because
704 // geometry update is asynchronous, so we don't want to use buffer flipping
706 UpdateFrontBuffer(offset_rect
, false);
709 const SkBitmap
& bitmap
=
710 front_buffer_canvas()->getDevice()->accessBitmap(false);
712 paint
.setXfermodeMode(
713 transparent_
? SkXfermode::kSrcATop_Mode
: SkXfermode::kSrc_Mode
);
714 SkIRect src_rect
= gfx::RectToSkIRect(offset_rect
);
715 canvas
->drawBitmapRect(bitmap
,
717 gfx::RectToSkRect(rect
),
720 if (invalidate_pending_
) {
721 // Only send the PaintAck message if this paint is in response to an
722 // invalidate from the plugin, since this message acts as an access token
723 // to ensure only one process is using the transport dib at a time.
724 invalidate_pending_
= false;
725 Send(new PluginMsg_DidPaint(instance_id_
));
729 NPObject
* WebPluginDelegateProxy::GetPluginScriptableObject() {
731 return WebBindings::retainObject(npobject_
);
733 if (!channel_host_
.get())
736 int route_id
= MSG_ROUTING_NONE
;
737 Send(new PluginMsg_GetPluginScriptableObject(instance_id_
, &route_id
));
738 if (route_id
== MSG_ROUTING_NONE
)
741 npobject_
= NPObjectProxy::Create(
742 channel_host_
.get(), route_id
, 0, page_url_
, GetPluginNPP());
744 return WebBindings::retainObject(npobject_
);
747 NPP
WebPluginDelegateProxy::GetPluginNPP() {
748 // Return a dummy NPP for WebKit to use to identify this plugin.
752 bool WebPluginDelegateProxy::GetFormValue(base::string16
* value
) {
753 bool success
= false;
754 Send(new PluginMsg_GetFormValue(instance_id_
, value
, &success
));
758 void WebPluginDelegateProxy::DidFinishLoadWithReason(
759 const GURL
& url
, NPReason reason
, int notify_id
) {
760 Send(new PluginMsg_DidFinishLoadWithReason(
761 instance_id_
, url
, reason
, notify_id
));
764 void WebPluginDelegateProxy::SetFocus(bool focused
) {
765 Send(new PluginMsg_SetFocus(instance_id_
, focused
));
768 render_view_
->PluginFocusChanged(focused
, instance_id_
);
772 bool WebPluginDelegateProxy::HandleInputEvent(
773 const WebInputEvent
& event
,
774 WebCursor::CursorInfo
* cursor_info
) {
775 bool handled
= false;
777 // A windowless plugin can enter a modal loop in the context of a
778 // NPP_HandleEvent call, in which case we need to pump messages to
779 // the plugin. We pass of the corresponding event handle to the
780 // plugin process, which is set if the plugin does enter a modal loop.
781 IPC::SyncMessage
* message
= new PluginMsg_HandleInputEvent(
782 instance_id_
, &event
, &handled
, &cursor
);
783 message
->set_pump_messages_event(modal_loop_pump_messages_event_
.get());
788 int WebPluginDelegateProxy::GetProcessId() {
789 return channel_host_
->peer_pid();
792 void WebPluginDelegateProxy::SetContentAreaFocus(bool has_focus
) {
793 IPC::Message
* msg
= new PluginMsg_SetContentAreaFocus(instance_id_
,
795 // Make sure focus events are delivered in the right order relative to
796 // sync messages they might interact with (Paint, HandleEvent, etc.).
797 msg
->set_unblock(true);
802 void WebPluginDelegateProxy::ImeCompositionUpdated(
803 const base::string16
& text
,
804 const std::vector
<int>& clauses
,
805 const std::vector
<int>& target
,
808 // Dispatch the raw IME data if this plug-in is the focused one.
809 if (instance_id_
!= plugin_id
)
812 IPC::Message
* msg
= new PluginMsg_ImeCompositionUpdated(instance_id_
,
813 text
, clauses
, target
, cursor_position
);
814 msg
->set_unblock(true);
818 void WebPluginDelegateProxy::ImeCompositionCompleted(const base::string16
& text
,
820 // Dispatch the IME text if this plug-in is the focused one.
821 if (instance_id_
!= plugin_id
)
824 IPC::Message
* msg
= new PluginMsg_ImeCompositionCompleted(instance_id_
, text
);
825 msg
->set_unblock(true);
830 #if defined(OS_MACOSX)
831 void WebPluginDelegateProxy::SetWindowFocus(bool window_has_focus
) {
832 IPC::Message
* msg
= new PluginMsg_SetWindowFocus(instance_id_
,
834 // Make sure focus events are delivered in the right order relative to
835 // sync messages they might interact with (Paint, HandleEvent, etc.).
836 msg
->set_unblock(true);
840 void WebPluginDelegateProxy::SetContainerVisibility(bool is_visible
) {
843 gfx::Rect window_frame
= render_view_
->rootWindowRect();
844 gfx::Rect view_frame
= render_view_
->windowRect();
845 blink::WebView
* webview
= render_view_
->webview();
846 msg
= new PluginMsg_ContainerShown(instance_id_
, window_frame
, view_frame
,
847 webview
&& webview
->isActive());
849 msg
= new PluginMsg_ContainerHidden(instance_id_
);
851 // Make sure visibility events are delivered in the right order relative to
852 // sync messages they might interact with (Paint, HandleEvent, etc.).
853 msg
->set_unblock(true);
857 void WebPluginDelegateProxy::WindowFrameChanged(gfx::Rect window_frame
,
858 gfx::Rect view_frame
) {
859 IPC::Message
* msg
= new PluginMsg_WindowFrameChanged(instance_id_
,
862 // Make sure frame events are delivered in the right order relative to
863 // sync messages they might interact with (e.g., HandleEvent).
864 msg
->set_unblock(true);
867 void WebPluginDelegateProxy::ImeCompositionCompleted(const base::string16
& text
,
869 // If the message isn't intended for this plugin, there's nothing to do.
870 if (instance_id_
!= plugin_id
)
873 IPC::Message
* msg
= new PluginMsg_ImeCompositionCompleted(instance_id_
,
875 // Order relative to other key events is important.
876 msg
->set_unblock(true);
881 void WebPluginDelegateProxy::OnSetWindow(gfx::PluginWindowHandle window
) {
882 #if defined(OS_MACOSX)
883 uses_shared_bitmaps_
= !window
&& !uses_compositor_
;
885 uses_shared_bitmaps_
= !window
;
889 plugin_
->SetWindow(window
);
892 void WebPluginDelegateProxy::WillDestroyWindow() {
894 plugin_
->WillDestroyWindow(window_
);
895 window_
= gfx::kNullPluginWindow
;
899 void WebPluginDelegateProxy::OnSetWindowlessData(
900 HANDLE modal_loop_pump_messages_event
,
901 gfx::NativeViewId dummy_activation_window
) {
902 DCHECK(modal_loop_pump_messages_event_
== NULL
);
903 DCHECK(dummy_activation_window_
== NULL
);
905 dummy_activation_window_
= dummy_activation_window
;
906 render_view_
->Send(new ViewHostMsg_WindowlessPluginDummyWindowCreated(
907 render_view_
->routing_id(), dummy_activation_window_
));
909 // Bug 25583: this can be null because some "virus scanners" block the
910 // DuplicateHandle call in the plugin process.
911 if (!modal_loop_pump_messages_event
)
914 modal_loop_pump_messages_event_
.reset(
915 new base::WaitableEvent(modal_loop_pump_messages_event
));
918 void WebPluginDelegateProxy::OnNotifyIMEStatus(int input_type
,
919 const gfx::Rect
& caret_rect
) {
923 render_view_
->Send(new ViewHostMsg_TextInputTypeChanged(
924 render_view_
->routing_id(),
925 static_cast<ui::TextInputType
>(input_type
),
926 ui::TEXT_INPUT_MODE_DEFAULT
,
929 ViewHostMsg_SelectionBounds_Params bounds_params
;
930 bounds_params
.anchor_rect
= bounds_params
.focus_rect
= caret_rect
;
931 bounds_params
.anchor_dir
= bounds_params
.focus_dir
=
932 blink::WebTextDirectionLeftToRight
;
933 bounds_params
.is_anchor_first
= true;
934 render_view_
->Send(new ViewHostMsg_SelectionBoundsChanged(
935 render_view_
->routing_id(),
940 void WebPluginDelegateProxy::OnCancelResource(int id
) {
942 plugin_
->CancelResource(id
);
945 void WebPluginDelegateProxy::OnInvalidateRect(const gfx::Rect
& rect
) {
949 // Clip the invalidation rect to the plugin bounds; the plugin may have been
950 // resized since the invalidate message was sent.
951 gfx::Rect clipped_rect
=
952 gfx::IntersectRects(rect
, gfx::Rect(plugin_rect_
.size()));
954 invalidate_pending_
= true;
955 // The plugin is blocked on the renderer because the invalidate message it has
956 // sent us is synchronous, so we can use buffer flipping here if the caller
958 UpdateFrontBuffer(clipped_rect
, true);
959 plugin_
->InvalidateRect(clipped_rect
);
962 void WebPluginDelegateProxy::OnGetWindowScriptNPObject(
963 int route_id
, bool* success
) {
965 NPObject
* npobject
= NULL
;
967 npobject
= plugin_
->GetWindowScriptNPObject();
972 // The stub will delete itself when the proxy tells it that it's released, or
973 // otherwise when the channel is closed.
974 new NPObjectStub(npobject
, channel_host_
.get(), route_id
, 0, page_url_
);
978 void WebPluginDelegateProxy::OnResolveProxy(const GURL
& url
,
980 std::string
* proxy_list
) {
981 *result
= RenderThreadImpl::current()->ResolveProxy(url
, proxy_list
);
984 void WebPluginDelegateProxy::OnGetPluginElement(int route_id
, bool* success
) {
986 NPObject
* npobject
= NULL
;
988 npobject
= plugin_
->GetPluginElement();
992 // The stub will delete itself when the proxy tells it that it's released, or
993 // otherwise when the channel is closed.
995 npobject
, channel_host_
.get(), route_id
, 0, page_url_
);
999 void WebPluginDelegateProxy::OnSetCookie(const GURL
& url
,
1000 const GURL
& first_party_for_cookies
,
1001 const std::string
& cookie
) {
1003 plugin_
->SetCookie(url
, first_party_for_cookies
, cookie
);
1006 void WebPluginDelegateProxy::OnGetCookies(const GURL
& url
,
1007 const GURL
& first_party_for_cookies
,
1008 std::string
* cookies
) {
1011 *cookies
= plugin_
->GetCookies(url
, first_party_for_cookies
);
1014 void WebPluginDelegateProxy::CopyFromBackBufferToFrontBuffer(
1015 const gfx::Rect
& rect
) {
1016 #if defined(OS_MACOSX)
1017 // Blitting the bits directly is much faster than going through CG, and since
1018 // the goal is just to move the raw pixels between two bitmaps with the same
1019 // pixel format (no compositing, color correction, etc.), it's safe.
1020 const size_t stride
=
1021 skia::PlatformCanvasStrideForWidth(plugin_rect_
.width());
1022 const size_t chunk_size
= 4 * rect
.width();
1023 DCHECK(back_buffer_dib() != NULL
);
1024 uint8
* source_data
= static_cast<uint8
*>(back_buffer_dib()->memory()) +
1025 rect
.y() * stride
+ 4 * rect
.x();
1026 DCHECK(front_buffer_dib() != NULL
);
1027 uint8
* target_data
= static_cast<uint8
*>(front_buffer_dib()->memory()) +
1028 rect
.y() * stride
+ 4 * rect
.x();
1029 for (int row
= 0; row
< rect
.height(); ++row
) {
1030 memcpy(target_data
, source_data
, chunk_size
);
1031 source_data
+= stride
;
1032 target_data
+= stride
;
1035 BlitCanvasToCanvas(front_buffer_canvas(),
1037 back_buffer_canvas(),
1042 void WebPluginDelegateProxy::UpdateFrontBuffer(
1043 const gfx::Rect
& rect
,
1044 bool allow_buffer_flipping
) {
1045 if (!front_buffer_canvas()) {
1050 // If SendUpdateGeometry() would block on the plugin process then we don't
1051 // want to use buffer flipping at all since it would add extra locking.
1052 // (Alternatively we could probably safely use async updates for buffer
1053 // flipping all the time since the size is not changing.)
1054 if (UseSynchronousGeometryUpdates()) {
1055 allow_buffer_flipping
= false;
1059 // Plugin has just painted "rect" into the back-buffer, so the front-buffer
1060 // no longer holds the latest content for that rectangle.
1061 front_buffer_diff_
.Subtract(rect
);
1062 if (allow_buffer_flipping
&& front_buffer_diff_
.IsEmpty()) {
1063 // Back-buffer contains the latest content for all areas; simply flip
1065 front_buffer_index_
= back_buffer_index();
1066 SendUpdateGeometry(false);
1067 // The front-buffer now holds newer content for this region than the
1069 front_buffer_diff_
= rect
;
1071 // Back-buffer contains the latest content for "rect" but the front-buffer
1072 // contains the latest content for some other areas (or buffer flipping not
1073 // allowed); fall back to copying the data.
1074 CopyFromBackBufferToFrontBuffer(rect
);
1076 transport_store_painted_
.Union(rect
);
1079 void WebPluginDelegateProxy::OnHandleURLRequest(
1080 const PluginHostMsg_URLRequest_Params
& params
) {
1081 const char* data
= NULL
;
1082 if (params
.buffer
.size())
1083 data
= ¶ms
.buffer
[0];
1085 const char* target
= NULL
;
1086 if (params
.target
.length())
1087 target
= params
.target
.c_str();
1089 plugin_
->HandleURLRequest(
1090 params
.url
.c_str(), params
.method
.c_str(), target
, data
,
1091 static_cast<unsigned int>(params
.buffer
.size()), params
.notify_id
,
1092 params
.popups_allowed
, params
.notify_redirects
);
1095 WebPluginResourceClient
* WebPluginDelegateProxy::CreateResourceClient(
1096 unsigned long resource_id
, const GURL
& url
, int notify_id
) {
1097 if (!channel_host_
.get())
1100 ResourceClientProxy
* proxy
=
1101 new ResourceClientProxy(channel_host_
.get(), instance_id_
);
1102 proxy
->Initialize(resource_id
, url
, notify_id
);
1106 WebPluginResourceClient
* WebPluginDelegateProxy::CreateSeekableResourceClient(
1107 unsigned long resource_id
, int range_request_id
) {
1108 if (!channel_host_
.get())
1111 ResourceClientProxy
* proxy
=
1112 new ResourceClientProxy(channel_host_
.get(), instance_id_
);
1113 proxy
->InitializeForSeekableStream(resource_id
, range_request_id
);
1117 void WebPluginDelegateProxy::FetchURL(unsigned long resource_id
,
1120 const GURL
& first_party_for_cookies
,
1121 const std::string
& method
,
1124 const GURL
& referrer
,
1125 bool notify_redirects
,
1126 bool is_plugin_src_load
,
1128 int render_frame_id
,
1129 int render_view_id
) {
1130 PluginMsg_FetchURL_Params params
;
1131 params
.resource_id
= resource_id
;
1132 params
.notify_id
= notify_id
;
1134 params
.first_party_for_cookies
= first_party_for_cookies
;
1135 params
.method
= method
;
1137 params
.post_data
.resize(len
);
1138 memcpy(¶ms
.post_data
.front(), buf
, len
);
1140 params
.referrer
= referrer
;
1141 params
.notify_redirect
= notify_redirects
;
1142 params
.is_plugin_src_load
= is_plugin_src_load
;
1143 params
.render_frame_id
= render_frame_id
;
1144 Send(new PluginMsg_FetchURL(instance_id_
, params
));
1147 #if defined(OS_MACOSX)
1148 void WebPluginDelegateProxy::OnFocusChanged(bool focused
) {
1150 render_view_
->PluginFocusChanged(focused
, instance_id_
);
1153 void WebPluginDelegateProxy::OnStartIme() {
1155 render_view_
->StartPluginIme();
1159 gfx::PluginWindowHandle
WebPluginDelegateProxy::GetPluginWindowHandle() {
1163 void WebPluginDelegateProxy::OnCancelDocumentLoad() {
1164 plugin_
->CancelDocumentLoad();
1167 void WebPluginDelegateProxy::OnInitiateHTTPRangeRequest(
1168 const std::string
& url
,
1169 const std::string
& range_info
,
1170 int range_request_id
) {
1171 plugin_
->InitiateHTTPRangeRequest(
1172 url
.c_str(), range_info
.c_str(), range_request_id
);
1175 void WebPluginDelegateProxy::OnDidStartLoading() {
1176 plugin_
->DidStartLoading();
1179 void WebPluginDelegateProxy::OnDidStopLoading() {
1180 plugin_
->DidStopLoading();
1183 void WebPluginDelegateProxy::OnDeferResourceLoading(unsigned long resource_id
,
1185 plugin_
->SetDeferResourceLoading(resource_id
, defer
);
1188 #if defined(OS_MACOSX)
1189 void WebPluginDelegateProxy::OnAcceleratedPluginEnabledRendering() {
1190 uses_compositor_
= true;
1191 OnSetWindow(gfx::kNullPluginWindow
);
1194 void WebPluginDelegateProxy::OnAcceleratedPluginAllocatedIOSurface(
1197 uint32 surface_id
) {
1199 plugin_
->AcceleratedPluginAllocatedIOSurface(width
, height
, surface_id
);
1202 void WebPluginDelegateProxy::OnAcceleratedPluginSwappedIOSurface() {
1204 plugin_
->AcceleratedPluginSwappedIOSurface();
1209 bool WebPluginDelegateProxy::UseSynchronousGeometryUpdates() {
1210 // Need to update geometry synchronously with WMP, otherwise if a site
1211 // scripts the plugin to start playing while it's in the middle of handling
1212 // an update geometry message, videos don't play. See urls in bug 20260.
1213 if (info_
.name
.find(base::ASCIIToUTF16("Windows Media Player")) !=
1214 base::string16::npos
)
1217 // The move networks plugin needs to be informed of geometry updates
1219 std::vector
<WebPluginMimeType
>::iterator index
;
1220 for (index
= info_
.mime_types
.begin(); index
!= info_
.mime_types
.end();
1222 if (index
->mime_type
== "application/x-vnd.moveplayer.qm" ||
1223 index
->mime_type
== "application/x-vnd.moveplay2.qm" ||
1224 index
->mime_type
== "application/x-vnd.movenetworks.qm" ||
1225 index
->mime_type
== "application/x-vnd.mnplayer.qm") {
1233 void WebPluginDelegateProxy::OnURLRedirectResponse(bool allow
,
1238 plugin_
->URLRedirectResponse(allow
, resource_id
);
1241 void WebPluginDelegateProxy::OnCheckIfRunInsecureContent(const GURL
& url
,
1243 *result
= plugin_
->CheckIfRunInsecureContent(url
);
1246 } // namespace content