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_manager.h"
21 #include "content/renderer/child_frame_compositing_helper.h"
22 #include "content/renderer/cursor_utils.h"
23 #include "content/renderer/drop_data_builder.h"
24 #include "content/renderer/render_thread_impl.h"
25 #include "content/renderer/sad_plugin.h"
26 #include "content/renderer/v8_value_converter_impl.h"
27 #include "third_party/WebKit/public/platform/WebRect.h"
28 #include "third_party/WebKit/public/web/WebBindings.h"
29 #include "third_party/WebKit/public/web/WebDOMCustomEvent.h"
30 #include "third_party/WebKit/public/web/WebDocument.h"
31 #include "third_party/WebKit/public/web/WebElement.h"
32 #include "third_party/WebKit/public/web/WebInputEvent.h"
33 #include "third_party/WebKit/public/web/WebLocalFrame.h"
34 #include "third_party/WebKit/public/web/WebPluginContainer.h"
35 #include "third_party/WebKit/public/web/WebPluginParams.h"
36 #include "third_party/WebKit/public/web/WebScriptSource.h"
37 #include "third_party/WebKit/public/web/WebView.h"
38 #include "third_party/skia/include/core/SkCanvas.h"
39 #include "ui/events/keycodes/keyboard_codes.h"
42 #include "base/sys_info.h"
45 using blink::WebCanvas;
46 using blink::WebPluginContainer;
47 using blink::WebPluginParams;
48 using blink::WebPoint;
51 using blink::WebVector;
57 const char* kCustomPersistPartition = "persist:custom_plugin";
59 static std::string GetInternalEventName(const char* event_name) {
60 return base::StringPrintf("-internal-%s", event_name);
65 BrowserPlugin::BrowserPlugin(RenderViewImpl* render_view,
66 blink::WebFrame* frame,
68 : guest_instance_id_(browser_plugin::kInstanceIDNone),
70 render_view_(render_view->AsWeakPtr()),
71 render_view_routing_id_(render_view->GetRoutingID()),
73 paint_ack_received_(true),
74 last_device_scale_factor_(1.0f),
76 guest_crashed_(false),
77 is_auto_size_state_dirty_(false),
78 persist_storage_(false),
79 valid_partition_id_(true),
80 content_window_routing_id_(MSG_ROUTING_NONE),
81 plugin_focused_(false),
83 auto_navigate_(auto_navigate),
84 before_first_navigation_(true),
86 browser_plugin_manager_(render_view->GetBrowserPluginManager()),
87 embedder_frame_url_(frame->document().url()),
88 weak_ptr_factory_(this) {
91 BrowserPlugin::~BrowserPlugin() {
92 // If the BrowserPlugin has never navigated then the browser process and
93 // BrowserPluginManager don't know about it and so there is nothing to do
95 if (!HasGuestInstanceID())
97 browser_plugin_manager()->RemoveBrowserPlugin(guest_instance_id_);
98 browser_plugin_manager()->Send(
99 new BrowserPluginHostMsg_PluginDestroyed(render_view_routing_id_,
100 guest_instance_id_));
103 bool BrowserPlugin::OnMessageReceived(const IPC::Message& message) {
105 IPC_BEGIN_MESSAGE_MAP(BrowserPlugin, message)
106 IPC_MESSAGE_HANDLER(BrowserPluginMsg_AdvanceFocus, OnAdvanceFocus)
107 IPC_MESSAGE_HANDLER(BrowserPluginMsg_Attach_ACK, OnAttachACK)
108 IPC_MESSAGE_HANDLER(BrowserPluginMsg_BuffersSwapped, OnBuffersSwapped)
109 IPC_MESSAGE_HANDLER_GENERIC(BrowserPluginMsg_CompositorFrameSwapped,
110 OnCompositorFrameSwapped(message))
111 IPC_MESSAGE_HANDLER(BrowserPluginMsg_CopyFromCompositingSurface,
112 OnCopyFromCompositingSurface)
113 IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestContentWindowReady,
114 OnGuestContentWindowReady)
115 IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestGone, OnGuestGone)
116 IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetCursor, OnSetCursor)
117 IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetMouseLock, OnSetMouseLock)
118 IPC_MESSAGE_HANDLER(BrowserPluginMsg_ShouldAcceptTouchEvents,
119 OnShouldAcceptTouchEvents)
120 IPC_MESSAGE_HANDLER(BrowserPluginMsg_UpdatedName, OnUpdatedName)
121 IPC_MESSAGE_HANDLER(BrowserPluginMsg_UpdateRect, OnUpdateRect)
122 IPC_MESSAGE_UNHANDLED(handled = false)
123 IPC_END_MESSAGE_MAP()
127 void BrowserPlugin::UpdateDOMAttribute(const std::string& attribute_name,
128 const std::string& attribute_value) {
132 blink::WebElement element = container()->element();
133 blink::WebString web_attribute_name =
134 blink::WebString::fromUTF8(attribute_name);
135 if (!HasDOMAttribute(attribute_name) ||
136 (std::string(element.getAttribute(web_attribute_name).utf8()) !=
138 element.setAttribute(web_attribute_name,
139 blink::WebString::fromUTF8(attribute_value));
143 void BrowserPlugin::RemoveDOMAttribute(const std::string& attribute_name) {
147 container()->element().removeAttribute(
148 blink::WebString::fromUTF8(attribute_name));
151 std::string BrowserPlugin::GetDOMAttributeValue(
152 const std::string& attribute_name) const {
154 return std::string();
156 return container()->element().getAttribute(
157 blink::WebString::fromUTF8(attribute_name)).utf8();
160 bool BrowserPlugin::HasDOMAttribute(const std::string& attribute_name) const {
164 return container()->element().hasAttribute(
165 blink::WebString::fromUTF8(attribute_name));
168 std::string BrowserPlugin::GetNameAttribute() const {
169 return GetDOMAttributeValue(browser_plugin::kAttributeName);
172 bool BrowserPlugin::GetAllowTransparencyAttribute() const {
173 return HasDOMAttribute(browser_plugin::kAttributeAllowTransparency);
176 std::string BrowserPlugin::GetSrcAttribute() const {
177 return GetDOMAttributeValue(browser_plugin::kAttributeSrc);
180 bool BrowserPlugin::GetAutoSizeAttribute() const {
181 return HasDOMAttribute(browser_plugin::kAttributeAutoSize);
184 int BrowserPlugin::GetMaxHeightAttribute() const {
186 base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMaxHeight),
191 int BrowserPlugin::GetMaxWidthAttribute() const {
193 base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMaxWidth),
198 int BrowserPlugin::GetMinHeightAttribute() const {
200 base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMinHeight),
205 int BrowserPlugin::GetMinWidthAttribute() const {
207 base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMinWidth),
212 int BrowserPlugin::GetAdjustedMaxHeight() const {
213 int max_height = GetMaxHeightAttribute();
214 return max_height ? max_height : height();
217 int BrowserPlugin::GetAdjustedMaxWidth() const {
218 int max_width = GetMaxWidthAttribute();
219 return max_width ? max_width : width();
222 int BrowserPlugin::GetAdjustedMinHeight() const {
223 int min_height = GetMinHeightAttribute();
224 // FrameView.cpp does not allow this value to be <= 0, so when the value is
225 // unset (or set to 0), we set it to the container size.
226 min_height = min_height ? min_height : height();
227 // For autosize, minHeight should not be bigger than maxHeight.
228 return std::min(min_height, GetAdjustedMaxHeight());
231 int BrowserPlugin::GetAdjustedMinWidth() const {
232 int min_width = GetMinWidthAttribute();
233 // FrameView.cpp does not allow this value to be <= 0, so when the value is
234 // unset (or set to 0), we set it to the container size.
235 min_width = min_width ? min_width : width();
236 // For autosize, minWidth should not be bigger than maxWidth.
237 return std::min(min_width, GetAdjustedMaxWidth());
240 std::string BrowserPlugin::GetPartitionAttribute() const {
241 return GetDOMAttributeValue(browser_plugin::kAttributePartition);
244 void BrowserPlugin::ParseNameAttribute() {
245 if (!HasGuestInstanceID())
247 browser_plugin_manager()->Send(
248 new BrowserPluginHostMsg_SetName(render_view_routing_id_,
250 GetNameAttribute()));
253 void BrowserPlugin::ParseAllowTransparencyAttribute() {
254 if (!HasGuestInstanceID())
257 bool opaque = !GetAllowTransparencyAttribute();
259 if (compositing_helper_)
260 compositing_helper_->SetContentsOpaque(opaque);
262 browser_plugin_manager()->Send(new BrowserPluginHostMsg_SetContentsOpaque(
263 render_view_routing_id_,
268 bool BrowserPlugin::ParseSrcAttribute(std::string* error_message) {
269 if (!valid_partition_id_) {
270 *error_message = browser_plugin::kErrorInvalidPartition;
273 std::string src = GetSrcAttribute();
277 // If we haven't created the guest yet, do so now. We will navigate it right
278 // after creation. If |src| is empty, we can delay the creation until we
280 if (!HasGuestInstanceID()) {
281 // On initial navigation, we request an instance ID from the browser
282 // process. We essentially ignore all subsequent calls to SetSrcAttribute
283 // until we receive an instance ID. |before_first_navigation_|
284 // prevents BrowserPlugin from allocating more than one instance ID.
285 // Upon receiving an instance ID from the browser process, we continue
286 // the process of navigation by populating the
287 // BrowserPluginHostMsg_Attach_Params with the current state of
288 // BrowserPlugin and sending a BrowserPluginHostMsg_CreateGuest to the
289 // browser process in order to create a new guest.
290 if (before_first_navigation_) {
291 browser_plugin_manager()->AllocateInstanceID(
292 weak_ptr_factory_.GetWeakPtr());
293 before_first_navigation_ = false;
298 browser_plugin_manager()->Send(
299 new BrowserPluginHostMsg_NavigateGuest(render_view_routing_id_,
305 void BrowserPlugin::ParseAutoSizeAttribute() {
306 last_view_size_ = plugin_rect_.size();
307 is_auto_size_state_dirty_ = true;
308 UpdateGuestAutoSizeState(GetAutoSizeAttribute());
311 void BrowserPlugin::PopulateAutoSizeParameters(
312 BrowserPluginHostMsg_AutoSize_Params* params, bool auto_size_enabled) {
313 params->enable = auto_size_enabled;
314 // No need to populate the params if autosize is off.
315 if (auto_size_enabled) {
316 params->max_size = gfx::Size(GetAdjustedMaxWidth(), GetAdjustedMaxHeight());
317 params->min_size = gfx::Size(GetAdjustedMinWidth(), GetAdjustedMinHeight());
319 if (max_auto_size_ != params->max_size)
320 is_auto_size_state_dirty_ = true;
322 max_auto_size_ = params->max_size;
324 max_auto_size_ = gfx::Size();
328 void BrowserPlugin::UpdateGuestAutoSizeState(bool auto_size_enabled) {
329 // If we haven't yet heard back from the guest about the last resize request,
330 // then we don't issue another request until we do in
331 // BrowserPlugin::OnUpdateRect.
332 if (!HasGuestInstanceID() || !paint_ack_received_)
335 BrowserPluginHostMsg_AutoSize_Params auto_size_params;
336 BrowserPluginHostMsg_ResizeGuest_Params resize_guest_params;
337 if (auto_size_enabled) {
338 GetSizeParams(&auto_size_params, &resize_guest_params, true);
340 GetSizeParams(NULL, &resize_guest_params, true);
342 paint_ack_received_ = false;
343 browser_plugin_manager()->Send(
344 new BrowserPluginHostMsg_SetAutoSize(render_view_routing_id_,
347 resize_guest_params));
350 void BrowserPlugin::OnInstanceIDAllocated(int guest_instance_id) {
351 CHECK(guest_instance_id != browser_plugin::kInstanceIDNone);
353 if (auto_navigate_) {
354 scoped_ptr<base::DictionaryValue> params(new base::DictionaryValue());
355 Attach(guest_instance_id, params.Pass());
359 std::map<std::string, base::Value*> props;
360 props[browser_plugin::kWindowID] =
361 new base::FundamentalValue(guest_instance_id);
362 TriggerEvent(browser_plugin::kEventInternalInstanceIDAllocated, &props);
365 void BrowserPlugin::Attach(int guest_instance_id,
366 scoped_ptr<base::DictionaryValue> extra_params) {
367 CHECK(guest_instance_id != browser_plugin::kInstanceIDNone);
369 // If this BrowserPlugin is already attached to a guest, then do nothing.
370 if (HasGuestInstanceID())
373 // This API may be called directly without setting the src attribute.
374 // In that case, we need to make sure we don't allocate another instance ID.
375 before_first_navigation_ = false;
376 guest_instance_id_ = guest_instance_id;
377 browser_plugin_manager()->AddBrowserPlugin(guest_instance_id, this);
379 BrowserPluginHostMsg_Attach_Params attach_params;
380 attach_params.focused = ShouldGuestBeFocused();
381 attach_params.visible = visible_;
382 attach_params.opaque = !GetAllowTransparencyAttribute();
383 attach_params.name = GetNameAttribute();
384 attach_params.storage_partition_id = storage_partition_id_;
385 attach_params.persist_storage = persist_storage_;
386 attach_params.src = GetSrcAttribute();
387 attach_params.embedder_frame_url = embedder_frame_url_;
388 GetSizeParams(&attach_params.auto_size_params,
389 &attach_params.resize_guest_params,
392 browser_plugin_manager()->Send(
393 new BrowserPluginHostMsg_Attach(render_view_routing_id_,
394 guest_instance_id_, attach_params,
398 void BrowserPlugin::DidCommitCompositorFrame() {
399 if (compositing_helper_.get())
400 compositing_helper_->DidCommitCompositorFrame();
403 void BrowserPlugin::OnAdvanceFocus(int guest_instance_id, bool reverse) {
404 DCHECK(render_view_.get());
405 render_view_->GetWebView()->advanceFocus(reverse);
408 void BrowserPlugin::OnAttachACK(
409 int guest_instance_id,
410 const BrowserPluginMsg_Attach_ACK_Params& params) {
411 // Update BrowserPlugin attributes to match the state of the guest.
412 if (!params.name.empty())
413 OnUpdatedName(guest_instance_id, params.name);
414 if (!params.storage_partition_id.empty()) {
415 std::string partition_name =
416 (params.persist_storage ? browser_plugin::kPersistPrefix : "") +
417 params.storage_partition_id;
418 UpdateDOMAttribute(browser_plugin::kAttributePartition, partition_name);
423 void BrowserPlugin::OnBuffersSwapped(
425 const FrameMsg_BuffersSwapped_Params& params) {
426 EnableCompositing(true);
428 compositing_helper_->OnBuffersSwapped(params.size,
432 GetDeviceScaleFactor());
435 void BrowserPlugin::OnCompositorFrameSwapped(const IPC::Message& message) {
436 BrowserPluginMsg_CompositorFrameSwapped::Param param;
437 if (!BrowserPluginMsg_CompositorFrameSwapped::Read(&message, ¶m))
439 scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame);
440 param.b.frame.AssignTo(frame.get());
442 EnableCompositing(true);
443 compositing_helper_->OnCompositorFrameSwapped(frame.Pass(),
444 param.b.producing_route_id,
445 param.b.output_surface_id,
446 param.b.producing_host_id,
447 param.b.shared_memory_handle);
450 void BrowserPlugin::OnCopyFromCompositingSurface(int guest_instance_id,
452 gfx::Rect source_rect,
453 gfx::Size dest_size) {
454 if (!compositing_helper_) {
455 browser_plugin_manager()->Send(
456 new BrowserPluginHostMsg_CopyFromCompositingSurfaceAck(
457 render_view_routing_id_,
463 compositing_helper_->CopyFromCompositingSurface(request_id, source_rect,
467 void BrowserPlugin::OnGuestContentWindowReady(int guest_instance_id,
468 int content_window_routing_id) {
469 DCHECK(content_window_routing_id != MSG_ROUTING_NONE);
470 content_window_routing_id_ = content_window_routing_id;
473 void BrowserPlugin::OnGuestGone(int guest_instance_id) {
474 guest_crashed_ = true;
476 // Turn off compositing so we can display the sad graphic. Changes to
477 // compositing state will show up at a later time after a layout and commit.
478 EnableCompositing(false);
480 // Queue up showing the sad graphic to give content embedders an opportunity
481 // to fire their listeners and potentially overlay the webview with custom
482 // behavior. If the BrowserPlugin is destroyed in the meantime, then the
483 // task will not be executed.
484 base::MessageLoop::current()->PostTask(
486 base::Bind(&BrowserPlugin::ShowSadGraphic,
487 weak_ptr_factory_.GetWeakPtr()));
490 void BrowserPlugin::OnSetCursor(int guest_instance_id,
491 const WebCursor& cursor) {
495 void BrowserPlugin::OnSetMouseLock(int guest_instance_id,
500 render_view_->mouse_lock_dispatcher()->LockMouse(this);
502 if (!mouse_locked_) {
503 OnLockMouseACK(false);
506 render_view_->mouse_lock_dispatcher()->UnlockMouse(this);
510 void BrowserPlugin::OnShouldAcceptTouchEvents(int guest_instance_id,
513 container()->requestTouchEventType(accept ?
514 blink::WebPluginContainer::TouchEventRequestTypeRaw :
515 blink::WebPluginContainer::TouchEventRequestTypeNone);
519 void BrowserPlugin::OnUpdatedName(int guest_instance_id,
520 const std::string& name) {
521 UpdateDOMAttribute(browser_plugin::kAttributeName, name);
524 void BrowserPlugin::OnUpdateRect(
525 int guest_instance_id,
526 const BrowserPluginMsg_UpdateRect_Params& params) {
527 // Note that there is no need to send ACK for this message.
528 // If the guest has updated pixels then it is no longer crashed.
529 guest_crashed_ = false;
531 bool auto_size = GetAutoSizeAttribute();
532 // We receive a resize ACK in regular mode, but not in autosize.
533 // In Compositing mode, we need to do it here so we can continue sending
534 // resize messages when needed.
535 if (params.is_resize_ack || (auto_size || is_auto_size_state_dirty_))
536 paint_ack_received_ = true;
538 bool was_auto_size_state_dirty = auto_size && is_auto_size_state_dirty_;
539 is_auto_size_state_dirty_ = false;
541 if ((!auto_size && (width() != params.view_size.width() ||
542 height() != params.view_size.height())) ||
543 (auto_size && was_auto_size_state_dirty) ||
544 GetDeviceScaleFactor() != params.scale_factor) {
545 UpdateGuestAutoSizeState(auto_size);
549 if (auto_size && (params.view_size != last_view_size_))
550 last_view_size_ = params.view_size;
552 BrowserPluginHostMsg_AutoSize_Params auto_size_params;
553 BrowserPluginHostMsg_ResizeGuest_Params resize_guest_params;
555 // BrowserPluginHostMsg_UpdateRect_ACK is used by both the compositing and
556 // software paths to piggyback updated autosize parameters.
558 PopulateAutoSizeParameters(&auto_size_params, auto_size);
560 browser_plugin_manager()->Send(
561 new BrowserPluginHostMsg_SetAutoSize(render_view_routing_id_,
564 resize_guest_params));
567 void BrowserPlugin::ParseSizeContraintsChanged() {
568 bool auto_size = GetAutoSizeAttribute();
570 is_auto_size_state_dirty_ = true;
571 UpdateGuestAutoSizeState(true);
575 bool BrowserPlugin::InAutoSizeBounds(const gfx::Size& size) const {
576 return size.width() <= GetAdjustedMaxWidth() &&
577 size.height() <= GetAdjustedMaxHeight();
580 NPObject* BrowserPlugin::GetContentWindow() const {
581 if (content_window_routing_id_ == MSG_ROUTING_NONE)
583 RenderViewImpl* guest_render_view = RenderViewImpl::FromRoutingID(
584 content_window_routing_id_);
585 if (!guest_render_view)
587 blink::WebFrame* guest_frame = guest_render_view->GetWebView()->mainFrame();
588 return guest_frame->windowObject();
591 bool BrowserPlugin::HasNavigated() const {
592 return !before_first_navigation_;
595 bool BrowserPlugin::HasGuestInstanceID() const {
596 return guest_instance_id_ != browser_plugin::kInstanceIDNone;
599 bool BrowserPlugin::ParsePartitionAttribute(std::string* error_message) {
600 if (HasNavigated()) {
601 *error_message = browser_plugin::kErrorAlreadyNavigated;
607 input = kCustomPersistPartition;
609 input = GetPartitionAttribute();
611 // Since the "persist:" prefix is in ASCII, StartsWith will work fine on
612 // UTF-8 encoded |partition_id|. If the prefix is a match, we can safely
613 // remove the prefix without splicing in the middle of a multi-byte codepoint.
614 // We can use the rest of the string as UTF-8 encoded one.
615 if (StartsWithASCII(input, browser_plugin::kPersistPrefix, true)) {
616 size_t index = input.find(":");
617 CHECK(index != std::string::npos);
618 // It is safe to do index + 1, since we tested for the full prefix above.
619 input = input.substr(index + 1);
621 valid_partition_id_ = false;
622 *error_message = browser_plugin::kErrorInvalidPartition;
625 persist_storage_ = true;
627 persist_storage_ = false;
630 valid_partition_id_ = true;
631 storage_partition_id_ = input;
635 bool BrowserPlugin::CanRemovePartitionAttribute(std::string* error_message) {
636 if (HasGuestInstanceID())
637 *error_message = browser_plugin::kErrorCannotRemovePartition;
638 return !HasGuestInstanceID();
641 void BrowserPlugin::ShowSadGraphic() {
642 // If the BrowserPlugin is scheduled to be deleted, then container_ will be
643 // NULL so we shouldn't attempt to access it.
645 container_->invalidate();
648 void BrowserPlugin::ParseAttributes() {
649 // TODO(mthiesse): Handle errors here?
651 ParsePartitionAttribute(&error);
653 // Parse the 'src' attribute last, as it will set the has_navigated_ flag to
654 // true, which prevents changing the 'partition' attribute.
655 ParseSrcAttribute(&error);
658 float BrowserPlugin::GetDeviceScaleFactor() const {
659 if (!render_view_.get())
661 return render_view_->GetWebView()->deviceScaleFactor();
664 void BrowserPlugin::UpdateDeviceScaleFactor(float device_scale_factor) {
665 if (last_device_scale_factor_ == device_scale_factor || !paint_ack_received_)
668 BrowserPluginHostMsg_ResizeGuest_Params params;
669 PopulateResizeGuestParameters(¶ms, plugin_rect(), false);
670 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest(
671 render_view_routing_id_,
676 void BrowserPlugin::TriggerEvent(const std::string& event_name,
677 std::map<std::string, base::Value*>* props) {
681 blink::WebLocalFrame* frame = container()->element().document().frame();
685 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
686 v8::Local<v8::Context> context = frame->mainWorldScriptContext();
687 v8::Context::Scope context_scope(context);
689 std::string json_string;
691 base::DictionaryValue dict;
692 for (std::map<std::string, base::Value*>::iterator iter = props->begin(),
693 end = props->end(); iter != end; ++iter) {
694 dict.Set(iter->first, iter->second);
697 JSONStringValueSerializer serializer(&json_string);
698 if (!serializer.Serialize(dict))
702 blink::WebDOMEvent dom_event = frame->document().createEvent("CustomEvent");
703 blink::WebDOMCustomEvent event = dom_event.to<blink::WebDOMCustomEvent>();
705 // The events triggered directly from the plugin <object> are internal events
706 // whose implementation details can (and likely will) change over time. The
707 // wrapper/shim (e.g. <webview> tag) should receive these events, and expose a
708 // more appropriate (and stable) event to the consumers as part of the API.
709 event.initCustomEvent(
710 blink::WebString::fromUTF8(GetInternalEventName(event_name.c_str())),
713 blink::WebSerializedScriptValue::serialize(
714 v8::String::NewFromUtf8(context->GetIsolate(),
716 v8::String::kNormalString,
717 json_string.size())));
718 container()->element().dispatchEvent(event);
721 void BrowserPlugin::UpdateGuestFocusState() {
722 if (!HasGuestInstanceID())
724 bool should_be_focused = ShouldGuestBeFocused();
725 browser_plugin_manager()->Send(new BrowserPluginHostMsg_SetFocus(
726 render_view_routing_id_,
731 bool BrowserPlugin::ShouldGuestBeFocused() const {
732 bool embedder_focused = false;
733 if (render_view_.get())
734 embedder_focused = render_view_->has_focus();
735 return plugin_focused_ && embedder_focused;
738 blink::WebPluginContainer* BrowserPlugin::container() const {
742 bool BrowserPlugin::initialize(WebPluginContainer* container) {
746 if (!GetContentClient()->renderer()->AllowBrowserPlugin(container) &&
750 // Tell |container| to allow this plugin to use script objects.
751 npp_.reset(new NPP_t);
752 container->allowScriptObjects();
754 bindings_.reset(new BrowserPluginBindings(this));
755 container_ = container;
756 container_->setWantsWheelEvents(true);
761 void BrowserPlugin::EnableCompositing(bool enable) {
762 bool enabled = !!compositing_helper_;
763 if (enabled == enable)
767 DCHECK(!compositing_helper_.get());
768 if (!compositing_helper_.get()) {
769 compositing_helper_ =
770 ChildFrameCompositingHelper::CreateCompositingHelperForBrowserPlugin(
771 weak_ptr_factory_.GetWeakPtr());
774 compositing_helper_->EnableCompositing(enable);
775 compositing_helper_->SetContentsOpaque(!GetAllowTransparencyAttribute());
778 DCHECK(compositing_helper_.get());
779 compositing_helper_->OnContainerDestroy();
780 compositing_helper_ = NULL;
784 void BrowserPlugin::destroy() {
785 // If the plugin was initialized then it has a valid |npp_| identifier, and
786 // the |container_| must clear references to the plugin's script objects.
787 DCHECK(!npp_ || container_);
789 container_->clearScriptObjects();
791 if (compositing_helper_.get())
792 compositing_helper_->OnContainerDestroy();
794 // Will be a no-op if the mouse is not currently locked.
795 if (render_view_.get())
796 render_view_->mouse_lock_dispatcher()->OnLockTargetDestroyed(this);
797 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
800 NPObject* BrowserPlugin::scriptableObject() {
804 NPObject* browser_plugin_np_object(bindings_->np_object());
805 // The object is expected to be retained before it is returned.
806 blink::WebBindings::retainObject(browser_plugin_np_object);
807 return browser_plugin_np_object;
810 NPP BrowserPlugin::pluginNPP() {
814 bool BrowserPlugin::supportsKeyboardFocus() const {
818 bool BrowserPlugin::supportsEditCommands() const {
822 bool BrowserPlugin::supportsInputMethod() const {
826 bool BrowserPlugin::canProcessDrag() const {
830 void BrowserPlugin::paint(WebCanvas* canvas, const WebRect& rect) {
831 if (guest_crashed_) {
832 if (!sad_guest_) // Lazily initialize bitmap.
833 sad_guest_ = content::GetContentClient()->renderer()->
834 GetSadWebViewBitmap();
835 // content_shell does not have the sad plugin bitmap, so we'll paint black
836 // instead to make it clear that something went wrong.
838 PaintSadPlugin(canvas, plugin_rect_, *sad_guest_);
842 SkAutoCanvasRestore auto_restore(canvas, true);
843 canvas->translate(plugin_rect_.x(), plugin_rect_.y());
844 SkRect image_data_rect = SkRect::MakeXYWH(
847 SkIntToScalar(plugin_rect_.width()),
848 SkIntToScalar(plugin_rect_.height()));
849 canvas->clipRect(image_data_rect);
850 // Paint black or white in case we have nothing in our backing store or we
851 // need to show a gutter.
853 paint.setStyle(SkPaint::kFill_Style);
854 paint.setColor(guest_crashed_ ? SK_ColorBLACK : SK_ColorWHITE);
855 canvas->drawRect(image_data_rect, paint);
859 bool BrowserPlugin::ShouldForwardToBrowserPlugin(
860 const IPC::Message& message) {
861 switch (message.type()) {
862 case BrowserPluginMsg_AdvanceFocus::ID:
863 case BrowserPluginMsg_Attach_ACK::ID:
864 case BrowserPluginMsg_BuffersSwapped::ID:
865 case BrowserPluginMsg_CompositorFrameSwapped::ID:
866 case BrowserPluginMsg_CopyFromCompositingSurface::ID:
867 case BrowserPluginMsg_GuestContentWindowReady::ID:
868 case BrowserPluginMsg_GuestGone::ID:
869 case BrowserPluginMsg_SetCursor::ID:
870 case BrowserPluginMsg_SetMouseLock::ID:
871 case BrowserPluginMsg_ShouldAcceptTouchEvents::ID:
872 case BrowserPluginMsg_UpdatedName::ID:
873 case BrowserPluginMsg_UpdateRect::ID:
881 void BrowserPlugin::updateGeometry(
882 const WebRect& window_rect,
883 const WebRect& clip_rect,
884 const WebVector<WebRect>& cut_outs_rects,
886 int old_width = width();
887 int old_height = height();
888 plugin_rect_ = window_rect;
892 // In AutoSize mode, guests don't care when the BrowserPlugin container is
893 // resized. If |!paint_ack_received_|, then we are still waiting on a
894 // previous resize to be ACK'ed and so we don't issue additional resizes
895 // until the previous one is ACK'ed.
896 // TODO(mthiesse): Assess the performance of calling GetAutoSizeAttribute() on
898 if (!paint_ack_received_ ||
899 (old_width == window_rect.width && old_height == window_rect.height) ||
900 GetAutoSizeAttribute()) {
901 // Let the browser know about the updated view rect.
902 browser_plugin_manager()->Send(new BrowserPluginHostMsg_UpdateGeometry(
903 render_view_routing_id_, guest_instance_id_, plugin_rect_));
907 BrowserPluginHostMsg_ResizeGuest_Params params;
908 PopulateResizeGuestParameters(¶ms, plugin_rect(), false);
909 paint_ack_received_ = false;
910 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest(
911 render_view_routing_id_,
916 void BrowserPlugin::PopulateResizeGuestParameters(
917 BrowserPluginHostMsg_ResizeGuest_Params* params,
918 const gfx::Rect& view_rect,
919 bool needs_repaint) {
920 params->size_changed = true;
921 params->view_rect = view_rect;
922 params->repaint = needs_repaint;
923 params->scale_factor = GetDeviceScaleFactor();
924 if (last_device_scale_factor_ != params->scale_factor){
925 params->repaint = true;
926 last_device_scale_factor_ = params->scale_factor;
930 void BrowserPlugin::GetSizeParams(
931 BrowserPluginHostMsg_AutoSize_Params* auto_size_params,
932 BrowserPluginHostMsg_ResizeGuest_Params* resize_guest_params,
933 bool needs_repaint) {
934 if (auto_size_params) {
935 PopulateAutoSizeParameters(auto_size_params, GetAutoSizeAttribute());
937 max_auto_size_ = gfx::Size();
939 gfx::Size view_size = (auto_size_params && auto_size_params->enable) ?
940 auto_size_params->max_size : gfx::Size(width(), height());
941 if (view_size.IsEmpty())
943 paint_ack_received_ = false;
944 gfx::Rect view_rect = gfx::Rect(plugin_rect_.origin(), view_size);
945 PopulateResizeGuestParameters(resize_guest_params, view_rect, needs_repaint);
948 void BrowserPlugin::updateFocus(bool focused) {
949 plugin_focused_ = focused;
950 UpdateGuestFocusState();
953 void BrowserPlugin::updateVisibility(bool visible) {
954 if (visible_ == visible)
958 if (!HasGuestInstanceID())
961 if (compositing_helper_.get())
962 compositing_helper_->UpdateVisibility(visible);
964 browser_plugin_manager()->Send(new BrowserPluginHostMsg_SetVisibility(
965 render_view_routing_id_,
970 bool BrowserPlugin::acceptsInputEvents() {
974 bool BrowserPlugin::handleInputEvent(const blink::WebInputEvent& event,
975 blink::WebCursorInfo& cursor_info) {
976 if (guest_crashed_ || !HasGuestInstanceID())
979 if (event.type == blink::WebInputEvent::ContextMenu)
982 const blink::WebInputEvent* modified_event = &event;
983 scoped_ptr<blink::WebTouchEvent> touch_event;
984 if (blink::WebInputEvent::isTouchEventType(event.type)) {
985 const blink::WebTouchEvent* orig_touch_event =
986 static_cast<const blink::WebTouchEvent*>(&event);
988 touch_event.reset(new blink::WebTouchEvent());
989 memcpy(touch_event.get(), orig_touch_event, sizeof(blink::WebTouchEvent));
991 // TODO(bokan): Blink passes back a WebGestureEvent with a touches,
992 // changedTouches, and targetTouches lists; however, it doesn't set
993 // the state field on the touches which is what the RenderWidget uses
994 // to create a WebCore::TouchEvent. crbug.com/358132 tracks removing
995 // these multiple lists from WebTouchEvent since they lead to misuse
996 // like this and are functionally unused. In the mean time we'll setup
997 // the state field here manually to fix multi-touch BrowserPlugins.
998 for (size_t i = 0; i < touch_event->touchesLength; ++i) {
999 blink::WebTouchPoint& touch = touch_event->touches[i];
1000 touch.state = blink::WebTouchPoint::StateStationary;
1001 for (size_t j = 0; j < touch_event->changedTouchesLength; ++j) {
1002 blink::WebTouchPoint& changed_touch = touch_event->changedTouches[j];
1003 if (touch.id == changed_touch.id) {
1004 touch.state = changed_touch.state;
1010 // For End and Cancel, Blink gives BrowserPlugin a list of touches that
1011 // are down, but the browser process expects a list of all touches. We
1012 // modify these events here to match these expectations.
1013 if (event.type == blink::WebInputEvent::TouchEnd ||
1014 event.type == blink::WebInputEvent::TouchCancel) {
1015 if (touch_event->changedTouchesLength > 0) {
1016 memcpy(&touch_event->touches[touch_event->touchesLength],
1017 &touch_event->changedTouches,
1018 touch_event->changedTouchesLength * sizeof(blink::WebTouchPoint));
1019 touch_event->touchesLength += touch_event->changedTouchesLength;
1022 modified_event = touch_event.get();
1025 if (blink::WebInputEvent::isKeyboardEventType(event.type) &&
1026 !edit_commands_.empty()) {
1027 browser_plugin_manager()->Send(
1028 new BrowserPluginHostMsg_SetEditCommandsForNextKeyEvent(
1029 render_view_routing_id_,
1032 edit_commands_.clear();
1035 browser_plugin_manager()->Send(
1036 new BrowserPluginHostMsg_HandleInputEvent(render_view_routing_id_,
1040 GetWebKitCursorInfo(cursor_, &cursor_info);
1044 bool BrowserPlugin::handleDragStatusUpdate(blink::WebDragStatus drag_status,
1045 const blink::WebDragData& drag_data,
1046 blink::WebDragOperationsMask mask,
1047 const blink::WebPoint& position,
1048 const blink::WebPoint& screen) {
1049 if (guest_crashed_ || !HasGuestInstanceID())
1051 browser_plugin_manager()->Send(
1052 new BrowserPluginHostMsg_DragStatusUpdate(
1053 render_view_routing_id_,
1056 DropDataBuilder::Build(drag_data),
1062 void BrowserPlugin::didReceiveResponse(
1063 const blink::WebURLResponse& response) {
1066 void BrowserPlugin::didReceiveData(const char* data, int data_length) {
1067 if (auto_navigate_) {
1068 std::string value(data, data_length);
1069 html_string_ += value;
1073 void BrowserPlugin::didFinishLoading() {
1074 if (auto_navigate_) {
1075 UpdateDOMAttribute(content::browser_plugin::kAttributeSrc, html_string_);
1080 void BrowserPlugin::didFailLoading(const blink::WebURLError& error) {
1083 void BrowserPlugin::didFinishLoadingFrameRequest(const blink::WebURL& url,
1084 void* notify_data) {
1087 void BrowserPlugin::didFailLoadingFrameRequest(
1088 const blink::WebURL& url,
1090 const blink::WebURLError& error) {
1093 bool BrowserPlugin::executeEditCommand(const blink::WebString& name) {
1094 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ExecuteEditCommand(
1095 render_view_routing_id_,
1099 // BrowserPlugin swallows edit commands.
1103 bool BrowserPlugin::executeEditCommand(const blink::WebString& name,
1104 const blink::WebString& value) {
1105 edit_commands_.push_back(EditCommand(name.utf8(), value.utf8()));
1106 // BrowserPlugin swallows edit commands.
1110 bool BrowserPlugin::setComposition(
1111 const blink::WebString& text,
1112 const blink::WebVector<blink::WebCompositionUnderline>& underlines,
1115 if (!HasGuestInstanceID())
1117 std::vector<blink::WebCompositionUnderline> std_underlines;
1118 for (size_t i = 0; i < underlines.size(); ++i) {
1119 std_underlines.push_back(underlines[i]);
1121 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ImeSetComposition(
1122 render_view_routing_id_,
1128 // TODO(kochi): This assumes the IPC handling always succeeds.
1132 bool BrowserPlugin::confirmComposition(
1133 const blink::WebString& text,
1134 blink::WebWidget::ConfirmCompositionBehavior selectionBehavior) {
1135 if (!HasGuestInstanceID())
1137 bool keep_selection = (selectionBehavior == blink::WebWidget::KeepSelection);
1138 browser_plugin_manager()->Send(new BrowserPluginHostMsg_ImeConfirmComposition(
1139 render_view_routing_id_,
1143 // TODO(kochi): This assumes the IPC handling always succeeds.
1147 void BrowserPlugin::extendSelectionAndDelete(int before, int after) {
1148 if (!HasGuestInstanceID())
1150 browser_plugin_manager()->Send(
1151 new BrowserPluginHostMsg_ExtendSelectionAndDelete(
1152 render_view_routing_id_,
1158 void BrowserPlugin::OnLockMouseACK(bool succeeded) {
1159 mouse_locked_ = succeeded;
1160 browser_plugin_manager()->Send(new BrowserPluginHostMsg_LockMouse_ACK(
1161 render_view_routing_id_,
1166 void BrowserPlugin::OnMouseLockLost() {
1167 mouse_locked_ = false;
1168 browser_plugin_manager()->Send(new BrowserPluginHostMsg_UnlockMouse_ACK(
1169 render_view_routing_id_,
1170 guest_instance_id_));
1173 bool BrowserPlugin::HandleMouseLockedInputEvent(
1174 const blink::WebMouseEvent& event) {
1175 browser_plugin_manager()->Send(
1176 new BrowserPluginHostMsg_HandleInputEvent(render_view_routing_id_,
1183 } // namespace content