diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index c8bf6fcb7772..1874a78c179b 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -5651,6 +5651,23 @@ AppendToTouchList(const uint32_t& aKey, nsCOMPtr& aData, void *aTou return PL_DHASH_NEXT; } +static PLDHashOperator +FindAnyTarget(const uint32_t& aKey, nsCOMPtr& aData, + void* aAnyTarget) +{ + if (aData) { + nsCOMPtr target; + aData->GetTarget(getter_AddRefs(target)); + if (target) { + nsCOMPtr* content = + static_cast*>(aAnyTarget); + *content = do_QueryInterface(target); + return PL_DHASH_STOP; + } + } + return PL_DHASH_NEXT; +} + nsIFrame* GetNearestFrameContainingPresShell(nsIPresShell* aPresShell) { nsIView* view = aPresShell->GetViewManager()->GetRootView(); @@ -5854,7 +5871,6 @@ PresShell::HandleEvent(nsIFrame *aFrame, if (!captureRetarget && !isWindowLevelMouseExit) { 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 @@ -5866,7 +5882,19 @@ PresShell::HandleEvent(nsIFrame *aFrame, EvictTouchPoint(touches[i]); } } - for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) { + // 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 + nsCOMPtr anyTarget; + if (gCaptureTouchList.Count() > 0) { + gCaptureTouchList.Enumerate(&FindAnyTarget, &anyTarget); + } else { + gPreventMouseEvents = false; + } + + // Add any new touches to the queue + for (int32_t i = touchEvent->touches.Length(); i; ) { + --i; nsIDOMTouch *touch = touchEvent->touches[i]; nsDOMTouch *domtouch = static_cast(touch); touch->mMessage = aEvent->message; @@ -5877,9 +5905,59 @@ PresShell::HandleEvent(nsIFrame *aFrame, // 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); + // find the target for this touch + uint32_t flags = 0; + eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, + touch->mRefPoint, + frame); + nsIFrame* target = + FindFrameTargetedByInputEvent(aEvent->eventStructType, + frame, + eventPoint, + flags); + if (target && !anyTarget) { + target->GetContentForEvent(aEvent, getter_AddRefs(anyTarget)); + while (anyTarget && !anyTarget->IsElement()) { + anyTarget = anyTarget->GetParent(); + } + domtouch->SetTarget(anyTarget); + gCaptureTouchList.Put(id, touch); + } 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->PresContext()->GetPresShell()->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->touches array. touchmove events that aren't + // in the captured touch list will be discarded + if (!newTargetFrame) { + touchEvent->touches.RemoveElementAt(i); + } else { + target = newTargetFrame; + nsCOMPtr targetContent; + target->GetContentForEvent(aEvent, getter_AddRefs(targetContent)); + while (targetContent && !targetContent->IsElement()) { + targetContent = targetContent->GetParent(); + } + touch->SetTarget(targetContent); + gCaptureTouchList.Put(id, touch); + } + } + + if (target) { + frame = target; + } } else { // This touch is an old touch, we need to ensure that is not // marked as changed and set its target correctly @@ -5950,47 +6028,6 @@ PresShell::HandleEvent(nsIFrame *aFrame, PresShell* shell = static_cast(frame->PresContext()->PresShell()); - switch (aEvent->message) { - case NS_TOUCH_MOVE: - 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 (uint32_t i = 0; i < touches.Length(); ++i) { - nsIDOMTouch *touch = touches[i]; - if (!touch) { - break; - } - - int32_t id; - touch->GetIdentifier(&id); - nsCOMPtr oldTouch; - gCaptureTouchList.Get(id, getter_AddRefs(oldTouch)); - if (!oldTouch) { - break; - } - - nsCOMPtr targetPtr; - oldTouch->GetTarget(getter_AddRefs(targetPtr)); - nsCOMPtr content = do_QueryInterface(targetPtr); - if (!content) { - break; - } - - nsIFrame* contentFrame = content->GetPrimaryFrame(); - if (!contentFrame) { - break; - } - - shell = static_cast( - contentFrame->PresContext()->PresShell()); - } - break; - } - } - // Check if we have an active EventStateManager which isn't the // EventStateManager of the current PresContext. // If that is the case, and mouse is over some ancestor document, @@ -6353,9 +6390,10 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsEventStatus* aStatus) case NS_TOUCH_MOVE: { // Check for touches that changed. Mark them add to queue nsTouchEvent* touchEvent = static_cast(aEvent); - nsTArray > touches = touchEvent->touches; + nsTArray >& touches = touchEvent->touches; bool haveChanged = false; - for (uint32_t i = 0; i < touches.Length(); ++i) { + for (int32_t i = touches.Length(); i; ) { + --i; nsIDOMTouch *touch = touches[i]; nsDOMTouch *domtouch = static_cast(touch); if (!touch) { @@ -6368,6 +6406,7 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsEventStatus* aStatus) nsCOMPtr oldTouch; gCaptureTouchList.Get(id, getter_AddRefs(oldTouch)); if (!oldTouch) { + touches.RemoveElementAt(i); continue; } if(domtouch->Equals(oldTouch)) { @@ -6377,6 +6416,10 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsEventStatus* aStatus) nsCOMPtr targetPtr; oldTouch->GetTarget(getter_AddRefs(targetPtr)); + if (!targetPtr) { + touches.RemoveElementAt(i); + continue; + } domtouch->SetTarget(targetPtr); gCaptureTouchList.Put(id, touch); @@ -6516,85 +6559,56 @@ PresShell::DispatchTouchEvent(nsEvent *aEvent, 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) { - for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) { - nsIDOMTouch *touch = touchEvent->touches[i]; - if (!touch || !touch->mChanged) { - continue; - } - // copy the event - nsCOMPtr targetPtr; - touch->GetTarget(getter_AddRefs(targetPtr)); - if (!targetPtr) { + + // loop over all touches and dispatch events on any that have changed + for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) { + nsIDOMTouch *touch = touchEvent->touches[i]; + if (!touch || !touch->mChanged) { + continue; + } + + nsCOMPtr targetPtr; + touch->GetTarget(getter_AddRefs(targetPtr)); + nsCOMPtr content = do_QueryInterface(targetPtr); + if (!content) { + continue; + } + + nsIContent* capturingContent = GetCapturingContent(); + if (capturingContent) { + if (capturingContent->OwnerDoc() != content->OwnerDoc()) { + // Wrong document, don't dispatch anything. continue; } + content = capturingContent; + } + // copy the event + nsTouchEvent newEvent(NS_IS_TRUSTED_EVENT(touchEvent) ? + true : false, + touchEvent); + newEvent.target = targetPtr; - nsTouchEvent newEvent(NS_IS_TRUSTED_EVENT(touchEvent) ? - true : false, - touchEvent); - newEvent.target = targetPtr; - - // If someone is capturing, all touch events are filtered to their target - nsCOMPtr content = GetCapturingContent(); - - // if no one is capturing, set the capturing target - if (!content) { - content = do_QueryInterface(targetPtr); - } - nsRefPtr contentPresShell; - if (content && content->OwnerDoc() == mDocument) { - contentPresShell = static_cast - (content->OwnerDoc()->GetShell()); - if (contentPresShell) { - contentPresShell->PushCurrentEventInfo( - content->GetPrimaryFrame(), content); - } - } - nsPresContext *context = nsContentUtils::GetContextForContent(content); - if (!context) { - context = mPresContext; - } - tmpStatus = nsEventStatus_eIgnore; - nsEventDispatcher::Dispatch(targetPtr, context, - &newEvent, nullptr, &tmpStatus, aEventCB); - if (nsEventStatus_eConsumeNoDefault == tmpStatus) { - preventDefault = true; - } + nsRefPtr contentPresShell; + if (content->OwnerDoc() == mDocument) { + contentPresShell = static_cast + (content->OwnerDoc()->GetShell()); if (contentPresShell) { - contentPresShell->PopCurrentEventInfo(); - } - } - } else { - // touchevents need to have the target attribute set on each touch - for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) { - nsIDOMTouch *touch = touchEvent->touches[i]; - if (touch->mChanged) { - touch->SetTarget(mCurrentEventContent); - } - } - - if (mCurrentEventContent) { - nsEventDispatcher::Dispatch(mCurrentEventContent, mPresContext, - aEvent, nullptr, &tmpStatus, aEventCB); - } else { - nsCOMPtr targetContent; - rv = mCurrentEventFrame->GetContentForEvent(aEvent, - getter_AddRefs(targetContent)); - if (NS_SUCCEEDED(rv) && targetContent) { - nsEventDispatcher::Dispatch(targetContent, mPresContext, aEvent, - nullptr, &tmpStatus, aEventCB); - } else if (mDocument) { - nsEventDispatcher::Dispatch(mDocument, mPresContext, aEvent, - nullptr, &tmpStatus, nullptr); + //XXXsmaug huge hack. Pushing possibly capturing content, + // even though event target is something else. + contentPresShell->PushCurrentEventInfo( + content->GetPrimaryFrame(), content); } } + nsPresContext *context = nsContentUtils::GetContextForContent(content); + tmpStatus = nsEventStatus_eIgnore; + nsEventDispatcher::Dispatch(targetPtr, context, + &newEvent, nullptr, &tmpStatus, aEventCB); if (nsEventStatus_eConsumeNoDefault == tmpStatus) { preventDefault = true; } - if (touchEvent->touches.Length() == 1) { - gPreventMouseEvents = false; + if (contentPresShell) { + contentPresShell->PopCurrentEventInfo(); } }