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_GuestGone
, OnGuestGone
)
99 IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetContentsOpaque
, OnSetContentsOpaque
)
100 IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetCursor
, OnSetCursor
)
101 IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetMouseLock
, OnSetMouseLock
)
102 IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetTooltipText
, OnSetTooltipText
)
103 IPC_MESSAGE_HANDLER(BrowserPluginMsg_ShouldAcceptTouchEvents
,
104 OnShouldAcceptTouchEvents
)
105 IPC_MESSAGE_UNHANDLED(handled
= false)
106 IPC_END_MESSAGE_MAP()
110 void BrowserPlugin::UpdateDOMAttribute(const std::string
& attribute_name
,
111 const base::string16
& attribute_value
) {
115 blink::WebElement element
= container()->element();
116 blink::WebString web_attribute_name
=
117 blink::WebString::fromUTF8(attribute_name
);
118 element
.setAttribute(web_attribute_name
, attribute_value
);
121 void BrowserPlugin::Attach() {
124 guest_crashed_
= false;
125 EnableCompositing(false);
126 if (compositing_helper_
.get()) {
127 compositing_helper_
->OnContainerDestroy();
128 compositing_helper_
= NULL
;
132 // TODO(fsamuel): Add support for reattachment.
133 BrowserPluginHostMsg_Attach_Params attach_params
;
134 attach_params
.focused
= ShouldGuestBeFocused();
135 attach_params
.visible
= visible_
;
136 attach_params
.origin
= plugin_rect().origin();
137 attach_params
.is_full_page_plugin
= false;
139 blink::WebLocalFrame
* frame
= container()->element().document().frame();
140 attach_params
.is_full_page_plugin
=
141 frame
->view()->mainFrame()->document().isPluginDocument();
143 gfx::Size
view_size(width(), height());
144 if (!view_size
.IsEmpty()) {
145 PopulateResizeGuestParameters(view_size
,
146 &attach_params
.resize_guest_params
);
148 browser_plugin_manager()->Send(new BrowserPluginHostMsg_Attach(
149 render_view_routing_id_
,
150 browser_plugin_instance_id_
,
153 attach_pending_
= true;
156 void BrowserPlugin::DidCommitCompositorFrame() {
157 if (compositing_helper_
.get())
158 compositing_helper_
->DidCommitCompositorFrame();
161 void BrowserPlugin::OnAdvanceFocus(int browser_plugin_instance_id
,
163 DCHECK(render_view_
);
164 render_view_
->GetWebView()->advanceFocus(reverse
);
167 void BrowserPlugin::OnAttachACK(int browser_plugin_instance_id
) {
170 attach_pending_
= false;
173 void BrowserPlugin::OnCompositorFrameSwapped(const IPC::Message
& message
) {
174 BrowserPluginMsg_CompositorFrameSwapped::Param param
;
175 if (!BrowserPluginMsg_CompositorFrameSwapped::Read(&message
, ¶m
))
177 // Note that there is no need to send ACK for this message.
178 // If the guest has updated pixels then it is no longer crashed.
179 guest_crashed_
= false;
181 scoped_ptr
<cc::CompositorFrame
> frame(new cc::CompositorFrame
);
182 param
.b
.frame
.AssignTo(frame
.get());
184 EnableCompositing(true);
185 compositing_helper_
->OnCompositorFrameSwapped(frame
.Pass(),
186 param
.b
.producing_route_id
,
187 param
.b
.output_surface_id
,
188 param
.b
.producing_host_id
,
189 param
.b
.shared_memory_handle
);
192 void BrowserPlugin::OnGuestGone(int browser_plugin_instance_id
) {
193 guest_crashed_
= true;
195 // Turn off compositing so we can display the sad graphic. Changes to
196 // compositing state will show up at a later time after a layout and commit.
197 EnableCompositing(false);
199 // Queue up showing the sad graphic to give content embedders an opportunity
200 // to fire their listeners and potentially overlay the webview with custom
201 // behavior. If the BrowserPlugin is destroyed in the meantime, then the
202 // task will not be executed.
203 base::MessageLoop::current()->PostTask(
205 base::Bind(&BrowserPlugin::ShowSadGraphic
,
206 weak_ptr_factory_
.GetWeakPtr()));
209 void BrowserPlugin::OnSetContentsOpaque(int browser_plugin_instance_id
,
211 if (contents_opaque_
== opaque
)
213 contents_opaque_
= opaque
;
214 if (compositing_helper_
.get())
215 compositing_helper_
->SetContentsOpaque(opaque
);
218 void BrowserPlugin::OnSetCursor(int browser_plugin_instance_id
,
219 const WebCursor
& cursor
) {
223 void BrowserPlugin::OnSetMouseLock(int browser_plugin_instance_id
,
228 render_view_
->mouse_lock_dispatcher()->LockMouse(this);
230 if (!mouse_locked_
) {
231 OnLockMouseACK(false);
234 render_view_
->mouse_lock_dispatcher()->UnlockMouse(this);
238 void BrowserPlugin::OnSetTooltipText(int instance_id
,
239 const base::string16
& tooltip_text
) {
240 // Show tooltip text by setting the BrowserPlugin's |title| attribute.
241 UpdateDOMAttribute("title", tooltip_text
);
244 void BrowserPlugin::OnShouldAcceptTouchEvents(int browser_plugin_instance_id
,
247 container()->requestTouchEventType(
248 accept
? WebPluginContainer::TouchEventRequestTypeRaw
249 : WebPluginContainer::TouchEventRequestTypeNone
);
253 void BrowserPlugin::ShowSadGraphic() {
254 // If the BrowserPlugin is scheduled to be deleted, then container_ will be
255 // NULL so we shouldn't attempt to access it.
257 container_
->invalidate();
260 float BrowserPlugin::GetDeviceScaleFactor() const {
263 return render_view_
->GetWebView()->deviceScaleFactor();
266 void BrowserPlugin::UpdateDeviceScaleFactor() {
267 if (last_device_scale_factor_
== GetDeviceScaleFactor())
270 BrowserPluginHostMsg_ResizeGuest_Params params
;
271 PopulateResizeGuestParameters(plugin_size(), ¶ms
);
272 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest(
273 render_view_routing_id_
,
274 browser_plugin_instance_id_
,
278 void BrowserPlugin::UpdateGuestFocusState() {
281 bool should_be_focused
= ShouldGuestBeFocused();
282 browser_plugin_manager()->Send(new BrowserPluginHostMsg_SetFocus(
283 render_view_routing_id_
,
284 browser_plugin_instance_id_
,
288 bool BrowserPlugin::ShouldGuestBeFocused() const {
289 bool embedder_focused
= false;
291 embedder_focused
= render_view_
->has_focus();
292 return plugin_focused_
&& embedder_focused
;
295 WebPluginContainer
* BrowserPlugin::container() const {
299 bool BrowserPlugin::initialize(WebPluginContainer
* container
) {
303 container_
= container
;
304 container_
->setWantsWheelEvents(true);
306 g_plugin_container_map
.Get().insert(std::make_pair(container_
, this));
308 browser_plugin_manager()->AddBrowserPlugin(browser_plugin_instance_id_
, this);
310 // This is a way to notify observers of our attributes that this plugin is
311 // available in render tree.
312 // TODO(lazyboy): This should be done through the delegate instead. Perhaps
313 // by firing an event from there.
315 "internalinstanceid",
316 base::UTF8ToUTF16(base::IntToString(browser_plugin_instance_id_
)));
321 void BrowserPlugin::EnableCompositing(bool enable
) {
322 bool enabled
= !!compositing_helper_
.get();
323 if (enabled
== enable
)
327 DCHECK(!compositing_helper_
.get());
328 if (!compositing_helper_
.get()) {
329 compositing_helper_
= ChildFrameCompositingHelper::CreateForBrowserPlugin(
330 weak_ptr_factory_
.GetWeakPtr());
333 compositing_helper_
->EnableCompositing(enable
);
334 compositing_helper_
->SetContentsOpaque(contents_opaque_
);
337 DCHECK(compositing_helper_
.get());
338 compositing_helper_
->OnContainerDestroy();
339 compositing_helper_
= NULL
;
343 void BrowserPlugin::destroy() {
345 //container_->clearScriptObjects();
347 // The BrowserPlugin's WebPluginContainer is deleted immediately after this
348 // call returns, so let's not keep a reference to it around.
349 g_plugin_container_map
.Get().erase(container_
);
352 if (compositing_helper_
.get())
353 compositing_helper_
->OnContainerDestroy();
355 // Will be a no-op if the mouse is not currently locked.
357 render_view_
->mouse_lock_dispatcher()->OnLockTargetDestroyed(this);
358 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
361 bool BrowserPlugin::supportsKeyboardFocus() const {
365 bool BrowserPlugin::supportsEditCommands() const {
369 bool BrowserPlugin::supportsInputMethod() const {
373 bool BrowserPlugin::canProcessDrag() const {
377 void BrowserPlugin::paint(WebCanvas
* canvas
, const WebRect
& rect
) {
378 if (guest_crashed_
) {
379 if (!sad_guest_
) // Lazily initialize bitmap.
380 sad_guest_
= content::GetContentClient()->renderer()->
381 GetSadWebViewBitmap();
382 // content_shell does not have the sad plugin bitmap, so we'll paint black
383 // instead to make it clear that something went wrong.
385 PaintSadPlugin(canvas
, plugin_rect_
, *sad_guest_
);
389 SkAutoCanvasRestore
auto_restore(canvas
, true);
390 canvas
->translate(plugin_rect_
.x(), plugin_rect_
.y());
391 SkRect image_data_rect
= SkRect::MakeXYWH(
394 SkIntToScalar(plugin_rect_
.width()),
395 SkIntToScalar(plugin_rect_
.height()));
396 canvas
->clipRect(image_data_rect
);
397 // Paint black or white in case we have nothing in our backing store or we
398 // need to show a gutter.
400 paint
.setStyle(SkPaint::kFill_Style
);
401 paint
.setColor(guest_crashed_
? SK_ColorBLACK
: SK_ColorWHITE
);
402 canvas
->drawRect(image_data_rect
, paint
);
406 bool BrowserPlugin::ShouldForwardToBrowserPlugin(
407 const IPC::Message
& message
) {
408 switch (message
.type()) {
409 case BrowserPluginMsg_Attach_ACK::ID
:
410 case BrowserPluginMsg_AdvanceFocus::ID
:
411 case BrowserPluginMsg_CompositorFrameSwapped::ID
:
412 case BrowserPluginMsg_GuestGone::ID
:
413 case BrowserPluginMsg_SetContentsOpaque::ID
:
414 case BrowserPluginMsg_SetCursor::ID
:
415 case BrowserPluginMsg_SetMouseLock::ID
:
416 case BrowserPluginMsg_SetTooltipText::ID
:
417 case BrowserPluginMsg_ShouldAcceptTouchEvents::ID
:
425 void BrowserPlugin::updateGeometry(
426 const WebRect
& window_rect
,
427 const WebRect
& clip_rect
,
428 const WebVector
<WebRect
>& cut_outs_rects
,
430 int old_width
= width();
431 int old_height
= height();
432 plugin_rect_
= window_rect
;
441 if (old_width
== window_rect
.width
&& old_height
== window_rect
.height
) {
442 // Let the browser know about the updated view rect.
443 browser_plugin_manager()->Send(new BrowserPluginHostMsg_UpdateGeometry(
444 render_view_routing_id_
, browser_plugin_instance_id_
, plugin_rect_
));
448 BrowserPluginHostMsg_ResizeGuest_Params params
;
449 PopulateResizeGuestParameters(plugin_size(), ¶ms
);
450 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest(
451 render_view_routing_id_
,
452 browser_plugin_instance_id_
,
456 void BrowserPlugin::PopulateResizeGuestParameters(
457 const gfx::Size
& view_size
,
458 BrowserPluginHostMsg_ResizeGuest_Params
* params
) {
459 params
->view_size
= view_size
;
460 params
->scale_factor
= GetDeviceScaleFactor();
461 if (last_device_scale_factor_
!= params
->scale_factor
) {
462 last_device_scale_factor_
= params
->scale_factor
;
463 params
->repaint
= true;
467 void BrowserPlugin::updateFocus(bool focused
) {
468 plugin_focused_
= focused
;
469 UpdateGuestFocusState();
472 void BrowserPlugin::updateVisibility(bool visible
) {
473 if (visible_
== visible
)
480 if (compositing_helper_
.get())
481 compositing_helper_
->UpdateVisibility(visible
);
483 browser_plugin_manager()->Send(new BrowserPluginHostMsg_SetVisibility(
484 render_view_routing_id_
,
485 browser_plugin_instance_id_
,
489 bool BrowserPlugin::acceptsInputEvents() {
493 bool BrowserPlugin::handleInputEvent(const blink::WebInputEvent
& event
,
494 blink::WebCursorInfo
& cursor_info
) {
495 if (guest_crashed_
|| !ready())
498 if (event
.type
== blink::WebInputEvent::ContextMenu
)
501 if (blink::WebInputEvent::isKeyboardEventType(event
.type
) &&
502 !edit_commands_
.empty()) {
503 browser_plugin_manager()->Send(
504 new BrowserPluginHostMsg_SetEditCommandsForNextKeyEvent(
505 render_view_routing_id_
,
506 browser_plugin_instance_id_
,
508 edit_commands_
.clear();
511 browser_plugin_manager()->Send(
512 new BrowserPluginHostMsg_HandleInputEvent(render_view_routing_id_
,
513 browser_plugin_instance_id_
,
516 GetWebKitCursorInfo(cursor_
, &cursor_info
);
520 bool BrowserPlugin::handleDragStatusUpdate(blink::WebDragStatus drag_status
,
521 const blink::WebDragData
& drag_data
,
522 blink::WebDragOperationsMask mask
,
523 const blink::WebPoint
& position
,
524 const blink::WebPoint
& screen
) {
525 if (guest_crashed_
|| !ready())
527 browser_plugin_manager()->Send(
528 new BrowserPluginHostMsg_DragStatusUpdate(
529 render_view_routing_id_
,
530 browser_plugin_instance_id_
,
532 DropDataBuilder::Build(drag_data
),
538 void BrowserPlugin::didReceiveResponse(
539 const blink::WebURLResponse
& response
) {
542 void BrowserPlugin::didReceiveData(const char* data
, int data_length
) {
544 delegate_
->DidReceiveData(data
, data_length
);
547 void BrowserPlugin::didFinishLoading() {
549 delegate_
->DidFinishLoading();
552 void BrowserPlugin::didFailLoading(const blink::WebURLError
& error
) {
555 void BrowserPlugin::didFinishLoadingFrameRequest(const blink::WebURL
& url
,
559 void BrowserPlugin::didFailLoadingFrameRequest(
560 const blink::WebURL
& url
,
562 const blink::WebURLError
& error
) {
565 bool BrowserPlugin::executeEditCommand(const blink::WebString
& name
) {
566 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ExecuteEditCommand(
567 render_view_routing_id_
,
568 browser_plugin_instance_id_
,
571 // BrowserPlugin swallows edit commands.
575 bool BrowserPlugin::executeEditCommand(const blink::WebString
& name
,
576 const blink::WebString
& value
) {
577 edit_commands_
.push_back(EditCommand(name
.utf8(), value
.utf8()));
578 // BrowserPlugin swallows edit commands.
582 bool BrowserPlugin::setComposition(
583 const blink::WebString
& text
,
584 const blink::WebVector
<blink::WebCompositionUnderline
>& underlines
,
589 std::vector
<blink::WebCompositionUnderline
> std_underlines
;
590 for (size_t i
= 0; i
< underlines
.size(); ++i
) {
591 std_underlines
.push_back(underlines
[i
]);
593 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ImeSetComposition(
594 render_view_routing_id_
,
595 browser_plugin_instance_id_
,
600 // TODO(kochi): This assumes the IPC handling always succeeds.
604 bool BrowserPlugin::confirmComposition(
605 const blink::WebString
& text
,
606 blink::WebWidget::ConfirmCompositionBehavior selectionBehavior
) {
609 bool keep_selection
= (selectionBehavior
== blink::WebWidget::KeepSelection
);
610 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ImeConfirmComposition(
611 render_view_routing_id_
,
612 browser_plugin_instance_id_
,
615 // TODO(kochi): This assumes the IPC handling always succeeds.
619 void BrowserPlugin::extendSelectionAndDelete(int before
, int after
) {
622 browser_plugin_manager()->Send(
623 new BrowserPluginHostMsg_ExtendSelectionAndDelete(
624 render_view_routing_id_
,
625 browser_plugin_instance_id_
,
630 void BrowserPlugin::OnLockMouseACK(bool succeeded
) {
631 mouse_locked_
= succeeded
;
632 browser_plugin_manager()->Send(new BrowserPluginHostMsg_LockMouse_ACK(
633 render_view_routing_id_
,
634 browser_plugin_instance_id_
,
638 void BrowserPlugin::OnMouseLockLost() {
639 mouse_locked_
= false;
640 browser_plugin_manager()->Send(new BrowserPluginHostMsg_UnlockMouse_ACK(
641 render_view_routing_id_
,
642 browser_plugin_instance_id_
));
645 bool BrowserPlugin::HandleMouseLockedInputEvent(
646 const blink::WebMouseEvent
& event
) {
647 browser_plugin_manager()->Send(
648 new BrowserPluginHostMsg_HandleInputEvent(render_view_routing_id_
,
649 browser_plugin_instance_id_
,
655 } // namespace content