summaryrefslogtreecommitdiff
path: root/dom
diff options
context:
space:
mode:
authorMartok <martok@martoks-place.de>2022-11-27 16:35:56 +0100
committerMartok <martok@martoks-place.de>2022-11-27 16:35:56 +0100
commit0494d561c58c59feff0f6b850446b8316b4a8dd9 (patch)
treefe5b09b8bd906499b64b853ba9a473258e0bf7e1 /dom
parent41fed1ebf882524a33d9bb8188a5c6ca013a26f9 (diff)
downloaduxp-0494d561c58c59feff0f6b850446b8316b4a8dd9.tar.gz
Issue #2030 - Dispatch click on common interactive ancestor if mousdown/up are not on the same element
For example, if the mouse is dragged from one element to another while staying inside the same button, dispatch from their common ancestor
Diffstat (limited to 'dom')
-rw-r--r--dom/base/nsContentUtils.cpp50
-rw-r--r--dom/base/nsContentUtils.h9
-rwxr-xr-xdom/events/Event.cpp5
-rw-r--r--dom/events/EventStateManager.cpp88
-rw-r--r--dom/events/EventStateManager.h3
5 files changed, 99 insertions, 56 deletions
diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
index 55bcf473e0..c964f0ce29 100644
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -2377,6 +2377,56 @@ nsContentUtils::GetCommonAncestor(nsINode* aNode1,
return parent;
}
+// static
+nsINode*
+nsContentUtils::GetCommonAncestorUnderInteractiveContent(nsINode* aNode1,
+ nsINode* aNode2)
+{
+ if (!aNode1 || !aNode2) {
+ return nullptr;
+ }
+
+ if (aNode1 == aNode2) {
+ return aNode1;
+ }
+
+ // Build the chain of parents
+ AutoTArray<nsINode*, 30> parents1;
+ do {
+ parents1.AppendElement(aNode1);
+ if (aNode1->IsElement() &&
+ aNode1->AsElement()->IsInteractiveHTMLContent(true)) {
+ break;
+ }
+ aNode1 = aNode1->GetFlattenedTreeParentNode();
+ } while (aNode1);
+
+ AutoTArray<nsINode*, 30> parents2;
+ do {
+ parents2.AppendElement(aNode2);
+ if (aNode2->IsElement() &&
+ aNode2->AsElement()->IsInteractiveHTMLContent(true)) {
+ break;
+ }
+ aNode2 = aNode2->GetFlattenedTreeParentNode();
+ } while (aNode2);
+
+ // Find where the parent chain differs
+ uint32_t pos1 = parents1.Length();
+ uint32_t pos2 = parents2.Length();
+ nsINode* parent = nullptr;
+ for (uint32_t len = std::min(pos1, pos2); len > 0; --len) {
+ nsINode* child1 = parents1.ElementAt(--pos1);
+ nsINode* child2 = parents2.ElementAt(--pos2);
+ if (child1 != child2) {
+ break;
+ }
+ parent = child1;
+ }
+
+ return parent;
+}
+
/* static */
bool
nsContentUtils::PositionIsBefore(nsINode* aNode1, nsINode* aNode2)
diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
index 3393a30c5c..eb67ff6a6a 100644
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -325,6 +325,15 @@ public:
nsINode* aNode2);
/**
+ * Returns the common ancestor under interactive content, if any.
+ * If neither one has interactive content as ancestor, common ancestor will be
+ * returned. If only one has interactive content as ancestor, null will be
+ * returned. If the nodes are the same, that node is returned.
+ */
+ static nsINode* GetCommonAncestorUnderInteractiveContent(nsINode* aNode1,
+ nsINode* aNode2);
+
+ /**
* Returns true if aNode1 is before aNode2 in the same connected
* tree.
*/
diff --git a/dom/events/Event.cpp b/dom/events/Event.cpp
index 3fbb138e7c..864678dd66 100755
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -194,6 +194,11 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Event)
default:
break;
}
+
+ if (WidgetMouseEvent* mouseEvent = tmp->mEvent->AsMouseEvent()) {
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mClickTarget");
+ cb.NoteXPCOMChild(mouseEvent->mClickTarget);
+ }
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresContext)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExplicitOriginalTarget)
diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp
index 3dcc6cef47..935281916b 100644
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -432,11 +432,8 @@ NS_IMPL_CYCLE_COLLECTION(EventStateManager,
mGestureDownContent,
mGestureDownFrameOwner,
mLastLeftMouseDownContent,
- mLastLeftMouseDownContentParent,
mLastMiddleMouseDownContent,
- mLastMiddleMouseDownContentParent,
mLastRightMouseDownContent,
- mLastRightMouseDownContentParent,
mActiveContent,
mHoverContent,
mURLTargetContent,
@@ -4570,16 +4567,13 @@ EventStateManager::SetClickCount(WidgetMouseEvent* aEvent,
nsEventStatus* aStatus)
{
nsCOMPtr<nsIContent> mouseContent;
- nsIContent* mouseContentParent = nullptr;
if (mCurrentTarget) {
mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(mouseContent));
}
- if (mouseContent) {
- if (mouseContent->IsNodeOfType(nsINode::eTEXT)) {
- mouseContent = mouseContent->GetParent();
- }
- if (mouseContent && mouseContent->IsRootOfNativeAnonymousSubtree()) {
- mouseContentParent = mouseContent->GetParent();
+ if (mouseContent && mouseContent->IsNodeOfType(nsINode::eTEXT)) {
+ nsINode* parent = mouseContent->GetFlattenedTreeParentNode();
+ if (parent && parent->IsContent()) {
+ mouseContent = parent->AsContent();
}
}
@@ -4587,54 +4581,51 @@ EventStateManager::SetClickCount(WidgetMouseEvent* aEvent,
case WidgetMouseEvent::eLeftButton:
if (aEvent->mMessage == eMouseDown) {
mLastLeftMouseDownContent = mouseContent;
- mLastLeftMouseDownContentParent = mouseContentParent;
} else if (aEvent->mMessage == eMouseUp) {
- if (mLastLeftMouseDownContent == mouseContent ||
- mLastLeftMouseDownContentParent == mouseContent ||
- mLastLeftMouseDownContent == mouseContentParent) {
+ aEvent->mClickTarget =
+ nsContentUtils::GetCommonAncestorUnderInteractiveContent(
+ mouseContent, mLastLeftMouseDownContent);
+ if (aEvent->mClickTarget) {
aEvent->mClickCount = mLClickCount;
mLClickCount = 0;
} else {
aEvent->mClickCount = 0;
}
mLastLeftMouseDownContent = nullptr;
- mLastLeftMouseDownContentParent = nullptr;
}
break;
case WidgetMouseEvent::eMiddleButton:
if (aEvent->mMessage == eMouseDown) {
mLastMiddleMouseDownContent = mouseContent;
- mLastMiddleMouseDownContentParent = mouseContentParent;
} else if (aEvent->mMessage == eMouseUp) {
- if (mLastMiddleMouseDownContent == mouseContent ||
- mLastMiddleMouseDownContentParent == mouseContent ||
- mLastMiddleMouseDownContent == mouseContentParent) {
+ aEvent->mClickTarget =
+ nsContentUtils::GetCommonAncestorUnderInteractiveContent(
+ mouseContent, mLastMiddleMouseDownContent);
+ if (aEvent->mClickTarget) {
aEvent->mClickCount = mMClickCount;
mMClickCount = 0;
} else {
aEvent->mClickCount = 0;
}
mLastMiddleMouseDownContent = nullptr;
- mLastMiddleMouseDownContentParent = nullptr;
}
break;
case WidgetMouseEvent::eRightButton:
if (aEvent->mMessage == eMouseDown) {
mLastRightMouseDownContent = mouseContent;
- mLastRightMouseDownContentParent = mouseContentParent;
} else if (aEvent->mMessage == eMouseUp) {
- if (mLastRightMouseDownContent == mouseContent ||
- mLastRightMouseDownContentParent == mouseContent ||
- mLastRightMouseDownContent == mouseContentParent) {
+ aEvent->mClickTarget =
+ nsContentUtils::GetCommonAncestorUnderInteractiveContent(
+ mouseContent, mLastRightMouseDownContent);
+ if (aEvent->mClickTarget) {
aEvent->mClickCount = mRClickCount;
mRClickCount = 0;
} else {
aEvent->mClickCount = 0;
}
mLastRightMouseDownContent = nullptr;
- mLastRightMouseDownContentParent = nullptr;
}
break;
}
@@ -4656,7 +4647,7 @@ EventStateManager::EventCausesClickEvents(const WidgetMouseEvent& aMouseEvent)
}
// If mouse is still over same element, clickcount will be > 1.
// If it has moved it will be zero, so no click.
- if (!aMouseEvent.mClickCount) {
+ if (!aMouseEvent.mClickCount || !aMouseEvent.mClickTarget) {
return false;
}
// Check that the window isn't disabled before firing a click
@@ -4675,7 +4666,7 @@ EventStateManager::InitAndDispatchClickEvent(WidgetMouseEvent* aMouseUpEvent,
{
MOZ_ASSERT(aMouseUpEvent);
MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent));
- MOZ_ASSERT(aMouseUpContent || aCurrentTarget || aOverrideClickTarget);
+ MOZ_ASSERT(aMouseUpContent || aCurrentTarget);
WidgetMouseEvent event(aMouseUpEvent->IsTrusted(), aMessage,
aMouseUpEvent->mWidget, WidgetMouseEvent::eReal);
@@ -4689,6 +4680,10 @@ EventStateManager::InitAndDispatchClickEvent(WidgetMouseEvent* aMouseUpEvent,
event.button = aMouseUpEvent->button;
event.inputSource = aMouseUpEvent->inputSource;
+ if (!aMouseUpContent->IsInComposedDoc()) {
+ return NS_OK;
+ }
+
// Use local event status for each click event dispatching since it'll be
// cleared by EventStateManager::PreHandleEvent(). Therefore, dispatching
// an event means that previous event status will be ignored.
@@ -4724,22 +4719,12 @@ EventStateManager::PostHandleMouseUp(WidgetMouseEvent* aMouseUpEvent,
return NS_OK;
}
- nsCOMPtr<nsIContent> mouseUpContent = GetEventTargetContent(aMouseUpEvent);
- // Click events apply to *elements* not nodes. At this point the target
- // content may have been reset to some non-element content, and so we need
- // to walk up the closest ancestor element, just like we do in
- // nsPresShell::HandlePositionedEvent.
- while (mouseUpContent && !mouseUpContent->IsElement()) {
- mouseUpContent = mouseUpContent->GetFlattenedTreeParent();
- }
-
- if (!mouseUpContent && !mCurrentTarget) {
- return NS_OK;
- }
+ nsCOMPtr<nsIContent> clickTarget = do_QueryInterface(aMouseUpEvent->mClickTarget);
+ NS_ENSURE_STATE(clickTarget);
// Fire click events if the event target is still available.
nsresult rv = DispatchClickEvents(presShell, aMouseUpEvent, aStatus,
- mouseUpContent);
+ clickTarget);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@@ -4750,13 +4735,13 @@ nsresult
EventStateManager::DispatchClickEvents(nsIPresShell* aPresShell,
WidgetMouseEvent* aMouseUpEvent,
nsEventStatus* aStatus,
- nsIContent* aMouseUpContent)
+ nsIContent* aClickTarget)
{
MOZ_ASSERT(aPresShell);
MOZ_ASSERT(aMouseUpEvent);
MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent));
MOZ_ASSERT(aStatus);
- MOZ_ASSERT(aMouseUpContent || mCurrentTarget || aOverrideClickTarget);
+ MOZ_ASSERT(aClickTarget);
bool notDispatchToContents =
(aMouseUpEvent->button == WidgetMouseEvent::eMiddleButton ||
@@ -4764,12 +4749,10 @@ EventStateManager::DispatchClickEvents(nsIPresShell* aPresShell,
bool fireAuxClick = notDispatchToContents;
-
- // HandleEvent clears out mCurrentTarget which we might need again
- nsWeakFrame currentTarget = mCurrentTarget;
+ nsWeakFrame currentTarget = aClickTarget->GetPrimaryFrame();
nsresult ret =
InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseClick,
- aPresShell, aMouseUpContent, currentTarget,
+ aPresShell, aClickTarget, currentTarget,
notDispatchToContents);
if (NS_WARN_IF(NS_FAILED(ret))) {
return ret;
@@ -4777,21 +4760,20 @@ EventStateManager::DispatchClickEvents(nsIPresShell* aPresShell,
// Fire double click event if click count is 2.
if (aMouseUpEvent->mClickCount == 2 &&
- aMouseUpContent && aMouseUpContent->IsInComposedDoc()) {
+ aClickTarget && aClickTarget->IsInComposedDoc()) {
ret = InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseDoubleClick,
- aPresShell, aMouseUpContent, currentTarget,
+ aPresShell, aClickTarget, currentTarget,
notDispatchToContents);
if (NS_WARN_IF(NS_FAILED(ret))) {
return ret;
-
- }
- }
+ }
+ }
// Fire auxclick even if necessary.
if (fireAuxClick &&
- aMouseUpContent && aMouseUpContent->IsInComposedDoc()) {
+ aClickTarget && aClickTarget->IsInComposedDoc()) {
ret = InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseAuxClick,
- aPresShell, aMouseUpContent, currentTarget,
+ aPresShell, aClickTarget, currentTarget,
false);
NS_WARNING_ASSERTION(NS_SUCCEEDED(ret), "Failed to dispatch eMouseAuxClick");
}
diff --git a/dom/events/EventStateManager.h b/dom/events/EventStateManager.h
index 6bf457b842..e0c250f3b7 100644
--- a/dom/events/EventStateManager.h
+++ b/dom/events/EventStateManager.h
@@ -1014,11 +1014,8 @@ private:
uint16_t mGestureDownButtons;
nsCOMPtr<nsIContent> mLastLeftMouseDownContent;
- nsCOMPtr<nsIContent> mLastLeftMouseDownContentParent;
nsCOMPtr<nsIContent> mLastMiddleMouseDownContent;
- nsCOMPtr<nsIContent> mLastMiddleMouseDownContentParent;
nsCOMPtr<nsIContent> mLastRightMouseDownContent;
- nsCOMPtr<nsIContent> mLastRightMouseDownContentParent;
nsCOMPtr<nsIContent> mActiveContent;
nsCOMPtr<nsIContent> mHoverContent;