Bug 1316251 Part2: Revise pointer event implementation. r?masayuki
Revise the pointer event implementation, includes
- Revise the implementation to fetch preference values.
- Merge the logic to generate pointerout and pointerleave when handling pointercancel and pointerup for touch device.
- Separate the logic to get the pointer capturing frame to PointerEventHandler.
MozReview-Commit-ID: 7pdAr0XFNT2
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -257,17 +257,16 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
/******************************************************************/
/* mozilla::EventStateManager */
/******************************************************************/
static uint32_t sESMInstanceCount = 0;
-static bool sPointerEventEnabled = false;
uint64_t EventStateManager::sUserInputCounter = 0;
int32_t EventStateManager::sUserInputEventDepth = 0;
bool EventStateManager::sNormalLMouseEventInProcess = false;
EventStateManager* EventStateManager::sActiveESM = nullptr;
nsIDocument* EventStateManager::sMouseOverDocument = nullptr;
AutoWeakFrame EventStateManager::sLastDragOverFrame = nullptr;
LayoutDeviceIntPoint EventStateManager::sPreLockPoint = LayoutDeviceIntPoint(0, 0);
@@ -305,23 +304,16 @@ EventStateManager::EventStateManager()
{
if (sESMInstanceCount == 0) {
gUserInteractionTimerCallback = new UITimerCallback();
if (gUserInteractionTimerCallback)
NS_ADDREF(gUserInteractionTimerCallback);
UpdateUserActivityTimer();
}
++sESMInstanceCount;
-
- static bool sAddedPointerEventEnabled = false;
- if (!sAddedPointerEventEnabled) {
- Preferences::AddBoolVarCache(&sPointerEventEnabled,
- "dom.w3c_pointer_events.enabled", false);
- sAddedPointerEventEnabled = true;
- }
WheelTransaction::InitializeStatics();
}
nsresult
EventStateManager::UpdateUserActivityTimer()
{
if (!gUserInteractionTimerCallback)
return NS_OK;
@@ -703,20 +695,18 @@ EventStateManager::PreHandleEvent(nsPres
if (mouseEvent->mExitFrom != WidgetMouseEvent::eTopLevel) {
// Treat it as a synthetic move so we don't generate spurious
// "exit" or "move" events. Any necessary "out" or "over" events
// will be generated by GenerateMouseEnterExit
mouseEvent->mMessage = eMouseMove;
mouseEvent->mReason = WidgetMouseEvent::eSynthesized;
// then fall through...
} else {
- if (sPointerEventEnabled) {
- // We should synthetize corresponding pointer events
- GeneratePointerEnterExit(ePointerLeave, mouseEvent);
- }
+ // We should synthetize corresponding pointer events
+ GeneratePointerEnterExit(ePointerLeave, mouseEvent);
GenerateMouseEnterExit(mouseEvent);
//This is a window level mouse exit event and should stop here
aEvent->mMessage = eVoidEvent;
break;
}
MOZ_FALLTHROUGH;
case eMouseMove:
case ePointerDown:
@@ -4258,16 +4248,19 @@ GetWindowClientRectCenter(nsIWidget* aWi
point.y = point.y / round * round;
return point - aWidget->WidgetToScreenOffset();
}
void
EventStateManager::GeneratePointerEnterExit(EventMessage aMessage,
WidgetMouseEvent* aEvent)
{
+ if (!PointerEventHandler::IsPointerEventEnabled()) {
+ return;
+ }
WidgetPointerEvent pointerEvent(*aEvent);
pointerEvent.mMessage = aMessage;
GenerateMouseEnterExit(&pointerEvent);
}
void
EventStateManager::GenerateMouseEnterExit(WidgetMouseEvent* aMouseEvent)
{
--- a/dom/events/PointerEventHandler.cpp
+++ b/dom/events/PointerEventHandler.cpp
@@ -41,29 +41,25 @@ static nsClassHashtable<nsUint32HashKey,
// Keeps information about pointers such as pointerId, activeState, pointerType,
// primaryState
static nsClassHashtable<nsUint32HashKey, PointerInfo>* sActivePointersIds;
/* static */ void
PointerEventHandler::Initialize()
{
- static bool addedPointerEventEnabled = false;
- if (!addedPointerEventEnabled) {
- Preferences::AddBoolVarCache(&sPointerEventEnabled,
- "dom.w3c_pointer_events.enabled", true);
- addedPointerEventEnabled = true;
+ static bool initialized = false;
+ if (initialized) {
+ return;
}
- static bool addedPointerEventImplicitCapture = false;
- if (!addedPointerEventImplicitCapture) {
- Preferences::AddBoolVarCache(&sPointerEventImplicitCapture,
- "dom.w3c_pointer_events.implicit_capture",
- true);
- addedPointerEventImplicitCapture = true;
- }
+ initialized = true;
+ Preferences::AddBoolVarCache(&sPointerEventEnabled,
+ "dom.w3c_pointer_events.enabled", true);
+ Preferences::AddBoolVarCache(&sPointerEventImplicitCapture,
+ "dom.w3c_pointer_events.implicit_capture", true);
}
/* static */ void
PointerEventHandler::InitializeStatics()
{
MOZ_ASSERT(!sPointerCaptureList, "InitializeStatics called multiple times!");
sPointerCaptureList =
new nsClassHashtable<nsUint32HashKey, PointerCaptureInfo>;
@@ -227,16 +223,45 @@ PointerEventHandler::GetPointerCapturing
{
PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
if (pointerCaptureInfo) {
return pointerCaptureInfo->mOverrideContent;
}
return nullptr;
}
+/* static */ nsIFrame*
+PointerEventHandler::GetPointerCapturingFrame(nsIFrame* aFrameUnderCursor,
+ WidgetGUIEvent* aEvent)
+{
+ if (!IsPointerEventEnabled() || (aEvent->mClass != ePointerEventClass &&
+ aEvent->mClass != eMouseEventClass) ||
+ aEvent->mMessage == ePointerDown || aEvent->mMessage == eMouseDown) {
+ // Pointer capture should only be applied to all pointer events and mouse
+ // events except ePointerDown and eMouseDown;
+ return aFrameUnderCursor;
+ }
+
+ WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
+ if (!mouseEvent) {
+ return aFrameUnderCursor;
+ }
+
+ // Find the content which captures the pointer.
+ nsIContent* capturingContent =
+ GetPointerCapturingContent(mouseEvent->pointerId);
+
+ if (!capturingContent) {
+ return aFrameUnderCursor;
+ }
+ // Return the content's primary frame as the target frame.
+ nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();
+ return capturingFrame ? capturingFrame : aFrameUnderCursor;
+}
+
/* static */ void
PointerEventHandler::ReleaseIfCaptureByDescendant(nsIContent* aContent)
{
// We should check that aChild does not contain pointer capturing elements.
// If it does we should release the pointer capture for the elements.
for (auto iter = sPointerCaptureList->Iter(); !iter.Done(); iter.Next()) {
PointerCaptureInfo* data = iter.UserData();
if (data && data->mPendingContent &&
@@ -297,51 +322,55 @@ PointerEventHandler::PostHandlePointerEv
// PreventDefault only applied for active pointers.
if (!pointerInfo->mActiveState) {
return;
}
aMouseOrTouchEvent->PreventDefault(false);
pointerInfo->mPreventMouseEventByContent = true;
}
-/* static */ nsresult
+/* static */ void
PointerEventHandler::DispatchPointerFromMouseOrTouch(
PresShell* aShell,
nsIFrame* aFrame,
WidgetGUIEvent* aEvent,
bool aDontRetargetEvents,
nsEventStatus* aStatus,
nsIContent** aTargetContent)
{
+ MOZ_ASSERT(IsPointerEventEnabled());
+ MOZ_ASSERT(aFrame);
+ MOZ_ASSERT(aEvent);
+
EventMessage pointerMessage = eVoidEvent;
if (aEvent->mClass == eMouseEventClass) {
WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
// 1. If it is not mouse then it is likely will come as touch event
// 2. We don't synthesize pointer events for those events that are not
// dispatched to DOM.
if (!mouseEvent->convertToPointer ||
!aEvent->IsAllowedToDispatchDOMEvent()) {
- return NS_OK;
+ return;
}
int16_t button = mouseEvent->button;
switch (mouseEvent->mMessage) {
case eMouseMove:
button = WidgetMouseEvent::eNoButton;
pointerMessage = ePointerMove;
break;
case eMouseUp:
pointerMessage = mouseEvent->buttons ? ePointerMove : ePointerUp;
break;
case eMouseDown:
pointerMessage =
mouseEvent->buttons & ~nsContentUtils::GetButtonsFlagForButton(button) ?
ePointerMove : ePointerDown;
break;
default:
- return NS_OK;
+ return;
}
WidgetPointerEvent event(*mouseEvent);
event.pointerId = mouseEvent->pointerId;
event.inputSource = mouseEvent->inputSource;
event.mMessage = pointerMessage;
event.button = button;
event.buttons = mouseEvent->buttons;
@@ -372,17 +401,17 @@ PointerEventHandler::DispatchPointerFrom
case eTouchStart:
pointerMessage = ePointerDown;
break;
case eTouchCancel:
case eTouchPointerCancel:
pointerMessage = ePointerCancel;
break;
default:
- return NS_OK;
+ return;
}
RefPtr<PresShell> shell(aShell);
for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) {
Touch* touch = touchEvent->mTouches[i];
if (!TouchManager::ShouldConvertTouchToPointer(touch, touchEvent)) {
continue;
}
@@ -405,17 +434,16 @@ PointerEventHandler::DispatchPointerFrom
event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
event.convertToPointer = touch->convertToPointer = false;
PreHandlePointerEventsPreventDefault(&event, aEvent);
shell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus,
aTargetContent);
PostHandlePointerEventsPreventDefault(&event, aEvent);
}
}
- return NS_OK;
}
/* static */ uint16_t
PointerEventHandler::GetPointerType(uint32_t aPointerId)
{
PointerInfo* pointerInfo = nullptr;
if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
return pointerInfo->mPointerType;
--- a/dom/events/PointerEventHandler.h
+++ b/dom/events/PointerEventHandler.h
@@ -73,16 +73,29 @@ public:
// aActiveState is additional information, which shows state of pointer like
// button state for mouse.
static bool GetPointerInfo(uint32_t aPointerId, bool& aActiveState);
// CheckPointerCaptureState checks cases, when got/lostpointercapture events
// should be fired.
static void CheckPointerCaptureState(WidgetPointerEvent* aEvent);
+ /**
+ * GetPointerCapturingFrame returns a target frame of aEvent. If the event is
+ * a mouse or pointer event, the pointer may be captured by a content. This
+ * method returns the capturing content's primary frame. Otherwise,
+ * aFrameUnderCursor.
+ *
+ * @param aFrameUnderCursor A frame under cursor.
+ * @param aEvent A mouse event or pointer event.
+ * @return Target frame for aEvent.
+ */
+ static nsIFrame* GetPointerCapturingFrame(nsIFrame* aFrameUnderCursor,
+ WidgetGUIEvent* aEvent);
+
static nsIContent* GetPointerCapturingContent(uint32_t aPointerId);
// Release pointer capture if captured by the specified content or it's
// descendant. This is called to handle the case that the pointer capturing
// content or it's parent is removed from the document.
static void ReleaseIfCaptureByDescendant(nsIContent* aContent);
/*
@@ -111,22 +124,22 @@ public:
* We add mPreventMouseEventByContent flag in PointerInfo to represent the
* active pointer won't firing compatible mouse events. It's set to true when
* content preventDefault on pointerdown
*/
static void PostHandlePointerEventsPreventDefault(
WidgetPointerEvent* aPointerEvent,
WidgetGUIEvent* aMouseOrTouchEvent);
- static nsresult DispatchPointerFromMouseOrTouch(PresShell* aShell,
- nsIFrame* aFrame,
- WidgetGUIEvent* aEvent,
- bool aDontRetargetEvents,
- nsEventStatus* aStatus,
- nsIContent** aTargetContent);
+ static void DispatchPointerFromMouseOrTouch(PresShell* aShell,
+ nsIFrame* aFrame,
+ WidgetGUIEvent* aEvent,
+ bool aDontRetargetEvents,
+ nsEventStatus* aStatus,
+ nsIContent** aTargetContent);
private:
// GetPointerType returns pointer type like mouse, pen or touch for pointer
// event with pointerId. The return value must be one of
// nsIDOMMouseEvent::MOZ_SOURCE_*
static uint16_t GetPointerType(uint32_t aPointerId);
// GetPointerPrimaryState returns state of attribute isPrimary for pointer
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -4446,16 +4446,17 @@ PresShell::ContentRemoved(nsIDocument *a
// not cases when the frame constructor calls its own methods to force
// frame reconstruction.
nsIContent* oldNextSibling = container->GetChildAt(aIndexInContainer);
mPresContext->RestyleManager()->ContentRemoved(container, aChild, oldNextSibling);
// After removing aChild from tree we should save information about live ancestor
if (mPointerEventTarget) {
+ MOZ_ASSERT(PointerEventHandler::IsPointerEventEnabled());
if (nsContentUtils::ContentIsDescendantOf(mPointerEventTarget, aChild)) {
mPointerEventTarget = aMaybeContainer;
}
}
PointerEventHandler::ReleaseIfCaptureByDescendant(aChild);
mFrameConstructor->ContentRemoved(aMaybeContainer, aChild, oldNextSibling,
@@ -7203,33 +7204,17 @@ PresShell::HandleEvent(nsIFrame* aFrame,
}
if (targetContent) {
PointerEventHandler::SetPointerCaptureById(pointerEvent->pointerId,
targetContent);
}
}
}
- // Mouse events should be fired to the same target as their mapped pointer
- // events
- if ((aEvent->mClass == ePointerEventClass ||
- aEvent->mClass == eMouseEventClass) &&
- aEvent->mMessage != ePointerDown && aEvent->mMessage != eMouseDown) {
- if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
- uint32_t pointerId = mouseEvent->pointerId;
- nsIContent* pointerCapturingContent =
- PointerEventHandler::GetPointerCapturingContent(pointerId);
-
- if (pointerCapturingContent) {
- if (nsIFrame* capturingFrame = pointerCapturingContent->GetPrimaryFrame()) {
- frame = capturingFrame;
- }
- }
- }
- }
+ frame = PointerEventHandler::GetPointerCapturingFrame(frame, aEvent);
// Suppress mouse event if it's being targeted at an element inside
// a document which needs events suppressed
if (aEvent->mClass == eMouseEventClass &&
frame->PresContext()->Document()->EventHandlingSuppressed()) {
if (aEvent->mMessage == eMouseDown) {
mNoDelayedMouseEvents = true;
} else if (!mNoDelayedMouseEvents && (aEvent->mMessage == eMouseUp ||
@@ -7315,16 +7300,17 @@ PresShell::HandleEvent(nsIFrame* aFrame,
}
}
}
// Before HandlePositionedEvent we should save mPointerEventTarget in some
// cases
AutoWeakFrame weakFrame;
if (aTargetContent && ePointerEventClass == aEvent->mClass) {
+ MOZ_ASSERT(PointerEventHandler::IsPointerEventEnabled());
weakFrame = frame;
shell->mPointerEventTarget = frame->GetContent();
MOZ_ASSERT(!frame->GetContent() ||
shell->GetDocument() == frame->GetContent()->OwnerDoc());
}
// Prevent deletion until we're done with event handling (bug 336582) and
// swap mPointerEventTarget to *aTargetContent
@@ -7339,16 +7325,17 @@ PresShell::HandleEvent(nsIFrame* aFrame,
rv = shell->HandlePositionedEvent(frame, aEvent, aEventStatus);
} else {
rv = HandlePositionedEvent(frame, aEvent, aEventStatus);
}
// After HandlePositionedEvent we should reestablish
// content (which still live in tree) in some cases
if (aTargetContent && ePointerEventClass == aEvent->mClass) {
+ MOZ_ASSERT(PointerEventHandler::IsPointerEventEnabled());
if (!weakFrame.IsAlive()) {
shell->mPointerEventTarget.swap(*aTargetContent);
}
}
return rv;
}