Bug 1073563 - The lostpointercapture event must be dispatched before any other pointer events. r=smaug
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1252,17 +1252,20 @@ public:
static nsClassHashtable<nsUint32HashKey, PointerInfo>* gActivePointersIds;
static void DispatchGotOrLostPointerCaptureEvent(bool aIsGotCapture,
uint32_t aPointerId,
nsIContent* aCaptureTarget);
static void SetPointerCapturingContent(uint32_t aPointerId, nsIContent* aContent);
static void ReleasePointerCapturingContent(uint32_t aPointerId, nsIContent* aContent);
static nsIContent* GetPointerCapturingContent(uint32_t aPointerId);
- static void CheckPointerCaptureState(uint32_t aPointerId);
+
+ // CheckPointerCaptureState checks cases, when got/lostpointercapture events should be fired.
+ // Function returns true, if any of events was fired; false, if no one event was fired.
+ static bool CheckPointerCaptureState(uint32_t aPointerId);
// GetPointerInfo returns true if pointer with aPointerId is situated in device, false otherwise.
// aActiveState is additional information, which shows state of pointer like button state for mouse.
static bool GetPointerInfo(uint32_t aPointerId, bool& aActiveState);
/**
* When capturing content is set, it traps all mouse events and retargets
* them at this content node. If capturing is not allowed
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -6331,51 +6331,55 @@ nsIPresShell::GetPointerCapturingContent
{
PointerCaptureInfo* pointerCaptureInfo = nullptr;
if (gPointerCaptureList->Get(aPointerId, &pointerCaptureInfo) && pointerCaptureInfo) {
return pointerCaptureInfo->mOverrideContent;
}
return nullptr;
}
-/* static */ void
+/* static */ bool
nsIPresShell::CheckPointerCaptureState(uint32_t aPointerId)
{
+ bool didDispatchEvent = false;
PointerCaptureInfo* pointerCaptureInfo = nullptr;
if (gPointerCaptureList->Get(aPointerId, &pointerCaptureInfo) && pointerCaptureInfo) {
// If pendingContent exist or anybody calls element.releasePointerCapture
// we should dispatch lostpointercapture event to overrideContent if it exist
if (pointerCaptureInfo->mPendingContent || pointerCaptureInfo->mReleaseContent) {
if (pointerCaptureInfo->mOverrideContent) {
nsCOMPtr<nsIContent> content;
pointerCaptureInfo->mOverrideContent.swap(content);
if (pointerCaptureInfo->mReleaseContent) {
pointerCaptureInfo->mPendingContent = nullptr;
}
if (pointerCaptureInfo->Empty()) {
gPointerCaptureList->Remove(aPointerId);
}
DispatchGotOrLostPointerCaptureEvent(false, aPointerId, content);
+ didDispatchEvent = true;
} else if (pointerCaptureInfo->mPendingContent && pointerCaptureInfo->mReleaseContent) {
// If anybody calls element.releasePointerCapture
// We should clear overrideContent and pendingContent
pointerCaptureInfo->mPendingContent = nullptr;
pointerCaptureInfo->mReleaseContent = false;
}
}
}
if (gPointerCaptureList->Get(aPointerId, &pointerCaptureInfo) && pointerCaptureInfo) {
// If pendingContent exist we should dispatch gotpointercapture event to it
if (pointerCaptureInfo && pointerCaptureInfo->mPendingContent) {
pointerCaptureInfo->mOverrideContent = pointerCaptureInfo->mPendingContent;
pointerCaptureInfo->mPendingContent = nullptr;
pointerCaptureInfo->mReleaseContent = false;
DispatchGotOrLostPointerCaptureEvent(true, aPointerId, pointerCaptureInfo->mOverrideContent);
- }
- }
+ didDispatchEvent = true;
+ }
+ }
+ return didDispatchEvent;
}
/* static */ bool
nsIPresShell::GetPointerInfo(uint32_t aPointerId, bool& aActiveState)
{
PointerInfo* pointerInfo = nullptr;
if (gActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
aActiveState = pointerInfo->mActiveState;
@@ -7244,17 +7248,19 @@ PresShell::HandleEvent(nsIFrame* aFrame,
frame = capturingFrame;
}
}
if (aEvent->mClass == ePointerEventClass) {
if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
// Before any pointer events, we should check state of pointer capture,
// Thus got/lostpointercapture events emulate asynchronous behavior.
- CheckPointerCaptureState(pointerEvent->pointerId);
+ // Handlers of got/lostpointercapture events can change capturing state,
+ // That's why we should re-check pointer capture state until stable state.
+ while(CheckPointerCaptureState(pointerEvent->pointerId));
}
}
if (aEvent->mClass == ePointerEventClass &&
aEvent->message != NS_POINTER_DOWN) {
if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
uint32_t pointerId = pointerEvent->pointerId;
nsIContent* pointerCapturingContent = GetPointerCapturingContent(pointerId);