Re-enable blink_perf.canvas on Windows
[chromium-blink-merge.git] / content / renderer / browser_plugin / browser_plugin.cc
blobeb9c93f6120dc4f650c020cb063e927e19c8dccd
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;
37 using blink::WebRect;
38 using blink::WebURL;
39 using blink::WebVector;
41 namespace {
42 using PluginContainerMap =
43 std::map<blink::WebPluginContainer*, content::BrowserPlugin*>;
44 static base::LazyInstance<PluginContainerMap> g_plugin_container_map =
45 LAZY_INSTANCE_INITIALIZER;
46 } // namespace
48 namespace content {
50 // static
51 BrowserPlugin* BrowserPlugin::GetFromNode(blink::WebNode& node) {
52 blink::WebPluginContainer* container = node.pluginContainer();
53 if (!container)
54 return nullptr;
56 PluginContainerMap* browser_plugins = g_plugin_container_map.Pointer();
57 PluginContainerMap::iterator it = browser_plugins->find(container);
58 return it == browser_plugins->end() ? nullptr : it->second;
61 BrowserPlugin::BrowserPlugin(RenderFrame* render_frame,
62 scoped_ptr<BrowserPluginDelegate> delegate)
63 : attached_(false),
64 render_frame_routing_id_(render_frame->GetRoutingID()),
65 container_(nullptr),
66 sad_guest_(nullptr),
67 guest_crashed_(false),
68 plugin_focused_(false),
69 visible_(true),
70 mouse_locked_(false),
71 ready_(false),
72 browser_plugin_instance_id_(browser_plugin::kInstanceIDNone),
73 contents_opaque_(true),
74 delegate_(delegate.Pass()),
75 weak_ptr_factory_(this) {
76 browser_plugin_instance_id_ =
77 BrowserPluginManager::Get()->GetNextInstanceID();
79 if (delegate_)
80 delegate_->SetElementInstanceID(browser_plugin_instance_id_);
83 BrowserPlugin::~BrowserPlugin() {
84 if (compositing_helper_.get())
85 compositing_helper_->OnContainerDestroy();
87 BrowserPluginManager::Get()->RemoveBrowserPlugin(browser_plugin_instance_id_);
90 bool BrowserPlugin::OnMessageReceived(const IPC::Message& message) {
91 bool handled = true;
92 IPC_BEGIN_MESSAGE_MAP(BrowserPlugin, message)
93 IPC_MESSAGE_HANDLER(BrowserPluginMsg_AdvanceFocus, OnAdvanceFocus)
94 IPC_MESSAGE_HANDLER_GENERIC(BrowserPluginMsg_CompositorFrameSwapped,
95 OnCompositorFrameSwapped(message))
96 IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestGone, OnGuestGone)
97 IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetContentsOpaque, OnSetContentsOpaque)
98 IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetCursor, OnSetCursor)
99 IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetMouseLock, OnSetMouseLock)
100 IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetTooltipText, OnSetTooltipText)
101 IPC_MESSAGE_HANDLER(BrowserPluginMsg_ShouldAcceptTouchEvents,
102 OnShouldAcceptTouchEvents)
103 IPC_MESSAGE_UNHANDLED(
104 handled = delegate_ && delegate_->OnMessageReceived(message))
105 IPC_END_MESSAGE_MAP()
106 return handled;
109 void BrowserPlugin::UpdateDOMAttribute(const std::string& attribute_name,
110 const base::string16& attribute_value) {
111 if (!container())
112 return;
114 blink::WebElement element = container()->element();
115 blink::WebString web_attribute_name =
116 blink::WebString::fromUTF8(attribute_name);
117 element.setAttribute(web_attribute_name, attribute_value);
120 void BrowserPlugin::Attach() {
121 Detach();
123 BrowserPluginHostMsg_Attach_Params attach_params;
124 attach_params.focused = ShouldGuestBeFocused();
125 attach_params.visible = visible_;
126 attach_params.view_rect = view_rect();
127 attach_params.is_full_page_plugin = false;
128 if (container()) {
129 blink::WebLocalFrame* frame = container()->element().document().frame();
130 attach_params.is_full_page_plugin =
131 frame->view()->mainFrame()->document().isPluginDocument();
133 BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_Attach(
134 render_frame_routing_id_,
135 browser_plugin_instance_id_,
136 attach_params));
138 attached_ = true;
141 void BrowserPlugin::Detach() {
142 if (!attached())
143 return;
145 attached_ = false;
146 guest_crashed_ = false;
147 EnableCompositing(false);
148 if (compositing_helper_.get()) {
149 compositing_helper_->OnContainerDestroy();
150 compositing_helper_ = nullptr;
153 BrowserPluginManager::Get()->Send(
154 new BrowserPluginHostMsg_Detach(browser_plugin_instance_id_));
157 void BrowserPlugin::DidCommitCompositorFrame() {
158 if (compositing_helper_.get())
159 compositing_helper_->DidCommitCompositorFrame();
162 void BrowserPlugin::OnAdvanceFocus(int browser_plugin_instance_id,
163 bool reverse) {
164 auto render_frame = RenderFrameImpl::FromRoutingID(render_frame_routing_id());
165 auto render_view = render_frame ? render_frame->GetRenderView() : nullptr;
166 if (!render_view)
167 return;
168 render_view->GetWebView()->advanceFocus(reverse);
171 void BrowserPlugin::OnCompositorFrameSwapped(const IPC::Message& message) {
172 if (!attached())
173 return;
175 BrowserPluginMsg_CompositorFrameSwapped::Param param;
176 if (!BrowserPluginMsg_CompositorFrameSwapped::Read(&message, &param))
177 return;
178 // Note that there is no need to send ACK for this message.
179 // If the guest has updated pixels then it is no longer crashed.
180 guest_crashed_ = false;
182 scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame);
183 get<1>(param).frame.AssignTo(frame.get());
185 EnableCompositing(true);
186 compositing_helper_->OnCompositorFrameSwapped(
187 frame.Pass(),
188 get<1>(param).producing_route_id,
189 get<1>(param).output_surface_id,
190 get<1>(param).producing_host_id,
191 get<1>(param).shared_memory_handle);
194 void BrowserPlugin::OnGuestGone(int browser_plugin_instance_id) {
195 guest_crashed_ = true;
197 // Turn off compositing so we can display the sad graphic. Changes to
198 // compositing state will show up at a later time after a layout and commit.
199 EnableCompositing(false);
201 // Queue up showing the sad graphic to give content embedders an opportunity
202 // to fire their listeners and potentially overlay the webview with custom
203 // behavior. If the BrowserPlugin is destroyed in the meantime, then the
204 // task will not be executed.
205 base::MessageLoop::current()->PostTask(
206 FROM_HERE,
207 base::Bind(&BrowserPlugin::ShowSadGraphic,
208 weak_ptr_factory_.GetWeakPtr()));
211 void BrowserPlugin::OnSetContentsOpaque(int browser_plugin_instance_id,
212 bool opaque) {
213 if (contents_opaque_ == opaque)
214 return;
215 contents_opaque_ = opaque;
216 if (compositing_helper_.get())
217 compositing_helper_->SetContentsOpaque(opaque);
220 void BrowserPlugin::OnSetCursor(int browser_plugin_instance_id,
221 const WebCursor& cursor) {
222 cursor_ = cursor;
225 void BrowserPlugin::OnSetMouseLock(int browser_plugin_instance_id,
226 bool enable) {
227 auto render_frame = RenderFrameImpl::FromRoutingID(render_frame_routing_id());
228 auto render_view = static_cast<RenderViewImpl*>(
229 render_frame ? render_frame->GetRenderView() : nullptr);
230 if (enable) {
231 if (mouse_locked_ || !render_view)
232 return;
233 render_view->mouse_lock_dispatcher()->LockMouse(this);
234 } else {
235 if (!mouse_locked_) {
236 OnLockMouseACK(false);
237 return;
239 if (!render_view)
240 return;
241 render_view->mouse_lock_dispatcher()->UnlockMouse(this);
245 void BrowserPlugin::OnSetTooltipText(int instance_id,
246 const base::string16& tooltip_text) {
247 // Show tooltip text by setting the BrowserPlugin's |title| attribute.
248 UpdateDOMAttribute("title", tooltip_text);
251 void BrowserPlugin::OnShouldAcceptTouchEvents(int browser_plugin_instance_id,
252 bool accept) {
253 if (container()) {
254 container()->requestTouchEventType(
255 accept ? WebPluginContainer::TouchEventRequestTypeRaw
256 : WebPluginContainer::TouchEventRequestTypeNone);
260 void BrowserPlugin::ShowSadGraphic() {
261 // If the BrowserPlugin is scheduled to be deleted, then container_ will be
262 // nullptr so we shouldn't attempt to access it.
263 if (container_)
264 container_->invalidate();
267 void BrowserPlugin::UpdateGuestFocusState(blink::WebFocusType focus_type) {
268 if (!attached())
269 return;
270 bool should_be_focused = ShouldGuestBeFocused();
271 BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_SetFocus(
272 browser_plugin_instance_id_,
273 should_be_focused,
274 focus_type));
277 bool BrowserPlugin::ShouldGuestBeFocused() const {
278 bool embedder_focused = false;
279 auto render_frame = RenderFrameImpl::FromRoutingID(render_frame_routing_id());
280 auto render_view = static_cast<RenderViewImpl*>(
281 render_frame ? render_frame->GetRenderView() : nullptr);
282 if (render_view)
283 embedder_focused = render_view->has_focus();
284 return plugin_focused_ && embedder_focused;
287 WebPluginContainer* BrowserPlugin::container() const {
288 return container_;
291 bool BrowserPlugin::initialize(WebPluginContainer* container) {
292 if (!container)
293 return false;
295 container_ = container;
296 container_->setWantsWheelEvents(true);
298 g_plugin_container_map.Get().insert(std::make_pair(container_, this));
300 BrowserPluginManager::Get()->AddBrowserPlugin(
301 browser_plugin_instance_id_, this);
303 // Defer attach call so that if there's any pending browser plugin
304 // destruction, then it can progress first.
305 base::MessageLoop::current()->PostTask(
306 FROM_HERE,
307 base::Bind(&BrowserPlugin::UpdateInternalInstanceId,
308 weak_ptr_factory_.GetWeakPtr()));
309 return true;
312 void BrowserPlugin::EnableCompositing(bool enable) {
313 bool enabled = !!compositing_helper_.get();
314 if (enabled == enable)
315 return;
317 if (enable) {
318 DCHECK(!compositing_helper_.get());
319 if (!compositing_helper_.get()) {
320 compositing_helper_ = ChildFrameCompositingHelper::CreateForBrowserPlugin(
321 weak_ptr_factory_.GetWeakPtr());
324 compositing_helper_->EnableCompositing(enable);
325 compositing_helper_->SetContentsOpaque(contents_opaque_);
327 if (!enable) {
328 DCHECK(compositing_helper_.get());
329 compositing_helper_->OnContainerDestroy();
330 compositing_helper_ = nullptr;
334 void BrowserPlugin::UpdateInternalInstanceId() {
335 // This is a way to notify observers of our attributes that this plugin is
336 // available in render tree.
337 // TODO(lazyboy): This should be done through the delegate instead. Perhaps
338 // by firing an event from there.
339 UpdateDOMAttribute(
340 "internalinstanceid",
341 base::UTF8ToUTF16(base::IntToString(browser_plugin_instance_id_)));
344 void BrowserPlugin::destroy() {
345 if (container_) {
346 // The BrowserPlugin's WebPluginContainer is deleted immediately after this
347 // call returns, so let's not keep a reference to it around.
348 g_plugin_container_map.Get().erase(container_);
351 container_ = nullptr;
352 // Will be a no-op if the mouse is not currently locked.
353 auto render_frame = RenderFrameImpl::FromRoutingID(render_frame_routing_id());
354 auto render_view = static_cast<RenderViewImpl*>(
355 render_frame ? render_frame->GetRenderView() : nullptr);
356 if (render_view)
357 render_view->mouse_lock_dispatcher()->OnLockTargetDestroyed(this);
358 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
361 v8::Local<v8::Object> BrowserPlugin::v8ScriptableObject(v8::Isolate* isolate) {
362 return delegate_->V8ScriptableObject(isolate);
365 bool BrowserPlugin::supportsKeyboardFocus() const {
366 return true;
369 bool BrowserPlugin::supportsEditCommands() const {
370 return true;
373 bool BrowserPlugin::supportsInputMethod() const {
374 return true;
377 bool BrowserPlugin::canProcessDrag() const {
378 return true;
381 void BrowserPlugin::paint(WebCanvas* canvas, const WebRect& rect) {
382 if (guest_crashed_) {
383 if (!sad_guest_) // Lazily initialize bitmap.
384 sad_guest_ = content::GetContentClient()->renderer()->
385 GetSadWebViewBitmap();
386 // content_shell does not have the sad plugin bitmap, so we'll paint black
387 // instead to make it clear that something went wrong.
388 if (sad_guest_) {
389 PaintSadPlugin(canvas, view_rect_, *sad_guest_);
390 return;
393 SkAutoCanvasRestore auto_restore(canvas, true);
394 canvas->translate(view_rect_.x(), view_rect_.y());
395 SkRect image_data_rect = SkRect::MakeXYWH(
396 SkIntToScalar(0),
397 SkIntToScalar(0),
398 SkIntToScalar(view_rect_.width()),
399 SkIntToScalar(view_rect_.height()));
400 canvas->clipRect(image_data_rect);
401 // Paint black or white in case we have nothing in our backing store or we
402 // need to show a gutter.
403 SkPaint paint;
404 paint.setStyle(SkPaint::kFill_Style);
405 paint.setColor(guest_crashed_ ? SK_ColorBLACK : SK_ColorWHITE);
406 canvas->drawRect(image_data_rect, paint);
409 // static
410 bool BrowserPlugin::ShouldForwardToBrowserPlugin(
411 const IPC::Message& message) {
412 return IPC_MESSAGE_CLASS(message) == BrowserPluginMsgStart;
415 void BrowserPlugin::updateGeometry(const WebRect& window_rect,
416 const WebRect& clip_rect,
417 const WebRect& unobscured_rect,
418 const WebVector<WebRect>& cut_outs_rects,
419 bool is_visible) {
420 int old_width = view_rect_.width();
421 int old_height = view_rect_.height();
422 view_rect_ = window_rect;
424 if (!ready_) {
425 if (delegate_)
426 delegate_->Ready();
427 ready_ = true;
430 if (delegate_) {
431 delegate_->DidResizeElement(
432 gfx::Size(old_width, old_height), view_rect_.size());
435 if (!attached())
436 return;
438 if (old_width == window_rect.width && old_height == window_rect.height) {
439 // Let the browser know about the updated view rect.
440 BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_UpdateGeometry(
441 browser_plugin_instance_id_, view_rect_));
442 return;
446 void BrowserPlugin::updateFocus(bool focused, blink::WebFocusType focus_type) {
447 plugin_focused_ = focused;
448 UpdateGuestFocusState(focus_type);
451 void BrowserPlugin::updateVisibility(bool visible) {
452 if (visible_ == visible)
453 return;
455 visible_ = visible;
456 if (!attached())
457 return;
459 if (compositing_helper_.get())
460 compositing_helper_->UpdateVisibility(visible);
462 BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_SetVisibility(
463 browser_plugin_instance_id_,
464 visible));
467 bool BrowserPlugin::acceptsInputEvents() {
468 return true;
471 bool BrowserPlugin::handleInputEvent(const blink::WebInputEvent& event,
472 blink::WebCursorInfo& cursor_info) {
473 if (guest_crashed_ || !attached())
474 return false;
476 if (event.type == blink::WebInputEvent::ContextMenu)
477 return true;
479 if (blink::WebInputEvent::isKeyboardEventType(event.type) &&
480 !edit_commands_.empty()) {
481 BrowserPluginManager::Get()->Send(
482 new BrowserPluginHostMsg_SetEditCommandsForNextKeyEvent(
483 browser_plugin_instance_id_,
484 edit_commands_));
485 edit_commands_.clear();
488 BrowserPluginManager::Get()->Send(
489 new BrowserPluginHostMsg_HandleInputEvent(browser_plugin_instance_id_,
490 view_rect_,
491 &event));
492 GetWebKitCursorInfo(cursor_, &cursor_info);
493 return true;
496 bool BrowserPlugin::handleDragStatusUpdate(blink::WebDragStatus drag_status,
497 const blink::WebDragData& drag_data,
498 blink::WebDragOperationsMask mask,
499 const blink::WebPoint& position,
500 const blink::WebPoint& screen) {
501 if (guest_crashed_ || !attached())
502 return false;
503 BrowserPluginManager::Get()->Send(
504 new BrowserPluginHostMsg_DragStatusUpdate(
505 browser_plugin_instance_id_,
506 drag_status,
507 DropDataBuilder::Build(drag_data),
508 mask,
509 position));
510 return true;
513 void BrowserPlugin::didReceiveResponse(
514 const blink::WebURLResponse& response) {
517 void BrowserPlugin::didReceiveData(const char* data, int data_length) {
518 if (delegate_)
519 delegate_->DidReceiveData(data, data_length);
522 void BrowserPlugin::didFinishLoading() {
523 if (delegate_)
524 delegate_->DidFinishLoading();
527 void BrowserPlugin::didFailLoading(const blink::WebURLError& error) {
530 void BrowserPlugin::didFinishLoadingFrameRequest(const blink::WebURL& url,
531 void* notify_data) {
534 void BrowserPlugin::didFailLoadingFrameRequest(
535 const blink::WebURL& url,
536 void* notify_data,
537 const blink::WebURLError& error) {
540 bool BrowserPlugin::executeEditCommand(const blink::WebString& name) {
541 BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_ExecuteEditCommand(
542 browser_plugin_instance_id_,
543 name.utf8()));
545 // BrowserPlugin swallows edit commands.
546 return true;
549 bool BrowserPlugin::executeEditCommand(const blink::WebString& name,
550 const blink::WebString& value) {
551 edit_commands_.push_back(EditCommand(name.utf8(), value.utf8()));
552 // BrowserPlugin swallows edit commands.
553 return true;
556 bool BrowserPlugin::setComposition(
557 const blink::WebString& text,
558 const blink::WebVector<blink::WebCompositionUnderline>& underlines,
559 int selectionStart,
560 int selectionEnd) {
561 if (!attached())
562 return false;
563 std::vector<blink::WebCompositionUnderline> std_underlines;
564 for (size_t i = 0; i < underlines.size(); ++i) {
565 std_underlines.push_back(underlines[i]);
567 BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_ImeSetComposition(
568 browser_plugin_instance_id_,
569 text.utf8(),
570 std_underlines,
571 selectionStart,
572 selectionEnd));
573 // TODO(kochi): This assumes the IPC handling always succeeds.
574 return true;
577 bool BrowserPlugin::confirmComposition(
578 const blink::WebString& text,
579 blink::WebWidget::ConfirmCompositionBehavior selectionBehavior) {
580 if (!attached())
581 return false;
582 bool keep_selection = (selectionBehavior == blink::WebWidget::KeepSelection);
583 BrowserPluginManager::Get()->Send(
584 new BrowserPluginHostMsg_ImeConfirmComposition(
585 browser_plugin_instance_id_,
586 text.utf8(),
587 keep_selection));
588 // TODO(kochi): This assumes the IPC handling always succeeds.
589 return true;
592 void BrowserPlugin::extendSelectionAndDelete(int before, int after) {
593 if (!attached())
594 return;
595 BrowserPluginManager::Get()->Send(
596 new BrowserPluginHostMsg_ExtendSelectionAndDelete(
597 browser_plugin_instance_id_,
598 before,
599 after));
602 void BrowserPlugin::OnLockMouseACK(bool succeeded) {
603 mouse_locked_ = succeeded;
604 BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_LockMouse_ACK(
605 browser_plugin_instance_id_,
606 succeeded));
609 void BrowserPlugin::OnMouseLockLost() {
610 mouse_locked_ = false;
611 BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_UnlockMouse_ACK(
612 browser_plugin_instance_id_));
615 bool BrowserPlugin::HandleMouseLockedInputEvent(
616 const blink::WebMouseEvent& event) {
617 BrowserPluginManager::Get()->Send(
618 new BrowserPluginHostMsg_HandleInputEvent(browser_plugin_instance_id_,
619 view_rect_,
620 &event));
621 return true;
624 } // namespace content