diff --git a/dom/events/Touch.h b/dom/events/Touch.h index df59b4f36c86..eb9a234ccdcb 100644 --- a/dom/events/Touch.h +++ b/dom/events/Touch.h @@ -8,6 +8,7 @@ #include "mozilla/Attributes.h" #include "mozilla/EventForwards.h" +#include "mozilla/MouseEvents.h" #include "nsWrapperCache.h" #include "nsAutoPtr.h" #include "Units.h" @@ -21,6 +22,7 @@ class EventTarget; class Touch MOZ_FINAL : public nsISupports , public nsWrapperCache + , public WidgetPointerHelper { public: static bool PrefEnabled(JSContext* aCx, JSObject* aGlobal); diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index f2854e6ccba1..45e45114c37e 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -688,6 +688,7 @@ nsIPresShell::FrameSelection() static bool sSynthMouseMove = true; static uint32_t sNextPresShellId; +static bool sPointerEventEnabled = true; PresShell::PresShell() : mMouseLocation(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) @@ -736,6 +737,12 @@ PresShell::PresShell() "layout.reflow.synthMouseMove", true); addedSynthMouseMove = true; } + static bool addedPointerEventEnabled = false; + if (addedPointerEventEnabled) { + Preferences::AddBoolVarCache(&sPointerEventEnabled, + "dom.w3c_pointer_events.enabled", true); + addedPointerEventEnabled = true; + } mPaintingIsFrozen = false; } @@ -6299,12 +6306,102 @@ FlushThrottledStyles(nsIDocument *aDocument, void *aData) return true; } +static nsresult +DispatchPointerFromMouseOrTouch(PresShell* aShell, + nsIFrame* aFrame, + WidgetGUIEvent* aEvent, + bool aDontRetargetEvents, + nsEventStatus* aStatus) +{ + uint32_t pointerMessage = 0; + if (aEvent->eventStructType == NS_MOUSE_EVENT) { + WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); + // if it is not mouse then it is likely will come as touch event + if (!mouseEvent->convertToPointer) { + return NS_OK; + } + int16_t button = mouseEvent->button; + switch (mouseEvent->message) { + case NS_MOUSE_MOVE: + if (mouseEvent->buttons == 0) { + button = -1; + } + pointerMessage = NS_POINTER_MOVE; + break; + case NS_MOUSE_BUTTON_UP: + pointerMessage = NS_POINTER_UP; + break; + case NS_MOUSE_BUTTON_DOWN: + pointerMessage = NS_POINTER_DOWN; + break; + default: + return NS_OK; + } + + WidgetPointerEvent event(*mouseEvent); + event.message = pointerMessage; + event.button = button; + event.convertToPointer = mouseEvent->convertToPointer = false; + aShell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus); + } else if (aEvent->eventStructType == NS_TOUCH_EVENT) { + WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent(); + // loop over all touches and dispatch pointer events on each touch + // copy the event + switch (touchEvent->message) { + case NS_TOUCH_MOVE: + pointerMessage = NS_POINTER_MOVE; + break; + case NS_TOUCH_END: + pointerMessage = NS_POINTER_UP; + break; + case NS_TOUCH_START: + pointerMessage = NS_POINTER_DOWN; + break; + case NS_TOUCH_CANCEL: + pointerMessage = NS_POINTER_CANCEL; + break; + default: + return NS_OK; + } + + for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) { + mozilla::dom::Touch* touch = touchEvent->touches[i]; + if (!touch || !touch->convertToPointer) { + continue; + } + + WidgetPointerEvent event(touchEvent->mFlags.mIsTrusted, pointerMessage, touchEvent->widget); + event.isPrimary = i == 0; + event.pointerId = touch->Identifier(); + event.refPoint.x = touch->mRefPoint.x; + event.refPoint.y = touch->mRefPoint.y; + event.modifiers = touchEvent->modifiers; + event.width = touch->RadiusX(); + event.height = touch->RadiusY(); + event.tiltX = touch->tiltX; + event.tiltY = touch->tiltY; + event.time = touchEvent->time; + event.mFlags = touchEvent->mFlags; + event.button = WidgetMouseEvent::eLeftButton; + event.buttons = WidgetMouseEvent::eLeftButtonFlag; + event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; + event.convertToPointer = touch->convertToPointer = false; + aShell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus); + } + } + return NS_OK; +} + nsresult PresShell::HandleEvent(nsIFrame* aFrame, WidgetGUIEvent* aEvent, bool aDontRetargetEvents, nsEventStatus* aEventStatus) { + if (sPointerEventEnabled) { + DispatchPointerFromMouseOrTouch(this, aFrame, aEvent, aDontRetargetEvents, aEventStatus); + } + NS_ASSERTION(aFrame, "null frame"); if (mIsDestroying || diff --git a/widget/MouseEvents.h b/widget/MouseEvents.h index e04bc9bb22e7..1d6c55448efa 100644 --- a/widget/MouseEvents.h +++ b/widget/MouseEvents.h @@ -36,6 +36,27 @@ namespace dom { class PBrowserChild; } // namespace dom +/****************************************************************************** + * mozilla::WidgetPointerHelper + ******************************************************************************/ + +class WidgetPointerHelper +{ +public: + bool convertToPointer; + uint32_t tiltX; + uint32_t tiltY; + + WidgetPointerHelper() : convertToPointer(true), tiltX(0), tiltY(0) {} + + void AssignPointerHelperData(const WidgetPointerHelper& aEvent) + { + convertToPointer = aEvent.convertToPointer; + tiltX = aEvent.tiltX; + tiltY = aEvent.tiltY; + } +}; + /****************************************************************************** * mozilla::WidgetMouseEventBase ******************************************************************************/ @@ -128,7 +149,7 @@ public: * mozilla::WidgetMouseEvent ******************************************************************************/ -class WidgetMouseEvent : public WidgetMouseEventBase +class WidgetMouseEvent : public WidgetMouseEventBase, public WidgetPointerHelper { private: friend class mozilla::dom::PBrowserParent; @@ -242,6 +263,7 @@ public: void AssignMouseEventData(const WidgetMouseEvent& aEvent, bool aCopyTargets) { AssignMouseEventBaseData(aEvent, aCopyTargets); + AssignPointerHelperData(aEvent); acceptActivation = aEvent.acceptActivation; ignoreRootScrollFrame = aEvent.ignoreRootScrollFrame; @@ -532,8 +554,6 @@ public: , pointerId(0) , width(0) , height(0) - , tiltX(0) - , tiltY(0) , isPrimary(true) { UpdateFlags(); @@ -544,8 +564,6 @@ public: , pointerId(0) , width(0) , height(0) - , tiltX(0) - , tiltY(0) , isPrimary(true) { eventStructType = NS_POINTER_EVENT; @@ -579,8 +597,6 @@ public: uint32_t pointerId; uint32_t width; uint32_t height; - uint32_t tiltX; - uint32_t tiltY; bool isPrimary; // XXX Not tested by test_assign_event_data.html @@ -592,8 +608,6 @@ public: pointerId = aEvent.pointerId; width = aEvent.width; height = aEvent.height; - tiltX = aEvent.tiltX; - tiltY = aEvent.tiltY; isPrimary = aEvent.isPrimary; } }; diff --git a/widget/windows/WinUtils.cpp b/widget/windows/WinUtils.cpp index 45c93fea1c73..78f99ed3062e 100644 --- a/widget/windows/WinUtils.cpp +++ b/widget/windows/WinUtils.cpp @@ -558,6 +558,16 @@ WinUtils::GetMouseInputSource() return static_cast(inputSource); } +bool +WinUtils::GetIsMouseFromTouch(uint32_t aEventType) +{ +#define MOUSEEVENTF_FROMTOUCH 0xFF515700 + return (aEventType == NS_MOUSE_BUTTON_DOWN || + aEventType == NS_MOUSE_BUTTON_UP || + aEventType == NS_MOUSE_MOVE) && + (GetMessageExtraInfo() & MOUSEEVENTF_FROMTOUCH); +} + /* static */ MSG WinUtils::InitMSG(UINT aMessage, WPARAM wParam, LPARAM lParam, HWND aWnd) diff --git a/widget/windows/WinUtils.h b/widget/windows/WinUtils.h index b5bf07687049..42d5af12b96e 100644 --- a/widget/windows/WinUtils.h +++ b/widget/windows/WinUtils.h @@ -237,6 +237,8 @@ public: */ static uint16_t GetMouseInputSource(); + static bool GetIsMouseFromTouch(uint32_t aEventType); + /** * SHCreateItemFromParsingName() calls native SHCreateItemFromParsingName() * API which is available on Vista and up. diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 4dd83221a065..6510712c8b44 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -283,6 +283,9 @@ static const int32_t kGlassMarginAdjustment = 2; // content. static const int32_t kResizableBorderMinSize = 3; +// Cached pointer events enabler value, True if pointer events are enabled. +static bool gIsPointerEventsEnabled = false; + // We should never really try to accelerate windows bigger than this. In some // cases this might lead to no D3D9 acceleration where we could have had it // but D3D9 does not reliably report when it supports bigger windows. 8192 @@ -382,6 +385,10 @@ nsWindow::nsWindow() : nsWindowBase() // Init theme data nsUXThemeData::UpdateNativeThemeInfo(); RedirectedKeyDownMessageManager::Forget(); + + Preferences::AddBoolVarCache(&gIsPointerEventsEnabled, + "dom.w3c_pointer_events.enabled", + gIsPointerEventsEnabled); } // !sInstanceCount mIdleService = nullptr; @@ -1271,7 +1278,8 @@ void nsWindow::SetThemeRegion() **************************************************************/ NS_METHOD nsWindow::RegisterTouchWindow() { - if (Preferences::GetInt("dom.w3c_touch_events.enabled", 0)) { + if (Preferences::GetInt("dom.w3c_touch_events.enabled", 0) || + gIsPointerEventsEnabled) { mTouchWindow = true; mGesture.RegisterTouchWindow(mWnd); ::EnumChildWindows(mWnd, nsWindow::RegisterTouchForDescendants, 0); @@ -3792,6 +3800,10 @@ bool nsWindow::DispatchMouseEvent(uint32_t aEventType, WPARAM wParam, modifierKeyState.InitInputEvent(event); event.button = aButton; event.inputSource = aInputSource; + // Convert Mouse events generated by pen device or if mouse not generated from touch + event.convertToPointer = + aInputSource == nsIDOMMouseEvent::MOZ_SOURCE_PEN || + !(WinUtils::GetIsMouseFromTouch(aEventType) && mTouchWindow); nsIntPoint mpScreen = eventPoint + WidgetToScreenOffset(); @@ -5231,6 +5243,11 @@ nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam, // A GestureNotify event is dispatched to decide which single-finger panning // direction should be active (including none) and if pan feedback should // be displayed. Java and plugin windows can make their own calls. + if (gIsPointerEventsEnabled) { + result = false; + break; + } + GESTURENOTIFYSTRUCT * gestureinfo = (GESTURENOTIFYSTRUCT*)lParam; nsPointWin touchPoint; touchPoint = gestureinfo->ptsLocation; @@ -6093,6 +6110,10 @@ static int32_t RoundDown(double aDouble) // Gesture event processing. Handles WM_GESTURE events. bool nsWindow::OnGesture(WPARAM wParam, LPARAM lParam) { + if (gIsPointerEventsEnabled) { + return false; + } + // Treatment for pan events which translate into scroll events: if (mGesture.IsPanEvent(lParam)) { if ( !mGesture.ProcessPanMessage(mWnd, wParam, lParam) ) diff --git a/widget/windows/winrt/MetroInput.cpp b/widget/windows/winrt/MetroInput.cpp index 46b705668ba0..4cf010bf83c6 100644 --- a/widget/windows/winrt/MetroInput.cpp +++ b/widget/windows/winrt/MetroInput.cpp @@ -76,18 +76,23 @@ namespace { uint32_t pointerId; Foundation::Rect contactRect; float pressure; + float tiltX; + float tiltY; aPoint->get_Properties(props.GetAddressOf()); aPoint->get_Position(&position); aPoint->get_PointerId(&pointerId); props->get_ContactRect(&contactRect); props->get_Pressure(&pressure); + props->get_XTilt(&tiltX); + props->get_YTilt(&tiltY); nsIntPoint touchPoint = MetroUtils::LogToPhys(position); nsIntPoint touchRadius; touchRadius.x = WinUtils::LogToPhys(contactRect.Width) / 2; touchRadius.y = WinUtils::LogToPhys(contactRect.Height) / 2; - return new Touch(pointerId, + Touch* touch = + new Touch(pointerId, touchPoint, // Rotation radius and angle. // W3C touch events v1 do not use these. @@ -109,6 +114,9 @@ namespace { // draft says that the value should be 0.0 if no value // known. pressure); + touch->tiltX = tiltX; + touch->tiltY = tiltY; + return touch; } /** @@ -224,6 +232,8 @@ namespace { aData->mRadius, aData->mRotationAngle, aData->mForce); + copy->tiltX = aData->tiltX; + copy->tiltY = aData->tiltY; touches->AppendElement(copy); aData->mChanged = false; return PL_DHASH_NEXT; @@ -789,6 +799,8 @@ MetroInput::InitGeckoMouseEventFromPointerPoint( uint64_t timestamp; float pressure; boolean canBeDoubleTap; + float tiltX; + float tiltY; aPointerPoint->get_Position(&position); aPointerPoint->get_Timestamp(×tamp); @@ -796,6 +808,9 @@ MetroInput::InitGeckoMouseEventFromPointerPoint( device->get_PointerDeviceType(&deviceType); aPointerPoint->get_Properties(props.GetAddressOf()); props->get_Pressure(&pressure); + props->get_XTilt(&tiltX); + props->get_YTilt(&tiltY); + mGestureRecognizer->CanBeDoubleTap(aPointerPoint, &canBeDoubleTap); TransformRefPoint(position, aEvent->refPoint); @@ -806,6 +821,8 @@ MetroInput::InitGeckoMouseEventFromPointerPoint( aEvent->clickCount = 2; } aEvent->pressure = pressure; + aEvent->tiltX = tiltX; + aEvent->tiltY = tiltY; aEvent->buttons = ButtonsForPointerPoint(aPointerPoint); MozInputSourceFromDeviceType(deviceType, aEvent->inputSource);