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 "cc/resources/shared_bitmap.h"
22 #include "content/child/child_process.h"
23 #include "content/child/child_shared_bitmap_manager.h"
24 #include "content/child/npapi/npobject_proxy.h"
25 #include "content/child/npapi/npobject_stub.h"
26 #include "content/child/npapi/npobject_util.h"
27 #include "content/child/npapi/webplugin_resource_client.h"
28 #include "content/child/plugin_messages.h"
29 #include "content/common/content_constants_internal.h"
30 #include "content/common/cursors/webcursor.h"
31 #include "content/common/frame_messages.h"
32 #include "content/common/view_messages.h"
33 #include "content/public/renderer/content_renderer_client.h"
34 #include "content/renderer/npapi/plugin_channel_host.h"
35 #include "content/renderer/npapi/webplugin_impl.h"
36 #include "content/renderer/render_thread_impl.h"
37 #include "content/renderer/render_view_impl.h"
38 #include "content/renderer/sad_plugin.h"
39 #include "ipc/ipc_channel_handle.h"
40 #include "net/base/mime_util.h"
41 #include "skia/ext/platform_canvas.h"
42 #include "third_party/WebKit/public/platform/WebDragData.h"
43 #include "third_party/WebKit/public/platform/WebString.h"
44 #include "third_party/WebKit/public/web/WebBindings.h"
45 #include "third_party/WebKit/public/web/WebDocument.h"
46 #include "third_party/WebKit/public/web/WebFrame.h"
47 #include "third_party/WebKit/public/web/WebView.h"
48 #include "ui/gfx/blit.h"
49 #include "ui/gfx/canvas.h"
50 #include "ui/gfx/geometry/size.h"
51 #include "ui/gfx/native_widget_types.h"
52 #include "ui/gfx/skia_util.h"
55 #include "ipc/ipc_channel_posix.h"
59 #include "base/win/scoped_handle.h"
60 #include "content/public/common/sandbox_init.h"
63 using blink::WebBindings
;
64 using blink::WebCursorInfo
;
65 using blink::WebDragData
;
66 using blink::WebInputEvent
;
67 using blink::WebString
;
74 class ScopedLogLevel
{
76 explicit ScopedLogLevel(int level
);
82 DISALLOW_COPY_AND_ASSIGN(ScopedLogLevel
);
85 ScopedLogLevel::ScopedLogLevel(int level
)
86 : old_level_(logging::GetMinLogLevel()) {
87 logging::SetMinLogLevel(level
);
90 ScopedLogLevel::~ScopedLogLevel() {
91 logging::SetMinLogLevel(old_level_
);
94 // Proxy for WebPluginResourceClient. The object owns itself after creation,
95 // deleting itself after its callback has been called.
96 class ResourceClientProxy
: public WebPluginResourceClient
{
98 ResourceClientProxy(PluginChannelHost
* channel
, int instance_id
)
99 : channel_(channel
), instance_id_(instance_id
), resource_id_(0),
100 multibyte_response_expected_(false) {
103 ~ResourceClientProxy() override
{}
105 void Initialize(unsigned long resource_id
, const GURL
& url
, int notify_id
) {
106 resource_id_
= resource_id
;
107 channel_
->Send(new PluginMsg_HandleURLRequestReply(
108 instance_id_
, resource_id
, url
, notify_id
));
111 void InitializeForSeekableStream(unsigned long resource_id
,
112 int range_request_id
) {
113 resource_id_
= resource_id
;
114 multibyte_response_expected_
= true;
115 channel_
->Send(new PluginMsg_HTTPRangeRequestReply(
116 instance_id_
, resource_id
, range_request_id
));
119 // PluginResourceClient implementation:
120 void WillSendRequest(const GURL
& url
, int http_status_code
) override
{
121 DCHECK(channel_
.get() != NULL
);
122 channel_
->Send(new PluginMsg_WillSendRequest(
123 instance_id_
, resource_id_
, url
, http_status_code
));
126 void DidReceiveResponse(const std::string
& mime_type
,
127 const std::string
& headers
,
128 uint32 expected_length
,
129 uint32 last_modified
,
130 bool request_is_seekable
) override
{
131 DCHECK(channel_
.get() != NULL
);
132 PluginMsg_DidReceiveResponseParams params
;
133 params
.id
= resource_id_
;
134 params
.mime_type
= mime_type
;
135 params
.headers
= headers
;
136 params
.expected_length
= expected_length
;
137 params
.last_modified
= last_modified
;
138 params
.request_is_seekable
= request_is_seekable
;
139 // Grab a reference on the underlying channel so it does not get
140 // deleted from under us.
141 scoped_refptr
<PluginChannelHost
> channel_ref(channel_
);
142 channel_
->Send(new PluginMsg_DidReceiveResponse(instance_id_
, params
));
145 void DidReceiveData(const char* buffer
,
147 int data_offset
) override
{
148 DCHECK(channel_
.get() != NULL
);
149 DCHECK_GT(length
, 0);
150 std::vector
<char> data
;
151 data
.resize(static_cast<size_t>(length
));
152 memcpy(&data
.front(), buffer
, length
);
153 // Grab a reference on the underlying channel so it does not get
154 // deleted from under us.
155 scoped_refptr
<PluginChannelHost
> channel_ref(channel_
);
156 channel_
->Send(new PluginMsg_DidReceiveData(instance_id_
, resource_id_
,
160 void DidFinishLoading(unsigned long resource_id
) override
{
161 DCHECK(channel_
.get() != NULL
);
162 DCHECK_EQ(resource_id
, resource_id_
);
163 channel_
->Send(new PluginMsg_DidFinishLoading(instance_id_
, resource_id_
));
165 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
168 void DidFail(unsigned long resource_id
) override
{
169 DCHECK(channel_
.get() != NULL
);
170 DCHECK_EQ(resource_id
, resource_id_
);
171 channel_
->Send(new PluginMsg_DidFail(instance_id_
, resource_id_
));
173 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
176 bool IsMultiByteResponseExpected() override
{
177 return multibyte_response_expected_
;
180 int ResourceId() override
{ return resource_id_
; }
183 scoped_refptr
<PluginChannelHost
> channel_
;
185 unsigned long resource_id_
;
186 // Set to true if the response expected is a multibyte response.
187 // For e.g. response for a HTTP byte range request.
188 bool multibyte_response_expected_
;
193 WebPluginDelegateProxy::WebPluginDelegateProxy(
194 WebPluginImpl
* plugin
,
195 const std::string
& mime_type
,
196 const base::WeakPtr
<RenderViewImpl
>& render_view
,
197 RenderFrameImpl
* render_frame
)
198 : render_view_(render_view
),
199 render_frame_(render_frame
),
201 uses_shared_bitmaps_(false),
202 #if defined(OS_MACOSX)
203 uses_compositor_(false),
204 #elif defined(OS_WIN)
205 dummy_activation_window_(NULL
),
207 window_(gfx::kNullPluginWindow
),
208 mime_type_(mime_type
),
209 instance_id_(MSG_ROUTING_NONE
),
213 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
;
246 if (render_view_
.get())
247 render_view_
->UnregisterPluginDelegate(this);
249 if (channel_host_
.get()) {
250 Send(new PluginMsg_DestroyInstance(instance_id_
));
252 // Must remove the route after sending the destroy message, rather than
253 // before, since RemoveRoute can lead to all the outstanding NPObjects
254 // being told the channel went away if this was the last instance.
255 channel_host_
->RemoveRoute(instance_id_
);
257 // Remove the mapping between our instance-Id and NPP identifiers, used by
258 // the channel to track object ownership, before releasing it.
259 channel_host_
->RemoveMappingForNPObjectOwner(instance_id_
);
261 // Release the channel host now. If we are is the last reference to the
262 // channel, this avoids a race where this renderer asks a new connection to
263 // the same plugin between now and the time 'this' is actually deleted.
264 // Destroying the channel host is what releases the channel name -> FD
265 // association on POSIX, and if we ask for a new connection before it is
266 // released, the plugin will give us a new FD, and we'll assert when trying
267 // to associate it with the channel name.
268 channel_host_
= NULL
;
273 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
276 bool WebPluginDelegateProxy::Initialize(
278 const std::vector
<std::string
>& arg_names
,
279 const std::vector
<std::string
>& arg_values
,
280 bool load_manually
) {
281 // TODO(shess): Attempt to work around http://crbug.com/97285 and
282 // http://crbug.com/141055 by retrying the connection. Reports seem
283 // to indicate that the plugin hasn't crashed, and that the problem
284 // is not 100% persistent.
285 const size_t kAttempts
= 2;
288 scoped_refptr
<PluginChannelHost
> channel_host
;
291 for (size_t attempt
= 0; !result
&& attempt
< kAttempts
; attempt
++) {
292 #if defined(OS_MACOSX)
293 // TODO(shess): Debugging for http://crbug.com/97285 . See comment
294 // in plugin_channel_host.cc.
295 scoped_ptr
<base::AutoReset
<bool> > track_nested_removes(
296 new base::AutoReset
<bool>(PluginChannelHost::GetRemoveTrackingFlag(),
300 IPC::ChannelHandle channel_handle
;
301 if (!RenderThreadImpl::current()->Send(new FrameHostMsg_OpenChannelToPlugin(
302 render_frame_
->GetRoutingID(), url
, page_url_
, mime_type_
,
303 &channel_handle
, &info_
))) {
307 if (channel_handle
.name
.empty()) {
308 // We got an invalid handle. Either the plugin couldn't be found (which
309 // shouldn't happen, since if we got here the plugin should exist) or the
310 // plugin crashed on initialization.
311 if (!info_
.path
.empty()) {
312 render_view_
->GetMainRenderFrame()->PluginCrashed(
313 info_
.path
, base::kNullProcessId
);
314 LOG(ERROR
) << "Plugin crashed on start";
316 // Return true so that the plugin widget is created and we can paint the
317 // crashed plugin there.
320 LOG(ERROR
) << "Plugin couldn't be found";
324 channel_host
= PluginChannelHost::GetPluginChannelHost(
325 channel_handle
, ChildProcess::current()->io_task_runner(),
326 RenderThreadImpl::current()->GetAttachmentBroker());
327 if (!channel_host
.get()) {
328 LOG(ERROR
) << "Couldn't get PluginChannelHost";
331 #if defined(OS_MACOSX)
332 track_nested_removes
.reset();
336 // TODO(bauerb): Debugging for http://crbug.com/141055.
337 ScopedLogLevel
log_level(-2); // Equivalent to --v=2
338 result
= channel_host
->Send(new PluginMsg_CreateInstance(
339 mime_type_
, &instance_id
));
341 LOG(ERROR
) << "Couldn't send PluginMsg_CreateInstance";
347 // Failed too often, give up.
351 channel_host_
= channel_host
;
352 instance_id_
= instance_id
;
354 channel_host_
->AddRoute(instance_id_
, this, NULL
);
356 // Inform the channel of the mapping between our instance-Id and dummy NPP
357 // identifier, for use in object ownership tracking.
358 channel_host_
->AddMappingForNPObjectOwner(instance_id_
, GetPluginNPP());
360 // Now tell the PluginInstance in the plugin process to initialize.
361 PluginMsg_Init_Params params
;
363 params
.page_url
= page_url_
;
364 params
.arg_names
= arg_names
;
365 params
.arg_values
= arg_values
;
366 params
.host_render_view_routing_id
= render_view_
->routing_id();
367 params
.load_manually
= load_manually
;
370 Send(new PluginMsg_Init(instance_id_
, params
, &transparent_
, &result
));
373 LOG(WARNING
) << "PluginMsg_Init returned false";
375 render_view_
->RegisterPluginDelegate(this);
380 bool WebPluginDelegateProxy::Send(IPC::Message
* msg
) {
381 if (!channel_host_
.get()) {
382 DLOG(WARNING
) << "dropping message because channel host is null";
387 return channel_host_
->Send(msg
);
390 void WebPluginDelegateProxy::SendJavaScriptStream(const GURL
& url
,
391 const std::string
& result
,
394 Send(new PluginMsg_SendJavaScriptStream(
395 instance_id_
, url
, result
, success
, notify_id
));
398 void WebPluginDelegateProxy::DidReceiveManualResponse(
399 const GURL
& url
, const std::string
& mime_type
,
400 const std::string
& headers
, uint32 expected_length
,
401 uint32 last_modified
) {
402 PluginMsg_DidReceiveResponseParams params
;
404 params
.mime_type
= mime_type
;
405 params
.headers
= headers
;
406 params
.expected_length
= expected_length
;
407 params
.last_modified
= last_modified
;
408 Send(new PluginMsg_DidReceiveManualResponse(instance_id_
, url
, params
));
411 void WebPluginDelegateProxy::DidReceiveManualData(const char* buffer
,
413 DCHECK_GT(length
, 0);
414 std::vector
<char> data
;
415 data
.resize(static_cast<size_t>(length
));
416 memcpy(&data
.front(), buffer
, length
);
417 Send(new PluginMsg_DidReceiveManualData(instance_id_
, data
));
420 void WebPluginDelegateProxy::DidFinishManualLoading() {
421 Send(new PluginMsg_DidFinishManualLoading(instance_id_
));
424 void WebPluginDelegateProxy::DidManualLoadFail() {
425 Send(new PluginMsg_DidManualLoadFail(instance_id_
));
428 bool WebPluginDelegateProxy::OnMessageReceived(const IPC::Message
& msg
) {
429 GetContentClient()->SetActiveURL(page_url_
);
432 IPC_BEGIN_MESSAGE_MAP(WebPluginDelegateProxy
, msg
)
433 IPC_MESSAGE_HANDLER(PluginHostMsg_SetWindow
, OnSetWindow
)
434 IPC_MESSAGE_HANDLER(PluginHostMsg_CancelResource
, OnCancelResource
)
435 IPC_MESSAGE_HANDLER(PluginHostMsg_InvalidateRect
, OnInvalidateRect
)
436 IPC_MESSAGE_HANDLER(PluginHostMsg_GetWindowScriptNPObject
,
437 OnGetWindowScriptNPObject
)
438 IPC_MESSAGE_HANDLER(PluginHostMsg_GetPluginElement
, OnGetPluginElement
)
439 IPC_MESSAGE_HANDLER(PluginHostMsg_ResolveProxy
, OnResolveProxy
)
440 IPC_MESSAGE_HANDLER(PluginHostMsg_SetCookie
, OnSetCookie
)
441 IPC_MESSAGE_HANDLER(PluginHostMsg_GetCookies
, OnGetCookies
)
442 IPC_MESSAGE_HANDLER(PluginHostMsg_URLRequest
, OnHandleURLRequest
)
443 IPC_MESSAGE_HANDLER(PluginHostMsg_CancelDocumentLoad
, OnCancelDocumentLoad
)
444 IPC_MESSAGE_HANDLER(PluginHostMsg_InitiateHTTPRangeRequest
,
445 OnInitiateHTTPRangeRequest
)
446 IPC_MESSAGE_HANDLER(PluginHostMsg_DidStartLoading
, OnDidStartLoading
)
447 IPC_MESSAGE_HANDLER(PluginHostMsg_DidStopLoading
, OnDidStopLoading
)
448 IPC_MESSAGE_HANDLER(PluginHostMsg_DeferResourceLoading
,
449 OnDeferResourceLoading
)
450 IPC_MESSAGE_HANDLER(PluginHostMsg_URLRedirectResponse
,
451 OnURLRedirectResponse
)
452 IPC_MESSAGE_HANDLER(PluginHostMsg_CheckIfRunInsecureContent
,
453 OnCheckIfRunInsecureContent
)
455 IPC_MESSAGE_HANDLER(PluginHostMsg_SetWindowlessData
, OnSetWindowlessData
)
456 IPC_MESSAGE_HANDLER(PluginHostMsg_NotifyIMEStatus
, OnNotifyIMEStatus
)
458 #if defined(OS_MACOSX)
459 IPC_MESSAGE_HANDLER(PluginHostMsg_FocusChanged
,
461 IPC_MESSAGE_HANDLER(PluginHostMsg_StartIme
,
463 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginEnabledRendering
,
464 OnAcceleratedPluginEnabledRendering
)
465 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginAllocatedIOSurface
,
466 OnAcceleratedPluginAllocatedIOSurface
)
467 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginSwappedIOSurface
,
468 OnAcceleratedPluginSwappedIOSurface
)
470 IPC_MESSAGE_UNHANDLED(handled
= false)
471 IPC_END_MESSAGE_MAP()
476 void WebPluginDelegateProxy::OnChannelError() {
479 // The actual WebPluginDelegate never got a chance to tell the WebPlugin
480 // its window was going away. Do it on its behalf.
483 plugin_
->Invalidate();
485 if (channel_host_
.get() && !channel_host_
->expecting_shutdown()) {
486 render_view_
->GetMainRenderFrame()->PluginCrashed(
487 info_
.path
, channel_host_
->peer_pid());
490 #if defined(OS_MACOSX) || defined(OS_WIN)
491 // Ensure that the renderer doesn't think the plugin still has focus.
493 render_view_
->PluginFocusChanged(false, instance_id_
);
497 static void CopySharedMemoryHandleForMessage(
498 const base::SharedMemoryHandle
& handle_in
,
499 base::SharedMemoryHandle
* handle_out
,
500 base::ProcessId peer_pid
) {
501 #if defined(OS_POSIX)
502 *handle_out
= base::SharedMemory::DuplicateHandle(handle_in
);
503 #elif defined(OS_WIN)
504 // On Windows we need to duplicate the handle for the plugin process.
506 BrokerDuplicateHandle(handle_in
, peer_pid
, handle_out
,
507 FILE_MAP_READ
| FILE_MAP_WRITE
, 0);
508 DCHECK(*handle_out
!= NULL
);
510 #error Shared memory copy not implemented.
514 void WebPluginDelegateProxy::SendUpdateGeometry(
515 bool bitmaps_changed
) {
516 if (!channel_host_
.get())
519 PluginMsg_UpdateGeometry_Param param
;
520 param
.window_rect
= plugin_rect_
;
521 param
.clip_rect
= clip_rect_
;
522 param
.windowless_buffer0
= base::SharedMemory::NULLHandle();
523 param
.windowless_buffer1
= base::SharedMemory::NULLHandle();
524 param
.windowless_buffer_index
= back_buffer_index();
526 #if defined(OS_POSIX)
527 // If we're using POSIX mmap'd TransportDIBs, sending the handle across
528 // IPC establishes a new mapping rather than just sending a window ID,
529 // so only do so if we've actually changed the shared memory bitmaps.
533 if (transport_stores_
[0].bitmap
)
534 CopySharedMemoryHandleForMessage(
535 transport_stores_
[0].bitmap
->shared_memory()->handle(),
536 ¶m
.windowless_buffer0
, channel_host_
->peer_pid());
538 if (transport_stores_
[1].bitmap
)
539 CopySharedMemoryHandleForMessage(
540 transport_stores_
[1].bitmap
->shared_memory()->handle(),
541 ¶m
.windowless_buffer1
, channel_host_
->peer_pid());
546 if (UseSynchronousGeometryUpdates()) {
547 msg
= new PluginMsg_UpdateGeometrySync(instance_id_
, param
);
551 msg
= new PluginMsg_UpdateGeometry(instance_id_
, param
);
552 msg
->set_unblock(true);
558 void WebPluginDelegateProxy::UpdateGeometry(const gfx::Rect
& window_rect
,
559 const gfx::Rect
& clip_rect
) {
560 // window_rect becomes either a window in native windowing system
561 // coords, or a backing buffer. In either case things will go bad
562 // if the rectangle is very large.
563 if (window_rect
.width() < 0 || window_rect
.width() > kMaxPluginSideLength
||
564 window_rect
.height() < 0 || window_rect
.height() > kMaxPluginSideLength
||
565 // We know this won't overflow due to above checks.
566 static_cast<uint32
>(window_rect
.width()) *
567 static_cast<uint32
>(window_rect
.height()) > kMaxPluginSize
) {
571 plugin_rect_
= window_rect
;
572 clip_rect_
= clip_rect
;
574 bool bitmaps_changed
= false;
576 if (uses_shared_bitmaps_
) {
577 if (!front_buffer_canvas() ||
578 (window_rect
.width() != front_buffer_canvas()->getDevice()->width() ||
579 window_rect
.height() != front_buffer_canvas()->getDevice()->height()))
581 bitmaps_changed
= true;
583 // Create a shared memory section that the plugin paints into
585 ResetWindowlessBitmaps();
586 if (!window_rect
.IsEmpty()) {
587 if (!CreateSharedBitmap(&transport_stores_
[0].bitmap
,
588 &transport_stores_
[0].canvas
) ||
589 !CreateSharedBitmap(&transport_stores_
[1].bitmap
,
590 &transport_stores_
[1].canvas
)) {
592 ResetWindowlessBitmaps();
599 SendUpdateGeometry(bitmaps_changed
);
602 void WebPluginDelegateProxy::ResetWindowlessBitmaps() {
603 transport_stores_
[0].bitmap
.reset();
604 transport_stores_
[1].bitmap
.reset();
606 transport_stores_
[0].canvas
.reset();
607 transport_stores_
[1].canvas
.reset();
608 transport_store_painted_
= gfx::Rect();
609 front_buffer_diff_
= gfx::Rect();
613 static size_t BitmapSizeForPluginRect(const gfx::Rect
& plugin_rect
) {
614 const size_t stride
=
615 skia::PlatformCanvasStrideForWidth(plugin_rect
.width());
616 return stride
* plugin_rect
.height();
619 bool WebPluginDelegateProxy::CreateLocalBitmap(
620 std::vector
<uint8
>* memory
,
621 scoped_ptr
<skia::PlatformCanvas
>* canvas
) {
622 const size_t size
= BitmapSizeForPluginRect(plugin_rect_
);
623 memory
->resize(size
);
624 if (memory
->size() != size
)
626 canvas
->reset(skia::CreatePlatformCanvas(
627 plugin_rect_
.width(), plugin_rect_
.height(), true, &((*memory
)[0]),
628 skia::CRASH_ON_FAILURE
));
633 bool WebPluginDelegateProxy::CreateSharedBitmap(
634 scoped_ptr
<SharedMemoryBitmap
>* memory
,
635 scoped_ptr
<skia::PlatformCanvas
>* canvas
) {
636 *memory
= ChildThreadImpl::current()
637 ->shared_bitmap_manager()
638 ->AllocateSharedMemoryBitmap(plugin_rect_
.size());
641 DCHECK((*memory
)->shared_memory());
642 #if defined(OS_POSIX)
643 canvas
->reset(skia::CreatePlatformCanvas(
644 plugin_rect_
.width(), plugin_rect_
.height(), true, (*memory
)->pixels(),
645 skia::RETURN_NULL_ON_FAILURE
));
647 canvas
->reset(skia::CreatePlatformCanvas(
648 plugin_rect_
.width(), plugin_rect_
.height(), true,
649 (*memory
)->shared_memory()->handle(), skia::RETURN_NULL_ON_FAILURE
));
651 return !!canvas
->get();
654 void WebPluginDelegateProxy::Paint(SkCanvas
* canvas
,
655 const gfx::Rect
& damaged_rect
) {
656 // Limit the damaged rectangle to whatever is contained inside the plugin
657 // rectangle, as that's the rectangle that we'll actually draw.
658 gfx::Rect rect
= gfx::IntersectRects(damaged_rect
, plugin_rect_
);
660 // If the plugin is no longer connected (channel crashed) draw a crashed
662 if (!channel_host_
.get() || !channel_host_
->channel_valid()) {
663 // Lazily load the sad plugin image.
665 sad_plugin_
= GetContentClient()->renderer()->GetSadPluginBitmap();
667 PaintSadPlugin(canvas
, plugin_rect_
, *sad_plugin_
);
671 if (!uses_shared_bitmaps_
)
674 // We got a paint before the plugin's coordinates, so there's no buffer to
676 if (!front_buffer_canvas())
679 gfx::Rect offset_rect
= rect
;
680 offset_rect
.Offset(-plugin_rect_
.x(), -plugin_rect_
.y());
682 // transport_store_painted_ is really a bounding box, so in principle this
683 // check could falsely indicate that we don't need to paint offset_rect, but
684 // in practice it works fine.
685 if (!transport_store_painted_
.Contains(offset_rect
)) {
686 Send(new PluginMsg_Paint(instance_id_
, offset_rect
));
687 // Since the plugin is not blocked on the renderer in this context, there is
688 // a chance that it will begin repainting the back-buffer before we complete
689 // capturing the data. Buffer flipping would increase that risk because
690 // geometry update is asynchronous, so we don't want to use buffer flipping
692 UpdateFrontBuffer(offset_rect
, false);
695 const SkBitmap
& bitmap
=
696 front_buffer_canvas()->getDevice()->accessBitmap(false);
698 paint
.setXfermodeMode(
699 transparent_
? SkXfermode::kSrcATop_Mode
: SkXfermode::kSrc_Mode
);
700 SkRect src_rect
= gfx::RectToSkRect(offset_rect
);
701 canvas
->drawBitmapRect(bitmap
,
703 gfx::RectToSkRect(rect
),
706 if (invalidate_pending_
) {
707 // Only send the PaintAck message if this paint is in response to an
708 // invalidate from the plugin, since this message acts as an access token
709 // to ensure only one process is using the shared bitmap at a time.
710 invalidate_pending_
= false;
711 Send(new PluginMsg_DidPaint(instance_id_
));
715 NPObject
* WebPluginDelegateProxy::GetPluginScriptableObject() {
717 return WebBindings::retainObject(npobject_
);
719 if (!channel_host_
.get())
722 int route_id
= MSG_ROUTING_NONE
;
723 Send(new PluginMsg_GetPluginScriptableObject(instance_id_
, &route_id
));
724 if (route_id
== MSG_ROUTING_NONE
)
727 if (!channel_host_
.get())
730 npobject_
= NPObjectProxy::Create(
731 channel_host_
.get(), route_id
, 0, page_url_
, GetPluginNPP());
733 return WebBindings::retainObject(npobject_
);
736 NPP
WebPluginDelegateProxy::GetPluginNPP() {
737 // Return a dummy NPP for WebKit to use to identify this plugin.
741 bool WebPluginDelegateProxy::GetFormValue(base::string16
* value
) {
742 bool success
= false;
743 Send(new PluginMsg_GetFormValue(instance_id_
, value
, &success
));
747 void WebPluginDelegateProxy::DidFinishLoadWithReason(
748 const GURL
& url
, NPReason reason
, int notify_id
) {
749 Send(new PluginMsg_DidFinishLoadWithReason(
750 instance_id_
, url
, reason
, notify_id
));
753 void WebPluginDelegateProxy::SetFocus(bool focused
) {
754 Send(new PluginMsg_SetFocus(instance_id_
, focused
));
757 render_view_
->PluginFocusChanged(focused
, instance_id_
);
761 bool WebPluginDelegateProxy::HandleInputEvent(
762 const WebInputEvent
& event
,
763 WebCursor::CursorInfo
* cursor_info
) {
764 bool handled
= false;
766 // A windowless plugin can enter a modal loop in the context of a
767 // NPP_HandleEvent call, in which case we need to pump messages to
768 // the plugin. We pass of the corresponding event handle to the
769 // plugin process, which is set if the plugin does enter a modal loop.
770 IPC::SyncMessage
* message
= new PluginMsg_HandleInputEvent(
771 instance_id_
, &event
, &handled
, &cursor
);
772 message
->set_pump_messages_event(modal_loop_pump_messages_event_
.get());
777 int WebPluginDelegateProxy::GetProcessId() {
778 return channel_host_
->peer_pid();
781 void WebPluginDelegateProxy::SetContentAreaFocus(bool has_focus
) {
782 IPC::Message
* msg
= new PluginMsg_SetContentAreaFocus(instance_id_
,
784 // Make sure focus events are delivered in the right order relative to
785 // sync messages they might interact with (Paint, HandleEvent, etc.).
786 msg
->set_unblock(true);
791 void WebPluginDelegateProxy::ImeCompositionUpdated(
792 const base::string16
& text
,
793 const std::vector
<int>& clauses
,
794 const std::vector
<int>& target
,
797 // Dispatch the raw IME data if this plugin is the focused one.
798 if (instance_id_
!= plugin_id
)
801 IPC::Message
* msg
= new PluginMsg_ImeCompositionUpdated(instance_id_
,
802 text
, clauses
, target
, cursor_position
);
803 msg
->set_unblock(true);
807 void WebPluginDelegateProxy::ImeCompositionCompleted(const base::string16
& text
,
809 // Dispatch the IME text if this plugin is the focused one.
810 if (instance_id_
!= plugin_id
)
813 IPC::Message
* msg
= new PluginMsg_ImeCompositionCompleted(instance_id_
, text
);
814 msg
->set_unblock(true);
819 #if defined(OS_MACOSX)
820 void WebPluginDelegateProxy::SetWindowFocus(bool window_has_focus
) {
821 IPC::Message
* msg
= new PluginMsg_SetWindowFocus(instance_id_
,
823 // Make sure focus events are delivered in the right order relative to
824 // sync messages they might interact with (Paint, HandleEvent, etc.).
825 msg
->set_unblock(true);
829 void WebPluginDelegateProxy::SetContainerVisibility(bool is_visible
) {
832 gfx::Rect window_frame
= render_view_
->rootWindowRect();
833 gfx::Rect view_frame
= render_view_
->windowRect();
834 blink::WebView
* webview
= render_view_
->webview();
835 msg
= new PluginMsg_ContainerShown(instance_id_
, window_frame
, view_frame
,
836 webview
&& webview
->isActive());
838 msg
= new PluginMsg_ContainerHidden(instance_id_
);
840 // Make sure visibility 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::WindowFrameChanged(gfx::Rect window_frame
,
847 gfx::Rect view_frame
) {
848 IPC::Message
* msg
= new PluginMsg_WindowFrameChanged(instance_id_
,
851 // Make sure frame events are delivered in the right order relative to
852 // sync messages they might interact with (e.g., HandleEvent).
853 msg
->set_unblock(true);
856 void WebPluginDelegateProxy::ImeCompositionCompleted(const base::string16
& text
,
858 // If the message isn't intended for this plugin, there's nothing to do.
859 if (instance_id_
!= plugin_id
)
862 IPC::Message
* msg
= new PluginMsg_ImeCompositionCompleted(instance_id_
,
864 // Order relative to other key events is important.
865 msg
->set_unblock(true);
870 void WebPluginDelegateProxy::OnSetWindow(gfx::PluginWindowHandle window
) {
871 #if defined(OS_MACOSX)
872 uses_shared_bitmaps_
= !window
&& !uses_compositor_
;
874 uses_shared_bitmaps_
= !window
;
878 plugin_
->SetWindow(window
);
881 void WebPluginDelegateProxy::WillDestroyWindow() {
883 plugin_
->WillDestroyWindow(window_
);
884 window_
= gfx::kNullPluginWindow
;
888 void WebPluginDelegateProxy::OnSetWindowlessData(
889 HANDLE modal_loop_pump_messages_event_handle
,
890 gfx::NativeViewId dummy_activation_window
) {
891 DCHECK(!modal_loop_pump_messages_event_
.get());
892 DCHECK(!dummy_activation_window_
);
893 base::win::ScopedHandle
modal_loop_pump_messages_event(
894 modal_loop_pump_messages_event_handle
);
896 dummy_activation_window_
= dummy_activation_window
;
897 render_view_
->Send(new ViewHostMsg_WindowlessPluginDummyWindowCreated(
898 render_view_
->routing_id(), dummy_activation_window_
));
900 // Bug 25583: this can be null because some "virus scanners" block the
901 // DuplicateHandle call in the plugin process.
902 if (!modal_loop_pump_messages_event
.IsValid())
905 modal_loop_pump_messages_event_
.reset(
906 new base::WaitableEvent(modal_loop_pump_messages_event
.Pass()));
909 void WebPluginDelegateProxy::OnNotifyIMEStatus(int input_type
,
910 const gfx::Rect
& caret_rect
) {
914 ViewHostMsg_TextInputState_Params params
;
915 params
.type
= static_cast<ui::TextInputType
>(input_type
);
916 params
.mode
= ui::TEXT_INPUT_MODE_DEFAULT
;
917 params
.can_compose_inline
= true;
918 render_view_
->Send(new ViewHostMsg_TextInputStateChanged(
919 render_view_
->routing_id(), params
));
921 ViewHostMsg_SelectionBounds_Params bounds_params
;
922 bounds_params
.anchor_rect
= bounds_params
.focus_rect
= caret_rect
;
923 bounds_params
.anchor_dir
= bounds_params
.focus_dir
=
924 blink::WebTextDirectionLeftToRight
;
925 bounds_params
.is_anchor_first
= true;
926 render_view_
->Send(new ViewHostMsg_SelectionBoundsChanged(
927 render_view_
->routing_id(),
932 void WebPluginDelegateProxy::OnCancelResource(int id
) {
934 plugin_
->CancelResource(id
);
937 void WebPluginDelegateProxy::OnInvalidateRect(const gfx::Rect
& rect
) {
941 // Clip the invalidation rect to the plugin bounds; the plugin may have been
942 // resized since the invalidate message was sent.
943 gfx::Rect clipped_rect
=
944 gfx::IntersectRects(rect
, gfx::Rect(plugin_rect_
.size()));
946 invalidate_pending_
= true;
947 // The plugin is blocked on the renderer because the invalidate message it has
948 // sent us is synchronous, so we can use buffer flipping here if the caller
950 UpdateFrontBuffer(clipped_rect
, true);
951 plugin_
->InvalidateRect(clipped_rect
);
954 void WebPluginDelegateProxy::OnGetWindowScriptNPObject(
955 int route_id
, bool* success
) {
957 NPObject
* npobject
= NULL
;
959 npobject
= plugin_
->GetWindowScriptNPObject();
964 // The stub will delete itself when the proxy tells it that it's released, or
965 // otherwise when the channel is closed.
966 new NPObjectStub(npobject
, channel_host_
.get(), route_id
, 0, page_url_
);
970 void WebPluginDelegateProxy::OnResolveProxy(const GURL
& url
,
972 std::string
* proxy_list
) {
973 *result
= RenderThreadImpl::current()->ResolveProxy(url
, proxy_list
);
976 void WebPluginDelegateProxy::OnGetPluginElement(int route_id
, bool* success
) {
978 NPObject
* npobject
= NULL
;
980 npobject
= plugin_
->GetPluginElement();
984 // The stub will delete itself when the proxy tells it that it's released, or
985 // otherwise when the channel is closed.
987 npobject
, channel_host_
.get(), route_id
, 0, page_url_
);
991 void WebPluginDelegateProxy::OnSetCookie(const GURL
& url
,
992 const GURL
& first_party_for_cookies
,
993 const std::string
& cookie
) {
995 plugin_
->SetCookie(url
, first_party_for_cookies
, cookie
);
998 void WebPluginDelegateProxy::OnGetCookies(const GURL
& url
,
999 const GURL
& first_party_for_cookies
,
1000 std::string
* cookies
) {
1003 *cookies
= plugin_
->GetCookies(url
, first_party_for_cookies
);
1006 void WebPluginDelegateProxy::CopyFromBackBufferToFrontBuffer(
1007 const gfx::Rect
& rect
) {
1008 #if defined(OS_MACOSX)
1009 // Blitting the bits directly is much faster than going through CG, and since
1010 // the goal is just to move the raw pixels between two bitmaps with the same
1011 // pixel format (no compositing, color correction, etc.), it's safe.
1012 const size_t stride
=
1013 skia::PlatformCanvasStrideForWidth(plugin_rect_
.width());
1014 const size_t chunk_size
= 4 * rect
.width();
1015 DCHECK(back_buffer_bitmap() != NULL
);
1016 uint8
* source_data
=
1017 back_buffer_bitmap()->pixels() + rect
.y() * stride
+ 4 * rect
.x();
1018 DCHECK(front_buffer_bitmap() != NULL
);
1019 uint8
* target_data
=
1020 front_buffer_bitmap()->pixels() + rect
.y() * stride
+ 4 * rect
.x();
1021 for (int row
= 0; row
< rect
.height(); ++row
) {
1022 memcpy(target_data
, source_data
, chunk_size
);
1023 source_data
+= stride
;
1024 target_data
+= stride
;
1027 BlitCanvasToCanvas(front_buffer_canvas(),
1029 back_buffer_canvas(),
1034 void WebPluginDelegateProxy::UpdateFrontBuffer(
1035 const gfx::Rect
& rect
,
1036 bool allow_buffer_flipping
) {
1037 if (!front_buffer_canvas()) {
1042 // If SendUpdateGeometry() would block on the plugin process then we don't
1043 // want to use buffer flipping at all since it would add extra locking.
1044 // (Alternatively we could probably safely use async updates for buffer
1045 // flipping all the time since the size is not changing.)
1046 if (UseSynchronousGeometryUpdates()) {
1047 allow_buffer_flipping
= false;
1051 // Plugin has just painted "rect" into the back-buffer, so the front-buffer
1052 // no longer holds the latest content for that rectangle.
1053 front_buffer_diff_
.Subtract(rect
);
1054 if (allow_buffer_flipping
&& front_buffer_diff_
.IsEmpty()) {
1055 // Back-buffer contains the latest content for all areas; simply flip
1057 front_buffer_index_
= back_buffer_index();
1058 SendUpdateGeometry(false);
1059 // The front-buffer now holds newer content for this region than the
1061 front_buffer_diff_
= rect
;
1063 // Back-buffer contains the latest content for "rect" but the front-buffer
1064 // contains the latest content for some other areas (or buffer flipping not
1065 // allowed); fall back to copying the data.
1066 CopyFromBackBufferToFrontBuffer(rect
);
1068 transport_store_painted_
.Union(rect
);
1071 void WebPluginDelegateProxy::OnHandleURLRequest(
1072 const PluginHostMsg_URLRequest_Params
& params
) {
1073 const char* data
= NULL
;
1074 if (params
.buffer
.size())
1075 data
= ¶ms
.buffer
[0];
1077 const char* target
= NULL
;
1078 if (params
.target
.length())
1079 target
= params
.target
.c_str();
1081 plugin_
->HandleURLRequest(
1082 params
.url
.c_str(), params
.method
.c_str(), target
, data
,
1083 static_cast<unsigned int>(params
.buffer
.size()), params
.notify_id
,
1084 params
.popups_allowed
, params
.notify_redirects
);
1087 WebPluginResourceClient
* WebPluginDelegateProxy::CreateResourceClient(
1088 unsigned long resource_id
, const GURL
& url
, int notify_id
) {
1089 if (!channel_host_
.get())
1092 ResourceClientProxy
* proxy
=
1093 new ResourceClientProxy(channel_host_
.get(), instance_id_
);
1094 proxy
->Initialize(resource_id
, url
, notify_id
);
1098 WebPluginResourceClient
* WebPluginDelegateProxy::CreateSeekableResourceClient(
1099 unsigned long resource_id
, int range_request_id
) {
1100 if (!channel_host_
.get())
1103 ResourceClientProxy
* proxy
=
1104 new ResourceClientProxy(channel_host_
.get(), instance_id_
);
1105 proxy
->InitializeForSeekableStream(resource_id
, range_request_id
);
1109 void WebPluginDelegateProxy::FetchURL(unsigned long resource_id
,
1112 const GURL
& first_party_for_cookies
,
1113 const std::string
& method
,
1116 const Referrer
& referrer
,
1117 bool notify_redirects
,
1118 bool is_plugin_src_load
,
1120 int render_frame_id
,
1121 int render_view_id
) {
1122 PluginMsg_FetchURL_Params params
;
1123 params
.resource_id
= resource_id
;
1124 params
.notify_id
= notify_id
;
1126 params
.first_party_for_cookies
= first_party_for_cookies
;
1127 params
.method
= method
;
1129 params
.post_data
.resize(len
);
1130 memcpy(¶ms
.post_data
.front(), buf
, len
);
1132 params
.referrer
= referrer
.url
;
1133 params
.referrer_policy
= referrer
.policy
;
1134 params
.notify_redirect
= notify_redirects
;
1135 params
.is_plugin_src_load
= is_plugin_src_load
;
1136 params
.render_frame_id
= render_frame_id
;
1137 Send(new PluginMsg_FetchURL(instance_id_
, params
));
1140 #if defined(OS_MACOSX)
1141 void WebPluginDelegateProxy::OnFocusChanged(bool focused
) {
1143 render_view_
->PluginFocusChanged(focused
, instance_id_
);
1146 void WebPluginDelegateProxy::OnStartIme() {
1148 render_view_
->StartPluginIme();
1152 gfx::PluginWindowHandle
WebPluginDelegateProxy::GetPluginWindowHandle() {
1156 void WebPluginDelegateProxy::OnCancelDocumentLoad() {
1157 plugin_
->CancelDocumentLoad();
1160 void WebPluginDelegateProxy::OnInitiateHTTPRangeRequest(
1161 const std::string
& url
,
1162 const std::string
& range_info
,
1163 int range_request_id
) {
1164 plugin_
->InitiateHTTPRangeRequest(
1165 url
.c_str(), range_info
.c_str(), range_request_id
);
1168 void WebPluginDelegateProxy::OnDidStartLoading() {
1169 plugin_
->DidStartLoading();
1172 void WebPluginDelegateProxy::OnDidStopLoading() {
1173 plugin_
->DidStopLoading();
1176 void WebPluginDelegateProxy::OnDeferResourceLoading(unsigned long resource_id
,
1178 plugin_
->SetDeferResourceLoading(resource_id
, defer
);
1181 #if defined(OS_MACOSX)
1182 void WebPluginDelegateProxy::OnAcceleratedPluginEnabledRendering() {
1183 uses_compositor_
= true;
1184 OnSetWindow(gfx::kNullPluginWindow
);
1187 void WebPluginDelegateProxy::OnAcceleratedPluginAllocatedIOSurface(
1190 uint32 surface_id
) {
1192 plugin_
->AcceleratedPluginAllocatedIOSurface(width
, height
, surface_id
);
1195 void WebPluginDelegateProxy::OnAcceleratedPluginSwappedIOSurface() {
1197 plugin_
->AcceleratedPluginSwappedIOSurface();
1202 bool WebPluginDelegateProxy::UseSynchronousGeometryUpdates() {
1203 // Need to update geometry synchronously with WMP, otherwise if a site
1204 // scripts the plugin to start playing while it's in the middle of handling
1205 // an update geometry message, videos don't play. See urls in bug 20260.
1206 if (info_
.name
.find(base::ASCIIToUTF16("Windows Media Player")) !=
1207 base::string16::npos
)
1210 // The move networks plugin needs to be informed of geometry updates
1212 std::vector
<WebPluginMimeType
>::iterator index
;
1213 for (index
= info_
.mime_types
.begin(); index
!= info_
.mime_types
.end();
1215 if (index
->mime_type
== "application/x-vnd.moveplayer.qm" ||
1216 index
->mime_type
== "application/x-vnd.moveplay2.qm" ||
1217 index
->mime_type
== "application/x-vnd.movenetworks.qm" ||
1218 index
->mime_type
== "application/x-vnd.mnplayer.qm") {
1226 void WebPluginDelegateProxy::OnURLRedirectResponse(bool allow
,
1231 plugin_
->URLRedirectResponse(allow
, resource_id
);
1234 void WebPluginDelegateProxy::OnCheckIfRunInsecureContent(const GURL
& url
,
1236 *result
= plugin_
->CheckIfRunInsecureContent(url
);
1239 } // namespace content