Bug 977003 - Make async got/lostpointercapture events. r=smaug

This commit is contained in:
Maksim Lebedev 2014-09-12 02:32:00 -04:00
parent 5e29ba50b8
commit d645e05bbd
3 changed files with 120 additions and 30 deletions

View File

@ -682,11 +682,16 @@ public:
aError.Throw(NS_ERROR_DOM_INVALID_POINTER_ERR);
return;
}
// Ignoring ReleasePointerCapture call on incorrect element (on element
// that didn't have capture before).
if (nsIPresShell::GetPointerCapturingContent(aPointerId) == this) {
nsIPresShell::ReleasePointerCapturingContent(aPointerId, this);
nsIPresShell::PointerCaptureInfo* pointerCaptureInfo = nullptr;
if (nsIPresShell::gPointerCaptureList->Get(aPointerId, &pointerCaptureInfo) && pointerCaptureInfo) {
// Call ReleasePointerCapture only on correct element
// (on element that have status pointer capture override
// or on element that have status pending pointer capture)
if (pointerCaptureInfo->mOverrideContent == this) {
nsIPresShell::ReleasePointerCapturingContent(aPointerId, this);
} else if (pointerCaptureInfo->mPendingContent == this) {
nsIPresShell::ReleasePointerCapturingContent(aPointerId, this);
}
}
}
void SetCapture(bool aRetargetToElement)

View File

@ -27,6 +27,7 @@
#include "nsTHashtable.h"
#include "nsHashKeys.h"
#include "nsISupports.h"
#include "nsIContent.h"
#include "nsQueryFrame.h"
#include "nsCoord.h"
#include "nsColor.h"
@ -43,7 +44,6 @@
#include "nsMargin.h"
#include "nsFrameState.h"
class nsIContent;
class nsDocShell;
class nsIDocument;
class nsIFrame;
@ -1213,10 +1213,32 @@ public:
static nsRefPtrHashtable<nsUint32HashKey, mozilla::dom::Touch>* gCaptureTouchList;
static bool gPreventMouseEvents;
struct PointerCaptureInfo
{
nsCOMPtr<nsIContent> mPendingContent;
nsCOMPtr<nsIContent> mOverrideContent;
bool mReleaseContent;
PointerCaptureInfo(nsIContent* aPendingContent) :
mPendingContent(aPendingContent), mReleaseContent(false)
{
MOZ_COUNT_CTOR(PointerCaptureInfo);
}
~PointerCaptureInfo()
{
MOZ_COUNT_DTOR(PointerCaptureInfo);
}
bool Empty()
{
return !(mPendingContent || mOverrideContent);
}
};
// Keeps a map between pointerId and element that currently capturing pointer
// with such pointerId. If pointerId is absent in this map then nobody is
// capturing it.
static nsRefPtrHashtable<nsUint32HashKey, nsIContent>* gPointerCaptureList;
// capturing it. Additionally keep information about pending capturing content.
static nsClassHashtable<nsUint32HashKey, PointerCaptureInfo>* gPointerCaptureList;
struct PointerInfo
{
@ -1229,11 +1251,12 @@ public:
static nsClassHashtable<nsUint32HashKey, PointerInfo>* gActivePointersIds;
static void DispatchGotOrLostPointerCaptureEvent(bool aIsGotCapture,
uint32_t aPointerId,
nsIContent* aCaptureTarget);
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);
// 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.

View File

@ -201,7 +201,7 @@ CapturingContentInfo nsIPresShell::gCaptureInfo =
false /* mPreventDrag */, nullptr /* mContent */ };
nsIContent* nsIPresShell::gKeyDownTarget;
nsRefPtrHashtable<nsUint32HashKey, dom::Touch>* nsIPresShell::gCaptureTouchList;
nsRefPtrHashtable<nsUint32HashKey, nsIContent>* nsIPresShell::gPointerCaptureList;
nsClassHashtable<nsUint32HashKey, nsIPresShell::PointerCaptureInfo>* nsIPresShell::gPointerCaptureList;
nsClassHashtable<nsUint32HashKey, nsIPresShell::PointerInfo>* nsIPresShell::gActivePointersIds;
bool nsIPresShell::gPreventMouseEvents = false;
@ -6274,10 +6274,27 @@ nsIPresShell::SetCapturingContent(nsIContent* aContent, uint8_t aFlags)
}
}
class AsyncCheckPointerCaptureStateCaller : public nsRunnable
{
public:
AsyncCheckPointerCaptureStateCaller(int32_t aPointerId) : mPointerId(aPointerId) {}
NS_IMETHOD Run()
{
nsIPresShell::CheckPointerCaptureState(mPointerId);
return NS_OK;
}
private:
int32_t mPointerId;
};
/* static */ void
nsIPresShell::SetPointerCapturingContent(uint32_t aPointerId, nsIContent* aContent)
{
nsIContent* content = GetPointerCapturingContent(aPointerId);
PointerCaptureInfo* pointerCaptureInfo = nullptr;
gPointerCaptureList->Get(aPointerId, &pointerCaptureInfo);
nsIContent* content = pointerCaptureInfo ? pointerCaptureInfo->mOverrideContent : nullptr;
PointerInfo* pointerInfo = nullptr;
if (!content && gActivePointersIds->Get(aPointerId, &pointerInfo) &&
@ -6286,19 +6303,11 @@ nsIPresShell::SetPointerCapturingContent(uint32_t aPointerId, nsIContent* aConte
SetCapturingContent(aContent, CAPTURE_PREVENTDRAG);
}
if (content) {
// Releasing capture for given pointer.
gPointerCaptureList->Remove(aPointerId);
DispatchGotOrLostPointerCaptureEvent(false, aPointerId, content);
// Need to check the state because a lostpointercapture listener
// may have called SetPointerCapture
if (GetPointerCapturingContent(aPointerId)) {
return;
}
if (pointerCaptureInfo) {
pointerCaptureInfo->mPendingContent = aContent;
} else {
gPointerCaptureList->Put(aPointerId, new PointerCaptureInfo(aContent));
}
gPointerCaptureList->Put(aPointerId, aContent);
DispatchGotOrLostPointerCaptureEvent(true, aPointerId, aContent);
}
/* static */ void
@ -6308,16 +6317,61 @@ nsIPresShell::ReleasePointerCapturingContent(uint32_t aPointerId, nsIContent* aC
SetCapturingContent(nullptr, CAPTURE_PREVENTDRAG);
}
// Releasing capture for given pointer.
gPointerCaptureList->Remove(aPointerId);
DispatchGotOrLostPointerCaptureEvent(false, aPointerId, aContent);
PointerCaptureInfo* pointerCaptureInfo = nullptr;
if (gPointerCaptureList->Get(aPointerId, &pointerCaptureInfo) && pointerCaptureInfo) {
// Set flag to asyncronously release capture for given pointer.
pointerCaptureInfo->mReleaseContent = true;
nsRefPtr<AsyncCheckPointerCaptureStateCaller> asyncCaller =
new AsyncCheckPointerCaptureStateCaller(aPointerId);
NS_DispatchToCurrentThread(asyncCaller);
}
}
/* static */ nsIContent*
nsIPresShell::GetPointerCapturingContent(uint32_t aPointerId)
{
return gPointerCaptureList->GetWeak(aPointerId);
PointerCaptureInfo* pointerCaptureInfo = nullptr;
if (gPointerCaptureList->Get(aPointerId, &pointerCaptureInfo) && pointerCaptureInfo) {
return pointerCaptureInfo->mOverrideContent;
}
return nullptr;
}
/* static */ void
nsIPresShell::CheckPointerCaptureState(uint32_t aPointerId)
{
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);
} 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);
}
}
}
/* static */ bool
@ -7188,6 +7242,14 @@ PresShell::HandleEvent(nsIFrame* aFrame,
}
}
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);
}
}
if (aEvent->mClass == ePointerEventClass &&
aEvent->message != NS_POINTER_DOWN) {
if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
@ -10345,7 +10407,7 @@ void nsIPresShell::InitializeStatics()
{
NS_ASSERTION(!gCaptureTouchList, "InitializeStatics called multiple times!");
gCaptureTouchList = new nsRefPtrHashtable<nsUint32HashKey, dom::Touch>;
gPointerCaptureList = new nsRefPtrHashtable<nsUint32HashKey, nsIContent>;
gPointerCaptureList = new nsClassHashtable<nsUint32HashKey, PointerCaptureInfo>;
gActivePointersIds = new nsClassHashtable<nsUint32HashKey, PointerInfo>;
}