summaryrefslogtreecommitdiffstats
path: root/layout/base/TouchManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/base/TouchManager.cpp')
-rw-r--r--layout/base/TouchManager.cpp130
1 files changed, 121 insertions, 9 deletions
diff --git a/layout/base/TouchManager.cpp b/layout/base/TouchManager.cpp
index 0c34567b65..42ea78eb2e 100644
--- a/layout/base/TouchManager.cpp
+++ b/layout/base/TouchManager.cpp
@@ -7,9 +7,13 @@
#include "TouchManager.h"
+#include "Units.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/PresShell.h"
+#include "mozilla/StaticPrefs_test.h"
+#include "mozilla/TimeStamp.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/EventTarget.h"
-#include "mozilla/PresShell.h"
#include "mozilla/layers/InputAPZContext.h"
#include "nsIContent.h"
#include "nsIFrame.h"
@@ -24,6 +28,8 @@ namespace mozilla {
StaticAutoPtr<nsTHashMap<nsUint32HashKey, TouchManager::TouchInfo>>
TouchManager::sCaptureTouchList;
layers::LayersId TouchManager::sCaptureTouchLayersId;
+TimeStamp TouchManager::sSingleTouchStartTimeStamp;
+LayoutDeviceIntPoint TouchManager::sSingleTouchStartPoint;
/*static*/
void TouchManager::InitializeStatics() {
@@ -168,7 +174,7 @@ nsIFrame* TouchManager::SuppressInvalidPointsAndGetTargetedFrame(
}
nsIFrame* frame = nullptr;
- for (int32_t i = aEvent->mTouches.Length(); i;) {
+ for (uint32_t i = aEvent->mTouches.Length(); i;) {
--i;
dom::Touch* touch = aEvent->mTouches[i];
if (TouchManager::HasCapturedTouch(touch->Identifier())) {
@@ -176,9 +182,35 @@ nsIFrame* TouchManager::SuppressInvalidPointsAndGetTargetedFrame(
}
MOZ_ASSERT(touch->mOriginalTarget);
- nsCOMPtr<nsIContent> targetContent = do_QueryInterface(touch->GetTarget());
- nsIFrame* targetFrame =
- targetContent ? targetContent->GetPrimaryFrame() : nullptr;
+ nsIContent* const targetContent =
+ nsIContent::FromEventTargetOrNull(touch->GetTarget());
+ if (MOZ_UNLIKELY(!targetContent)) {
+ touch->mIsTouchEventSuppressed = true;
+ continue;
+ }
+
+ // Even if the target content is not connected, we should dispatch the touch
+ // start event except when the target content is owned by different
+ // document.
+ if (MOZ_UNLIKELY(!targetContent->IsInComposedDoc())) {
+ if (anyTarget && anyTarget->OwnerDoc() != targetContent->OwnerDoc()) {
+ touch->mIsTouchEventSuppressed = true;
+ continue;
+ }
+ if (!anyTarget) {
+ anyTarget = targetContent;
+ }
+ touch->SetTouchTarget(targetContent->GetAsElementOrParentElement());
+ if (PresShell* const presShell =
+ targetContent->OwnerDoc()->GetPresShell()) {
+ if (nsIFrame* rootFrame = presShell->GetRootFrame()) {
+ frame = rootFrame;
+ }
+ }
+ continue;
+ }
+
+ nsIFrame* targetFrame = targetContent->GetPrimaryFrame();
if (targetFrame && !anyTarget) {
anyTarget = targetContent;
} else {
@@ -202,10 +234,12 @@ nsIFrame* TouchManager::SuppressInvalidPointsAndGetTargetedFrame(
touch->mIsTouchEventSuppressed = true;
} else {
targetFrame = newTargetFrame;
- targetFrame->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
- touch->SetTouchTarget(targetContent
- ? targetContent->GetAsElementOrParentElement()
- : nullptr);
+ nsCOMPtr<nsIContent> newTargetContent;
+ targetFrame->GetContentForEvent(aEvent,
+ getter_AddRefs(newTargetContent));
+ touch->SetTouchTarget(
+ newTargetContent ? newTargetContent->GetAsElementOrParentElement()
+ : nullptr);
}
}
if (targetFrame) {
@@ -236,8 +270,11 @@ bool TouchManager::PreHandleEvent(WidgetEvent* aEvent, nsEventStatus* aStatus,
// touch event associated to. We cache layers id of the first touchstart
// event, all subsequent touch events will use the same layers id.
sCaptureTouchLayersId = aEvent->mLayersId;
+ sSingleTouchStartTimeStamp = aEvent->mTimeStamp;
+ sSingleTouchStartPoint = aEvent->AsTouchEvent()->mTouches[0]->mRefPoint;
} else {
touchEvent->mLayersId = sCaptureTouchLayersId;
+ sSingleTouchStartTimeStamp = TimeStamp();
}
// Add any new touches to the queue
WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
@@ -404,6 +441,60 @@ bool TouchManager::PreHandleEvent(WidgetEvent* aEvent, nsEventStatus* aStatus,
return true;
}
+void TouchManager::PostHandleEvent(const WidgetEvent* aEvent,
+ const nsEventStatus* aStatus) {
+ switch (aEvent->mMessage) {
+ case eTouchMove: {
+ if (sSingleTouchStartTimeStamp.IsNull()) {
+ break;
+ }
+ if (*aStatus == nsEventStatus_eConsumeNoDefault) {
+ sSingleTouchStartTimeStamp = TimeStamp();
+ break;
+ }
+ const WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
+ if (touchEvent->mTouches.Length() > 1) {
+ sSingleTouchStartTimeStamp = TimeStamp();
+ break;
+ }
+ if (touchEvent->mTouches.Length() == 1) {
+ // If the touch moved too far from the start point, don't treat the
+ // touch as a tap.
+ const float distance =
+ static_cast<float>((sSingleTouchStartPoint -
+ aEvent->AsTouchEvent()->mTouches[0]->mRefPoint)
+ .Length());
+ const float maxDistance =
+ StaticPrefs::apz_touch_start_tolerance() *
+ (MOZ_LIKELY(touchEvent->mWidget) ? touchEvent->mWidget->GetDPI()
+ : 96.0f);
+ if (distance > maxDistance) {
+ sSingleTouchStartTimeStamp = TimeStamp();
+ }
+ }
+ break;
+ }
+ case eTouchStart:
+ case eTouchEnd:
+ if (*aStatus == nsEventStatus_eConsumeNoDefault &&
+ !sSingleTouchStartTimeStamp.IsNull()) {
+ sSingleTouchStartTimeStamp = TimeStamp();
+ }
+ break;
+ case eTouchCancel:
+ case eTouchPointerCancel:
+ case eMouseLongTap:
+ case eContextMenu: {
+ if (!sSingleTouchStartTimeStamp.IsNull()) {
+ sSingleTouchStartTimeStamp = TimeStamp();
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
/*static*/
already_AddRefed<nsIContent> TouchManager::GetAnyCapturedTouchTarget() {
nsCOMPtr<nsIContent> result = nullptr;
@@ -473,4 +564,25 @@ bool TouchManager::ShouldConvertTouchToPointer(const Touch* aTouch,
return true;
}
+/* static */
+bool TouchManager::IsSingleTapEndToDoDefault(
+ const WidgetTouchEvent* aTouchEndEvent) {
+ MOZ_ASSERT(aTouchEndEvent);
+ MOZ_ASSERT(aTouchEndEvent->mFlags.mIsSynthesizedForTests);
+ MOZ_ASSERT(!StaticPrefs::test_events_async_enabled());
+ if (sSingleTouchStartTimeStamp.IsNull() ||
+ aTouchEndEvent->mTouches.Length() != 1) {
+ return false;
+ }
+ // If it's pressed long time, we should not treat it as a single tap because
+ // a long press should cause opening context menu by default.
+ if ((aTouchEndEvent->mTimeStamp - sSingleTouchStartTimeStamp)
+ .ToMilliseconds() > StaticPrefs::apz_max_tap_time()) {
+ return false;
+ }
+ NS_WARNING_ASSERTION(aTouchEndEvent->mTouches[0]->mChanged,
+ "The single tap end should be changed");
+ return true;
+}
+
} // namespace mozilla