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/location.h"
9 #include "base/single_thread_task_runner.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "cc/surfaces/surface.h"
14 #include "content/common/browser_plugin/browser_plugin_constants.h"
15 #include "content/common/browser_plugin/browser_plugin_messages.h"
16 #include "content/common/view_messages.h"
17 #include "content/public/common/content_client.h"
18 #include "content/public/common/content_switches.h"
19 #include "content/public/renderer/browser_plugin_delegate.h"
20 #include "content/public/renderer/content_renderer_client.h"
21 #include "content/renderer/browser_plugin/browser_plugin_manager.h"
22 #include "content/renderer/child_frame_compositing_helper.h"
23 #include "content/renderer/cursor_utils.h"
24 #include "content/renderer/drop_data_builder.h"
25 #include "content/renderer/render_thread_impl.h"
26 #include "content/renderer/sad_plugin.h"
27 #include "third_party/WebKit/public/platform/WebRect.h"
28 #include "third_party/WebKit/public/web/WebDocument.h"
29 #include "third_party/WebKit/public/web/WebElement.h"
30 #include "third_party/WebKit/public/web/WebInputEvent.h"
31 #include "third_party/WebKit/public/web/WebLocalFrame.h"
32 #include "third_party/WebKit/public/web/WebPluginContainer.h"
33 #include "third_party/WebKit/public/web/WebView.h"
34 #include "third_party/skia/include/core/SkCanvas.h"
35 #include "ui/events/keycodes/keyboard_codes.h"
37 using blink::WebCanvas
;
38 using blink::WebPluginContainer
;
39 using blink::WebPoint
;
42 using blink::WebVector
;
45 using PluginContainerMap
=
46 std::map
<blink::WebPluginContainer
*, content::BrowserPlugin
*>;
47 static base::LazyInstance
<PluginContainerMap
> g_plugin_container_map
=
48 LAZY_INSTANCE_INITIALIZER
;
54 BrowserPlugin
* BrowserPlugin::GetFromNode(blink::WebNode
& node
) {
55 blink::WebPluginContainer
* container
= node
.pluginContainer();
59 PluginContainerMap
* browser_plugins
= g_plugin_container_map
.Pointer();
60 PluginContainerMap::iterator it
= browser_plugins
->find(container
);
61 return it
== browser_plugins
->end() ? nullptr : it
->second
;
64 BrowserPlugin::BrowserPlugin(
65 RenderFrame
* render_frame
,
66 const base::WeakPtr
<BrowserPluginDelegate
>& delegate
)
68 render_frame_routing_id_(render_frame
->GetRoutingID()),
71 guest_crashed_(false),
72 plugin_focused_(false),
76 browser_plugin_instance_id_(browser_plugin::kInstanceIDNone
),
77 contents_opaque_(true),
79 weak_ptr_factory_(this) {
80 browser_plugin_instance_id_
=
81 BrowserPluginManager::Get()->GetNextInstanceID();
84 delegate_
->SetElementInstanceID(browser_plugin_instance_id_
);
87 BrowserPlugin::~BrowserPlugin() {
88 if (compositing_helper_
.get())
89 compositing_helper_
->OnContainerDestroy();
92 delegate_
->DidDestroyElement();
96 BrowserPluginManager::Get()->RemoveBrowserPlugin(browser_plugin_instance_id_
);
99 bool BrowserPlugin::OnMessageReceived(const IPC::Message
& message
) {
101 IPC_BEGIN_MESSAGE_MAP(BrowserPlugin
, message
)
102 IPC_MESSAGE_HANDLER(BrowserPluginMsg_AdvanceFocus
, OnAdvanceFocus
)
103 IPC_MESSAGE_HANDLER_GENERIC(BrowserPluginMsg_CompositorFrameSwapped
,
104 OnCompositorFrameSwapped(message
))
105 IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestGone
, OnGuestGone
)
106 IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetContentsOpaque
, OnSetContentsOpaque
)
107 IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetCursor
, OnSetCursor
)
108 IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetMouseLock
, OnSetMouseLock
)
109 IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetTooltipText
, OnSetTooltipText
)
110 IPC_MESSAGE_HANDLER(BrowserPluginMsg_ShouldAcceptTouchEvents
,
111 OnShouldAcceptTouchEvents
)
112 IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetChildFrameSurface
,
113 OnSetChildFrameSurface
)
114 IPC_END_MESSAGE_MAP()
118 void BrowserPlugin::OnSetChildFrameSurface(
119 int browser_plugin_instance_id
,
120 const cc::SurfaceId
& surface_id
,
121 const gfx::Size
& frame_size
,
123 const cc::SurfaceSequence
& sequence
) {
127 EnableCompositing(true);
128 DCHECK(compositing_helper_
.get());
130 compositing_helper_
->OnSetSurface(surface_id
, frame_size
, scale_factor
,
134 void BrowserPlugin::SendSatisfySequence(const cc::SurfaceSequence
& sequence
) {
135 BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_SatisfySequence(
136 render_frame_routing_id_
, browser_plugin_instance_id_
, sequence
));
139 void BrowserPlugin::UpdateDOMAttribute(const std::string
& attribute_name
,
140 const base::string16
& attribute_value
) {
144 blink::WebElement element
= container()->element();
145 blink::WebString web_attribute_name
=
146 blink::WebString::fromUTF8(attribute_name
);
147 element
.setAttribute(web_attribute_name
, attribute_value
);
150 void BrowserPlugin::Attach() {
153 BrowserPluginHostMsg_Attach_Params attach_params
;
154 attach_params
.focused
= ShouldGuestBeFocused();
155 attach_params
.visible
= visible_
;
156 attach_params
.view_rect
= view_rect();
157 attach_params
.is_full_page_plugin
= false;
159 blink::WebLocalFrame
* frame
= container()->element().document().frame();
160 attach_params
.is_full_page_plugin
=
161 frame
->view()->mainFrame()->isWebLocalFrame() &&
162 frame
->view()->mainFrame()->document().isPluginDocument();
164 BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_Attach(
165 render_frame_routing_id_
,
166 browser_plugin_instance_id_
,
172 void BrowserPlugin::Detach() {
177 guest_crashed_
= false;
178 EnableCompositing(false);
179 if (compositing_helper_
.get()) {
180 compositing_helper_
->OnContainerDestroy();
181 compositing_helper_
= nullptr;
184 BrowserPluginManager::Get()->Send(
185 new BrowserPluginHostMsg_Detach(browser_plugin_instance_id_
));
188 void BrowserPlugin::DidCommitCompositorFrame() {
189 if (compositing_helper_
.get())
190 compositing_helper_
->DidCommitCompositorFrame();
193 void BrowserPlugin::OnAdvanceFocus(int browser_plugin_instance_id
,
195 auto render_frame
= RenderFrameImpl::FromRoutingID(render_frame_routing_id());
196 auto render_view
= render_frame
? render_frame
->GetRenderView() : nullptr;
199 render_view
->GetWebView()->advanceFocus(reverse
);
202 void BrowserPlugin::OnCompositorFrameSwapped(const IPC::Message
& message
) {
206 BrowserPluginMsg_CompositorFrameSwapped::Param param
;
207 if (!BrowserPluginMsg_CompositorFrameSwapped::Read(&message
, ¶m
))
209 // Note that there is no need to send ACK for this message.
210 // If the guest has updated pixels then it is no longer crashed.
211 guest_crashed_
= false;
213 scoped_ptr
<cc::CompositorFrame
> frame(new cc::CompositorFrame
);
214 base::get
<1>(param
).frame
.AssignTo(frame
.get());
216 EnableCompositing(true);
217 compositing_helper_
->OnCompositorFrameSwapped(
219 base::get
<1>(param
).producing_route_id
,
220 base::get
<1>(param
).output_surface_id
,
221 base::get
<1>(param
).producing_host_id
,
222 base::get
<1>(param
).shared_memory_handle
);
225 void BrowserPlugin::OnGuestGone(int browser_plugin_instance_id
) {
226 guest_crashed_
= true;
228 // Turn off compositing so we can display the sad graphic. Changes to
229 // compositing state will show up at a later time after a layout and commit.
230 EnableCompositing(false);
232 // Queue up showing the sad graphic to give content embedders an opportunity
233 // to fire their listeners and potentially overlay the webview with custom
234 // behavior. If the BrowserPlugin is destroyed in the meantime, then the
235 // task will not be executed.
236 base::ThreadTaskRunnerHandle::Get()->PostTask(
237 FROM_HERE
, base::Bind(&BrowserPlugin::ShowSadGraphic
,
238 weak_ptr_factory_
.GetWeakPtr()));
241 void BrowserPlugin::OnSetContentsOpaque(int browser_plugin_instance_id
,
243 if (contents_opaque_
== opaque
)
245 contents_opaque_
= opaque
;
246 if (compositing_helper_
.get())
247 compositing_helper_
->SetContentsOpaque(opaque
);
250 void BrowserPlugin::OnSetCursor(int browser_plugin_instance_id
,
251 const WebCursor
& cursor
) {
255 void BrowserPlugin::OnSetMouseLock(int browser_plugin_instance_id
,
257 auto render_frame
= RenderFrameImpl::FromRoutingID(render_frame_routing_id());
258 auto render_view
= static_cast<RenderViewImpl
*>(
259 render_frame
? render_frame
->GetRenderView() : nullptr);
261 if (mouse_locked_
|| !render_view
)
263 render_view
->mouse_lock_dispatcher()->LockMouse(this);
265 if (!mouse_locked_
) {
266 OnLockMouseACK(false);
271 render_view
->mouse_lock_dispatcher()->UnlockMouse(this);
275 void BrowserPlugin::OnSetTooltipText(int instance_id
,
276 const base::string16
& tooltip_text
) {
277 // Show tooltip text by setting the BrowserPlugin's |title| attribute.
278 UpdateDOMAttribute("title", tooltip_text
);
281 void BrowserPlugin::OnShouldAcceptTouchEvents(int browser_plugin_instance_id
,
284 container()->requestTouchEventType(
285 accept
? WebPluginContainer::TouchEventRequestTypeRaw
286 : WebPluginContainer::TouchEventRequestTypeNone
);
290 void BrowserPlugin::ShowSadGraphic() {
291 // If the BrowserPlugin is scheduled to be deleted, then container_ will be
292 // nullptr so we shouldn't attempt to access it.
294 container_
->invalidate();
297 void BrowserPlugin::UpdateInternalInstanceId() {
298 // This is a way to notify observers of our attributes that this plugin is
299 // available in render tree.
300 // TODO(lazyboy): This should be done through the delegate instead. Perhaps
301 // by firing an event from there.
303 "internalinstanceid",
304 base::UTF8ToUTF16(base::IntToString(browser_plugin_instance_id_
)));
307 void BrowserPlugin::UpdateGuestFocusState(blink::WebFocusType focus_type
) {
310 bool should_be_focused
= ShouldGuestBeFocused();
311 BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_SetFocus(
312 browser_plugin_instance_id_
,
317 bool BrowserPlugin::ShouldGuestBeFocused() const {
318 bool embedder_focused
= false;
319 auto render_frame
= RenderFrameImpl::FromRoutingID(render_frame_routing_id());
320 auto render_view
= static_cast<RenderViewImpl
*>(
321 render_frame
? render_frame
->GetRenderView() : nullptr);
323 embedder_focused
= render_view
->has_focus();
324 return plugin_focused_
&& embedder_focused
;
327 WebPluginContainer
* BrowserPlugin::container() const {
331 bool BrowserPlugin::initialize(WebPluginContainer
* container
) {
335 container_
= container
;
336 container_
->setWantsWheelEvents(true);
338 g_plugin_container_map
.Get().insert(std::make_pair(container_
, this));
340 BrowserPluginManager::Get()->AddBrowserPlugin(
341 browser_plugin_instance_id_
, this);
343 // Defer attach call so that if there's any pending browser plugin
344 // destruction, then it can progress first.
345 base::ThreadTaskRunnerHandle::Get()->PostTask(
346 FROM_HERE
, base::Bind(&BrowserPlugin::UpdateInternalInstanceId
,
347 weak_ptr_factory_
.GetWeakPtr()));
351 void BrowserPlugin::EnableCompositing(bool enable
) {
352 bool enabled
= !!compositing_helper_
.get();
353 if (enabled
== enable
)
357 DCHECK(!compositing_helper_
.get());
358 if (!compositing_helper_
.get()) {
359 compositing_helper_
= ChildFrameCompositingHelper::CreateForBrowserPlugin(
360 weak_ptr_factory_
.GetWeakPtr());
363 compositing_helper_
->EnableCompositing(enable
);
364 compositing_helper_
->SetContentsOpaque(contents_opaque_
);
367 DCHECK(compositing_helper_
.get());
368 compositing_helper_
->OnContainerDestroy();
369 compositing_helper_
= nullptr;
373 void BrowserPlugin::destroy() {
375 // The BrowserPlugin's WebPluginContainer is deleted immediately after this
376 // call returns, so let's not keep a reference to it around.
377 g_plugin_container_map
.Get().erase(container_
);
380 container_
= nullptr;
381 // Will be a no-op if the mouse is not currently locked.
382 auto render_frame
= RenderFrameImpl::FromRoutingID(render_frame_routing_id());
383 auto render_view
= static_cast<RenderViewImpl
*>(
384 render_frame
? render_frame
->GetRenderView() : nullptr);
386 render_view
->mouse_lock_dispatcher()->OnLockTargetDestroyed(this);
387 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
390 v8::Local
<v8::Object
> BrowserPlugin::v8ScriptableObject(v8::Isolate
* isolate
) {
392 return v8::Local
<v8::Object
>();
394 return delegate_
->V8ScriptableObject(isolate
);
397 bool BrowserPlugin::supportsKeyboardFocus() const {
401 bool BrowserPlugin::supportsEditCommands() const {
405 bool BrowserPlugin::supportsInputMethod() const {
409 bool BrowserPlugin::canProcessDrag() const {
413 void BrowserPlugin::paint(WebCanvas
* canvas
, const WebRect
& rect
) {
414 if (guest_crashed_
) {
415 if (!sad_guest_
) // Lazily initialize bitmap.
416 sad_guest_
= GetContentClient()->renderer()->GetSadWebViewBitmap();
417 // content_shell does not have the sad plugin bitmap, so we'll paint black
418 // instead to make it clear that something went wrong.
420 PaintSadPlugin(canvas
, view_rect_
, *sad_guest_
);
424 SkAutoCanvasRestore
auto_restore(canvas
, true);
425 canvas
->translate(view_rect_
.x(), view_rect_
.y());
426 SkRect image_data_rect
= SkRect::MakeXYWH(
429 SkIntToScalar(view_rect_
.width()),
430 SkIntToScalar(view_rect_
.height()));
431 canvas
->clipRect(image_data_rect
);
432 // Paint black or white in case we have nothing in our backing store or we
433 // need to show a gutter.
435 paint
.setStyle(SkPaint::kFill_Style
);
436 paint
.setColor(guest_crashed_
? SK_ColorBLACK
: SK_ColorWHITE
);
437 canvas
->drawRect(image_data_rect
, paint
);
441 bool BrowserPlugin::ShouldForwardToBrowserPlugin(
442 const IPC::Message
& message
) {
443 return IPC_MESSAGE_CLASS(message
) == BrowserPluginMsgStart
;
446 void BrowserPlugin::updateGeometry(const WebRect
& window_rect
,
447 const WebRect
& clip_rect
,
448 const WebRect
& unobscured_rect
,
449 const WebVector
<WebRect
>& cut_outs_rects
,
451 gfx::Rect old_view_rect
= view_rect_
;
452 view_rect_
= window_rect
;
460 if (delegate_
&& (view_rect_
.size() != old_view_rect
.size()))
461 delegate_
->DidResizeElement(view_rect_
.size());
466 if (old_view_rect
.size() == view_rect_
.size()) {
467 // Let the browser know about the updated view rect.
468 BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_UpdateGeometry(
469 browser_plugin_instance_id_
, view_rect_
));
474 void BrowserPlugin::updateFocus(bool focused
, blink::WebFocusType focus_type
) {
475 plugin_focused_
= focused
;
476 UpdateGuestFocusState(focus_type
);
479 void BrowserPlugin::updateVisibility(bool visible
) {
480 if (visible_
== visible
)
487 if (compositing_helper_
.get())
488 compositing_helper_
->UpdateVisibility(visible
);
490 BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_SetVisibility(
491 browser_plugin_instance_id_
,
495 bool BrowserPlugin::acceptsInputEvents() {
499 bool BrowserPlugin::handleInputEvent(const blink::WebInputEvent
& event
,
500 blink::WebCursorInfo
& cursor_info
) {
501 if (guest_crashed_
|| !attached())
504 if (event
.type
== blink::WebInputEvent::ContextMenu
)
507 if (blink::WebInputEvent::isKeyboardEventType(event
.type
) &&
508 !edit_commands_
.empty()) {
509 BrowserPluginManager::Get()->Send(
510 new BrowserPluginHostMsg_SetEditCommandsForNextKeyEvent(
511 browser_plugin_instance_id_
,
513 edit_commands_
.clear();
516 BrowserPluginManager::Get()->Send(
517 new BrowserPluginHostMsg_HandleInputEvent(browser_plugin_instance_id_
,
520 GetWebKitCursorInfo(cursor_
, &cursor_info
);
524 bool BrowserPlugin::handleDragStatusUpdate(blink::WebDragStatus drag_status
,
525 const blink::WebDragData
& drag_data
,
526 blink::WebDragOperationsMask mask
,
527 const blink::WebPoint
& position
,
528 const blink::WebPoint
& screen
) {
529 if (guest_crashed_
|| !attached())
531 BrowserPluginManager::Get()->Send(
532 new BrowserPluginHostMsg_DragStatusUpdate(
533 browser_plugin_instance_id_
,
535 DropDataBuilder::Build(drag_data
),
541 void BrowserPlugin::didReceiveResponse(
542 const blink::WebURLResponse
& response
) {
545 void BrowserPlugin::didReceiveData(const char* data
, int data_length
) {
547 delegate_
->DidReceiveData(data
, data_length
);
550 void BrowserPlugin::didFinishLoading() {
552 delegate_
->DidFinishLoading();
555 void BrowserPlugin::didFailLoading(const blink::WebURLError
& error
) {
558 void BrowserPlugin::didFinishLoadingFrameRequest(const blink::WebURL
& url
,
562 void BrowserPlugin::didFailLoadingFrameRequest(
563 const blink::WebURL
& url
,
565 const blink::WebURLError
& error
) {
568 bool BrowserPlugin::executeEditCommand(const blink::WebString
& name
) {
569 BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_ExecuteEditCommand(
570 browser_plugin_instance_id_
,
573 // BrowserPlugin swallows edit commands.
577 bool BrowserPlugin::executeEditCommand(const blink::WebString
& name
,
578 const blink::WebString
& value
) {
579 edit_commands_
.push_back(EditCommand(name
.utf8(), value
.utf8()));
580 // BrowserPlugin swallows edit commands.
584 bool BrowserPlugin::setComposition(
585 const blink::WebString
& text
,
586 const blink::WebVector
<blink::WebCompositionUnderline
>& underlines
,
591 std::vector
<blink::WebCompositionUnderline
> std_underlines
;
592 for (size_t i
= 0; i
< underlines
.size(); ++i
) {
593 std_underlines
.push_back(underlines
[i
]);
595 BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_ImeSetComposition(
596 browser_plugin_instance_id_
,
601 // TODO(kochi): This assumes the IPC handling always succeeds.
605 bool BrowserPlugin::confirmComposition(
606 const blink::WebString
& text
,
607 blink::WebWidget::ConfirmCompositionBehavior selectionBehavior
) {
610 bool keep_selection
= (selectionBehavior
== blink::WebWidget::KeepSelection
);
611 BrowserPluginManager::Get()->Send(
612 new BrowserPluginHostMsg_ImeConfirmComposition(
613 browser_plugin_instance_id_
,
616 // TODO(kochi): This assumes the IPC handling always succeeds.
620 void BrowserPlugin::extendSelectionAndDelete(int before
, int after
) {
623 BrowserPluginManager::Get()->Send(
624 new BrowserPluginHostMsg_ExtendSelectionAndDelete(
625 browser_plugin_instance_id_
,
630 void BrowserPlugin::OnLockMouseACK(bool succeeded
) {
631 mouse_locked_
= succeeded
;
632 BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_LockMouse_ACK(
633 browser_plugin_instance_id_
,
637 void BrowserPlugin::OnMouseLockLost() {
638 mouse_locked_
= false;
639 BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_UnlockMouse_ACK(
640 browser_plugin_instance_id_
));
643 bool BrowserPlugin::HandleMouseLockedInputEvent(
644 const blink::WebMouseEvent
& event
) {
645 BrowserPluginManager::Get()->Send(
646 new BrowserPluginHostMsg_HandleInputEvent(browser_plugin_instance_id_
,
652 } // namespace content