// 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/message_loop/message_loop.h"
#include "base/strings/string_number_conversions.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_manager.h"
#include "content/renderer/child_frame_compositing_helper.h"
#include "content/renderer/cursor_utils.h"
#include "content/renderer/drop_data_builder.h"
#include "content/renderer/render_thread_impl.h"
#include "content/renderer/sad_plugin.h"
#include "third_party/WebKit/public/platform/WebRect.h"
#include "third_party/WebKit/public/web/WebBindings.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebElement.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/WebView.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "ui/events/keycodes/keyboard_codes.h"
using blink::WebCanvas;
using blink::WebPluginContainer;
using blink::WebPluginParams;
using blink::WebPoint;
using blink::WebRect;
using blink::WebURL;
using blink::WebVector;
namespace content {
BrowserPlugin::BrowserPlugin(RenderViewImpl* render_view,
blink::WebFrame* frame,
bool auto_navigate)
: guest_instance_id_(browser_plugin::kInstanceIDNone),
attached_(false),
render_view_(render_view->AsWeakPtr()),
render_view_routing_id_(render_view->GetRoutingID()),
container_(NULL),
paint_ack_received_(true),
last_device_scale_factor_(GetDeviceScaleFactor()),
sad_guest_(NULL),
guest_crashed_(false),
is_auto_size_state_dirty_(false),
content_window_routing_id_(MSG_ROUTING_NONE),
plugin_focused_(false),
visible_(true),
auto_navigate_(auto_navigate),
mouse_locked_(false),
browser_plugin_manager_(render_view->GetBrowserPluginManager()),
embedder_frame_url_(frame->document().url()),
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_));
}
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_CopyFromCompositingSurface,
OnCopyFromCompositingSurface)
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_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;
blink::WebElement element = container()->element();
blink::WebString web_attribute_name =
blink::WebString::fromUTF8(attribute_name);
if (!HasDOMAttribute(attribute_name) ||
(std::string(element.getAttribute(web_attribute_name).utf8()) !=
attribute_value)) {
element.setAttribute(web_attribute_name,
blink::WebString::fromUTF8(attribute_value));
}
}
void BrowserPlugin::RemoveDOMAttribute(const std::string& attribute_name) {
if (!container())
return;
container()->element().removeAttribute(
blink::WebString::fromUTF8(attribute_name));
}
std::string BrowserPlugin::GetDOMAttributeValue(
const std::string& attribute_name) const {
if (!container())
return std::string();
return container()->element().getAttribute(
blink::WebString::fromUTF8(attribute_name)).utf8();
}
bool BrowserPlugin::HasDOMAttribute(const std::string& attribute_name) const {
if (!container())
return false;
return container()->element().hasAttribute(
blink::WebString::fromUTF8(attribute_name));
}
bool BrowserPlugin::GetAllowTransparencyAttribute() const {
return HasDOMAttribute(browser_plugin::kAttributeAllowTransparency);
}
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());
}
void BrowserPlugin::ParseAllowTransparencyAttribute() {
if (!HasGuestInstanceID())
return;
bool opaque = !GetAllowTransparencyAttribute();
if (compositing_helper_)
compositing_helper_->SetContentsOpaque(opaque);
browser_plugin_manager()->Send(new BrowserPluginHostMsg_SetContentsOpaque(
render_view_routing_id_,
guest_instance_id_,
opaque));
}
void BrowserPlugin::ParseAutoSizeAttribute() {
last_view_size_ = plugin_size();
is_auto_size_state_dirty_ = true;
UpdateGuestAutoSizeState(GetAutoSizeAttribute());
}
void BrowserPlugin::PopulateAutoSizeParameters(
BrowserPluginHostMsg_AutoSize_Params* params, bool auto_size_enabled) {
params->enable = auto_size_enabled;
// No need to populate the params if autosize is off.
if (auto_size_enabled) {
params->max_size = gfx::Size(GetAdjustedMaxWidth(), GetAdjustedMaxHeight());
params->min_size = gfx::Size(GetAdjustedMinWidth(), GetAdjustedMinHeight());
if (max_auto_size_ != params->max_size)
is_auto_size_state_dirty_ = true;
max_auto_size_ = params->max_size;
} else {
max_auto_size_ = gfx::Size();
}
}
void BrowserPlugin::UpdateGuestAutoSizeState(bool auto_size_enabled) {
// 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::OnUpdateRect.
if (!HasGuestInstanceID() || !paint_ack_received_)
return;
BrowserPluginHostMsg_AutoSize_Params auto_size_params;
BrowserPluginHostMsg_ResizeGuest_Params resize_guest_params;
if (auto_size_enabled) {
GetSizeParams(&auto_size_params, &resize_guest_params, true);
} else {
GetSizeParams(NULL, &resize_guest_params, true);
}
paint_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::Attach(int guest_instance_id,
scoped_ptr<base::DictionaryValue> extra_params) {
CHECK(guest_instance_id != browser_plugin::kInstanceIDNone);
// If this BrowserPlugin is already attached to a guest, then do nothing.
if (HasGuestInstanceID())
return;
// This API may be called directly without setting the src attribute.
// In that case, we need to make sure we don't allocate another instance ID.
guest_instance_id_ = guest_instance_id;
browser_plugin_manager()->AddBrowserPlugin(guest_instance_id, this);
BrowserPluginHostMsg_Attach_Params attach_params;
attach_params.focused = ShouldGuestBeFocused();
attach_params.visible = visible_;
attach_params.opaque = !GetAllowTransparencyAttribute();
attach_params.embedder_frame_url = embedder_frame_url_;
attach_params.origin = plugin_rect().origin();
GetSizeParams(&attach_params.auto_size_params,
&attach_params.resize_guest_params,
false);
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) {
attached_ = true;
}
void BrowserPlugin::OnBuffersSwapped(
int instance_id,
const FrameMsg_BuffersSwapped_Params& params) {
EnableCompositing(true);
compositing_helper_->OnBuffersSwapped(params.size,
params.mailbox,
params.gpu_route_id,
params.gpu_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.frame.AssignTo(frame.get());
EnableCompositing(true);
compositing_helper_->OnCompositorFrameSwapped(frame.Pass(),
param.b.producing_route_id,
param.b.output_surface_id,
param.b.producing_host_id,
param.b.shared_memory_handle);
}
void BrowserPlugin::OnCopyFromCompositingSurface(int guest_instance_id,
int request_id,
gfx::Rect source_rect,
gfx::Size dest_size) {
if (!compositing_helper_) {
browser_plugin_manager()->Send(
new BrowserPluginHostMsg_CopyFromCompositingSurfaceAck(
render_view_routing_id_,
guest_instance_id_,
request_id,
SkBitmap()));
return;
}
compositing_helper_->CopyFromCompositingSurface(request_id, source_rect,
dest_size);
}
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;
// Turn off compositing so we can display the sad graphic. Changes to
// compositing state will show up at a later time after a layout and commit.
EnableCompositing(false);
// 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 ?
blink::WebPluginContainer::TouchEventRequestTypeRaw :
blink::WebPluginContainer::TouchEventRequestTypeNone);
}
}
void BrowserPlugin::OnUpdateRect(
int guest_instance_id,
const BrowserPluginMsg_UpdateRect_Params& params) {
// Note that there is no need to send ACK for this message.
// If the guest has updated pixels then it is no longer crashed.
guest_crashed_ = false;
bool auto_size = GetAutoSizeAttribute();
// We receive a resize ACK in regular mode, but not in autosize.
// In Compositing mode, we need to do it here so we can continue sending
// resize messages when needed.
if (params.is_resize_ack || (auto_size || is_auto_size_state_dirty_))
paint_ack_received_ = true;
bool was_auto_size_state_dirty = auto_size && is_auto_size_state_dirty_;
is_auto_size_state_dirty_ = false;
if ((!auto_size && (width() != params.view_size.width() ||
height() != params.view_size.height())) ||
(auto_size && was_auto_size_state_dirty) ||
GetDeviceScaleFactor() != params.scale_factor) {
UpdateGuestAutoSizeState(auto_size);
return;
}
if (auto_size && (params.view_size != last_view_size_))
last_view_size_ = params.view_size;
BrowserPluginHostMsg_AutoSize_Params auto_size_params;
BrowserPluginHostMsg_ResizeGuest_Params resize_guest_params;
// 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_SetAutoSize(render_view_routing_id_,
guest_instance_id_,
auto_size_params,
resize_guest_params));
}
void BrowserPlugin::ParseSizeContraintsChanged() {
bool auto_size = GetAutoSizeAttribute();
if (auto_size) {
is_auto_size_state_dirty_ = true;
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;
blink::WebFrame* guest_frame = guest_render_view->GetWebView()->mainFrame();
return guest_frame->windowObject();
}
bool BrowserPlugin::HasGuestInstanceID() const {
return guest_instance_id_ != browser_plugin::kInstanceIDNone;
}
void BrowserPlugin::ShowSadGraphic() {
// 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();
}
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 || !paint_ack_received_)
return;
BrowserPluginHostMsg_ResizeGuest_Params params;
PopulateResizeGuestParameters(¶ms, plugin_size(), true);
browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest(
render_view_routing_id_,
guest_instance_id_,
params));
}
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;
}
blink::WebPluginContainer* BrowserPlugin::container() const {
return container_;
}
bool BrowserPlugin::initialize(WebPluginContainer* container) {
if (!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);
// This is a way to notify observers of our attributes that we have the
// bindings ready. This also means that this plugin is available in render
// tree.
UpdateDOMAttribute("internalbindings", "true");
return true;
}
void BrowserPlugin::EnableCompositing(bool enable) {
bool enabled = !!compositing_helper_;
if (enabled == enable)
return;
if (enable) {
DCHECK(!compositing_helper_.get());
if (!compositing_helper_.get()) {
compositing_helper_ =
ChildFrameCompositingHelper::CreateCompositingHelperForBrowserPlugin(
weak_ptr_factory_.GetWeakPtr());
}
}
compositing_helper_->EnableCompositing(enable);
compositing_helper_->SetContentsOpaque(!GetAllowTransparencyAttribute());
if (!enable) {
DCHECK(compositing_helper_.get());
compositing_helper_->OnContainerDestroy();
compositing_helper_ = NULL;
}
}
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();
if (compositing_helper_.get())
compositing_helper_->OnContainerDestroy();
container_ = NULL;
// 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.
blink::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::supportsInputMethod() 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);
}
// 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_CopyFromCompositingSurface::ID:
case BrowserPluginMsg_GuestContentWindowReady::ID:
case BrowserPluginMsg_GuestGone::ID:
case BrowserPluginMsg_SetCursor::ID:
case BrowserPluginMsg_SetMouseLock::ID:
case BrowserPluginMsg_ShouldAcceptTouchEvents::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 |!paint_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 (!paint_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_size(), false);
paint_ack_received_ = false;
browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest(
render_view_routing_id_,
guest_instance_id_,
params));
}
void BrowserPlugin::PopulateResizeGuestParameters(
BrowserPluginHostMsg_ResizeGuest_Params* params,
const gfx::Size& view_size,
bool needs_repaint) {
params->size_changed = true;
params->view_size = view_size;
params->repaint = needs_repaint;
params->scale_factor = GetDeviceScaleFactor();
if (last_device_scale_factor_ != params->scale_factor){
DCHECK(params->repaint);
last_device_scale_factor_ = params->scale_factor;
}
}
void BrowserPlugin::GetSizeParams(
BrowserPluginHostMsg_AutoSize_Params* auto_size_params,
BrowserPluginHostMsg_ResizeGuest_Params* resize_guest_params,
bool needs_repaint) {
if (auto_size_params) {
PopulateAutoSizeParameters(auto_size_params, GetAutoSizeAttribute());
} else {
max_auto_size_ = gfx::Size();
}
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;
paint_ack_received_ = false;
PopulateResizeGuestParameters(resize_guest_params, view_size, needs_repaint);
}
void BrowserPlugin::updateFocus(bool focused) {
plugin_focused_ = focused;
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 blink::WebInputEvent& event,
blink::WebCursorInfo& cursor_info) {
if (guest_crashed_ || !HasGuestInstanceID())
return false;
if (event.type == blink::WebInputEvent::ContextMenu)
return true;
const blink::WebInputEvent* modified_event = &event;
scoped_ptr<blink::WebTouchEvent> touch_event;
if (blink::WebInputEvent::isTouchEventType(event.type)) {
const blink::WebTouchEvent* orig_touch_event =
static_cast<const blink::WebTouchEvent*>(&event);
touch_event.reset(new blink::WebTouchEvent());
memcpy(touch_event.get(), orig_touch_event, sizeof(blink::WebTouchEvent));
// TODO(bokan): Blink passes back a WebGestureEvent with a touches,
// changedTouches, and targetTouches lists; however, it doesn't set
// the state field on the touches which is what the RenderWidget uses
// to create a WebCore::TouchEvent. crbug.com/358132 tracks removing
// these multiple lists from WebTouchEvent since they lead to misuse
// like this and are functionally unused. In the mean time we'll setup
// the state field here manually to fix multi-touch BrowserPlugins.
for (size_t i = 0; i < touch_event->touchesLength; ++i) {
blink::WebTouchPoint& touch = touch_event->touches[i];
touch.state = blink::WebTouchPoint::StateStationary;
for (size_t j = 0; j < touch_event->changedTouchesLength; ++j) {
blink::WebTouchPoint& changed_touch = touch_event->changedTouches[j];
if (touch.id == changed_touch.id) {
touch.state = changed_touch.state;
break;
}
}
}
// For End and Cancel, Blink gives BrowserPlugin a list of touches that
// are down, but the browser process expects a list of all touches. We
// modify these events here to match these expectations.
if (event.type == blink::WebInputEvent::TouchEnd ||
event.type == blink::WebInputEvent::TouchCancel) {
if (touch_event->changedTouchesLength > 0) {
memcpy(&touch_event->touches[touch_event->touchesLength],
&touch_event->changedTouches,
touch_event->changedTouchesLength * sizeof(blink::WebTouchPoint));
touch_event->touchesLength += touch_event->changedTouchesLength;
}
}
modified_event = touch_event.get();
}
if (blink::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(blink::WebDragStatus drag_status,
const blink::WebDragData& drag_data,
blink::WebDragOperationsMask mask,
const blink::WebPoint& position,
const blink::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 blink::WebURLResponse& response) {
}
void BrowserPlugin::didReceiveData(const char* data, int data_length) {
if (auto_navigate_) {
std::string value(data, data_length);
html_string_ += value;
}
}
void BrowserPlugin::didFinishLoading() {
if (auto_navigate_) {
// TODO(lazyboy): Make |auto_navigate_| stuff work.
UpdateDOMAttribute(content::browser_plugin::kAttributeSrc, html_string_);
}
}
void BrowserPlugin::didFailLoading(const blink::WebURLError& error) {
}
void BrowserPlugin::didFinishLoadingFrameRequest(const blink::WebURL& url,
void* notify_data) {
}
void BrowserPlugin::didFailLoadingFrameRequest(
const blink::WebURL& url,
void* notify_data,
const blink::WebURLError& error) {
}
bool BrowserPlugin::executeEditCommand(const blink::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 blink::WebString& name,
const blink::WebString& value) {
edit_commands_.push_back(EditCommand(name.utf8(), value.utf8()));
// BrowserPlugin swallows edit commands.
return true;
}
bool BrowserPlugin::setComposition(
const blink::WebString& text,
const blink::WebVector<blink::WebCompositionUnderline>& underlines,
int selectionStart,
int selectionEnd) {
if (!HasGuestInstanceID())
return false;
std::vector<blink::WebCompositionUnderline> std_underlines;
for (size_t i = 0; i < underlines.size(); ++i) {
std_underlines.push_back(underlines[i]);
}
browser_plugin_manager()->Send(new BrowserPluginHostMsg_ImeSetComposition(
render_view_routing_id_,
guest_instance_id_,
text.utf8(),
std_underlines,
selectionStart,
selectionEnd));
// TODO(kochi): This assumes the IPC handling always succeeds.
return true;
}
bool BrowserPlugin::confirmComposition(
const blink::WebString& text,
blink::WebWidget::ConfirmCompositionBehavior selectionBehavior) {
if (!HasGuestInstanceID())
return false;
bool keep_selection = (selectionBehavior == blink::WebWidget::KeepSelection);
browser_plugin_manager()->Send(new BrowserPluginHostMsg_ImeConfirmComposition(
render_view_routing_id_,
guest_instance_id_,
text.utf8(),
keep_selection));
// TODO(kochi): This assumes the IPC handling always succeeds.
return true;
}
void BrowserPlugin::extendSelectionAndDelete(int before, int after) {
if (!HasGuestInstanceID())
return;
browser_plugin_manager()->Send(
new BrowserPluginHostMsg_ExtendSelectionAndDelete(
render_view_routing_id_,
guest_instance_id_,
before,
after));
}
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 blink::WebMouseEvent& event) {
browser_plugin_manager()->Send(
new BrowserPluginHostMsg_HandleInputEvent(render_view_routing_id_,
guest_instance_id_,
plugin_rect_,
&event));
return true;
}
} // namespace content