Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / renderer / browser_plugin / browser_plugin.cc
blob48491e6c5861af1089e573f46c264eef382ee4e9
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;
40 using blink::WebRect;
41 using blink::WebURL;
42 using blink::WebVector;
44 namespace {
45 using PluginContainerMap =
46 std::map<blink::WebPluginContainer*, content::BrowserPlugin*>;
47 static base::LazyInstance<PluginContainerMap> g_plugin_container_map =
48 LAZY_INSTANCE_INITIALIZER;
49 } // namespace
51 namespace content {
53 // static
54 BrowserPlugin* BrowserPlugin::GetFromNode(blink::WebNode& node) {
55 blink::WebPluginContainer* container = node.pluginContainer();
56 if (!container)
57 return nullptr;
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)
67 : attached_(false),
68 render_frame_routing_id_(render_frame->GetRoutingID()),
69 container_(nullptr),
70 sad_guest_(nullptr),
71 guest_crashed_(false),
72 plugin_focused_(false),
73 visible_(true),
74 mouse_locked_(false),
75 ready_(false),
76 browser_plugin_instance_id_(browser_plugin::kInstanceIDNone),
77 contents_opaque_(true),
78 delegate_(delegate),
79 weak_ptr_factory_(this) {
80 browser_plugin_instance_id_ =
81 BrowserPluginManager::Get()->GetNextInstanceID();
83 if (delegate_)
84 delegate_->SetElementInstanceID(browser_plugin_instance_id_);
87 BrowserPlugin::~BrowserPlugin() {
88 if (compositing_helper_.get())
89 compositing_helper_->OnContainerDestroy();
91 if (delegate_) {
92 delegate_->DidDestroyElement();
93 delegate_.reset();
96 BrowserPluginManager::Get()->RemoveBrowserPlugin(browser_plugin_instance_id_);
99 bool BrowserPlugin::OnMessageReceived(const IPC::Message& message) {
100 bool handled = true;
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()
115 return handled;
118 void BrowserPlugin::OnSetChildFrameSurface(
119 int browser_plugin_instance_id,
120 const cc::SurfaceId& surface_id,
121 const gfx::Size& frame_size,
122 float scale_factor,
123 const cc::SurfaceSequence& sequence) {
124 if (!attached())
125 return;
127 EnableCompositing(true);
128 DCHECK(compositing_helper_.get());
130 compositing_helper_->OnSetSurface(surface_id, frame_size, scale_factor,
131 sequence);
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) {
141 if (!container())
142 return;
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() {
151 Detach();
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;
158 if (container()) {
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_,
167 attach_params));
169 attached_ = true;
172 void BrowserPlugin::Detach() {
173 if (!attached())
174 return;
176 attached_ = false;
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,
194 bool reverse) {
195 auto render_frame = RenderFrameImpl::FromRoutingID(render_frame_routing_id());
196 auto render_view = render_frame ? render_frame->GetRenderView() : nullptr;
197 if (!render_view)
198 return;
199 render_view->GetWebView()->advanceFocus(reverse);
202 void BrowserPlugin::OnCompositorFrameSwapped(const IPC::Message& message) {
203 if (!attached())
204 return;
206 BrowserPluginMsg_CompositorFrameSwapped::Param param;
207 if (!BrowserPluginMsg_CompositorFrameSwapped::Read(&message, &param))
208 return;
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(
218 frame.Pass(),
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,
242 bool opaque) {
243 if (contents_opaque_ == opaque)
244 return;
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) {
252 cursor_ = cursor;
255 void BrowserPlugin::OnSetMouseLock(int browser_plugin_instance_id,
256 bool enable) {
257 auto render_frame = RenderFrameImpl::FromRoutingID(render_frame_routing_id());
258 auto render_view = static_cast<RenderViewImpl*>(
259 render_frame ? render_frame->GetRenderView() : nullptr);
260 if (enable) {
261 if (mouse_locked_ || !render_view)
262 return;
263 render_view->mouse_lock_dispatcher()->LockMouse(this);
264 } else {
265 if (!mouse_locked_) {
266 OnLockMouseACK(false);
267 return;
269 if (!render_view)
270 return;
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,
282 bool accept) {
283 if (container()) {
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.
293 if (container_)
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.
302 UpdateDOMAttribute(
303 "internalinstanceid",
304 base::UTF8ToUTF16(base::IntToString(browser_plugin_instance_id_)));
307 void BrowserPlugin::UpdateGuestFocusState(blink::WebFocusType focus_type) {
308 if (!attached())
309 return;
310 bool should_be_focused = ShouldGuestBeFocused();
311 BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_SetFocus(
312 browser_plugin_instance_id_,
313 should_be_focused,
314 focus_type));
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);
322 if (render_view)
323 embedder_focused = render_view->has_focus();
324 return plugin_focused_ && embedder_focused;
327 WebPluginContainer* BrowserPlugin::container() const {
328 return container_;
331 bool BrowserPlugin::initialize(WebPluginContainer* container) {
332 if (!container)
333 return false;
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()));
348 return true;
351 void BrowserPlugin::EnableCompositing(bool enable) {
352 bool enabled = !!compositing_helper_.get();
353 if (enabled == enable)
354 return;
356 if (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_);
366 if (!enable) {
367 DCHECK(compositing_helper_.get());
368 compositing_helper_->OnContainerDestroy();
369 compositing_helper_ = nullptr;
373 void BrowserPlugin::destroy() {
374 if (container_) {
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);
385 if (render_view)
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) {
391 if (!delegate_)
392 return v8::Local<v8::Object>();
394 return delegate_->V8ScriptableObject(isolate);
397 bool BrowserPlugin::supportsKeyboardFocus() const {
398 return true;
401 bool BrowserPlugin::supportsEditCommands() const {
402 return true;
405 bool BrowserPlugin::supportsInputMethod() const {
406 return true;
409 bool BrowserPlugin::canProcessDrag() const {
410 return true;
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.
419 if (sad_guest_) {
420 PaintSadPlugin(canvas, view_rect_, *sad_guest_);
421 return;
424 SkAutoCanvasRestore auto_restore(canvas, true);
425 canvas->translate(view_rect_.x(), view_rect_.y());
426 SkRect image_data_rect = SkRect::MakeXYWH(
427 SkIntToScalar(0),
428 SkIntToScalar(0),
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.
434 SkPaint paint;
435 paint.setStyle(SkPaint::kFill_Style);
436 paint.setColor(guest_crashed_ ? SK_ColorBLACK : SK_ColorWHITE);
437 canvas->drawRect(image_data_rect, paint);
440 // static
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,
450 bool is_visible) {
451 gfx::Rect old_view_rect = view_rect_;
452 view_rect_ = window_rect;
454 if (!ready_) {
455 if (delegate_)
456 delegate_->Ready();
457 ready_ = true;
460 if (delegate_ && (view_rect_.size() != old_view_rect.size()))
461 delegate_->DidResizeElement(view_rect_.size());
463 if (!attached())
464 return;
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_));
470 return;
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)
481 return;
483 visible_ = visible;
484 if (!attached())
485 return;
487 if (compositing_helper_.get())
488 compositing_helper_->UpdateVisibility(visible);
490 BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_SetVisibility(
491 browser_plugin_instance_id_,
492 visible));
495 bool BrowserPlugin::acceptsInputEvents() {
496 return true;
499 bool BrowserPlugin::handleInputEvent(const blink::WebInputEvent& event,
500 blink::WebCursorInfo& cursor_info) {
501 if (guest_crashed_ || !attached())
502 return false;
504 if (event.type == blink::WebInputEvent::ContextMenu)
505 return true;
507 if (blink::WebInputEvent::isKeyboardEventType(event.type) &&
508 !edit_commands_.empty()) {
509 BrowserPluginManager::Get()->Send(
510 new BrowserPluginHostMsg_SetEditCommandsForNextKeyEvent(
511 browser_plugin_instance_id_,
512 edit_commands_));
513 edit_commands_.clear();
516 BrowserPluginManager::Get()->Send(
517 new BrowserPluginHostMsg_HandleInputEvent(browser_plugin_instance_id_,
518 view_rect_,
519 &event));
520 GetWebKitCursorInfo(cursor_, &cursor_info);
521 return true;
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())
530 return false;
531 BrowserPluginManager::Get()->Send(
532 new BrowserPluginHostMsg_DragStatusUpdate(
533 browser_plugin_instance_id_,
534 drag_status,
535 DropDataBuilder::Build(drag_data),
536 mask,
537 position));
538 return true;
541 void BrowserPlugin::didReceiveResponse(
542 const blink::WebURLResponse& response) {
545 void BrowserPlugin::didReceiveData(const char* data, int data_length) {
546 if (delegate_)
547 delegate_->DidReceiveData(data, data_length);
550 void BrowserPlugin::didFinishLoading() {
551 if (delegate_)
552 delegate_->DidFinishLoading();
555 void BrowserPlugin::didFailLoading(const blink::WebURLError& error) {
558 void BrowserPlugin::didFinishLoadingFrameRequest(const blink::WebURL& url,
559 void* notify_data) {
562 void BrowserPlugin::didFailLoadingFrameRequest(
563 const blink::WebURL& url,
564 void* notify_data,
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_,
571 name.utf8()));
573 // BrowserPlugin swallows edit commands.
574 return true;
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.
581 return true;
584 bool BrowserPlugin::setComposition(
585 const blink::WebString& text,
586 const blink::WebVector<blink::WebCompositionUnderline>& underlines,
587 int selectionStart,
588 int selectionEnd) {
589 if (!attached())
590 return false;
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_,
597 text.utf8(),
598 std_underlines,
599 selectionStart,
600 selectionEnd));
601 // TODO(kochi): This assumes the IPC handling always succeeds.
602 return true;
605 bool BrowserPlugin::confirmComposition(
606 const blink::WebString& text,
607 blink::WebWidget::ConfirmCompositionBehavior selectionBehavior) {
608 if (!attached())
609 return false;
610 bool keep_selection = (selectionBehavior == blink::WebWidget::KeepSelection);
611 BrowserPluginManager::Get()->Send(
612 new BrowserPluginHostMsg_ImeConfirmComposition(
613 browser_plugin_instance_id_,
614 text.utf8(),
615 keep_selection));
616 // TODO(kochi): This assumes the IPC handling always succeeds.
617 return true;
620 void BrowserPlugin::extendSelectionAndDelete(int before, int after) {
621 if (!attached())
622 return;
623 BrowserPluginManager::Get()->Send(
624 new BrowserPluginHostMsg_ExtendSelectionAndDelete(
625 browser_plugin_instance_id_,
626 before,
627 after));
630 void BrowserPlugin::OnLockMouseACK(bool succeeded) {
631 mouse_locked_ = succeeded;
632 BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_LockMouse_ACK(
633 browser_plugin_instance_id_,
634 succeeded));
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_,
647 view_rect_,
648 &event));
649 return true;
652 } // namespace content