Commit 5f615a95 authored by Riku Palomäki's avatar Riku Palomäki Committed by Marshall Greenblatt
Browse files

Add multi-touch support for OSR (issue #1059)

parent 9ba28dd7
......@@ -443,6 +443,8 @@ static_library("libcef_static") {
"libcef/browser/origin_whitelist_impl.h",
"libcef/browser/osr/browser_platform_delegate_osr.cc",
"libcef/browser/osr/browser_platform_delegate_osr.h",
"libcef/browser/osr/motion_event_osr.cc",
"libcef/browser/osr/motion_event_osr.h",
"libcef/browser/osr/osr_accessibility_util.cc",
"libcef/browser/osr/osr_accessibility_util.h",
"libcef/browser/osr/osr_util.cc",
......@@ -1993,6 +1995,7 @@ if (is_mac) {
"gtk+-2.0",
"gthread-2.0",
"gtk+-unix-print-2.0",
"xi",
]
}
......
......@@ -33,7 +33,7 @@
// by hand. See the translator.README.txt file in the tools directory for
// more information.
//
// $hash=f99ba0878f8b6313adc7a43ad1fa295304fa9bcb$
// $hash=15f23de47af54fa690b6c5810e3049f97ae2aabd$
//
#ifndef CEF_INCLUDE_CAPI_CEF_BROWSER_CAPI_H_
......@@ -617,6 +617,12 @@ typedef struct _cef_browser_host_t {
int deltaX,
int deltaY);
///
// Send a touch event to the browser for a windowless browser.
///
void(CEF_CALLBACK* send_touch_event)(struct _cef_browser_host_t* self,
const struct _cef_touch_event_t* event);
///
// Send a focus event to the browser.
///
......
......@@ -638,6 +638,12 @@ class CefBrowserHost : public virtual CefBaseRefCounted {
int deltaX,
int deltaY) = 0;
///
// Send a touch event to the browser for a windowless browser.
///
/*--cef()--*/
virtual void SendTouchEvent(const CefTouchEvent& event) = 0;
///
// Send a focus event to the browser.
///
......
......@@ -1696,6 +1696,74 @@ typedef struct _cef_mouse_event_t {
uint32 modifiers;
} cef_mouse_event_t;
///
// Touch points states types.
///
typedef enum {
CEF_TET_RELEASED = 0,
CEF_TET_PRESSED,
CEF_TET_MOVED,
CEF_TET_CANCELLED
} cef_touch_event_type_t;
///
// Structure representing touch event information.
///
typedef struct _cef_touch_event_t {
///
// Id of a touch point. Must be unique per touch, can be any number except -1.
// Note that a maximum of 16 concurrent touches will be tracked; touches
// beyond that will be ignored.
///
int id;
///
// X coordinate relative to the left side of the view.
///
float x;
///
// Y coordinate relative to the top side of the view.
///
float y;
///
// X radius in pixels. Set to 0 if not applicable.
///
float radius_x;
///
// Y radius in pixels. Set to 0 if not applicable.
///
float radius_y;
///
// Rotation angle in radians. Set to 0 if not applicable.
///
float rotation_angle;
///
// The normalized pressure of the pointer input in the range of [0,1].
// Set to 0 if not applicable.
///
float pressure;
///
// The state of the touch point. Touches begin with one CEF_TET_PRESSED event
// followed by zero or more CEF_TET_MOVED events and finally one
// CEF_TET_RELEASED or CEF_TET_CANCELLED event. Events not respecting this
// order will be ignored.
///
cef_touch_event_type_t type;
///
// Bit flags describing any pressed modifier keys. See
// cef_event_flags_t for values.
///
uint32 modifiers;
} cef_touch_event_t;
///
// Paint element types.
///
......
......@@ -481,6 +481,25 @@ struct CefMouseEventTraits {
///
typedef CefStructBase<CefMouseEventTraits> CefMouseEvent;
struct CefTouchEventTraits {
typedef cef_touch_event_t struct_type;
static inline void init(struct_type* s) {}
static inline void clear(struct_type* s) {}
static inline void set(const struct_type* src,
struct_type* target,
bool copy) {
*target = *src;
}
};
///
// Class representing a touch event.
///
typedef CefStructBase<CefTouchEventTraits> CefTouchEvent;
struct CefPopupFeaturesTraits {
typedef cef_popup_features_t struct_type;
......
......@@ -3697,3 +3697,21 @@ bool CefBrowserHostImpl::Send(IPC::Message* message) {
delete message;
return false;
}
void CefBrowserHostImpl::SendTouchEvent(const CefTouchEvent& event) {
if (!IsWindowless()) {
NOTREACHED() << "Window rendering is not disabled";
return;
}
if (!CEF_CURRENTLY_ON_UIT()) {
CEF_POST_TASK(CEF_UIT,
base::Bind(&CefBrowserHostImpl::SendTouchEvent, this, event));
return;
}
if (!web_contents() || !platform_delegate_)
return;
platform_delegate_->SendTouchEvent(event);
}
......@@ -222,6 +222,7 @@ class CefBrowserHostImpl : public CefBrowserHost,
void SendMouseWheelEvent(const CefMouseEvent& event,
int deltaX,
int deltaY) override;
void SendTouchEvent(const CefTouchEvent& event) override;
void SendFocusEvent(bool setFocus) override;
void SendCaptureLostEvent() override;
void NotifyMoveOrResizeStarted() override;
......
......@@ -22,6 +22,7 @@ namespace blink {
class WebMouseEvent;
class WebMouseWheelEvent;
class WebInputEvent;
class WebTouchEvent;
} // namespace blink
namespace content {
......@@ -150,6 +151,9 @@ class CefBrowserPlatformDelegate {
virtual void SendMouseEvent(const blink::WebMouseEvent& event) = 0;
virtual void SendMouseWheelEvent(const blink::WebMouseWheelEvent& event) = 0;
// Send touch events.
virtual void SendTouchEvent(const CefTouchEvent& event) = 0;
// Send focus event. The browser's WebContents may be NULL when this method is
// called.
virtual void SendFocusEvent(bool setFocus) = 0;
......
......@@ -66,6 +66,11 @@ void CefBrowserPlatformDelegateBackground::SendMouseWheelEvent(
// Nothing to do here.
}
void CefBrowserPlatformDelegateBackground::SendTouchEvent(
const CefTouchEvent& event) {
// Nothing to do here.
}
void CefBrowserPlatformDelegateBackground::SendFocusEvent(bool setFocus) {
// Nothing to do here.
}
......
......@@ -28,6 +28,7 @@ class CefBrowserPlatformDelegateBackground
void SendKeyEvent(const content::NativeWebKeyboardEvent& event) override;
void SendMouseEvent(const blink::WebMouseEvent& event) override;
void SendMouseWheelEvent(const blink::WebMouseWheelEvent& event) override;
void SendTouchEvent(const CefTouchEvent& event) override;
void SendFocusEvent(bool setFocus) override;
gfx::Point GetScreenPoint(const gfx::Point& view) const override;
void ViewText(const std::string& text) override;
......
......@@ -59,6 +59,9 @@ void CefBrowserPlatformDelegateNative::SendMouseWheelEvent(
host->GetWidget()->ForwardWheelEvent(event);
}
void CefBrowserPlatformDelegateNative::SendTouchEvent(
const CefTouchEvent& event) {}
bool CefBrowserPlatformDelegateNative::IsWindowless() const {
return false;
}
......
......@@ -32,6 +32,7 @@ class CefBrowserPlatformDelegateNative : public CefBrowserPlatformDelegate {
void SendKeyEvent(const content::NativeWebKeyboardEvent& event) override;
void SendMouseEvent(const blink::WebMouseEvent& event) override;
void SendMouseWheelEvent(const blink::WebMouseWheelEvent& event) override;
void SendTouchEvent(const CefTouchEvent& event) override;
bool IsWindowless() const override;
bool IsViewsHosted() const override;
......
......@@ -17,6 +17,7 @@
#include "content/browser/renderer_host/render_widget_host_input_event_router.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/render_view_host.h"
#include "ui/events/base_event_utils.h"
CefBrowserPlatformDelegateOsr::CefBrowserPlatformDelegateOsr(
std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate)
......@@ -109,6 +110,12 @@ void CefBrowserPlatformDelegateOsr::SendMouseWheelEvent(
view->SendMouseWheelEvent(event);
}
void CefBrowserPlatformDelegateOsr::SendTouchEvent(const CefTouchEvent& event) {
CefRenderWidgetHostViewOSR* view = GetOSRHostView();
if (view)
view->SendTouchEvent(event);
}
void CefBrowserPlatformDelegateOsr::SendFocusEvent(bool setFocus) {
CefRenderWidgetHostViewOSR* view = GetOSRHostView();
if (view)
......
......@@ -35,6 +35,7 @@ class CefBrowserPlatformDelegateOsr
void SendKeyEvent(const content::NativeWebKeyboardEvent& event) override;
void SendMouseEvent(const blink::WebMouseEvent& event) override;
void SendMouseWheelEvent(const blink::WebMouseWheelEvent& event) override;
void SendTouchEvent(const CefTouchEvent& event) override;
void SendFocusEvent(bool setFocus) override;
gfx::Point GetScreenPoint(const gfx::Point& view) const override;
void ViewText(const std::string& text) override;
......
// Copyright (c) 2017 The Chromium Embedded Framework Authors.
// Portions 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 "libcef/browser/osr/motion_event_osr.h"
#include <algorithm>
#include "ui/events/base_event_utils.h"
#include "ui/events/gesture_detection/gesture_configuration.h"
CefMotionEventOSR::CefMotionEventOSR() {
std::fill(id_map_, id_map_ + blink::WebTouchEvent::kTouchesLengthCap, -1);
}
CefMotionEventOSR::~CefMotionEventOSR() {}
int CefMotionEventOSR::GetSourceDeviceId(size_t pointer_index) const {
if (IsValidIndex(pointer_index))
return pointer(pointer_index).source_device_id;
else
return -1;
}
// Returns true if the touch was valid.
bool CefMotionEventOSR::OnTouch(const CefTouchEvent& touch) {
int id = LookupId(touch.id);
bool pointer_id_is_active = id != -1;
if (touch.type == CEF_TET_PRESSED && pointer_id_is_active) {
// Ignore pressed events for already active touches.
return false;
} else if (touch.type != CEF_TET_PRESSED && !pointer_id_is_active) {
// When a window begins capturing touch events, we could have an active
// touch stream transfered to us, resulting in touch move or touch up
// events without associated touch down events. Ignore them.
return false;
}
switch (touch.type) {
case CEF_TET_PRESSED:
id = AddId(touch.id);
if (id == -1)
return false;
if (!AddTouch(touch, id))
return false;
break;
case CEF_TET_MOVED: {
// Discard if touch is stationary.
int index = FindPointerIndexOfId(id);
if (IsValidIndex(index) &&
(touch.x == GetX(index) && touch.y == GetY(index))) {
return false;
}
}
[[fallthrough]];
// No break.
case CEF_TET_RELEASED:
case CEF_TET_CANCELLED:
// Removing these touch points needs to be postponed until after the
// MotionEvent has been dispatched. This cleanup occurs in
// CleanupRemovedTouchPoints.
UpdateTouch(touch, id);
break;
}
UpdateCachedAction(touch, id);
set_unique_event_id(ui::GetNextTouchEventId());
set_flags(touch.modifiers);
set_event_time(base::TimeTicks::Now());
return true;
}
// We can't cleanup removed touch points immediately upon receipt of a
// TouchCancel or TouchRelease, as the MotionEvent needs to be able to report
// information about those touch events. Once the MotionEvent has been
// processed, we call CleanupRemovedTouchPoints to do the required
// book-keeping.
void CefMotionEventOSR::CleanupRemovedTouchPoints(const CefTouchEvent& event) {
if (event.type != CEF_TET_RELEASED && event.type != CEF_TET_CANCELLED) {
return;
}
DCHECK(GetPointerCount());
int id = LookupId(event.id);
int index_to_delete = FindPointerIndexOfId(id);
set_action_index(-1);
set_action(ui::MotionEvent::Action::NONE);
if (IsValidIndex(index_to_delete)) {
pointer(index_to_delete) = pointer(GetPointerCount() - 1);
PopPointer();
RemoveId(event.id);
}
}
// Reset unchanged touch point to StateStationary for touchmove and touchcancel.
void CefMotionEventOSR::MarkUnchangedTouchPointsAsStationary(
blink::WebTouchEvent* event,
const CefTouchEvent& cef_event) {
int id = LookupId(cef_event.id);
if (event->GetType() == blink::WebInputEvent::kTouchMove ||
event->GetType() == blink::WebInputEvent::kTouchCancel) {
for (size_t i = 0; i < event->touches_length; ++i) {
if (event->touches[i].id != id)
event->touches[i].state = blink::WebTouchPoint::kStateStationary;
}
}
}
int CefMotionEventOSR::LookupId(int id) {
if (id == -1)
return -1;
for (int i = 0; i < blink::WebTouchEvent::kTouchesLengthCap; i++) {
if (id_map_[i] == id) {
return i;
}
}
return -1;
}
int CefMotionEventOSR::AddId(int id) {
if (id == -1 || LookupId(id) >= 0)
return -1;
for (int i = 0; i < blink::WebTouchEvent::kTouchesLengthCap; i++) {
if (id_map_[i] == -1) {
id_map_[i] = id;
return i;
}
}
return -1;
}
void CefMotionEventOSR::RemoveId(int id) {
for (int i = 0; i < blink::WebTouchEvent::kTouchesLengthCap; i++) {
if (id_map_[i] == id) {
id_map_[i] = -1;
}
}
}
bool CefMotionEventOSR::AddTouch(const CefTouchEvent& touch, int id) {
if (GetPointerCount() == MotionEvent::MAX_TOUCH_POINT_COUNT)
return false;
PushPointer(GetPointerPropertiesFromTouchEvent(touch, id));
return true;
}
void CefMotionEventOSR::UpdateTouch(const CefTouchEvent& touch, int id) {
int index_to_update = FindPointerIndexOfId(id);
if (IsValidIndex(index_to_update))
pointer(index_to_update) = GetPointerPropertiesFromTouchEvent(touch, id);
}
void CefMotionEventOSR::UpdateCachedAction(const CefTouchEvent& touch, int id) {
DCHECK(GetPointerCount());
switch (touch.type) {
case CEF_TET_PRESSED:
if (GetPointerCount() == 1) {
set_action(ui::MotionEvent::Action::DOWN);
} else {
set_action(ui::MotionEvent::Action::POINTER_DOWN);
set_action_index(FindPointerIndexOfId(id));
}
break;
case CEF_TET_RELEASED:
if (GetPointerCount() == 1) {
set_action(ui::MotionEvent::Action::UP);
} else {
set_action(ui::MotionEvent::Action::POINTER_UP);
set_action_index(FindPointerIndexOfId(id));
}
break;
case CEF_TET_CANCELLED:
set_action(ui::MotionEvent::Action::CANCEL);
break;
case CEF_TET_MOVED:
set_action(ui::MotionEvent::Action::MOVE);
break;
}
}
bool CefMotionEventOSR::IsValidIndex(int index) const {
return (index >= 0) && (index < static_cast<int>(GetPointerCount()));
}
ui::PointerProperties CefMotionEventOSR::GetPointerPropertiesFromTouchEvent(
const CefTouchEvent& touch,
int id) {
ui::PointerProperties pointer_properties;
pointer_properties.x = touch.x;
pointer_properties.y = touch.y;
pointer_properties.raw_x = touch.x;
pointer_properties.raw_y = touch.y;
pointer_properties.id = id;
pointer_properties.pressure = touch.pressure;
pointer_properties.source_device_id = 0;
pointer_properties.SetAxesAndOrientation(touch.radius_x, touch.radius_y,
touch.rotation_angle);
if (!pointer_properties.touch_major) {
pointer_properties.touch_major =
2.f * ui::GestureConfiguration::GetInstance()->default_radius();
pointer_properties.touch_minor =
2.f * ui::GestureConfiguration::GetInstance()->default_radius();
pointer_properties.orientation = 0;
}
pointer_properties.tool_type = ui::MotionEvent::ToolType::FINGER;
return pointer_properties;
}
// Copyright (c) 2017 The Chromium Embedded Framework Authors.
// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CEF_LIBCEF_BROWSER_OSR_MOTION_EVENT_OSR_H_
#define CEF_LIBCEF_BROWSER_OSR_MOTION_EVENT_OSR_H_
#pragma once
#include "include/cef_base.h"
#include "third_party/blink/public/platform/web_touch_event.h"
#include "ui/events/gesture_detection/motion_event_generic.h"
// Implementation of MotionEvent which takes a stream of CefTouchEvents.
// This class is based on ui::MotionEventAura.
class CefMotionEventOSR : public ui::MotionEventGeneric {
public:
CefMotionEventOSR();
~CefMotionEventOSR() override;
int GetSourceDeviceId(size_t pointer_index) const override;
// Returns true if the touch was valid.
bool OnTouch(const CefTouchEvent& touch);
// We can't cleanup removed touch points immediately upon receipt of a
// TouchCancel or TouchRelease, as the MotionEvent needs to be able to report
// information about those touch events. Once the MotionEvent has been
// processed, we call CleanupRemovedTouchPoints to do the required
// book-keeping.
void CleanupRemovedTouchPoints(const CefTouchEvent& event);
// Reset unchanged touch point to StateStationary for touchmove and
// touchcancel to make sure only send one ack per WebTouchEvent.
void MarkUnchangedTouchPointsAsStationary(blink::WebTouchEvent* event,
const CefTouchEvent& cef_event);
private:
// Chromium can't cope with touch ids >31, so let's map the incoming
// ids to a safe range.
int id_map_[blink::WebTouchEvent::kTouchesLengthCap];
int LookupId(int id);
int AddId(int id);
void RemoveId(int id);
bool AddTouch(const CefTouchEvent& touch, int id);
void UpdateTouch(const CefTouchEvent& touch, int id);
void UpdateCachedAction(const CefTouchEvent& touch, int id);
bool IsValidIndex(int index) const;
ui::PointerProperties GetPointerPropertiesFromTouchEvent(
const CefTouchEvent& touch,
int id);
DISALLOW_COPY_AND_ASSIGN(CefMotionEventOSR);
};
#endif
......@@ -31,9 +31,12 @@
#include "content/browser/renderer_host/cursor_manager.h"
#include "content/browser/renderer_host/delegated_frame_host.h"
#include "content/browser/renderer_host/dip_util.h"
#include "content/browser/renderer_host/input/motion_event_web.h"
#include "content/browser/renderer_host/input/synthetic_gesture_target_base.h"
#include "content/browser/renderer_host/render_widget_host_delegate.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_input_event_router.h"
#include "content/common/content_switches_internal.h"
#include "content/common/input_messages.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_task_traits.h"
......@@ -44,6 +47,9 @@
#include "content/public/common/content_switches.h"
#include "media/base/video_frame.h"
#include "ui/compositor/compositor_vsync_manager.h"
#include "ui/events/blink/blink_event_util.h"
#include "ui/events/gesture_detection/gesture_provider_config_helper.h"
#include "ui/events/gesture_detection/motion_event.h"
#include "ui/gfx/geometry/dip_util.h"
#include "ui/gfx/geometry/size_conversions.h"
......@@ -165,6 +171,23 @@ class CefDelegatedFrameHostClient : public content::DelegatedFrameHostClient {
#endif // !defined(OS_MACOSX)
ui::GestureProvider::Config CreateGestureProviderConfig() {
ui::GestureProvider::Config config = ui::GetGestureProviderConfig(
ui::GestureProviderConfigType::CURRENT_PLATFORM);
return config;
}
ui::LatencyInfo CreateLatencyInfo(const blink::WebInputEvent& event) {
ui::LatencyInfo latency_info;
// The latency number should only be added if the timestamp is valid.
base::TimeTicks time = event.TimeStamp();
if (!time.is_null()) {
latency_info.AddLatencyNumberWithTimestamp(
ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, time, 1);
}
return latency_info;
}