1 // Copyright 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/browser_plugin/browser_plugin.h"
7 #include "base/command_line.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "content/common/browser_plugin/browser_plugin_constants.h"
12 #include "content/common/browser_plugin/browser_plugin_messages.h"
13 #include "content/common/view_messages.h"
14 #include "content/public/common/content_client.h"
15 #include "content/public/common/content_switches.h"
16 #include "content/public/renderer/browser_plugin_delegate.h"
17 #include "content/public/renderer/content_renderer_client.h"
18 #include "content/renderer/browser_plugin/browser_plugin_manager.h"
19 #include "content/renderer/child_frame_compositing_helper.h"
20 #include "content/renderer/cursor_utils.h"
21 #include "content/renderer/drop_data_builder.h"
22 #include "content/renderer/render_thread_impl.h"
23 #include "content/renderer/sad_plugin.h"
24 #include "third_party/WebKit/public/platform/WebRect.h"
25 #include "third_party/WebKit/public/web/WebDocument.h"
26 #include "third_party/WebKit/public/web/WebElement.h"
27 #include "third_party/WebKit/public/web/WebInputEvent.h"
28 #include "third_party/WebKit/public/web/WebLocalFrame.h"
29 #include "third_party/WebKit/public/web/WebPluginContainer.h"
30 #include "third_party/WebKit/public/web/WebView.h"
31 #include "third_party/skia/include/core/SkCanvas.h"
32 #include "ui/events/keycodes/keyboard_codes.h"
34 using blink::WebCanvas
;
35 using blink::WebPluginContainer
;
36 using blink::WebPoint
;
39 using blink::WebVector
;
42 typedef std::map
<blink::WebPluginContainer
*, content::BrowserPlugin
*>
44 static base::LazyInstance
<PluginContainerMap
> g_plugin_container_map
=
45 LAZY_INSTANCE_INITIALIZER
;
51 BrowserPlugin
* BrowserPlugin::GetFromNode(blink::WebNode
& node
) {
52 blink::WebPluginContainer
* container
= node
.pluginContainer();
56 PluginContainerMap
* browser_plugins
= g_plugin_container_map
.Pointer();
57 PluginContainerMap::iterator it
= browser_plugins
->find(container
);
58 return it
== browser_plugins
->end() ? NULL
: it
->second
;
61 BrowserPlugin::BrowserPlugin(RenderViewImpl
* render_view
,
62 blink::WebFrame
* frame
,
63 scoped_ptr
<BrowserPluginDelegate
> delegate
)
65 attach_pending_(false),
66 render_view_(render_view
->AsWeakPtr()),
67 render_view_routing_id_(render_view
->GetRoutingID()),
69 last_device_scale_factor_(GetDeviceScaleFactor()),
71 guest_crashed_(false),
72 plugin_focused_(false),
76 browser_plugin_manager_(render_view
->GetBrowserPluginManager()),
77 browser_plugin_instance_id_(browser_plugin::kInstanceIDNone
),
78 contents_opaque_(true),
79 delegate_(delegate
.Pass()),
80 weak_ptr_factory_(this) {
81 browser_plugin_instance_id_
= browser_plugin_manager()->GetNextInstanceID();
84 delegate_
->SetElementInstanceID(browser_plugin_instance_id_
);
87 BrowserPlugin::~BrowserPlugin() {
88 browser_plugin_manager()->RemoveBrowserPlugin(browser_plugin_instance_id_
);
91 bool BrowserPlugin::OnMessageReceived(const IPC::Message
& message
) {
93 IPC_BEGIN_MESSAGE_MAP(BrowserPlugin
, message
)
94 IPC_MESSAGE_HANDLER(BrowserPluginMsg_Attach_ACK
, OnAttachACK
)
95 IPC_MESSAGE_HANDLER(BrowserPluginMsg_AdvanceFocus
, OnAdvanceFocus
)
96 IPC_MESSAGE_HANDLER_GENERIC(BrowserPluginMsg_CompositorFrameSwapped
,
97 OnCompositorFrameSwapped(message
))
98 IPC_MESSAGE_HANDLER(BrowserPluginMsg_CopyFromCompositingSurface
,
99 OnCopyFromCompositingSurface
)
100 IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestGone
, OnGuestGone
)
101 IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetContentsOpaque
, OnSetContentsOpaque
)
102 IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetCursor
, OnSetCursor
)
103 IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetMouseLock
, OnSetMouseLock
)
104 IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetTooltipText
, OnSetTooltipText
)
105 IPC_MESSAGE_HANDLER(BrowserPluginMsg_ShouldAcceptTouchEvents
,
106 OnShouldAcceptTouchEvents
)
107 IPC_MESSAGE_UNHANDLED(handled
= false)
108 IPC_END_MESSAGE_MAP()
112 void BrowserPlugin::UpdateDOMAttribute(const std::string
& attribute_name
,
113 const base::string16
& attribute_value
) {
117 blink::WebElement element
= container()->element();
118 blink::WebString web_attribute_name
=
119 blink::WebString::fromUTF8(attribute_name
);
120 element
.setAttribute(web_attribute_name
, attribute_value
);
123 void BrowserPlugin::Attach() {
126 guest_crashed_
= false;
127 EnableCompositing(false);
128 if (compositing_helper_
.get()) {
129 compositing_helper_
->OnContainerDestroy();
130 compositing_helper_
= NULL
;
134 // TODO(fsamuel): Add support for reattachment.
135 BrowserPluginHostMsg_Attach_Params attach_params
;
136 attach_params
.focused
= ShouldGuestBeFocused();
137 attach_params
.visible
= visible_
;
138 attach_params
.origin
= plugin_rect().origin();
139 attach_params
.is_full_page_plugin
= false;
141 blink::WebLocalFrame
* frame
= container()->element().document().frame();
142 attach_params
.is_full_page_plugin
=
143 frame
->view()->mainFrame()->document().isPluginDocument();
145 gfx::Size
view_size(width(), height());
146 if (!view_size
.IsEmpty()) {
147 PopulateResizeGuestParameters(view_size
,
148 &attach_params
.resize_guest_params
);
150 browser_plugin_manager()->Send(new BrowserPluginHostMsg_Attach(
151 render_view_routing_id_
,
152 browser_plugin_instance_id_
,
155 attach_pending_
= true;
158 void BrowserPlugin::DidCommitCompositorFrame() {
159 if (compositing_helper_
.get())
160 compositing_helper_
->DidCommitCompositorFrame();
163 void BrowserPlugin::OnAdvanceFocus(int browser_plugin_instance_id
,
165 DCHECK(render_view_
);
166 render_view_
->GetWebView()->advanceFocus(reverse
);
169 void BrowserPlugin::OnAttachACK(int browser_plugin_instance_id
) {
172 attach_pending_
= false;
175 void BrowserPlugin::OnCompositorFrameSwapped(const IPC::Message
& message
) {
176 BrowserPluginMsg_CompositorFrameSwapped::Param param
;
177 if (!BrowserPluginMsg_CompositorFrameSwapped::Read(&message
, ¶m
))
179 // Note that there is no need to send ACK for this message.
180 // If the guest has updated pixels then it is no longer crashed.
181 guest_crashed_
= false;
183 scoped_ptr
<cc::CompositorFrame
> frame(new cc::CompositorFrame
);
184 param
.b
.frame
.AssignTo(frame
.get());
186 EnableCompositing(true);
187 compositing_helper_
->OnCompositorFrameSwapped(frame
.Pass(),
188 param
.b
.producing_route_id
,
189 param
.b
.output_surface_id
,
190 param
.b
.producing_host_id
,
191 param
.b
.shared_memory_handle
);
194 void BrowserPlugin::OnCopyFromCompositingSurface(int browser_plugin_instance_id
,
196 gfx::Rect source_rect
,
197 gfx::Size dest_size
) {
198 if (!compositing_helper_
.get()) {
199 browser_plugin_manager()->Send(
200 new BrowserPluginHostMsg_CopyFromCompositingSurfaceAck(
201 render_view_routing_id_
,
202 browser_plugin_instance_id_
,
207 compositing_helper_
->CopyFromCompositingSurface(request_id
, source_rect
,
211 void BrowserPlugin::OnGuestGone(int browser_plugin_instance_id
) {
212 guest_crashed_
= true;
214 // Turn off compositing so we can display the sad graphic. Changes to
215 // compositing state will show up at a later time after a layout and commit.
216 EnableCompositing(false);
218 // Queue up showing the sad graphic to give content embedders an opportunity
219 // to fire their listeners and potentially overlay the webview with custom
220 // behavior. If the BrowserPlugin is destroyed in the meantime, then the
221 // task will not be executed.
222 base::MessageLoop::current()->PostTask(
224 base::Bind(&BrowserPlugin::ShowSadGraphic
,
225 weak_ptr_factory_
.GetWeakPtr()));
228 void BrowserPlugin::OnSetContentsOpaque(int browser_plugin_instance_id
,
230 if (contents_opaque_
== opaque
)
232 contents_opaque_
= opaque
;
233 if (compositing_helper_
.get())
234 compositing_helper_
->SetContentsOpaque(opaque
);
237 void BrowserPlugin::OnSetCursor(int browser_plugin_instance_id
,
238 const WebCursor
& cursor
) {
242 void BrowserPlugin::OnSetMouseLock(int browser_plugin_instance_id
,
247 render_view_
->mouse_lock_dispatcher()->LockMouse(this);
249 if (!mouse_locked_
) {
250 OnLockMouseACK(false);
253 render_view_
->mouse_lock_dispatcher()->UnlockMouse(this);
257 void BrowserPlugin::OnSetTooltipText(int instance_id
,
258 const base::string16
& tooltip_text
) {
259 // Show tooltip text by setting the BrowserPlugin's |title| attribute.
260 UpdateDOMAttribute("title", tooltip_text
);
263 void BrowserPlugin::OnShouldAcceptTouchEvents(int browser_plugin_instance_id
,
266 container()->requestTouchEventType(
267 accept
? WebPluginContainer::TouchEventRequestTypeRaw
268 : WebPluginContainer::TouchEventRequestTypeNone
);
272 void BrowserPlugin::ShowSadGraphic() {
273 // If the BrowserPlugin is scheduled to be deleted, then container_ will be
274 // NULL so we shouldn't attempt to access it.
276 container_
->invalidate();
279 float BrowserPlugin::GetDeviceScaleFactor() const {
282 return render_view_
->GetWebView()->deviceScaleFactor();
285 void BrowserPlugin::UpdateDeviceScaleFactor() {
286 if (last_device_scale_factor_
== GetDeviceScaleFactor())
289 BrowserPluginHostMsg_ResizeGuest_Params params
;
290 PopulateResizeGuestParameters(plugin_size(), ¶ms
);
291 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest(
292 render_view_routing_id_
,
293 browser_plugin_instance_id_
,
297 void BrowserPlugin::UpdateGuestFocusState() {
300 bool should_be_focused
= ShouldGuestBeFocused();
301 browser_plugin_manager()->Send(new BrowserPluginHostMsg_SetFocus(
302 render_view_routing_id_
,
303 browser_plugin_instance_id_
,
307 bool BrowserPlugin::ShouldGuestBeFocused() const {
308 bool embedder_focused
= false;
310 embedder_focused
= render_view_
->has_focus();
311 return plugin_focused_
&& embedder_focused
;
314 WebPluginContainer
* BrowserPlugin::container() const {
318 bool BrowserPlugin::initialize(WebPluginContainer
* container
) {
322 container_
= container
;
323 container_
->setWantsWheelEvents(true);
325 g_plugin_container_map
.Get().insert(std::make_pair(container_
, this));
327 browser_plugin_manager()->AddBrowserPlugin(browser_plugin_instance_id_
, this);
329 // This is a way to notify observers of our attributes that this plugin is
330 // available in render tree.
331 // TODO(lazyboy): This should be done through the delegate instead. Perhaps
332 // by firing an event from there.
334 "internalinstanceid",
335 base::UTF8ToUTF16(base::IntToString(browser_plugin_instance_id_
)));
340 void BrowserPlugin::EnableCompositing(bool enable
) {
341 bool enabled
= !!compositing_helper_
.get();
342 if (enabled
== enable
)
346 DCHECK(!compositing_helper_
.get());
347 if (!compositing_helper_
.get()) {
348 compositing_helper_
= ChildFrameCompositingHelper::CreateForBrowserPlugin(
349 weak_ptr_factory_
.GetWeakPtr());
352 compositing_helper_
->EnableCompositing(enable
);
353 compositing_helper_
->SetContentsOpaque(contents_opaque_
);
356 DCHECK(compositing_helper_
.get());
357 compositing_helper_
->OnContainerDestroy();
358 compositing_helper_
= NULL
;
362 void BrowserPlugin::destroy() {
364 //container_->clearScriptObjects();
366 // The BrowserPlugin's WebPluginContainer is deleted immediately after this
367 // call returns, so let's not keep a reference to it around.
368 g_plugin_container_map
.Get().erase(container_
);
371 if (compositing_helper_
.get())
372 compositing_helper_
->OnContainerDestroy();
374 // Will be a no-op if the mouse is not currently locked.
376 render_view_
->mouse_lock_dispatcher()->OnLockTargetDestroyed(this);
377 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
380 bool BrowserPlugin::supportsKeyboardFocus() const {
384 bool BrowserPlugin::supportsEditCommands() const {
388 bool BrowserPlugin::supportsInputMethod() const {
392 bool BrowserPlugin::canProcessDrag() const {
396 void BrowserPlugin::paint(WebCanvas
* canvas
, const WebRect
& rect
) {
397 if (guest_crashed_
) {
398 if (!sad_guest_
) // Lazily initialize bitmap.
399 sad_guest_
= content::GetContentClient()->renderer()->
400 GetSadWebViewBitmap();
401 // content_shell does not have the sad plugin bitmap, so we'll paint black
402 // instead to make it clear that something went wrong.
404 PaintSadPlugin(canvas
, plugin_rect_
, *sad_guest_
);
408 SkAutoCanvasRestore
auto_restore(canvas
, true);
409 canvas
->translate(plugin_rect_
.x(), plugin_rect_
.y());
410 SkRect image_data_rect
= SkRect::MakeXYWH(
413 SkIntToScalar(plugin_rect_
.width()),
414 SkIntToScalar(plugin_rect_
.height()));
415 canvas
->clipRect(image_data_rect
);
416 // Paint black or white in case we have nothing in our backing store or we
417 // need to show a gutter.
419 paint
.setStyle(SkPaint::kFill_Style
);
420 paint
.setColor(guest_crashed_
? SK_ColorBLACK
: SK_ColorWHITE
);
421 canvas
->drawRect(image_data_rect
, paint
);
425 bool BrowserPlugin::ShouldForwardToBrowserPlugin(
426 const IPC::Message
& message
) {
427 switch (message
.type()) {
428 case BrowserPluginMsg_Attach_ACK::ID
:
429 case BrowserPluginMsg_AdvanceFocus::ID
:
430 case BrowserPluginMsg_CompositorFrameSwapped::ID
:
431 case BrowserPluginMsg_CopyFromCompositingSurface::ID
:
432 case BrowserPluginMsg_GuestGone::ID
:
433 case BrowserPluginMsg_SetContentsOpaque::ID
:
434 case BrowserPluginMsg_SetCursor::ID
:
435 case BrowserPluginMsg_SetMouseLock::ID
:
436 case BrowserPluginMsg_SetTooltipText::ID
:
437 case BrowserPluginMsg_ShouldAcceptTouchEvents::ID
:
445 void BrowserPlugin::updateGeometry(
446 const WebRect
& window_rect
,
447 const WebRect
& clip_rect
,
448 const WebVector
<WebRect
>& cut_outs_rects
,
450 int old_width
= width();
451 int old_height
= height();
452 plugin_rect_
= window_rect
;
461 if (old_width
== window_rect
.width
&& old_height
== window_rect
.height
) {
462 // Let the browser know about the updated view rect.
463 browser_plugin_manager()->Send(new BrowserPluginHostMsg_UpdateGeometry(
464 render_view_routing_id_
, browser_plugin_instance_id_
, plugin_rect_
));
468 BrowserPluginHostMsg_ResizeGuest_Params params
;
469 PopulateResizeGuestParameters(plugin_size(), ¶ms
);
470 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest(
471 render_view_routing_id_
,
472 browser_plugin_instance_id_
,
476 void BrowserPlugin::PopulateResizeGuestParameters(
477 const gfx::Size
& view_size
,
478 BrowserPluginHostMsg_ResizeGuest_Params
* params
) {
479 params
->view_size
= view_size
;
480 params
->scale_factor
= GetDeviceScaleFactor();
481 if (last_device_scale_factor_
!= params
->scale_factor
) {
482 last_device_scale_factor_
= params
->scale_factor
;
483 params
->repaint
= true;
487 void BrowserPlugin::updateFocus(bool focused
) {
488 plugin_focused_
= focused
;
489 UpdateGuestFocusState();
492 void BrowserPlugin::updateVisibility(bool visible
) {
493 if (visible_
== visible
)
500 if (compositing_helper_
.get())
501 compositing_helper_
->UpdateVisibility(visible
);
503 browser_plugin_manager()->Send(new BrowserPluginHostMsg_SetVisibility(
504 render_view_routing_id_
,
505 browser_plugin_instance_id_
,
509 bool BrowserPlugin::acceptsInputEvents() {
513 bool BrowserPlugin::handleInputEvent(const blink::WebInputEvent
& event
,
514 blink::WebCursorInfo
& cursor_info
) {
515 if (guest_crashed_
|| !ready())
518 if (event
.type
== blink::WebInputEvent::ContextMenu
)
521 if (blink::WebInputEvent::isKeyboardEventType(event
.type
) &&
522 !edit_commands_
.empty()) {
523 browser_plugin_manager()->Send(
524 new BrowserPluginHostMsg_SetEditCommandsForNextKeyEvent(
525 render_view_routing_id_
,
526 browser_plugin_instance_id_
,
528 edit_commands_
.clear();
531 browser_plugin_manager()->Send(
532 new BrowserPluginHostMsg_HandleInputEvent(render_view_routing_id_
,
533 browser_plugin_instance_id_
,
536 GetWebKitCursorInfo(cursor_
, &cursor_info
);
540 bool BrowserPlugin::handleDragStatusUpdate(blink::WebDragStatus drag_status
,
541 const blink::WebDragData
& drag_data
,
542 blink::WebDragOperationsMask mask
,
543 const blink::WebPoint
& position
,
544 const blink::WebPoint
& screen
) {
545 if (guest_crashed_
|| !ready())
547 browser_plugin_manager()->Send(
548 new BrowserPluginHostMsg_DragStatusUpdate(
549 render_view_routing_id_
,
550 browser_plugin_instance_id_
,
552 DropDataBuilder::Build(drag_data
),
558 void BrowserPlugin::didReceiveResponse(
559 const blink::WebURLResponse
& response
) {
562 void BrowserPlugin::didReceiveData(const char* data
, int data_length
) {
564 delegate_
->DidReceiveData(data
, data_length
);
567 void BrowserPlugin::didFinishLoading() {
569 delegate_
->DidFinishLoading();
572 void BrowserPlugin::didFailLoading(const blink::WebURLError
& error
) {
575 void BrowserPlugin::didFinishLoadingFrameRequest(const blink::WebURL
& url
,
579 void BrowserPlugin::didFailLoadingFrameRequest(
580 const blink::WebURL
& url
,
582 const blink::WebURLError
& error
) {
585 bool BrowserPlugin::executeEditCommand(const blink::WebString
& name
) {
586 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ExecuteEditCommand(
587 render_view_routing_id_
,
588 browser_plugin_instance_id_
,
591 // BrowserPlugin swallows edit commands.
595 bool BrowserPlugin::executeEditCommand(const blink::WebString
& name
,
596 const blink::WebString
& value
) {
597 edit_commands_
.push_back(EditCommand(name
.utf8(), value
.utf8()));
598 // BrowserPlugin swallows edit commands.
602 bool BrowserPlugin::setComposition(
603 const blink::WebString
& text
,
604 const blink::WebVector
<blink::WebCompositionUnderline
>& underlines
,
609 std::vector
<blink::WebCompositionUnderline
> std_underlines
;
610 for (size_t i
= 0; i
< underlines
.size(); ++i
) {
611 std_underlines
.push_back(underlines
[i
]);
613 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ImeSetComposition(
614 render_view_routing_id_
,
615 browser_plugin_instance_id_
,
620 // TODO(kochi): This assumes the IPC handling always succeeds.
624 bool BrowserPlugin::confirmComposition(
625 const blink::WebString
& text
,
626 blink::WebWidget::ConfirmCompositionBehavior selectionBehavior
) {
629 bool keep_selection
= (selectionBehavior
== blink::WebWidget::KeepSelection
);
630 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ImeConfirmComposition(
631 render_view_routing_id_
,
632 browser_plugin_instance_id_
,
635 // TODO(kochi): This assumes the IPC handling always succeeds.
639 void BrowserPlugin::extendSelectionAndDelete(int before
, int after
) {
642 browser_plugin_manager()->Send(
643 new BrowserPluginHostMsg_ExtendSelectionAndDelete(
644 render_view_routing_id_
,
645 browser_plugin_instance_id_
,
650 void BrowserPlugin::OnLockMouseACK(bool succeeded
) {
651 mouse_locked_
= succeeded
;
652 browser_plugin_manager()->Send(new BrowserPluginHostMsg_LockMouse_ACK(
653 render_view_routing_id_
,
654 browser_plugin_instance_id_
,
658 void BrowserPlugin::OnMouseLockLost() {
659 mouse_locked_
= false;
660 browser_plugin_manager()->Send(new BrowserPluginHostMsg_UnlockMouse_ACK(
661 render_view_routing_id_
,
662 browser_plugin_instance_id_
));
665 bool BrowserPlugin::HandleMouseLockedInputEvent(
666 const blink::WebMouseEvent
& event
) {
667 browser_plugin_manager()->Send(
668 new BrowserPluginHostMsg_HandleInputEvent(render_view_routing_id_
,
669 browser_plugin_instance_id_
,
675 } // namespace content