Bug 1930931 - Make the dragging interaction still happens when the event target frame is moved/destroyed upon mousedown r=masayuki,dom-core

Differential Revision: https://phabricator.services.mozilla.com/D238318
This commit is contained in:
Sean Feng
2025-02-21 18:25:06 +00:00
parent aa102388a8
commit d8ecdd8d22
5 changed files with 84 additions and 1 deletions

View File

@@ -7593,4 +7593,24 @@ bool EventStateManager::WheelPrefs::IsOverOnePageScrollAllowedY(
MIN_MULTIPLIER_VALUE_ALLOWING_OVER_ONE_PAGE_SCROLL;
}
void EventStateManager::NotifyDestroyingFrameForGesture(nsIFrame* aFrame) {
MOZ_ASSERT(aFrame);
if (mGestureDownContent != aFrame->GetContent()) {
return;
}
if (nsIFrame* parent = aFrame->GetParent()) {
nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(parent);
MOZ_ASSERT(f);
nsIContent* content = f->GetContent();
mGestureDownContent = content;
mGestureDownFrameOwner = content;
mGestureDownInTextControl =
content && content->IsInNativeAnonymousSubtree() &&
TextControlElement::FromNodeOrNull(
content->GetClosestNativeAnonymousSubtreeRootParentOrHost());
}
}
} // namespace mozilla

View File

@@ -526,6 +526,17 @@ class EventStateManager : public nsSupportsWeakReference, public nsIObserver {
return mMouseEnterLeaveHelper;
}
nsIContent* GetTrackingDragGestureContent() const {
return mGestureDownContent;
}
// If the current frame is for the current gesture down content (being
// dragged), when it's destroyed, we should continue the gesture on its
// parent.
void NotifyDestroyingFrameForGesture(nsIFrame* aFrame);
bool IsTrackingDragGesture() const { return mGestureDownContent != nullptr; }
protected:
/*
* If aTargetFrame's widget has a cached cursor value, resets the cursor
@@ -1215,7 +1226,6 @@ class EventStateManager : public nsSupportsWeakReference, public nsIObserver {
dom::RemoteDragStartData* aDragStartData, nsIPrincipal* aPrincipal,
nsIContentSecurityPolicy* aCsp, nsICookieJarSettings* aCookieJarSettings);
bool IsTrackingDragGesture() const { return mGestureDownContent != nullptr; }
/**
* Set the fields of aEvent to reflect the mouse position and modifier keys
* that were set when the user first pressed the mouse button (stored by

View File

@@ -2230,6 +2230,9 @@ void PresShell::NotifyDestroyingFrame(nsIFrame* aFrame) {
}
}
EventStateManager* const esm = mPresContext->EventStateManager();
esm->NotifyDestroyingFrameForGesture(aFrame);
mFramesToDirty.Remove(aFrame);
if (ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(aFrame)) {

View File

@@ -4696,6 +4696,17 @@ nsresult nsIFrame::MoveCaretToEventPoint(nsPresContext* aPresContext,
return NS_OK;
}
EventStateManager* const esm = aPresContext->EventStateManager();
if (nsIContent* dragGestureContent = esm->GetTrackingDragGestureContent()) {
if (dragGestureContent != this->GetContent()) {
// When the current tracked dragging gesture is different
// than this frame, it means this frame was being dragged, however
// it got moved/destroyed. So we should consider the drag is
// still happening, so return early here.
return NS_OK;
}
}
const nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
aMouseEvent, RelativeTo{this});

View File

@@ -0,0 +1,39 @@
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<head>
<title>Test dragging still occurs when mousedown moves the inner element</title>
</head>
<body>
<div id="element" draggable="true" style="width: 40px; height: 40px; background-color:red;">
<div id="inner" style="width: 30px; height: 30px; background-color:black;"></div>
</div>
<div id="element2"></div>
<script>
promise_test(function() {
return new Promise(r => {
element.addEventListener("dragstart", function(e) {
assert_equals(e.target, element);
r();
});
element.addEventListener("mousedown", function() {
element2.appendChild(inner);
});
new test_driver.Actions()
.pointerMove(0, 0, {origin: inner})
.pointerDown()
.pointerMove(10, 10, {origin:inner})
.pointerUp()
.send();
});
}, "dragstart should still fire when the mousedown event moves the inner element around");
</script>
</body>
</html>