| // 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); |
| } |
| |
| static std::string PermissionTypeToString(BrowserPluginPermissionType type) { |
| switch (type) { |
| case BrowserPluginPermissionTypeDownload: |
| return browser_plugin::kPermissionTypeDownload; |
| case BrowserPluginPermissionTypeGeolocation: |
| return browser_plugin::kPermissionTypeGeolocation; |
| case BrowserPluginPermissionTypeMedia: |
| return browser_plugin::kPermissionTypeMedia; |
| case BrowserPluginPermissionTypeNewWindow: |
| return browser_plugin::kPermissionTypeNewWindow; |
| case BrowserPluginPermissionTypePointerLock: |
| return browser_plugin::kPermissionTypePointerLock; |
| case BrowserPluginPermissionTypeJavaScriptDialog: |
| return browser_plugin::kPermissionTypeDialog; |
| case BrowserPluginPermissionTypeUnknown: |
| default: |
| NOTREACHED(); |
| break; |
| } |
| return std::string(); |
| } |
| |
| 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), |
| 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), |
| size_changed_in_flight_(false), |
| 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_RequestPermission, OnRequestPermission) |
| 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)); |
| } |
| |
| void BrowserPlugin::SizeChangedDueToAutoSize(const gfx::Size& old_view_size) { |
| size_changed_in_flight_ = false; |
| |
| std::map<std::string, base::Value*> props; |
| props[browser_plugin::kOldHeight] = |
| new base::FundamentalValue(old_view_size.height()); |
| props[browser_plugin::kOldWidth] = |
| new base::FundamentalValue(old_view_size.width()); |
| props[browser_plugin::kNewHeight] = |
| new base::FundamentalValue(last_view_size_.height()); |
| props[browser_plugin::kNewWidth] = |
| new base::FundamentalValue(last_view_size_.width()); |
| TriggerEvent(browser_plugin::kEventSizeChanged, &props); |
| } |
| |
| // 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); |
| } |
| } |
| |
| 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::OnRequestPermission( |
| int guest_instance_id, |
| BrowserPluginPermissionType permission_type, |
| int request_id, |
| const base::DictionaryValue& request_info) { |
| // The New Window API is very similiar to the permission API in structure, |
| // but exposes a slightly different interface to the developer and so we put |
| // it in a separate event. |
| const char* event_name = NULL; |
| if (permission_type == BrowserPluginPermissionTypeNewWindow) { |
| event_name = browser_plugin::kEventNewWindow; |
| } else if (permission_type == BrowserPluginPermissionTypeJavaScriptDialog) { |
| event_name = browser_plugin::kEventDialog; |
| } else { |
| event_name = browser_plugin::kEventRequestPermission; |
| } |
| AddPermissionRequestToSet(request_id); |
| |
| std::map<std::string, base::Value*> props; |
| props[browser_plugin::kPermission] = |
| base::Value::CreateStringValue(PermissionTypeToString(permission_type)); |
| props[browser_plugin::kRequestId] = |
| base::Value::CreateIntegerValue(request_id); |
| |
| // Fill in the info provided by the browser. |
| for (DictionaryValue::Iterator iter(request_info); !iter.IsAtEnd(); |
| iter.Advance()) { |
| props[iter.key()] = iter.value().DeepCopy(); |
| } |
| TriggerEvent(event_name, &props); |
| } |
| |
| 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::AddPermissionRequestToSet(int request_id) { |
| DCHECK(!pending_permission_requests_.count(request_id)); |
| pending_permission_requests_.insert(request_id); |
| } |
| |
| 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); |
| gfx::Size old_view_size = last_view_size_; |
| last_view_size_ = params.view_size; |
| // Schedule a SizeChanged instead of calling it directly to ensure that |
| // the backing store has been updated before the developer attempts to |
| // resize to avoid flicker. |size_changed_in_flight_| acts as a form of |
| // flow control for SizeChanged events. If the guest's view size is changing |
| // rapidly before a SizeChanged event fires, then we avoid scheduling |
| // another SizeChanged event. SizeChanged reads the new size from |
| // |last_view_size_| so we can be sure that it always fires an event |
| // with the last seen view size. |
| if (container_ && !size_changed_in_flight_) { |
| size_changed_in_flight_ = true; |
| base::MessageLoop::current()->PostTask( |
| FROM_HERE, |
| base::Bind(&BrowserPlugin::SizeChangedDueToAutoSize, |
| base::Unretained(this), |
| old_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_; |
| } |
| |
| void BrowserPlugin::RespondPermission( |
| int request_id, bool allow, const std::string& user_input) { |
| browser_plugin_manager()->Send( |
| new BrowserPluginHostMsg_RespondPermission( |
| render_view_routing_id_, guest_instance_id_, |
| request_id, allow, user_input)); |
| } |
| |
| bool BrowserPlugin::RespondPermissionIfRequestIsPending( |
| int request_id, bool allow, const std::string& user_input) { |
| PendingPermissionRequests::iterator iter = |
| pending_permission_requests_.find(request_id); |
| if (iter == pending_permission_requests_.end()) |
| return false; |
| |
| pending_permission_requests_.erase(iter); |
| RespondPermission(request_id, allow, user_input); |
| return true; |
| } |
| |
| 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_RequestPermission::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 (!HasGuestInstanceID()) |
| 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 |