Bug 1420589 Part6: Keep those touch points that are not in the same document so that we can use them to dispatch pointer events.

MozReview-Commit-ID: JlXHDwpbcEO
This commit is contained in:
Stone Shih 2017-12-09 10:06:18 +08:00
parent cfd5f075ba
commit cd8b01e32a
5 changed files with 138 additions and 62 deletions

View File

@ -50,6 +50,7 @@ Touch::Touch(EventTarget* aTarget,
int32_t aRadiusY,
float aRotationAngle,
float aForce)
: mIsTouchEventSuppressed(false)
{
mTarget = aTarget;
mIdentifier = aIdentifier;
@ -73,6 +74,7 @@ Touch::Touch(int32_t aIdentifier,
LayoutDeviceIntPoint aRadius,
float aRotationAngle,
float aForce)
: mIsTouchEventSuppressed(false)
{
mIdentifier = aIdentifier;
mPagePoint = CSSIntPoint(0, 0);
@ -93,6 +95,7 @@ Touch::Touch(const Touch& aOther)
: mTarget(aOther.mTarget)
, mRefPoint(aOther.mRefPoint)
, mChanged(aOther.mChanged)
, mIsTouchEventSuppressed(aOther.mIsTouchEventSuppressed)
, mMessage(aOther.mMessage)
, mIdentifier(aOther.mIdentifier)
, mPagePoint(aOther.mPagePoint)

View File

@ -82,6 +82,12 @@ public:
nsCOMPtr<EventTarget> mTarget;
LayoutDeviceIntPoint mRefPoint;
bool mChanged;
// Is this touch instance being suppressed to dispatch touch event to content.
// We can't remove touch instance from WidgetTouchEvent::mTouches because we
// still need it when dispatching pointer events.
bool mIsTouchEventSuppressed;
uint32_t mMessage;
int32_t mIdentifier;
CSSIntPoint mPagePoint;

View File

@ -7184,6 +7184,11 @@ PresShell::HandleEvent(nsIFrame* aFrame,
if (!captureRetarget && !isWindowLevelMouseExit) {
if (aEvent->mClass == eTouchEventClass) {
frame = TouchManager::SetupTarget(aEvent->AsTouchEvent(), frame);
if (nsIFrame* newFrame =
TouchManager::SuppressInvalidPointsAndGetTargetedFrame(
aEvent->AsTouchEvent())) {
frame = newFrame;
}
} else {
uint32_t flags = 0;
nsPoint eventPoint =
@ -7944,6 +7949,10 @@ PresShell::DispatchTouchEventToDOM(WidgetEvent* aEvent,
// loop over all touches and dispatch events on any that have changed
for (dom::Touch* touch : touchEvent->mTouches) {
// We should remove all suppressed touch instances in
// TouchManager::PreHandleEvent.
MOZ_ASSERT(!touch->mIsTouchEventSuppressed);
if (!touch || !touch->mChanged) {
continue;
}

View File

@ -129,6 +129,52 @@ TouchManager::SetupTarget(WidgetTouchEvent* aEvent, nsIFrame* aFrame)
if (gfxPrefs::APZAllowZooming()) {
flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
}
nsIFrame* target = aFrame;
for (int32_t i = aEvent->mTouches.Length(); i; ) {
--i;
dom::Touch* touch = aEvent->mTouches[i];
int32_t id = touch->Identifier();
if (!TouchManager::HasCapturedTouch(id)) {
// find the target for this touch
nsPoint eventPoint =
nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, touch->mRefPoint,
aFrame);
target = FindFrameTargetedByInputEvent(aEvent, aFrame, eventPoint, flags);
if (target) {
nsCOMPtr<nsIContent> targetContent;
target->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
while (targetContent && !targetContent->IsElement()) {
targetContent = targetContent->GetParent();
}
touch->SetTarget(targetContent);
} else {
aEvent->mTouches.RemoveElementAt(i);
}
} 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;
RefPtr<dom::Touch> oldTouch = TouchManager::GetCapturedTouch(id);
if (oldTouch) {
touch->SetTarget(oldTouch->mTarget);
}
}
}
return target;
}
/* static */ nsIFrame*
TouchManager::SuppressInvalidPointsAndGetTargetedFrame(WidgetTouchEvent* aEvent)
{
MOZ_ASSERT(aEvent);
if (!aEvent || aEvent->mMessage != eTouchStart) {
// All touch events except for touchstart use a captured target.
return nullptr;
}
// if this is a continuing session, ensure that all these events are
// in the same document by taking the target of the events already in
// the capture list
@ -137,73 +183,52 @@ TouchManager::SetupTarget(WidgetTouchEvent* aEvent, nsIFrame* aFrame)
anyTarget = TouchManager::GetAnyCapturedTouchTarget();
}
nsPoint eventPoint;
nsIFrame* frame = aFrame;
nsIFrame* frame = nullptr;
for (int32_t i = aEvent->mTouches.Length(); i; ) {
--i;
dom::Touch* touch = aEvent->mTouches[i];
if (TouchManager::HasCapturedTouch(touch->Identifier())) {
continue;
}
int32_t id = touch->Identifier();
if (!TouchManager::HasCapturedTouch(id)) {
// find the target for this touch
eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent,
touch->mRefPoint,
aFrame);
nsIFrame* target = FindFrameTargetedByInputEvent(aEvent,
aFrame,
eventPoint,
flags);
if (target && !anyTarget) {
target->GetContentForEvent(aEvent, getter_AddRefs(anyTarget));
while (anyTarget && !anyTarget->IsElement()) {
anyTarget = anyTarget->GetParent();
}
touch->SetTarget(anyTarget);
} else {
nsIFrame* newTargetFrame = nullptr;
for (nsIFrame* f = target; f;
f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
if (f->PresContext()->Document() == anyTarget->OwnerDoc()) {
newTargetFrame = f;
break;
}
// We must be in a subdocument so jump directly to the root frame.
// GetParentOrPlaceholderForCrossDoc gets called immediately to
// jump up to the containing document.
f = f->PresShell()->GetRootFrame();
}
// if we couldn't find a target frame in the same document as
// anyTarget, remove the touch from the capture touch list, as
// well as the event->mTouches array. touchmove events that aren't
// in the captured touch list will be discarded
if (!newTargetFrame) {
aEvent->mTouches.RemoveElementAt(i);
} else {
target = newTargetFrame;
nsCOMPtr<nsIContent> targetContent;
target->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
while (targetContent && !targetContent->IsElement()) {
targetContent = targetContent->GetParent();
}
touch->SetTarget(targetContent);
}
}
if (target) {
frame = target;
}
MOZ_ASSERT(touch->mTarget);
nsCOMPtr<nsIContent> targetContent = do_QueryInterface(touch->GetTarget());
nsIFrame* targetFrame = targetContent->GetPrimaryFrame();
if (targetFrame && !anyTarget) {
anyTarget = targetContent;
} 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;
RefPtr<dom::Touch> oldTouch = TouchManager::GetCapturedTouch(id);
if (oldTouch) {
touch->SetTarget(oldTouch->mTarget);
nsIFrame* newTargetFrame = nullptr;
for (nsIFrame* f = targetFrame; f;
f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
if (f->PresContext()->Document() == anyTarget->OwnerDoc()) {
newTargetFrame = f;
break;
}
// We must be in a subdocument so jump directly to the root frame.
// GetParentOrPlaceholderForCrossDoc gets called immediately to
// jump up to the containing document.
f = f->PresShell()->GetRootFrame();
}
// if we couldn't find a target frame in the same document as
// anyTarget, remove the touch from the capture touch list, as
// well as the event->mTouches array. touchmove events that aren't
// in the captured touch list will be discarded
if (!newTargetFrame) {
touch->mIsTouchEventSuppressed = true;
} else {
targetFrame = newTargetFrame;
}
targetFrame->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
while (targetContent && !targetContent->IsElement()) {
targetContent = targetContent->GetParent();
}
touch->SetTarget(targetContent);
}
if (targetFrame) {
frame = targetFrame;
}
}
return FindFrameTargetedByInputEvent(aEvent, frame, eventPoint, flags);
return frame;
}
bool
@ -228,8 +253,10 @@ TouchManager::PreHandleEvent(WidgetEvent* aEvent,
}
}
// Add any new touches to the queue
for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) {
Touch* touch = touchEvent->mTouches[i];
WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
for (int32_t i = touches.Length(); i; ) {
--i;
Touch* touch = touches[i];
int32_t id = touch->Identifier();
if (!sCaptureTouchList->Get(id, nullptr)) {
// If it is not already in the queue, it is a new touch
@ -239,6 +266,12 @@ TouchManager::PreHandleEvent(WidgetEvent* aEvent,
TouchInfo info = { touch, GetNonAnonymousAncestor(touch->mTarget),
true };
sCaptureTouchList->Put(id, info);
if (touch->mIsTouchEventSuppressed) {
// We're going to dispatch touch event. Remove this touch instance if
// it is suppressed.
touches.RemoveElementAt(i);
continue;
}
}
break;
}
@ -286,6 +319,11 @@ TouchManager::PreHandleEvent(WidgetEvent* aEvent,
if (oldTouch->mMessage != touch->mMessage) {
aTouchIsNew = true;
}
if (oldTouch->mIsTouchEventSuppressed) {
touch->mIsTouchEventSuppressed = true;
touches.RemoveElementAt(i);
continue;
}
}
// is nothing has changed, we should just return
if (!haveChanged) {
@ -317,7 +355,8 @@ TouchManager::PreHandleEvent(WidgetEvent* aEvent,
// need to make sure we only remove touches that are ending here
WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
for (uint32_t i = 0; i < touches.Length(); ++i) {
for (int32_t i = touches.Length(); i; ) {
--i;
Touch* touch = touches[i];
if (!touch) {
continue;
@ -339,6 +378,10 @@ TouchManager::PreHandleEvent(WidgetEvent* aEvent,
aCurrentEventContent = do_QueryInterface(targetPtr);
touch->SetTarget(targetPtr);
sCaptureTouchList->Remove(id);
if (info.mTouch->mIsTouchEventSuppressed) {
touches.RemoveElementAt(i);
continue;
}
}
// add any touches left in the touch list, but ensure changed=false
AppendToTouchList(&touches);

View File

@ -35,6 +35,21 @@ public:
// events are dispatched to the same target as touchstart.
static nsIFrame* SetupTarget(WidgetTouchEvent* aEvent, nsIFrame* aFrame);
/**
* This function checks whether all touch points hit elements in the same
* document. If not, we try to find its cross document parent which is in the
* same document of the existing target as the event target. We mark the
* touch point as suppressed if can't find it. The suppressed touch points are
* removed in TouchManager::PreHandleEvent so that we don't dispatch them to
* content.
*
* @param aEvent A touch event to be checked.
*
* @return The targeted frame of aEvent.
*/
static nsIFrame* SuppressInvalidPointsAndGetTargetedFrame(
WidgetTouchEvent* aEvent);
bool PreHandleEvent(mozilla::WidgetEvent* aEvent,
nsEventStatus* aStatus,
bool& aTouchIsNew,