From 72f42280fdba7055d61cd187ae68ddf45055954f Mon Sep 17 00:00:00 2001 From: Wes Johnston Date: Fri, 16 Dec 2011 16:24:11 -0800 Subject: [PATCH] Bug 603008 - Platform support for multitouch events. r=smaug * * * Bug 603008 - Platform support for multitouch events. * * * ifdefandroid --- config/autoconf.mk.in | 1 + configure.in | 2 + content/events/public/nsEventNameList.h | 24 +- content/events/public/nsIPrivateDOMEvent.h | 2 +- content/events/src/nsDOMEvent.cpp | 113 +++++ content/events/src/nsDOMEvent.h | 17 + content/events/src/nsDOMMouseEvent.cpp | 26 + content/events/src/nsDOMTouchEvent.cpp | 137 +++++- content/events/src/nsDOMTouchEvent.h | 96 ++-- content/events/src/nsDOMUIEvent.cpp | 39 ++ content/events/src/nsDOMUIEvent.h | 2 +- content/events/src/nsEventDispatcher.cpp | 3 + content/events/test/Makefile.in | 6 + content/events/test/test_bug603008.html | 524 +++++++++++++++++++++ dom/base/nsDOMWindowUtils.cpp | 75 +++ dom/interfaces/base/nsIDOMWindowUtils.idl | 42 +- dom/interfaces/events/nsIDOMTouchEvent.idl | 14 +- layout/base/nsIPresShell.h | 5 + layout/base/nsLayoutUtils.cpp | 72 ++- layout/base/nsLayoutUtils.h | 14 + layout/base/nsPresShell.cpp | 285 ++++++++++- layout/base/nsPresShell.h | 6 + view/src/nsView.cpp | 9 +- widget/nsEvent.h | 1 + widget/nsGUIEvent.h | 37 ++ 25 files changed, 1478 insertions(+), 74 deletions(-) create mode 100644 content/events/test/test_bug603008.html diff --git a/config/autoconf.mk.in b/config/autoconf.mk.in index 1fb373ba53c..c476a671cfe 100644 --- a/config/autoconf.mk.in +++ b/config/autoconf.mk.in @@ -148,6 +148,7 @@ MOZ_SPELLCHECK = @MOZ_SPELLCHECK@ MOZ_ANDROID_HISTORY = @MOZ_ANDROID_HISTORY@ MOZ_WEBSMS_BACKEND = @MOZ_WEBSMS_BACKEND@ MOZ_JAVA_COMPOSITOR = @MOZ_JAVA_COMPOSITOR@ +MOZ_TOUCH = @MOZ_TOUCH@ MOZ_PROFILELOCKING = @MOZ_PROFILELOCKING@ MOZ_FEEDS = @MOZ_FEEDS@ MOZ_TOOLKIT_SEARCH = @MOZ_TOOLKIT_SEARCH@ diff --git a/configure.in b/configure.in index dfbdc396b01..8b6c8c33504 100644 --- a/configure.in +++ b/configure.in @@ -4902,6 +4902,7 @@ cairo-uikit) cairo-android) AC_DEFINE(MOZ_WIDGET_ANDROID) + AC_DEFINE(MOZ_TOUCH) MOZ_WIDGET_TOOLKIT=android TK_CFLAGS='$(MOZ_CAIRO_CFLAGS)' TK_LIBS='$(MOZ_CAIRO_LIBS)' @@ -4909,6 +4910,7 @@ cairo-android) MOZ_PDF_PRINTING=1 MOZ_INSTRUMENT_EVENT_LOOP=1 MOZ_OLD_LINKER=1 + MOZ_TOUCH=1 ;; cairo-gonk) diff --git a/content/events/public/nsEventNameList.h b/content/events/public/nsEventNameList.h index 9fb65f100a5..cc486f77e3c 100644 --- a/content/events/public/nsEventNameList.h +++ b/content/events/public/nsEventNameList.h @@ -449,29 +449,29 @@ WINDOW_ONLY_EVENT(deviceorientation, NS_EVENT) TOUCH_EVENT(touchstart, - NS_USER_DEFINED_EVENT, + NS_TOUCH_START, EventNameType_All, - NS_INPUT_EVENT) + NS_TOUCH_EVENT) TOUCH_EVENT(touchend, - NS_USER_DEFINED_EVENT, + NS_TOUCH_END, EventNameType_All, - NS_INPUT_EVENT) + NS_TOUCH_EVENT) TOUCH_EVENT(touchmove, - NS_USER_DEFINED_EVENT, + NS_TOUCH_MOVE, EventNameType_All, - NS_INPUT_EVENT ) + NS_TOUCH_EVENT ) TOUCH_EVENT(touchenter, - NS_USER_DEFINED_EVENT, + NS_TOUCH_ENTER, EventNameType_All, - NS_INPUT_EVENT ) + NS_TOUCH_EVENT ) TOUCH_EVENT(touchleave, - NS_USER_DEFINED_EVENT, + NS_TOUCH_LEAVE, EventNameType_All, - NS_INPUT_EVENT) + NS_TOUCH_EVENT) TOUCH_EVENT(touchcancel, - NS_USER_DEFINED_EVENT, + NS_TOUCH_CANCEL, EventNameType_All, - NS_INPUT_EVENT) + NS_TOUCH_EVENT) DOCUMENT_ONLY_EVENT(readystatechange, NS_READYSTATECHANGE, diff --git a/content/events/public/nsIPrivateDOMEvent.h b/content/events/public/nsIPrivateDOMEvent.h index 7124b98698f..9b37c05eb06 100644 --- a/content/events/public/nsIPrivateDOMEvent.h +++ b/content/events/public/nsIPrivateDOMEvent.h @@ -141,7 +141,7 @@ NS_NewDOMCloseEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContex nsresult NS_NewDOMMozTouchEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsMozTouchEvent* aEvent); nsresult -NS_NewDOMTouchEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsInputEvent *aEvent); +NS_NewDOMTouchEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsTouchEvent *aEvent); nsresult NS_NewDOMCustomEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, nsEvent* aEvent); nsresult diff --git a/content/events/src/nsDOMEvent.cpp b/content/events/src/nsDOMEvent.cpp index 99c6e2b9dc4..1e36b45f0b6 100644 --- a/content/events/src/nsDOMEvent.cpp +++ b/content/events/src/nsDOMEvent.cpp @@ -63,6 +63,8 @@ #include "mozilla/Preferences.h" #include "nsJSUtils.h" #include "DictionaryHelpers.h" +#include "nsLayoutUtils.h" +#include "nsIScrollableFrame.h" using namespace mozilla; @@ -110,6 +112,12 @@ static const char* const sEventNames[] = { "MozTouchDown", "MozTouchMove", "MozTouchUp", + "touchstart", + "touchend", + "touchmove", + "touchcancel", + "touchenter", + "touchleave", "MozScrolledAreaChanged", "transitionend", "animationstart", @@ -902,6 +910,13 @@ NS_METHOD nsDOMEvent::DuplicatePrivateData() isInputEvent = true; break; } + case NS_TOUCH_EVENT: + { + newEvent = new nsTouchEvent(false, msg, nsnull); + NS_ENSURE_TRUE(newEvent, NS_ERROR_OUT_OF_MEMORY); + isInputEvent = true; + break; + } default: { NS_WARNING("Unknown event type!!!"); @@ -1165,6 +1180,92 @@ nsDOMEvent::Shutdown() } } +nsIntPoint +nsDOMEvent::GetScreenCoords(nsPresContext* aPresContext, + nsEvent* aEvent, + nsIntPoint aPoint) +{ + if (!aEvent || + (aEvent->eventStructType != NS_MOUSE_EVENT && + aEvent->eventStructType != NS_POPUP_EVENT && + aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT && + aEvent->eventStructType != NS_MOZTOUCH_EVENT && + aEvent->eventStructType != NS_TOUCH_EVENT && + aEvent->eventStructType != NS_DRAG_EVENT && + aEvent->eventStructType != NS_SIMPLE_GESTURE_EVENT)) { + return nsIntPoint(0, 0); + } + + nsGUIEvent* guiEvent = static_cast(aEvent); + if (!guiEvent->widget) { + return aPoint; + } + + nsIntPoint offset = aPoint + guiEvent->widget->WidgetToScreenOffset(); + nscoord factor = aPresContext->DeviceContext()->UnscaledAppUnitsPerDevPixel(); + return nsIntPoint(nsPresContext::AppUnitsToIntCSSPixels(offset.x * factor), + nsPresContext::AppUnitsToIntCSSPixels(offset.y * factor)); +} + +//static +nsIntPoint +nsDOMEvent::GetPageCoords(nsPresContext* aPresContext, + nsEvent* aEvent, + nsIntPoint aPoint, + nsIntPoint aDefaultPoint) +{ + nsIntPoint pagePoint = nsDOMEvent::GetClientCoords(aPresContext, + aEvent, + aPoint, + aDefaultPoint); + + // If there is some scrolling, add scroll info to client point. + if (aPresContext && aPresContext->GetPresShell()) { + nsIPresShell* shell = aPresContext->GetPresShell(); + nsIScrollableFrame* scrollframe = shell->GetRootScrollFrameAsScrollable(); + if (scrollframe) { + nsPoint pt = scrollframe->GetScrollPosition(); + pagePoint += nsIntPoint(nsPresContext::AppUnitsToIntCSSPixels(pt.x), + nsPresContext::AppUnitsToIntCSSPixels(pt.y)); + } + } + + return pagePoint; +} + +// static +nsIntPoint +nsDOMEvent::GetClientCoords(nsPresContext* aPresContext, + nsEvent* aEvent, + nsIntPoint aPoint, + nsIntPoint aDefaultPoint) +{ + if (!aEvent || + (aEvent->eventStructType != NS_MOUSE_EVENT && + aEvent->eventStructType != NS_POPUP_EVENT && + aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT && + aEvent->eventStructType != NS_MOZTOUCH_EVENT && + aEvent->eventStructType != NS_TOUCH_EVENT && + aEvent->eventStructType != NS_DRAG_EVENT && + aEvent->eventStructType != NS_SIMPLE_GESTURE_EVENT) || + !aPresContext || + !((nsGUIEvent*)aEvent)->widget) { + return aDefaultPoint; + } + + nsPoint pt(0, 0); + nsIPresShell* shell = aPresContext->GetPresShell(); + if (!shell) { + return nsIntPoint(0, 0); + } + nsIFrame* rootFrame = shell->GetRootFrame(); + if (rootFrame) + pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, aPoint, rootFrame); + + return nsIntPoint(nsPresContext::AppUnitsToIntCSSPixels(pt.x), + nsPresContext::AppUnitsToIntCSSPixels(pt.y)); +} + // To be called ONLY by nsDOMEvent::GetType (which has the additional // logic for handling user-defined events). // static @@ -1347,6 +1448,18 @@ const char* nsDOMEvent::GetEventName(PRUint32 aEventType) return sEventNames[eDOMEvents_SVGScroll]; case NS_SVG_ZOOM: return sEventNames[eDOMEvents_SVGZoom]; + case NS_TOUCH_START: + return sEventNames[eDOMEvents_touchstart]; + case NS_TOUCH_MOVE: + return sEventNames[eDOMEvents_touchmove]; + case NS_TOUCH_END: + return sEventNames[eDOMEvents_touchend]; + case NS_TOUCH_ENTER: + return sEventNames[eDOMEvents_touchenter]; + case NS_TOUCH_LEAVE: + return sEventNames[eDOMEvents_touchleave]; + case NS_TOUCH_CANCEL: + return sEventNames[eDOMEvents_touchcancel]; case NS_SMIL_BEGIN: return sEventNames[eDOMEvents_beginEvent]; case NS_SMIL_END: diff --git a/content/events/src/nsDOMEvent.h b/content/events/src/nsDOMEvent.h index d00be0695bd..f3b53c04890 100644 --- a/content/events/src/nsDOMEvent.h +++ b/content/events/src/nsDOMEvent.h @@ -195,6 +195,12 @@ public: eDOMEvents_MozTouchDown, eDOMEvents_MozTouchMove, eDOMEvents_MozTouchUp, + eDOMEvents_touchstart, + eDOMEvents_touchend, + eDOMEvents_touchmove, + eDOMEvents_touchcancel, + eDOMEvents_touchenter, + eDOMEvents_touchleave, eDOMEvents_MozScrolledAreaChanged, eDOMEvents_transitionend, eDOMEvents_animationstart, @@ -242,6 +248,17 @@ public: static void Shutdown(); static const char* GetEventName(PRUint32 aEventType); + static nsIntPoint GetClientCoords(nsPresContext* aPresContext, + nsEvent* aEvent, + nsIntPoint aPoint, + nsIntPoint aDefaultPoint); + static nsIntPoint GetPageCoords(nsPresContext* aPresContext, + nsEvent* aEvent, + nsIntPoint aPoint, + nsIntPoint aDefaultPoint); + static nsIntPoint GetScreenCoords(nsPresContext* aPresContext, + nsEvent* aEvent, + nsIntPoint aPoint); protected: // Internal helper functions diff --git a/content/events/src/nsDOMMouseEvent.cpp b/content/events/src/nsDOMMouseEvent.cpp index cbb05267f61..cf0ba93ecf1 100644 --- a/content/events/src/nsDOMMouseEvent.cpp +++ b/content/events/src/nsDOMMouseEvent.cpp @@ -236,7 +236,13 @@ nsDOMMouseEvent::GetRelatedTarget(nsIDOMEventTarget** aRelatedTarget) NS_METHOD nsDOMMouseEvent::GetScreenX(PRInt32* aScreenX) { NS_ENSURE_ARG_POINTER(aScreenX); +#ifdef MOZ_TOUCH + *aScreenX = nsDOMEvent::GetScreenCoords(mPresContext, + mEvent, + mEvent->refPoint).x; +#else *aScreenX = GetScreenPoint().x; +#endif return NS_OK; } @@ -244,7 +250,13 @@ NS_IMETHODIMP nsDOMMouseEvent::GetScreenY(PRInt32* aScreenY) { NS_ENSURE_ARG_POINTER(aScreenY); +#ifdef MOZ_TOUCH + *aScreenY = nsDOMEvent::GetScreenCoords(mPresContext, + mEvent, + mEvent->refPoint).y; +#else *aScreenY = GetScreenPoint().y; +#endif return NS_OK; } @@ -252,7 +264,14 @@ nsDOMMouseEvent::GetScreenY(PRInt32* aScreenY) NS_METHOD nsDOMMouseEvent::GetClientX(PRInt32* aClientX) { NS_ENSURE_ARG_POINTER(aClientX); +#ifdef MOZ_TOUCH + *aClientX = nsDOMEvent::GetClientCoords(mPresContext, + mEvent, + mEvent->refPoint, + mClientPoint).x; +#else *aClientX = GetClientPoint().x; +#endif return NS_OK; } @@ -260,7 +279,14 @@ NS_IMETHODIMP nsDOMMouseEvent::GetClientY(PRInt32* aClientY) { NS_ENSURE_ARG_POINTER(aClientY); +#ifdef MOZ_TOUCH + *aClientY = nsDOMEvent::GetClientCoords(mPresContext, + mEvent, + mEvent->refPoint, + mClientPoint).y; +#else *aClientY = GetClientPoint().y; +#endif return NS_OK; } diff --git a/content/events/src/nsDOMTouchEvent.cpp b/content/events/src/nsDOMTouchEvent.cpp index 0aae2f2302c..b8ce7eaf316 100644 --- a/content/events/src/nsDOMTouchEvent.cpp +++ b/content/events/src/nsDOMTouchEvent.cpp @@ -42,6 +42,7 @@ #include "nsIXPCScriptable.h" #include "nsContentUtils.h" #include "mozilla/Preferences.h" +#include "nsPresContext.h" using namespace mozilla; @@ -75,56 +76,56 @@ nsDOMTouch::GetTarget(nsIDOMEventTarget** aTarget) NS_IMETHODIMP nsDOMTouch::GetScreenX(PRInt32* aScreenX) { - *aScreenX = mScreenX; + *aScreenX = mScreenPoint.x; return NS_OK; } NS_IMETHODIMP nsDOMTouch::GetScreenY(PRInt32* aScreenY) { - *aScreenY = mScreenY; + *aScreenY = mScreenPoint.y; return NS_OK; } NS_IMETHODIMP nsDOMTouch::GetClientX(PRInt32* aClientX) { - *aClientX = mClientX; + *aClientX = mClientPoint.x; return NS_OK; } NS_IMETHODIMP nsDOMTouch::GetClientY(PRInt32* aClientY) { - *aClientY = mClientY; + *aClientY = mClientPoint.y; return NS_OK; } NS_IMETHODIMP nsDOMTouch::GetPageX(PRInt32* aPageX) { - *aPageX = mPageX; + *aPageX = mPagePoint.x; return NS_OK; } NS_IMETHODIMP nsDOMTouch::GetPageY(PRInt32* aPageY) { - *aPageY = mPageY; + *aPageY = mPagePoint.y; return NS_OK; } NS_IMETHODIMP nsDOMTouch::GetRadiusX(PRInt32* aRadiusX) { - *aRadiusX = mRadiusX; + *aRadiusX = mRadius.x; return NS_OK; } NS_IMETHODIMP nsDOMTouch::GetRadiusY(PRInt32* aRadiusY) { - *aRadiusY = mRadiusY; + *aRadiusY = mRadius.y; return NS_OK; } @@ -142,7 +143,27 @@ nsDOMTouch::GetForce(float* aForce) return NS_OK; } +bool +nsDOMTouch::Equals(nsIDOMTouch* aTouch) +{ + float force; + float orientation; + PRInt32 radiusX, radiusY; + aTouch->GetForce(&force); + aTouch->GetRotationAngle(&orientation); + aTouch->GetRadiusX(&radiusX); + aTouch->GetRadiusY(&radiusY); + return mRefPoint != aTouch->mRefPoint || + (mForce != force) || + (mRotationAngle != orientation) || + (mRadius.x != radiusX) || (mRadius.y != radiusY); +} + // TouchList +nsDOMTouchList::nsDOMTouchList(nsTArray > &aTouches) +{ + mPoints.AppendElements(aTouches); +} DOMCI_DATA(TouchList, nsDOMTouchList) @@ -155,11 +176,11 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMTouchList) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMTouchList) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mPoints) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_OF_NSCOMPTR(mPoints) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMTouchList) - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mPoints) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mPoints) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMTouchList) @@ -168,14 +189,14 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMTouchList) NS_IMETHODIMP nsDOMTouchList::GetLength(PRUint32* aLength) { - *aLength = mPoints.Count(); + *aLength = mPoints.Length(); return NS_OK; } NS_IMETHODIMP nsDOMTouchList::Item(PRUint32 aIndex, nsIDOMTouch** aRetVal) { - NS_IF_ADDREF(*aRetVal = mPoints.SafeObjectAt(aIndex)); + NS_IF_ADDREF(*aRetVal = mPoints.SafeElementAt(aIndex, nsnull)); return NS_OK; } @@ -183,7 +204,7 @@ NS_IMETHODIMP nsDOMTouchList::IdentifiedTouch(PRInt32 aIdentifier, nsIDOMTouch** aRetVal) { *aRetVal = nsnull; - for (PRInt32 i = 0; i < mPoints.Count(); ++i) { + for (PRUint32 i = 0; i < mPoints.Length(); ++i) { nsCOMPtr point = mPoints[i]; PRInt32 identifier; if (point && NS_SUCCEEDED(point->GetIdentifier(&identifier)) && @@ -198,12 +219,18 @@ nsDOMTouchList::IdentifiedTouch(PRInt32 aIdentifier, nsIDOMTouch** aRetVal) // TouchEvent nsDOMTouchEvent::nsDOMTouchEvent(nsPresContext* aPresContext, - nsInputEvent* aEvent) + nsTouchEvent* aEvent) : nsDOMUIEvent(aPresContext, aEvent ? aEvent : - new nsInputEvent(false, 0, nsnull)) + new nsTouchEvent(false, 0, nsnull)) { if (aEvent) { mEventIsInternal = false; + + for (PRUint32 i = 0; i < aEvent->touches.Length(); ++i) { + nsIDOMTouch *touch = aEvent->touches[i]; + nsDOMTouch *domtouch = static_cast(touch); + domtouch->InitializePoints(mPresContext, aEvent); + } } else { mEventIsInternal = true; mEvent->time = PR_Now(); @@ -213,7 +240,7 @@ nsDOMTouchEvent::nsDOMTouchEvent(nsPresContext* aPresContext, nsDOMTouchEvent::~nsDOMTouchEvent() { if (mEventIsInternal && mEvent) { - delete static_cast(mEvent); + delete static_cast(mEvent); mEvent = nsnull; } } @@ -257,7 +284,11 @@ nsDOMTouchEvent::InitTouchEvent(const nsAString& aType, nsIDOMTouchList* aTargetTouches, nsIDOMTouchList* aChangedTouches) { - nsresult rv = nsDOMUIEvent::InitUIEvent(aType, aCanBubble, aCancelable, aView, aDetail); + nsresult rv = nsDOMUIEvent::InitUIEvent(aType, + aCanBubble, + aCancelable, + aView, + aDetail); NS_ENSURE_SUCCESS(rv, rv); static_cast(mEvent)->isControl = aCtrlKey; @@ -273,22 +304,80 @@ nsDOMTouchEvent::InitTouchEvent(const nsAString& aType, NS_IMETHODIMP nsDOMTouchEvent::GetTouches(nsIDOMTouchList** aTouches) { - NS_IF_ADDREF(*aTouches = mTouches); - return NS_OK; + NS_ENSURE_ARG_POINTER(aTouches); + NS_ENSURE_STATE(mEvent); + nsRefPtr t; + + if (mTouches) { + return CallQueryInterface(mTouches, aTouches); + } + + nsTouchEvent* touchEvent = static_cast(mEvent); + if (mEvent->message == NS_TOUCH_END || mEvent->message == NS_TOUCH_CANCEL) { + // for touchend events, remove any changed touches from the touches array + nsTArray > unchangedTouches; + nsTArray > touches = touchEvent->touches; + for (PRUint32 i = 0; i < touches.Length(); ++i) { + if (!touches[i]->mChanged) { + unchangedTouches.AppendElement(touches[i]); + } + } + t = new nsDOMTouchList(unchangedTouches); + } else { + t = new nsDOMTouchList(touchEvent->touches); + } + mTouches = t; + return CallQueryInterface(mTouches, aTouches); } NS_IMETHODIMP nsDOMTouchEvent::GetTargetTouches(nsIDOMTouchList** aTargetTouches) { - NS_IF_ADDREF(*aTargetTouches = mTargetTouches); - return NS_OK; + NS_ENSURE_ARG_POINTER(aTargetTouches); + NS_ENSURE_STATE(mEvent); + + if (mTargetTouches) { + return CallQueryInterface(mTargetTouches, aTargetTouches); + } + + nsTArray > targetTouches; + nsTouchEvent* touchEvent = static_cast(mEvent); + nsTArray > touches = touchEvent->touches; + for (PRUint32 i = 0; i < touches.Length(); ++i) { + // for touchend/cancel events, don't append to the target list if this is a + // touch that is ending + if ((mEvent->message != NS_TOUCH_END && + mEvent->message != NS_TOUCH_CANCEL) || !touches[i]->mChanged) { + nsIDOMEventTarget* targetPtr = touches[i]->GetTarget(); + if (targetPtr == mEvent->target) { + targetTouches.AppendElement(touches[i]); + } + } + } + mTargetTouches = new nsDOMTouchList(targetTouches); + return CallQueryInterface(mTargetTouches, aTargetTouches); } NS_IMETHODIMP nsDOMTouchEvent::GetChangedTouches(nsIDOMTouchList** aChangedTouches) { - NS_IF_ADDREF(*aChangedTouches = mChangedTouches); - return NS_OK; + NS_ENSURE_ARG_POINTER(aChangedTouches); + NS_ENSURE_STATE(mEvent); + + if (mChangedTouches) { + return CallQueryInterface(mChangedTouches, aChangedTouches); + } + + nsTArray > changedTouches; + nsTouchEvent* touchEvent = static_cast(mEvent); + nsTArray > touches = touchEvent->touches; + for (PRUint32 i = 0; i < touches.Length(); ++i) { + if (touches[i]->mChanged) { + changedTouches.AppendElement(touches[i]); + } + } + mChangedTouches = new nsDOMTouchList(changedTouches); + return CallQueryInterface(mChangedTouches, aChangedTouches); } NS_IMETHODIMP @@ -337,7 +426,7 @@ nsDOMTouchEvent::PrefEnabled() nsresult NS_NewDOMTouchEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, - nsInputEvent *aEvent) + nsTouchEvent *aEvent) { nsDOMTouchEvent* it = new nsDOMTouchEvent(aPresContext, aEvent); diff --git a/content/events/src/nsDOMTouchEvent.h b/content/events/src/nsDOMTouchEvent.h index 182d158f24b..52119b2a0f8 100644 --- a/content/events/src/nsDOMTouchEvent.h +++ b/content/events/src/nsDOMTouchEvent.h @@ -40,7 +40,7 @@ #include "nsDOMUIEvent.h" #include "nsIDOMTouchEvent.h" #include "nsString.h" -#include "nsCOMArray.h" +#include "nsTArray.h" class nsDOMTouch : public nsIDOMTouch { @@ -57,33 +57,72 @@ public: PRInt32 aRadiusY, float aRotationAngle, float aForce) - : mTarget(aTarget), - mIdentifier(aIdentifier), - mPageX(aPageX), - mPageY(aPageY), - mScreenX(aScreenX), - mScreenY(aScreenY), - mClientX(aClientX), - mClientY(aClientY), - mRadiusX(aRadiusX), - mRadiusY(aRadiusY), - mRotationAngle(aRotationAngle), - mForce(aForce) - {} + { + mTarget = aTarget; + mIdentifier = aIdentifier; + mPagePoint = nsIntPoint(aPageX, aPageY); + mScreenPoint = nsIntPoint(aScreenX, aScreenY); + mClientPoint = nsIntPoint(aClientX, aClientY); + mRefPoint = nsIntPoint(0, 0); + mPointsInitialized = true; + mRadius.x = aRadiusX; + mRadius.y = aRadiusY; + mRotationAngle = aRotationAngle; + mForce = aForce; + + mChanged = false; + mMessage = 0; + } + nsDOMTouch(PRInt32 aIdentifier, + nsIntPoint aPoint, + nsIntPoint aRadius, + float aRotationAngle, + float aForce) + { + mIdentifier = aIdentifier; + mPagePoint = nsIntPoint(0, 0); + mScreenPoint = nsIntPoint(0, 0); + mClientPoint = nsIntPoint(0, 0); + mRefPoint = aPoint; + mPointsInitialized = false; + mRadius = aRadius; + mRotationAngle = aRotationAngle; + mForce = aForce; + + mChanged = false; + mMessage = 0; + } NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_CLASS(nsDOMTouch) NS_DECL_NSIDOMTOUCH + void InitializePoints(nsPresContext* aPresContext, nsEvent* aEvent) + { + if (mPointsInitialized) { + return; + } + mClientPoint = nsDOMEvent::GetClientCoords(aPresContext, + aEvent, + mRefPoint, + mClientPoint); + mPagePoint = nsDOMEvent::GetPageCoords(aPresContext, + aEvent, + mRefPoint, + mClientPoint); + mScreenPoint = nsDOMEvent::GetScreenCoords(aPresContext, aEvent, mRefPoint); + mPointsInitialized = true; + } + void SetTarget(nsIDOMEventTarget *aTarget) + { + mTarget = aTarget; + } + bool Equals(nsIDOMTouch* aTouch); protected: - nsCOMPtr mTarget; + bool mPointsInitialized; PRInt32 mIdentifier; - PRInt32 mPageX; - PRInt32 mPageY; - PRInt32 mScreenX; - PRInt32 mScreenY; - PRInt32 mClientX; - PRInt32 mClientY; - PRInt32 mRadiusX; - PRInt32 mRadiusY; + nsIntPoint mPagePoint; + nsIntPoint mClientPoint; + nsIntPoint mScreenPoint; + nsIntPoint mRadius; float mRotationAngle; float mForce; }; @@ -94,25 +133,28 @@ public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_CLASS(nsDOMTouchList) NS_DECL_NSIDOMTOUCHLIST + + nsDOMTouchList() { } + nsDOMTouchList(nsTArray > &aTouches); void Append(nsIDOMTouch* aPoint) { - mPoints.AppendObject(aPoint); + mPoints.AppendElement(aPoint); } nsIDOMTouch* GetItemAt(PRUint32 aIndex) { - return mPoints.SafeObjectAt(aIndex); + return mPoints.SafeElementAt(aIndex, nsnull); } protected: - nsCOMArray mPoints; + nsTArray > mPoints; }; class nsDOMTouchEvent : public nsDOMUIEvent, public nsIDOMTouchEvent { public: - nsDOMTouchEvent(nsPresContext* aPresContext, nsInputEvent* aEvent); + nsDOMTouchEvent(nsPresContext* aPresContext, nsTouchEvent* aEvent); virtual ~nsDOMTouchEvent(); NS_DECL_ISUPPORTS_INHERITED diff --git a/content/events/src/nsDOMUIEvent.cpp b/content/events/src/nsDOMUIEvent.cpp index cd067ec7a57..2adcd5c70e6 100644 --- a/content/events/src/nsDOMUIEvent.cpp +++ b/content/events/src/nsDOMUIEvent.cpp @@ -244,7 +244,18 @@ NS_IMETHODIMP nsDOMUIEvent::GetPageX(PRInt32* aPageX) { NS_ENSURE_ARG_POINTER(aPageX); +#ifdef MOZ_TOUCH + if (mPrivateDataDuplicated) { + *aPageX = mPagePoint.x; + } else { + *aPageX = nsDOMEvent::GetPageCoords(mPresContext, + mEvent, + mEvent->refPoint, + mClientPoint).x; + } +#else *aPageX = GetPagePoint().x; +#endif return NS_OK; } @@ -252,7 +263,18 @@ NS_IMETHODIMP nsDOMUIEvent::GetPageY(PRInt32* aPageY) { NS_ENSURE_ARG_POINTER(aPageY); +#ifdef MOZ_TOUCH + if (mPrivateDataDuplicated) { + *aPageY = mPagePoint.y; + } else { + *aPageY = nsDOMEvent::GetPageCoords(mPresContext, + mEvent, + mEvent->refPoint, + mClientPoint).y; + } +#else *aPageY = GetPagePoint().y; +#endif return NS_OK; } @@ -338,6 +360,7 @@ nsDOMUIEvent::GetLayerPoint() mEvent->eventStructType != NS_POPUP_EVENT && mEvent->eventStructType != NS_MOUSE_SCROLL_EVENT && mEvent->eventStructType != NS_MOZTOUCH_EVENT && + mEvent->eventStructType != NS_TOUCH_EVENT && mEvent->eventStructType != NS_DRAG_EVENT && mEvent->eventStructType != NS_SIMPLE_GESTURE_EVENT) || !mPresContext || @@ -390,11 +413,27 @@ nsDOMUIEvent::GetIsChar(bool* aIsChar) NS_METHOD nsDOMUIEvent::DuplicatePrivateData() { +#ifdef MOZ_TOUCH + mClientPoint = nsDOMEvent::GetClientCoords(mPresContext, + mEvent, + mEvent->refPoint, + mClientPoint); + mLayerPoint = GetLayerPoint(); + mPagePoint = nsDOMEvent::GetPageCoords(mPresContext, + mEvent, + mEvent->refPoint, + mClientPoint); + // GetScreenPoint converts mEvent->refPoint to right coordinates. + nsIntPoint screenPoint = nsDOMEvent::GetScreenCoords(mPresContext, + mEvent, + mEvent->refPoint); +#else mClientPoint = GetClientPoint(); mLayerPoint = GetLayerPoint(); mPagePoint = GetPagePoint(); // GetScreenPoint converts mEvent->refPoint to right coordinates. nsIntPoint screenPoint = GetScreenPoint(); +#endif nsresult rv = nsDOMEvent::DuplicatePrivateData(); if (NS_SUCCEEDED(rv)) { mEvent->refPoint = screenPoint; diff --git a/content/events/src/nsDOMUIEvent.h b/content/events/src/nsDOMUIEvent.h index 05f3969991e..69626f4f9da 100644 --- a/content/events/src/nsDOMUIEvent.h +++ b/content/events/src/nsDOMUIEvent.h @@ -68,8 +68,8 @@ public: JSContext* aCx, jsval* aVal); protected: // Internal helper functions - nsIntPoint GetClientPoint(); nsIntPoint GetScreenPoint(); + nsIntPoint GetClientPoint(); nsIntPoint GetLayerPoint(); nsIntPoint GetPagePoint(); diff --git a/content/events/src/nsEventDispatcher.cpp b/content/events/src/nsEventDispatcher.cpp index 81a0028015d..96d22a7f2bf 100644 --- a/content/events/src/nsEventDispatcher.cpp +++ b/content/events/src/nsEventDispatcher.cpp @@ -808,6 +808,9 @@ nsEventDispatcher::CreateEvent(nsPresContext* aPresContext, case NS_MOZTOUCH_EVENT: return NS_NewDOMMozTouchEvent(aDOMEvent, aPresContext, static_cast(aEvent)); + case NS_TOUCH_EVENT: + return NS_NewDOMTouchEvent(aDOMEvent, aPresContext, + static_cast(aEvent)); case NS_TRANSITION_EVENT: return NS_NewDOMTransitionEvent(aDOMEvent, aPresContext, static_cast(aEvent)); diff --git a/content/events/test/Makefile.in b/content/events/test/Makefile.in index 3ff61261ffb..447a3b5a88e 100644 --- a/content/events/test/Makefile.in +++ b/content/events/test/Makefile.in @@ -130,6 +130,12 @@ _TEST_FILES += \ $(NULL) endif +ifeq (android,$(MOZ_WIDGET_TOOLKIT)) +_TEST_FILES += \ + test_bug603008.html \ + $(NULL) +endif + _CHROME_FILES = \ test_bug336682_2.xul \ test_bug336682.js \ diff --git a/content/events/test/test_bug603008.html b/content/events/test/test_bug603008.html new file mode 100644 index 00000000000..d466e4b3836 --- /dev/null +++ b/content/events/test/test_bug603008.html @@ -0,0 +1,524 @@ + + + + + Test for Bug 603008 + + + + +Mozilla Bug 603008 +

+ +
+
+
+
+ testTarget + testTarget +
+ + diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index c4ae9246385..b662b230cbd 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -49,6 +49,8 @@ #include "nsEventStateManager.h" #include "nsFrameManager.h" #include "nsRefreshDriver.h" +#include "nsDOMTouchEvent.h" +#include "nsIDOMTouchEvent.h" #include "nsIScrollableFrame.h" @@ -550,6 +552,79 @@ nsDOMWindowUtils::SendMouseScrollEvent(const nsAString& aType, return widget->DispatchEvent(&event, status); } + +NS_IMETHODIMP +nsDOMWindowUtils::SendTouchEvent(const nsAString& aType, + PRUint32 *aIdentifiers, + PRInt32 *aXs, + PRInt32 *aYs, + PRUint32 *aRxs, + PRUint32 *aRys, + float *aRotationAngles, + float *aForces, + PRUint32 aCount, + PRInt32 aModifiers, + bool aIgnoreRootScrollFrame, + bool *aPreventDefault) +{ + if (!IsUniversalXPConnectCapable()) { + return NS_ERROR_DOM_SECURITY_ERR; + } + + // get the widget to send the event to + nsPoint offset; + nsCOMPtr widget = GetWidget(&offset); + if (!widget) { + return NS_ERROR_NULL_POINTER; + } + PRInt32 msg; + if (aType.EqualsLiteral("touchstart")) { + msg = NS_TOUCH_START; + } else if (aType.EqualsLiteral("touchmove")) { + msg = NS_TOUCH_MOVE; + } else if (aType.EqualsLiteral("touchend")) { + msg = NS_TOUCH_END; + } else if (aType.EqualsLiteral("touchcancel")) { + msg = NS_TOUCH_CANCEL; + } else { + return NS_ERROR_UNEXPECTED; + } + nsTouchEvent event(true, msg, widget); + event.isShift = (aModifiers & nsIDOMNSEvent::SHIFT_MASK) ? true : false; + event.isControl = (aModifiers & nsIDOMNSEvent::CONTROL_MASK) ? true : false; + event.isAlt = (aModifiers & nsIDOMNSEvent::ALT_MASK) ? true : false; + event.isMeta = (aModifiers & nsIDOMNSEvent::META_MASK) ? true : false; + event.widget = widget; + event.time = PR_Now(); + + nsPresContext* presContext = GetPresContext(); + if (!presContext) { + return NS_ERROR_FAILURE; + } + event.touches.SetCapacity(aCount); + PRInt32 appPerDev = presContext->AppUnitsPerDevPixel(); + for (int i = 0; i < aCount; ++i) { + nsIntPoint pt(0, 0); + pt.x = + NSAppUnitsToIntPixels(nsPresContext::CSSPixelsToAppUnits(aXs[i]) + offset.x, + appPerDev); + pt.y = + NSAppUnitsToIntPixels(nsPresContext::CSSPixelsToAppUnits(aYs[i]) + offset.y, + appPerDev); + nsCOMPtr t(new nsDOMTouch(aIdentifiers[i], + pt, + nsIntPoint(aRxs[i], aRys[i]), + aRotationAngles[i], + aForces[i])); + event.touches.AppendElement(t); + } + + nsEventStatus status; + nsresult rv = widget->DispatchEvent(&event, status); + *aPreventDefault = (status == nsEventStatus_eConsumeNoDefault); + return rv; +} + NS_IMETHODIMP nsDOMWindowUtils::SendKeyEvent(const nsAString& aType, PRInt32 aKeyCode, diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index 3f9330791b8..efc272d1bea 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -68,8 +68,9 @@ interface nsIDOMWindow; interface nsIDOMBlob; interface nsIDOMFile; interface nsIFile; +interface nsIDOMTouch; -[scriptable, uuid(b9c1f815-c2f2-4607-a060-6a8566581927)] +[scriptable, uuid(e01171b0-712a-47ce-8552-b7b2ef0a2507)] interface nsIDOMWindowUtils : nsISupports { /** @@ -227,6 +228,45 @@ interface nsIDOMWindowUtils : nsISupports { in long aModifiers, [optional] in boolean aIgnoreRootScrollFrame); + /** Synthesize a touch event. The event types supported are: + * touchstart, touchend, touchmove, and touchcancel + * + * Events are sent in coordinates offset by aX and aY from the window. + * + * Cannot be accessed from unprivileged context (not content-accessible) + * Will throw a DOM security error if called without UniversalXPConnect + * privileges. + * + * The event is dispatched via the toplevel window, so it could go to any + * window under the toplevel window, in some cases it could never reach this + * window at all. + * + * @param aType event type + * @param xs array of offsets in CSS pixels for each touch to be sent + * @param ys array of offsets in CSS pixels for each touch to be sent + * @param rxs array of radii in CSS pixels for each touch to be sent + * @param rys array of radii in CSS pixels for each touch to be sent + * @param rotationAngles array of angles in degrees for each touch to be sent + * @param forces array of forces (floats from 0 to 1) for each touch to be sent + * @param count number of touches in this set + * @param aModifiers modifiers pressed, using constants defined in nsIDOMNSEvent + * @param aIgnoreRootScrollFrame whether the event should ignore viewport bounds + * during dispatch + * + * returns true if the page called prevent default on this touch event + */ + boolean sendTouchEvent(in AString aType, + [array, size_is(count)] in PRUint32 aIdentifiers, + [array, size_is(count)] in PRInt32 aXs, + [array, size_is(count)] in PRInt32 aYs, + [array, size_is(count)] in PRUint32 aRxs, + [array, size_is(count)] in PRUint32 aRys, + [array, size_is(count)] in float aRotationAngles, + [array, size_is(count)] in float aForces, + in PRUint32 count, + in long aModifiers, + [optional] in boolean aIgnoreRootScrollFrame); + /** The same as sendMouseEvent but ensures that the event is dispatched to * this DOM window or one of its children. */ diff --git a/dom/interfaces/events/nsIDOMTouchEvent.idl b/dom/interfaces/events/nsIDOMTouchEvent.idl index 66d84c4947b..f2993f68a62 100644 --- a/dom/interfaces/events/nsIDOMTouchEvent.idl +++ b/dom/interfaces/events/nsIDOMTouchEvent.idl @@ -36,13 +36,17 @@ * ***** END LICENSE BLOCK ***** */ #include "nsIDOMUIEvent.idl" +%{C++ +#include "nsWeakPtr.h" +#include "nsPoint.h" +%} interface nsIVariant; /** * @see http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html */ -[scriptable, uuid(98bc0f7d-5bff-4387-9c42-58af54b48dd5)] +[scriptable, builtinclass, uuid(98bc0f7d-5bff-4387-9c42-58af54b48dd5)] interface nsIDOMTouch : nsISupports { readonly attribute long identifier; readonly attribute nsIDOMEventTarget target; @@ -56,6 +60,14 @@ interface nsIDOMTouch : nsISupports { readonly attribute long radiusY; readonly attribute float rotationAngle; readonly attribute float force; + %{C++ + nsCOMPtr mTarget; + nsIDOMEventTarget *GetTarget() { return mTarget; } + void SetTarget(nsIDOMEventTarget *target) { mTarget = target; } + nsIntPoint mRefPoint; + bool mChanged; + PRUint32 mMessage; + %} }; [scriptable, uuid(60706eb7-d50d-4379-b01c-e78e6af84213)] diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index d33061ee79e..c13f99b04da 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -69,6 +69,8 @@ #include "nsWeakReference.h" #include // for FILE definition #include "nsChangeHint.h" +#include "nsGUIEvent.h" +#include "nsInterfaceHashtable.h" class nsIContent; class nsIDocument; @@ -1047,6 +1049,9 @@ public: static CapturingContentInfo gCaptureInfo; + static nsInterfaceHashtable gCaptureTouchList; + static bool gPreventMouseEvents; + /** * When capturing content is set, it traps all mouse events and retargets * them at this content node. If capturing is not allowed diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index da914b4d6ae..ed956b46951 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -956,13 +956,20 @@ nsLayoutUtils::GetEventCoordinatesRelativeTo(const nsEvent* aEvent, nsIFrame* aF aEvent->eventStructType != NS_SIMPLE_GESTURE_EVENT && aEvent->eventStructType != NS_GESTURENOTIFY_EVENT && aEvent->eventStructType != NS_MOZTOUCH_EVENT && +#ifdef MOZ_TOUCH + aEvent->eventStructType != NS_TOUCH_EVENT && +#endif aEvent->eventStructType != NS_QUERY_CONTENT_EVENT)) return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); const nsGUIEvent* GUIEvent = static_cast(aEvent); +#ifdef MOZ_TOUCH + return GetEventCoordinatesRelativeTo(aEvent, + GUIEvent->refPoint, + aFrame); +#else if (!GUIEvent->widget) return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); - /* If we walk up the frame tree and discover that any of the frames are * transformed, we need to do extra work to convert from the global * space to the local space. @@ -973,7 +980,6 @@ nsLayoutUtils::GetEventCoordinatesRelativeTo(const nsEvent* aEvent, nsIFrame* aF for (nsIFrame* f = aFrame; f; f = GetCrossDocParentFrame(f)) { if (f->IsTransformed()) transformFound = true; - rootFrame = f; } @@ -1004,6 +1010,68 @@ nsLayoutUtils::GetEventCoordinatesRelativeTo(const nsEvent* aEvent, nsIFrame* aF * so we can just subtract out the different. */ return widgetToView - aFrame->GetOffsetToCrossDoc(rootFrame); +#endif +} + +nsPoint +nsLayoutUtils::GetEventCoordinatesRelativeTo(const nsEvent* aEvent, + const nsIntPoint aPoint, + nsIFrame* aFrame) +{ + if (!aFrame) { + return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); + } + + const nsGUIEvent* GUIEvent = static_cast(aEvent); + nsIWidget* widget = GUIEvent->widget; + if (!widget) { + return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); + } + + /* If we walk up the frame tree and discover that any of the frames are + * transformed, we need to do extra work to convert from the global + * space to the local space. + */ + nsIFrame* rootFrame = aFrame; + bool transformFound = false; + for (nsIFrame* f = aFrame; f; f = GetCrossDocParentFrame(f)) { + if (f->IsTransformed()) { + transformFound = true; + } + + rootFrame = f; + } + + nsIView* rootView = rootFrame->GetView(); + if (!rootView) { + return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); + } + + nsPoint widgetToView = TranslateWidgetToView(rootFrame->PresContext(), + widget, aPoint, rootView); + + if (widgetToView == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) { + return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); + } + + // Convert from root document app units to app units of the document aFrame + // is in. + PRInt32 rootAPD = rootFrame->PresContext()->AppUnitsPerDevPixel(); + PRInt32 localAPD = aFrame->PresContext()->AppUnitsPerDevPixel(); + widgetToView = widgetToView.ConvertAppUnits(rootAPD, localAPD); + + /* If we encountered a transform, we can't do simple arithmetic to figure + * out how to convert back to aFrame's coordinates and must use the CTM. + */ + if (transformFound) { + return TransformRootPointToFrame(aFrame, widgetToView); + } + + /* Otherwise, all coordinate systems are translations of one another, + * so we can just subtract out the different. + */ + nsPoint offset = aFrame->GetOffsetToCrossDoc(rootFrame); + return widgetToView - offset; } nsIFrame* diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index de4096dbdf5..8e6676f3f3d 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -425,6 +425,20 @@ public: static nsPoint GetEventCoordinatesRelativeTo(const nsEvent* aEvent, nsIFrame* aFrame); + /** + * Get the coordinates of a given point relative to an event and a + * given frame. + * @param aEvent the event + * @param aPoint the point to get the coordinates relative to + * @param aFrame the frame to make coordinates relative to + * @return the point, or (NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) if + * for some reason the coordinates for the mouse are not known (e.g., + * the event is not a GUI event). + */ + static nsPoint GetEventCoordinatesRelativeTo(const nsEvent* aEvent, + const nsIntPoint aPoint, + nsIFrame* aFrame); + /** * Get the popup frame of a given native mouse event. * @param aPresContext only check popups within aPresContext or a descendant diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index e8e6d43eecd..b04d0a2128b 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -119,6 +119,7 @@ #include "nsILineIterator.h" // for ScrollContentIntoView #include "nsWeakPtr.h" #include "pldhash.h" +#include "nsDOMTouchEvent.h" #include "nsIObserverService.h" #include "nsIDocShell.h" // for reflow observation #include "nsIBaseWindow.h" @@ -235,6 +236,8 @@ CapturingContentInfo nsIPresShell::gCaptureInfo = { false /* mAllowed */, false /* mRetargetToElement */, false /* mPreventDrag */, nsnull /* mContent */ }; nsIContent* nsIPresShell::gKeyDownTarget; +nsInterfaceHashtable nsIPresShell::gCaptureTouchList; +bool nsIPresShell::gPreventMouseEvents = false; static PRUint32 ChangeFlag(PRUint32 aFlags, bool aOnOff, PRUint32 aFlag) @@ -5709,6 +5712,54 @@ PresShell::RecordMouseLocation(nsGUIEvent* aEvent) } } +static void +EvictTouchPoint(nsCOMPtr& aTouch) +{ + nsIWidget *widget = nsnull; + // is there an easier/better way to dig out the widget? + nsCOMPtr node(do_QueryInterface(aTouch->GetTarget())); + if (!node) { + return; + } + nsIDocument* doc = node->GetCurrentDoc(); + if (!doc) { + return; + } + nsIPresShell *presShell = doc->GetShell(); + if (!presShell) { + return; + } + nsIFrame* frame = presShell->GetRootFrame(); + if (!frame) { + return; + } + nsPoint *pt = new nsPoint(aTouch->mRefPoint.x, aTouch->mRefPoint.y); + widget = frame->GetView()->GetNearestWidget(pt); + if (!widget) { + return; + } + nsTouchEvent event(true, NS_TOUCH_END, widget); + event.isShift = false; + event.isControl = false; + event.isAlt = false; + event.isMeta = false; + event.widget = widget; + event.time = PR_IntervalNow(); + event.touches.AppendElement(aTouch); + + nsEventStatus status; + widget->DispatchEvent(&event, status); +} + +static PLDHashOperator +AppendToTouchList(const PRUint32& aKey, nsCOMPtr& aData, void *aTouchList) +{ + nsTArray > *touches = static_cast > *>(aTouchList); + aData->mChanged = false; + touches->AppendElement(aData); + return PL_DHASH_NEXT; +} + nsresult PresShell::HandleEvent(nsIFrame *aFrame, nsGUIEvent* aEvent, @@ -5921,8 +5972,59 @@ PresShell::HandleEvent(nsIFrame *aFrame, // with a window-level mouse exit event since we want to start sending // mouse out events at the root EventStateManager. if (!captureRetarget && !isWindowLevelMouseExit) { +#ifdef MOZ_TOUCH + nsPoint eventPoint; + if (aEvent->message == NS_TOUCH_START) { + // Add any new touches to the queue + nsTouchEvent* touchEvent = static_cast(aEvent); + // if there is only one touch in this touchstart event, assume that it is + // the start of a new touch session and evict any old touches in the + // queue + if (touchEvent->touches.Length() == 1) { + nsTArray > touches; + gCaptureTouchList.Enumerate(&AppendToTouchList, (void *)&touches); + for (PRUint32 i = 0; i < touches.Length(); ++i) { + EvictTouchPoint(touches[i]); + } + } + for (PRUint32 i = 0; i < touchEvent->touches.Length(); ++i) { + nsIDOMTouch *touch = touchEvent->touches[i]; + nsDOMTouch *domtouch = static_cast(touch); + touch->mMessage = aEvent->message; + + PRInt32 id = 0; + touch->GetIdentifier(&id); + if (!gCaptureTouchList.Get(id, nsnull)) { + // This event is a new touch. Mark it as a changedTouch and + // add it to the queue. + touch->mChanged = true; + gCaptureTouchList.Put(id, touch); + + eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, touch->mRefPoint, frame); + } else { + // This touch is an old touch, we need to ensure that is not + // marked as changed and set its target correctly + touch->mChanged = false; + PRInt32 id; + touch->GetIdentifier(&id); + + nsCOMPtr oldTouch; + gCaptureTouchList.Get(id, getter_AddRefs(oldTouch)); + if (oldTouch) { + nsCOMPtr targetPtr; + oldTouch->GetTarget(getter_AddRefs(targetPtr)); + domtouch->SetTarget(targetPtr); + gCaptureTouchList.Put(id, touch); + } + } + } + } else { + eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame); + } +#else nsPoint eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame); +#endif { bool ignoreRootScrollFrame = false; if (aEvent->eventStructType == NS_MOUSE_EVENT) { @@ -6322,6 +6424,9 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsEventStatus* aStatus) nsresult rv = NS_OK; if (!NS_EVENT_NEEDS_FRAME(aEvent) || GetCurrentEventFrame()) { +#ifdef MOZ_TOUCH + bool touchIsNew = false; +#endif bool isHandlingUserInput = false; // XXX How about IME events and input events for plugins? @@ -6364,6 +6469,84 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsEventStatus* aStatus) case NS_MOUSE_BUTTON_UP: isHandlingUserInput = true; break; +#ifdef MOZ_TOUCH + case NS_TOUCH_CANCEL: + case NS_TOUCH_END: { + // Remove the changed touches + // need to make sure we only remove touches that are ending here + nsTouchEvent* touchEvent = static_cast(aEvent); + nsTArray > &touches = touchEvent->touches; + for (PRUint32 i = 0; i < touches.Length(); ++i) { + nsIDOMTouch *touch = touches[i]; + nsDOMTouch *domtouch = static_cast(touch); + if (!touch) { + continue; + } + touch->mMessage = aEvent->message; + touch->mChanged = true; + nsCOMPtr oldTouch; + + PRInt32 id; + touch->GetIdentifier(&id); + + gCaptureTouchList.Get(id, getter_AddRefs(oldTouch)); + if (!oldTouch) { + continue; + } + nsCOMPtr targetPtr; + oldTouch->GetTarget(getter_AddRefs(targetPtr)); + + mCurrentEventContent = do_QueryInterface(targetPtr); + domtouch->SetTarget(targetPtr); + gCaptureTouchList.Remove(id); + } + // add any touches left in the touch list, but ensure changed=false + gCaptureTouchList.Enumerate(&AppendToTouchList, (void *)&touches); + break; + } + case NS_TOUCH_MOVE: { + // Check for touches that changed. Mark them add to queue + nsTouchEvent* touchEvent = static_cast(aEvent); + nsTArray > touches = touchEvent->touches; + bool haveChanged = false; + for (PRUint32 i = 0; i < touches.Length(); ++i) { + nsIDOMTouch *touch = touches[i]; + nsDOMTouch *domtouch = static_cast(touch); + if (!touch) { + continue; + } + PRInt32 id; + touch->GetIdentifier(&id); + touch->mMessage = aEvent->message; + + nsCOMPtr oldTouch; + gCaptureTouchList.Get(id, getter_AddRefs(oldTouch)); + if (!oldTouch) { + continue; + } + if(domtouch->Equals(oldTouch)) { + touch->mChanged = true; + haveChanged = true; + } + + nsCOMPtr targetPtr; + oldTouch->GetTarget(getter_AddRefs(targetPtr)); + domtouch->SetTarget(targetPtr); + + gCaptureTouchList.Put(id, touch); + // if we're moving from touchstart to touchmove for this touch + // we allow preventDefault to prevent mouse events + if (oldTouch->mMessage != touch->mMessage) { + touchIsNew = true; + } + } + // is nothing has changed, we should just return + if (!haveChanged) { + return NS_OK; + } + break; + } +#endif case NS_DRAGDROP_DROP: nsCOMPtr session = nsContentUtils::GetDragSession(); if (session) { @@ -6389,7 +6572,7 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsEventStatus* aStatus) if (me->isShift) aEvent->flags |= NS_EVENT_FLAG_ONLY_CHROME_DISPATCH | NS_EVENT_RETARGET_TO_NON_NATIVE_ANONYMOUS; - } + } nsAutoHandlingUserInputStatePusher userInpStatePusher(isHandlingUserInput, aEvent, mDocument); @@ -6422,7 +6605,14 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsEventStatus* aStatus) // because they do not have a reliable refPoint. if (!IsSynthesizedMouseEvent(aEvent)) { nsPresShellEventCB eventCB(this); +#ifdef MOZ_TOUCH + if (aEvent->eventStructType == NS_TOUCH_EVENT) { + DispatchTouchEvent(aEvent, aStatus, &eventCB, touchIsNew); + } + else if (mCurrentEventContent) { +#else if (mCurrentEventContent) { +#endif nsEventDispatcher::Dispatch(mCurrentEventContent, mPresContext, aEvent, nsnull, aStatus, &eventCB); } @@ -6460,6 +6650,98 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsEventStatus* aStatus) return rv; } +void +PresShell::DispatchTouchEvent(nsEvent *aEvent, + nsEventStatus* aStatus, + nsPresShellEventCB* aEventCB, + bool aTouchIsNew) +{ + nsresult rv = NS_OK; + // calling preventDefault on touchstart or the first touchmove for a + // point prevents mouse events + bool canPrevent = aEvent->message == NS_TOUCH_START || + (aEvent->message == NS_TOUCH_MOVE && aTouchIsNew); + bool preventDefault = false; + nsEventStatus tmpStatus = nsEventStatus_eIgnore; + nsTouchEvent* touchEvent = static_cast(aEvent); + // touch events should fire on all targets + if (aEvent->message != NS_TOUCH_START) { + nsTArray > touches = touchEvent->touches; + for (PRUint32 i = 0; i < touches.Length(); ++i) { + nsIDOMTouch *touch = touches[i]; + if (!touch || !touch->mChanged) { + continue; + } + // copy the event + nsCOMPtr targetPtr; + touch->GetTarget(getter_AddRefs(targetPtr)); + if (!targetPtr) { + continue; + } + + nsTouchEvent newEvent(touchEvent); + newEvent.target = targetPtr; + + nsCOMPtr content(do_QueryInterface(targetPtr)); + nsPresContext *context = nsContentUtils::GetContextForContent(content); + if (!context) { + context = mPresContext; + } + tmpStatus = nsEventStatus_eIgnore; + nsEventDispatcher::Dispatch(targetPtr, context, + &newEvent, nsnull, &tmpStatus, aEventCB); + if (nsEventStatus_eConsumeNoDefault == tmpStatus) { + preventDefault = true; + } + } + } else { + // touchevents need to have the target attribute set on each touch + nsTArray > touches = touchEvent->touches; + for (PRUint32 i = 0; i < touches.Length(); ++i) { + nsIDOMTouch *touch = touches[i]; + if (touch->mChanged) { + touch->SetTarget(mCurrentEventContent); + } + } + + if (mCurrentEventContent) { + nsEventDispatcher::Dispatch(mCurrentEventContent, mPresContext, + aEvent, nsnull, &tmpStatus, aEventCB); + } else { + nsCOMPtr targetContent; + rv = mCurrentEventFrame->GetContentForEvent(aEvent, + getter_AddRefs(targetContent)); + if (NS_SUCCEEDED(rv) && targetContent) { + nsEventDispatcher::Dispatch(targetContent, mPresContext, aEvent, + nsnull, &tmpStatus, aEventCB); + } else if (mDocument) { + nsEventDispatcher::Dispatch(mDocument, mPresContext, aEvent, + nsnull, &tmpStatus, nsnull); + } + } + if (nsEventStatus_eConsumeNoDefault == tmpStatus) { + preventDefault = true; + } + + if (touchEvent->touches.Length() == 1) { + gPreventMouseEvents = false; + } + } + + // if preventDefault was called on any of the events dispatched + // and this is touchstart, or the first touchmove, widget should consume + // other events that would be associated with this touch session + if (preventDefault && canPrevent) { + gPreventMouseEvents = true; + } + + if (gPreventMouseEvents) { + *aStatus = nsEventStatus_eConsumeNoDefault; + } else { + *aStatus = nsEventStatus_eIgnore; + } +} + // Dispatch event to content only (NOT full processing) // See also HandleEventWithTarget which does full event processing. nsresult @@ -8723,6 +9005,7 @@ void nsIPresShell::InitializeStatics() NS_ASSERTION(sLiveShells == nsnull, "InitializeStatics called multiple times!"); sLiveShells = new nsTHashtable(); sLiveShells->Init(); + gCaptureTouchList.Init(); } void nsIPresShell::ReleaseStatics() diff --git a/layout/base/nsPresShell.h b/layout/base/nsPresShell.h index 0f1e05be004..f2b80b21982 100644 --- a/layout/base/nsPresShell.h +++ b/layout/base/nsPresShell.h @@ -459,6 +459,12 @@ protected: nsresult DidCauseReflow(); friend class nsAutoCauseReflowNotifier; + bool TouchesAreEqual(nsIDOMTouch *aTouch1, nsIDOMTouch *aTouch2); + void DispatchTouchEvent(nsEvent *aEvent, + nsEventStatus* aStatus, + nsPresShellEventCB* aEventCB, + bool aTouchIsNew); + void WillDoReflow(); void DidDoReflow(bool aInterruptible); // ProcessReflowCommands returns whether we processed all our dirty roots diff --git a/view/src/nsView.cpp b/view/src/nsView.cpp index f3a737c2438..6bf7ca5200b 100644 --- a/view/src/nsView.cpp +++ b/view/src/nsView.cpp @@ -147,8 +147,7 @@ static ViewWrapper* GetWrapperFor(nsIWidget* aWidget) static nsEventStatus HandleEvent(nsGUIEvent *aEvent) { #if 0 - printf(" %d %d %d (%d,%d) \n", aEvent->widget, aEvent->widgetSupports, - aEvent->message, aEvent->point.x, aEvent->point.y); + printf(" %d %d %d (%d,%d) \n", aEvent->widget, aEvent->message); #endif nsEventStatus result = nsEventStatus_eIgnore; nsView *view = nsView::GetViewFor(aEvent->widget); @@ -327,11 +326,13 @@ nsIView* nsIView::GetViewFor(nsIWidget* aWidget) ViewWrapper* wrapper = GetWrapperFor(aWidget); - if (!wrapper) + if (!wrapper) { wrapper = GetAttachedWrapperFor(aWidget); + } - if (wrapper) + if (wrapper) { return wrapper->GetView(); + } return nsnull; } diff --git a/widget/nsEvent.h b/widget/nsEvent.h index b11f38b68a9..177d3596ad8 100644 --- a/widget/nsEvent.h +++ b/widget/nsEvent.h @@ -104,6 +104,7 @@ class nsFocusEvent; class nsSelectionEvent; class nsContentCommandEvent; class nsMozTouchEvent; +class nsTouchEvent; class nsFormEvent; class nsCommandEvent; class nsUIEvent; diff --git a/widget/nsGUIEvent.h b/widget/nsGUIEvent.h index 13a24f5cdeb..0b29d405207 100644 --- a/widget/nsGUIEvent.h +++ b/widget/nsGUIEvent.h @@ -42,6 +42,7 @@ #ifndef nsGUIEvent_h__ #define nsGUIEvent_h__ +#include "nsCOMArray.h" #include "nsPoint.h" #include "nsRect.h" #include "nsRegion.h" @@ -53,6 +54,7 @@ #include "nsIDOMMouseEvent.h" #include "nsIDOMDataTransfer.h" #include "nsIDOMEventTarget.h" +#include "nsIDOMTouchEvent.h" #include "nsWeakPtr.h" #include "nsIWidget.h" #include "nsTArray.h" @@ -60,6 +62,7 @@ #include "nsITransferable.h" #include "nsIVariant.h" #include "nsStyleConsts.h" +#include "nsAutoPtr.h" namespace mozilla { namespace dom { @@ -124,6 +127,7 @@ class nsHashKey; #define NS_UISTATECHANGE_EVENT 41 #define NS_MOZTOUCH_EVENT 42 #define NS_PLUGIN_EVENT 43 +#define NS_TOUCH_EVENT 44 // These flags are sort of a mess. They're sort of shared between event // listener flags and event flags, but only some of them. You've been @@ -546,6 +550,14 @@ class nsHashKey; #define NS_FULLSCREENCHANGE (NS_FULL_SCREEN_START) #define NS_FULLSCREENERROR (NS_FULL_SCREEN_START + 1) +#define NS_TOUCH_EVENT_START 5200 +#define NS_TOUCH_START (NS_TOUCH_EVENT_START) +#define NS_TOUCH_MOVE (NS_TOUCH_EVENT_START+1) +#define NS_TOUCH_END (NS_TOUCH_EVENT_START+2) +#define NS_TOUCH_ENTER (NS_TOUCH_EVENT_START+3) +#define NS_TOUCH_LEAVE (NS_TOUCH_EVENT_START+4) +#define NS_TOUCH_CANCEL (NS_TOUCH_EVENT_START+5) + /** * Return status for event processors, nsEventStatus, is defined in * nsEvent.h. @@ -1528,6 +1540,31 @@ public: PRUint32 streamId; }; +class nsTouchEvent : public nsInputEvent +{ +public: + nsTouchEvent(nsTouchEvent *aEvent) + :nsInputEvent(aEvent->flags & NS_EVENT_FLAG_TRUSTED ? true : false, + aEvent->message, + aEvent->widget, + NS_TOUCH_EVENT) + { + touches.AppendElements(aEvent->touches); + MOZ_COUNT_CTOR(nsTouchEvent); + } + nsTouchEvent(bool isTrusted, PRUint32 msg, nsIWidget* w) + : nsInputEvent(isTrusted, msg, w, NS_TOUCH_EVENT) + { + MOZ_COUNT_CTOR(nsTouchEvent); + } + ~nsTouchEvent() + { + MOZ_COUNT_DTOR(nsTouchEvent); + } + + nsTArray > touches; +}; + /** * Form event *