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 if (!channel_host
.get()) {
327 LOG(ERROR
) << "Couldn't get PluginChannelHost";
330 #if defined(OS_MACOSX)
331 track_nested_removes
.reset();
335 // TODO(bauerb): Debugging for http://crbug.com/141055.
336 ScopedLogLevel
log_level(-2); // Equivalent to --v=2
337 result
= channel_host
->Send(new PluginMsg_CreateInstance(
338 mime_type_
, &instance_id
));
340 LOG(ERROR
) << "Couldn't send PluginMsg_CreateInstance";
346 // Failed too often, give up.
350 channel_host_
= channel_host
;
351 instance_id_
= instance_id
;
353 channel_host_
->AddRoute(instance_id_
, this, NULL
);
355 // Inform the channel of the mapping between our instance-Id and dummy NPP
356 // identifier, for use in object ownership tracking.
357 channel_host_
->AddMappingForNPObjectOwner(instance_id_
, GetPluginNPP());
359 // Now tell the PluginInstance in the plugin process to initialize.
360 PluginMsg_Init_Params params
;
362 params
.page_url
= page_url_
;
363 params
.arg_names
= arg_names
;
364 params
.arg_values
= arg_values
;
365 params
.host_render_view_routing_id
= render_view_
->routing_id();
366 params
.load_manually
= load_manually
;
369 Send(new PluginMsg_Init(instance_id_
, params
, &transparent_
, &result
));
372 LOG(WARNING
) << "PluginMsg_Init returned false";
374 render_view_
->RegisterPluginDelegate(this);
379 bool WebPluginDelegateProxy::Send(IPC::Message
* msg
) {
380 if (!channel_host_
.get()) {
381 DLOG(WARNING
) << "dropping message because channel host is null";
386 return channel_host_
->Send(msg
);
389 void WebPluginDelegateProxy::SendJavaScriptStream(const GURL
& url
,
390 const std::string
& result
,
393 Send(new PluginMsg_SendJavaScriptStream(
394 instance_id_
, url
, result
, success
, notify_id
));
397 void WebPluginDelegateProxy::DidReceiveManualResponse(
398 const GURL
& url
, const std::string
& mime_type
,
399 const std::string
& headers
, uint32 expected_length
,
400 uint32 last_modified
) {
401 PluginMsg_DidReceiveResponseParams params
;
403 params
.mime_type
= mime_type
;
404 params
.headers
= headers
;
405 params
.expected_length
= expected_length
;
406 params
.last_modified
= last_modified
;
407 Send(new PluginMsg_DidReceiveManualResponse(instance_id_
, url
, params
));
410 void WebPluginDelegateProxy::DidReceiveManualData(const char* buffer
,
412 DCHECK_GT(length
, 0);
413 std::vector
<char> data
;
414 data
.resize(static_cast<size_t>(length
));
415 memcpy(&data
.front(), buffer
, length
);
416 Send(new PluginMsg_DidReceiveManualData(instance_id_
, data
));
419 void WebPluginDelegateProxy::DidFinishManualLoading() {
420 Send(new PluginMsg_DidFinishManualLoading(instance_id_
));
423 void WebPluginDelegateProxy::DidManualLoadFail() {
424 Send(new PluginMsg_DidManualLoadFail(instance_id_
));
427 bool WebPluginDelegateProxy::OnMessageReceived(const IPC::Message
& msg
) {
428 GetContentClient()->SetActiveURL(page_url_
);
431 IPC_BEGIN_MESSAGE_MAP(WebPluginDelegateProxy
, msg
)
432 IPC_MESSAGE_HANDLER(PluginHostMsg_SetWindow
, OnSetWindow
)
433 IPC_MESSAGE_HANDLER(PluginHostMsg_CancelResource
, OnCancelResource
)
434 IPC_MESSAGE_HANDLER(PluginHostMsg_InvalidateRect
, OnInvalidateRect
)
435 IPC_MESSAGE_HANDLER(PluginHostMsg_GetWindowScriptNPObject
,
436 OnGetWindowScriptNPObject
)
437 IPC_MESSAGE_HANDLER(PluginHostMsg_GetPluginElement
, OnGetPluginElement
)
438 IPC_MESSAGE_HANDLER(PluginHostMsg_ResolveProxy
, OnResolveProxy
)
439 IPC_MESSAGE_HANDLER(PluginHostMsg_SetCookie
, OnSetCookie
)
440 IPC_MESSAGE_HANDLER(PluginHostMsg_GetCookies
, OnGetCookies
)
441 IPC_MESSAGE_HANDLER(PluginHostMsg_URLRequest
, OnHandleURLRequest
)
442 IPC_MESSAGE_HANDLER(PluginHostMsg_CancelDocumentLoad
, OnCancelDocumentLoad
)
443 IPC_MESSAGE_HANDLER(PluginHostMsg_InitiateHTTPRangeRequest
,
444 OnInitiateHTTPRangeRequest
)
445 IPC_MESSAGE_HANDLER(PluginHostMsg_DidStartLoading
, OnDidStartLoading
)
446 IPC_MESSAGE_HANDLER(PluginHostMsg_DidStopLoading
, OnDidStopLoading
)
447 IPC_MESSAGE_HANDLER(PluginHostMsg_DeferResourceLoading
,
448 OnDeferResourceLoading
)
449 IPC_MESSAGE_HANDLER(PluginHostMsg_URLRedirectResponse
,
450 OnURLRedirectResponse
)
451 IPC_MESSAGE_HANDLER(PluginHostMsg_CheckIfRunInsecureContent
,
452 OnCheckIfRunInsecureContent
)
454 IPC_MESSAGE_HANDLER(PluginHostMsg_SetWindowlessData
, OnSetWindowlessData
)
455 IPC_MESSAGE_HANDLER(PluginHostMsg_NotifyIMEStatus
, OnNotifyIMEStatus
)
457 #if defined(OS_MACOSX)
458 IPC_MESSAGE_HANDLER(PluginHostMsg_FocusChanged
,
460 IPC_MESSAGE_HANDLER(PluginHostMsg_StartIme
,
462 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginEnabledRendering
,
463 OnAcceleratedPluginEnabledRendering
)
464 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginAllocatedIOSurface
,
465 OnAcceleratedPluginAllocatedIOSurface
)
466 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginSwappedIOSurface
,
467 OnAcceleratedPluginSwappedIOSurface
)
469 IPC_MESSAGE_UNHANDLED(handled
= false)
470 IPC_END_MESSAGE_MAP()
475 void WebPluginDelegateProxy::OnChannelError() {
478 // The actual WebPluginDelegate never got a chance to tell the WebPlugin
479 // its window was going away. Do it on its behalf.
482 plugin_
->Invalidate();
484 if (channel_host_
.get() && !channel_host_
->expecting_shutdown()) {
485 render_view_
->GetMainRenderFrame()->PluginCrashed(
486 info_
.path
, channel_host_
->peer_pid());
489 #if defined(OS_MACOSX) || defined(OS_WIN)
490 // Ensure that the renderer doesn't think the plugin still has focus.
492 render_view_
->PluginFocusChanged(false, instance_id_
);
496 static void CopySharedMemoryHandleForMessage(
497 const base::SharedMemoryHandle
& handle_in
,
498 base::SharedMemoryHandle
* handle_out
,
499 base::ProcessId peer_pid
) {
500 #if defined(OS_POSIX)
501 *handle_out
= base::SharedMemory::DuplicateHandle(handle_in
);
502 #elif defined(OS_WIN)
503 // On Windows we need to duplicate the handle for the plugin process.
505 BrokerDuplicateHandle(handle_in
, peer_pid
, handle_out
,
506 FILE_MAP_READ
| FILE_MAP_WRITE
, 0);
507 DCHECK(*handle_out
!= NULL
);
509 #error Shared memory copy not implemented.
513 void WebPluginDelegateProxy::SendUpdateGeometry(
514 bool bitmaps_changed
) {
515 if (!channel_host_
.get())
518 PluginMsg_UpdateGeometry_Param param
;
519 param
.window_rect
= plugin_rect_
;
520 param
.clip_rect
= clip_rect_
;
521 param
.windowless_buffer0
= base::SharedMemory::NULLHandle();
522 param
.windowless_buffer1
= base::SharedMemory::NULLHandle();
523 param
.windowless_buffer_index
= back_buffer_index();
525 #if defined(OS_POSIX)
526 // If we're using POSIX mmap'd TransportDIBs, sending the handle across
527 // IPC establishes a new mapping rather than just sending a window ID,
528 // so only do so if we've actually changed the shared memory bitmaps.
532 if (transport_stores_
[0].bitmap
)
533 CopySharedMemoryHandleForMessage(
534 transport_stores_
[0].bitmap
->shared_memory()->handle(),
535 ¶m
.windowless_buffer0
, channel_host_
->peer_pid());
537 if (transport_stores_
[1].bitmap
)
538 CopySharedMemoryHandleForMessage(
539 transport_stores_
[1].bitmap
->shared_memory()->handle(),
540 ¶m
.windowless_buffer1
, channel_host_
->peer_pid());
545 if (UseSynchronousGeometryUpdates()) {
546 msg
= new PluginMsg_UpdateGeometrySync(instance_id_
, param
);
550 msg
= new PluginMsg_UpdateGeometry(instance_id_
, param
);
551 msg
->set_unblock(true);
557 void WebPluginDelegateProxy::UpdateGeometry(const gfx::Rect
& window_rect
,
558 const gfx::Rect
& clip_rect
) {
559 // window_rect becomes either a window in native windowing system
560 // coords, or a backing buffer. In either case things will go bad
561 // if the rectangle is very large.
562 if (window_rect
.width() < 0 || window_rect
.width() > kMaxPluginSideLength
||
563 window_rect
.height() < 0 || window_rect
.height() > kMaxPluginSideLength
||
564 // We know this won't overflow due to above checks.
565 static_cast<uint32
>(window_rect
.width()) *
566 static_cast<uint32
>(window_rect
.height()) > kMaxPluginSize
) {
570 plugin_rect_
= window_rect
;
571 clip_rect_
= clip_rect
;
573 bool bitmaps_changed
= false;
575 if (uses_shared_bitmaps_
) {
576 if (!front_buffer_canvas() ||
577 (window_rect
.width() != front_buffer_canvas()->getDevice()->width() ||
578 window_rect
.height() != front_buffer_canvas()->getDevice()->height()))
580 bitmaps_changed
= true;
582 // Create a shared memory section that the plugin paints into
584 ResetWindowlessBitmaps();
585 if (!window_rect
.IsEmpty()) {
586 if (!CreateSharedBitmap(&transport_stores_
[0].bitmap
,
587 &transport_stores_
[0].canvas
) ||
588 !CreateSharedBitmap(&transport_stores_
[1].bitmap
,
589 &transport_stores_
[1].canvas
)) {
591 ResetWindowlessBitmaps();
598 SendUpdateGeometry(bitmaps_changed
);
601 void WebPluginDelegateProxy::ResetWindowlessBitmaps() {
602 transport_stores_
[0].bitmap
.reset();
603 transport_stores_
[1].bitmap
.reset();
605 transport_stores_
[0].canvas
.reset();
606 transport_stores_
[1].canvas
.reset();
607 transport_store_painted_
= gfx::Rect();
608 front_buffer_diff_
= gfx::Rect();
612 static size_t BitmapSizeForPluginRect(const gfx::Rect
& plugin_rect
) {
613 const size_t stride
=
614 skia::PlatformCanvasStrideForWidth(plugin_rect
.width());
615 return stride
* plugin_rect
.height();
618 bool WebPluginDelegateProxy::CreateLocalBitmap(
619 std::vector
<uint8
>* memory
,
620 scoped_ptr
<skia::PlatformCanvas
>* canvas
) {
621 const size_t size
= BitmapSizeForPluginRect(plugin_rect_
);
622 memory
->resize(size
);
623 if (memory
->size() != size
)
625 canvas
->reset(skia::CreatePlatformCanvas(
626 plugin_rect_
.width(), plugin_rect_
.height(), true, &((*memory
)[0]),
627 skia::CRASH_ON_FAILURE
));
632 bool WebPluginDelegateProxy::CreateSharedBitmap(
633 scoped_ptr
<SharedMemoryBitmap
>* memory
,
634 scoped_ptr
<skia::PlatformCanvas
>* canvas
) {
635 *memory
= ChildThreadImpl::current()
636 ->shared_bitmap_manager()
637 ->AllocateSharedMemoryBitmap(plugin_rect_
.size());
640 DCHECK((*memory
)->shared_memory());
641 #if defined(OS_POSIX)
642 canvas
->reset(skia::CreatePlatformCanvas(
643 plugin_rect_
.width(), plugin_rect_
.height(), true, (*memory
)->pixels(),
644 skia::RETURN_NULL_ON_FAILURE
));
646 canvas
->reset(skia::CreatePlatformCanvas(
647 plugin_rect_
.width(), plugin_rect_
.height(), true,
648 (*memory
)->shared_memory()->handle(), skia::RETURN_NULL_ON_FAILURE
));
650 return !!canvas
->get();
653 void WebPluginDelegateProxy::Paint(SkCanvas
* canvas
,
654 const gfx::Rect
& damaged_rect
) {
655 // Limit the damaged rectangle to whatever is contained inside the plugin
656 // rectangle, as that's the rectangle that we'll actually draw.
657 gfx::Rect rect
= gfx::IntersectRects(damaged_rect
, plugin_rect_
);
659 // If the plugin is no longer connected (channel crashed) draw a crashed
661 if (!channel_host_
.get() || !channel_host_
->channel_valid()) {
662 // Lazily load the sad plugin image.
664 sad_plugin_
= GetContentClient()->renderer()->GetSadPluginBitmap();
666 PaintSadPlugin(canvas
, plugin_rect_
, *sad_plugin_
);
670 if (!uses_shared_bitmaps_
)
673 // We got a paint before the plugin's coordinates, so there's no buffer to
675 if (!front_buffer_canvas())
678 gfx::Rect offset_rect
= rect
;
679 offset_rect
.Offset(-plugin_rect_
.x(), -plugin_rect_
.y());
681 // transport_store_painted_ is really a bounding box, so in principle this
682 // check could falsely indicate that we don't need to paint offset_rect, but
683 // in practice it works fine.
684 if (!transport_store_painted_
.Contains(offset_rect
)) {
685 Send(new PluginMsg_Paint(instance_id_
, offset_rect
));
686 // Since the plugin is not blocked on the renderer in this context, there is
687 // a chance that it will begin repainting the back-buffer before we complete
688 // capturing the data. Buffer flipping would increase that risk because
689 // geometry update is asynchronous, so we don't want to use buffer flipping
691 UpdateFrontBuffer(offset_rect
, false);
694 const SkBitmap
& bitmap
=
695 front_buffer_canvas()->getDevice()->accessBitmap(false);
697 paint
.setXfermodeMode(
698 transparent_
? SkXfermode::kSrcATop_Mode
: SkXfermode::kSrc_Mode
);
699 SkRect src_rect
= gfx::RectToSkRect(offset_rect
);
700 canvas
->drawBitmapRect(bitmap
,
702 gfx::RectToSkRect(rect
),
705 if (invalidate_pending_
) {
706 // Only send the PaintAck message if this paint is in response to an
707 // invalidate from the plugin, since this message acts as an access token
708 // to ensure only one process is using the shared bitmap at a time.
709 invalidate_pending_
= false;
710 Send(new PluginMsg_DidPaint(instance_id_
));
714 NPObject
* WebPluginDelegateProxy::GetPluginScriptableObject() {
716 return WebBindings::retainObject(npobject_
);
718 if (!channel_host_
.get())
721 int route_id
= MSG_ROUTING_NONE
;
722 Send(new PluginMsg_GetPluginScriptableObject(instance_id_
, &route_id
));
723 if (route_id
== MSG_ROUTING_NONE
)
726 if (!channel_host_
.get())
729 npobject_
= NPObjectProxy::Create(
730 channel_host_
.get(), route_id
, 0, page_url_
, GetPluginNPP());
732 return WebBindings::retainObject(npobject_
);
735 NPP
WebPluginDelegateProxy::GetPluginNPP() {
736 // Return a dummy NPP for WebKit to use to identify this plugin.
740 bool WebPluginDelegateProxy::GetFormValue(base::string16
* value
) {
741 bool success
= false;
742 Send(new PluginMsg_GetFormValue(instance_id_
, value
, &success
));
746 void WebPluginDelegateProxy::DidFinishLoadWithReason(
747 const GURL
& url
, NPReason reason
, int notify_id
) {
748 Send(new PluginMsg_DidFinishLoadWithReason(
749 instance_id_
, url
, reason
, notify_id
));
752 void WebPluginDelegateProxy::SetFocus(bool focused
) {
753 Send(new PluginMsg_SetFocus(instance_id_
, focused
));
756 render_view_
->PluginFocusChanged(focused
, instance_id_
);
760 bool WebPluginDelegateProxy::HandleInputEvent(
761 const WebInputEvent
& event
,
762 WebCursor::CursorInfo
* cursor_info
) {
763 bool handled
= false;
765 // A windowless plugin can enter a modal loop in the context of a
766 // NPP_HandleEvent call, in which case we need to pump messages to
767 // the plugin. We pass of the corresponding event handle to the
768 // plugin process, which is set if the plugin does enter a modal loop.
769 IPC::SyncMessage
* message
= new PluginMsg_HandleInputEvent(
770 instance_id_
, &event
, &handled
, &cursor
);
771 message
->set_pump_messages_event(modal_loop_pump_messages_event_
.get());
776 int WebPluginDelegateProxy::GetProcessId() {
777 return channel_host_
->peer_pid();
780 void WebPluginDelegateProxy::SetContentAreaFocus(bool has_focus
) {
781 IPC::Message
* msg
= new PluginMsg_SetContentAreaFocus(instance_id_
,
783 // Make sure focus events are delivered in the right order relative to
784 // sync messages they might interact with (Paint, HandleEvent, etc.).
785 msg
->set_unblock(true);
790 void WebPluginDelegateProxy::ImeCompositionUpdated(
791 const base::string16
& text
,
792 const std::vector
<int>& clauses
,
793 const std::vector
<int>& target
,
796 // Dispatch the raw IME data if this plugin is the focused one.
797 if (instance_id_
!= plugin_id
)
800 IPC::Message
* msg
= new PluginMsg_ImeCompositionUpdated(instance_id_
,
801 text
, clauses
, target
, cursor_position
);
802 msg
->set_unblock(true);
806 void WebPluginDelegateProxy::ImeCompositionCompleted(const base::string16
& text
,
808 // Dispatch the IME text if this plugin is the focused one.
809 if (instance_id_
!= plugin_id
)
812 IPC::Message
* msg
= new PluginMsg_ImeCompositionCompleted(instance_id_
, text
);
813 msg
->set_unblock(true);
818 #if defined(OS_MACOSX)
819 void WebPluginDelegateProxy::SetWindowFocus(bool window_has_focus
) {
820 IPC::Message
* msg
= new PluginMsg_SetWindowFocus(instance_id_
,
822 // Make sure focus events are delivered in the right order relative to
823 // sync messages they might interact with (Paint, HandleEvent, etc.).
824 msg
->set_unblock(true);
828 void WebPluginDelegateProxy::SetContainerVisibility(bool is_visible
) {
831 gfx::Rect window_frame
= render_view_
->rootWindowRect();
832 gfx::Rect view_frame
= render_view_
->windowRect();
833 blink::WebView
* webview
= render_view_
->webview();
834 msg
= new PluginMsg_ContainerShown(instance_id_
, window_frame
, view_frame
,
835 webview
&& webview
->isActive());
837 msg
= new PluginMsg_ContainerHidden(instance_id_
);
839 // Make sure visibility events are delivered in the right order relative to
840 // sync messages they might interact with (Paint, HandleEvent, etc.).
841 msg
->set_unblock(true);
845 void WebPluginDelegateProxy::WindowFrameChanged(gfx::Rect window_frame
,
846 gfx::Rect view_frame
) {
847 IPC::Message
* msg
= new PluginMsg_WindowFrameChanged(instance_id_
,
850 // Make sure frame events are delivered in the right order relative to
851 // sync messages they might interact with (e.g., HandleEvent).
852 msg
->set_unblock(true);
855 void WebPluginDelegateProxy::ImeCompositionCompleted(const base::string16
& text
,
857 // If the message isn't intended for this plugin, there's nothing to do.
858 if (instance_id_
!= plugin_id
)
861 IPC::Message
* msg
= new PluginMsg_ImeCompositionCompleted(instance_id_
,
863 // Order relative to other key events is important.
864 msg
->set_unblock(true);
869 void WebPluginDelegateProxy::OnSetWindow(gfx::PluginWindowHandle window
) {
870 #if defined(OS_MACOSX)
871 uses_shared_bitmaps_
= !window
&& !uses_compositor_
;
873 uses_shared_bitmaps_
= !window
;
877 plugin_
->SetWindow(window
);
880 void WebPluginDelegateProxy::WillDestroyWindow() {
882 plugin_
->WillDestroyWindow(window_
);
883 window_
= gfx::kNullPluginWindow
;
887 void WebPluginDelegateProxy::OnSetWindowlessData(
888 HANDLE modal_loop_pump_messages_event_handle
,
889 gfx::NativeViewId dummy_activation_window
) {
890 DCHECK(!modal_loop_pump_messages_event_
.get());
891 DCHECK(!dummy_activation_window_
);
892 base::win::ScopedHandle
modal_loop_pump_messages_event(
893 modal_loop_pump_messages_event_handle
);
895 dummy_activation_window_
= dummy_activation_window
;
896 render_view_
->Send(new ViewHostMsg_WindowlessPluginDummyWindowCreated(
897 render_view_
->routing_id(), dummy_activation_window_
));
899 // Bug 25583: this can be null because some "virus scanners" block the
900 // DuplicateHandle call in the plugin process.
901 if (!modal_loop_pump_messages_event
.IsValid())
904 modal_loop_pump_messages_event_
.reset(
905 new base::WaitableEvent(modal_loop_pump_messages_event
.Pass()));
908 void WebPluginDelegateProxy::OnNotifyIMEStatus(int input_type
,
909 const gfx::Rect
& caret_rect
) {
913 ViewHostMsg_TextInputState_Params params
;
914 params
.type
= static_cast<ui::TextInputType
>(input_type
);
915 params
.mode
= ui::TEXT_INPUT_MODE_DEFAULT
;
916 params
.can_compose_inline
= true;
917 render_view_
->Send(new ViewHostMsg_TextInputStateChanged(
918 render_view_
->routing_id(), params
));
920 ViewHostMsg_SelectionBounds_Params bounds_params
;
921 bounds_params
.anchor_rect
= bounds_params
.focus_rect
= caret_rect
;
922 bounds_params
.anchor_dir
= bounds_params
.focus_dir
=
923 blink::WebTextDirectionLeftToRight
;
924 bounds_params
.is_anchor_first
= true;
925 render_view_
->Send(new ViewHostMsg_SelectionBoundsChanged(
926 render_view_
->routing_id(),
931 void WebPluginDelegateProxy::OnCancelResource(int id
) {
933 plugin_
->CancelResource(id
);
936 void WebPluginDelegateProxy::OnInvalidateRect(const gfx::Rect
& rect
) {
940 // Clip the invalidation rect to the plugin bounds; the plugin may have been
941 // resized since the invalidate message was sent.
942 gfx::Rect clipped_rect
=
943 gfx::IntersectRects(rect
, gfx::Rect(plugin_rect_
.size()));
945 invalidate_pending_
= true;
946 // The plugin is blocked on the renderer because the invalidate message it has
947 // sent us is synchronous, so we can use buffer flipping here if the caller
949 UpdateFrontBuffer(clipped_rect
, true);
950 plugin_
->InvalidateRect(clipped_rect
);
953 void WebPluginDelegateProxy::OnGetWindowScriptNPObject(
954 int route_id
, bool* success
) {
956 NPObject
* npobject
= NULL
;
958 npobject
= plugin_
->GetWindowScriptNPObject();
963 // The stub will delete itself when the proxy tells it that it's released, or
964 // otherwise when the channel is closed.
965 new NPObjectStub(npobject
, channel_host_
.get(), route_id
, 0, page_url_
);
969 void WebPluginDelegateProxy::OnResolveProxy(const GURL
& url
,
971 std::string
* proxy_list
) {
972 *result
= RenderThreadImpl::current()->ResolveProxy(url
, proxy_list
);
975 void WebPluginDelegateProxy::OnGetPluginElement(int route_id
, bool* success
) {
977 NPObject
* npobject
= NULL
;
979 npobject
= plugin_
->GetPluginElement();
983 // The stub will delete itself when the proxy tells it that it's released, or
984 // otherwise when the channel is closed.
986 npobject
, channel_host_
.get(), route_id
, 0, page_url_
);
990 void WebPluginDelegateProxy::OnSetCookie(const GURL
& url
,
991 const GURL
& first_party_for_cookies
,
992 const std::string
& cookie
) {
994 plugin_
->SetCookie(url
, first_party_for_cookies
, cookie
);
997 void WebPluginDelegateProxy::OnGetCookies(const GURL
& url
,
998 const GURL
& first_party_for_cookies
,
999 std::string
* cookies
) {
1002 *cookies
= plugin_
->GetCookies(url
, first_party_for_cookies
);
1005 void WebPluginDelegateProxy::CopyFromBackBufferToFrontBuffer(
1006 const gfx::Rect
& rect
) {
1007 #if defined(OS_MACOSX)
1008 // Blitting the bits directly is much faster than going through CG, and since
1009 // the goal is just to move the raw pixels between two bitmaps with the same
1010 // pixel format (no compositing, color correction, etc.), it's safe.
1011 const size_t stride
=
1012 skia::PlatformCanvasStrideForWidth(plugin_rect_
.width());
1013 const size_t chunk_size
= 4 * rect
.width();
1014 DCHECK(back_buffer_bitmap() != NULL
);
1015 uint8
* source_data
=
1016 back_buffer_bitmap()->pixels() + rect
.y() * stride
+ 4 * rect
.x();
1017 DCHECK(front_buffer_bitmap() != NULL
);
1018 uint8
* target_data
=
1019 front_buffer_bitmap()->pixels() + rect
.y() * stride
+ 4 * rect
.x();
1020 for (int row
= 0; row
< rect
.height(); ++row
) {
1021 memcpy(target_data
, source_data
, chunk_size
);
1022 source_data
+= stride
;
1023 target_data
+= stride
;
1026 BlitCanvasToCanvas(front_buffer_canvas(),
1028 back_buffer_canvas(),
1033 void WebPluginDelegateProxy::UpdateFrontBuffer(
1034 const gfx::Rect
& rect
,
1035 bool allow_buffer_flipping
) {
1036 if (!front_buffer_canvas()) {
1041 // If SendUpdateGeometry() would block on the plugin process then we don't
1042 // want to use buffer flipping at all since it would add extra locking.
1043 // (Alternatively we could probably safely use async updates for buffer
1044 // flipping all the time since the size is not changing.)
1045 if (UseSynchronousGeometryUpdates()) {
1046 allow_buffer_flipping
= false;
1050 // Plugin has just painted "rect" into the back-buffer, so the front-buffer
1051 // no longer holds the latest content for that rectangle.
1052 front_buffer_diff_
.Subtract(rect
);
1053 if (allow_buffer_flipping
&& front_buffer_diff_
.IsEmpty()) {
1054 // Back-buffer contains the latest content for all areas; simply flip
1056 front_buffer_index_
= back_buffer_index();
1057 SendUpdateGeometry(false);
1058 // The front-buffer now holds newer content for this region than the
1060 front_buffer_diff_
= rect
;
1062 // Back-buffer contains the latest content for "rect" but the front-buffer
1063 // contains the latest content for some other areas (or buffer flipping not
1064 // allowed); fall back to copying the data.
1065 CopyFromBackBufferToFrontBuffer(rect
);
1067 transport_store_painted_
.Union(rect
);
1070 void WebPluginDelegateProxy::OnHandleURLRequest(
1071 const PluginHostMsg_URLRequest_Params
& params
) {
1072 const char* data
= NULL
;
1073 if (params
.buffer
.size())
1074 data
= ¶ms
.buffer
[0];
1076 const char* target
= NULL
;
1077 if (params
.target
.length())
1078 target
= params
.target
.c_str();
1080 plugin_
->HandleURLRequest(
1081 params
.url
.c_str(), params
.method
.c_str(), target
, data
,
1082 static_cast<unsigned int>(params
.buffer
.size()), params
.notify_id
,
1083 params
.popups_allowed
, params
.notify_redirects
);
1086 WebPluginResourceClient
* WebPluginDelegateProxy::CreateResourceClient(
1087 unsigned long resource_id
, const GURL
& url
, int notify_id
) {
1088 if (!channel_host_
.get())
1091 ResourceClientProxy
* proxy
=
1092 new ResourceClientProxy(channel_host_
.get(), instance_id_
);
1093 proxy
->Initialize(resource_id
, url
, notify_id
);
1097 WebPluginResourceClient
* WebPluginDelegateProxy::CreateSeekableResourceClient(
1098 unsigned long resource_id
, int range_request_id
) {
1099 if (!channel_host_
.get())
1102 ResourceClientProxy
* proxy
=
1103 new ResourceClientProxy(channel_host_
.get(), instance_id_
);
1104 proxy
->InitializeForSeekableStream(resource_id
, range_request_id
);
1108 void WebPluginDelegateProxy::FetchURL(unsigned long resource_id
,
1111 const GURL
& first_party_for_cookies
,
1112 const std::string
& method
,
1115 const Referrer
& referrer
,
1116 bool notify_redirects
,
1117 bool is_plugin_src_load
,
1119 int render_frame_id
,
1120 int render_view_id
) {
1121 PluginMsg_FetchURL_Params params
;
1122 params
.resource_id
= resource_id
;
1123 params
.notify_id
= notify_id
;
1125 params
.first_party_for_cookies
= first_party_for_cookies
;
1126 params
.method
= method
;
1128 params
.post_data
.resize(len
);
1129 memcpy(¶ms
.post_data
.front(), buf
, len
);
1131 params
.referrer
= referrer
.url
;
1132 params
.referrer_policy
= referrer
.policy
;
1133 params
.notify_redirect
= notify_redirects
;
1134 params
.is_plugin_src_load
= is_plugin_src_load
;
1135 params
.render_frame_id
= render_frame_id
;
1136 Send(new PluginMsg_FetchURL(instance_id_
, params
));
1139 #if defined(OS_MACOSX)
1140 void WebPluginDelegateProxy::OnFocusChanged(bool focused
) {
1142 render_view_
->PluginFocusChanged(focused
, instance_id_
);
1145 void WebPluginDelegateProxy::OnStartIme() {
1147 render_view_
->StartPluginIme();
1151 gfx::PluginWindowHandle
WebPluginDelegateProxy::GetPluginWindowHandle() {
1155 void WebPluginDelegateProxy::OnCancelDocumentLoad() {
1156 plugin_
->CancelDocumentLoad();
1159 void WebPluginDelegateProxy::OnInitiateHTTPRangeRequest(
1160 const std::string
& url
,
1161 const std::string
& range_info
,
1162 int range_request_id
) {
1163 plugin_
->InitiateHTTPRangeRequest(
1164 url
.c_str(), range_info
.c_str(), range_request_id
);
1167 void WebPluginDelegateProxy::OnDidStartLoading() {
1168 plugin_
->DidStartLoading();
1171 void WebPluginDelegateProxy::OnDidStopLoading() {
1172 plugin_
->DidStopLoading();
1175 void WebPluginDelegateProxy::OnDeferResourceLoading(unsigned long resource_id
,
1177 plugin_
->SetDeferResourceLoading(resource_id
, defer
);
1180 #if defined(OS_MACOSX)
1181 void WebPluginDelegateProxy::OnAcceleratedPluginEnabledRendering() {
1182 uses_compositor_
= true;
1183 OnSetWindow(gfx::kNullPluginWindow
);
1186 void WebPluginDelegateProxy::OnAcceleratedPluginAllocatedIOSurface(
1189 uint32 surface_id
) {
1191 plugin_
->AcceleratedPluginAllocatedIOSurface(width
, height
, surface_id
);
1194 void WebPluginDelegateProxy::OnAcceleratedPluginSwappedIOSurface() {
1196 plugin_
->AcceleratedPluginSwappedIOSurface();
1201 bool WebPluginDelegateProxy::UseSynchronousGeometryUpdates() {
1202 // Need to update geometry synchronously with WMP, otherwise if a site
1203 // scripts the plugin to start playing while it's in the middle of handling
1204 // an update geometry message, videos don't play. See urls in bug 20260.
1205 if (info_
.name
.find(base::ASCIIToUTF16("Windows Media Player")) !=
1206 base::string16::npos
)
1209 // The move networks plugin needs to be informed of geometry updates
1211 std::vector
<WebPluginMimeType
>::iterator index
;
1212 for (index
= info_
.mime_types
.begin(); index
!= info_
.mime_types
.end();
1214 if (index
->mime_type
== "application/x-vnd.moveplayer.qm" ||
1215 index
->mime_type
== "application/x-vnd.moveplay2.qm" ||
1216 index
->mime_type
== "application/x-vnd.movenetworks.qm" ||
1217 index
->mime_type
== "application/x-vnd.mnplayer.qm") {
1225 void WebPluginDelegateProxy::OnURLRedirectResponse(bool allow
,
1230 plugin_
->URLRedirectResponse(allow
, resource_id
);
1233 void WebPluginDelegateProxy::OnCheckIfRunInsecureContent(const GURL
& url
,
1235 *result
= plugin_
->CheckIfRunInsecureContent(url
);
1238 } // namespace content