blob: a0f21f87c5babe831a330bcabcbeabf4e6c72b6e [file] [log] [blame]
// 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/render_widget_fullscreen_pepper.h"
#include <vector>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/macros.h"
#include "build/build_config.h"
#include "cc/paint/paint_canvas.h"
#include "content/common/view_messages.h"
#include "content/common/widget_messages.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/use_zoom_for_dsf_policy.h"
#include "content/renderer/pepper/pepper_plugin_instance_impl.h"
#include "content/renderer/render_thread_impl.h"
#include "gpu/ipc/client/gpu_channel_host.h"
#include "skia/ext/platform_canvas.h"
#include "third_party/blink/public/common/input/web_gesture_event.h"
#include "third_party/blink/public/common/input/web_mouse_wheel_event.h"
#include "third_party/blink/public/platform/web_size.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "third_party/blink/public/web/web_widget.h"
#include "ui/base/cursor/cursor.h"
#include "ui/gfx/geometry/dip_util.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gl/gpu_preference.h"
using blink::WebCoalescedInputEvent;
using blink::WebGestureEvent;
using blink::WebInputEvent;
using blink::WebInputEventResult;
using blink::WebMouseEvent;
using blink::WebMouseWheelEvent;
using blink::WebRect;
using blink::WebSize;
using blink::WebString;
using blink::WebTextInputType;
using blink::WebVector;
namespace content {
namespace {
class FullscreenMouseLockDispatcher : public MouseLockDispatcher {
public:
explicit FullscreenMouseLockDispatcher(RenderWidgetFullscreenPepper* widget);
~FullscreenMouseLockDispatcher() override;
private:
// MouseLockDispatcher implementation.
void SendLockMouseRequest(blink::WebLocalFrame* requester_frame,
bool request_unadjusted_movement) override;
RenderWidgetFullscreenPepper* widget_;
base::WeakPtrFactory<FullscreenMouseLockDispatcher> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(FullscreenMouseLockDispatcher);
};
WebMouseEvent WebMouseEventFromGestureEvent(const WebGestureEvent& gesture) {
// Only convert touch screen gesture events, do not convert
// touchpad/mouse wheel gesture events. (crbug.com/620974)
if (gesture.SourceDevice() != blink::WebGestureDevice::kTouchscreen)
return WebMouseEvent();
WebInputEvent::Type type = WebInputEvent::Type::kUndefined;
switch (gesture.GetType()) {
case WebInputEvent::Type::kGestureScrollBegin:
type = WebInputEvent::Type::kMouseDown;
break;
case WebInputEvent::Type::kGestureScrollUpdate:
type = WebInputEvent::Type::kMouseMove;
break;
case WebInputEvent::Type::kGestureFlingStart:
// A scroll gesture on the touchscreen may end with a GestureScrollEnd
// when there is no velocity, or a GestureFlingStart when it has a
// velocity. In both cases, it should end the drag that was initiated by
// the GestureScrollBegin (and subsequent GestureScrollUpdate) events.
type = WebInputEvent::Type::kMouseUp;
break;
case WebInputEvent::Type::kGestureScrollEnd:
type = WebInputEvent::Type::kMouseUp;
break;
default:
return WebMouseEvent();
}
WebMouseEvent mouse(type,
gesture.GetModifiers() | WebInputEvent::kLeftButtonDown,
gesture.TimeStamp());
mouse.button = WebMouseEvent::Button::kLeft;
mouse.click_count = (mouse.GetType() == WebInputEvent::Type::kMouseDown ||
mouse.GetType() == WebInputEvent::Type::kMouseUp);
mouse.SetPositionInWidget(gesture.PositionInWidget());
mouse.SetPositionInScreen(gesture.PositionInScreen());
return mouse;
}
FullscreenMouseLockDispatcher::FullscreenMouseLockDispatcher(
RenderWidgetFullscreenPepper* widget) : widget_(widget) {
}
FullscreenMouseLockDispatcher::~FullscreenMouseLockDispatcher() {
}
void FullscreenMouseLockDispatcher::SendLockMouseRequest(
blink::WebLocalFrame* requester_frame,
bool request_unadjusted_movement) {
bool has_transient_user_activation =
requester_frame ? requester_frame->HasTransientUserActivation() : false;
widget_->GetWebWidget()->RequestMouseLock(
has_transient_user_activation, /*privileged=*/true,
request_unadjusted_movement,
base::BindOnce(&MouseLockDispatcher::OnLockMouseACK,
weak_ptr_factory_.GetWeakPtr()));
}
} // anonymous namespace
// We place the WebExternalWidgetClient interface on a separate class because
// RenderWidget implements blink::WebWidgetClient, which is not used for
// WebExternalWidgets, but may have similar method definitions as this
// interface.
class PepperExternalWidgetClient : public blink::WebExternalWidgetClient {
public:
explicit PepperExternalWidgetClient(RenderWidgetFullscreenPepper* widget)
: widget_(widget) {}
~PepperExternalWidgetClient() override = default;
// blink::WebExternalWidgetClient overrides:
blink::WebInputEventResult HandleInputEvent(
const blink::WebCoalescedInputEvent& event) override {
return widget_->ProcessInputEvent(event);
}
blink::WebInputEventResult DispatchBufferedTouchEvents() override {
return WebInputEventResult::kNotHandled;
}
void DidResize(const gfx::Size& size) override { widget_->DidResize(size); }
void RequestNewLayerTreeFrameSink(
LayerTreeFrameSinkCallback callback) override {
widget_->RequestNewLayerTreeFrameSink(std::move(callback));
}
void RecordTimeToFirstActivePaint(base::TimeDelta duration) override {
widget_->RecordTimeToFirstActivePaint(duration);
}
void DidCommitAndDrawCompositorFrame() override {
widget_->DidInitiatePaint();
}
void FocusChanged(bool enabled) override { widget_->FocusChanged(enabled); }
void UpdateVisualProperties(
const blink::VisualProperties& visual_properties) override {
widget_->UpdateVisualProperties(/*emulator_enabled=*/false,
visual_properties);
widget_->UpdateLayerBounds();
}
private:
RenderWidgetFullscreenPepper* widget_;
};
// static
RenderWidgetFullscreenPepper* RenderWidgetFullscreenPepper::Create(
int32_t routing_id,
RenderWidget::ShowCallback show_callback,
CompositorDependencies* compositor_deps,
const blink::ScreenInfo& screen_info,
PepperPluginInstanceImpl* plugin,
const blink::WebURL& local_main_frame_url,
mojo::PendingAssociatedRemote<blink::mojom::WidgetHost> blink_widget_host,
mojo::PendingAssociatedReceiver<blink::mojom::Widget> blink_widget) {
DCHECK_NE(MSG_ROUTING_NONE, routing_id);
DCHECK(show_callback);
RenderWidgetFullscreenPepper* render_widget =
new RenderWidgetFullscreenPepper(
routing_id, compositor_deps, plugin, std::move(blink_widget_host),
std::move(blink_widget), local_main_frame_url);
render_widget->InitForPepperFullscreen(std::move(show_callback),
render_widget->blink_widget_.get(),
screen_info);
return render_widget;
}
RenderWidgetFullscreenPepper::RenderWidgetFullscreenPepper(
int32_t routing_id,
CompositorDependencies* compositor_deps,
PepperPluginInstanceImpl* plugin,
mojo::PendingAssociatedRemote<blink::mojom::WidgetHost> mojo_widget_host,
mojo::PendingAssociatedReceiver<blink::mojom::Widget> mojo_widget,
blink::WebURL main_frame_url)
: RenderWidget(routing_id,
compositor_deps,
/*hidden=*/false,
/*never_composited=*/false),
plugin_(plugin),
mouse_lock_dispatcher_(
std::make_unique<FullscreenMouseLockDispatcher>(this)),
widget_client_(std::make_unique<PepperExternalWidgetClient>(this)) {
blink_widget_ = blink::WebExternalWidget::Create(
widget_client_.get(), main_frame_url, std::move(mojo_widget_host),
std::move(mojo_widget));
}
RenderWidgetFullscreenPepper::~RenderWidgetFullscreenPepper() = default;
void RenderWidgetFullscreenPepper::Destroy() {
// The plugin instance is going away reset any lock target that is set
// on the dispatcher since this object can still live and receive IPC
// responses and may call a dangling lock_target.
mouse_lock_dispatcher_->ClearLockTarget();
// This function is called by the plugin instance as it's going away, so reset
// plugin_ to NULL to avoid calling into a dangling pointer e.g. on Close().
plugin_ = nullptr;
// After calling Destroy(), the plugin instance assumes that the layer is not
// used by us anymore, so it may destroy the layer before this object goes
// away.
SetLayer(nullptr);
// This instructs the browser process, which owns this object, to send back a
// WidgetMsg_Close to destroy this object.
Send(new WidgetHostMsg_Close(routing_id()));
}
void RenderWidgetFullscreenPepper::PepperDidChangeCursor(
const ui::Cursor& cursor) {
blink_widget_->SetCursor(cursor);
}
void RenderWidgetFullscreenPepper::SetLayer(scoped_refptr<cc::Layer> layer) {
layer_ = layer.get();
if (!layer_) {
blink_widget_->SetRootLayer(nullptr);
return;
}
UpdateLayerBounds();
layer_->SetIsDrawable(true);
layer_->SetHitTestable(true);
blink_widget_->SetRootLayer(std::move(layer));
}
void RenderWidgetFullscreenPepper::DidInitiatePaint() {
if (plugin_)
plugin_->ViewInitiatedPaint();
}
void RenderWidgetFullscreenPepper::Close(std::unique_ptr<RenderWidget> widget) {
// If the fullscreen window is closed (e.g. user pressed escape), reset to
// normal mode.
if (plugin_)
plugin_->FlashSetFullscreen(false, false);
// Call Close on the base class to destroy the WebWidget instance.
RenderWidget::Close(std::move(widget));
}
void RenderWidgetFullscreenPepper::UpdateLayerBounds() {
if (!layer_)
return;
// The |layer_| is sized here to cover the entire renderer's compositor
// viewport.
gfx::Size layer_size = gfx::Rect(GetWebWidget()->ViewRect()).size();
// When IsUseZoomForDSFEnabled() is true, layout and compositor layer sizes
// given by blink are all in physical pixels, and the compositor does not do
// any scaling. But the ViewRect() is always in DIP so we must scale the layer
// here as the compositor won't.
if (compositor_deps()->IsUseZoomForDSFEnabled()) {
layer_size = gfx::ScaleToCeiledSize(
layer_size,
GetWebWidget()->GetOriginalScreenInfo().device_scale_factor);
}
layer_->SetBounds(layer_size);
}
WebInputEventResult RenderWidgetFullscreenPepper::ProcessInputEvent(
const WebCoalescedInputEvent& coalesced_event) {
if (!plugin())
return WebInputEventResult::kNotHandled;
const WebInputEvent& event = coalesced_event.Event();
// This cursor is ignored, we always set the cursor directly from
// RenderWidgetFullscreenPepper::DidChangeCursor.
ui::Cursor cursor;
// Pepper plugins do not accept gesture events. So do not send the gesture
// events directly to the plugin. Instead, try to convert them to equivalent
// mouse events, and then send to the plugin.
if (blink::WebInputEvent::IsGestureEventType(event.GetType())) {
bool result = false;
const WebGestureEvent* gesture_event =
static_cast<const WebGestureEvent*>(&event);
switch (event.GetType()) {
case WebInputEvent::Type::kGestureTap: {
WebMouseEvent mouse(WebInputEvent::Type::kMouseMove,
gesture_event->GetModifiers(),
gesture_event->TimeStamp());
mouse.SetPositionInWidget(gesture_event->PositionInWidget());
mouse.SetPositionInScreen(gesture_event->PositionInScreen());
mouse.movement_x = 0;
mouse.movement_y = 0;
result |= plugin()->HandleInputEvent(mouse, &cursor);
mouse.SetType(WebInputEvent::Type::kMouseDown);
mouse.button = WebMouseEvent::Button::kLeft;
mouse.click_count = gesture_event->data.tap.tap_count;
result |= plugin()->HandleInputEvent(mouse, &cursor);
mouse.SetType(WebInputEvent::Type::kMouseUp);
result |= plugin()->HandleInputEvent(mouse, &cursor);
break;
}
default: {
WebMouseEvent mouse = WebMouseEventFromGestureEvent(*gesture_event);
if (mouse.GetType() != WebInputEvent::Type::kUndefined)
result |= plugin()->HandleInputEvent(mouse, &cursor);
break;
}
}
return result ? WebInputEventResult::kHandledApplication
: WebInputEventResult::kNotHandled;
}
bool result = plugin()->HandleInputEvent(event, &cursor);
// For normal web pages, WebViewImpl does input event translations and
// generates context menu events. Since we don't have a WebView, we need to
// do the necessary translation ourselves.
if (WebInputEvent::IsMouseEventType(event.GetType())) {
const WebMouseEvent& mouse_event =
reinterpret_cast<const WebMouseEvent&>(event);
bool send_context_menu_event = false;
// On Mac/Linux, we handle it on mouse down.
// On Windows, we handle it on mouse up.
#if defined(OS_WIN)
send_context_menu_event =
mouse_event.GetType() == WebInputEvent::Type::kMouseUp &&
mouse_event.button == WebMouseEvent::Button::kRight;
#elif defined(OS_MAC)
send_context_menu_event =
mouse_event.GetType() == WebInputEvent::Type::kMouseDown &&
(mouse_event.button == WebMouseEvent::Button::kRight ||
(mouse_event.button == WebMouseEvent::Button::kLeft &&
mouse_event.GetModifiers() & WebMouseEvent::kControlKey));
#else
send_context_menu_event =
mouse_event.GetType() == WebInputEvent::Type::kMouseDown &&
mouse_event.button == WebMouseEvent::Button::kRight;
#endif
if (send_context_menu_event) {
WebMouseEvent context_menu_event(mouse_event);
context_menu_event.SetType(WebInputEvent::Type::kContextMenu);
plugin()->HandleInputEvent(context_menu_event, &cursor);
}
}
return result ? WebInputEventResult::kHandledApplication
: WebInputEventResult::kNotHandled;
}
void RenderWidgetFullscreenPepper::DidResize(const gfx::Size& size) {
if (!plugin())
return;
gfx::Rect plugin_rect(size);
plugin()->ViewChanged(plugin_rect, plugin_rect, plugin_rect);
}
} // namespace content