Bug 790454 - Find target for touch during touchstart. r=smaug

This commit is contained in:
Wes Johnston 2012-10-29 14:02:17 -07:00
Родитель 2200a5623c
Коммит 3360f6f087
1 изменённых файлов: 130 добавлений и 116 удалений

Просмотреть файл

@ -5651,6 +5651,23 @@ AppendToTouchList(const uint32_t& aKey, nsCOMPtr<nsIDOMTouch>& aData, void *aTou
return PL_DHASH_NEXT; return PL_DHASH_NEXT;
} }
static PLDHashOperator
FindAnyTarget(const uint32_t& aKey, nsCOMPtr<nsIDOMTouch>& aData,
void* aAnyTarget)
{
if (aData) {
nsCOMPtr<nsIDOMEventTarget> target;
aData->GetTarget(getter_AddRefs(target));
if (target) {
nsCOMPtr<nsIContent>* content =
static_cast<nsCOMPtr<nsIContent>*>(aAnyTarget);
*content = do_QueryInterface(target);
return PL_DHASH_STOP;
}
}
return PL_DHASH_NEXT;
}
nsIFrame* GetNearestFrameContainingPresShell(nsIPresShell* aPresShell) nsIFrame* GetNearestFrameContainingPresShell(nsIPresShell* aPresShell)
{ {
nsIView* view = aPresShell->GetViewManager()->GetRootView(); nsIView* view = aPresShell->GetViewManager()->GetRootView();
@ -5854,7 +5871,6 @@ PresShell::HandleEvent(nsIFrame *aFrame,
if (!captureRetarget && !isWindowLevelMouseExit) { if (!captureRetarget && !isWindowLevelMouseExit) {
nsPoint eventPoint; nsPoint eventPoint;
if (aEvent->message == NS_TOUCH_START) { if (aEvent->message == NS_TOUCH_START) {
// Add any new touches to the queue
nsTouchEvent* touchEvent = static_cast<nsTouchEvent*>(aEvent); nsTouchEvent* touchEvent = static_cast<nsTouchEvent*>(aEvent);
// if there is only one touch in this touchstart event, assume that it is // 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 // 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]); 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<nsIContent> 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]; nsIDOMTouch *touch = touchEvent->touches[i];
nsDOMTouch *domtouch = static_cast<nsDOMTouch*>(touch); nsDOMTouch *domtouch = static_cast<nsDOMTouch*>(touch);
touch->mMessage = aEvent->message; touch->mMessage = aEvent->message;
@ -5877,9 +5905,59 @@ PresShell::HandleEvent(nsIFrame *aFrame,
// This event is a new touch. Mark it as a changedTouch and // This event is a new touch. Mark it as a changedTouch and
// add it to the queue. // add it to the queue.
touch->mChanged = true; 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<nsIContent> 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 { } else {
// This touch is an old touch, we need to ensure that is not // This touch is an old touch, we need to ensure that is not
// marked as changed and set its target correctly // marked as changed and set its target correctly
@ -5950,47 +6028,6 @@ PresShell::HandleEvent(nsIFrame *aFrame,
PresShell* shell = PresShell* shell =
static_cast<PresShell*>(frame->PresContext()->PresShell()); static_cast<PresShell*>(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<nsTouchEvent*>(aEvent);
nsTArray<nsCOMPtr<nsIDOMTouch> > &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<nsIDOMTouch> oldTouch;
gCaptureTouchList.Get(id, getter_AddRefs(oldTouch));
if (!oldTouch) {
break;
}
nsCOMPtr<nsPIDOMEventTarget> targetPtr;
oldTouch->GetTarget(getter_AddRefs(targetPtr));
nsCOMPtr<nsIContent> content = do_QueryInterface(targetPtr);
if (!content) {
break;
}
nsIFrame* contentFrame = content->GetPrimaryFrame();
if (!contentFrame) {
break;
}
shell = static_cast<PresShell*>(
contentFrame->PresContext()->PresShell());
}
break;
}
}
// Check if we have an active EventStateManager which isn't the // Check if we have an active EventStateManager which isn't the
// EventStateManager of the current PresContext. // EventStateManager of the current PresContext.
// If that is the case, and mouse is over some ancestor document, // 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: { case NS_TOUCH_MOVE: {
// Check for touches that changed. Mark them add to queue // Check for touches that changed. Mark them add to queue
nsTouchEvent* touchEvent = static_cast<nsTouchEvent*>(aEvent); nsTouchEvent* touchEvent = static_cast<nsTouchEvent*>(aEvent);
nsTArray<nsCOMPtr<nsIDOMTouch> > touches = touchEvent->touches; nsTArray<nsCOMPtr<nsIDOMTouch> >& touches = touchEvent->touches;
bool haveChanged = false; bool haveChanged = false;
for (uint32_t i = 0; i < touches.Length(); ++i) { for (int32_t i = touches.Length(); i; ) {
--i;
nsIDOMTouch *touch = touches[i]; nsIDOMTouch *touch = touches[i];
nsDOMTouch *domtouch = static_cast<nsDOMTouch*>(touch); nsDOMTouch *domtouch = static_cast<nsDOMTouch*>(touch);
if (!touch) { if (!touch) {
@ -6368,6 +6406,7 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsEventStatus* aStatus)
nsCOMPtr<nsIDOMTouch> oldTouch; nsCOMPtr<nsIDOMTouch> oldTouch;
gCaptureTouchList.Get(id, getter_AddRefs(oldTouch)); gCaptureTouchList.Get(id, getter_AddRefs(oldTouch));
if (!oldTouch) { if (!oldTouch) {
touches.RemoveElementAt(i);
continue; continue;
} }
if(domtouch->Equals(oldTouch)) { if(domtouch->Equals(oldTouch)) {
@ -6377,6 +6416,10 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsEventStatus* aStatus)
nsCOMPtr<nsPIDOMEventTarget> targetPtr; nsCOMPtr<nsPIDOMEventTarget> targetPtr;
oldTouch->GetTarget(getter_AddRefs(targetPtr)); oldTouch->GetTarget(getter_AddRefs(targetPtr));
if (!targetPtr) {
touches.RemoveElementAt(i);
continue;
}
domtouch->SetTarget(targetPtr); domtouch->SetTarget(targetPtr);
gCaptureTouchList.Put(id, touch); gCaptureTouchList.Put(id, touch);
@ -6516,85 +6559,56 @@ PresShell::DispatchTouchEvent(nsEvent *aEvent,
bool preventDefault = false; bool preventDefault = false;
nsEventStatus tmpStatus = nsEventStatus_eIgnore; nsEventStatus tmpStatus = nsEventStatus_eIgnore;
nsTouchEvent* touchEvent = static_cast<nsTouchEvent*>(aEvent); nsTouchEvent* touchEvent = static_cast<nsTouchEvent*>(aEvent);
// touch events should fire on all targets
if (aEvent->message != NS_TOUCH_START) { // loop over all touches and dispatch events on any that have changed
for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) { for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) {
nsIDOMTouch *touch = touchEvent->touches[i]; nsIDOMTouch *touch = touchEvent->touches[i];
if (!touch || !touch->mChanged) { if (!touch || !touch->mChanged) {
continue; continue;
} }
// copy the event
nsCOMPtr<nsPIDOMEventTarget> targetPtr; nsCOMPtr<nsPIDOMEventTarget> targetPtr;
touch->GetTarget(getter_AddRefs(targetPtr)); touch->GetTarget(getter_AddRefs(targetPtr));
if (!targetPtr) { nsCOMPtr<nsIContent> content = do_QueryInterface(targetPtr);
if (!content) {
continue;
}
nsIContent* capturingContent = GetCapturingContent();
if (capturingContent) {
if (capturingContent->OwnerDoc() != content->OwnerDoc()) {
// Wrong document, don't dispatch anything.
continue; 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) ? nsRefPtr<PresShell> contentPresShell;
true : false, if (content->OwnerDoc() == mDocument) {
touchEvent); contentPresShell = static_cast<PresShell*>
newEvent.target = targetPtr; (content->OwnerDoc()->GetShell());
// If someone is capturing, all touch events are filtered to their target
nsCOMPtr<nsIContent> content = GetCapturingContent();
// if no one is capturing, set the capturing target
if (!content) {
content = do_QueryInterface(targetPtr);
}
nsRefPtr<PresShell> contentPresShell;
if (content && content->OwnerDoc() == mDocument) {
contentPresShell = static_cast<PresShell*>
(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;
}
if (contentPresShell) { if (contentPresShell) {
contentPresShell->PopCurrentEventInfo(); //XXXsmaug huge hack. Pushing possibly capturing content,
} // even though event target is something else.
} contentPresShell->PushCurrentEventInfo(
} else { content->GetPrimaryFrame(), content);
// 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<nsIContent> 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);
} }
} }
nsPresContext *context = nsContentUtils::GetContextForContent(content);
tmpStatus = nsEventStatus_eIgnore;
nsEventDispatcher::Dispatch(targetPtr, context,
&newEvent, nullptr, &tmpStatus, aEventCB);
if (nsEventStatus_eConsumeNoDefault == tmpStatus) { if (nsEventStatus_eConsumeNoDefault == tmpStatus) {
preventDefault = true; preventDefault = true;
} }
if (touchEvent->touches.Length() == 1) { if (contentPresShell) {
gPreventMouseEvents = false; contentPresShell->PopCurrentEventInfo();
} }
} }