diff options
author | Zeno Albisser <zeno.albisser@digia.com> | 2013-08-15 21:46:11 +0200 |
---|---|---|
committer | Zeno Albisser <zeno.albisser@digia.com> | 2013-08-15 21:46:11 +0200 |
commit | 679147eead574d186ebf3069647b4c23e8ccace6 (patch) | |
tree | fc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/content/renderer/browser_plugin | |
download | qtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz |
Initial import.
Diffstat (limited to 'chromium/content/renderer/browser_plugin')
20 files changed, 4383 insertions, 0 deletions
diff --git a/chromium/content/renderer/browser_plugin/OWNERS b/chromium/content/renderer/browser_plugin/OWNERS new file mode 100644 index 00000000000..c92691f05e4 --- /dev/null +++ b/chromium/content/renderer/browser_plugin/OWNERS @@ -0,0 +1 @@ +fsamuel@chromium.org diff --git a/chromium/content/renderer/browser_plugin/browser_plugin.cc b/chromium/content/renderer/browser_plugin/browser_plugin.cc new file mode 100644 index 00000000000..cf0bdcc54b9 --- /dev/null +++ b/chromium/content/renderer/browser_plugin/browser_plugin.cc @@ -0,0 +1,1343 @@ +// Copyright 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/browser_plugin/browser_plugin.h" + +#include "base/command_line.h" +#include "base/json/json_string_value_serializer.h" +#include "base/message_loop/message_loop.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "content/common/browser_plugin/browser_plugin_constants.h" +#include "content/common/browser_plugin/browser_plugin_messages.h" +#include "content/common/view_messages.h" +#include "content/public/common/content_client.h" +#include "content/public/common/content_switches.h" +#include "content/public/renderer/content_renderer_client.h" +#include "content/renderer/browser_plugin/browser_plugin_bindings.h" +#include "content/renderer/browser_plugin/browser_plugin_compositing_helper.h" +#include "content/renderer/browser_plugin/browser_plugin_manager.h" +#include "content/renderer/cursor_utils.h" +#include "content/renderer/drop_data_builder.h" +#include "content/renderer/render_process_impl.h" +#include "content/renderer/render_thread_impl.h" +#include "content/renderer/sad_plugin.h" +#include "content/renderer/v8_value_converter_impl.h" +#include "skia/ext/platform_canvas.h" +#include "third_party/WebKit/public/platform/WebRect.h" +#include "third_party/WebKit/public/web/WebBindings.h" +#include "third_party/WebKit/public/web/WebDOMCustomEvent.h" +#include "third_party/WebKit/public/web/WebDocument.h" +#include "third_party/WebKit/public/web/WebElement.h" +#include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" +#include "third_party/WebKit/public/web/WebPluginContainer.h" +#include "third_party/WebKit/public/web/WebPluginParams.h" +#include "third_party/WebKit/public/web/WebScriptSource.h" +#include "third_party/WebKit/public/web/WebView.h" +#include "ui/base/keycodes/keyboard_codes.h" + +#if defined (OS_WIN) +#include "base/sys_info.h" +#endif + +using WebKit::WebCanvas; +using WebKit::WebPluginContainer; +using WebKit::WebPluginParams; +using WebKit::WebPoint; +using WebKit::WebRect; +using WebKit::WebURL; +using WebKit::WebVector; + +namespace content { + +namespace { + +static std::string GetInternalEventName(const char* event_name) { + return base::StringPrintf("-internal-%s", event_name); +} + +typedef std::map<WebKit::WebPluginContainer*, + BrowserPlugin*> PluginContainerMap; +static base::LazyInstance<PluginContainerMap> g_plugin_container_map = + LAZY_INSTANCE_INITIALIZER; + +} // namespace + +BrowserPlugin::BrowserPlugin( + RenderViewImpl* render_view, + WebKit::WebFrame* frame, + const WebPluginParams& params) + : guest_instance_id_(browser_plugin::kInstanceIDNone), + attached_(false), + render_view_(render_view->AsWeakPtr()), + render_view_routing_id_(render_view->GetRoutingID()), + container_(NULL), + damage_buffer_sequence_id_(0), + resize_ack_received_(true), + last_device_scale_factor_(1.0f), + sad_guest_(NULL), + guest_crashed_(false), + auto_size_ack_pending_(false), + persist_storage_(false), + valid_partition_id_(true), + content_window_routing_id_(MSG_ROUTING_NONE), + plugin_focused_(false), + visible_(true), + before_first_navigation_(true), + mouse_locked_(false), + browser_plugin_manager_(render_view->GetBrowserPluginManager()), + compositing_enabled_(false), + weak_ptr_factory_(this) { +} + +BrowserPlugin::~BrowserPlugin() { + // If the BrowserPlugin has never navigated then the browser process and + // BrowserPluginManager don't know about it and so there is nothing to do + // here. + if (!HasGuestInstanceID()) + return; + browser_plugin_manager()->RemoveBrowserPlugin(guest_instance_id_); + browser_plugin_manager()->Send( + new BrowserPluginHostMsg_PluginDestroyed(render_view_routing_id_, + guest_instance_id_)); +} + +/*static*/ +BrowserPlugin* BrowserPlugin::FromContainer( + WebKit::WebPluginContainer* container) { + PluginContainerMap* browser_plugins = g_plugin_container_map.Pointer(); + PluginContainerMap::iterator it = browser_plugins->find(container); + return it == browser_plugins->end() ? NULL : it->second; +} + +bool BrowserPlugin::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(BrowserPlugin, message) + IPC_MESSAGE_HANDLER(BrowserPluginMsg_AdvanceFocus, OnAdvanceFocus) + IPC_MESSAGE_HANDLER(BrowserPluginMsg_Attach_ACK, OnAttachACK) + IPC_MESSAGE_HANDLER(BrowserPluginMsg_BuffersSwapped, OnBuffersSwapped) + IPC_MESSAGE_HANDLER_GENERIC(BrowserPluginMsg_CompositorFrameSwapped, + OnCompositorFrameSwapped(message)) + IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestContentWindowReady, + OnGuestContentWindowReady) + IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestGone, OnGuestGone) + IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetCursor, OnSetCursor) + IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetMouseLock, OnSetMouseLock) + IPC_MESSAGE_HANDLER(BrowserPluginMsg_ShouldAcceptTouchEvents, + OnShouldAcceptTouchEvents) + IPC_MESSAGE_HANDLER(BrowserPluginMsg_UpdatedName, OnUpdatedName) + IPC_MESSAGE_HANDLER(BrowserPluginMsg_UpdateRect, OnUpdateRect) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void BrowserPlugin::UpdateDOMAttribute(const std::string& attribute_name, + const std::string& attribute_value) { + if (!container()) + return; + + WebKit::WebElement element = container()->element(); + WebKit::WebString web_attribute_name = + WebKit::WebString::fromUTF8(attribute_name); + if (!HasDOMAttribute(attribute_name) || + (std::string(element.getAttribute(web_attribute_name).utf8()) != + attribute_value)) { + element.setAttribute(web_attribute_name, + WebKit::WebString::fromUTF8(attribute_value)); + } +} + +void BrowserPlugin::RemoveDOMAttribute(const std::string& attribute_name) { + if (!container()) + return; + + container()->element().removeAttribute( + WebKit::WebString::fromUTF8(attribute_name)); +} + +std::string BrowserPlugin::GetDOMAttributeValue( + const std::string& attribute_name) const { + if (!container()) + return std::string(); + + return container()->element().getAttribute( + WebKit::WebString::fromUTF8(attribute_name)).utf8(); +} + +bool BrowserPlugin::HasDOMAttribute(const std::string& attribute_name) const { + if (!container()) + return false; + + return container()->element().hasAttribute( + WebKit::WebString::fromUTF8(attribute_name)); +} + +std::string BrowserPlugin::GetNameAttribute() const { + return GetDOMAttributeValue(browser_plugin::kAttributeName); +} + +std::string BrowserPlugin::GetSrcAttribute() const { + return GetDOMAttributeValue(browser_plugin::kAttributeSrc); +} + +bool BrowserPlugin::GetAutoSizeAttribute() const { + return HasDOMAttribute(browser_plugin::kAttributeAutoSize); +} + +int BrowserPlugin::GetMaxHeightAttribute() const { + int max_height; + base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMaxHeight), + &max_height); + return max_height; +} + +int BrowserPlugin::GetMaxWidthAttribute() const { + int max_width; + base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMaxWidth), + &max_width); + return max_width; +} + +int BrowserPlugin::GetMinHeightAttribute() const { + int min_height; + base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMinHeight), + &min_height); + return min_height; +} + +int BrowserPlugin::GetMinWidthAttribute() const { + int min_width; + base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMinWidth), + &min_width); + return min_width; +} + +int BrowserPlugin::GetAdjustedMaxHeight() const { + int max_height = GetMaxHeightAttribute(); + return max_height ? max_height : height(); +} + +int BrowserPlugin::GetAdjustedMaxWidth() const { + int max_width = GetMaxWidthAttribute(); + return max_width ? max_width : width(); +} + +int BrowserPlugin::GetAdjustedMinHeight() const { + int min_height = GetMinHeightAttribute(); + // FrameView.cpp does not allow this value to be <= 0, so when the value is + // unset (or set to 0), we set it to the container size. + min_height = min_height ? min_height : height(); + // For autosize, minHeight should not be bigger than maxHeight. + return std::min(min_height, GetAdjustedMaxHeight()); +} + +int BrowserPlugin::GetAdjustedMinWidth() const { + int min_width = GetMinWidthAttribute(); + // FrameView.cpp does not allow this value to be <= 0, so when the value is + // unset (or set to 0), we set it to the container size. + min_width = min_width ? min_width : width(); + // For autosize, minWidth should not be bigger than maxWidth. + return std::min(min_width, GetAdjustedMaxWidth()); +} + +std::string BrowserPlugin::GetPartitionAttribute() const { + return GetDOMAttributeValue(browser_plugin::kAttributePartition); +} + +void BrowserPlugin::ParseNameAttribute() { + if (!HasGuestInstanceID()) + return; + browser_plugin_manager()->Send( + new BrowserPluginHostMsg_SetName(render_view_routing_id_, + guest_instance_id_, + GetNameAttribute())); +} + +bool BrowserPlugin::ParseSrcAttribute(std::string* error_message) { + if (!valid_partition_id_) { + *error_message = browser_plugin::kErrorInvalidPartition; + return false; + } + std::string src = GetSrcAttribute(); + if (src.empty()) + return true; + + // If we haven't created the guest yet, do so now. We will navigate it right + // after creation. If |src| is empty, we can delay the creation until we + // actually need it. + if (!HasGuestInstanceID()) { + // On initial navigation, we request an instance ID from the browser + // process. We essentially ignore all subsequent calls to SetSrcAttribute + // until we receive an instance ID. |before_first_navigation_| + // prevents BrowserPlugin from allocating more than one instance ID. + // Upon receiving an instance ID from the browser process, we continue + // the process of navigation by populating the + // BrowserPluginHostMsg_Attach_Params with the current state of + // BrowserPlugin and sending a BrowserPluginHostMsg_CreateGuest to the + // browser process in order to create a new guest. + if (before_first_navigation_) { + browser_plugin_manager()->AllocateInstanceID(this); + before_first_navigation_ = false; + } + return true; + } + + browser_plugin_manager()->Send( + new BrowserPluginHostMsg_NavigateGuest(render_view_routing_id_, + guest_instance_id_, + src)); + return true; +} + +void BrowserPlugin::ParseAutoSizeAttribute() { + auto_size_ack_pending_ = true; + last_view_size_ = plugin_rect_.size(); + UpdateGuestAutoSizeState(GetAutoSizeAttribute()); +} + +void BrowserPlugin::PopulateAutoSizeParameters( + BrowserPluginHostMsg_AutoSize_Params* params, bool current_auto_size) { + params->enable = current_auto_size; + // No need to populate the params if autosize is off. + if (current_auto_size) { + params->max_size = gfx::Size(GetAdjustedMaxWidth(), GetAdjustedMaxHeight()); + params->min_size = gfx::Size(GetAdjustedMinWidth(), GetAdjustedMinHeight()); + } +} + +void BrowserPlugin::UpdateGuestAutoSizeState(bool current_auto_size) { + // If we haven't yet heard back from the guest about the last resize request, + // then we don't issue another request until we do in + // BrowserPlugin::UpdateRect. + if (!HasGuestInstanceID() || !resize_ack_received_) + return; + BrowserPluginHostMsg_AutoSize_Params auto_size_params; + BrowserPluginHostMsg_ResizeGuest_Params resize_guest_params; + if (current_auto_size) { + GetDamageBufferWithSizeParams(&auto_size_params, &resize_guest_params); + } else { + GetDamageBufferWithSizeParams(NULL, &resize_guest_params); + } + resize_ack_received_ = false; + browser_plugin_manager()->Send( + new BrowserPluginHostMsg_SetAutoSize(render_view_routing_id_, + guest_instance_id_, + auto_size_params, + resize_guest_params)); +} + +// static +bool BrowserPlugin::UsesDamageBuffer( + const BrowserPluginMsg_UpdateRect_Params& params) { + return params.damage_buffer_sequence_id != 0 || params.needs_ack; +} + +bool BrowserPlugin::UsesPendingDamageBuffer( + const BrowserPluginMsg_UpdateRect_Params& params) { + if (!pending_damage_buffer_) + return false; + return damage_buffer_sequence_id_ == params.damage_buffer_sequence_id; +} + +void BrowserPlugin::OnInstanceIDAllocated(int guest_instance_id) { + CHECK(guest_instance_id != browser_plugin::kInstanceIDNone); + before_first_navigation_ = false; + guest_instance_id_ = guest_instance_id; + browser_plugin_manager()->AddBrowserPlugin(guest_instance_id, this); + + std::map<std::string, base::Value*> props; + props[browser_plugin::kWindowID] = + new base::FundamentalValue(guest_instance_id); + TriggerEvent(browser_plugin::kEventInternalInstanceIDAllocated, &props); +} + +void BrowserPlugin::Attach(scoped_ptr<base::DictionaryValue> extra_params) { + BrowserPluginHostMsg_Attach_Params attach_params; + attach_params.focused = ShouldGuestBeFocused(); + attach_params.visible = visible_; + attach_params.name = GetNameAttribute(); + attach_params.storage_partition_id = storage_partition_id_; + attach_params.persist_storage = persist_storage_; + attach_params.src = GetSrcAttribute(); + GetDamageBufferWithSizeParams(&attach_params.auto_size_params, + &attach_params.resize_guest_params); + + browser_plugin_manager()->Send( + new BrowserPluginHostMsg_Attach(render_view_routing_id_, + guest_instance_id_, attach_params, + *extra_params)); +} + +void BrowserPlugin::DidCommitCompositorFrame() { + if (compositing_helper_.get()) + compositing_helper_->DidCommitCompositorFrame(); +} + +void BrowserPlugin::OnAdvanceFocus(int guest_instance_id, bool reverse) { + DCHECK(render_view_.get()); + render_view_->GetWebView()->advanceFocus(reverse); +} + +void BrowserPlugin::OnAttachACK( + int guest_instance_id, + const BrowserPluginMsg_Attach_ACK_Params& params) { + // Update BrowserPlugin attributes to match the state of the guest. + if (!params.name.empty()) + OnUpdatedName(guest_instance_id, params.name); + if (!params.storage_partition_id.empty()) { + std::string partition_name = + (params.persist_storage ? browser_plugin::kPersistPrefix : "") + + params.storage_partition_id; + UpdateDOMAttribute(browser_plugin::kAttributePartition, partition_name); + } + attached_ = true; +} + +void BrowserPlugin::OnBuffersSwapped( + int guest_instance_id, + const BrowserPluginMsg_BuffersSwapped_Params& params) { + DCHECK(guest_instance_id == guest_instance_id_); + EnableCompositing(true); + + compositing_helper_->OnBuffersSwapped(params.size, + params.mailbox_name, + params.route_id, + params.host_id, + GetDeviceScaleFactor()); +} + +void BrowserPlugin::OnCompositorFrameSwapped(const IPC::Message& message) { + BrowserPluginMsg_CompositorFrameSwapped::Param param; + if (!BrowserPluginMsg_CompositorFrameSwapped::Read(&message, ¶m)) + return; + scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame); + param.b.AssignTo(frame.get()); + + EnableCompositing(true); + compositing_helper_->OnCompositorFrameSwapped(frame.Pass(), + param.c /* route_id */, + param.d /* output_surface_id */, + param.e /* host_id */); +} + +void BrowserPlugin::OnGuestContentWindowReady(int guest_instance_id, + int content_window_routing_id) { + DCHECK(content_window_routing_id != MSG_ROUTING_NONE); + content_window_routing_id_ = content_window_routing_id; +} + +void BrowserPlugin::OnGuestGone(int guest_instance_id) { + guest_crashed_ = true; + + // Queue up showing the sad graphic to give content embedders an opportunity + // to fire their listeners and potentially overlay the webview with custom + // behavior. If the BrowserPlugin is destroyed in the meantime, then the + // task will not be executed. + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&BrowserPlugin::ShowSadGraphic, + weak_ptr_factory_.GetWeakPtr())); +} + +void BrowserPlugin::OnSetCursor(int guest_instance_id, + const WebCursor& cursor) { + cursor_ = cursor; +} + +void BrowserPlugin::OnSetMouseLock(int guest_instance_id, + bool enable) { + if (enable) { + if (mouse_locked_) + return; + render_view_->mouse_lock_dispatcher()->LockMouse(this); + } else { + if (!mouse_locked_) { + OnLockMouseACK(false); + return; + } + render_view_->mouse_lock_dispatcher()->UnlockMouse(this); + } +} + +void BrowserPlugin::OnShouldAcceptTouchEvents(int guest_instance_id, + bool accept) { + if (container()) { + container()->requestTouchEventType(accept ? + WebKit::WebPluginContainer::TouchEventRequestTypeRaw : + WebKit::WebPluginContainer::TouchEventRequestTypeNone); + } +} + +void BrowserPlugin::OnUpdatedName(int guest_instance_id, + const std::string& name) { + UpdateDOMAttribute(browser_plugin::kAttributeName, name); +} + +void BrowserPlugin::OnUpdateRect( + int guest_instance_id, + const BrowserPluginMsg_UpdateRect_Params& params) { + // If the guest has updated pixels then it is no longer crashed. + guest_crashed_ = false; + + bool use_new_damage_buffer = !backing_store_; + BrowserPluginHostMsg_AutoSize_Params auto_size_params; + BrowserPluginHostMsg_ResizeGuest_Params resize_guest_params; + // If we have a pending damage buffer, and the guest has begun to use the + // damage buffer then we know the guest will no longer use the current + // damage buffer. At this point, we drop the current damage buffer, and + // mark the pending damage buffer as the current damage buffer. + if (UsesPendingDamageBuffer(params)) { + SwapDamageBuffers(); + use_new_damage_buffer = true; + } + + bool auto_size = GetAutoSizeAttribute(); + // We receive a resize ACK in regular mode, but not in autosize. + // In SW, |resize_ack_received_| is reset in SwapDamageBuffers(). + // In HW mode, we need to do it here so we can continue sending + // resize messages when needed. + if (params.is_resize_ack || + (!params.needs_ack && (auto_size || auto_size_ack_pending_))) { + resize_ack_received_ = true; + } + + auto_size_ack_pending_ = false; + + if ((!auto_size && (width() != params.view_size.width() || + height() != params.view_size.height())) || + (auto_size && (!InAutoSizeBounds(params.view_size))) || + GetDeviceScaleFactor() != params.scale_factor) { + // We are HW accelerated, render widget does not expect an ack, + // but we still need to update the size. + if (!params.needs_ack) { + UpdateGuestAutoSizeState(auto_size); + return; + } + + if (!resize_ack_received_) { + // The guest has not yet responded to the last resize request, and + // so we don't want to do anything at this point other than ACK the guest. + if (auto_size) + PopulateAutoSizeParameters(&auto_size_params, auto_size); + } else { + // If we have no pending damage buffer, then the guest has not caught up + // with the BrowserPlugin container. We now tell the guest about the new + // container size. + if (auto_size) { + GetDamageBufferWithSizeParams(&auto_size_params, + &resize_guest_params); + } else { + GetDamageBufferWithSizeParams(NULL, &resize_guest_params); + } + } + browser_plugin_manager()->Send(new BrowserPluginHostMsg_UpdateRect_ACK( + render_view_routing_id_, + guest_instance_id_, + true, + auto_size_params, + resize_guest_params)); + return; + } + + if (auto_size && (params.view_size != last_view_size_)) { + if (backing_store_) + backing_store_->Clear(SK_ColorWHITE); + last_view_size_ = params.view_size; + } + + if (UsesDamageBuffer(params)) { + + // If we are seeing damage buffers, HW compositing should be turned off. + EnableCompositing(false); + + // If we are now using a new damage buffer, then that means that the guest + // has updated its size state in response to a resize request. We change + // the backing store's size to accomodate the new damage buffer size. + if (use_new_damage_buffer) { + int backing_store_width = auto_size ? GetAdjustedMaxWidth() : width(); + int backing_store_height = auto_size ? GetAdjustedMaxHeight(): height(); + backing_store_.reset( + new BrowserPluginBackingStore( + gfx::Size(backing_store_width, backing_store_height), + params.scale_factor)); + } + + // If we just transitioned from the compositing path to the software path + // then we might not yet have a damage buffer. + if (current_damage_buffer_) { + // Update the backing store. + if (!params.scroll_rect.IsEmpty()) { + backing_store_->ScrollBackingStore(params.scroll_delta, + params.scroll_rect, + params.view_size); + } + backing_store_->PaintToBackingStore(params.bitmap_rect, + params.copy_rects, + current_damage_buffer_->memory()); + // Invalidate the container. + // If the BrowserPlugin is scheduled to be deleted, then container_ will + // be NULL so we shouldn't attempt to access it. + if (container_) + container_->invalidate(); + } + } + + // BrowserPluginHostMsg_UpdateRect_ACK is used by both the compositing and + // software paths to piggyback updated autosize parameters. + if (auto_size) + PopulateAutoSizeParameters(&auto_size_params, auto_size); + browser_plugin_manager()->Send(new BrowserPluginHostMsg_UpdateRect_ACK( + render_view_routing_id_, + guest_instance_id_, + UsesDamageBuffer(params), + auto_size_params, + resize_guest_params)); +} + +void BrowserPlugin::ParseSizeContraintsChanged() { + bool auto_size = GetAutoSizeAttribute(); + if (auto_size) + UpdateGuestAutoSizeState(true); +} + +bool BrowserPlugin::InAutoSizeBounds(const gfx::Size& size) const { + return size.width() <= GetAdjustedMaxWidth() && + size.height() <= GetAdjustedMaxHeight(); +} + +NPObject* BrowserPlugin::GetContentWindow() const { + if (content_window_routing_id_ == MSG_ROUTING_NONE) + return NULL; + RenderViewImpl* guest_render_view = RenderViewImpl::FromRoutingID( + content_window_routing_id_); + if (!guest_render_view) + return NULL; + WebKit::WebFrame* guest_frame = guest_render_view->GetWebView()->mainFrame(); + return guest_frame->windowObject(); +} + +// static +bool BrowserPlugin::AttachWindowTo(const WebKit::WebNode& node, int window_id) { + if (node.isNull()) + return false; + + if (!node.isElementNode()) + return false; + + WebKit::WebElement shim_element = node.toConst<WebKit::WebElement>(); + // The shim containing the BrowserPlugin must be attached to a document. + if (shim_element.document().isNull()) + return false; + + WebKit::WebNode shadow_root = shim_element.shadowRoot(); + if (shadow_root.isNull() || !shadow_root.hasChildNodes()) + return false; + + WebKit::WebNode plugin_element = shadow_root.firstChild(); + WebKit::WebPluginContainer* plugin_container = + plugin_element.pluginContainer(); + if (!plugin_container) + return false; + + BrowserPlugin* browser_plugin = + BrowserPlugin::FromContainer(plugin_container); + if (!browser_plugin) + return false; + + // If the BrowserPlugin has already begun to navigate then we shouldn't allow + // attaching a different guest. + // + // Navigation happens in two stages. + // 1. BrowserPlugin requests an instance ID from the browser process. + // 2. The browser process returns an instance ID and BrowserPlugin is + // "Attach"ed to that instance ID. + // If the instance ID is new then a new guest will be created. + // If the instance ID corresponds to an unattached guest then BrowserPlugin + // is attached to that guest. + // + // Between step 1, and step 2, BrowserPlugin::AttachWindowTo may be called. + // The check below ensures that BrowserPlugin:Attach does not get called with + // a different instance ID after step 1 has happened. + // TODO(fsamuel): We may wish to support reattaching guests in the future: + // http://crbug.com/156219. + if (browser_plugin->HasNavigated()) + return false; + + browser_plugin->OnInstanceIDAllocated(window_id); + return true; +} + +bool BrowserPlugin::HasNavigated() const { + return !before_first_navigation_; +} + +bool BrowserPlugin::HasGuestInstanceID() const { + return guest_instance_id_ != browser_plugin::kInstanceIDNone; +} + +bool BrowserPlugin::ParsePartitionAttribute(std::string* error_message) { + if (HasNavigated()) { + *error_message = browser_plugin::kErrorAlreadyNavigated; + return false; + } + + std::string input = GetPartitionAttribute(); + + // Since the "persist:" prefix is in ASCII, StartsWith will work fine on + // UTF-8 encoded |partition_id|. If the prefix is a match, we can safely + // remove the prefix without splicing in the middle of a multi-byte codepoint. + // We can use the rest of the string as UTF-8 encoded one. + if (StartsWithASCII(input, browser_plugin::kPersistPrefix, true)) { + size_t index = input.find(":"); + CHECK(index != std::string::npos); + // It is safe to do index + 1, since we tested for the full prefix above. + input = input.substr(index + 1); + if (input.empty()) { + valid_partition_id_ = false; + *error_message = browser_plugin::kErrorInvalidPartition; + return false; + } + persist_storage_ = true; + } else { + persist_storage_ = false; + } + + valid_partition_id_ = true; + storage_partition_id_ = input; + return true; +} + +bool BrowserPlugin::CanRemovePartitionAttribute(std::string* error_message) { + if (HasGuestInstanceID()) + *error_message = browser_plugin::kErrorCannotRemovePartition; + return !HasGuestInstanceID(); +} + +void BrowserPlugin::ShowSadGraphic() { + // We won't paint the contents of the current backing store again so we might + // as well toss it out and save memory. + backing_store_.reset(); + // If the BrowserPlugin is scheduled to be deleted, then container_ will be + // NULL so we shouldn't attempt to access it. + if (container_) + container_->invalidate(); + // Turn off compositing so we can display the sad graphic. + EnableCompositing(false); +} + +void BrowserPlugin::ParseAttributes() { + // TODO(mthiesse): Handle errors here? + std::string error; + ParsePartitionAttribute(&error); + + // Parse the 'src' attribute last, as it will set the has_navigated_ flag to + // true, which prevents changing the 'partition' attribute. + ParseSrcAttribute(&error); +} + +float BrowserPlugin::GetDeviceScaleFactor() const { + if (!render_view_.get()) + return 1.0f; + return render_view_->GetWebView()->deviceScaleFactor(); +} + +void BrowserPlugin::UpdateDeviceScaleFactor(float device_scale_factor) { + if (last_device_scale_factor_ == device_scale_factor || !resize_ack_received_) + return; + + BrowserPluginHostMsg_ResizeGuest_Params params; + PopulateResizeGuestParameters(¶ms, plugin_rect()); + browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest( + render_view_routing_id_, + guest_instance_id_, + params)); +} + +void BrowserPlugin::TriggerEvent(const std::string& event_name, + std::map<std::string, base::Value*>* props) { + if (!container()) + return; + + WebKit::WebFrame* frame = container()->element().document().frame(); + if (!frame) + return; + + v8::HandleScope handle_scope; + v8::Local<v8::Context> context = frame->mainWorldScriptContext(); + v8::Context::Scope context_scope(context); + + std::string json_string; + if (props) { + base::DictionaryValue dict; + for (std::map<std::string, base::Value*>::iterator iter = props->begin(), + end = props->end(); iter != end; ++iter) { + dict.Set(iter->first, iter->second); + } + + JSONStringValueSerializer serializer(&json_string); + if (!serializer.Serialize(dict)) + return; + } + + WebKit::WebDOMEvent dom_event = frame->document().createEvent("CustomEvent"); + WebKit::WebDOMCustomEvent event = dom_event.to<WebKit::WebDOMCustomEvent>(); + + // The events triggered directly from the plugin <object> are internal events + // whose implementation details can (and likely will) change over time. The + // wrapper/shim (e.g. <webview> tag) should receive these events, and expose a + // more appropriate (and stable) event to the consumers as part of the API. + event.initCustomEvent( + WebKit::WebString::fromUTF8(GetInternalEventName(event_name.c_str())), + false, false, + WebKit::WebSerializedScriptValue::serialize( + v8::String::New(json_string.c_str(), json_string.size()))); + container()->element().dispatchEvent(event); +} + +void BrowserPlugin::OnTrackedObjectGarbageCollected(int id) { + // Remove from alive objects. + std::map<int, TrackedV8ObjectID*>::iterator iter = + tracked_v8_objects_.find(id); + if (iter != tracked_v8_objects_.end()) + tracked_v8_objects_.erase(iter); + + std::map<std::string, base::Value*> props; + props[browser_plugin::kId] = new base::FundamentalValue(id); + TriggerEvent(browser_plugin::kEventInternalTrackedObjectGone, &props); +} + +void BrowserPlugin::TrackObjectLifetime(const NPVariant* request, int id) { + // An object of a given ID can only be tracked once. + if (tracked_v8_objects_.find(id) != tracked_v8_objects_.end()) + return; + + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::Persistent<v8::Value> weak_request( + isolate, WebKit::WebBindings::toV8Value(request)); + + TrackedV8ObjectID* new_item = + new std::pair<int, base::WeakPtr<BrowserPlugin> >( + id, weak_ptr_factory_.GetWeakPtr()); + + std::pair<std::map<int, TrackedV8ObjectID*>::iterator, bool> + result = tracked_v8_objects_.insert( + std::make_pair(id, new_item)); + CHECK(result.second); // Inserted in the map. + TrackedV8ObjectID* request_item = result.first->second; + weak_request.MakeWeak(static_cast<void*>(request_item), + WeakCallbackForTrackedObject); +} + +// static +void BrowserPlugin::WeakCallbackForTrackedObject( + v8::Isolate* isolate, v8::Persistent<v8::Value>* object, void* param) { + + TrackedV8ObjectID* item_ptr = static_cast<TrackedV8ObjectID*>(param); + int object_id = item_ptr->first; + base::WeakPtr<BrowserPlugin> plugin = item_ptr->second; + delete item_ptr; + + object->Dispose(); + if (plugin.get()) { + // Asynchronously remove item from |tracked_v8_objects_|. + // Note that we are using weak pointer for the following PostTask, so we + // don't need to worry about BrowserPlugin going away. + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&BrowserPlugin::OnTrackedObjectGarbageCollected, + plugin, + object_id)); + } +} + +void BrowserPlugin::UpdateGuestFocusState() { + if (!HasGuestInstanceID()) + return; + bool should_be_focused = ShouldGuestBeFocused(); + browser_plugin_manager()->Send(new BrowserPluginHostMsg_SetFocus( + render_view_routing_id_, + guest_instance_id_, + should_be_focused)); +} + +bool BrowserPlugin::ShouldGuestBeFocused() const { + bool embedder_focused = false; + if (render_view_.get()) + embedder_focused = render_view_->has_focus(); + return plugin_focused_ && embedder_focused; +} + +WebKit::WebPluginContainer* BrowserPlugin::container() const { + return container_; +} + +bool BrowserPlugin::initialize(WebPluginContainer* container) { + if (!container) + return false; + + if (!GetContentClient()->renderer()->AllowBrowserPlugin(container)) + return false; + + // Tell |container| to allow this plugin to use script objects. + npp_.reset(new NPP_t); + container->allowScriptObjects(); + + bindings_.reset(new BrowserPluginBindings(this)); + container_ = container; + container_->setWantsWheelEvents(true); + ParseAttributes(); + g_plugin_container_map.Get().insert(std::make_pair(container_, this)); + return true; +} + +void BrowserPlugin::EnableCompositing(bool enable) { + if (compositing_enabled_ == enable) + return; + + compositing_enabled_ = enable; + if (enable) { + // No need to keep the backing store and damage buffer around if we're now + // compositing. + backing_store_.reset(); + current_damage_buffer_.reset(); + if (!compositing_helper_.get()) { + compositing_helper_ = + new BrowserPluginCompositingHelper(container_, + browser_plugin_manager(), + guest_instance_id_, + render_view_routing_id_); + } + } else { + // We're switching back to the software path. We create a new damage + // buffer that can accommodate the current size of the container. + BrowserPluginHostMsg_ResizeGuest_Params params; + PopulateResizeGuestParameters(¶ms, plugin_rect()); + // Request a full repaint from the guest even if its size is not actually + // changing. + params.repaint = true; + resize_ack_received_ = false; + browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest( + render_view_routing_id_, + guest_instance_id_, + params)); + } + compositing_helper_->EnableCompositing(enable); +} + +void BrowserPlugin::destroy() { + // If the plugin was initialized then it has a valid |npp_| identifier, and + // the |container_| must clear references to the plugin's script objects. + DCHECK(!npp_ || container_); + if (container_) + container_->clearScriptObjects(); + + // The BrowserPlugin's WebPluginContainer is deleted immediately after this + // call returns, so let's not keep a reference to it around. + g_plugin_container_map.Get().erase(container_); + container_ = NULL; + if (compositing_helper_.get()) + compositing_helper_->OnContainerDestroy(); + // Will be a no-op if the mouse is not currently locked. + if (render_view_.get()) + render_view_->mouse_lock_dispatcher()->OnLockTargetDestroyed(this); + base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); +} + +NPObject* BrowserPlugin::scriptableObject() { + if (!bindings_) + return NULL; + + NPObject* browser_plugin_np_object(bindings_->np_object()); + // The object is expected to be retained before it is returned. + WebKit::WebBindings::retainObject(browser_plugin_np_object); + return browser_plugin_np_object; +} + +NPP BrowserPlugin::pluginNPP() { + return npp_.get(); +} + +bool BrowserPlugin::supportsKeyboardFocus() const { + return true; +} + +bool BrowserPlugin::supportsEditCommands() const { + return true; +} + +bool BrowserPlugin::canProcessDrag() const { + return true; +} + +void BrowserPlugin::paint(WebCanvas* canvas, const WebRect& rect) { + if (guest_crashed_) { + if (!sad_guest_) // Lazily initialize bitmap. + sad_guest_ = content::GetContentClient()->renderer()-> + GetSadWebViewBitmap(); + // content_shell does not have the sad plugin bitmap, so we'll paint black + // instead to make it clear that something went wrong. + if (sad_guest_) { + PaintSadPlugin(canvas, plugin_rect_, *sad_guest_); + return; + } + } + SkAutoCanvasRestore auto_restore(canvas, true); + canvas->translate(plugin_rect_.x(), plugin_rect_.y()); + SkRect image_data_rect = SkRect::MakeXYWH( + SkIntToScalar(0), + SkIntToScalar(0), + SkIntToScalar(plugin_rect_.width()), + SkIntToScalar(plugin_rect_.height())); + canvas->clipRect(image_data_rect); + // Paint black or white in case we have nothing in our backing store or we + // need to show a gutter. + SkPaint paint; + paint.setStyle(SkPaint::kFill_Style); + paint.setColor(guest_crashed_ ? SK_ColorBLACK : SK_ColorWHITE); + canvas->drawRect(image_data_rect, paint); + // Stay a solid color if we have never set a non-empty src, or we don't have a + // backing store. + if (!backing_store_.get() || !HasGuestInstanceID()) + return; + float inverse_scale_factor = 1.0f / backing_store_->GetScaleFactor(); + canvas->scale(inverse_scale_factor, inverse_scale_factor); + canvas->drawBitmap(backing_store_->GetBitmap(), 0, 0); +} + +bool BrowserPlugin::InBounds(const gfx::Point& position) const { + // Note that even for plugins that are rotated using rotate transformations, + // we use the the |plugin_rect_| provided by updateGeometry, which means we + // will be off if |position| is within the plugin rect but does not fall + // within the actual plugin boundary. Not supporting such edge case is OK + // since this function should not be used for making security-sensitive + // decisions. + // This also does not take overlapping plugins into account. + bool result = position.x() >= plugin_rect_.x() && + position.x() < plugin_rect_.x() + plugin_rect_.width() && + position.y() >= plugin_rect_.y() && + position.y() < plugin_rect_.y() + plugin_rect_.height(); + return result; +} + +gfx::Point BrowserPlugin::ToLocalCoordinates(const gfx::Point& point) const { + if (container_) + return container_->windowToLocalPoint(WebKit::WebPoint(point)); + return gfx::Point(point.x() - plugin_rect_.x(), point.y() - plugin_rect_.y()); +} + +// static +bool BrowserPlugin::ShouldForwardToBrowserPlugin( + const IPC::Message& message) { + switch (message.type()) { + case BrowserPluginMsg_AdvanceFocus::ID: + case BrowserPluginMsg_Attach_ACK::ID: + case BrowserPluginMsg_BuffersSwapped::ID: + case BrowserPluginMsg_CompositorFrameSwapped::ID: + case BrowserPluginMsg_GuestContentWindowReady::ID: + case BrowserPluginMsg_GuestGone::ID: + case BrowserPluginMsg_SetCursor::ID: + case BrowserPluginMsg_SetMouseLock::ID: + case BrowserPluginMsg_ShouldAcceptTouchEvents::ID: + case BrowserPluginMsg_UpdatedName::ID: + case BrowserPluginMsg_UpdateRect::ID: + return true; + default: + break; + } + return false; +} + +void BrowserPlugin::updateGeometry( + const WebRect& window_rect, + const WebRect& clip_rect, + const WebVector<WebRect>& cut_outs_rects, + bool is_visible) { + int old_width = width(); + int old_height = height(); + plugin_rect_ = window_rect; + if (!attached()) + return; + + // In AutoSize mode, guests don't care when the BrowserPlugin container is + // resized. If |!resize_ack_received_|, then we are still waiting on a + // previous resize to be ACK'ed and so we don't issue additional resizes + // until the previous one is ACK'ed. + // TODO(mthiesse): Assess the performance of calling GetAutoSizeAttribute() on + // resize. + if (!resize_ack_received_ || + (old_width == window_rect.width && old_height == window_rect.height) || + GetAutoSizeAttribute()) { + // Let the browser know about the updated view rect. + browser_plugin_manager()->Send(new BrowserPluginHostMsg_UpdateGeometry( + render_view_routing_id_, guest_instance_id_, plugin_rect_)); + return; + } + + BrowserPluginHostMsg_ResizeGuest_Params params; + PopulateResizeGuestParameters(¶ms, plugin_rect()); + resize_ack_received_ = false; + browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest( + render_view_routing_id_, + guest_instance_id_, + params)); +} + +void BrowserPlugin::SwapDamageBuffers() { + current_damage_buffer_.reset(pending_damage_buffer_.release()); + resize_ack_received_ = true; +} + +void BrowserPlugin::PopulateResizeGuestParameters( + BrowserPluginHostMsg_ResizeGuest_Params* params, + const gfx::Rect& view_rect) { + params->size_changed = true; + params->view_rect = view_rect; + params->scale_factor = GetDeviceScaleFactor(); + if (last_device_scale_factor_ != params->scale_factor){ + params->repaint = true; + last_device_scale_factor_ = params->scale_factor; + } + + // In HW compositing mode, we do not need a damage buffer. + if (compositing_enabled_) + return; + + const size_t stride = skia::PlatformCanvasStrideForWidth(view_rect.width()); + // Make sure the size of the damage buffer is at least four bytes so that we + // can fit in a magic word to verify that the memory is shared correctly. + size_t size = + std::max(sizeof(unsigned int), + static_cast<size_t>(view_rect.height() * + stride * + GetDeviceScaleFactor() * + GetDeviceScaleFactor())); + + params->damage_buffer_size = size; + pending_damage_buffer_.reset( + CreateDamageBuffer(size, ¶ms->damage_buffer_handle)); + if (!pending_damage_buffer_) + NOTREACHED(); + params->damage_buffer_sequence_id = ++damage_buffer_sequence_id_; +} + +void BrowserPlugin::GetDamageBufferWithSizeParams( + BrowserPluginHostMsg_AutoSize_Params* auto_size_params, + BrowserPluginHostMsg_ResizeGuest_Params* resize_guest_params) { + if (auto_size_params) + PopulateAutoSizeParameters(auto_size_params, GetAutoSizeAttribute()); + gfx::Size view_size = (auto_size_params && auto_size_params->enable) ? + auto_size_params->max_size : gfx::Size(width(), height()); + if (view_size.IsEmpty()) + return; + resize_ack_received_ = false; + gfx::Rect view_rect = gfx::Rect(plugin_rect_.origin(), view_size); + PopulateResizeGuestParameters(resize_guest_params, view_rect); +} + +#if defined(OS_POSIX) +base::SharedMemory* BrowserPlugin::CreateDamageBuffer( + const size_t size, + base::SharedMemoryHandle* damage_buffer_handle) { + scoped_ptr<base::SharedMemory> shared_buf( + content::RenderThread::Get()->HostAllocateSharedMemoryBuffer( + size).release()); + + if (shared_buf) { + if (shared_buf->Map(size)) { + // Insert the magic word. + *static_cast<unsigned int*>(shared_buf->memory()) = 0xdeadbeef; + shared_buf->ShareToProcess(base::GetCurrentProcessHandle(), + damage_buffer_handle); + return shared_buf.release(); + } + } + NOTREACHED(); + return NULL; +} +#elif defined(OS_WIN) +base::SharedMemory* BrowserPlugin::CreateDamageBuffer( + const size_t size, + base::SharedMemoryHandle* damage_buffer_handle) { + scoped_ptr<base::SharedMemory> shared_buf(new base::SharedMemory()); + + if (!shared_buf->CreateAndMapAnonymous(size)) { + NOTREACHED() << "Buffer allocation failed"; + return NULL; + } + + // Insert the magic word. + *static_cast<unsigned int*>(shared_buf->memory()) = 0xdeadbeef; + if (shared_buf->ShareToProcess(base::GetCurrentProcessHandle(), + damage_buffer_handle)) + return shared_buf.release(); + NOTREACHED(); + return NULL; +} +#endif + +void BrowserPlugin::updateFocus(bool focused) { + if (plugin_focused_ == focused) + return; + + bool old_guest_focus_state = ShouldGuestBeFocused(); + plugin_focused_ = focused; + + if (ShouldGuestBeFocused() != old_guest_focus_state) + UpdateGuestFocusState(); +} + +void BrowserPlugin::updateVisibility(bool visible) { + if (visible_ == visible) + return; + + visible_ = visible; + if (!HasGuestInstanceID()) + return; + + if (compositing_helper_.get()) + compositing_helper_->UpdateVisibility(visible); + + browser_plugin_manager()->Send(new BrowserPluginHostMsg_SetVisibility( + render_view_routing_id_, + guest_instance_id_, + visible)); +} + +bool BrowserPlugin::acceptsInputEvents() { + return true; +} + +bool BrowserPlugin::handleInputEvent(const WebKit::WebInputEvent& event, + WebKit::WebCursorInfo& cursor_info) { + if (guest_crashed_ || !HasGuestInstanceID()) + return false; + + if (event.type == WebKit::WebInputEvent::ContextMenu) + return true; + + const WebKit::WebInputEvent* modified_event = &event; + scoped_ptr<WebKit::WebTouchEvent> touch_event; + // WebKit gives BrowserPlugin a list of touches that are down, but the browser + // process expects a list of all touches. We modify the TouchEnd event here to + // match these expectations. + if (event.type == WebKit::WebInputEvent::TouchEnd) { + const WebKit::WebTouchEvent* orig_touch_event = + static_cast<const WebKit::WebTouchEvent*>(&event); + touch_event.reset(new WebKit::WebTouchEvent()); + memcpy(touch_event.get(), orig_touch_event, sizeof(WebKit::WebTouchEvent)); + if (touch_event->changedTouchesLength > 0) { + memcpy(&touch_event->touches[touch_event->touchesLength], + &touch_event->changedTouches, + touch_event->changedTouchesLength * sizeof(WebKit::WebTouchPoint)); + } + touch_event->touchesLength += touch_event->changedTouchesLength; + modified_event = touch_event.get(); + } + + if (WebKit::WebInputEvent::isKeyboardEventType(event.type) && + !edit_commands_.empty()) { + browser_plugin_manager()->Send( + new BrowserPluginHostMsg_SetEditCommandsForNextKeyEvent( + render_view_routing_id_, + guest_instance_id_, + edit_commands_)); + edit_commands_.clear(); + } + + browser_plugin_manager()->Send( + new BrowserPluginHostMsg_HandleInputEvent(render_view_routing_id_, + guest_instance_id_, + plugin_rect_, + modified_event)); + GetWebKitCursorInfo(cursor_, &cursor_info); + return true; +} + +bool BrowserPlugin::handleDragStatusUpdate(WebKit::WebDragStatus drag_status, + const WebKit::WebDragData& drag_data, + WebKit::WebDragOperationsMask mask, + const WebKit::WebPoint& position, + const WebKit::WebPoint& screen) { + if (guest_crashed_ || !HasGuestInstanceID()) + return false; + browser_plugin_manager()->Send( + new BrowserPluginHostMsg_DragStatusUpdate( + render_view_routing_id_, + guest_instance_id_, + drag_status, + DropDataBuilder::Build(drag_data), + mask, + position)); + return true; +} + +void BrowserPlugin::didReceiveResponse( + const WebKit::WebURLResponse& response) { +} + +void BrowserPlugin::didReceiveData(const char* data, int data_length) { +} + +void BrowserPlugin::didFinishLoading() { +} + +void BrowserPlugin::didFailLoading(const WebKit::WebURLError& error) { +} + +void BrowserPlugin::didFinishLoadingFrameRequest(const WebKit::WebURL& url, + void* notify_data) { +} + +void BrowserPlugin::didFailLoadingFrameRequest( + const WebKit::WebURL& url, + void* notify_data, + const WebKit::WebURLError& error) { +} + +bool BrowserPlugin::executeEditCommand(const WebKit::WebString& name) { + browser_plugin_manager()->Send(new BrowserPluginHostMsg_ExecuteEditCommand( + render_view_routing_id_, + guest_instance_id_, + name.utf8())); + + // BrowserPlugin swallows edit commands. + return true; +} + +bool BrowserPlugin::executeEditCommand(const WebKit::WebString& name, + const WebKit::WebString& value) { + edit_commands_.push_back(EditCommand(name.utf8(), value.utf8())); + // BrowserPlugin swallows edit commands. + return true; +} + +void BrowserPlugin::OnLockMouseACK(bool succeeded) { + mouse_locked_ = succeeded; + browser_plugin_manager()->Send(new BrowserPluginHostMsg_LockMouse_ACK( + render_view_routing_id_, + guest_instance_id_, + succeeded)); +} + +void BrowserPlugin::OnMouseLockLost() { + mouse_locked_ = false; + browser_plugin_manager()->Send(new BrowserPluginHostMsg_UnlockMouse_ACK( + render_view_routing_id_, + guest_instance_id_)); +} + +bool BrowserPlugin::HandleMouseLockedInputEvent( + const WebKit::WebMouseEvent& event) { + browser_plugin_manager()->Send( + new BrowserPluginHostMsg_HandleInputEvent(render_view_routing_id_, + guest_instance_id_, + plugin_rect_, + &event)); + return true; +} + +} // namespace content diff --git a/chromium/content/renderer/browser_plugin/browser_plugin.h b/chromium/content/renderer/browser_plugin/browser_plugin.h new file mode 100644 index 00000000000..89481196bec --- /dev/null +++ b/chromium/content/renderer/browser_plugin/browser_plugin.h @@ -0,0 +1,376 @@ +// Copyright 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_H_ +#define CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_H_ + +#include "third_party/WebKit/public/web/WebPlugin.h" + +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/sequenced_task_runner_helpers.h" +#if defined(OS_WIN) +#include "base/memory/shared_memory.h" +#endif +#include "base/values.h" +#include "content/public/common/browser_plugin_permission_type.h" +#include "content/renderer/browser_plugin/browser_plugin_backing_store.h" +#include "content/renderer/browser_plugin/browser_plugin_bindings.h" +#include "content/renderer/mouse_lock_dispatcher.h" +#include "content/renderer/render_view_impl.h" +#include "third_party/WebKit/public/web/WebDragStatus.h" + +struct BrowserPluginHostMsg_AutoSize_Params; +struct BrowserPluginHostMsg_ResizeGuest_Params; +struct BrowserPluginMsg_Attach_ACK_Params; +struct BrowserPluginMsg_BuffersSwapped_Params; +struct BrowserPluginMsg_UpdateRect_Params; + +namespace content { + +class BrowserPluginCompositingHelper; +class BrowserPluginManager; +class MockBrowserPlugin; + +class CONTENT_EXPORT BrowserPlugin : + NON_EXPORTED_BASE(public WebKit::WebPlugin), + public MouseLockDispatcher::LockTarget { + public: + RenderViewImpl* render_view() const { return render_view_.get(); } + int render_view_routing_id() const { return render_view_routing_id_; } + int guest_instance_id() const { return guest_instance_id_; } + bool attached() const { return attached_; } + + static BrowserPlugin* FromContainer(WebKit::WebPluginContainer* container); + + bool OnMessageReceived(const IPC::Message& msg); + + // Update Browser Plugin's DOM Node attribute |attribute_name| with the value + // |attribute_value|. + void UpdateDOMAttribute(const std::string& attribute_name, + const std::string& attribute_value); + // Remove the DOM Node attribute with the name |attribute_name|. + void RemoveDOMAttribute(const std::string& attribute_name); + // Get Browser Plugin's DOM Node attribute |attribute_name|'s value. + std::string GetDOMAttributeValue(const std::string& attribute_name) const; + // Checks if the attribute |attribute_name| exists in the DOM. + bool HasDOMAttribute(const std::string& attribute_name) const; + + // Get the name attribute value. + std::string GetNameAttribute() const; + // Parse the name attribute value. + void ParseNameAttribute(); + // Get the src attribute value of the BrowserPlugin instance. + std::string GetSrcAttribute() const; + // Parse the src attribute value of the BrowserPlugin instance. + bool ParseSrcAttribute(std::string* error_message); + // Get the autosize attribute value. + bool GetAutoSizeAttribute() const; + // Parses the autosize attribute value. + void ParseAutoSizeAttribute(); + // Get the maxheight attribute value. + int GetMaxHeightAttribute() const; + // Get the maxwidth attribute value. + int GetMaxWidthAttribute() const; + // Get the minheight attribute value. + int GetMinHeightAttribute() const; + // Get the minwidth attribute value. + int GetMinWidthAttribute() const; + // Parse the minwidth, maxwidth, minheight, and maxheight attribute values. + void ParseSizeContraintsChanged(); + // The partition identifier string is stored as UTF-8. + std::string GetPartitionAttribute() const; + // This method can be successfully called only before the first navigation for + // this instance of BrowserPlugin. If an error occurs, the |error_message| is + // set appropriately to indicate the failure reason. + bool ParsePartitionAttribute(std::string* error_message); + // True if the partition attribute can be removed. + bool CanRemovePartitionAttribute(std::string* error_message); + + bool InAutoSizeBounds(const gfx::Size& size) const; + + // Get the guest's DOMWindow proxy. + NPObject* GetContentWindow() const; + + // Returns whether the guest process has crashed. + bool guest_crashed() const { return guest_crashed_; } + // Returns whether this BrowserPlugin has requested an instance ID. + bool HasNavigated() const; + // Returns whether this BrowserPlugin has allocated an instance ID. + bool HasGuestInstanceID() const; + + // Attaches the window identified by |window_id| to the the given node + // encapsulating a BrowserPlugin. + static bool AttachWindowTo(const WebKit::WebNode& node, + int window_id); + + // Informs the guest of an updated focus state. + void UpdateGuestFocusState(); + // Indicates whether the guest should be focused. + bool ShouldGuestBeFocused() const; + + // Embedder's device scale factor changed, we need to update the guest + // renderer. + void UpdateDeviceScaleFactor(float device_scale_factor); + + // A request to enable hardware compositing. + void EnableCompositing(bool enable); + // A request from content client to track lifetime of a JavaScript object. + // This is used to track permission request objects, and new window API + // window objects. + void TrackObjectLifetime(const NPVariant* request, int id); + + // Returns true if |point| lies within the bounds of the plugin rectangle. + // Not OK to use this function for making security-sensitive decision since it + // can return false positives when the plugin has rotation transformation + // applied. + bool InBounds(const gfx::Point& point) const; + + gfx::Point ToLocalCoordinates(const gfx::Point& point) const; + + // Called when a guest instance ID has been allocated by the browser process. + void OnInstanceIDAllocated(int guest_instance_id); + // Provided that a guest instance ID has been allocated, this method attaches + // this BrowserPlugin instance to that guest. |extra_params| are parameters + // passed in by the content embedder to the browser process. + void Attach(scoped_ptr<base::DictionaryValue> extra_params); + + // Notify the plugin about a compositor commit so that frame ACKs could be + // sent, if needed. + void DidCommitCompositorFrame(); + + // Returns whether a message should be forwarded to BrowserPlugin. + static bool ShouldForwardToBrowserPlugin(const IPC::Message& message); + + // WebKit::WebPlugin implementation. + virtual WebKit::WebPluginContainer* container() const OVERRIDE; + virtual bool initialize(WebKit::WebPluginContainer* container) OVERRIDE; + virtual void destroy() OVERRIDE; + virtual NPObject* scriptableObject() OVERRIDE; + virtual struct _NPP* pluginNPP() OVERRIDE; + virtual bool supportsKeyboardFocus() const OVERRIDE; + virtual bool supportsEditCommands() const OVERRIDE; + virtual bool canProcessDrag() const OVERRIDE; + virtual void paint( + WebKit::WebCanvas* canvas, + const WebKit::WebRect& rect) OVERRIDE; + virtual void updateGeometry( + const WebKit::WebRect& frame_rect, + const WebKit::WebRect& clip_rect, + const WebKit::WebVector<WebKit::WebRect>& cut_outs_rects, + bool is_visible) OVERRIDE; + virtual void updateFocus(bool focused) OVERRIDE; + virtual void updateVisibility(bool visible) OVERRIDE; + virtual bool acceptsInputEvents() OVERRIDE; + virtual bool handleInputEvent( + const WebKit::WebInputEvent& event, + WebKit::WebCursorInfo& cursor_info) OVERRIDE; + virtual bool handleDragStatusUpdate(WebKit::WebDragStatus drag_status, + const WebKit::WebDragData& drag_data, + WebKit::WebDragOperationsMask mask, + const WebKit::WebPoint& position, + const WebKit::WebPoint& screen) OVERRIDE; + virtual void didReceiveResponse( + const WebKit::WebURLResponse& response) OVERRIDE; + virtual void didReceiveData(const char* data, int data_length) OVERRIDE; + virtual void didFinishLoading() OVERRIDE; + virtual void didFailLoading(const WebKit::WebURLError& error) OVERRIDE; + virtual void didFinishLoadingFrameRequest( + const WebKit::WebURL& url, + void* notify_data) OVERRIDE; + virtual void didFailLoadingFrameRequest( + const WebKit::WebURL& url, + void* notify_data, + const WebKit::WebURLError& error) OVERRIDE; + virtual bool executeEditCommand(const WebKit::WebString& name) OVERRIDE; + virtual bool executeEditCommand(const WebKit::WebString& name, + const WebKit::WebString& value) OVERRIDE; + + // MouseLockDispatcher::LockTarget implementation. + virtual void OnLockMouseACK(bool succeeded) OVERRIDE; + virtual void OnMouseLockLost() OVERRIDE; + virtual bool HandleMouseLockedInputEvent( + const WebKit::WebMouseEvent& event) OVERRIDE; + + private: + friend class base::DeleteHelper<BrowserPlugin>; + // Only the manager is allowed to create a BrowserPlugin. + friend class BrowserPluginManagerImpl; + friend class MockBrowserPluginManager; + + // For unit/integration tests. + friend class MockBrowserPlugin; + + // A BrowserPlugin object is a controller that represents an instance of a + // browser plugin within the embedder renderer process. Each BrowserPlugin + // within a RenderView has a unique instance_id that is used to track per- + // BrowserPlugin state in the browser process. Once a BrowserPlugin does + // an initial navigation or is attached to a newly created guest, it acquires + // a guest_instance_id as well. The guest instance ID uniquely identifies a + // guest WebContents that's hosted by this BrowserPlugin. + BrowserPlugin( + RenderViewImpl* render_view, + WebKit::WebFrame* frame, + const WebKit::WebPluginParams& params); + + virtual ~BrowserPlugin(); + + int width() const { return plugin_rect_.width(); } + int height() const { return plugin_rect_.height(); } + gfx::Rect plugin_rect() { return plugin_rect_; } + // Gets the Max Height value used for auto size. + int GetAdjustedMaxHeight() const; + // Gets the Max Width value used for auto size. + int GetAdjustedMaxWidth() const; + // Gets the Min Height value used for auto size. + int GetAdjustedMinHeight() const; + // Gets the Min Width value used for auto size. + int GetAdjustedMinWidth() const; + BrowserPluginManager* browser_plugin_manager() const { + return browser_plugin_manager_.get(); + } + + // Virtual to allow for mocking in tests. + virtual float GetDeviceScaleFactor() const; + + void ShowSadGraphic(); + + // Parses the attributes of the browser plugin from the element's attributes + // and sets them appropriately. + void ParseAttributes(); + + // Triggers the event-listeners for |event_name|. Note that the function + // frees all the values in |props|. + void TriggerEvent(const std::string& event_name, + std::map<std::string, base::Value*>* props); + + // Creates and maps a shared damage buffer. + virtual base::SharedMemory* CreateDamageBuffer( + const size_t size, + base::SharedMemoryHandle* shared_memory_handle); + // Swaps out the |current_damage_buffer_| with the |pending_damage_buffer_|. + void SwapDamageBuffers(); + + // Populates BrowserPluginHostMsg_ResizeGuest_Params with resize state and + // allocates a new |pending_damage_buffer_| if in software rendering mode. + void PopulateResizeGuestParameters( + BrowserPluginHostMsg_ResizeGuest_Params* params, + const gfx::Rect& view_size); + + // Populates BrowserPluginHostMsg_AutoSize_Params object with autosize state. + void PopulateAutoSizeParameters( + BrowserPluginHostMsg_AutoSize_Params* params, bool current_auto_size); + + // Populates both AutoSize and ResizeGuest parameters based on the current + // autosize state. + void GetDamageBufferWithSizeParams( + BrowserPluginHostMsg_AutoSize_Params* auto_size_params, + BrowserPluginHostMsg_ResizeGuest_Params* resize_guest_params); + + // Informs the guest of an updated autosize state. + void UpdateGuestAutoSizeState(bool current_auto_size); + + // Indicates whether a damage buffer was used by the guest process for the + // provided |params|. + static bool UsesDamageBuffer( + const BrowserPluginMsg_UpdateRect_Params& params); + + // Indicates whether the |pending_damage_buffer_| was used to copy over pixels + // given the provided |params|. + bool UsesPendingDamageBuffer( + const BrowserPluginMsg_UpdateRect_Params& params); + + // Called when the tracked object of |id| ID becomes unreachable in + // JavaScript. + void OnTrackedObjectGarbageCollected(int id); + // V8 garbage collection callback for |object|. + static void WeakCallbackForTrackedObject(v8::Isolate* isolate, + v8::Persistent<v8::Value>* object, + void* param); + + // IPC message handlers. + // Please keep in alphabetical order. + void OnAdvanceFocus(int instance_id, bool reverse); + void OnAttachACK(int instance_id, + const BrowserPluginMsg_Attach_ACK_Params& ack_params); + void OnBuffersSwapped(int instance_id, + const BrowserPluginMsg_BuffersSwapped_Params& params); + void OnCompositorFrameSwapped(const IPC::Message& message); + void OnGuestContentWindowReady(int instance_id, + int content_window_routing_id); + void OnGuestGone(int instance_id); + void OnSetCursor(int instance_id, const WebCursor& cursor); + void OnSetMouseLock(int instance_id, bool enable); + void OnShouldAcceptTouchEvents(int instance_id, bool accept); + void OnUpdatedName(int instance_id, const std::string& name); + void OnUpdateRect(int instance_id, + const BrowserPluginMsg_UpdateRect_Params& params); + + // This is the browser-process-allocated instance ID that uniquely identifies + // a guest WebContents. + int guest_instance_id_; + // This indicates whether this BrowserPlugin has been attached to a + // WebContents. + bool attached_; + base::WeakPtr<RenderViewImpl> render_view_; + // We cache the |render_view_|'s routing ID because we need it on destruction. + // If the |render_view_| is destroyed before the BrowserPlugin is destroyed + // then we will attempt to access a NULL pointer. + int render_view_routing_id_; + WebKit::WebPluginContainer* container_; + scoped_ptr<BrowserPluginBindings> bindings_; + scoped_ptr<BrowserPluginBackingStore> backing_store_; + scoped_ptr<base::SharedMemory> current_damage_buffer_; + scoped_ptr<base::SharedMemory> pending_damage_buffer_; + uint32 damage_buffer_sequence_id_; + bool resize_ack_received_; + gfx::Rect plugin_rect_; + float last_device_scale_factor_; + // Bitmap for crashed plugin. Lazily initialized, non-owning pointer. + SkBitmap* sad_guest_; + bool guest_crashed_; + scoped_ptr<BrowserPluginHostMsg_ResizeGuest_Params> pending_resize_params_; + bool auto_size_ack_pending_; + std::string storage_partition_id_; + bool persist_storage_; + bool valid_partition_id_; + int content_window_routing_id_; + bool plugin_focused_; + // Tracks the visibility of the browser plugin regardless of the whole + // embedder RenderView's visibility. + bool visible_; + + WebCursor cursor_; + + gfx::Size last_view_size_; + bool before_first_navigation_; + bool mouse_locked_; + + typedef std::pair<int, base::WeakPtr<BrowserPlugin> > TrackedV8ObjectID; + std::map<int, TrackedV8ObjectID*> tracked_v8_objects_; + + // BrowserPlugin outlives RenderViewImpl in Chrome Apps and so we need to + // store the BrowserPlugin's BrowserPluginManager in a member variable to + // avoid accessing the RenderViewImpl. + scoped_refptr<BrowserPluginManager> browser_plugin_manager_; + + // Used for HW compositing. + bool compositing_enabled_; + scoped_refptr<BrowserPluginCompositingHelper> compositing_helper_; + + // Used to identify the plugin to WebBindings. + scoped_ptr<struct _NPP> npp_; + + // Weak factory used in v8 |MakeWeak| callback, since the v8 callback might + // get called after BrowserPlugin has been destroyed. + base::WeakPtrFactory<BrowserPlugin> weak_ptr_factory_; + + std::vector<EditCommand> edit_commands_; + + DISALLOW_COPY_AND_ASSIGN(BrowserPlugin); +}; + +} // namespace content + +#endif // CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_H_ diff --git a/chromium/content/renderer/browser_plugin/browser_plugin_backing_store.cc b/chromium/content/renderer/browser_plugin/browser_plugin_backing_store.cc new file mode 100644 index 00000000000..60e57067e5e --- /dev/null +++ b/chromium/content/renderer/browser_plugin/browser_plugin_backing_store.cc @@ -0,0 +1,99 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/browser_plugin/browser_plugin_backing_store.h" + +#include "ui/gfx/canvas.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/rect_conversions.h" +#include "ui/gfx/size_conversions.h" +#include "ui/gfx/vector2d_conversions.h" +#include "ui/surface/transport_dib.h" + +namespace content { + +// Max height and width for layers +static const int kMaxSize = 23170; + +BrowserPluginBackingStore::BrowserPluginBackingStore( + const gfx::Size& size, + float scale_factor) + : size_(size), + scale_factor_(scale_factor) { + gfx::Size pixel_size = gfx::ToFlooredSize(gfx::ScaleSize(size, scale_factor)); + bitmap_.setConfig(SkBitmap::kARGB_8888_Config, + pixel_size.width(), pixel_size.height()); + bitmap_.allocPixels(); + canvas_.reset(new SkCanvas(bitmap_)); +} + +BrowserPluginBackingStore::~BrowserPluginBackingStore() { +} + +void BrowserPluginBackingStore::PaintToBackingStore( + const gfx::Rect& bitmap_rect, + const std::vector<gfx::Rect>& copy_rects, + void* bitmap) { + if (bitmap_rect.IsEmpty()) + return; + + gfx::Rect pixel_bitmap_rect = gfx::ToFlooredRectDeprecated( + gfx::ScaleRect(bitmap_rect, scale_factor_)); + + const int width = pixel_bitmap_rect.width(); + const int height = pixel_bitmap_rect.height(); + + if (width <= 0 || width > kMaxSize || + height <= 0 || height > kMaxSize) + return; + + if (!bitmap) + return; + + SkPaint copy_paint; + copy_paint.setXfermodeMode(SkXfermode::kSrc_Mode); + + SkBitmap sk_bitmap; + sk_bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); + sk_bitmap.setPixels(bitmap); + for (size_t i = 0; i < copy_rects.size(); i++) { + const gfx::Rect& pixel_copy_rect = gfx::ToEnclosingRect( + gfx::ScaleRect(copy_rects[i], scale_factor_)); + int x = pixel_copy_rect.x() - pixel_bitmap_rect.x(); + int y = pixel_copy_rect.y() - pixel_bitmap_rect.y(); + SkIRect srcrect = SkIRect::MakeXYWH(x, y, + pixel_copy_rect.width(), + pixel_copy_rect.height()); + + SkRect dstrect = SkRect::MakeXYWH( + SkIntToScalar(pixel_copy_rect.x()), + SkIntToScalar(pixel_copy_rect.y()), + SkIntToScalar(pixel_copy_rect.width()), + SkIntToScalar(pixel_copy_rect.height())); + canvas_.get()->drawBitmapRect(sk_bitmap, &srcrect, dstrect, ©_paint); + } +} + +void BrowserPluginBackingStore::ScrollBackingStore( + const gfx::Vector2d& delta, + const gfx::Rect& clip_rect, + const gfx::Size& view_size) { + gfx::Rect pixel_rect = gfx::ToEnclosingRect( + gfx::ScaleRect(clip_rect, scale_factor_)); + gfx::Vector2d pixel_delta = gfx::ToFlooredVector2d( + gfx::ScaleVector2d(delta, scale_factor_)); + + int x = std::min(pixel_rect.x(), pixel_rect.x() - pixel_delta.x()); + int y = std::min(pixel_rect.y(), pixel_rect.y() - pixel_delta.y()); + int w = pixel_rect.width() + abs(pixel_delta.x()); + int h = pixel_rect.height() + abs(pixel_delta.y()); + SkIRect rect = SkIRect::MakeXYWH(x, y, w, h); + bitmap_.scrollRect(&rect, pixel_delta.x(), pixel_delta.y()); +} + +void BrowserPluginBackingStore::Clear(SkColor clear_color) { + canvas_->clear(clear_color); +} + +} // namespace content diff --git a/chromium/content/renderer/browser_plugin/browser_plugin_backing_store.h b/chromium/content/renderer/browser_plugin/browser_plugin_backing_store.h new file mode 100644 index 00000000000..5b030321fc7 --- /dev/null +++ b/chromium/content/renderer/browser_plugin/browser_plugin_backing_store.h @@ -0,0 +1,66 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_BACKING_STORE_H__ +#define CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_BACKING_STORE_H__ + +#include <vector> + +#include "base/memory/scoped_ptr.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/size.h" +#include "ui/gfx/vector2d.h" + +class SkCanvas; +class TransportDIB; + +namespace gfx { +class Canvas; +class Point; +class Rect; +} + +namespace content { + +// The BrowserPluginBackingStore is a wrapper around an SkBitmap that is used +// in the software rendering path of the browser plugin. The backing store has +// two write operations: +// 1. PaintToBackingStore copies pixel regions to the bitmap. +// 2. ScrollBackingStore scrolls a region of the bitmap by dx, and dy. +// These are called in response to changes in the guest relayed via +// BrowserPluginMsg_UpdateRect. See BrowserPlugin::UpdateRect. +class BrowserPluginBackingStore { + public: + BrowserPluginBackingStore(const gfx::Size& size, float scale_factor); + virtual ~BrowserPluginBackingStore(); + + void PaintToBackingStore( + const gfx::Rect& bitmap_rect, + const std::vector<gfx::Rect>& copy_rects, + void* bitmap); + + void ScrollBackingStore(const gfx::Vector2d& delta, + const gfx::Rect& clip_rect, + const gfx::Size& view_size); + + void Clear(SkColor clear_color); + + const gfx::Size& GetSize() const { return size_; } + + const SkBitmap& GetBitmap() const { return bitmap_; } + + float GetScaleFactor() const { return scale_factor_; } + + private: + gfx::Size size_; + SkBitmap bitmap_; + scoped_ptr<SkCanvas> canvas_; + float scale_factor_; + + DISALLOW_COPY_AND_ASSIGN(BrowserPluginBackingStore); +}; + +} // namespace content + +#endif // CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_BACKING_STORE_H__ diff --git a/chromium/content/renderer/browser_plugin/browser_plugin_bindings.cc b/chromium/content/renderer/browser_plugin/browser_plugin_bindings.cc new file mode 100644 index 00000000000..26f00504e32 --- /dev/null +++ b/chromium/content/renderer/browser_plugin/browser_plugin_bindings.cc @@ -0,0 +1,760 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/browser_plugin/browser_plugin_bindings.h" + +#include <cstdlib> +#include <string> + +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "base/strings/string16.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "base/strings/utf_string_conversions.h" +#include "content/common/browser_plugin/browser_plugin_constants.h" +#include "content/public/renderer/v8_value_converter.h" +#include "content/renderer/browser_plugin/browser_plugin.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/web/WebBindings.h" +#include "third_party/WebKit/public/web/WebDOMMessageEvent.h" +#include "third_party/WebKit/public/web/WebDocument.h" +#include "third_party/WebKit/public/web/WebElement.h" +#include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebNode.h" +#include "third_party/WebKit/public/web/WebPluginContainer.h" +#include "third_party/WebKit/public/web/WebView.h" +#include "third_party/npapi/bindings/npapi.h" +#include "v8/include/v8.h" + +using WebKit::WebBindings; +using WebKit::WebElement; +using WebKit::WebDOMEvent; +using WebKit::WebDOMMessageEvent; +using WebKit::WebPluginContainer; +using WebKit::WebString; + +namespace content { + +namespace { + +BrowserPluginBindings* GetBindings(NPObject* object) { + return static_cast<BrowserPluginBindings::BrowserPluginNPObject*>(object)-> + message_channel.get(); +} + +std::string StringFromNPVariant(const NPVariant& variant) { + if (!NPVARIANT_IS_STRING(variant)) + return std::string(); + const NPString& np_string = NPVARIANT_TO_STRING(variant); + return std::string(np_string.UTF8Characters, np_string.UTF8Length); +} + +bool StringToNPVariant(const std::string &in, NPVariant *variant) { + size_t length = in.size(); + NPUTF8 *chars = static_cast<NPUTF8 *>(malloc(length)); + if (!chars) { + VOID_TO_NPVARIANT(*variant); + return false; + } + memcpy(chars, in.c_str(), length); + STRINGN_TO_NPVARIANT(chars, length, *variant); + return true; +} + +// Depending on where the attribute comes from it could be a string, int32, +// or a double. Javascript tends to produce an int32 or a string, but setting +// the value from the developer tools console may also produce a double. +int IntFromNPVariant(const NPVariant& variant) { + int value = 0; + switch (variant.type) { + case NPVariantType_Double: + value = NPVARIANT_TO_DOUBLE(variant); + break; + case NPVariantType_Int32: + value = NPVARIANT_TO_INT32(variant); + break; + case NPVariantType_String: + base::StringToInt(StringFromNPVariant(variant), &value); + break; + default: + break; + } + return value; +} + +//------------------------------------------------------------------------------ +// Implementations of NPClass functions. These are here to: +// - Implement src attribute. +//------------------------------------------------------------------------------ +NPObject* BrowserPluginBindingsAllocate(NPP npp, NPClass* the_class) { + return new BrowserPluginBindings::BrowserPluginNPObject; +} + +void BrowserPluginBindingsDeallocate(NPObject* object) { + BrowserPluginBindings::BrowserPluginNPObject* instance = + static_cast<BrowserPluginBindings::BrowserPluginNPObject*>(object); + delete instance; +} + +bool BrowserPluginBindingsHasMethod(NPObject* np_obj, NPIdentifier name) { + if (!np_obj) + return false; + + BrowserPluginBindings* bindings = GetBindings(np_obj); + if (!bindings) + return false; + + return bindings->HasMethod(name); +} + +bool BrowserPluginBindingsInvoke(NPObject* np_obj, NPIdentifier name, + const NPVariant* args, uint32 arg_count, + NPVariant* result) { + if (!np_obj) + return false; + + BrowserPluginBindings* bindings = GetBindings(np_obj); + if (!bindings) + return false; + + return bindings->InvokeMethod(name, args, arg_count, result); +} + +bool BrowserPluginBindingsInvokeDefault(NPObject* np_obj, + const NPVariant* args, + uint32 arg_count, + NPVariant* result) { + NOTIMPLEMENTED(); + return false; +} + +bool BrowserPluginBindingsHasProperty(NPObject* np_obj, NPIdentifier name) { + if (!np_obj) + return false; + + BrowserPluginBindings* bindings = GetBindings(np_obj); + if (!bindings) + return false; + + return bindings->HasProperty(name); +} + +bool BrowserPluginBindingsGetProperty(NPObject* np_obj, NPIdentifier name, + NPVariant* result) { + if (!np_obj) + return false; + + if (!result) + return false; + + // All attributes from here on rely on the bindings, so retrieve it once and + // return on failure. + BrowserPluginBindings* bindings = GetBindings(np_obj); + if (!bindings) + return false; + + return bindings->GetProperty(name, result); +} + +bool BrowserPluginBindingsSetProperty(NPObject* np_obj, NPIdentifier name, + const NPVariant* variant) { + if (!np_obj) + return false; + if (!variant) + return false; + + // All attributes from here on rely on the bindings, so retrieve it once and + // return on failure. + BrowserPluginBindings* bindings = GetBindings(np_obj); + if (!bindings) + return false; + + if (variant->type == NPVariantType_Null) + return bindings->RemoveProperty(np_obj, name); + + return bindings->SetProperty(np_obj, name, variant); +} + +bool BrowserPluginBindingsEnumerate(NPObject *np_obj, NPIdentifier **value, + uint32_t *count) { + NOTIMPLEMENTED(); + return true; +} + +NPClass browser_plugin_message_class = { + NP_CLASS_STRUCT_VERSION, + &BrowserPluginBindingsAllocate, + &BrowserPluginBindingsDeallocate, + NULL, + &BrowserPluginBindingsHasMethod, + &BrowserPluginBindingsInvoke, + &BrowserPluginBindingsInvokeDefault, + &BrowserPluginBindingsHasProperty, + &BrowserPluginBindingsGetProperty, + &BrowserPluginBindingsSetProperty, + NULL, + &BrowserPluginBindingsEnumerate, +}; + +} // namespace + +// BrowserPluginMethodBinding -------------------------------------------------- + +class BrowserPluginMethodBinding { + public: + BrowserPluginMethodBinding(const char name[], uint32 arg_count) + : name_(name), + arg_count_(arg_count) { + } + + virtual ~BrowserPluginMethodBinding() {} + + bool MatchesName(NPIdentifier name) const { + return WebBindings::getStringIdentifier(name_.c_str()) == name; + } + + uint32 arg_count() const { return arg_count_; } + + virtual bool Invoke(BrowserPluginBindings* bindings, + const NPVariant* args, + NPVariant* result) = 0; + + private: + std::string name_; + uint32 arg_count_; + + DISALLOW_COPY_AND_ASSIGN(BrowserPluginMethodBinding); +}; + +class BrowserPluginBindingAttach: public BrowserPluginMethodBinding { + public: + BrowserPluginBindingAttach() + : BrowserPluginMethodBinding( + browser_plugin::kMethodInternalAttach, 1) { + } + + virtual bool Invoke(BrowserPluginBindings* bindings, + const NPVariant* args, + NPVariant* result) OVERRIDE { + if (!bindings->instance()->render_view()) + return false; + + scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); + v8::Handle<v8::Value> obj(WebKit::WebBindings::toV8Value(&args[0])); + scoped_ptr<base::Value> value( + converter->FromV8Value(obj, bindings->instance()->render_view()-> + GetWebView()->mainFrame()->mainWorldScriptContext())); + if (!value) + return false; + + if (!value->IsType(Value::TYPE_DICTIONARY)) + return false; + + scoped_ptr<base::DictionaryValue> extra_params( + static_cast<base::DictionaryValue*>(value.release())); + bindings->instance()->Attach(extra_params.Pass()); + return true; + } + + private: + DISALLOW_COPY_AND_ASSIGN(BrowserPluginBindingAttach); +}; + +class BrowserPluginBindingAttachWindowTo : public BrowserPluginMethodBinding { + public: + BrowserPluginBindingAttachWindowTo() + : BrowserPluginMethodBinding( + browser_plugin::kMethodInternalAttachWindowTo, 2) { + } + + virtual bool Invoke(BrowserPluginBindings* bindings, + const NPVariant* args, + NPVariant* result) OVERRIDE { + WebKit::WebNode node; + WebBindings::getNode(NPVARIANT_TO_OBJECT(args[0]), &node); + int window_id = IntFromNPVariant(args[1]); + BOOLEAN_TO_NPVARIANT(BrowserPlugin::AttachWindowTo(node, window_id), + *result); + return true; + } + + private: + DISALLOW_COPY_AND_ASSIGN(BrowserPluginBindingAttachWindowTo); +}; + +// Note: This is a method that is used internally by the <webview> shim only. +// This should not be exposed to developers. +class BrowserPluginBindingTrackObjectLifetime + : public BrowserPluginMethodBinding { + public: + BrowserPluginBindingTrackObjectLifetime() + : BrowserPluginMethodBinding( + browser_plugin::kMethodInternalTrackObjectLifetime, 2) { + } + + virtual bool Invoke(BrowserPluginBindings* bindings, + const NPVariant* args, + NPVariant* result) OVERRIDE { + bindings->instance()->TrackObjectLifetime(args, IntFromNPVariant(args[1])); + return true; + } + + private: + DISALLOW_COPY_AND_ASSIGN(BrowserPluginBindingTrackObjectLifetime); +}; + +// BrowserPluginPropertyBinding ------------------------------------------------ + +class BrowserPluginPropertyBinding { + public: + explicit BrowserPluginPropertyBinding(const char name[]) : name_(name) {} + virtual ~BrowserPluginPropertyBinding() {} + const std::string& name() const { return name_; } + bool MatchesName(NPIdentifier name) const { + return WebBindings::getStringIdentifier(name_.c_str()) == name; + } + virtual bool GetProperty(BrowserPluginBindings* bindings, + NPVariant* result) = 0; + virtual bool SetProperty(BrowserPluginBindings* bindings, + NPObject* np_obj, + const NPVariant* variant) = 0; + virtual void RemoveProperty(BrowserPluginBindings* bindings, + NPObject* np_obj) = 0; + // Updates the DOM Attribute value with the current property value. + void UpdateDOMAttribute(BrowserPluginBindings* bindings, + std::string new_value) { + bindings->instance()->UpdateDOMAttribute(name(), new_value); + } + private: + std::string name_; + + DISALLOW_COPY_AND_ASSIGN(BrowserPluginPropertyBinding); +}; + +class BrowserPluginPropertyBindingAutoSize + : public BrowserPluginPropertyBinding { + public: + BrowserPluginPropertyBindingAutoSize() + : BrowserPluginPropertyBinding(browser_plugin::kAttributeAutoSize) { + } + virtual bool GetProperty(BrowserPluginBindings* bindings, + NPVariant* result) OVERRIDE { + bool auto_size = bindings->instance()->GetAutoSizeAttribute(); + BOOLEAN_TO_NPVARIANT(auto_size, *result); + return true; + } + virtual bool SetProperty(BrowserPluginBindings* bindings, + NPObject* np_obj, + const NPVariant* variant) OVERRIDE { + std::string value = StringFromNPVariant(*variant); + if (!bindings->instance()->HasDOMAttribute(name())) { + UpdateDOMAttribute(bindings, value); + bindings->instance()->ParseAutoSizeAttribute(); + } else { + UpdateDOMAttribute(bindings, value); + } + return true; + } + virtual void RemoveProperty(BrowserPluginBindings* bindings, + NPObject* np_obj) OVERRIDE { + bindings->instance()->RemoveDOMAttribute(name()); + bindings->instance()->ParseAutoSizeAttribute(); + } + private: + DISALLOW_COPY_AND_ASSIGN(BrowserPluginPropertyBindingAutoSize); +}; + +class BrowserPluginPropertyBindingContentWindow + : public BrowserPluginPropertyBinding { + public: + BrowserPluginPropertyBindingContentWindow() + : BrowserPluginPropertyBinding(browser_plugin::kAttributeContentWindow) { + } + virtual bool GetProperty(BrowserPluginBindings* bindings, + NPVariant* result) OVERRIDE { + NPObject* obj = bindings->instance()->GetContentWindow(); + if (obj) { + result->type = NPVariantType_Object; + result->value.objectValue = WebBindings::retainObject(obj); + } + return true; + } + virtual bool SetProperty(BrowserPluginBindings* bindings, + NPObject* np_obj, + const NPVariant* variant) OVERRIDE { + return false; + } + virtual void RemoveProperty(BrowserPluginBindings* bindings, + NPObject* np_obj) OVERRIDE {} + private: + DISALLOW_COPY_AND_ASSIGN(BrowserPluginPropertyBindingContentWindow); +}; + +class BrowserPluginPropertyBindingMaxHeight + : public BrowserPluginPropertyBinding { + public: + BrowserPluginPropertyBindingMaxHeight() + : BrowserPluginPropertyBinding(browser_plugin::kAttributeMaxHeight) { + } + virtual bool GetProperty(BrowserPluginBindings* bindings, + NPVariant* result) OVERRIDE { + int max_height = bindings->instance()->GetMaxHeightAttribute(); + INT32_TO_NPVARIANT(max_height, *result); + return true; + } + virtual bool SetProperty(BrowserPluginBindings* bindings, + NPObject* np_obj, + const NPVariant* variant) OVERRIDE { + int new_value = IntFromNPVariant(*variant); + if (bindings->instance()->GetMaxHeightAttribute() != new_value) { + UpdateDOMAttribute(bindings, base::IntToString(new_value)); + bindings->instance()->ParseSizeContraintsChanged(); + } + return true; + } + virtual void RemoveProperty(BrowserPluginBindings* bindings, + NPObject* np_obj) OVERRIDE { + bindings->instance()->RemoveDOMAttribute(name()); + bindings->instance()->ParseSizeContraintsChanged(); + } + private: + DISALLOW_COPY_AND_ASSIGN(BrowserPluginPropertyBindingMaxHeight); +}; + +class BrowserPluginPropertyBindingMaxWidth + : public BrowserPluginPropertyBinding { + public: + BrowserPluginPropertyBindingMaxWidth() + : BrowserPluginPropertyBinding(browser_plugin::kAttributeMaxWidth) { + } + virtual bool GetProperty(BrowserPluginBindings* bindings, + NPVariant* result) OVERRIDE { + int max_width = bindings->instance()->GetMaxWidthAttribute(); + INT32_TO_NPVARIANT(max_width, *result); + return true; + } + virtual bool SetProperty(BrowserPluginBindings* bindings, + NPObject* np_obj, + const NPVariant* variant) OVERRIDE { + int new_value = IntFromNPVariant(*variant); + if (bindings->instance()->GetMaxWidthAttribute() != new_value) { + UpdateDOMAttribute(bindings, base::IntToString(new_value)); + bindings->instance()->ParseSizeContraintsChanged(); + } + return true; + } + virtual void RemoveProperty(BrowserPluginBindings* bindings, + NPObject* np_obj) OVERRIDE { + bindings->instance()->RemoveDOMAttribute(name()); + bindings->instance()->ParseSizeContraintsChanged(); + } + private: + DISALLOW_COPY_AND_ASSIGN(BrowserPluginPropertyBindingMaxWidth); +}; + +class BrowserPluginPropertyBindingMinHeight + : public BrowserPluginPropertyBinding { + public: + BrowserPluginPropertyBindingMinHeight() + : BrowserPluginPropertyBinding(browser_plugin::kAttributeMinHeight) { + } + virtual bool GetProperty(BrowserPluginBindings* bindings, + NPVariant* result) OVERRIDE { + int min_height = bindings->instance()->GetMinHeightAttribute(); + INT32_TO_NPVARIANT(min_height, *result); + return true; + } + virtual bool SetProperty(BrowserPluginBindings* bindings, + NPObject* np_obj, + const NPVariant* variant) OVERRIDE { + int new_value = IntFromNPVariant(*variant); + if (bindings->instance()->GetMinHeightAttribute() != new_value) { + UpdateDOMAttribute(bindings, base::IntToString(new_value)); + bindings->instance()->ParseSizeContraintsChanged(); + } + return true; + } + virtual void RemoveProperty(BrowserPluginBindings* bindings, + NPObject* np_obj) OVERRIDE { + bindings->instance()->RemoveDOMAttribute(name()); + bindings->instance()->ParseSizeContraintsChanged(); + } + private: + DISALLOW_COPY_AND_ASSIGN(BrowserPluginPropertyBindingMinHeight); +}; + +class BrowserPluginPropertyBindingMinWidth + : public BrowserPluginPropertyBinding { + public: + BrowserPluginPropertyBindingMinWidth() + : BrowserPluginPropertyBinding(browser_plugin::kAttributeMinWidth) { + } + virtual bool GetProperty(BrowserPluginBindings* bindings, + NPVariant* result) OVERRIDE { + int min_width = bindings->instance()->GetMinWidthAttribute(); + INT32_TO_NPVARIANT(min_width, *result); + return true; + } + virtual bool SetProperty(BrowserPluginBindings* bindings, + NPObject* np_obj, + const NPVariant* variant) OVERRIDE { + int new_value = IntFromNPVariant(*variant); + if (bindings->instance()->GetMinWidthAttribute() != new_value) { + UpdateDOMAttribute(bindings, base::IntToString(new_value)); + bindings->instance()->ParseSizeContraintsChanged(); + } + return true; + } + virtual void RemoveProperty(BrowserPluginBindings* bindings, + NPObject* np_obj) OVERRIDE { + bindings->instance()->RemoveDOMAttribute(name()); + bindings->instance()->ParseSizeContraintsChanged(); + } + private: + DISALLOW_COPY_AND_ASSIGN(BrowserPluginPropertyBindingMinWidth); +}; + +class BrowserPluginPropertyBindingName + : public BrowserPluginPropertyBinding { + public: + BrowserPluginPropertyBindingName() + : BrowserPluginPropertyBinding(browser_plugin::kAttributeName) { + } + virtual bool GetProperty(BrowserPluginBindings* bindings, + NPVariant* result) OVERRIDE { + std::string name = bindings->instance()->GetNameAttribute(); + return StringToNPVariant(name, result); + } + virtual bool SetProperty(BrowserPluginBindings* bindings, + NPObject* np_obj, + const NPVariant* variant) OVERRIDE { + std::string new_value = StringFromNPVariant(*variant); + if (bindings->instance()->GetNameAttribute() != new_value) { + UpdateDOMAttribute(bindings, new_value); + bindings->instance()->ParseNameAttribute(); + } + return true; + } + virtual void RemoveProperty(BrowserPluginBindings* bindings, + NPObject* np_obj) OVERRIDE { + bindings->instance()->RemoveDOMAttribute(name()); + bindings->instance()->ParseNameAttribute(); + } + private: + DISALLOW_COPY_AND_ASSIGN(BrowserPluginPropertyBindingName); +}; + +class BrowserPluginPropertyBindingPartition + : public BrowserPluginPropertyBinding { + public: + BrowserPluginPropertyBindingPartition() + : BrowserPluginPropertyBinding(browser_plugin::kAttributePartition) { + } + virtual bool GetProperty(BrowserPluginBindings* bindings, + NPVariant* result) OVERRIDE { + std::string partition_id = bindings->instance()->GetPartitionAttribute(); + return StringToNPVariant(partition_id, result); + } + virtual bool SetProperty(BrowserPluginBindings* bindings, + NPObject* np_obj, + const NPVariant* variant) OVERRIDE { + std::string new_value = StringFromNPVariant(*variant); + std::string old_value = bindings->instance()->GetPartitionAttribute(); + if (old_value != new_value) { + UpdateDOMAttribute(bindings, new_value); + std::string error_message; + if (!bindings->instance()->ParsePartitionAttribute(&error_message)) { + // Reset to old value on error. + UpdateDOMAttribute(bindings, old_value); + // Exceptions must be set as the last operation before returning to + // script. + WebBindings::setException( + np_obj, static_cast<const NPUTF8 *>(error_message.c_str())); + return false; + } + } + return true; + } + virtual void RemoveProperty(BrowserPluginBindings* bindings, + NPObject* np_obj) OVERRIDE { + std::string error_message; + if (bindings->instance()->CanRemovePartitionAttribute(&error_message)) { + bindings->instance()->RemoveDOMAttribute(name()); + } else { + WebBindings::setException( + np_obj, static_cast<const NPUTF8 *>(error_message.c_str())); + } + } + private: + DISALLOW_COPY_AND_ASSIGN(BrowserPluginPropertyBindingPartition); +}; + +class BrowserPluginPropertyBindingSrc : public BrowserPluginPropertyBinding { + public: + BrowserPluginPropertyBindingSrc() + : BrowserPluginPropertyBinding(browser_plugin::kAttributeSrc) { + } + virtual bool GetProperty(BrowserPluginBindings* bindings, + NPVariant* result) OVERRIDE { + std::string src = bindings->instance()->GetSrcAttribute(); + return StringToNPVariant(src, result); + } + virtual bool SetProperty(BrowserPluginBindings* bindings, + NPObject* np_obj, + const NPVariant* variant) OVERRIDE { + std::string new_value = StringFromNPVariant(*variant); + // We should not be issuing navigation IPCs if we attempt to set the + // src property to the empty string. Instead, we want to simply restore + // the src attribute back to its old value. + if (new_value.empty()) { + RemoveProperty(bindings, np_obj); + return true; + } + std::string old_value = bindings->instance()->GetSrcAttribute(); + bool guest_crashed = bindings->instance()->guest_crashed(); + if ((old_value != new_value) || guest_crashed) { + // If the new value was empty then we're effectively resetting the + // attribute to the old value here. This will be picked up by <webview>'s + // mutation observer and will restore the src attribute after it has been + // removed. + UpdateDOMAttribute(bindings, new_value); + std::string error_message; + if (!bindings->instance()->ParseSrcAttribute(&error_message)) { + // Reset to old value on error. + UpdateDOMAttribute(bindings, old_value); + // Exceptions must be set as the last operation before returning to + // script. + WebBindings::setException( + np_obj, static_cast<const NPUTF8 *>(error_message.c_str())); + return false; + } + } + return true; + } + virtual void RemoveProperty(BrowserPluginBindings* bindings, + NPObject* np_obj) OVERRIDE { + std::string old_value = bindings->instance()->GetSrcAttribute(); + if (old_value.empty()) + return; + // Remove the DOM attribute to trigger the mutation observer when it is + // restored to its original value again. + bindings->instance()->RemoveDOMAttribute(name()); + UpdateDOMAttribute(bindings, old_value); + } + private: + DISALLOW_COPY_AND_ASSIGN(BrowserPluginPropertyBindingSrc); +}; + + +// BrowserPluginBindings ------------------------------------------------------ + +BrowserPluginBindings::BrowserPluginNPObject::BrowserPluginNPObject() { +} + +BrowserPluginBindings::BrowserPluginNPObject::~BrowserPluginNPObject() { +} + +BrowserPluginBindings::BrowserPluginBindings(BrowserPlugin* instance) + : instance_(instance), + np_object_(NULL), + weak_ptr_factory_(this) { + NPObject* obj = + WebBindings::createObject(instance->pluginNPP(), + &browser_plugin_message_class); + np_object_ = static_cast<BrowserPluginBindings::BrowserPluginNPObject*>(obj); + np_object_->message_channel = weak_ptr_factory_.GetWeakPtr(); + + method_bindings_.push_back(new BrowserPluginBindingAttach); + method_bindings_.push_back(new BrowserPluginBindingAttachWindowTo); + method_bindings_.push_back(new BrowserPluginBindingTrackObjectLifetime); + + property_bindings_.push_back(new BrowserPluginPropertyBindingAutoSize); + property_bindings_.push_back(new BrowserPluginPropertyBindingContentWindow); + property_bindings_.push_back(new BrowserPluginPropertyBindingMaxHeight); + property_bindings_.push_back(new BrowserPluginPropertyBindingMaxWidth); + property_bindings_.push_back(new BrowserPluginPropertyBindingMinHeight); + property_bindings_.push_back(new BrowserPluginPropertyBindingMinWidth); + property_bindings_.push_back(new BrowserPluginPropertyBindingName); + property_bindings_.push_back(new BrowserPluginPropertyBindingPartition); + property_bindings_.push_back(new BrowserPluginPropertyBindingSrc); +} + +BrowserPluginBindings::~BrowserPluginBindings() { + WebBindings::releaseObject(np_object_); +} + +bool BrowserPluginBindings::HasMethod(NPIdentifier name) const { + for (BindingList::const_iterator iter = method_bindings_.begin(); + iter != method_bindings_.end(); + ++iter) { + if ((*iter)->MatchesName(name)) + return true; + } + return false; +} + +bool BrowserPluginBindings::InvokeMethod(NPIdentifier name, + const NPVariant* args, + uint32 arg_count, + NPVariant* result) { + for (BindingList::iterator iter = method_bindings_.begin(); + iter != method_bindings_.end(); + ++iter) { + if ((*iter)->MatchesName(name) && (*iter)->arg_count() == arg_count) + return (*iter)->Invoke(this, args, result); + } + return false; +} + +bool BrowserPluginBindings::HasProperty(NPIdentifier name) const { + for (PropertyBindingList::const_iterator iter = property_bindings_.begin(); + iter != property_bindings_.end(); + ++iter) { + if ((*iter)->MatchesName(name)) + return true; + } + return false; +} + +bool BrowserPluginBindings::SetProperty(NPObject* np_obj, + NPIdentifier name, + const NPVariant* variant) { + for (PropertyBindingList::iterator iter = property_bindings_.begin(); + iter != property_bindings_.end(); + ++iter) { + if ((*iter)->MatchesName(name)) { + if ((*iter)->SetProperty(this, np_obj, variant)) { + return true; + } + break; + } + } + return false; +} + +bool BrowserPluginBindings::RemoveProperty(NPObject* np_obj, + NPIdentifier name) { + for (PropertyBindingList::iterator iter = property_bindings_.begin(); + iter != property_bindings_.end(); + ++iter) { + if ((*iter)->MatchesName(name)) { + (*iter)->RemoveProperty(this, np_obj); + return true; + } + } + return false; +} + +bool BrowserPluginBindings::GetProperty(NPIdentifier name, NPVariant* result) { + for (PropertyBindingList::iterator iter = property_bindings_.begin(); + iter != property_bindings_.end(); + ++iter) { + if ((*iter)->MatchesName(name)) + return (*iter)->GetProperty(this, result); + } + return false; +} + +} // namespace content diff --git a/chromium/content/renderer/browser_plugin/browser_plugin_bindings.h b/chromium/content/renderer/browser_plugin/browser_plugin_bindings.h new file mode 100644 index 00000000000..91e8ced8439 --- /dev/null +++ b/chromium/content/renderer/browser_plugin/browser_plugin_bindings.h @@ -0,0 +1,71 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_BINDINGS_H__ +#define CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_BINDINGS_H__ + +#include "base/memory/scoped_vector.h" +#include "base/memory/weak_ptr.h" +#include "ppapi/shared_impl/resource.h" +#include "third_party/npapi/bindings/npruntime.h" + +namespace content { + +class BrowserPlugin; +class BrowserPluginMethodBinding; +class BrowserPluginPropertyBinding; + +class BrowserPluginBindings { + public: + // BrowserPluginNPObject is a simple struct that adds a pointer back to a + // BrowserPluginBindings instance. This way, we can use an NPObject to allow + // JavaScript interactions without forcing BrowserPluginBindings to inherit + // from NPObject. + struct BrowserPluginNPObject : public NPObject { + BrowserPluginNPObject(); + ~BrowserPluginNPObject(); + + base::WeakPtr<BrowserPluginBindings> message_channel; + }; + + explicit BrowserPluginBindings(BrowserPlugin* instance); + ~BrowserPluginBindings(); + + NPObject* np_object() const { return np_object_; } + + BrowserPlugin* instance() const { return instance_; } + + bool HasMethod(NPIdentifier name) const; + + bool InvokeMethod(NPIdentifier name, + const NPVariant* args, + uint32 arg_count, + NPVariant* result); + + bool HasProperty(NPIdentifier name) const; + bool SetProperty(NPObject* np_obj, + NPIdentifier name, + const NPVariant* variant); + bool GetProperty(NPIdentifier name, NPVariant* result); + bool RemoveProperty(NPObject *np_obj, NPIdentifier name); + private: + BrowserPlugin* instance_; + // The NPObject we use to expose postMessage to JavaScript. + BrowserPluginNPObject* np_object_; + + typedef ScopedVector<BrowserPluginMethodBinding> BindingList; + BindingList method_bindings_; + typedef ScopedVector<BrowserPluginPropertyBinding> PropertyBindingList; + PropertyBindingList property_bindings_; + + // This is used to ensure pending tasks will not fire after this object is + // destroyed. + base::WeakPtrFactory<BrowserPluginBindings> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BrowserPluginBindings); +}; + +} // namespace content + +#endif // CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_BINDINGS_H__ diff --git a/chromium/content/renderer/browser_plugin/browser_plugin_browsertest.cc b/chromium/content/renderer/browser_plugin/browser_plugin_browsertest.cc new file mode 100644 index 00000000000..693752c618a --- /dev/null +++ b/chromium/content/renderer/browser_plugin/browser_plugin_browsertest.cc @@ -0,0 +1,632 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/browser_plugin/browser_plugin_browsertest.h" + +#include "base/files/file_path.h" +#include "base/memory/singleton.h" +#include "base/path_service.h" +#include "base/pickle.h" +#include "content/public/common/content_constants.h" +#include "content/renderer/browser_plugin/browser_plugin.h" +#include "content/renderer/browser_plugin/browser_plugin_manager_factory.h" +#include "content/renderer/browser_plugin/mock_browser_plugin.h" +#include "content/renderer/browser_plugin/mock_browser_plugin_manager.h" +#include "content/renderer/render_thread_impl.h" +#include "content/renderer/renderer_webkitplatformsupport_impl.h" +#include "skia/ext/platform_canvas.h" +#include "third_party/WebKit/public/web/WebCursorInfo.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" +#include "third_party/WebKit/public/web/WebScriptSource.h" + +namespace { +const char kHTMLForBrowserPluginObject[] = + "<object id='browserplugin' width='640px' height='480px'" + " src='foo' type='%s'>"; + +const char kHTMLForBrowserPluginWithAllAttributes[] = + "<object id='browserplugin' width='640' height='480' type='%s'" + " autosize maxheight='600' maxwidth='800' minheight='240'" + " minwidth='320' name='Jim' partition='someid' src='foo'>"; + +const char kHTMLForSourcelessPluginObject[] = + "<object id='browserplugin' width='640px' height='480px' type='%s'>"; + +const char kHTMLForPartitionedPluginObject[] = + "<object id='browserplugin' width='640px' height='480px'" + " src='foo' type='%s' partition='someid'>"; + +const char kHTMLForInvalidPartitionedPluginObject[] = + "<object id='browserplugin' width='640px' height='480px'" + " type='%s' partition='persist:'>"; + +const char kHTMLForPartitionedPersistedPluginObject[] = + "<object id='browserplugin' width='640px' height='480px'" + " src='foo' type='%s' partition='persist:someid'>"; + +std::string GetHTMLForBrowserPluginObject() { + return base::StringPrintf(kHTMLForBrowserPluginObject, + content::kBrowserPluginMimeType); +} + +} // namespace + +namespace content { + +class TestContentRendererClient : public ContentRendererClient { + public: + TestContentRendererClient() : ContentRendererClient() { + } + virtual ~TestContentRendererClient() { + } + virtual bool AllowBrowserPlugin( + WebKit::WebPluginContainer* container) OVERRIDE { + // Allow BrowserPlugin for tests. + return true; + } +}; + +// Test factory for creating test instances of BrowserPluginManager. +class TestBrowserPluginManagerFactory : public BrowserPluginManagerFactory { + public: + virtual MockBrowserPluginManager* CreateBrowserPluginManager( + RenderViewImpl* render_view) OVERRIDE { + return new MockBrowserPluginManager(render_view); + } + + // Singleton getter. + static TestBrowserPluginManagerFactory* GetInstance() { + return Singleton<TestBrowserPluginManagerFactory>::get(); + } + + protected: + TestBrowserPluginManagerFactory() {} + virtual ~TestBrowserPluginManagerFactory() {} + + private: + // For Singleton. + friend struct DefaultSingletonTraits<TestBrowserPluginManagerFactory>; + + DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginManagerFactory); +}; + +BrowserPluginTest::BrowserPluginTest() {} + +BrowserPluginTest::~BrowserPluginTest() {} + +void BrowserPluginTest::SetUp() { + test_content_renderer_client_.reset(new TestContentRendererClient); + SetRendererClientForTesting(test_content_renderer_client_.get()); + BrowserPluginManager::set_factory_for_testing( + TestBrowserPluginManagerFactory::GetInstance()); + content::RenderViewTest::SetUp(); +} + +void BrowserPluginTest::TearDown() { + BrowserPluginManager::set_factory_for_testing( + TestBrowserPluginManagerFactory::GetInstance()); + content::RenderViewTest::TearDown(); + test_content_renderer_client_.reset(); +} + +std::string BrowserPluginTest::ExecuteScriptAndReturnString( + const std::string& script) { + v8::HandleScope handle_scope; + v8::Handle<v8::Value> value = GetMainFrame()->executeScriptAndReturnValue( + WebKit::WebScriptSource(WebKit::WebString::fromUTF8(script.c_str()))); + if (value.IsEmpty() || !value->IsString()) + return std::string(); + + v8::Local<v8::String> v8_str = value->ToString(); + int length = v8_str->Utf8Length() + 1; + scoped_ptr<char[]> str(new char[length]); + v8_str->WriteUtf8(str.get(), length); + return str.get(); +} + +int BrowserPluginTest::ExecuteScriptAndReturnInt( + const std::string& script) { + v8::HandleScope handle_scope; + v8::Handle<v8::Value> value = GetMainFrame()->executeScriptAndReturnValue( + WebKit::WebScriptSource(WebKit::WebString::fromUTF8(script.c_str()))); + if (value.IsEmpty() || !value->IsInt32()) + return 0; + + return value->Int32Value(); +} + +// A return value of false means that a value was not present. The return value +// of the script is stored in |result| +bool BrowserPluginTest::ExecuteScriptAndReturnBool( + const std::string& script, bool* result) { + v8::HandleScope handle_scope; + v8::Handle<v8::Value> value = GetMainFrame()->executeScriptAndReturnValue( + WebKit::WebScriptSource(WebKit::WebString::fromUTF8(script.c_str()))); + if (value.IsEmpty() || !value->IsBoolean()) + return false; + + *result = value->BooleanValue(); + return true; +} + +MockBrowserPlugin* BrowserPluginTest::GetCurrentPlugin() { + BrowserPluginHostMsg_Attach_Params params; + return GetCurrentPluginWithAttachParams(¶ms); +} + +MockBrowserPlugin* BrowserPluginTest::GetCurrentPluginWithAttachParams( + BrowserPluginHostMsg_Attach_Params* params) { + int instance_id = 0; + const IPC::Message* msg = + browser_plugin_manager()->sink().GetUniqueMessageMatching( + BrowserPluginHostMsg_Attach::ID); + if (!msg) + return NULL; + + PickleIterator iter(*msg); + if (!iter.ReadInt(&instance_id)) + return NULL; + + if (!IPC::ParamTraits<BrowserPluginHostMsg_Attach_Params>::Read( + msg, &iter, params)) + return NULL; + + MockBrowserPlugin* browser_plugin = static_cast<MockBrowserPlugin*>( + browser_plugin_manager()->GetBrowserPlugin(instance_id)); + + BrowserPluginMsg_Attach_ACK_Params attach_ack_params; + browser_plugin->OnAttachACK(instance_id, attach_ack_params); + + return browser_plugin; +} + +// This test verifies that an initial resize occurs when we instantiate the +// browser plugin. This test also verifies that the browser plugin is waiting +// for a BrowserPluginMsg_UpdateRect in response. We issue an UpdateRect, and +// we observe an UpdateRect_ACK, with the |pending_damage_buffer_| reset, +// indiciating that the BrowserPlugin is not waiting for any more UpdateRects to +// satisfy its resize request. +TEST_F(BrowserPluginTest, InitialResize) { + LoadHTML(GetHTMLForBrowserPluginObject().c_str()); + // Verify that the information in Attach is correct. + BrowserPluginHostMsg_Attach_Params params; + MockBrowserPlugin* browser_plugin = GetCurrentPluginWithAttachParams(¶ms); + + EXPECT_EQ(640, params.resize_guest_params.view_rect.width()); + EXPECT_EQ(480, params.resize_guest_params.view_rect.height()); + ASSERT_TRUE(browser_plugin); + // Now the browser plugin is expecting a UpdateRect resize. + int instance_id = browser_plugin->guest_instance_id(); + EXPECT_TRUE(browser_plugin->pending_damage_buffer_.get()); + + // Send the BrowserPlugin an UpdateRect equal to its container size with + // the same damage buffer. That should clear |pending_damage_buffer_|. + BrowserPluginMsg_UpdateRect_Params update_rect_params; + update_rect_params.damage_buffer_sequence_id = + browser_plugin->damage_buffer_sequence_id_; + update_rect_params.view_size = gfx::Size(640, 480); + update_rect_params.scale_factor = 1.0f; + update_rect_params.is_resize_ack = true; + update_rect_params.needs_ack = true; + BrowserPluginMsg_UpdateRect msg(instance_id, update_rect_params); + browser_plugin->OnMessageReceived(msg); + EXPECT_FALSE(browser_plugin->pending_damage_buffer_.get()); +} + +// This test verifies that all attributes (present at the time of writing) are +// parsed on initialization. However, this test does minimal checking of +// correct behavior. +TEST_F(BrowserPluginTest, ParseAllAttributes) { + std::string html = base::StringPrintf(kHTMLForBrowserPluginWithAllAttributes, + content::kBrowserPluginMimeType); + LoadHTML(html.c_str()); + bool result; + bool has_value = ExecuteScriptAndReturnBool( + "document.getElementById('browserplugin').autosize", &result); + EXPECT_TRUE(has_value); + EXPECT_TRUE(result); + int maxHeight = ExecuteScriptAndReturnInt( + "document.getElementById('browserplugin').maxheight"); + EXPECT_EQ(600, maxHeight); + int maxWidth = ExecuteScriptAndReturnInt( + "document.getElementById('browserplugin').maxwidth"); + EXPECT_EQ(800, maxWidth); + int minHeight = ExecuteScriptAndReturnInt( + "document.getElementById('browserplugin').minheight"); + EXPECT_EQ(240, minHeight); + int minWidth = ExecuteScriptAndReturnInt( + "document.getElementById('browserplugin').minwidth"); + EXPECT_EQ(320, minWidth); + std::string name = ExecuteScriptAndReturnString( + "document.getElementById('browserplugin').name"); + EXPECT_STREQ("Jim", name.c_str()); + std::string partition = ExecuteScriptAndReturnString( + "document.getElementById('browserplugin').partition"); + EXPECT_STREQ("someid", partition.c_str()); + std::string src = ExecuteScriptAndReturnString( + "document.getElementById('browserplugin').src"); + EXPECT_STREQ("foo", src.c_str()); +} + +// Verify that the src attribute on the browser plugin works as expected. +TEST_F(BrowserPluginTest, SrcAttribute) { + LoadHTML(GetHTMLForBrowserPluginObject().c_str()); + // Verify that we're reporting the correct URL to navigate to based on the + // src attribute. + { + BrowserPluginHostMsg_Attach_Params params; + MockBrowserPlugin* browser_plugin = + GetCurrentPluginWithAttachParams(¶ms); + ASSERT_TRUE(browser_plugin); + EXPECT_EQ("foo", params.src); + } + + browser_plugin_manager()->sink().ClearMessages(); + // Navigate to bar and observe the associated + // BrowserPluginHostMsg_NavigateGuest message. + // Verify that the src attribute is updated as well. + ExecuteJavaScript("document.getElementById('browserplugin').src = 'bar'"); + { + // Verify that we do not get a Attach on subsequent navigations. + const IPC::Message* create_msg = + browser_plugin_manager()->sink().GetUniqueMessageMatching( + BrowserPluginHostMsg_Attach::ID); + ASSERT_FALSE(create_msg); + + const IPC::Message* msg = + browser_plugin_manager()->sink().GetUniqueMessageMatching( + BrowserPluginHostMsg_NavigateGuest::ID); + ASSERT_TRUE(msg); + + int instance_id = 0; + std::string src; + BrowserPluginHostMsg_NavigateGuest::Read(msg, &instance_id, &src); + EXPECT_EQ("bar", src); + std::string src_value = + ExecuteScriptAndReturnString( + "document.getElementById('browserplugin').src"); + EXPECT_EQ("bar", src_value); + } +} + +TEST_F(BrowserPluginTest, ResizeFlowControl) { + LoadHTML(GetHTMLForBrowserPluginObject().c_str()); + MockBrowserPlugin* browser_plugin = GetCurrentPlugin(); + ASSERT_TRUE(browser_plugin); + int instance_id = browser_plugin->guest_instance_id(); + EXPECT_TRUE(browser_plugin->pending_damage_buffer_.get()); + // Send an UpdateRect to the BrowserPlugin to make it use the pending damage + // buffer. + { + // We send a stale UpdateRect to the BrowserPlugin. + BrowserPluginMsg_UpdateRect_Params update_rect_params; + update_rect_params.view_size = gfx::Size(640, 480); + update_rect_params.scale_factor = 1.0f; + update_rect_params.is_resize_ack = true; + update_rect_params.needs_ack = true; + // By sending |damage_buffer_sequence_id| back to BrowserPlugin on + // UpdateRect, then the BrowserPlugin knows that the browser process has + // received and has begun to use the |pending_damage_buffer_|. + update_rect_params.damage_buffer_sequence_id = + browser_plugin->damage_buffer_sequence_id_; + BrowserPluginMsg_UpdateRect msg(instance_id, update_rect_params); + browser_plugin->OnMessageReceived(msg); + EXPECT_EQ(NULL, browser_plugin->pending_damage_buffer_.get()); + } + + browser_plugin_manager()->sink().ClearMessages(); + + // Resize the browser plugin three times. + ExecuteJavaScript("document.getElementById('browserplugin').width = '641px'"); + ProcessPendingMessages(); + ExecuteJavaScript("document.getElementById('browserplugin').width = '642px'"); + ProcessPendingMessages(); + ExecuteJavaScript("document.getElementById('browserplugin').width = '643px'"); + ProcessPendingMessages(); + + // Expect to see one resize messsage in the sink. BrowserPlugin will not issue + // subsequent resize requests until the first request is satisfied by the + // guest. The rest of the messages could be + // BrowserPluginHostMsg_UpdateGeometry msgs. + EXPECT_LE(1u, browser_plugin_manager()->sink().message_count()); + for (size_t i = 0; i < browser_plugin_manager()->sink().message_count(); + ++i) { + const IPC::Message* msg = browser_plugin_manager()->sink().GetMessageAt(i); + if (msg->type() != BrowserPluginHostMsg_ResizeGuest::ID) + EXPECT_EQ(msg->type(), BrowserPluginHostMsg_UpdateGeometry::ID); + } + const IPC::Message* msg = + browser_plugin_manager()->sink().GetUniqueMessageMatching( + BrowserPluginHostMsg_ResizeGuest::ID); + ASSERT_TRUE(msg); + BrowserPluginHostMsg_ResizeGuest_Params params; + BrowserPluginHostMsg_ResizeGuest::Read(msg, &instance_id, ¶ms); + EXPECT_EQ(641, params.view_rect.width()); + EXPECT_EQ(480, params.view_rect.height()); + // This indicates that the BrowserPlugin has sent out a previous resize + // request but has not yet received an UpdateRect for that request. + EXPECT_TRUE(browser_plugin->pending_damage_buffer_.get()); + + { + // We send a stale UpdateRect to the BrowserPlugin. + BrowserPluginMsg_UpdateRect_Params update_rect_params; + update_rect_params.view_size = gfx::Size(641, 480); + update_rect_params.scale_factor = 1.0f; + update_rect_params.is_resize_ack = true; + update_rect_params.needs_ack = true; + update_rect_params.damage_buffer_sequence_id = + browser_plugin->damage_buffer_sequence_id_; + BrowserPluginMsg_UpdateRect msg(instance_id, update_rect_params); + browser_plugin->OnMessageReceived(msg); + // This tells us that the BrowserPlugin is still expecting another + // UpdateRect with the most recent size. + EXPECT_TRUE(browser_plugin->pending_damage_buffer_.get()); + } + // Send the BrowserPlugin another UpdateRect, but this time with a size + // that matches the size of the container. + { + BrowserPluginMsg_UpdateRect_Params update_rect_params; + update_rect_params.view_size = gfx::Size(643, 480); + update_rect_params.scale_factor = 1.0f; + update_rect_params.is_resize_ack = true; + update_rect_params.needs_ack = true; + update_rect_params.damage_buffer_sequence_id = + browser_plugin->damage_buffer_sequence_id_; + BrowserPluginMsg_UpdateRect msg(instance_id, update_rect_params); + browser_plugin->OnMessageReceived(msg); + // The BrowserPlugin has finally received an UpdateRect that satisifes + // its current size, and so it is happy. + EXPECT_FALSE(browser_plugin->pending_damage_buffer_.get()); + } +} + +TEST_F(BrowserPluginTest, RemovePlugin) { + LoadHTML(GetHTMLForBrowserPluginObject().c_str()); + EXPECT_FALSE(browser_plugin_manager()->sink().GetUniqueMessageMatching( + BrowserPluginHostMsg_PluginDestroyed::ID)); + ExecuteJavaScript("x = document.getElementById('browserplugin'); " + "x.parentNode.removeChild(x);"); + ProcessPendingMessages(); + EXPECT_TRUE(browser_plugin_manager()->sink().GetUniqueMessageMatching( + BrowserPluginHostMsg_PluginDestroyed::ID)); +} + +// This test verifies that PluginDestroyed messages do not get sent from a +// BrowserPlugin that has never navigated. +TEST_F(BrowserPluginTest, RemovePluginBeforeNavigation) { + std::string html = base::StringPrintf(kHTMLForSourcelessPluginObject, + content::kBrowserPluginMimeType); + LoadHTML(html.c_str()); + EXPECT_FALSE(browser_plugin_manager()->sink().GetUniqueMessageMatching( + BrowserPluginHostMsg_PluginDestroyed::ID)); + ExecuteJavaScript("x = document.getElementById('browserplugin'); " + "x.parentNode.removeChild(x);"); + ProcessPendingMessages(); + EXPECT_FALSE(browser_plugin_manager()->sink().GetUniqueMessageMatching( + BrowserPluginHostMsg_PluginDestroyed::ID)); +} + +// Verify that the 'partition' attribute on the browser plugin is parsed +// correctly. +TEST_F(BrowserPluginTest, PartitionAttribute) { + std::string html = base::StringPrintf(kHTMLForPartitionedPluginObject, + content::kBrowserPluginMimeType); + LoadHTML(html.c_str()); + std::string partition_value = ExecuteScriptAndReturnString( + "document.getElementById('browserplugin').partition"); + EXPECT_STREQ("someid", partition_value.c_str()); + + html = base::StringPrintf(kHTMLForPartitionedPersistedPluginObject, + content::kBrowserPluginMimeType); + LoadHTML(html.c_str()); + partition_value = ExecuteScriptAndReturnString( + "document.getElementById('browserplugin').partition"); + EXPECT_STREQ("persist:someid", partition_value.c_str()); + + // Verify that once HTML has defined a source and partition, we cannot change + // the partition anymore. + ExecuteJavaScript( + "try {" + " document.getElementById('browserplugin').partition = 'foo';" + " document.title = 'success';" + "} catch (e) { document.title = e.message; }"); + std::string title = ExecuteScriptAndReturnString("document.title"); + EXPECT_STREQ( + "The object has already navigated, so its partition cannot be changed.", + title.c_str()); + + // Load a browser tag without 'src' defined. + html = base::StringPrintf(kHTMLForSourcelessPluginObject, + content::kBrowserPluginMimeType); + LoadHTML(html.c_str()); + + // Ensure we don't parse just "persist:" string and return exception. + ExecuteJavaScript( + "try {" + " document.getElementById('browserplugin').partition = 'persist:';" + " document.title = 'success';" + "} catch (e) { document.title = e.message; }"); + title = ExecuteScriptAndReturnString("document.title"); + EXPECT_STREQ("Invalid partition attribute.", title.c_str()); +} + +// This test verifies that BrowserPlugin enters an error state when the +// partition attribute is invalid. +TEST_F(BrowserPluginTest, InvalidPartition) { + std::string html = base::StringPrintf(kHTMLForInvalidPartitionedPluginObject, + content::kBrowserPluginMimeType); + LoadHTML(html.c_str()); + // Attempt to navigate with an invalid partition. + { + ExecuteJavaScript( + "try {" + " document.getElementById('browserplugin').src = 'bar';" + " document.title = 'success';" + "} catch (e) { document.title = e.message; }"); + std::string title = ExecuteScriptAndReturnString("document.title"); + EXPECT_STREQ("Invalid partition attribute.", title.c_str()); + // Verify that the 'src' attribute has not been updated. + EXPECT_EQ("", ExecuteScriptAndReturnString( + "document.getElementById('browserplugin').src")); + } + + // Verify that the BrowserPlugin accepts changes to its src attribue after + // setting the partition to a valid value. + ExecuteJavaScript( + "document.getElementById('browserplugin').partition = 'persist:foo'"); + ExecuteJavaScript("document.getElementById('browserplugin').src = 'bar'"); + EXPECT_EQ("bar", ExecuteScriptAndReturnString( + "document.getElementById('browserplugin').src")); + ProcessPendingMessages(); + // Verify that the BrowserPlugin does not 'deadlock': it can recover from + // the partition ID error state. + { + ExecuteJavaScript( + "try {" + " document.getElementById('browserplugin').partition = 'persist:1337';" + " document.title = 'success';" + "} catch (e) { document.title = e.message; }"); + std::string title = ExecuteScriptAndReturnString("document.title"); + EXPECT_STREQ( + "The object has already navigated, so its partition cannot be changed.", + title.c_str()); + ExecuteJavaScript("document.getElementById('browserplugin').src = '42'"); + EXPECT_EQ("42", ExecuteScriptAndReturnString( + "document.getElementById('browserplugin').src")); + } +} + +// Test to verify that after the first navigation, the partition attribute +// cannot be modified. +TEST_F(BrowserPluginTest, ImmutableAttributesAfterNavigation) { + std::string html = base::StringPrintf(kHTMLForSourcelessPluginObject, + content::kBrowserPluginMimeType); + LoadHTML(html.c_str()); + + ExecuteJavaScript( + "document.getElementById('browserplugin').partition = 'storage'"); + std::string partition_value = ExecuteScriptAndReturnString( + "document.getElementById('browserplugin').partition"); + EXPECT_STREQ("storage", partition_value.c_str()); + + std::string src_value = ExecuteScriptAndReturnString( + "document.getElementById('browserplugin').src"); + EXPECT_STREQ("", src_value.c_str()); + + ExecuteJavaScript("document.getElementById('browserplugin').src = 'bar'"); + ProcessPendingMessages(); + { + BrowserPluginHostMsg_Attach_Params params; + MockBrowserPlugin* browser_plugin = + GetCurrentPluginWithAttachParams(¶ms); + ASSERT_TRUE(browser_plugin); + + EXPECT_STREQ("storage", params.storage_partition_id.c_str()); + EXPECT_FALSE(params.persist_storage); + EXPECT_STREQ("bar", params.src.c_str()); + } + + // Setting the partition should throw an exception and the value should not + // change. + ExecuteJavaScript( + "try {" + " document.getElementById('browserplugin').partition = 'someid';" + " document.title = 'success';" + "} catch (e) { document.title = e.message; }"); + + std::string title = ExecuteScriptAndReturnString("document.title"); + EXPECT_STREQ( + "The object has already navigated, so its partition cannot be changed.", + title.c_str()); + + partition_value = ExecuteScriptAndReturnString( + "document.getElementById('browserplugin').partition"); + EXPECT_STREQ("storage", partition_value.c_str()); +} + +TEST_F(BrowserPluginTest, AutoSizeAttributes) { + std::string html = base::StringPrintf(kHTMLForSourcelessPluginObject, + content::kBrowserPluginMimeType); + LoadHTML(html.c_str()); + const char* kSetAutoSizeParametersAndNavigate = + "var browserplugin = document.getElementById('browserplugin');" + "browserplugin.autosize = true;" + "browserplugin.minwidth = 42;" + "browserplugin.minheight = 43;" + "browserplugin.maxwidth = 1337;" + "browserplugin.maxheight = 1338;" + "browserplugin.src = 'foobar';"; + const char* kDisableAutoSize = + "document.getElementById('browserplugin').removeAttribute('autosize');"; + + int instance_id = 0; + // Set some autosize parameters before navigating then navigate. + // Verify that the BrowserPluginHostMsg_Attach message contains + // the correct autosize parameters. + ExecuteJavaScript(kSetAutoSizeParametersAndNavigate); + ProcessPendingMessages(); + + BrowserPluginHostMsg_Attach_Params params; + MockBrowserPlugin* browser_plugin = + GetCurrentPluginWithAttachParams(¶ms); + ASSERT_TRUE(browser_plugin); + + EXPECT_TRUE(params.auto_size_params.enable); + EXPECT_EQ(42, params.auto_size_params.min_size.width()); + EXPECT_EQ(43, params.auto_size_params.min_size.height()); + EXPECT_EQ(1337, params.auto_size_params.max_size.width()); + EXPECT_EQ(1338, params.auto_size_params.max_size.height()); + + // Verify that we are waiting for the browser process to grab the new + // damage buffer. + EXPECT_TRUE(browser_plugin->pending_damage_buffer_.get()); + // Disable autosize. AutoSize state will not be sent to the guest until + // the guest has responded to the last resize request. + ExecuteJavaScript(kDisableAutoSize); + ProcessPendingMessages(); + + const IPC::Message* auto_size_msg = + browser_plugin_manager()->sink().GetUniqueMessageMatching( + BrowserPluginHostMsg_SetAutoSize::ID); + EXPECT_FALSE(auto_size_msg); + + // Send the BrowserPlugin an UpdateRect equal to its |max_size| with + // the same damage buffer. + BrowserPluginMsg_UpdateRect_Params update_rect_params; + update_rect_params.damage_buffer_sequence_id = + browser_plugin->damage_buffer_sequence_id_; + update_rect_params.view_size = gfx::Size(1337, 1338); + update_rect_params.scale_factor = 1.0f; + update_rect_params.is_resize_ack = true; + update_rect_params.needs_ack = true; + BrowserPluginMsg_UpdateRect msg(instance_id, update_rect_params); + browser_plugin->OnMessageReceived(msg); + + // Verify that the autosize state has been updated. + { + const IPC::Message* auto_size_msg = + browser_plugin_manager()->sink().GetUniqueMessageMatching( + BrowserPluginHostMsg_UpdateRect_ACK::ID); + ASSERT_TRUE(auto_size_msg); + + int instance_id = 0; + bool needs_ack = false; + BrowserPluginHostMsg_AutoSize_Params auto_size_params; + BrowserPluginHostMsg_ResizeGuest_Params resize_params; + BrowserPluginHostMsg_UpdateRect_ACK::Read(auto_size_msg, + &instance_id, + &needs_ack, + &auto_size_params, + &resize_params); + EXPECT_FALSE(auto_size_params.enable); + // These value are not populated (as an optimization) if autosize is + // disabled. + EXPECT_EQ(0, auto_size_params.min_size.width()); + EXPECT_EQ(0, auto_size_params.min_size.height()); + EXPECT_EQ(0, auto_size_params.max_size.width()); + EXPECT_EQ(0, auto_size_params.max_size.height()); + } +} + +} // namespace content diff --git a/chromium/content/renderer/browser_plugin/browser_plugin_browsertest.h b/chromium/content/renderer/browser_plugin/browser_plugin_browsertest.h new file mode 100644 index 00000000000..28676901378 --- /dev/null +++ b/chromium/content/renderer/browser_plugin/browser_plugin_browsertest.h @@ -0,0 +1,53 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_BROWSERETEST_H_ +#define CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_BROWSERETEST_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "content/common/browser_plugin/browser_plugin_messages.h" +#include "content/public/renderer/content_renderer_client.h" +#include "content/public/test/render_view_test.h" +#include "content/renderer/browser_plugin/mock_browser_plugin_manager.h" +#include "content/renderer/render_view_impl.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/platform/WebSize.h" +#include "third_party/WebKit/public/web/WebView.h" + +class RenderThreadImpl; + +namespace content { + +class MockBrowserPlugin; +class TestContentRendererClient; + +class BrowserPluginTest : public RenderViewTest { + public: + BrowserPluginTest(); + virtual ~BrowserPluginTest(); + + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; + MockBrowserPluginManager* browser_plugin_manager() const { + return static_cast<MockBrowserPluginManager*>( + static_cast<RenderViewImpl*>(view_)->GetBrowserPluginManager()); + } + std::string ExecuteScriptAndReturnString(const std::string& script); + int ExecuteScriptAndReturnInt(const std::string& script); + bool ExecuteScriptAndReturnBool(const std::string& script, bool* result); + // Returns NULL if there is no plugin. + MockBrowserPlugin* GetCurrentPlugin(); + // Returns NULL if there is no plugin. + MockBrowserPlugin* GetCurrentPluginWithAttachParams( + BrowserPluginHostMsg_Attach_Params* params); + private: + scoped_ptr<TestContentRendererClient> test_content_renderer_client_; +}; + +} // namespace content + +#endif // CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_BROWSERETEST_H_ + diff --git a/chromium/content/renderer/browser_plugin/browser_plugin_compositing_helper.cc b/chromium/content/renderer/browser_plugin/browser_plugin_compositing_helper.cc new file mode 100644 index 00000000000..7d02f31aea9 --- /dev/null +++ b/chromium/content/renderer/browser_plugin/browser_plugin_compositing_helper.cc @@ -0,0 +1,356 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/browser_plugin/browser_plugin_compositing_helper.h" + +#include "cc/layers/delegated_renderer_layer.h" +#include "cc/layers/solid_color_layer.h" +#include "cc/layers/texture_layer.h" +#include "cc/output/context_provider.h" +#include "content/common/browser_plugin/browser_plugin_messages.h" +#include "content/common/gpu/client/context_provider_command_buffer.h" +#include "content/renderer/browser_plugin/browser_plugin_manager.h" +#include "content/renderer/render_thread_impl.h" +#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" +#include "third_party/WebKit/public/web/WebPluginContainer.h" +#include "third_party/khronos/GLES2/gl2.h" +#include "ui/gfx/size_conversions.h" +#include "webkit/renderer/compositor_bindings/web_layer_impl.h" + +namespace content { + +BrowserPluginCompositingHelper::SwapBuffersInfo::SwapBuffersInfo() + : route_id(0), + output_surface_id(0), + host_id(0), + software_frame_id(0), + shared_memory(NULL) { +} + +BrowserPluginCompositingHelper::BrowserPluginCompositingHelper( + WebKit::WebPluginContainer* container, + BrowserPluginManager* manager, + int instance_id, + int host_routing_id) + : instance_id_(instance_id), + host_routing_id_(host_routing_id), + last_route_id_(0), + last_output_surface_id_(0), + last_host_id_(0), + last_mailbox_valid_(false), + ack_pending_(true), + container_(container), + browser_plugin_manager_(manager) { +} + +BrowserPluginCompositingHelper::~BrowserPluginCompositingHelper() { +} + +void BrowserPluginCompositingHelper::DidCommitCompositorFrame() { + if (!delegated_layer_.get() || !ack_pending_) + return; + + cc::CompositorFrameAck ack; + delegated_layer_->TakeUnusedResourcesForChildCompositor(&ack.resources); + + browser_plugin_manager_->Send( + new BrowserPluginHostMsg_CompositorFrameACK( + host_routing_id_, + instance_id_, + last_route_id_, + last_output_surface_id_, + last_host_id_, + ack)); + + ack_pending_ = false; +} + +void BrowserPluginCompositingHelper::EnableCompositing(bool enable) { + if (enable && !background_layer_.get()) { + background_layer_ = cc::SolidColorLayer::Create(); + background_layer_->SetMasksToBounds(true); + background_layer_->SetBackgroundColor( + SkColorSetARGBInline(255, 255, 255, 255)); + web_layer_.reset(new webkit::WebLayerImpl(background_layer_)); + } + + container_->setWebLayer(enable ? web_layer_.get() : NULL); +} + +void BrowserPluginCompositingHelper::CheckSizeAndAdjustLayerBounds( + const gfx::Size& new_size, + float device_scale_factor, + cc::Layer* layer) { + if (buffer_size_ != new_size) { + buffer_size_ = new_size; + // The container size is in DIP, so is the layer size. + // Buffer size is in physical pixels, so we need to adjust + // it by the device scale factor. + gfx::Size device_scale_adjusted_size = gfx::ToFlooredSize( + gfx::ScaleSize(buffer_size_, 1.0f / device_scale_factor)); + layer->SetBounds(device_scale_adjusted_size); + } +} + +void BrowserPluginCompositingHelper::MailboxReleased( + SwapBuffersInfo mailbox, + unsigned sync_point, + bool lost_resource) { + if (mailbox.type == SOFTWARE_COMPOSITOR_FRAME) { + delete mailbox.shared_memory; + mailbox.shared_memory = NULL; + } else if (lost_resource) { + // Reset mailbox's name if the resource was lost. + mailbox.name.SetZero(); + } + + // This means the GPU process crashed or guest crashed. + if (last_host_id_ != mailbox.host_id || + last_output_surface_id_ != mailbox.output_surface_id || + last_route_id_ != mailbox.route_id) + return; + + // We need to send an ACK to for every buffer sent to us. + // However, if a buffer is freed up from + // the compositor in cases like switching back to SW mode without a new + // buffer arriving, no ACK is needed. + if (!ack_pending_) { + last_mailbox_valid_ = false; + return; + } + ack_pending_ = false; + switch (mailbox.type) { + case TEXTURE_IMAGE_TRANSPORT: { + std::string mailbox_name(reinterpret_cast<const char*>(mailbox.name.name), + sizeof(mailbox.name.name)); + browser_plugin_manager_->Send( + new BrowserPluginHostMsg_BuffersSwappedACK( + host_routing_id_, + instance_id_, + mailbox.route_id, + mailbox.host_id, + mailbox_name, + sync_point)); + break; + } + case GL_COMPOSITOR_FRAME: { + cc::CompositorFrameAck ack; + ack.gl_frame_data.reset(new cc::GLFrameData()); + ack.gl_frame_data->mailbox = mailbox.name; + ack.gl_frame_data->size = mailbox.size; + ack.gl_frame_data->sync_point = sync_point; + + browser_plugin_manager_->Send( + new BrowserPluginHostMsg_CompositorFrameACK( + host_routing_id_, + instance_id_, + mailbox.route_id, + mailbox.output_surface_id, + mailbox.host_id, + ack)); + break; + } + case SOFTWARE_COMPOSITOR_FRAME: { + cc::CompositorFrameAck ack; + ack.last_software_frame_id = mailbox.software_frame_id; + + browser_plugin_manager_->Send( + new BrowserPluginHostMsg_CompositorFrameACK( + host_routing_id_, + instance_id_, + mailbox.route_id, + mailbox.output_surface_id, + mailbox.host_id, + ack)); + break; + } + } +} + +void BrowserPluginCompositingHelper::OnContainerDestroy() { + if (container_) + container_->setWebLayer(NULL); + container_ = NULL; + + texture_layer_ = NULL; + delegated_layer_ = NULL; + background_layer_ = NULL; + web_layer_.reset(); +} + +void BrowserPluginCompositingHelper::OnBuffersSwappedPrivate( + const SwapBuffersInfo& mailbox, + unsigned sync_point, + float device_scale_factor) { + DCHECK(!delegated_layer_.get()); + // If these mismatch, we are either just starting up, GPU process crashed or + // guest renderer crashed. + // In this case, we are communicating with a new image transport + // surface and must ACK with the new ID's and an empty mailbox. + if (last_route_id_ != mailbox.route_id || + last_output_surface_id_ != mailbox.output_surface_id || + last_host_id_ != mailbox.host_id) + last_mailbox_valid_ = false; + + last_route_id_ = mailbox.route_id; + last_output_surface_id_ = mailbox.output_surface_id; + last_host_id_ = mailbox.host_id; + + ack_pending_ = true; + // Browser plugin getting destroyed, do a fast ACK. + if (!background_layer_.get()) { + MailboxReleased(mailbox, sync_point, false); + return; + } + + if (!texture_layer_.get()) { + texture_layer_ = cc::TextureLayer::CreateForMailbox(NULL); + texture_layer_->SetIsDrawable(true); + texture_layer_->SetContentsOpaque(true); + + background_layer_->AddChild(texture_layer_); + } + + // The size of browser plugin container is not always equal to the size + // of the buffer that arrives here. This could be for a number of reasons, + // including autosize and a resize in progress. + // During resize, the container size changes first and then some time + // later, a new buffer with updated size will arrive. During this process, + // we need to make sure that things are still displayed pixel perfect. + // We accomplish this by modifying bounds of the texture layer only + // when a new buffer arrives. + // Visually, this will either display a smaller part of the buffer + // or introduce a gutter around it. + CheckSizeAndAdjustLayerBounds(mailbox.size, + device_scale_factor, + texture_layer_.get()); + + bool is_software_frame = mailbox.type == SOFTWARE_COMPOSITOR_FRAME; + bool current_mailbox_valid = is_software_frame ? + mailbox.shared_memory != NULL : !mailbox.name.IsZero(); + if (!is_software_frame && !last_mailbox_valid_) { + SwapBuffersInfo empty_info = mailbox; + empty_info.name.SetZero(); + MailboxReleased(empty_info, 0, false); + if (!current_mailbox_valid) + return; + } + + cc::TextureMailbox texture_mailbox; + if (current_mailbox_valid) { + cc::TextureMailbox::ReleaseCallback callback = + base::Bind(&BrowserPluginCompositingHelper::MailboxReleased, + scoped_refptr<BrowserPluginCompositingHelper>(this), + mailbox); + if (is_software_frame) { + texture_mailbox = cc::TextureMailbox(mailbox.shared_memory, + mailbox.size, callback); + } else { + texture_mailbox = cc::TextureMailbox(mailbox.name, callback, sync_point); + } + } + + texture_layer_->SetFlipped(!is_software_frame); + texture_layer_->SetTextureMailbox(texture_mailbox); + texture_layer_->SetNeedsDisplay(); + last_mailbox_valid_ = current_mailbox_valid; +} + +void BrowserPluginCompositingHelper::OnBuffersSwapped( + const gfx::Size& size, + const std::string& mailbox_name, + int gpu_route_id, + int gpu_host_id, + float device_scale_factor) { + SwapBuffersInfo swap_info; + swap_info.name.SetName(reinterpret_cast<const int8*>(mailbox_name.data())); + swap_info.type = TEXTURE_IMAGE_TRANSPORT; + swap_info.size = size; + swap_info.route_id = gpu_route_id; + swap_info.output_surface_id = 0; + swap_info.host_id = gpu_host_id; + OnBuffersSwappedPrivate(swap_info, 0, device_scale_factor); +} + +void BrowserPluginCompositingHelper::OnCompositorFrameSwapped( + scoped_ptr<cc::CompositorFrame> frame, + int route_id, + uint32 output_surface_id, + int host_id) { + if (frame->gl_frame_data) { + SwapBuffersInfo swap_info; + swap_info.name = frame->gl_frame_data->mailbox; + swap_info.type = GL_COMPOSITOR_FRAME; + swap_info.size = frame->gl_frame_data->size; + swap_info.route_id = route_id; + swap_info.output_surface_id = output_surface_id; + swap_info.host_id = host_id; + OnBuffersSwappedPrivate(swap_info, + frame->gl_frame_data->sync_point, + frame->metadata.device_scale_factor); + return; + } + + if (frame->software_frame_data) { + cc::SoftwareFrameData* frame_data = frame->software_frame_data.get(); + + SwapBuffersInfo swap_info; + swap_info.type = SOFTWARE_COMPOSITOR_FRAME; + swap_info.size = frame_data->size; + swap_info.route_id = route_id; + swap_info.output_surface_id = output_surface_id; + swap_info.host_id = host_id; + swap_info.software_frame_id = frame_data->id; + + scoped_ptr<base::SharedMemory> shared_memory( + new base::SharedMemory(frame_data->handle, true)); + const size_t size_in_bytes = 4 * frame_data->size.GetArea(); + if (!shared_memory->Map(size_in_bytes)) { + LOG(ERROR) << "Failed to map shared memory of size " + << size_in_bytes; + // Send ACK right away. + ack_pending_ = true; + MailboxReleased(swap_info, 0, false); + return; + } + + swap_info.shared_memory = shared_memory.release(); + OnBuffersSwappedPrivate(swap_info, 0, + frame->metadata.device_scale_factor); + return; + } + + DCHECK(!texture_layer_.get()); + if (!delegated_layer_.get()) { + delegated_layer_ = cc::DelegatedRendererLayer::Create(NULL); + delegated_layer_->SetIsDrawable(true); + delegated_layer_->SetContentsOpaque(true); + + background_layer_->AddChild(delegated_layer_); + } + + cc::DelegatedFrameData *frame_data = frame->delegated_frame_data.get(); + if (!frame_data) + return; + + CheckSizeAndAdjustLayerBounds( + frame_data->render_pass_list.back()->output_rect.size(), + frame->metadata.device_scale_factor, + delegated_layer_.get()); + + delegated_layer_->SetFrameData(frame->delegated_frame_data.Pass()); + last_route_id_ = route_id; + last_output_surface_id_ = output_surface_id; + last_host_id_ = host_id; + ack_pending_ = true; +} + +void BrowserPluginCompositingHelper::UpdateVisibility(bool visible) { + if (texture_layer_.get()) + texture_layer_->SetIsDrawable(visible); + if (delegated_layer_.get()) + delegated_layer_->SetIsDrawable(visible); +} + +} // namespace content diff --git a/chromium/content/renderer/browser_plugin/browser_plugin_compositing_helper.h b/chromium/content/renderer/browser_plugin/browser_plugin_compositing_helper.h new file mode 100644 index 00000000000..268a9b46259 --- /dev/null +++ b/chromium/content/renderer/browser_plugin/browser_plugin_compositing_helper.h @@ -0,0 +1,109 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_COMPOSITING_HELPER_H_ +#define CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_COMPOSITING_HELPER_H_ + +#include <string> + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "content/common/content_export.h" +#include "gpu/command_buffer/common/mailbox.h" +#include "ui/gfx/size.h" + +namespace base { +class SharedMemory; +} + +namespace cc { +class CompositorFrame; +class Layer; +class SolidColorLayer; +class TextureLayer; +class DelegatedRendererLayer; +} + +namespace WebKit { +class WebPluginContainer; +class WebLayer; +} + +namespace content { + +class BrowserPluginManager; + +class CONTENT_EXPORT BrowserPluginCompositingHelper : + public base::RefCounted<BrowserPluginCompositingHelper> { + public: + BrowserPluginCompositingHelper(WebKit::WebPluginContainer* container, + BrowserPluginManager* manager, + int instance_id, + int host_routing_id); + void DidCommitCompositorFrame(); + void EnableCompositing(bool); + void OnContainerDestroy(); + void OnBuffersSwapped(const gfx::Size& size, + const std::string& mailbox_name, + int gpu_route_id, + int gpu_host_id, + float device_scale_factor); + void OnCompositorFrameSwapped(scoped_ptr<cc::CompositorFrame> frame, + int route_id, + uint32 output_surface_id, + int host_id); + void UpdateVisibility(bool); + protected: + // Friend RefCounted so that the dtor can be non-public. + friend class base::RefCounted<BrowserPluginCompositingHelper>; + private: + enum SwapBuffersType { + TEXTURE_IMAGE_TRANSPORT, + GL_COMPOSITOR_FRAME, + SOFTWARE_COMPOSITOR_FRAME, + }; + struct SwapBuffersInfo { + SwapBuffersInfo(); + + gpu::Mailbox name; + SwapBuffersType type; + gfx::Size size; + int route_id; + uint32 output_surface_id; + int host_id; + unsigned software_frame_id; + base::SharedMemory* shared_memory; + }; + ~BrowserPluginCompositingHelper(); + void CheckSizeAndAdjustLayerBounds(const gfx::Size& new_size, + float device_scale_factor, + cc::Layer* layer); + void OnBuffersSwappedPrivate(const SwapBuffersInfo& mailbox, + unsigned sync_point, + float device_scale_factor); + void MailboxReleased(SwapBuffersInfo mailbox, + unsigned sync_point, + bool lost_resource); + int instance_id_; + int host_routing_id_; + int last_route_id_; + uint32 last_output_surface_id_; + int last_host_id_; + bool last_mailbox_valid_; + bool ack_pending_; + + gfx::Size buffer_size_; + + scoped_refptr<cc::SolidColorLayer> background_layer_; + scoped_refptr<cc::TextureLayer> texture_layer_; + scoped_refptr<cc::DelegatedRendererLayer> delegated_layer_; + scoped_ptr<WebKit::WebLayer> web_layer_; + WebKit::WebPluginContainer* container_; + + scoped_refptr<BrowserPluginManager> browser_plugin_manager_; +}; + +} // namespace content + +#endif // CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_COMPOSITING_HELPER_H_ diff --git a/chromium/content/renderer/browser_plugin/browser_plugin_manager.cc b/chromium/content/renderer/browser_plugin/browser_plugin_manager.cc new file mode 100644 index 00000000000..03120d82f78 --- /dev/null +++ b/chromium/content/renderer/browser_plugin/browser_plugin_manager.cc @@ -0,0 +1,65 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/browser_plugin/browser_plugin_manager.h" + +#include "base/lazy_instance.h" +#include "base/threading/thread_local.h" +#include "content/public/renderer/render_thread.h" +#include "content/renderer/browser_plugin/browser_plugin.h" +#include "content/renderer/browser_plugin/browser_plugin_manager_factory.h" +#include "content/renderer/browser_plugin/browser_plugin_manager_impl.h" + +namespace content { + +// static +BrowserPluginManagerFactory* BrowserPluginManager::factory_ = NULL; + +BrowserPluginManager* BrowserPluginManager::Create( + RenderViewImpl* render_view) { + if (factory_) + return factory_->CreateBrowserPluginManager(render_view); + return new BrowserPluginManagerImpl(render_view); +} + +BrowserPluginManager::BrowserPluginManager(RenderViewImpl* render_view) + : RenderViewObserver(render_view), + render_view_(render_view->AsWeakPtr()) { +} + +BrowserPluginManager::~BrowserPluginManager() { +} + +void BrowserPluginManager::AddBrowserPlugin( + int guest_instance_id, + BrowserPlugin* browser_plugin) { + instances_.AddWithID(browser_plugin, guest_instance_id); +} + +void BrowserPluginManager::RemoveBrowserPlugin(int guest_instance_id) { + instances_.Remove(guest_instance_id); +} + +BrowserPlugin* BrowserPluginManager::GetBrowserPlugin( + int guest_instance_id) const { + return instances_.Lookup(guest_instance_id); +} + +void BrowserPluginManager::UpdateDeviceScaleFactor(float device_scale_factor) { + IDMap<BrowserPlugin>::iterator iter(&instances_); + while (!iter.IsAtEnd()) { + iter.GetCurrentValue()->UpdateDeviceScaleFactor(device_scale_factor); + iter.Advance(); + } +} + +void BrowserPluginManager::UpdateFocusState() { + IDMap<BrowserPlugin>::iterator iter(&instances_); + while (!iter.IsAtEnd()) { + iter.GetCurrentValue()->UpdateGuestFocusState(); + iter.Advance(); + } +} + +} // namespace content diff --git a/chromium/content/renderer/browser_plugin/browser_plugin_manager.h b/chromium/content/renderer/browser_plugin/browser_plugin_manager.h new file mode 100644 index 00000000000..54c4bbdbacb --- /dev/null +++ b/chromium/content/renderer/browser_plugin/browser_plugin_manager.h @@ -0,0 +1,88 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_MANAGER_H_ +#define CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_MANAGER_H_ + +#include "base/id_map.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/observer_list.h" +#include "content/public/renderer/render_view_observer.h" +#include "ipc/ipc_sender.h" + +namespace WebKit { +class WebFrame; +struct WebPluginParams; +} + +namespace content { + +class BrowserPlugin; +class BrowserPluginManagerFactory; +class RenderViewImpl; + +// BrowserPluginManager manages the routing of messages to the appropriate +// BrowserPlugin object based on its instance ID. +class CONTENT_EXPORT BrowserPluginManager + : public RenderViewObserver, + public base::RefCounted<BrowserPluginManager> { + public: + // Returns the one BrowserPluginManager for this process. + static BrowserPluginManager* Create(RenderViewImpl* render_view); + + // Overrides factory for testing. Default (NULL) value indicates regular + // (non-test) environment. + static void set_factory_for_testing(BrowserPluginManagerFactory* factory) { + BrowserPluginManager::factory_ = factory; + } + + explicit BrowserPluginManager(RenderViewImpl* render_view); + + // Creates a new BrowserPlugin object with a unique identifier. + // BrowserPlugin is responsible for associating itself with the + // BrowserPluginManager via AddBrowserPlugin. When it is destroyed, it is + // responsible for removing its association via RemoveBrowserPlugin. + virtual BrowserPlugin* CreateBrowserPlugin( + RenderViewImpl* render_view, + WebKit::WebFrame* frame, + const WebKit::WebPluginParams& params) = 0; + virtual void AllocateInstanceID(BrowserPlugin* browser_plugin) = 0; + + void AddBrowserPlugin(int guest_instance_id, BrowserPlugin* browser_plugin); + void RemoveBrowserPlugin(int guest_instance_id); + BrowserPlugin* GetBrowserPlugin(int guest_instance_id) const; + void UpdateDeviceScaleFactor(float device_scale_factor); + void UpdateFocusState(); + RenderViewImpl* render_view() const { return render_view_.get(); } + + // RenderViewObserver implementation. + + // BrowserPluginManager must override the default Send behavior. + virtual bool Send(IPC::Message* msg) OVERRIDE = 0; + + // Don't destroy the BrowserPluginManager when the RenderViewImpl goes away. + // BrowserPluginManager's lifetime is managed by a reference count. Once + // the host RenderViewImpl and all BrowserPlugins release their references, + // then the BrowserPluginManager will be destroyed. + virtual void OnDestruct() OVERRIDE {} + + protected: + // Friend RefCounted so that the dtor can be non-public. + friend class base::RefCounted<BrowserPluginManager>; + + // Static factory instance (always NULL for non-test). + static BrowserPluginManagerFactory* factory_; + + virtual ~BrowserPluginManager(); + // This map is keyed by guest instance IDs. + IDMap<BrowserPlugin> instances_; + base::WeakPtr<RenderViewImpl> render_view_; + + DISALLOW_COPY_AND_ASSIGN(BrowserPluginManager); +}; + +} // namespace content + +#endif // CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_MANAGER_H_ diff --git a/chromium/content/renderer/browser_plugin/browser_plugin_manager_factory.h b/chromium/content/renderer/browser_plugin/browser_plugin_manager_factory.h new file mode 100644 index 00000000000..3178fededbd --- /dev/null +++ b/chromium/content/renderer/browser_plugin/browser_plugin_manager_factory.h @@ -0,0 +1,21 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_MANAGER_FACTORY_H_ +#define CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_MANAGER_FACTORY_H_ + +namespace content { + +class CONTENT_EXPORT BrowserPluginManagerFactory { + public: + virtual BrowserPluginManager* CreateBrowserPluginManager( + RenderViewImpl* render_view) = 0; + + protected: + virtual ~BrowserPluginManagerFactory() {} +}; + +} // namespace content + +#endif // CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_MANAGER_FACTORY_H_ diff --git a/chromium/content/renderer/browser_plugin/browser_plugin_manager_impl.cc b/chromium/content/renderer/browser_plugin/browser_plugin_manager_impl.cc new file mode 100644 index 00000000000..2f7cdac4620 --- /dev/null +++ b/chromium/content/renderer/browser_plugin/browser_plugin_manager_impl.cc @@ -0,0 +1,111 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/browser_plugin/browser_plugin_manager_impl.h" + +#include "content/common/browser_plugin/browser_plugin_constants.h" +#include "content/common/browser_plugin/browser_plugin_messages.h" +#include "content/renderer/browser_plugin/browser_plugin.h" +#include "content/renderer/render_thread_impl.h" +#include "ui/gfx/point.h" +#include "webkit/common/cursors/webcursor.h" + +namespace content { + +BrowserPluginManagerImpl::BrowserPluginManagerImpl( + RenderViewImpl* render_view) + : BrowserPluginManager(render_view), + request_id_counter_(0) { +} + +BrowserPluginManagerImpl::~BrowserPluginManagerImpl() { +} + +BrowserPlugin* BrowserPluginManagerImpl::CreateBrowserPlugin( + RenderViewImpl* render_view, + WebKit::WebFrame* frame, + const WebKit::WebPluginParams& params) { + return new BrowserPlugin(render_view, frame, params); +} + +void BrowserPluginManagerImpl::AllocateInstanceID( + BrowserPlugin* browser_plugin) { + int request_id = ++request_id_counter_; + pending_allocate_guest_instance_id_requests_.AddWithID(browser_plugin, + request_id); + Send(new BrowserPluginHostMsg_AllocateInstanceID( + browser_plugin->render_view_routing_id(), request_id)); +} + +bool BrowserPluginManagerImpl::Send(IPC::Message* msg) { + return RenderThread::Get()->Send(msg); +} + +bool BrowserPluginManagerImpl::OnMessageReceived( + const IPC::Message& message) { + if (BrowserPlugin::ShouldForwardToBrowserPlugin(message)) { + int guest_instance_id = browser_plugin::kInstanceIDNone; + // All allowed messages must have |guest_instance_id| as their first + // parameter. + PickleIterator iter(message); + bool success = iter.ReadInt(&guest_instance_id); + DCHECK(success); + BrowserPlugin* plugin = GetBrowserPlugin(guest_instance_id); + if (plugin && plugin->OnMessageReceived(message)) + return true; + } + + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(BrowserPluginManagerImpl, message) + IPC_MESSAGE_HANDLER(BrowserPluginMsg_AllocateInstanceID_ACK, + OnAllocateInstanceIDACK) + IPC_MESSAGE_HANDLER(BrowserPluginMsg_PluginAtPositionRequest, + OnPluginAtPositionRequest); + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void BrowserPluginManagerImpl::DidCommitCompositorFrame() { + IDMap<BrowserPlugin>::iterator iter(&instances_); + while (!iter.IsAtEnd()) { + iter.GetCurrentValue()->DidCommitCompositorFrame(); + iter.Advance(); + } +} + +void BrowserPluginManagerImpl::OnAllocateInstanceIDACK( + const IPC::Message& message, + int request_id, + int guest_instance_id) { + BrowserPlugin* plugin = + pending_allocate_guest_instance_id_requests_.Lookup(request_id); + if (!plugin) + return; + pending_allocate_guest_instance_id_requests_.Remove(request_id); + plugin->OnInstanceIDAllocated(guest_instance_id); +} + +void BrowserPluginManagerImpl::OnPluginAtPositionRequest( + const IPC::Message& message, + int request_id, + const gfx::Point& position) { + int guest_instance_id = browser_plugin::kInstanceIDNone; + IDMap<BrowserPlugin>::iterator it(&instances_); + gfx::Point local_position = position; + while (!it.IsAtEnd()) { + const BrowserPlugin* plugin = it.GetCurrentValue(); + if (!plugin->guest_crashed() && plugin->InBounds(position)) { + guest_instance_id = plugin->guest_instance_id(); + local_position = plugin->ToLocalCoordinates(position); + break; + } + it.Advance(); + } + + Send(new BrowserPluginHostMsg_PluginAtPositionResponse( + message.routing_id(), guest_instance_id, request_id, local_position)); +} + +} // namespace content diff --git a/chromium/content/renderer/browser_plugin/browser_plugin_manager_impl.h b/chromium/content/renderer/browser_plugin/browser_plugin_manager_impl.h new file mode 100644 index 00000000000..9045d9d1d6b --- /dev/null +++ b/chromium/content/renderer/browser_plugin/browser_plugin_manager_impl.h @@ -0,0 +1,56 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_MANAGER_IMPL_H_ +#define CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_MANAGER_IMPL_H_ + +#include "content/renderer/browser_plugin/browser_plugin_manager.h" +#include "ui/gfx/size.h" + +struct BrowserPluginMsg_UpdateRect_Params; +class WebCursor; + +namespace gfx { +class Point; +} + +namespace content { + +class BrowserPluginManagerImpl : public BrowserPluginManager { + public: + explicit BrowserPluginManagerImpl(RenderViewImpl* render_view); + + // BrowserPluginManager implementation. + virtual BrowserPlugin* CreateBrowserPlugin( + RenderViewImpl* render_view, + WebKit::WebFrame* frame, + const WebKit::WebPluginParams& params) OVERRIDE; + virtual void AllocateInstanceID(BrowserPlugin* browser_plugin) OVERRIDE; + + // IPC::Sender implementation. + virtual bool Send(IPC::Message* msg) OVERRIDE; + + // RenderViewObserver override. Call on render thread. + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + virtual void DidCommitCompositorFrame() OVERRIDE; + + private: + virtual ~BrowserPluginManagerImpl(); + + void OnAllocateInstanceIDACK(const IPC::Message& message, + int request_id, + int guest_instance_id); + void OnPluginAtPositionRequest(const IPC::Message& message, + int request_id, + const gfx::Point& position); + + int request_id_counter_; + IDMap<BrowserPlugin> pending_allocate_guest_instance_id_requests_; + + DISALLOW_COPY_AND_ASSIGN(BrowserPluginManagerImpl); +}; + +} // namespace content + +#endif // CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_MANAGER_IMPL_H_ diff --git a/chromium/content/renderer/browser_plugin/mock_browser_plugin.cc b/chromium/content/renderer/browser_plugin/mock_browser_plugin.cc new file mode 100644 index 00000000000..39b2d2dc047 --- /dev/null +++ b/chromium/content/renderer/browser_plugin/mock_browser_plugin.cc @@ -0,0 +1,18 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/browser_plugin/mock_browser_plugin.h" +#include "content/renderer/render_process_impl.h" + +namespace content { + +MockBrowserPlugin::MockBrowserPlugin(RenderViewImpl* render_view, + WebKit::WebFrame* frame, + const WebKit::WebPluginParams& params) + : BrowserPlugin(render_view, frame, params) { +} + +MockBrowserPlugin::~MockBrowserPlugin() {} + +} // namespace content diff --git a/chromium/content/renderer/browser_plugin/mock_browser_plugin.h b/chromium/content/renderer/browser_plugin/mock_browser_plugin.h new file mode 100644 index 00000000000..acd001b64ca --- /dev/null +++ b/chromium/content/renderer/browser_plugin/mock_browser_plugin.h @@ -0,0 +1,29 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_BROWSER_PLUGIN_MOCK_BROWSER_PLUGIN_H_ +#define CONTENT_RENDERER_BROWSER_PLUGIN_MOCK_BROWSER_PLUGIN_H_ + +#include "content/renderer/browser_plugin/browser_plugin.h" + +namespace content { + +class MockBrowserPlugin : public BrowserPlugin { + public: + MockBrowserPlugin(RenderViewImpl* render_view, + WebKit::WebFrame* frame, + const WebKit::WebPluginParams& params); + + virtual ~MockBrowserPlugin(); + + // Allow poking at a few private members. + using BrowserPlugin::OnAttachACK; + using BrowserPlugin::guest_crashed_; + using BrowserPlugin::pending_damage_buffer_; + using BrowserPlugin::damage_buffer_sequence_id_; +}; + +} // namespace content + +#endif // CONTENT_RENDERER_BROWSER_PLUGIN_MOCK_BROWSER_PLUGIN_H_ diff --git a/chromium/content/renderer/browser_plugin/mock_browser_plugin_manager.cc b/chromium/content/renderer/browser_plugin/mock_browser_plugin_manager.cc new file mode 100644 index 00000000000..c3b8fd8e72a --- /dev/null +++ b/chromium/content/renderer/browser_plugin/mock_browser_plugin_manager.cc @@ -0,0 +1,79 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/browser_plugin/mock_browser_plugin_manager.h" + +#include "base/message_loop/message_loop.h" +#include "content/common/browser_plugin/browser_plugin_messages.h" +#include "content/renderer/browser_plugin/mock_browser_plugin.h" +#include "ipc/ipc_message.h" + +namespace content { + +MockBrowserPluginManager::MockBrowserPluginManager( + RenderViewImpl* render_view) + : BrowserPluginManager(render_view), + guest_instance_id_counter_(0) { +} + +MockBrowserPluginManager::~MockBrowserPluginManager() { +} + +BrowserPlugin* MockBrowserPluginManager::CreateBrowserPlugin( + RenderViewImpl* render_view, + WebKit::WebFrame* frame, + const WebKit::WebPluginParams& params) { + return new MockBrowserPlugin(render_view, frame, params); +} + +void MockBrowserPluginManager::AllocateInstanceID( + BrowserPlugin* browser_plugin) { + int guest_instance_id = ++guest_instance_id_counter_; + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&MockBrowserPluginManager::AllocateInstanceIDACK, + this, + base::Unretained(browser_plugin), + guest_instance_id)); +} + +void MockBrowserPluginManager::AllocateInstanceIDACK( + BrowserPlugin* browser_plugin, + int guest_instance_id) { + browser_plugin->OnInstanceIDAllocated(guest_instance_id); + scoped_ptr<base::DictionaryValue> extra_params(new base::DictionaryValue()); + browser_plugin->Attach(extra_params.Pass()); +} + +bool MockBrowserPluginManager::Send(IPC::Message* msg) { + // This is a copy-and-paste from MockRenderThread::Send. + // We need to simulate a synchronous channel, thus we are going to receive + // through this function messages, messages with reply and reply messages. + // We can only handle one synchronous message at a time. + if (msg->is_reply()) { + if (reply_deserializer_) { + reply_deserializer_->SerializeOutputParameters(*msg); + reply_deserializer_.reset(); + } + } else { + if (msg->is_sync()) { + // We actually need to handle deleting the reply deserializer for sync + // messages. + reply_deserializer_.reset( + static_cast<IPC::SyncMessage*>(msg)->GetReplyDeserializer()); + } + OnMessageReceived(*msg); + } + delete msg; + return true; +} + +bool MockBrowserPluginManager::OnMessageReceived( + const IPC::Message& message) { + // Save the message in the sink. + sink_.OnMessageReceived(message); + return false; +} + +} // namespace content diff --git a/chromium/content/renderer/browser_plugin/mock_browser_plugin_manager.h b/chromium/content/renderer/browser_plugin/mock_browser_plugin_manager.h new file mode 100644 index 00000000000..eda85f15f9b --- /dev/null +++ b/chromium/content/renderer/browser_plugin/mock_browser_plugin_manager.h @@ -0,0 +1,50 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_BROWSER_PLUGIN_MOCK_BROWSER_PLUGIN_MANAGER_H_ +#define CONTENT_RENDERER_BROWSER_PLUGIN_MOCK_BROWSER_PLUGIN_MANAGER_H_ + +#include "content/renderer/browser_plugin/browser_plugin_manager.h" + +#include "base/memory/scoped_ptr.h" +#include "ipc/ipc_message_utils.h" +#include "ipc/ipc_test_sink.h" + +namespace content { + +class MockBrowserPluginManager : public BrowserPluginManager { + public: + MockBrowserPluginManager(RenderViewImpl* render_view); + + // BrowserPluginManager implementation. + virtual BrowserPlugin* CreateBrowserPlugin( + RenderViewImpl* render_view, + WebKit::WebFrame* frame, + const WebKit::WebPluginParams& params) OVERRIDE; + virtual void AllocateInstanceID(BrowserPlugin* browser_plugin) OVERRIDE; + + // Provides access to the messages that have been received by this thread. + IPC::TestSink& sink() { return sink_; } + + // RenderViewObserver override. + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + virtual bool Send(IPC::Message* msg) OVERRIDE; + protected: + virtual ~MockBrowserPluginManager(); + void AllocateInstanceIDACK(BrowserPlugin* browser_plugin, + int guest_instance_id); + + IPC::TestSink sink_; + + // The last known good deserializer for sync messages. + scoped_ptr<IPC::MessageReplyDeserializer> reply_deserializer_; + + int guest_instance_id_counter_; + + DISALLOW_COPY_AND_ASSIGN(MockBrowserPluginManager); +}; + +} // namespace content + +#endif // CONTENT_RENDERER_BROWSER_PLUGIN_MOCK_BROWSER_PLUGIN_MANAGER_H_ |