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/json/json_string_value_serializer.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "content/common/browser_plugin/browser_plugin_constants.h"
14 #include "content/common/browser_plugin/browser_plugin_messages.h"
15 #include "content/common/view_messages.h"
16 #include "content/public/common/content_client.h"
17 #include "content/public/common/content_switches.h"
18 #include "content/public/renderer/content_renderer_client.h"
19 #include "content/renderer/browser_plugin/browser_plugin_bindings.h"
20 #include "content/renderer/browser_plugin/browser_plugin_compositing_helper.h"
21 #include "content/renderer/browser_plugin/browser_plugin_manager.h"
22 #include "content/renderer/cursor_utils.h"
23 #include "content/renderer/drop_data_builder.h"
24 #include "content/renderer/render_process_impl.h"
25 #include "content/renderer/render_thread_impl.h"
26 #include "content/renderer/sad_plugin.h"
27 #include "content/renderer/v8_value_converter_impl.h"
28 #include "skia/ext/platform_canvas.h"
29 #include "third_party/WebKit/public/platform/WebRect.h"
30 #include "third_party/WebKit/public/web/WebBindings.h"
31 #include "third_party/WebKit/public/web/WebDOMCustomEvent.h"
32 #include "third_party/WebKit/public/web/WebDocument.h"
33 #include "third_party/WebKit/public/web/WebElement.h"
34 #include "third_party/WebKit/public/web/WebFrame.h"
35 #include "third_party/WebKit/public/web/WebInputEvent.h"
36 #include "third_party/WebKit/public/web/WebPluginContainer.h"
37 #include "third_party/WebKit/public/web/WebPluginParams.h"
38 #include "third_party/WebKit/public/web/WebScriptSource.h"
39 #include "third_party/WebKit/public/web/WebView.h"
40 #include "ui/events/keycodes/keyboard_codes.h"
43 #include "base/sys_info.h"
46 using WebKit::WebCanvas;
47 using WebKit::WebPluginContainer;
48 using WebKit::WebPluginParams;
49 using WebKit::WebPoint;
50 using WebKit::WebRect;
52 using WebKit::WebVector;
58 static std::string GetInternalEventName(const char* event_name) {
59 return base::StringPrintf("-internal-%s", event_name);
62 typedef std::map<WebKit::WebPluginContainer*,
63 BrowserPlugin*> PluginContainerMap;
64 static base::LazyInstance<PluginContainerMap> g_plugin_container_map =
65 LAZY_INSTANCE_INITIALIZER;
69 BrowserPlugin::BrowserPlugin(
70 RenderViewImpl* render_view,
71 WebKit::WebFrame* frame,
72 const WebPluginParams& params)
73 : guest_instance_id_(browser_plugin::kInstanceIDNone),
75 render_view_(render_view->AsWeakPtr()),
76 render_view_routing_id_(render_view->GetRoutingID()),
78 damage_buffer_sequence_id_(0),
79 paint_ack_received_(true),
80 last_device_scale_factor_(1.0f),
82 guest_crashed_(false),
83 is_auto_size_state_dirty_(false),
84 persist_storage_(false),
85 valid_partition_id_(true),
86 content_window_routing_id_(MSG_ROUTING_NONE),
87 plugin_focused_(false),
89 before_first_navigation_(true),
91 browser_plugin_manager_(render_view->GetBrowserPluginManager()),
92 compositing_enabled_(false),
93 embedder_frame_url_(frame->document().url()),
94 weak_ptr_factory_(this) {
97 BrowserPlugin::~BrowserPlugin() {
98 // If the BrowserPlugin has never navigated then the browser process and
99 // BrowserPluginManager don't know about it and so there is nothing to do
101 if (!HasGuestInstanceID())
103 browser_plugin_manager()->RemoveBrowserPlugin(guest_instance_id_);
104 browser_plugin_manager()->Send(
105 new BrowserPluginHostMsg_PluginDestroyed(render_view_routing_id_,
106 guest_instance_id_));
110 BrowserPlugin* BrowserPlugin::FromContainer(
111 WebKit::WebPluginContainer* container) {
112 PluginContainerMap* browser_plugins = g_plugin_container_map.Pointer();
113 PluginContainerMap::iterator it = browser_plugins->find(container);
114 return it == browser_plugins->end() ? NULL : it->second;
117 bool BrowserPlugin::OnMessageReceived(const IPC::Message& message) {
119 IPC_BEGIN_MESSAGE_MAP(BrowserPlugin, message)
120 IPC_MESSAGE_HANDLER(BrowserPluginMsg_AdvanceFocus, OnAdvanceFocus)
121 IPC_MESSAGE_HANDLER(BrowserPluginMsg_Attach_ACK, OnAttachACK)
122 IPC_MESSAGE_HANDLER(BrowserPluginMsg_BuffersSwapped, OnBuffersSwapped)
123 IPC_MESSAGE_HANDLER_GENERIC(BrowserPluginMsg_CompositorFrameSwapped,
124 OnCompositorFrameSwapped(message))
125 IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestContentWindowReady,
126 OnGuestContentWindowReady)
127 IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestGone, OnGuestGone)
128 IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetCursor, OnSetCursor)
129 IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetMouseLock, OnSetMouseLock)
130 IPC_MESSAGE_HANDLER(BrowserPluginMsg_ShouldAcceptTouchEvents,
131 OnShouldAcceptTouchEvents)
132 IPC_MESSAGE_HANDLER(BrowserPluginMsg_UpdatedName, OnUpdatedName)
133 IPC_MESSAGE_HANDLER(BrowserPluginMsg_UpdateRect, OnUpdateRect)
134 IPC_MESSAGE_UNHANDLED(handled = false)
135 IPC_END_MESSAGE_MAP()
139 void BrowserPlugin::UpdateDOMAttribute(const std::string& attribute_name,
140 const std::string& attribute_value) {
144 WebKit::WebElement element = container()->element();
145 WebKit::WebString web_attribute_name =
146 WebKit::WebString::fromUTF8(attribute_name);
147 if (!HasDOMAttribute(attribute_name) ||
148 (std::string(element.getAttribute(web_attribute_name).utf8()) !=
150 element.setAttribute(web_attribute_name,
151 WebKit::WebString::fromUTF8(attribute_value));
155 void BrowserPlugin::RemoveDOMAttribute(const std::string& attribute_name) {
159 container()->element().removeAttribute(
160 WebKit::WebString::fromUTF8(attribute_name));
163 std::string BrowserPlugin::GetDOMAttributeValue(
164 const std::string& attribute_name) const {
166 return std::string();
168 return container()->element().getAttribute(
169 WebKit::WebString::fromUTF8(attribute_name)).utf8();
172 bool BrowserPlugin::HasDOMAttribute(const std::string& attribute_name) const {
176 return container()->element().hasAttribute(
177 WebKit::WebString::fromUTF8(attribute_name));
180 std::string BrowserPlugin::GetNameAttribute() const {
181 return GetDOMAttributeValue(browser_plugin::kAttributeName);
184 std::string BrowserPlugin::GetSrcAttribute() const {
185 return GetDOMAttributeValue(browser_plugin::kAttributeSrc);
188 bool BrowserPlugin::GetAutoSizeAttribute() const {
189 return HasDOMAttribute(browser_plugin::kAttributeAutoSize);
192 int BrowserPlugin::GetMaxHeightAttribute() const {
194 base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMaxHeight),
199 int BrowserPlugin::GetMaxWidthAttribute() const {
201 base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMaxWidth),
206 int BrowserPlugin::GetMinHeightAttribute() const {
208 base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMinHeight),
213 int BrowserPlugin::GetMinWidthAttribute() const {
215 base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMinWidth),
220 int BrowserPlugin::GetAdjustedMaxHeight() const {
221 int max_height = GetMaxHeightAttribute();
222 return max_height ? max_height : height();
225 int BrowserPlugin::GetAdjustedMaxWidth() const {
226 int max_width = GetMaxWidthAttribute();
227 return max_width ? max_width : width();
230 int BrowserPlugin::GetAdjustedMinHeight() const {
231 int min_height = GetMinHeightAttribute();
232 // FrameView.cpp does not allow this value to be <= 0, so when the value is
233 // unset (or set to 0), we set it to the container size.
234 min_height = min_height ? min_height : height();
235 // For autosize, minHeight should not be bigger than maxHeight.
236 return std::min(min_height, GetAdjustedMaxHeight());
239 int BrowserPlugin::GetAdjustedMinWidth() const {
240 int min_width = GetMinWidthAttribute();
241 // FrameView.cpp does not allow this value to be <= 0, so when the value is
242 // unset (or set to 0), we set it to the container size.
243 min_width = min_width ? min_width : width();
244 // For autosize, minWidth should not be bigger than maxWidth.
245 return std::min(min_width, GetAdjustedMaxWidth());
248 std::string BrowserPlugin::GetPartitionAttribute() const {
249 return GetDOMAttributeValue(browser_plugin::kAttributePartition);
252 void BrowserPlugin::ParseNameAttribute() {
253 if (!HasGuestInstanceID())
255 browser_plugin_manager()->Send(
256 new BrowserPluginHostMsg_SetName(render_view_routing_id_,
258 GetNameAttribute()));
261 bool BrowserPlugin::ParseSrcAttribute(std::string* error_message) {
262 if (!valid_partition_id_) {
263 *error_message = browser_plugin::kErrorInvalidPartition;
266 std::string src = GetSrcAttribute();
270 // If we haven't created the guest yet, do so now. We will navigate it right
271 // after creation. If |src| is empty, we can delay the creation until we
273 if (!HasGuestInstanceID()) {
274 // On initial navigation, we request an instance ID from the browser
275 // process. We essentially ignore all subsequent calls to SetSrcAttribute
276 // until we receive an instance ID. |before_first_navigation_|
277 // prevents BrowserPlugin from allocating more than one instance ID.
278 // Upon receiving an instance ID from the browser process, we continue
279 // the process of navigation by populating the
280 // BrowserPluginHostMsg_Attach_Params with the current state of
281 // BrowserPlugin and sending a BrowserPluginHostMsg_CreateGuest to the
282 // browser process in order to create a new guest.
283 if (before_first_navigation_) {
284 browser_plugin_manager()->AllocateInstanceID(
285 weak_ptr_factory_.GetWeakPtr());
286 before_first_navigation_ = false;
291 browser_plugin_manager()->Send(
292 new BrowserPluginHostMsg_NavigateGuest(render_view_routing_id_,
298 void BrowserPlugin::ParseAutoSizeAttribute() {
299 last_view_size_ = plugin_rect_.size();
300 is_auto_size_state_dirty_ = true;
301 UpdateGuestAutoSizeState(GetAutoSizeAttribute());
304 void BrowserPlugin::PopulateAutoSizeParameters(
305 BrowserPluginHostMsg_AutoSize_Params* params, bool auto_size_enabled) {
306 params->enable = auto_size_enabled;
307 // No need to populate the params if autosize is off.
308 if (auto_size_enabled) {
309 params->max_size = gfx::Size(GetAdjustedMaxWidth(), GetAdjustedMaxHeight());
310 params->min_size = gfx::Size(GetAdjustedMinWidth(), GetAdjustedMinHeight());
312 if (max_auto_size_ != params->max_size)
313 is_auto_size_state_dirty_ = true;
315 max_auto_size_ = params->max_size;
317 max_auto_size_ = gfx::Size();
321 void BrowserPlugin::UpdateGuestAutoSizeState(bool auto_size_enabled) {
322 // If we haven't yet heard back from the guest about the last resize request,
323 // then we don't issue another request until we do in
324 // BrowserPlugin::UpdateRect.
325 if (!HasGuestInstanceID() || !paint_ack_received_)
328 BrowserPluginHostMsg_AutoSize_Params auto_size_params;
329 BrowserPluginHostMsg_ResizeGuest_Params resize_guest_params;
330 if (auto_size_enabled) {
331 GetDamageBufferWithSizeParams(&auto_size_params,
332 &resize_guest_params,
335 GetDamageBufferWithSizeParams(NULL, &resize_guest_params, true);
337 paint_ack_received_ = false;
338 browser_plugin_manager()->Send(
339 new BrowserPluginHostMsg_SetAutoSize(render_view_routing_id_,
342 resize_guest_params));
346 bool BrowserPlugin::UsesDamageBuffer(
347 const BrowserPluginMsg_UpdateRect_Params& params) {
348 return params.damage_buffer_sequence_id != 0 || params.needs_ack;
351 bool BrowserPlugin::UsesPendingDamageBuffer(
352 const BrowserPluginMsg_UpdateRect_Params& params) {
353 if (!pending_damage_buffer_)
355 return damage_buffer_sequence_id_ == params.damage_buffer_sequence_id;
358 void BrowserPlugin::OnInstanceIDAllocated(int guest_instance_id) {
359 CHECK(guest_instance_id != browser_plugin::kInstanceIDNone);
360 before_first_navigation_ = false;
361 guest_instance_id_ = guest_instance_id;
362 browser_plugin_manager()->AddBrowserPlugin(guest_instance_id, this);
364 std::map<std::string, base::Value*> props;
365 props[browser_plugin::kWindowID] =
366 new base::FundamentalValue(guest_instance_id);
367 TriggerEvent(browser_plugin::kEventInternalInstanceIDAllocated, &props);
370 void BrowserPlugin::Attach(scoped_ptr<base::DictionaryValue> extra_params) {
371 BrowserPluginHostMsg_Attach_Params attach_params;
372 attach_params.focused = ShouldGuestBeFocused();
373 attach_params.visible = visible_;
374 attach_params.name = GetNameAttribute();
375 attach_params.storage_partition_id = storage_partition_id_;
376 attach_params.persist_storage = persist_storage_;
377 attach_params.src = GetSrcAttribute();
378 attach_params.embedder_frame_url = embedder_frame_url_;
379 GetDamageBufferWithSizeParams(&attach_params.auto_size_params,
380 &attach_params.resize_guest_params,
383 browser_plugin_manager()->Send(
384 new BrowserPluginHostMsg_Attach(render_view_routing_id_,
385 guest_instance_id_, attach_params,
389 void BrowserPlugin::DidCommitCompositorFrame() {
390 if (compositing_helper_.get())
391 compositing_helper_->DidCommitCompositorFrame();
394 void BrowserPlugin::OnAdvanceFocus(int guest_instance_id, bool reverse) {
395 DCHECK(render_view_.get());
396 render_view_->GetWebView()->advanceFocus(reverse);
399 void BrowserPlugin::OnAttachACK(
400 int guest_instance_id,
401 const BrowserPluginMsg_Attach_ACK_Params& params) {
402 // Update BrowserPlugin attributes to match the state of the guest.
403 if (!params.name.empty())
404 OnUpdatedName(guest_instance_id, params.name);
405 if (!params.storage_partition_id.empty()) {
406 std::string partition_name =
407 (params.persist_storage ? browser_plugin::kPersistPrefix : "") +
408 params.storage_partition_id;
409 UpdateDOMAttribute(browser_plugin::kAttributePartition, partition_name);
414 void BrowserPlugin::OnBuffersSwapped(
415 int guest_instance_id,
416 const BrowserPluginMsg_BuffersSwapped_Params& params) {
417 DCHECK(guest_instance_id == guest_instance_id_);
418 EnableCompositing(true);
420 compositing_helper_->OnBuffersSwapped(params.size,
424 GetDeviceScaleFactor());
427 void BrowserPlugin::OnCompositorFrameSwapped(const IPC::Message& message) {
428 BrowserPluginMsg_CompositorFrameSwapped::Param param;
429 if (!BrowserPluginMsg_CompositorFrameSwapped::Read(&message, ¶m))
431 scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame);
432 param.b.AssignTo(frame.get());
434 EnableCompositing(true);
435 compositing_helper_->OnCompositorFrameSwapped(frame.Pass(),
436 param.c /* route_id */,
437 param.d /* output_surface_id */,
438 param.e /* host_id */);
441 void BrowserPlugin::OnGuestContentWindowReady(int guest_instance_id,
442 int content_window_routing_id) {
443 DCHECK(content_window_routing_id != MSG_ROUTING_NONE);
444 content_window_routing_id_ = content_window_routing_id;
447 void BrowserPlugin::OnGuestGone(int guest_instance_id) {
448 guest_crashed_ = true;
450 // Queue up showing the sad graphic to give content embedders an opportunity
451 // to fire their listeners and potentially overlay the webview with custom
452 // behavior. If the BrowserPlugin is destroyed in the meantime, then the
453 // task will not be executed.
454 base::MessageLoop::current()->PostTask(
456 base::Bind(&BrowserPlugin::ShowSadGraphic,
457 weak_ptr_factory_.GetWeakPtr()));
460 void BrowserPlugin::OnSetCursor(int guest_instance_id,
461 const WebCursor& cursor) {
465 void BrowserPlugin::OnSetMouseLock(int guest_instance_id,
470 render_view_->mouse_lock_dispatcher()->LockMouse(this);
472 if (!mouse_locked_) {
473 OnLockMouseACK(false);
476 render_view_->mouse_lock_dispatcher()->UnlockMouse(this);
480 void BrowserPlugin::OnShouldAcceptTouchEvents(int guest_instance_id,
483 container()->requestTouchEventType(accept ?
484 WebKit::WebPluginContainer::TouchEventRequestTypeRaw :
485 WebKit::WebPluginContainer::TouchEventRequestTypeNone);
489 void BrowserPlugin::OnUpdatedName(int guest_instance_id,
490 const std::string& name) {
491 UpdateDOMAttribute(browser_plugin::kAttributeName, name);
494 void BrowserPlugin::OnUpdateRect(
495 int guest_instance_id,
496 const BrowserPluginMsg_UpdateRect_Params& params) {
497 // If the guest has updated pixels then it is no longer crashed.
498 guest_crashed_ = false;
500 bool use_new_damage_buffer = !backing_store_;
501 BrowserPluginHostMsg_AutoSize_Params auto_size_params;
502 BrowserPluginHostMsg_ResizeGuest_Params resize_guest_params;
503 // If we have a pending damage buffer, and the guest has begun to use the
504 // damage buffer then we know the guest will no longer use the current
505 // damage buffer. At this point, we drop the current damage buffer, and
506 // mark the pending damage buffer as the current damage buffer.
507 if (UsesPendingDamageBuffer(params)) {
509 use_new_damage_buffer = true;
512 bool auto_size = GetAutoSizeAttribute();
513 // We receive a resize ACK in regular mode, but not in autosize.
514 // In SW, |paint_ack_received_| is reset in SwapDamageBuffers().
515 // In HW mode, we need to do it here so we can continue sending
516 // resize messages when needed.
517 if (params.is_resize_ack ||
518 (!params.needs_ack && (auto_size || is_auto_size_state_dirty_))) {
519 paint_ack_received_ = true;
522 bool was_auto_size_state_dirty = auto_size && is_auto_size_state_dirty_;
523 is_auto_size_state_dirty_ = false;
525 if ((!auto_size && (width() != params.view_size.width() ||
526 height() != params.view_size.height())) ||
527 (auto_size && was_auto_size_state_dirty) ||
528 GetDeviceScaleFactor() != params.scale_factor) {
529 // We are HW accelerated, render widget does not expect an ack,
530 // but we still need to update the size.
531 if (!params.needs_ack) {
532 UpdateGuestAutoSizeState(auto_size);
536 if (!paint_ack_received_) {
537 // The guest has not yet responded to the last resize request, and
538 // so we don't want to do anything at this point other than ACK the guest.
540 PopulateAutoSizeParameters(&auto_size_params, auto_size);
542 // If we have no pending damage buffer, then the guest has not caught up
543 // with the BrowserPlugin container. We now tell the guest about the new
546 GetDamageBufferWithSizeParams(&auto_size_params,
547 &resize_guest_params,
548 was_auto_size_state_dirty);
550 GetDamageBufferWithSizeParams(NULL,
551 &resize_guest_params,
552 was_auto_size_state_dirty);
555 browser_plugin_manager()->Send(new BrowserPluginHostMsg_UpdateRect_ACK(
556 render_view_routing_id_,
560 resize_guest_params));
564 if (auto_size && (params.view_size != last_view_size_)) {
566 backing_store_->Clear(SK_ColorWHITE);
567 last_view_size_ = params.view_size;
570 if (UsesDamageBuffer(params)) {
572 // If we are seeing damage buffers, HW compositing should be turned off.
573 EnableCompositing(false);
575 // If we are now using a new damage buffer, then that means that the guest
576 // has updated its size state in response to a resize request. We change
577 // the backing store's size to accomodate the new damage buffer size.
578 if (use_new_damage_buffer) {
579 int backing_store_width = auto_size ? GetAdjustedMaxWidth() : width();
580 int backing_store_height = auto_size ? GetAdjustedMaxHeight(): height();
581 backing_store_.reset(
582 new BrowserPluginBackingStore(
583 gfx::Size(backing_store_width, backing_store_height),
584 params.scale_factor));
587 // If we just transitioned from the compositing path to the software path
588 // then we might not yet have a damage buffer.
589 if (current_damage_buffer_) {
590 // Update the backing store.
591 if (!params.scroll_rect.IsEmpty()) {
592 backing_store_->ScrollBackingStore(params.scroll_delta,
596 backing_store_->PaintToBackingStore(params.bitmap_rect,
598 current_damage_buffer_->memory());
599 // Invalidate the container.
600 // If the BrowserPlugin is scheduled to be deleted, then container_ will
601 // be NULL so we shouldn't attempt to access it.
603 container_->invalidate();
607 // BrowserPluginHostMsg_UpdateRect_ACK is used by both the compositing and
608 // software paths to piggyback updated autosize parameters.
610 PopulateAutoSizeParameters(&auto_size_params, auto_size);
611 browser_plugin_manager()->Send(new BrowserPluginHostMsg_UpdateRect_ACK(
612 render_view_routing_id_,
614 UsesDamageBuffer(params),
616 resize_guest_params));
619 void BrowserPlugin::ParseSizeContraintsChanged() {
620 bool auto_size = GetAutoSizeAttribute();
622 is_auto_size_state_dirty_ = true;
623 UpdateGuestAutoSizeState(true);
627 bool BrowserPlugin::InAutoSizeBounds(const gfx::Size& size) const {
628 return size.width() <= GetAdjustedMaxWidth() &&
629 size.height() <= GetAdjustedMaxHeight();
632 NPObject* BrowserPlugin::GetContentWindow() const {
633 if (content_window_routing_id_ == MSG_ROUTING_NONE)
635 RenderViewImpl* guest_render_view = RenderViewImpl::FromRoutingID(
636 content_window_routing_id_);
637 if (!guest_render_view)
639 WebKit::WebFrame* guest_frame = guest_render_view->GetWebView()->mainFrame();
640 return guest_frame->windowObject();
644 bool BrowserPlugin::AttachWindowTo(const WebKit::WebNode& node, int window_id) {
648 if (!node.isElementNode())
651 WebKit::WebElement shim_element = node.toConst<WebKit::WebElement>();
652 // The shim containing the BrowserPlugin must be attached to a document.
653 if (shim_element.document().isNull())
656 WebKit::WebNode shadow_root = shim_element.shadowRoot();
657 if (shadow_root.isNull() || !shadow_root.hasChildNodes())
660 WebKit::WebNode plugin_element = shadow_root.firstChild();
661 WebKit::WebPluginContainer* plugin_container =
662 plugin_element.pluginContainer();
663 if (!plugin_container)
666 BrowserPlugin* browser_plugin =
667 BrowserPlugin::FromContainer(plugin_container);
671 // If the BrowserPlugin has already begun to navigate then we shouldn't allow
672 // attaching a different guest.
674 // Navigation happens in two stages.
675 // 1. BrowserPlugin requests an instance ID from the browser process.
676 // 2. The browser process returns an instance ID and BrowserPlugin is
677 // "Attach"ed to that instance ID.
678 // If the instance ID is new then a new guest will be created.
679 // If the instance ID corresponds to an unattached guest then BrowserPlugin
680 // is attached to that guest.
682 // Between step 1, and step 2, BrowserPlugin::AttachWindowTo may be called.
683 // The check below ensures that BrowserPlugin:Attach does not get called with
684 // a different instance ID after step 1 has happened.
685 // TODO(fsamuel): We may wish to support reattaching guests in the future:
686 // http://crbug.com/156219.
687 if (browser_plugin->HasNavigated())
690 browser_plugin->OnInstanceIDAllocated(window_id);
694 bool BrowserPlugin::HasNavigated() const {
695 return !before_first_navigation_;
698 bool BrowserPlugin::HasGuestInstanceID() const {
699 return guest_instance_id_ != browser_plugin::kInstanceIDNone;
702 bool BrowserPlugin::ParsePartitionAttribute(std::string* error_message) {
703 if (HasNavigated()) {
704 *error_message = browser_plugin::kErrorAlreadyNavigated;
708 std::string input = GetPartitionAttribute();
710 // Since the "persist:" prefix is in ASCII, StartsWith will work fine on
711 // UTF-8 encoded |partition_id|. If the prefix is a match, we can safely
712 // remove the prefix without splicing in the middle of a multi-byte codepoint.
713 // We can use the rest of the string as UTF-8 encoded one.
714 if (StartsWithASCII(input, browser_plugin::kPersistPrefix, true)) {
715 size_t index = input.find(":");
716 CHECK(index != std::string::npos);
717 // It is safe to do index + 1, since we tested for the full prefix above.
718 input = input.substr(index + 1);
720 valid_partition_id_ = false;
721 *error_message = browser_plugin::kErrorInvalidPartition;
724 persist_storage_ = true;
726 persist_storage_ = false;
729 valid_partition_id_ = true;
730 storage_partition_id_ = input;
734 bool BrowserPlugin::CanRemovePartitionAttribute(std::string* error_message) {
735 if (HasGuestInstanceID())
736 *error_message = browser_plugin::kErrorCannotRemovePartition;
737 return !HasGuestInstanceID();
740 void BrowserPlugin::ShowSadGraphic() {
741 // We won't paint the contents of the current backing store again so we might
742 // as well toss it out and save memory.
743 backing_store_.reset();
744 // If the BrowserPlugin is scheduled to be deleted, then container_ will be
745 // NULL so we shouldn't attempt to access it.
747 container_->invalidate();
748 // Turn off compositing so we can display the sad graphic.
749 EnableCompositing(false);
752 void BrowserPlugin::ParseAttributes() {
753 // TODO(mthiesse): Handle errors here?
755 ParsePartitionAttribute(&error);
757 // Parse the 'src' attribute last, as it will set the has_navigated_ flag to
758 // true, which prevents changing the 'partition' attribute.
759 ParseSrcAttribute(&error);
762 float BrowserPlugin::GetDeviceScaleFactor() const {
763 if (!render_view_.get())
765 return render_view_->GetWebView()->deviceScaleFactor();
768 void BrowserPlugin::UpdateDeviceScaleFactor(float device_scale_factor) {
769 if (last_device_scale_factor_ == device_scale_factor || !paint_ack_received_)
772 BrowserPluginHostMsg_ResizeGuest_Params params;
773 PopulateResizeGuestParameters(¶ms, plugin_rect(), false);
774 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest(
775 render_view_routing_id_,
780 void BrowserPlugin::TriggerEvent(const std::string& event_name,
781 std::map<std::string, base::Value*>* props) {
785 WebKit::WebFrame* frame = container()->element().document().frame();
789 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
790 v8::Local<v8::Context> context = frame->mainWorldScriptContext();
791 v8::Context::Scope context_scope(context);
793 std::string json_string;
795 base::DictionaryValue dict;
796 for (std::map<std::string, base::Value*>::iterator iter = props->begin(),
797 end = props->end(); iter != end; ++iter) {
798 dict.Set(iter->first, iter->second);
801 JSONStringValueSerializer serializer(&json_string);
802 if (!serializer.Serialize(dict))
806 WebKit::WebDOMEvent dom_event = frame->document().createEvent("CustomEvent");
807 WebKit::WebDOMCustomEvent event = dom_event.to<WebKit::WebDOMCustomEvent>();
809 // The events triggered directly from the plugin <object> are internal events
810 // whose implementation details can (and likely will) change over time. The
811 // wrapper/shim (e.g. <webview> tag) should receive these events, and expose a
812 // more appropriate (and stable) event to the consumers as part of the API.
813 event.initCustomEvent(
814 WebKit::WebString::fromUTF8(GetInternalEventName(event_name.c_str())),
816 WebKit::WebSerializedScriptValue::serialize(
817 v8::String::New(json_string.c_str(), json_string.size())));
818 container()->element().dispatchEvent(event);
821 void BrowserPlugin::UpdateGuestFocusState() {
822 if (!HasGuestInstanceID())
824 bool should_be_focused = ShouldGuestBeFocused();
825 browser_plugin_manager()->Send(new BrowserPluginHostMsg_SetFocus(
826 render_view_routing_id_,
831 bool BrowserPlugin::ShouldGuestBeFocused() const {
832 bool embedder_focused = false;
833 if (render_view_.get())
834 embedder_focused = render_view_->has_focus();
835 return plugin_focused_ && embedder_focused;
838 WebKit::WebPluginContainer* BrowserPlugin::container() const {
842 bool BrowserPlugin::initialize(WebPluginContainer* container) {
846 if (!GetContentClient()->renderer()->AllowBrowserPlugin(container))
849 // Tell |container| to allow this plugin to use script objects.
850 npp_.reset(new NPP_t);
851 container->allowScriptObjects();
853 bindings_.reset(new BrowserPluginBindings(this));
854 container_ = container;
855 container_->setWantsWheelEvents(true);
857 g_plugin_container_map.Get().insert(std::make_pair(container_, this));
861 void BrowserPlugin::EnableCompositing(bool enable) {
862 if (compositing_enabled_ == enable)
865 compositing_enabled_ = enable;
867 // No need to keep the backing store and damage buffer around if we're now
869 backing_store_.reset();
870 current_damage_buffer_.reset();
871 if (!compositing_helper_.get()) {
872 compositing_helper_ =
873 new BrowserPluginCompositingHelper(container_,
874 browser_plugin_manager(),
876 render_view_routing_id_);
879 // We're switching back to the software path. We create a new damage
880 // buffer that can accommodate the current size of the container.
881 BrowserPluginHostMsg_ResizeGuest_Params params;
882 // Request a full repaint from the guest even if its size is not actually
884 PopulateResizeGuestParameters(¶ms,
886 true /* needs_repaint */);
887 paint_ack_received_ = false;
888 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest(
889 render_view_routing_id_,
893 compositing_helper_->EnableCompositing(enable);
896 void BrowserPlugin::destroy() {
897 // If the plugin was initialized then it has a valid |npp_| identifier, and
898 // the |container_| must clear references to the plugin's script objects.
899 DCHECK(!npp_ || container_);
901 container_->clearScriptObjects();
903 // The BrowserPlugin's WebPluginContainer is deleted immediately after this
904 // call returns, so let's not keep a reference to it around.
905 g_plugin_container_map.Get().erase(container_);
907 if (compositing_helper_.get())
908 compositing_helper_->OnContainerDestroy();
909 // Will be a no-op if the mouse is not currently locked.
910 if (render_view_.get())
911 render_view_->mouse_lock_dispatcher()->OnLockTargetDestroyed(this);
912 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
915 NPObject* BrowserPlugin::scriptableObject() {
919 NPObject* browser_plugin_np_object(bindings_->np_object());
920 // The object is expected to be retained before it is returned.
921 WebKit::WebBindings::retainObject(browser_plugin_np_object);
922 return browser_plugin_np_object;
925 NPP BrowserPlugin::pluginNPP() {
929 bool BrowserPlugin::supportsKeyboardFocus() const {
933 bool BrowserPlugin::supportsEditCommands() const {
937 bool BrowserPlugin::canProcessDrag() const {
941 void BrowserPlugin::paint(WebCanvas* canvas, const WebRect& rect) {
942 if (guest_crashed_) {
943 if (!sad_guest_) // Lazily initialize bitmap.
944 sad_guest_ = content::GetContentClient()->renderer()->
945 GetSadWebViewBitmap();
946 // content_shell does not have the sad plugin bitmap, so we'll paint black
947 // instead to make it clear that something went wrong.
949 PaintSadPlugin(canvas, plugin_rect_, *sad_guest_);
953 SkAutoCanvasRestore auto_restore(canvas, true);
954 canvas->translate(plugin_rect_.x(), plugin_rect_.y());
955 SkRect image_data_rect = SkRect::MakeXYWH(
958 SkIntToScalar(plugin_rect_.width()),
959 SkIntToScalar(plugin_rect_.height()));
960 canvas->clipRect(image_data_rect);
961 // Paint black or white in case we have nothing in our backing store or we
962 // need to show a gutter.
964 paint.setStyle(SkPaint::kFill_Style);
965 paint.setColor(guest_crashed_ ? SK_ColorBLACK : SK_ColorWHITE);
966 canvas->drawRect(image_data_rect, paint);
967 // Stay a solid color if we have never set a non-empty src, or we don't have a
969 if (!backing_store_.get() || !HasGuestInstanceID())
971 float inverse_scale_factor = 1.0f / backing_store_->GetScaleFactor();
972 canvas->scale(inverse_scale_factor, inverse_scale_factor);
973 canvas->drawBitmap(backing_store_->GetBitmap(), 0, 0);
976 bool BrowserPlugin::InBounds(const gfx::Point& position) const {
977 // Note that even for plugins that are rotated using rotate transformations,
978 // we use the the |plugin_rect_| provided by updateGeometry, which means we
979 // will be off if |position| is within the plugin rect but does not fall
980 // within the actual plugin boundary. Not supporting such edge case is OK
981 // since this function should not be used for making security-sensitive
983 // This also does not take overlapping plugins into account.
984 bool result = position.x() >= plugin_rect_.x() &&
985 position.x() < plugin_rect_.x() + plugin_rect_.width() &&
986 position.y() >= plugin_rect_.y() &&
987 position.y() < plugin_rect_.y() + plugin_rect_.height();
991 gfx::Point BrowserPlugin::ToLocalCoordinates(const gfx::Point& point) const {
993 return container_->windowToLocalPoint(WebKit::WebPoint(point));
994 return gfx::Point(point.x() - plugin_rect_.x(), point.y() - plugin_rect_.y());
998 bool BrowserPlugin::ShouldForwardToBrowserPlugin(
999 const IPC::Message& message) {
1000 switch (message.type()) {
1001 case BrowserPluginMsg_AdvanceFocus::ID:
1002 case BrowserPluginMsg_Attach_ACK::ID:
1003 case BrowserPluginMsg_BuffersSwapped::ID:
1004 case BrowserPluginMsg_CompositorFrameSwapped::ID:
1005 case BrowserPluginMsg_GuestContentWindowReady::ID:
1006 case BrowserPluginMsg_GuestGone::ID:
1007 case BrowserPluginMsg_SetCursor::ID:
1008 case BrowserPluginMsg_SetMouseLock::ID:
1009 case BrowserPluginMsg_ShouldAcceptTouchEvents::ID:
1010 case BrowserPluginMsg_UpdatedName::ID:
1011 case BrowserPluginMsg_UpdateRect::ID:
1019 void BrowserPlugin::updateGeometry(
1020 const WebRect& window_rect,
1021 const WebRect& clip_rect,
1022 const WebVector<WebRect>& cut_outs_rects,
1024 int old_width = width();
1025 int old_height = height();
1026 plugin_rect_ = window_rect;
1030 // In AutoSize mode, guests don't care when the BrowserPlugin container is
1031 // resized. If |!paint_ack_received_|, then we are still waiting on a
1032 // previous resize to be ACK'ed and so we don't issue additional resizes
1033 // until the previous one is ACK'ed.
1034 // TODO(mthiesse): Assess the performance of calling GetAutoSizeAttribute() on
1036 if (!paint_ack_received_ ||
1037 (old_width == window_rect.width && old_height == window_rect.height) ||
1038 GetAutoSizeAttribute()) {
1039 // Let the browser know about the updated view rect.
1040 browser_plugin_manager()->Send(new BrowserPluginHostMsg_UpdateGeometry(
1041 render_view_routing_id_, guest_instance_id_, plugin_rect_));
1045 BrowserPluginHostMsg_ResizeGuest_Params params;
1046 PopulateResizeGuestParameters(¶ms, plugin_rect(), false);
1047 paint_ack_received_ = false;
1048 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest(
1049 render_view_routing_id_,
1054 void BrowserPlugin::SwapDamageBuffers() {
1055 current_damage_buffer_.reset(pending_damage_buffer_.release());
1056 paint_ack_received_ = true;
1059 void BrowserPlugin::PopulateResizeGuestParameters(
1060 BrowserPluginHostMsg_ResizeGuest_Params* params,
1061 const gfx::Rect& view_rect,
1062 bool needs_repaint) {
1063 params->size_changed = true;
1064 params->view_rect = view_rect;
1065 params->repaint = needs_repaint;
1066 params->scale_factor = GetDeviceScaleFactor();
1067 if (last_device_scale_factor_ != params->scale_factor){
1068 params->repaint = true;
1069 last_device_scale_factor_ = params->scale_factor;
1072 // In HW compositing mode, we do not need a damage buffer.
1073 if (compositing_enabled_)
1076 const size_t stride = skia::PlatformCanvasStrideForWidth(view_rect.width());
1077 // Make sure the size of the damage buffer is at least four bytes so that we
1078 // can fit in a magic word to verify that the memory is shared correctly.
1080 std::max(sizeof(unsigned int),
1081 static_cast<size_t>(view_rect.height() *
1083 GetDeviceScaleFactor() *
1084 GetDeviceScaleFactor()));
1086 params->damage_buffer_size = size;
1087 pending_damage_buffer_.reset(
1088 CreateDamageBuffer(size, ¶ms->damage_buffer_handle));
1089 if (!pending_damage_buffer_)
1091 params->damage_buffer_sequence_id = ++damage_buffer_sequence_id_;
1094 void BrowserPlugin::GetDamageBufferWithSizeParams(
1095 BrowserPluginHostMsg_AutoSize_Params* auto_size_params,
1096 BrowserPluginHostMsg_ResizeGuest_Params* resize_guest_params,
1097 bool needs_repaint) {
1098 if (auto_size_params) {
1099 PopulateAutoSizeParameters(auto_size_params, GetAutoSizeAttribute());
1101 max_auto_size_ = gfx::Size();
1103 gfx::Size view_size = (auto_size_params && auto_size_params->enable) ?
1104 auto_size_params->max_size : gfx::Size(width(), height());
1105 if (view_size.IsEmpty())
1107 paint_ack_received_ = false;
1108 gfx::Rect view_rect = gfx::Rect(plugin_rect_.origin(), view_size);
1109 PopulateResizeGuestParameters(resize_guest_params, view_rect, needs_repaint);
1112 #if defined(OS_POSIX)
1113 base::SharedMemory* BrowserPlugin::CreateDamageBuffer(
1115 base::SharedMemoryHandle* damage_buffer_handle) {
1116 scoped_ptr<base::SharedMemory> shared_buf(
1117 content::RenderThread::Get()->HostAllocateSharedMemoryBuffer(
1121 if (shared_buf->Map(size)) {
1122 // Insert the magic word.
1123 *static_cast<unsigned int*>(shared_buf->memory()) = 0xdeadbeef;
1124 shared_buf->ShareToProcess(base::GetCurrentProcessHandle(),
1125 damage_buffer_handle);
1126 return shared_buf.release();
1132 #elif defined(OS_WIN)
1133 base::SharedMemory* BrowserPlugin::CreateDamageBuffer(
1135 base::SharedMemoryHandle* damage_buffer_handle) {
1136 scoped_ptr<base::SharedMemory> shared_buf(new base::SharedMemory());
1138 if (!shared_buf->CreateAndMapAnonymous(size)) {
1139 NOTREACHED() << "Buffer allocation failed";
1143 // Insert the magic word.
1144 *static_cast<unsigned int*>(shared_buf->memory()) = 0xdeadbeef;
1145 if (shared_buf->ShareToProcess(base::GetCurrentProcessHandle(),
1146 damage_buffer_handle))
1147 return shared_buf.release();
1153 void BrowserPlugin::updateFocus(bool focused) {
1154 if (plugin_focused_ == focused)
1157 bool old_guest_focus_state = ShouldGuestBeFocused();
1158 plugin_focused_ = focused;
1160 if (ShouldGuestBeFocused() != old_guest_focus_state)
1161 UpdateGuestFocusState();
1164 void BrowserPlugin::updateVisibility(bool visible) {
1165 if (visible_ == visible)
1169 if (!HasGuestInstanceID())
1172 if (compositing_helper_.get())
1173 compositing_helper_->UpdateVisibility(visible);
1175 browser_plugin_manager()->Send(new BrowserPluginHostMsg_SetVisibility(
1176 render_view_routing_id_,
1181 bool BrowserPlugin::acceptsInputEvents() {
1185 bool BrowserPlugin::handleInputEvent(const WebKit::WebInputEvent& event,
1186 WebKit::WebCursorInfo& cursor_info) {
1187 if (guest_crashed_ || !HasGuestInstanceID())
1190 if (event.type == WebKit::WebInputEvent::ContextMenu)
1193 const WebKit::WebInputEvent* modified_event = &event;
1194 scoped_ptr<WebKit::WebTouchEvent> touch_event;
1195 // WebKit gives BrowserPlugin a list of touches that are down, but the browser
1196 // process expects a list of all touches. We modify the TouchEnd event here to
1197 // match these expectations.
1198 if (event.type == WebKit::WebInputEvent::TouchEnd) {
1199 const WebKit::WebTouchEvent* orig_touch_event =
1200 static_cast<const WebKit::WebTouchEvent*>(&event);
1201 touch_event.reset(new WebKit::WebTouchEvent());
1202 memcpy(touch_event.get(), orig_touch_event, sizeof(WebKit::WebTouchEvent));
1203 if (touch_event->changedTouchesLength > 0) {
1204 memcpy(&touch_event->touches[touch_event->touchesLength],
1205 &touch_event->changedTouches,
1206 touch_event->changedTouchesLength * sizeof(WebKit::WebTouchPoint));
1208 touch_event->touchesLength += touch_event->changedTouchesLength;
1209 modified_event = touch_event.get();
1212 if (WebKit::WebInputEvent::isKeyboardEventType(event.type) &&
1213 !edit_commands_.empty()) {
1214 browser_plugin_manager()->Send(
1215 new BrowserPluginHostMsg_SetEditCommandsForNextKeyEvent(
1216 render_view_routing_id_,
1219 edit_commands_.clear();
1222 browser_plugin_manager()->Send(
1223 new BrowserPluginHostMsg_HandleInputEvent(render_view_routing_id_,
1227 GetWebKitCursorInfo(cursor_, &cursor_info);
1231 bool BrowserPlugin::handleDragStatusUpdate(WebKit::WebDragStatus drag_status,
1232 const WebKit::WebDragData& drag_data,
1233 WebKit::WebDragOperationsMask mask,
1234 const WebKit::WebPoint& position,
1235 const WebKit::WebPoint& screen) {
1236 if (guest_crashed_ || !HasGuestInstanceID())
1238 browser_plugin_manager()->Send(
1239 new BrowserPluginHostMsg_DragStatusUpdate(
1240 render_view_routing_id_,
1243 DropDataBuilder::Build(drag_data),
1249 void BrowserPlugin::didReceiveResponse(
1250 const WebKit::WebURLResponse& response) {
1253 void BrowserPlugin::didReceiveData(const char* data, int data_length) {
1256 void BrowserPlugin::didFinishLoading() {
1259 void BrowserPlugin::didFailLoading(const WebKit::WebURLError& error) {
1262 void BrowserPlugin::didFinishLoadingFrameRequest(const WebKit::WebURL& url,
1263 void* notify_data) {
1266 void BrowserPlugin::didFailLoadingFrameRequest(
1267 const WebKit::WebURL& url,
1269 const WebKit::WebURLError& error) {
1272 bool BrowserPlugin::executeEditCommand(const WebKit::WebString& name) {
1273 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ExecuteEditCommand(
1274 render_view_routing_id_,
1278 // BrowserPlugin swallows edit commands.
1282 bool BrowserPlugin::executeEditCommand(const WebKit::WebString& name,
1283 const WebKit::WebString& value) {
1284 edit_commands_.push_back(EditCommand(name.utf8(), value.utf8()));
1285 // BrowserPlugin swallows edit commands.
1289 void BrowserPlugin::OnLockMouseACK(bool succeeded) {
1290 mouse_locked_ = succeeded;
1291 browser_plugin_manager()->Send(new BrowserPluginHostMsg_LockMouse_ACK(
1292 render_view_routing_id_,
1297 void BrowserPlugin::OnMouseLockLost() {
1298 mouse_locked_ = false;
1299 browser_plugin_manager()->Send(new BrowserPluginHostMsg_UnlockMouse_ACK(
1300 render_view_routing_id_,
1301 guest_instance_id_));
1304 bool BrowserPlugin::HandleMouseLockedInputEvent(
1305 const WebKit::WebMouseEvent& event) {
1306 browser_plugin_manager()->Send(
1307 new BrowserPluginHostMsg_HandleInputEvent(render_view_routing_id_,
1314 } // namespace content