mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	Add multi-touch support for OSR (issue #1059)
This commit is contained in:
		
				
					committed by
					
						 Marshall Greenblatt
						Marshall Greenblatt
					
				
			
			
				
	
			
			
			
						parent
						
							9ba28dd730
						
					
				
				
					commit
					5f615a95bc
				
			
							
								
								
									
										3
									
								
								BUILD.gn
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								BUILD.gn
									
									
									
									
									
								
							| @@ -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; | ||||
|   | ||||
							
								
								
									
										218
									
								
								libcef/browser/osr/motion_event_osr.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								libcef/browser/osr/motion_event_osr.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,218 @@ | ||||
| // 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; | ||||
| } | ||||
							
								
								
									
										59
									
								
								libcef/browser/osr/motion_event_osr.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								libcef/browser/osr/motion_event_osr.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| // 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; | ||||
| } | ||||
|  | ||||
| }  // namespace | ||||
|  | ||||
| // Used for managing copy requests when GPU compositing is enabled. Based on | ||||
| @@ -327,8 +350,11 @@ CefRenderWidgetHostViewOSR::CefRenderWidgetHostViewOSR( | ||||
|       child_host_view_(NULL), | ||||
|       is_showing_(!render_widget_host_->is_hidden()), | ||||
|       is_destroyed_(false), | ||||
|       pinch_zoom_enabled_(content::IsPinchToZoomEnabled()), | ||||
|       is_scroll_offset_changed_pending_(false), | ||||
|       mouse_wheel_phase_handler_(this), | ||||
|       gesture_provider_(CreateGestureProviderConfig(), this), | ||||
|       forward_touch_to_popup_(false), | ||||
|       weak_ptr_factory_(this) { | ||||
|   DCHECK(render_widget_host_); | ||||
|   DCHECK(!render_widget_host_->GetView()); | ||||
| @@ -399,6 +425,12 @@ CefRenderWidgetHostViewOSR::CefRenderWidgetHostViewOSR( | ||||
|  | ||||
|   if (GetTextInputManager()) | ||||
|     GetTextInputManager()->AddObserver(this); | ||||
|  | ||||
|   if (render_widget_host_->delegate() && | ||||
|       render_widget_host_->delegate()->GetInputEventRouter()) { | ||||
|     render_widget_host_->delegate()->GetInputEventRouter()->AddFrameSinkIdOwner( | ||||
|         GetFrameSinkId(), this); | ||||
|   } | ||||
| } | ||||
|  | ||||
| CefRenderWidgetHostViewOSR::~CefRenderWidgetHostViewOSR() { | ||||
| @@ -855,7 +887,13 @@ content::CursorManager* CefRenderWidgetHostViewOSR::GetCursorManager() { | ||||
|   return cursor_manager_.get(); | ||||
| } | ||||
|  | ||||
| void CefRenderWidgetHostViewOSR::SetIsLoading(bool is_loading) {} | ||||
| void CefRenderWidgetHostViewOSR::SetIsLoading(bool is_loading) { | ||||
|   if (!is_loading) | ||||
|     return; | ||||
|   // Make sure gesture detection is fresh. | ||||
|   gesture_provider_.ResetDetection(); | ||||
|   forward_touch_to_popup_ = false; | ||||
| } | ||||
|  | ||||
| void CefRenderWidgetHostViewOSR::RenderProcessGone( | ||||
|     base::TerminationStatus status, | ||||
| @@ -1085,6 +1123,14 @@ const viz::FrameSinkId& CefRenderWidgetHostViewOSR::GetFrameSinkId() const { | ||||
|   return GetDelegatedFrameHost()->frame_sink_id(); | ||||
| } | ||||
|  | ||||
| viz::FrameSinkId CefRenderWidgetHostViewOSR::GetRootFrameSinkId() { | ||||
| #if defined(OS_MACOSX) | ||||
|   return browser_compositor_->GetRootFrameSinkId(); | ||||
| #else | ||||
|   return compositor_->frame_sink_id(); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| std::unique_ptr<content::SyntheticGestureTarget> | ||||
| CefRenderWidgetHostViewOSR::CreateSyntheticGestureTarget() { | ||||
|   // TODO(cef): This is likely incorrect for OOPIF. | ||||
| @@ -1412,6 +1458,78 @@ void CefRenderWidgetHostViewOSR::SendMouseWheelEvent( | ||||
|   } | ||||
| } | ||||
|  | ||||
| void CefRenderWidgetHostViewOSR::SendTouchEvent(const CefTouchEvent& event) { | ||||
|   TRACE_EVENT0("cef", "CefRenderWidgetHostViewOSR::SendTouchEvent"); | ||||
|  | ||||
|   if (!IsPopupWidget() && popup_host_view_) { | ||||
|     if (!forward_touch_to_popup_ && event.type == CEF_TET_PRESSED && | ||||
|         pointer_state_.GetPointerCount() == 0) { | ||||
|       forward_touch_to_popup_ = | ||||
|           popup_host_view_->popup_position_.Contains(event.x, event.y); | ||||
|     } | ||||
|  | ||||
|     if (forward_touch_to_popup_) { | ||||
|       CefTouchEvent popup_event(event); | ||||
|       popup_event.x -= popup_host_view_->popup_position_.x(); | ||||
|       popup_event.y -= popup_host_view_->popup_position_.y(); | ||||
|       popup_host_view_->SendTouchEvent(popup_event); | ||||
|       return; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Update the touch event first. | ||||
|   if (!pointer_state_.OnTouch(event)) | ||||
|     return; | ||||
|  | ||||
|   ui::FilteredGestureProvider::TouchHandlingResult result = | ||||
|       gesture_provider_.OnTouchEvent(pointer_state_); | ||||
|  | ||||
|   blink::WebTouchEvent touch_event = ui::CreateWebTouchEventFromMotionEvent( | ||||
|       pointer_state_, result.moved_beyond_slop_region, false); | ||||
|  | ||||
|   pointer_state_.CleanupRemovedTouchPoints(event); | ||||
|  | ||||
|   // Set unchanged touch point to StateStationary for touchmove and | ||||
|   // touchcancel to make sure only send one ack per WebTouchEvent. | ||||
|   if (!result.succeeded) | ||||
|     pointer_state_.MarkUnchangedTouchPointsAsStationary(&touch_event, event); | ||||
|  | ||||
|   if (!render_widget_host_) | ||||
|     return; | ||||
|  | ||||
|   ui::LatencyInfo latency_info = CreateLatencyInfo(touch_event); | ||||
|   if (ShouldRouteEvents()) { | ||||
|     render_widget_host_->delegate()->GetInputEventRouter()->RouteTouchEvent( | ||||
|         this, &touch_event, latency_info); | ||||
|   } else { | ||||
|     render_widget_host_->ForwardTouchEventWithLatencyInfo(touch_event, | ||||
|                                                           latency_info); | ||||
|   } | ||||
|  | ||||
|   bool touch_end = touch_event.GetType() == blink::WebInputEvent::kTouchEnd || | ||||
|                    touch_event.GetType() == blink::WebInputEvent::kTouchCancel; | ||||
|  | ||||
|   if (touch_end && IsPopupWidget() && parent_host_view_ && | ||||
|       parent_host_view_->popup_host_view_ == this) { | ||||
|     parent_host_view_->forward_touch_to_popup_ = false; | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool CefRenderWidgetHostViewOSR::ShouldRouteEvents() const { | ||||
|   if (!render_widget_host_->delegate()) | ||||
|     return false; | ||||
|  | ||||
|   // Do not route events that are currently targeted to page popups such as | ||||
|   // <select> element drop-downs, since these cannot contain cross-process | ||||
|   // frames. | ||||
|   if (!render_widget_host_->delegate()->IsWidgetForMainFrame( | ||||
|           render_widget_host_)) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   return !!render_widget_host_->delegate()->GetInputEventRouter(); | ||||
| } | ||||
|  | ||||
| void CefRenderWidgetHostViewOSR::SendFocusEvent(bool focus) { | ||||
|   if (!render_widget_host_) | ||||
|     return; | ||||
| @@ -1455,6 +1573,41 @@ void CefRenderWidgetHostViewOSR::OnUpdateTextInputStateCalled( | ||||
|   handler->OnVirtualKeyboardRequested(browser_impl_->GetBrowser(), mode); | ||||
| } | ||||
|  | ||||
| void CefRenderWidgetHostViewOSR::ProcessAckedTouchEvent( | ||||
|     const content::TouchEventWithLatencyInfo& touch, | ||||
|     content::InputEventAckState ack_result) { | ||||
|   const bool event_consumed = | ||||
|       ack_result == content::INPUT_EVENT_ACK_STATE_CONSUMED; | ||||
|   gesture_provider_.OnTouchEventAck(touch.event.unique_touch_event_id, | ||||
|                                     event_consumed, false); | ||||
| } | ||||
|  | ||||
| void CefRenderWidgetHostViewOSR::OnGestureEvent( | ||||
|     const ui::GestureEventData& gesture) { | ||||
|   if ((gesture.type() == ui::ET_GESTURE_PINCH_BEGIN || | ||||
|        gesture.type() == ui::ET_GESTURE_PINCH_UPDATE || | ||||
|        gesture.type() == ui::ET_GESTURE_PINCH_END) && | ||||
|       !pinch_zoom_enabled_) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   blink::WebGestureEvent web_event = | ||||
|       ui::CreateWebGestureEventFromGestureEventData(gesture); | ||||
|  | ||||
|   // without this check, forwarding gestures does not work! | ||||
|   if (web_event.GetType() == blink::WebInputEvent::kUndefined) | ||||
|     return; | ||||
|  | ||||
|   ui::LatencyInfo latency_info = CreateLatencyInfo(web_event); | ||||
|   if (ShouldRouteEvents()) { | ||||
|     render_widget_host_->delegate()->GetInputEventRouter()->RouteGestureEvent( | ||||
|         this, &web_event, latency_info); | ||||
|   } else { | ||||
|     render_widget_host_->ForwardGestureEventWithLatencyInfo(web_event, | ||||
|                                                             latency_info); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void CefRenderWidgetHostViewOSR::UpdateFrameRate() { | ||||
|   frame_rate_threshold_us_ = 0; | ||||
|   SetFrameRate(); | ||||
| @@ -1684,6 +1837,9 @@ void CefRenderWidgetHostViewOSR::SendBeginFrame(base::TimeTicks frame_time, | ||||
|   DCHECK(begin_frame_args.IsValid()); | ||||
|   begin_frame_number_++; | ||||
|  | ||||
|   if (render_widget_host_) | ||||
|     render_widget_host_->ProgressFlingIfNeeded(frame_time); | ||||
|  | ||||
|   if (renderer_compositor_frame_sink_) { | ||||
|     renderer_compositor_frame_sink_->OnBeginFrame(begin_frame_args, {}); | ||||
|   } | ||||
|   | ||||
| @@ -14,6 +14,8 @@ | ||||
| #include "include/cef_base.h" | ||||
| #include "include/cef_browser.h" | ||||
|  | ||||
| #include "libcef/browser/osr/motion_event_osr.h" | ||||
|  | ||||
| #include "base/memory/weak_ptr.h" | ||||
| #include "build/build_config.h" | ||||
| #include "components/viz/common/frame_sinks/begin_frame_source.h" | ||||
| @@ -24,6 +26,10 @@ | ||||
| #include "content/public/common/widget_type.h" | ||||
| #include "ui/compositor/compositor.h" | ||||
| #include "ui/compositor/external_begin_frame_client.h" | ||||
| #include "ui/events/base_event_utils.h" | ||||
| #include "ui/events/gesture_detection/filtered_gesture_provider.h" | ||||
| #include "ui/events/gesture_detection/gesture_configuration.h" | ||||
| #include "ui/events/gesture_detection/motion_event_generic.h" | ||||
| #include "ui/gfx/geometry/rect.h" | ||||
|  | ||||
| #if defined(OS_LINUX) | ||||
| @@ -98,7 +104,8 @@ class MacHelper; | ||||
| class CefRenderWidgetHostViewOSR : public content::RenderWidgetHostViewBase, | ||||
|                                    public ui::ExternalBeginFrameClient, | ||||
|                                    public ui::CompositorDelegate, | ||||
|                                    public content::TextInputManager::Observer { | ||||
|                                    public content::TextInputManager::Observer, | ||||
|                                    public ui::GestureProviderClient { | ||||
|  public: | ||||
|   CefRenderWidgetHostViewOSR(SkColor background_color, | ||||
|                              bool use_shared_texture, | ||||
| @@ -198,6 +205,7 @@ class CefRenderWidgetHostViewOSR : public content::RenderWidgetHostViewBase, | ||||
|   const viz::LocalSurfaceIdAllocation& GetLocalSurfaceIdAllocation() | ||||
|       const override; | ||||
|   const viz::FrameSinkId& GetFrameSinkId() const override; | ||||
|   viz::FrameSinkId GetRootFrameSinkId() override; | ||||
|  | ||||
|   // ui::ExternalBeginFrameClient implementation: | ||||
|   void OnDisplayDidFinishFrame(const viz::BeginFrameAck& ack) override; | ||||
| @@ -213,6 +221,11 @@ class CefRenderWidgetHostViewOSR : public content::RenderWidgetHostViewBase, | ||||
|       RenderWidgetHostViewBase* updated_view, | ||||
|       bool did_update_state) override; | ||||
|  | ||||
|   // ui::GestureProviderClient implementation. | ||||
|   void ProcessAckedTouchEvent(const content::TouchEventWithLatencyInfo& touch, | ||||
|                               content::InputEventAckState ack_result) override; | ||||
|   void OnGestureEvent(const ui::GestureEventData& gesture) override; | ||||
|  | ||||
|   bool InstallTransparency(); | ||||
|  | ||||
|   void SynchronizeVisualProperties(); | ||||
| @@ -222,6 +235,8 @@ class CefRenderWidgetHostViewOSR : public content::RenderWidgetHostViewBase, | ||||
|   void SendKeyEvent(const content::NativeWebKeyboardEvent& event); | ||||
|   void SendMouseEvent(const blink::WebMouseEvent& event); | ||||
|   void SendMouseWheelEvent(const blink::WebMouseWheelEvent& event); | ||||
|   void SendTouchEvent(const CefTouchEvent& event); | ||||
|   bool ShouldRouteEvents() const; | ||||
|   void SendFocusEvent(bool focus); | ||||
|   void UpdateFrameRate(); | ||||
|  | ||||
| @@ -253,6 +268,8 @@ class CefRenderWidgetHostViewOSR : public content::RenderWidgetHostViewBase, | ||||
|   } | ||||
|  | ||||
|   void set_popup_host_view(CefRenderWidgetHostViewOSR* popup_view) { | ||||
|     if (popup_view != popup_host_view_) | ||||
|       forward_touch_to_popup_ = false; | ||||
|     popup_host_view_ = popup_view; | ||||
|   } | ||||
|   void set_child_host_view(CefRenderWidgetHostViewOSR* popup_view) { | ||||
| @@ -354,8 +371,8 @@ class CefRenderWidgetHostViewOSR : public content::RenderWidgetHostViewBase, | ||||
|   std::unique_ptr<content::BrowserCompositorMac> browser_compositor_; | ||||
|   MacHelper* mac_helper_; | ||||
| #elif defined(USE_X11) | ||||
|   CefWindowX11* window_; | ||||
|   std::unique_ptr<ui::XScopedCursor> invisible_cursor_; | ||||
| CefWindowX11* window_; | ||||
| std::unique_ptr<ui::XScopedCursor> invisible_cursor_; | ||||
| #endif | ||||
|  | ||||
|   std::unique_ptr<content::CursorManager> cursor_manager_; | ||||
| @@ -401,6 +418,10 @@ class CefRenderWidgetHostViewOSR : public content::RenderWidgetHostViewBase, | ||||
|   base::Lock damage_rect_lock_; | ||||
|   std::map<uint32_t, gfx::Rect> damage_rects_; | ||||
|  | ||||
|   // Whether pinch-to-zoom should be enabled and pinch events forwarded to the | ||||
|   // renderer. | ||||
|   bool pinch_zoom_enabled_; | ||||
|  | ||||
|   // The last scroll offset of the view. | ||||
|   gfx::Vector2dF last_scroll_offset_; | ||||
|   bool is_scroll_offset_changed_pending_; | ||||
| @@ -415,6 +436,12 @@ class CefRenderWidgetHostViewOSR : public content::RenderWidgetHostViewBase, | ||||
|   // EnsureSurfaceSynchronizedForLayoutTest(). | ||||
|   uint32_t latest_capture_sequence_number_ = 0u; | ||||
|  | ||||
|   // ui::GestureProviderClient implementation. | ||||
|   ui::FilteredGestureProvider gesture_provider_; | ||||
|  | ||||
|   CefMotionEventOSR pointer_state_; | ||||
|   bool forward_touch_to_popup_; | ||||
|  | ||||
|   base::WeakPtrFactory<CefRenderWidgetHostViewOSR> weak_ptr_factory_; | ||||
|  | ||||
|   DISALLOW_COPY_AND_ASSIGN(CefRenderWidgetHostViewOSR); | ||||
|   | ||||
| @@ -204,6 +204,9 @@ void CefBrowserPlatformDelegateViews::SendMouseWheelEvent( | ||||
|     host->GetWidget()->ForwardWheelEvent(event); | ||||
| } | ||||
|  | ||||
| void CefBrowserPlatformDelegateViews::SendTouchEvent( | ||||
|     const CefTouchEvent& event) {} | ||||
|  | ||||
| void CefBrowserPlatformDelegateViews::SendFocusEvent(bool setFocus) { | ||||
|   // Will result in a call to WebContents::Focus(). | ||||
|   if (setFocus && browser_view_->root_view()) | ||||
|   | ||||
| @@ -48,6 +48,7 @@ class CefBrowserPlatformDelegateViews | ||||
|   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; | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| // implementations. See the translator.README.txt file in the tools directory | ||||
| // for more information. | ||||
| // | ||||
| // $hash=db01cea0d5a144e6998c6f897df832b992034646$ | ||||
| // $hash=48d8640bfbc9fede99ac411c163b2717ad84d373$ | ||||
| // | ||||
|  | ||||
| #include "libcef_dll/cpptoc/browser_host_cpptoc.h" | ||||
| @@ -801,6 +801,30 @@ browser_host_send_mouse_wheel_event(struct _cef_browser_host_t* self, | ||||
|                                                        deltaY); | ||||
| } | ||||
|  | ||||
| void CEF_CALLBACK | ||||
| browser_host_send_touch_event(struct _cef_browser_host_t* self, | ||||
|                               const struct _cef_touch_event_t* event) { | ||||
|   shutdown_checker::AssertNotShutdown(); | ||||
|  | ||||
|   // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING | ||||
|  | ||||
|   DCHECK(self); | ||||
|   if (!self) | ||||
|     return; | ||||
|   // Verify param: event; type: struct_byref_const | ||||
|   DCHECK(event); | ||||
|   if (!event) | ||||
|     return; | ||||
|  | ||||
|   // Translate param: event; type: struct_byref_const | ||||
|   CefTouchEvent eventObj; | ||||
|   if (event) | ||||
|     eventObj.Set(*event, false); | ||||
|  | ||||
|   // Execute | ||||
|   CefBrowserHostCppToC::Get(self)->SendTouchEvent(eventObj); | ||||
| } | ||||
|  | ||||
| void CEF_CALLBACK | ||||
| browser_host_send_focus_event(struct _cef_browser_host_t* self, int setFocus) { | ||||
|   shutdown_checker::AssertNotShutdown(); | ||||
| @@ -1276,6 +1300,7 @@ CefBrowserHostCppToC::CefBrowserHostCppToC() { | ||||
|   GetStruct()->send_mouse_click_event = browser_host_send_mouse_click_event; | ||||
|   GetStruct()->send_mouse_move_event = browser_host_send_mouse_move_event; | ||||
|   GetStruct()->send_mouse_wheel_event = browser_host_send_mouse_wheel_event; | ||||
|   GetStruct()->send_touch_event = browser_host_send_touch_event; | ||||
|   GetStruct()->send_focus_event = browser_host_send_focus_event; | ||||
|   GetStruct()->send_capture_lost_event = browser_host_send_capture_lost_event; | ||||
|   GetStruct()->notify_move_or_resize_started = | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| // implementations. See the translator.README.txt file in the tools directory | ||||
| // for more information. | ||||
| // | ||||
| // $hash=5774cd86b6aa84a3e3e4982d578200c11fdde391$ | ||||
| // $hash=1357aad2c8a79a3917ca38d93f3ed84712c2d9cd$ | ||||
| // | ||||
|  | ||||
| #include "libcef_dll/ctocpp/browser_host_ctocpp.h" | ||||
| @@ -699,6 +699,20 @@ void CefBrowserHostCToCpp::SendMouseWheelEvent(const CefMouseEvent& event, | ||||
|   _struct->send_mouse_wheel_event(_struct, &event, deltaX, deltaY); | ||||
| } | ||||
|  | ||||
| NO_SANITIZE("cfi-icall") | ||||
| void CefBrowserHostCToCpp::SendTouchEvent(const CefTouchEvent& event) { | ||||
|   shutdown_checker::AssertNotShutdown(); | ||||
|  | ||||
|   cef_browser_host_t* _struct = GetStruct(); | ||||
|   if (CEF_MEMBER_MISSING(_struct, send_touch_event)) | ||||
|     return; | ||||
|  | ||||
|   // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING | ||||
|  | ||||
|   // Execute | ||||
|   _struct->send_touch_event(_struct, &event); | ||||
| } | ||||
|  | ||||
| NO_SANITIZE("cfi-icall") | ||||
| void CefBrowserHostCToCpp::SendFocusEvent(bool setFocus) { | ||||
|   shutdown_checker::AssertNotShutdown(); | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| // implementations. See the translator.README.txt file in the tools directory | ||||
| // for more information. | ||||
| // | ||||
| // $hash=14cebadd19516a54e746345f42adc90fbeed0a2d$ | ||||
| // $hash=d9a6d69ef996bfb961d0b675fe37e5a2b23b12cc$ | ||||
| // | ||||
|  | ||||
| #ifndef CEF_LIBCEF_DLL_CTOCPP_BROWSER_HOST_CTOCPP_H_ | ||||
| @@ -97,6 +97,7 @@ class CefBrowserHostCToCpp : public CefCToCppRefCounted<CefBrowserHostCToCpp, | ||||
|   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; | ||||
|   | ||||
| @@ -33,6 +33,19 @@ index fc252b6ceffd..b0dfd847ec03 100644 | ||||
|   public: | ||||
|    MouseWheelPhaseHandler(RenderWidgetHostViewBase* const host_view); | ||||
|    ~MouseWheelPhaseHandler() {} | ||||
| diff --git content/common/content_switches_internal.h content/common/content_switches_internal.h | ||||
| index 886bdf0edf8f..1d714000cce5 100644 | ||||
| --- content/common/content_switches_internal.h | ||||
| +++ content/common/content_switches_internal.h | ||||
| @@ -15,7 +15,7 @@ class CommandLine; | ||||
|   | ||||
|  namespace content { | ||||
|   | ||||
| -bool IsPinchToZoomEnabled(); | ||||
| +CONTENT_EXPORT bool IsPinchToZoomEnabled(); | ||||
|   | ||||
|  blink::mojom::V8CacheOptions GetV8CacheOptions(); | ||||
|   | ||||
| diff --git content/browser/renderer_host/input/synthetic_gesture_target_base.h content/browser/renderer_host/input/synthetic_gesture_target_base.h | ||||
| index 53476354343e..fd1a7a24455b 100644 | ||||
| --- content/browser/renderer_host/input/synthetic_gesture_target_base.h | ||||
|   | ||||
| @@ -15,6 +15,7 @@ | ||||
| #define XK_3270  // for XK_3270_BackTab | ||||
| #include <X11/XF86keysym.h> | ||||
| #include <X11/Xcursor/Xcursor.h> | ||||
| #include <X11/extensions/XInput2.h> | ||||
| #include <X11/keysym.h> | ||||
|  | ||||
| #include "include/base/cef_logging.h" | ||||
| @@ -28,6 +29,17 @@ namespace client { | ||||
|  | ||||
| namespace { | ||||
|  | ||||
| // Major opcode of XInputExtension, or -1 if XInput 2.2 is not available. | ||||
| int g_xinput_extension = -1; | ||||
|  | ||||
| // Static BrowserWindowOsrGtk::EventFilter needs to forward touch events | ||||
| // to correct browser, so we maintain a vector of all windows. | ||||
| std::vector<BrowserWindowOsrGtk*> g_browser_windows; | ||||
|  | ||||
| bool IsTouchAvailable() { | ||||
|   return g_xinput_extension != -1; | ||||
| } | ||||
|  | ||||
| int GetCefStateModifiers(guint state) { | ||||
|   int modifiers = 0; | ||||
|   if (state & GDK_SHIFT_MASK) | ||||
| @@ -47,6 +59,20 @@ int GetCefStateModifiers(guint state) { | ||||
|   return modifiers; | ||||
| } | ||||
|  | ||||
| int GetCefStateModifiers(XIModifierState mods, XIButtonState buttons) { | ||||
|   guint state = mods.effective; | ||||
|   if (buttons.mask_len >= 1) { | ||||
|     if (XIMaskIsSet(buttons.mask, 1)) | ||||
|       state |= GDK_BUTTON1_MASK; | ||||
|     if (XIMaskIsSet(buttons.mask, 2)) | ||||
|       state |= GDK_BUTTON2_MASK; | ||||
|     if (XIMaskIsSet(buttons.mask, 3)) | ||||
|       state |= GDK_BUTTON3_MASK; | ||||
|   } | ||||
|  | ||||
|   return GetCefStateModifiers(state); | ||||
| } | ||||
|  | ||||
| // From ui/events/keycodes/keyboard_codes_posix.h. | ||||
| enum KeyboardCode { | ||||
|   VKEY_BACK = 0x08, | ||||
| @@ -948,9 +974,12 @@ BrowserWindowOsrGtk::BrowserWindowOsrGtk(BrowserWindow::Delegate* delegate, | ||||
|       drag_drop_(false), | ||||
|       device_scale_factor_(1.0f) { | ||||
|   client_handler_ = new ClientHandlerOsr(this, this, startup_url); | ||||
|   g_browser_windows.push_back(this); | ||||
| } | ||||
|  | ||||
| BrowserWindowOsrGtk::~BrowserWindowOsrGtk() { | ||||
|   g_browser_windows.erase( | ||||
|       std::find(g_browser_windows.begin(), g_browser_windows.end(), this)); | ||||
|   ScopedGdkThreadsEnter scoped_gdk_threads; | ||||
|  | ||||
|   if (drag_trigger_event_) { | ||||
| @@ -1387,6 +1416,11 @@ void BrowserWindowOsrGtk::Create(ClientWindowHandle parent_handle) { | ||||
|  | ||||
|   // Make the GlArea visible in the parent container. | ||||
|   gtk_widget_show_all(parent_handle); | ||||
|  | ||||
|   InitializeXinput(xdisplay_); | ||||
|  | ||||
|   if (IsTouchAvailable()) | ||||
|     RegisterTouch(); | ||||
| } | ||||
|  | ||||
| // static | ||||
| @@ -1640,6 +1674,108 @@ gint BrowserWindowOsrGtk::FocusEvent(GtkWidget* widget, | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| void BrowserWindowOsrGtk::TouchEvent(CefXIDeviceEvent event) { | ||||
|   if (!browser_.get()) | ||||
|     return; | ||||
|  | ||||
|   XIDeviceEvent* ev = static_cast<XIDeviceEvent*>(event); | ||||
|   CefTouchEvent cef_event; | ||||
|   switch (ev->evtype) { | ||||
|     case XI_TouchBegin: | ||||
|       cef_event.type = CEF_TET_PRESSED; | ||||
|       break; | ||||
|     case XI_TouchUpdate: | ||||
|       cef_event.type = CEF_TET_MOVED; | ||||
|       break; | ||||
|     case XI_TouchEnd: | ||||
|       cef_event.type = CEF_TET_RELEASED; | ||||
|       break; | ||||
|     default: | ||||
|       return; | ||||
|   } | ||||
|  | ||||
|   cef_event.id = ev->detail; | ||||
|   cef_event.x = ev->event_x; | ||||
|   cef_event.y = ev->event_y; | ||||
|   cef_event.radius_x = 0; | ||||
|   cef_event.radius_y = 0; | ||||
|   cef_event.rotation_angle = 0; | ||||
|   cef_event.pressure = 0; | ||||
|   cef_event.modifiers = GetCefStateModifiers(ev->mods, ev->buttons); | ||||
|  | ||||
|   browser_->GetHost()->SendTouchEvent(cef_event); | ||||
| } | ||||
|  | ||||
| void BrowserWindowOsrGtk::RegisterTouch() { | ||||
|   GdkWindow* glwindow = gtk_widget_get_window(glarea_); | ||||
|   ::Window xwindow = GDK_WINDOW_XID(glwindow); | ||||
|   uint32_t bitMask = XI_TouchBeginMask | XI_TouchUpdateMask | XI_TouchEndMask; | ||||
|  | ||||
|   XIEventMask mask; | ||||
|   mask.deviceid = XIAllMasterDevices; | ||||
|   mask.mask = reinterpret_cast<unsigned char*>(&bitMask); | ||||
|   mask.mask_len = sizeof(bitMask); | ||||
|   XISelectEvents(xdisplay_, xwindow, &mask, 1); | ||||
| } | ||||
|  | ||||
| // static | ||||
| GdkFilterReturn BrowserWindowOsrGtk::EventFilter(GdkXEvent* gdk_xevent, | ||||
|                                                  GdkEvent* event, | ||||
|                                                  gpointer data) { | ||||
|   XEvent* xevent = static_cast<XEvent*>(gdk_xevent); | ||||
|   if (xevent->type == GenericEvent && | ||||
|       xevent->xgeneric.extension == g_xinput_extension) { | ||||
|     XGetEventData(xevent->xcookie.display, &xevent->xcookie); | ||||
|     XIDeviceEvent* ev = static_cast<XIDeviceEvent*>(xevent->xcookie.data); | ||||
|  | ||||
|     if (!ev) | ||||
|       return GDK_FILTER_REMOVE; | ||||
|  | ||||
|     for (BrowserWindowOsrGtk* browser_window : g_browser_windows) { | ||||
|       GtkWidget* widget = browser_window->GetWindowHandle(); | ||||
|       ::Window xwindow = GDK_WINDOW_XID(gtk_widget_get_window(widget)); | ||||
|       if (xwindow == ev->event) { | ||||
|         browser_window->TouchEvent(ev); | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     XFreeEventData(xevent->xcookie.display, &xevent->xcookie); | ||||
|     // Even if we didn't find a consumer for this event, we will make sure Gdk | ||||
|     // doesn't attempt to process the event, since it can't parse GenericEvents | ||||
|     return GDK_FILTER_REMOVE; | ||||
|   } | ||||
|  | ||||
|   return GDK_FILTER_CONTINUE; | ||||
| } | ||||
|  | ||||
| // static | ||||
| void BrowserWindowOsrGtk::InitializeXinput(XDisplay* xdisplay) { | ||||
|   static bool initialized = false; | ||||
|   if (initialized) | ||||
|     return; | ||||
|   initialized = true; | ||||
|  | ||||
|   int firstEvent, firstError; | ||||
|   if (XQueryExtension(xdisplay, "XInputExtension", &g_xinput_extension, | ||||
|                       &firstEvent, &firstError)) { | ||||
|     int major = 2, minor = 2; | ||||
|     // X Input Extension 2.2 is needed for multitouch events. | ||||
|     if (XIQueryVersion(xdisplay, &major, &minor) == Success) { | ||||
|       // Ideally we would add an event filter for each glarea_ window | ||||
|       // separately, but unfortunately GDK can't parse X GenericEvents | ||||
|       // which have the target window stored in different way compared | ||||
|       // to other X events. That is why we add this global event filter | ||||
|       // just once, and dispatch the event to correct BrowserWindowOsrGtk | ||||
|       // manually. | ||||
|       gdk_window_add_filter(nullptr, &BrowserWindowOsrGtk::EventFilter, | ||||
|                             nullptr); | ||||
|     } else { | ||||
|       g_xinput_extension = -1; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool BrowserWindowOsrGtk::IsOverPopupWidget(int x, int y) const { | ||||
|   const CefRect& rc = renderer_.popup_rect(); | ||||
|   int popup_right = rc.x + rc.width; | ||||
|   | ||||
| @@ -20,6 +20,8 @@ namespace client { | ||||
| class BrowserWindowOsrGtk : public BrowserWindow, | ||||
|                             public ClientHandlerOsr::OsrDelegate { | ||||
|  public: | ||||
|   typedef void* CefXIDeviceEvent; | ||||
|  | ||||
|   // Constructor may be called on any thread. | ||||
|   // |delegate| must outlive this object. | ||||
|   BrowserWindowOsrGtk(BrowserWindow::Delegate* delegate, | ||||
| @@ -114,6 +116,9 @@ class BrowserWindowOsrGtk : public BrowserWindow, | ||||
|                          GdkEventFocus* event, | ||||
|                          BrowserWindowOsrGtk* self); | ||||
|  | ||||
|   void TouchEvent(CefXIDeviceEvent event); | ||||
|   void RegisterTouch(); | ||||
|  | ||||
|   bool IsOverPopupWidget(int x, int y) const; | ||||
|   int GetPopupXOffset() const; | ||||
|   int GetPopupYOffset() const; | ||||
| @@ -166,6 +171,10 @@ class BrowserWindowOsrGtk : public BrowserWindow, | ||||
|                                guint info, | ||||
|                                guint time, | ||||
|                                BrowserWindowOsrGtk* self); | ||||
|   static GdkFilterReturn EventFilter(GdkXEvent* gdk_xevent, | ||||
|                                      GdkEvent* event, | ||||
|                                      gpointer data); | ||||
|   static void InitializeXinput(XDisplay* xdisplay); | ||||
|  | ||||
|   XDisplay* xdisplay_; | ||||
|  | ||||
|   | ||||
| @@ -33,7 +33,7 @@ CefTextInputClientOSRMac* GetInputClientFromContext( | ||||
| }  // namespace | ||||
|  | ||||
| @interface BrowserOpenGLView | ||||
|     : NSOpenGLView<NSDraggingSource, NSDraggingDestination, NSAccessibility> { | ||||
|     : NSOpenGLView <NSDraggingSource, NSDraggingDestination, NSAccessibility> { | ||||
|  @private | ||||
|   NSTrackingArea* tracking_area_; | ||||
|   client::BrowserWindowOsrMac* browser_window_; | ||||
| @@ -238,8 +238,8 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) { | ||||
|   point = [self convertPointToBackingInternal:point]; | ||||
|  | ||||
|   if (!isUp) { | ||||
|     was_last_mouse_down_on_view_ = | ||||
|         ![self isOverPopupWidgetX:point.x andY:point.y]; | ||||
|     was_last_mouse_down_on_view_ = ![self isOverPopupWidgetX:point.x | ||||
|                                                         andY:point.y]; | ||||
|   } else if (was_last_mouse_down_on_view_ && | ||||
|              [self isOverPopupWidgetX:point.x andY:point.y] && | ||||
|              ([self getPopupXOffset] || [self getPopupYOffset])) { | ||||
| @@ -359,6 +359,110 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) { | ||||
|       [client HandleKeyEventAfterTextInputClient:keyEvent]; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Check for Caps lock and Toggle Touch Emulation | ||||
|   if (client::MainContext::Get()->TouchEventsEnabled()) | ||||
|     [self toggleTouchEmulation:event]; | ||||
| } | ||||
|  | ||||
| // OSX does not have touch screens, so we emulate it by mapping multitouch | ||||
| // events on TrackPad to Touch Events on Screen. To ensure it does not | ||||
| // interfere with other Trackpad events, this mapping is only enabled if | ||||
| // touch-events=enabled commandline is passed and caps lock key is on. | ||||
| - (void)toggleTouchEmulation:(NSEvent*)event { | ||||
|   if ([event type] == NSFlagsChanged && [event keyCode] == 0x39) { | ||||
|     NSUInteger flags = [event modifierFlags]; | ||||
|     BOOL touch_enabled = flags & NSAlphaShiftKeyMask ? YES : NO; | ||||
|     [self setAcceptsTouchEvents:touch_enabled]; | ||||
|   } | ||||
| } | ||||
|  | ||||
| - (cef_touch_event_type_t)getTouchPhase:(NSTouchPhase)phase { | ||||
|   cef_touch_event_type_t event_type = CEF_TET_RELEASED; | ||||
|   switch (phase) { | ||||
|     case NSTouchPhaseBegan: | ||||
|       event_type = CEF_TET_PRESSED; | ||||
|       break; | ||||
|     case NSTouchPhaseMoved: | ||||
|       event_type = CEF_TET_MOVED; | ||||
|       break; | ||||
|     case NSTouchPhaseEnded: | ||||
|       event_type = CEF_TET_RELEASED; | ||||
|       break; | ||||
|     case NSTouchPhaseCancelled: | ||||
|       event_type = CEF_TET_CANCELLED; | ||||
|       break; | ||||
|     default: | ||||
|       break; | ||||
|   } | ||||
|   return event_type; | ||||
| } | ||||
|  | ||||
| // Translate NSTouch events to CefTouchEvents and send to browser. | ||||
| - (void)sendTouchEvent:(NSEvent*)event touchPhase:(NSTouchPhase)phase { | ||||
|   int modifiers = [self getModifiersForEvent:event]; | ||||
|   CefRefPtr<CefBrowser> browser = [self getBrowser]; | ||||
|  | ||||
|   NSSet* touches = [event touchesMatchingPhase:phase inView:self]; | ||||
|  | ||||
|   for (NSTouch* touch in touches) { | ||||
|     // Convert NSTouch to CefTouchEvent. | ||||
|     CefTouchEvent touch_event; | ||||
|  | ||||
|     // NSTouch.identity is unique during the life of the touch | ||||
|     touch_event.id = touch.identity.hash; | ||||
|     touch_event.type = [self getTouchPhase:phase]; | ||||
|  | ||||
|     NSPoint scaled_pos = [touch normalizedPosition]; | ||||
|     NSSize view_size = [self bounds].size; | ||||
|  | ||||
|     // Map point on Touch Device to View coordinates. | ||||
|     NSPoint touch_point = NSMakePoint(scaled_pos.x * view_size.width, | ||||
|                                       scaled_pos.y * view_size.height); | ||||
|  | ||||
|     NSPoint contentLocal = [self convertPoint:touch_point fromView:nil]; | ||||
|     NSPoint point; | ||||
|     point.x = contentLocal.x; | ||||
|     point.y = [self frame].size.height - contentLocal.y;  // Flip y. | ||||
|  | ||||
|     // Convert to device coordinates. | ||||
|     point = [self convertPointToBackingInternal:point]; | ||||
|  | ||||
|     int device_x = point.x; | ||||
|     int device_y = point.y; | ||||
|  | ||||
|     const float device_scale_factor = [self getDeviceScaleFactor]; | ||||
|     // Convert to browser view coordinates. | ||||
|     touch_event.x = client::DeviceToLogical(device_x, device_scale_factor); | ||||
|     touch_event.y = client::DeviceToLogical(device_y, device_scale_factor); | ||||
|  | ||||
|     touch_event.radius_x = 0; | ||||
|     touch_event.radius_y = 0; | ||||
|  | ||||
|     touch_event.rotation_angle = 0; | ||||
|     touch_event.pressure = 0; | ||||
|  | ||||
|     touch_event.modifiers = modifiers; | ||||
|  | ||||
|     // Notify the browser of touch event. | ||||
|     browser->GetHost()->SendTouchEvent(touch_event); | ||||
|   } | ||||
| } | ||||
|  | ||||
| - (void)touchesBeganWithEvent:(NSEvent*)event { | ||||
|   [self sendTouchEvent:event touchPhase:NSTouchPhaseBegan]; | ||||
| } | ||||
|  | ||||
| - (void)touchesMovedWithEvent:(NSEvent*)event { | ||||
|   [self sendTouchEvent:event touchPhase:NSTouchPhaseMoved]; | ||||
| } | ||||
|  | ||||
| - (void)touchesEndedWithEvent:(NSEvent*)event { | ||||
|   [self sendTouchEvent:event touchPhase:NSTouchPhaseEnded]; | ||||
| } | ||||
|  | ||||
| - (void)touchesCancelledWithEvent:(NSEvent*)event { | ||||
|   [self sendTouchEvent:event touchPhase:NSTouchPhaseCancelled]; | ||||
| } | ||||
|  | ||||
| - (void)keyUp:(NSEvent*)event { | ||||
| @@ -490,7 +594,7 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) { | ||||
|     browser->GetFocusedFrame()->Paste(); | ||||
| } | ||||
|  | ||||
| - (void) delete:(id)sender { | ||||
| - (void)delete:(id)sender { | ||||
|   CefRefPtr<CefBrowser> browser = [self getBrowser]; | ||||
|   if (browser.get()) | ||||
|     browser->GetFocusedFrame()->Delete(); | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
| // can be found in the LICENSE file. | ||||
|  | ||||
| #include "tests/cefclient/browser/client_browser.h" | ||||
| #include "tests/cefclient/browser/main_context.h" | ||||
|  | ||||
| #include "include/cef_command_line.h" | ||||
| #include "include/cef_crash_util.h" | ||||
| @@ -39,6 +40,14 @@ class ClientBrowserDelegate : public ClientAppBrowser::Delegate { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void OnBeforeCommandLineProcessing( | ||||
|       CefRefPtr<ClientAppBrowser> app, | ||||
|       CefRefPtr<CefCommandLine> command_line) OVERRIDE { | ||||
|     // Append Chromium command line parameters if touch events are enabled | ||||
|     if (client::MainContext::Get()->TouchEventsEnabled()) | ||||
|       command_line->AppendSwitchWithValue("touch-events", "enabled"); | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   DISALLOW_COPY_AND_ASSIGN(ClientBrowserDelegate); | ||||
|   IMPLEMENT_REFCOUNTING(ClientBrowserDelegate); | ||||
|   | ||||
| @@ -44,6 +44,9 @@ class MainContext { | ||||
|   // Returns true if windowless (off-screen) rendering will be used. | ||||
|   virtual bool UseWindowlessRendering() = 0; | ||||
|  | ||||
|   // Returns true if touch events are enabled. | ||||
|   virtual bool TouchEventsEnabled() = 0; | ||||
|  | ||||
|   // Populate |settings| based on command-line arguments. | ||||
|   virtual void PopulateSettings(CefSettings* settings) = 0; | ||||
|   virtual void PopulateBrowserSettings(CefBrowserSettings* settings) = 0; | ||||
|   | ||||
| @@ -163,6 +163,10 @@ bool MainContextImpl::UseWindowlessRendering() { | ||||
|   return use_windowless_rendering_; | ||||
| } | ||||
|  | ||||
| bool MainContextImpl::TouchEventsEnabled() { | ||||
|   return command_line_->GetSwitchValue("touch-events") == "enabled"; | ||||
| } | ||||
|  | ||||
| void MainContextImpl::PopulateSettings(CefSettings* settings) { | ||||
| #if defined(OS_WIN) || defined(OS_LINUX) | ||||
|   settings->multi_threaded_message_loop = | ||||
|   | ||||
| @@ -29,6 +29,7 @@ class MainContextImpl : public MainContext { | ||||
|   cef_color_t GetBackgroundColor() OVERRIDE; | ||||
|   bool UseViews() OVERRIDE; | ||||
|   bool UseWindowlessRendering() OVERRIDE; | ||||
|   bool TouchEventsEnabled() OVERRIDE; | ||||
|   void PopulateSettings(CefSettings* settings) OVERRIDE; | ||||
|   void PopulateBrowserSettings(CefBrowserSettings* settings) OVERRIDE; | ||||
|   void PopulateOsrSettings(OsrRendererSettings* settings) OVERRIDE; | ||||
| @@ -70,6 +71,7 @@ class MainContextImpl : public MainContext { | ||||
|   bool use_windowless_rendering_; | ||||
|   int windowless_frame_rate_; | ||||
|   bool use_views_; | ||||
|   bool touch_events_enabled_; | ||||
|  | ||||
|   scoped_ptr<RootWindowManager> root_window_manager_; | ||||
|  | ||||
|   | ||||
| @@ -27,6 +27,29 @@ namespace { | ||||
|  | ||||
| const wchar_t kWndClass[] = L"Client_OsrWindow"; | ||||
|  | ||||
| // Helper funtion to check if it is Windows8 or greater. | ||||
| // https://msdn.microsoft.com/en-us/library/ms724833(v=vs.85).aspx | ||||
| inline BOOL IsWindows_8_Or_Newer() { | ||||
|   OSVERSIONINFOEX osvi = {0}; | ||||
|   osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); | ||||
|   osvi.dwMajorVersion = 6; | ||||
|   osvi.dwMinorVersion = 2; | ||||
|   DWORDLONG dwlConditionMask = 0; | ||||
|   VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); | ||||
|   VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL); | ||||
|   return ::VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, | ||||
|                              dwlConditionMask); | ||||
| } | ||||
|  | ||||
| // Helper function to detect mouse messages coming from emulation of touch | ||||
| // events. These should be ignored. | ||||
| bool IsMouseEventFromTouch(UINT message) { | ||||
| #define MOUSEEVENTF_FROMTOUCH 0xFF515700 | ||||
|   return (message >= WM_MOUSEFIRST) && (message <= WM_MOUSELAST) && | ||||
|          (GetMessageExtraInfo() & MOUSEEVENTF_FROMTOUCH) == | ||||
|              MOUSEEVENTF_FROMTOUCH; | ||||
| } | ||||
|  | ||||
| }  // namespace | ||||
|  | ||||
| OsrWindowWin::OsrWindowWin(Delegate* delegate, | ||||
| @@ -256,6 +279,10 @@ void OsrWindowWin::Create(HWND parent_hwnd, const RECT& rect) { | ||||
|  | ||||
|   ime_handler_.reset(new OsrImeHandlerWin(hwnd_)); | ||||
|  | ||||
|   // Enable Touch Events if requested | ||||
|   if (client::MainContext::Get()->TouchEventsEnabled()) | ||||
|     RegisterTouchWindow(hwnd_, 0); | ||||
|  | ||||
|   // Notify the window owner. | ||||
|   NotifyNativeWindowCreated(hwnd_); | ||||
| } | ||||
| @@ -476,6 +503,15 @@ LRESULT CALLBACK OsrWindowWin::OsrWndProc(HWND hWnd, | ||||
|       // Don't erase the background. | ||||
|       return 0; | ||||
|  | ||||
|     // If your application does not require Win7 support, please do consider | ||||
|     // using WM_POINTER* messages instead of WM_TOUCH. WM_POINTER are more | ||||
|     // intutive, complete and simpler to code. | ||||
|     // https://msdn.microsoft.com/en-us/library/hh454903(v=vs.85).aspx | ||||
|     case WM_TOUCH: | ||||
|       if (self->OnTouchEvent(message, wParam, lParam)) | ||||
|         return 0; | ||||
|       break; | ||||
|  | ||||
|     case WM_NCDESTROY: | ||||
|       // Clear the reference to |self|. | ||||
|       SetUserDataPtr(hWnd, NULL); | ||||
| @@ -487,6 +523,9 @@ LRESULT CALLBACK OsrWindowWin::OsrWndProc(HWND hWnd, | ||||
| } | ||||
|  | ||||
| void OsrWindowWin::OnMouseEvent(UINT message, WPARAM wParam, LPARAM lParam) { | ||||
|   if (IsMouseEventFromTouch(message)) | ||||
|     return; | ||||
|  | ||||
|   CefRefPtr<CefBrowserHost> browser_host; | ||||
|   if (browser_) | ||||
|     browser_host = browser_->GetHost(); | ||||
| @@ -733,6 +772,63 @@ bool OsrWindowWin::OnEraseBkgnd() { | ||||
|   return (browser_ == NULL); | ||||
| } | ||||
|  | ||||
| bool OsrWindowWin::OnTouchEvent(UINT message, WPARAM wParam, LPARAM lParam) { | ||||
|   // Handle touch events on Windows. | ||||
|   int num_points = LOWORD(wParam); | ||||
|   // Chromium only supports upto 16 touch points. | ||||
|   if (num_points < 0 || num_points > 16) | ||||
|     return false; | ||||
|   std::unique_ptr<TOUCHINPUT[]> input(new TOUCHINPUT[num_points]); | ||||
|   if (GetTouchInputInfo(reinterpret_cast<HTOUCHINPUT>(lParam), num_points, | ||||
|                         input.get(), sizeof(TOUCHINPUT))) { | ||||
|     CefTouchEvent touch_event; | ||||
|     for (int i = 0; i < num_points; ++i) { | ||||
|       POINT point; | ||||
|       point.x = TOUCH_COORD_TO_PIXEL(input[i].x); | ||||
|       point.y = TOUCH_COORD_TO_PIXEL(input[i].y); | ||||
|  | ||||
|       if (!IsWindows_8_Or_Newer()) { | ||||
|         // Windows 7 sends touch events for touches in the non-client area, | ||||
|         // whereas Windows 8 does not. In order to unify the behaviour, always | ||||
|         // ignore touch events in the non-client area. | ||||
|         LPARAM l_param_ht = MAKELPARAM(point.x, point.y); | ||||
|         LRESULT hittest = SendMessage(hwnd_, WM_NCHITTEST, 0, l_param_ht); | ||||
|         if (hittest != HTCLIENT) | ||||
|           return false; | ||||
|       } | ||||
|  | ||||
|       ScreenToClient(hwnd_, &point); | ||||
|       touch_event.x = DeviceToLogical(point.x, device_scale_factor_); | ||||
|       touch_event.y = DeviceToLogical(point.y, device_scale_factor_); | ||||
|  | ||||
|       // Touch point identifier stays consistent in a touch contact sequence | ||||
|       touch_event.id = input[i].dwID; | ||||
|  | ||||
|       if (input[i].dwFlags & TOUCHEVENTF_DOWN) { | ||||
|         touch_event.type = CEF_TET_PRESSED; | ||||
|       } else if (input[i].dwFlags & TOUCHEVENTF_MOVE) { | ||||
|         touch_event.type = CEF_TET_MOVED; | ||||
|       } else if (input[i].dwFlags & TOUCHEVENTF_UP) { | ||||
|         touch_event.type = CEF_TET_RELEASED; | ||||
|       } | ||||
|  | ||||
|       touch_event.radius_x = 0; | ||||
|       touch_event.radius_y = 0; | ||||
|       touch_event.rotation_angle = 0; | ||||
|       touch_event.pressure = 0; | ||||
|       touch_event.modifiers = 0; | ||||
|  | ||||
|       // Notify the browser of touch event | ||||
|       if (browser_) | ||||
|         browser_->GetHost()->SendTouchEvent(touch_event); | ||||
|     } | ||||
|     CloseTouchInputHandle(reinterpret_cast<HTOUCHINPUT>(lParam)); | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| bool OsrWindowWin::IsOverPopupWidget(int x, int y) const { | ||||
|   if (!render_handler_) | ||||
|     return false; | ||||
|   | ||||
| @@ -93,6 +93,7 @@ class OsrWindowWin | ||||
|   void OnKeyEvent(UINT message, WPARAM wParam, LPARAM lParam); | ||||
|   void OnPaint(); | ||||
|   bool OnEraseBkgnd(); | ||||
|   bool OnTouchEvent(UINT message, WPARAM wParam, LPARAM lParam); | ||||
|  | ||||
|   void OnIMESetContext(UINT message, WPARAM wParam, LPARAM lParam); | ||||
|   void OnIMEStartComposition(); | ||||
|   | ||||
| @@ -36,6 +36,7 @@ | ||||
| <li><a href="urlrequest">CefURLRequest</a></li> | ||||
| <li><a href="xmlhttprequest">XMLHttpRequest</a></li> | ||||
| <li><a href="javascript:window.print();">Print this page with "javascript:window.print();"</a></li> | ||||
| <li><a href="https://patrickhlauke.github.io/touch">Touch Feature Tests</a> - requires "touch-events=enabled" flag (and CAPS LOCK on Mac for Trackpad simulation) </li> | ||||
| </ul> | ||||
| </body> | ||||
| </html> | ||||
|   | ||||
| @@ -160,7 +160,14 @@ enum OSRTestType { | ||||
|   OSR_TEST_TEXT_SELECTION_CHANGE, | ||||
|   // clicking on text input will call OnVirtualKeyboardRequested | ||||
|   OSR_TEST_VIRTUAL_KEYBOARD, | ||||
|  | ||||
|   // touchStart event is triggered and contains the touch point count | ||||
|   OSR_TEST_TOUCH_START, | ||||
|   // touchMove event is triggered and contains the changed touch points | ||||
|   OSR_TEST_TOUCH_MOVE, | ||||
|   // touchEnd is triggered on completion | ||||
|   OSR_TEST_TOUCH_END, | ||||
|   // touchCancel is triggered on dismissing | ||||
|   OSR_TEST_TOUCH_CANCEL, | ||||
|   // Define the range for popup tests. | ||||
|   OSR_TEST_POPUP_FIRST = OSR_TEST_POPUP_PAINT, | ||||
|   OSR_TEST_POPUP_LAST = OSR_TEST_POPUP_SCROLL_INSIDE, | ||||
| @@ -177,7 +184,8 @@ class OSRTestHandler : public RoutingTestHandler, | ||||
|         scale_factor_(scale_factor), | ||||
|         event_count_(0), | ||||
|         event_total_(1), | ||||
|         started_(false) {} | ||||
|         started_(false), | ||||
|         touch_state_(CEF_TET_CANCELLED) {} | ||||
|  | ||||
|   // TestHandler methods | ||||
|   void RunTest() override { | ||||
| @@ -262,11 +270,58 @@ class OSRTestHandler : public RoutingTestHandler, | ||||
|         EXPECT_STREQ(messageStr.c_str(), "osrdrop"); | ||||
|         DestroySucceededTestSoon(); | ||||
|         break; | ||||
|       case OSR_TEST_TOUCH_START: | ||||
|       case OSR_TEST_TOUCH_MOVE: | ||||
|       case OSR_TEST_TOUCH_END: | ||||
|       case OSR_TEST_TOUCH_CANCEL: { | ||||
|         switch (touch_state_) { | ||||
|           case CEF_TET_CANCELLED: { | ||||
|             // The first message expected is touchstart. | ||||
|             // We expect multitouch, so touches length should be 2. | ||||
|             // Ignore intermediate touch start events. | ||||
|             if (messageStr == "osrtouchstart1") | ||||
|               break; | ||||
|             EXPECT_STREQ(messageStr.c_str(), "osrtouchstart2"); | ||||
|             // Close Touch Start Tests. | ||||
|             if (test_type_ == OSR_TEST_TOUCH_START) { | ||||
|               DestroySucceededTestSoon(); | ||||
|               touch_state_ = CEF_TET_RELEASED; | ||||
|             } else { | ||||
|               touch_state_ = CEF_TET_PRESSED; | ||||
|             } | ||||
|           } break; | ||||
|           case CEF_TET_PRESSED: { | ||||
|             // Touch Move include the touches that changed, should be 2. | ||||
|             EXPECT_STREQ(messageStr.c_str(), "osrtouchmove2"); | ||||
|             if (test_type_ == OSR_TEST_TOUCH_MOVE) { | ||||
|               DestroySucceededTestSoon(); | ||||
|               touch_state_ = CEF_TET_RELEASED; | ||||
|             } else { | ||||
|               touch_state_ = CEF_TET_MOVED; | ||||
|             } | ||||
|           } break; | ||||
|           case CEF_TET_MOVED: { | ||||
|             // There might be multiple touchmove events, ignore. | ||||
|             if (messageStr != "osrtouchmove2") { | ||||
|               if (test_type_ == OSR_TEST_TOUCH_END) { | ||||
|                 EXPECT_STREQ(messageStr.c_str(), "osrtouchend"); | ||||
|                 DestroySucceededTestSoon(); | ||||
|                 touch_state_ = CEF_TET_RELEASED; | ||||
|               } else if (test_type_ == OSR_TEST_TOUCH_CANCEL) { | ||||
|                 EXPECT_STREQ(messageStr.c_str(), "osrtouchcancel"); | ||||
|                 DestroySucceededTestSoon(); | ||||
|                 touch_state_ = CEF_TET_RELEASED; | ||||
|               } | ||||
|             } | ||||
|           } break; | ||||
|           default: | ||||
|             break; | ||||
|         } | ||||
|       } break; | ||||
|       default: | ||||
|         // Intentionally left blank | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     callback->Success(""); | ||||
|     return true; | ||||
|   } | ||||
| @@ -963,6 +1018,50 @@ class OSRTestHandler : public RoutingTestHandler, | ||||
|                                                   1); | ||||
|         } | ||||
|       } break; | ||||
|       case OSR_TEST_TOUCH_START: | ||||
|       case OSR_TEST_TOUCH_MOVE: | ||||
|       case OSR_TEST_TOUCH_END: | ||||
|       case OSR_TEST_TOUCH_CANCEL: { | ||||
|         // We trigger a valid Touch workflow sequence and close the tests | ||||
|         // at seperate points for the 4 cases | ||||
|         if (StartTest()) { | ||||
|           const CefRect& touchdiv = GetElementBounds("touchdiv"); | ||||
|           // click inside edit box so that text could be entered | ||||
|           CefTouchEvent touch_event1; | ||||
|           touch_event1.id = 0; | ||||
|           touch_event1.x = MiddleX(touchdiv) - 45; | ||||
|           touch_event1.y = MiddleY(touchdiv); | ||||
|           touch_event1.modifiers = 0; | ||||
|           touch_event1.type = CEF_TET_PRESSED; | ||||
|  | ||||
|           CefTouchEvent touch_event2; | ||||
|           touch_event2.id = 1; | ||||
|           touch_event2.x = MiddleX(touchdiv) + 45; | ||||
|           touch_event2.y = MiddleY(touchdiv); | ||||
|           touch_event2.modifiers = 0; | ||||
|           touch_event2.type = CEF_TET_PRESSED; | ||||
|  | ||||
|           browser->GetHost()->SendTouchEvent(touch_event1); | ||||
|           browser->GetHost()->SendTouchEvent(touch_event2); | ||||
|  | ||||
|           // Move the Touch fingers closer | ||||
|           touch_event1.type = touch_event2.type = CEF_TET_MOVED; | ||||
|           for (size_t i = 0; i < 40; i++) { | ||||
|             touch_event1.x++; | ||||
|             touch_event2.x--; | ||||
|             browser->GetHost()->SendTouchEvent(touch_event1); | ||||
|             browser->GetHost()->SendTouchEvent(touch_event2); | ||||
|           } | ||||
|  | ||||
|           // Now release the Touch fingers or cancel them | ||||
|           if (test_type_ == OSR_TEST_TOUCH_CANCEL) | ||||
|             touch_event1.type = touch_event2.type = CEF_TET_CANCELLED; | ||||
|           else | ||||
|             touch_event1.type = touch_event2.type = CEF_TET_RELEASED; | ||||
|           browser->GetHost()->SendTouchEvent(touch_event1); | ||||
|           browser->GetHost()->SendTouchEvent(touch_event2); | ||||
|         } | ||||
|       } break; | ||||
|       default: | ||||
|         break; | ||||
|     } | ||||
| @@ -1352,6 +1451,7 @@ class OSRTestHandler : public RoutingTestHandler, | ||||
|   int event_count_; | ||||
|   int event_total_; | ||||
|   bool started_; | ||||
|   cef_touch_event_type_t touch_state_; | ||||
|   TrackCallback got_update_cursor_; | ||||
|   TrackCallback got_navigation_focus_event_; | ||||
|   TrackCallback got_system_focus_event_; | ||||
| @@ -1444,3 +1544,11 @@ OSR_TEST(IMECancelComposition2x, OSR_TEST_IME_CANCEL_COMPOSITION, 2.0f); | ||||
| OSR_TEST(TextSelectionChanged, OSR_TEST_TEXT_SELECTION_CHANGE, 1.0f); | ||||
| OSR_TEST(TextSelectionChanged2x, OSR_TEST_TEXT_SELECTION_CHANGE, 2.0f); | ||||
| OSR_TEST(VirtualKeyboard, OSR_TEST_VIRTUAL_KEYBOARD, 1.0f); | ||||
| OSR_TEST(TouchStart, OSR_TEST_TOUCH_START, 1.0f); | ||||
| OSR_TEST(TouchStart2X, OSR_TEST_TOUCH_START, 2.0f); | ||||
| OSR_TEST(TouchMove, OSR_TEST_TOUCH_MOVE, 1.0f); | ||||
| OSR_TEST(TouchMove2X, OSR_TEST_TOUCH_MOVE, 2.0f); | ||||
| OSR_TEST(TouchEnd, OSR_TEST_TOUCH_END, 1.0f); | ||||
| OSR_TEST(TouchEnd2X, OSR_TEST_TOUCH_END, 2.0f); | ||||
| OSR_TEST(TouchCancel, OSR_TEST_TOUCH_CANCEL, 1.0f); | ||||
| OSR_TEST(TouchCancel2X, OSR_TEST_TOUCH_CANCEL, 2.0f); | ||||
|   | ||||
| @@ -25,6 +25,13 @@ | ||||
|     left: 7px; | ||||
|     opacity: 0.4; | ||||
|   } | ||||
|   #touchdiv { | ||||
|     width: 100px; | ||||
|     height: 50px; | ||||
|     background-color: red; | ||||
|     float: left; | ||||
|     margin-left: 10px; | ||||
|   } | ||||
|   </style> | ||||
|   <script> | ||||
|   function getElement(id) { return document.getElementById(id); } | ||||
| @@ -53,6 +60,7 @@ | ||||
|     elems.push(getElementBounds('btnnavigate')); | ||||
|     elems.push(getElementBounds('dropdiv')); | ||||
|     elems.push(getElementBounds('dragdiv')); | ||||
|     elems.push(getElementBounds('touchdiv')); | ||||
|  | ||||
|     if (window.testQuery) | ||||
|       window.testQuery({request: JSON.stringify(param)}); | ||||
| @@ -115,6 +123,21 @@ | ||||
|     selection.removeAllRanges(); | ||||
|     selection.addRange(range); | ||||
|   } | ||||
|  | ||||
|   function onTouchEvent(ev) { | ||||
|     var param = 'osr' + ev.type; | ||||
|     // For Touch start also include touch points. | ||||
|     if (event.type == "touchstart") | ||||
|       param += ev.touches.length; | ||||
|     // For Touch Move include the touches that changed. | ||||
|     if (event.type == "touchmove") | ||||
|       param += ev.changedTouches.length; | ||||
|  | ||||
|     // Results in a call to the OnQuery method in os_rendering_unittest.cc. | ||||
|     if (window.testQuery) | ||||
|       window.testQuery({request: param}); | ||||
|   } | ||||
|  | ||||
|   </script> | ||||
|   <body onfocus='onEventTest(event)' onblur='onEventTest(event)' onload='load();'> | ||||
|   <h1 id='LI00' onclick="onEventTest(event)"> | ||||
| @@ -146,12 +169,15 @@ | ||||
|   <li id='LI11' onclick='selectText(event)'>SELECTED_TEXT_RANGE</li> | ||||
|   <li><input id='email' type='text' size=10 inputmode='email'></li> | ||||
|   </ol> | ||||
|  | ||||
|   <div class="dropdiv" id="dropdiv" ondrop="drop(event)" ondragover="allowDrop(event)"> | ||||
|     <span id="draghere">Drag here</span> | ||||
|   </div> | ||||
|   <div class="dropdiv"> | ||||
|     <div id="dragdiv" draggable="true" ondragstart="drag(event)"></div> | ||||
|   </div> | ||||
|   <div id="touchdiv" ontouchstart="onTouchEvent(event)" ontouchend="onTouchEvent(event)" ontouchmove="onTouchEvent(event)" ontouchcancel="onTouchEvent(event)"> | ||||
|   </div> | ||||
|   <br /> | ||||
|   <br /> | ||||
|   <br /> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user