mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-24 18:55:30 +00:00
653167e88f
The event targets of touch events are not necessarily to be the same as their corresponding pointer events. So we don't have to cache the event target of pointer events when they are generated from touch. MozReview-Commit-ID: 9Gd6ion7NXf
666 lines
22 KiB
C++
666 lines
22 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "PointerEventHandler.h"
|
|
#include "nsIFrame.h"
|
|
#include "PointerEvent.h"
|
|
#include "mozilla/PresShell.h"
|
|
|
|
namespace mozilla {
|
|
|
|
using namespace dom;
|
|
|
|
static bool sPointerEventEnabled = true;
|
|
static bool sPointerEventImplicitCapture = false;
|
|
|
|
class PointerInfo final
|
|
{
|
|
public:
|
|
uint16_t mPointerType;
|
|
bool mActiveState;
|
|
bool mPrimaryState;
|
|
bool mPreventMouseEventByContent;
|
|
explicit PointerInfo(bool aActiveState, uint16_t aPointerType,
|
|
bool aPrimaryState)
|
|
: mPointerType(aPointerType)
|
|
, mActiveState(aActiveState)
|
|
, mPrimaryState(aPrimaryState)
|
|
, mPreventMouseEventByContent(false)
|
|
{
|
|
}
|
|
};
|
|
|
|
// 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. Additionally keep information about pending capturing content.
|
|
static nsClassHashtable<nsUint32HashKey,
|
|
PointerCaptureInfo>* sPointerCaptureList;
|
|
|
|
// Keeps information about pointers such as pointerId, activeState, pointerType,
|
|
// primaryState
|
|
static nsClassHashtable<nsUint32HashKey, PointerInfo>* sActivePointersIds;
|
|
|
|
/* static */ void
|
|
PointerEventHandler::Initialize()
|
|
{
|
|
static bool initialized = false;
|
|
if (initialized) {
|
|
return;
|
|
}
|
|
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>;
|
|
sActivePointersIds = new nsClassHashtable<nsUint32HashKey, PointerInfo>;
|
|
}
|
|
|
|
/* static */ void
|
|
PointerEventHandler::ReleaseStatics()
|
|
{
|
|
MOZ_ASSERT(sPointerCaptureList, "ReleaseStatics called without Initialize!");
|
|
delete sPointerCaptureList;
|
|
sPointerCaptureList = nullptr;
|
|
delete sActivePointersIds;
|
|
sActivePointersIds = nullptr;
|
|
}
|
|
|
|
/* static */ bool
|
|
PointerEventHandler::IsPointerEventEnabled()
|
|
{
|
|
return sPointerEventEnabled;
|
|
}
|
|
|
|
/* static */ bool
|
|
PointerEventHandler::IsPointerEventImplicitCaptureForTouchEnabled()
|
|
{
|
|
return sPointerEventEnabled && sPointerEventImplicitCapture;
|
|
}
|
|
|
|
/* static */ void
|
|
PointerEventHandler::UpdateActivePointerState(WidgetMouseEvent* aEvent)
|
|
{
|
|
if (!IsPointerEventEnabled() || !aEvent) {
|
|
return;
|
|
}
|
|
switch (aEvent->mMessage) {
|
|
case eMouseEnterIntoWidget:
|
|
// In this case we have to know information about available mouse pointers
|
|
sActivePointersIds->Put(aEvent->pointerId,
|
|
new PointerInfo(false, aEvent->inputSource, true));
|
|
break;
|
|
case ePointerDown:
|
|
// In this case we switch pointer to active state
|
|
if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
|
|
sActivePointersIds->Put(pointerEvent->pointerId,
|
|
new PointerInfo(true, pointerEvent->inputSource,
|
|
pointerEvent->mIsPrimary));
|
|
}
|
|
break;
|
|
case ePointerCancel:
|
|
// pointercancel means a pointer is unlikely to continue to produce pointer
|
|
// events. In that case, we should turn off active state or remove the
|
|
// pointer from active pointers.
|
|
case ePointerUp:
|
|
// In this case we remove information about pointer or turn off active state
|
|
if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
|
|
if(pointerEvent->inputSource != nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
|
|
sActivePointersIds->Put(pointerEvent->pointerId,
|
|
new PointerInfo(false,
|
|
pointerEvent->inputSource,
|
|
pointerEvent->mIsPrimary));
|
|
} else {
|
|
sActivePointersIds->Remove(pointerEvent->pointerId);
|
|
}
|
|
}
|
|
break;
|
|
case eMouseExitFromWidget:
|
|
// In this case we have to remove information about disappeared mouse
|
|
// pointers
|
|
sActivePointersIds->Remove(aEvent->pointerId);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* static */ void
|
|
PointerEventHandler::SetPointerCaptureById(uint32_t aPointerId,
|
|
nsIContent* aContent)
|
|
{
|
|
MOZ_ASSERT(aContent);
|
|
if (nsIDOMMouseEvent::MOZ_SOURCE_MOUSE == GetPointerType(aPointerId)) {
|
|
nsIPresShell::SetCapturingContent(aContent, CAPTURE_PREVENTDRAG);
|
|
}
|
|
|
|
PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
|
|
if (pointerCaptureInfo) {
|
|
pointerCaptureInfo->mPendingContent = aContent;
|
|
} else {
|
|
sPointerCaptureList->Put(aPointerId, new PointerCaptureInfo(aContent));
|
|
}
|
|
}
|
|
|
|
/* static */ PointerCaptureInfo*
|
|
PointerEventHandler::GetPointerCaptureInfo(uint32_t aPointerId)
|
|
{
|
|
PointerCaptureInfo* pointerCaptureInfo = nullptr;
|
|
sPointerCaptureList->Get(aPointerId, &pointerCaptureInfo);
|
|
return pointerCaptureInfo;
|
|
}
|
|
|
|
/* static */ void
|
|
PointerEventHandler::ReleasePointerCaptureById(uint32_t aPointerId)
|
|
{
|
|
PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
|
|
if (pointerCaptureInfo && pointerCaptureInfo->mPendingContent) {
|
|
if (nsIDOMMouseEvent::MOZ_SOURCE_MOUSE == GetPointerType(aPointerId)) {
|
|
nsIPresShell::SetCapturingContent(nullptr, CAPTURE_PREVENTDRAG);
|
|
}
|
|
pointerCaptureInfo->mPendingContent = nullptr;
|
|
}
|
|
}
|
|
|
|
/* static */ void
|
|
PointerEventHandler::ReleaseAllPointerCapture()
|
|
{
|
|
for (auto iter = sPointerCaptureList->Iter(); !iter.Done(); iter.Next()) {
|
|
PointerCaptureInfo* data = iter.UserData();
|
|
if (data && data->mPendingContent) {
|
|
ReleasePointerCaptureById(iter.Key());
|
|
}
|
|
}
|
|
}
|
|
|
|
/* static */ bool
|
|
PointerEventHandler::GetPointerInfo(uint32_t aPointerId, bool& aActiveState)
|
|
{
|
|
PointerInfo* pointerInfo = nullptr;
|
|
if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
|
|
aActiveState = pointerInfo->mActiveState;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* static */ void
|
|
PointerEventHandler::MaybeProcessPointerCapture(WidgetGUIEvent* aEvent)
|
|
{
|
|
switch (aEvent->mClass) {
|
|
case eMouseEventClass:
|
|
ProcessPointerCaptureForMouse(aEvent->AsMouseEvent());
|
|
break;
|
|
case eTouchEventClass:
|
|
ProcessPointerCaptureForTouch(aEvent->AsTouchEvent());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* static */ void
|
|
PointerEventHandler::ProcessPointerCaptureForMouse(WidgetMouseEvent* aEvent)
|
|
{
|
|
if (!ShouldGeneratePointerEventFromMouse(aEvent)) {
|
|
return;
|
|
}
|
|
|
|
PointerCaptureInfo* info = GetPointerCaptureInfo(aEvent->pointerId);
|
|
if (!info || info->mPendingContent == info->mOverrideContent) {
|
|
return;
|
|
}
|
|
WidgetPointerEvent localEvent(*aEvent);
|
|
InitPointerEventFromMouse(&localEvent, aEvent, eVoidEvent);
|
|
CheckPointerCaptureState(&localEvent);
|
|
}
|
|
|
|
/* static */ void
|
|
PointerEventHandler::ProcessPointerCaptureForTouch(WidgetTouchEvent* aEvent)
|
|
{
|
|
if (!ShouldGeneratePointerEventFromTouch(aEvent)) {
|
|
return;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < aEvent->mTouches.Length(); ++i) {
|
|
Touch* touch = aEvent->mTouches[i];
|
|
if (!TouchManager::ShouldConvertTouchToPointer(touch, aEvent)) {
|
|
continue;
|
|
}
|
|
PointerCaptureInfo* info = GetPointerCaptureInfo(touch->Identifier());
|
|
if (!info || info->mPendingContent == info->mOverrideContent) {
|
|
continue;
|
|
}
|
|
WidgetPointerEvent event(aEvent->IsTrusted(), eVoidEvent, aEvent->mWidget);
|
|
InitPointerEventFromTouch(&event, aEvent, touch, i == 0);
|
|
CheckPointerCaptureState(&event);
|
|
}
|
|
}
|
|
|
|
/* static */ void
|
|
PointerEventHandler::CheckPointerCaptureState(WidgetPointerEvent* aEvent)
|
|
{
|
|
// Handle pending pointer capture before any pointer events except
|
|
// gotpointercapture / lostpointercapture.
|
|
if (!aEvent) {
|
|
return;
|
|
}
|
|
MOZ_ASSERT(IsPointerEventEnabled());
|
|
MOZ_ASSERT(aEvent->mClass == ePointerEventClass);
|
|
|
|
PointerCaptureInfo* captureInfo = GetPointerCaptureInfo(aEvent->pointerId);
|
|
|
|
if (!captureInfo ||
|
|
captureInfo->mPendingContent == captureInfo->mOverrideContent) {
|
|
return;
|
|
}
|
|
// cache captureInfo->mPendingContent since it may be changed in the pointer
|
|
// event listener
|
|
nsIContent* pendingContent = captureInfo->mPendingContent.get();
|
|
if (captureInfo->mOverrideContent) {
|
|
DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ false, aEvent,
|
|
captureInfo->mOverrideContent);
|
|
}
|
|
if (pendingContent) {
|
|
DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ true, aEvent,
|
|
pendingContent);
|
|
}
|
|
|
|
captureInfo->mOverrideContent = pendingContent;
|
|
if (captureInfo->Empty()) {
|
|
sPointerCaptureList->Remove(aEvent->pointerId);
|
|
}
|
|
}
|
|
|
|
/* static */ void
|
|
PointerEventHandler::ImplicitlyCapturePointer(nsIFrame* aFrame,
|
|
WidgetEvent* aEvent)
|
|
{
|
|
MOZ_ASSERT(aEvent->mMessage == ePointerDown);
|
|
if (!aFrame || !IsPointerEventEnabled() ||
|
|
!IsPointerEventImplicitCaptureForTouchEnabled()) {
|
|
return;
|
|
}
|
|
WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
|
|
NS_WARNING_ASSERTION(pointerEvent,
|
|
"Call ImplicitlyCapturePointer with non-pointer event");
|
|
if (pointerEvent->inputSource != nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
|
|
// We only implicitly capture the pointer for touch device.
|
|
return;
|
|
}
|
|
nsCOMPtr<nsIContent> target;
|
|
aFrame->GetContentForEvent(aEvent, getter_AddRefs(target));
|
|
while (target && !target->IsElement()) {
|
|
target = target->GetParent();
|
|
}
|
|
if (NS_WARN_IF(!target)) {
|
|
return;
|
|
}
|
|
SetPointerCaptureById(pointerEvent->pointerId, target);
|
|
}
|
|
|
|
/* static */ void
|
|
PointerEventHandler::ImplicitlyReleasePointerCapture(WidgetEvent* aEvent)
|
|
{
|
|
MOZ_ASSERT(aEvent);
|
|
if (aEvent->mMessage != ePointerUp && aEvent->mMessage != ePointerCancel) {
|
|
return;
|
|
}
|
|
WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
|
|
ReleasePointerCaptureById(pointerEvent->pointerId);
|
|
CheckPointerCaptureState(pointerEvent);
|
|
}
|
|
|
|
/* static */ nsIContent*
|
|
PointerEventHandler::GetPointerCapturingContent(uint32_t aPointerId)
|
|
{
|
|
PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
|
|
if (pointerCaptureInfo) {
|
|
return pointerCaptureInfo->mOverrideContent;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
/* static */ nsIContent*
|
|
PointerEventHandler::GetPointerCapturingContent(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 nullptr;
|
|
}
|
|
|
|
WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
|
|
if (!mouseEvent) {
|
|
return nullptr;
|
|
}
|
|
return GetPointerCapturingContent(mouseEvent->pointerId);
|
|
}
|
|
|
|
/* 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 &&
|
|
nsContentUtils::ContentIsDescendantOf(data->mPendingContent,
|
|
aContent)) {
|
|
ReleasePointerCaptureById(iter.Key());
|
|
}
|
|
}
|
|
}
|
|
|
|
/* static */ void
|
|
PointerEventHandler::PreHandlePointerEventsPreventDefault(
|
|
WidgetPointerEvent* aPointerEvent,
|
|
WidgetGUIEvent* aMouseOrTouchEvent)
|
|
{
|
|
if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage == ePointerDown) {
|
|
return;
|
|
}
|
|
PointerInfo* pointerInfo = nullptr;
|
|
if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
|
|
!pointerInfo) {
|
|
// The PointerInfo for active pointer should be added for normal cases. But
|
|
// in some cases, we may receive mouse events before adding PointerInfo in
|
|
// sActivePointersIds. (e.g. receive mousemove before eMouseEnterIntoWidget
|
|
// or change preference 'dom.w3c_pointer_events.enabled' from off to on).
|
|
// In these cases, we could ignore them because they are not the events
|
|
// between a DefaultPrevented pointerdown and the corresponding pointerup.
|
|
return;
|
|
}
|
|
if (!pointerInfo->mPreventMouseEventByContent) {
|
|
return;
|
|
}
|
|
aMouseOrTouchEvent->PreventDefault(false);
|
|
if (aPointerEvent->mMessage == ePointerUp) {
|
|
pointerInfo->mPreventMouseEventByContent = false;
|
|
}
|
|
}
|
|
|
|
/* static */ void
|
|
PointerEventHandler::PostHandlePointerEventsPreventDefault(
|
|
WidgetPointerEvent* aPointerEvent,
|
|
WidgetGUIEvent* aMouseOrTouchEvent)
|
|
{
|
|
if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage != ePointerDown ||
|
|
!aPointerEvent->DefaultPreventedByContent()) {
|
|
return;
|
|
}
|
|
PointerInfo* pointerInfo = nullptr;
|
|
if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
|
|
!pointerInfo) {
|
|
// We already added the PointerInfo for active pointer when
|
|
// PresShell::HandleEvent handling pointerdown event.
|
|
#ifdef DEBUG
|
|
MOZ_CRASH("Got ePointerDown w/o active pointer info!!");
|
|
#endif // #ifdef DEBUG
|
|
return;
|
|
}
|
|
// PreventDefault only applied for active pointers.
|
|
if (!pointerInfo->mActiveState) {
|
|
return;
|
|
}
|
|
aMouseOrTouchEvent->PreventDefault(false);
|
|
pointerInfo->mPreventMouseEventByContent = true;
|
|
}
|
|
|
|
/* static */ void
|
|
PointerEventHandler::InitPointerEventFromMouse(
|
|
WidgetPointerEvent* aPointerEvent,
|
|
WidgetMouseEvent* aMouseEvent,
|
|
EventMessage aMessage)
|
|
{
|
|
MOZ_ASSERT(aPointerEvent);
|
|
MOZ_ASSERT(aMouseEvent);
|
|
aPointerEvent->pointerId = aMouseEvent->pointerId;
|
|
aPointerEvent->inputSource = aMouseEvent->inputSource;
|
|
aPointerEvent->mMessage = aMessage;
|
|
aPointerEvent->button = aMouseEvent->mMessage == eMouseMove ?
|
|
WidgetMouseEvent::eNoButton : aMouseEvent->button;
|
|
|
|
aPointerEvent->buttons = aMouseEvent->buttons;
|
|
aPointerEvent->pressure = aPointerEvent->buttons ?
|
|
aMouseEvent->pressure ?
|
|
aMouseEvent->pressure : 0.5f :
|
|
0.0f;
|
|
}
|
|
|
|
/* static */ void
|
|
PointerEventHandler::InitPointerEventFromTouch(
|
|
WidgetPointerEvent* aPointerEvent,
|
|
WidgetTouchEvent* aTouchEvent,
|
|
mozilla::dom::Touch* aTouch,
|
|
bool aIsPrimary)
|
|
{
|
|
MOZ_ASSERT(aPointerEvent);
|
|
MOZ_ASSERT(aTouchEvent);
|
|
|
|
int16_t button = aTouchEvent->mMessage == eTouchMove ?
|
|
WidgetMouseEvent::eNoButton :
|
|
WidgetMouseEvent::eLeftButton;
|
|
|
|
int16_t buttons = aTouchEvent->mMessage == eTouchEnd ?
|
|
WidgetMouseEvent::eNoButtonFlag :
|
|
WidgetMouseEvent::eLeftButtonFlag;
|
|
|
|
aPointerEvent->mIsPrimary = aIsPrimary;
|
|
aPointerEvent->pointerId = aTouch->Identifier();
|
|
aPointerEvent->mRefPoint = aTouch->mRefPoint;
|
|
aPointerEvent->mModifiers = aTouchEvent->mModifiers;
|
|
aPointerEvent->mWidth = aTouch->RadiusX(CallerType::System);
|
|
aPointerEvent->mHeight = aTouch->RadiusY(CallerType::System);
|
|
aPointerEvent->tiltX = aTouch->tiltX;
|
|
aPointerEvent->tiltY = aTouch->tiltY;
|
|
aPointerEvent->mTime = aTouchEvent->mTime;
|
|
aPointerEvent->mTimeStamp = aTouchEvent->mTimeStamp;
|
|
aPointerEvent->mFlags = aTouchEvent->mFlags;
|
|
aPointerEvent->button = button;
|
|
aPointerEvent->buttons = buttons;
|
|
aPointerEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
|
|
}
|
|
|
|
/* static */ void
|
|
PointerEventHandler::DispatchPointerFromMouseOrTouch(
|
|
PresShell* aShell,
|
|
nsIFrame* aFrame,
|
|
nsIContent* aContent,
|
|
WidgetGUIEvent* aEvent,
|
|
bool aDontRetargetEvents,
|
|
nsEventStatus* aStatus,
|
|
nsIContent** aTargetContent)
|
|
{
|
|
MOZ_ASSERT(IsPointerEventEnabled());
|
|
MOZ_ASSERT(aFrame || aContent);
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
|
|
WidgetPointerEvent event(*mouseEvent);
|
|
InitPointerEventFromMouse(&event, mouseEvent, pointerMessage);
|
|
event.convertToPointer = mouseEvent->convertToPointer = false;
|
|
RefPtr<PresShell> shell(aShell);
|
|
if (!aFrame) {
|
|
shell = PresShell::GetShellForEventTarget(nullptr, aContent);
|
|
if (!shell) {
|
|
return;
|
|
}
|
|
}
|
|
PreHandlePointerEventsPreventDefault(&event, aEvent);
|
|
// Dispatch pointer event to the same target which is found by the
|
|
// corresponding mouse event.
|
|
shell->HandleEventWithTarget(&event, aFrame, aContent, aStatus, true,
|
|
aTargetContent);
|
|
PostHandlePointerEventsPreventDefault(&event, aEvent);
|
|
} else if (aEvent->mClass == eTouchEventClass) {
|
|
WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
|
|
// loop over all touches and dispatch pointer events on each touch
|
|
// copy the event
|
|
switch (touchEvent->mMessage) {
|
|
case eTouchMove:
|
|
pointerMessage = ePointerMove;
|
|
break;
|
|
case eTouchEnd:
|
|
pointerMessage = ePointerUp;
|
|
break;
|
|
case eTouchStart:
|
|
pointerMessage = ePointerDown;
|
|
break;
|
|
case eTouchCancel:
|
|
case eTouchPointerCancel:
|
|
pointerMessage = ePointerCancel;
|
|
break;
|
|
default:
|
|
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;
|
|
}
|
|
|
|
WidgetPointerEvent event(touchEvent->IsTrusted(), pointerMessage,
|
|
touchEvent->mWidget);
|
|
|
|
InitPointerEventFromTouch(&event, touchEvent, touch, i == 0);
|
|
event.convertToPointer = touch->convertToPointer = false;
|
|
if (aEvent->mMessage == eTouchStart) {
|
|
// We already did hit test for touchstart in PresShell. We should
|
|
// dispatch pointerdown to the same target as touchstart.
|
|
nsCOMPtr<nsIContent> content = do_QueryInterface(touch->mTarget);
|
|
if (!content) {
|
|
continue;
|
|
}
|
|
|
|
nsIFrame* frame = content->GetPrimaryFrame();
|
|
shell = PresShell::GetShellForEventTarget(frame, content);
|
|
if (!shell) {
|
|
continue;
|
|
}
|
|
|
|
PreHandlePointerEventsPreventDefault(&event, aEvent);
|
|
shell->HandleEventWithTarget(&event, frame, content, aStatus, true,
|
|
nullptr);
|
|
PostHandlePointerEventsPreventDefault(&event, aEvent);
|
|
} else {
|
|
// We didn't hit test for other touch events. Spec doesn't mention that
|
|
// all pointer events should be dispatched to the same target as their
|
|
// corresponding touch events. Call PresShell::HandleEvent so that we do
|
|
// hit test for pointer events.
|
|
PreHandlePointerEventsPreventDefault(&event, aEvent);
|
|
shell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus);
|
|
PostHandlePointerEventsPreventDefault(&event, aEvent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* static */ uint16_t
|
|
PointerEventHandler::GetPointerType(uint32_t aPointerId)
|
|
{
|
|
PointerInfo* pointerInfo = nullptr;
|
|
if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
|
|
return pointerInfo->mPointerType;
|
|
}
|
|
return nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN;
|
|
}
|
|
|
|
/* static */ bool
|
|
PointerEventHandler::GetPointerPrimaryState(uint32_t aPointerId)
|
|
{
|
|
PointerInfo* pointerInfo = nullptr;
|
|
if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
|
|
return pointerInfo->mPrimaryState;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* static */ void
|
|
PointerEventHandler::DispatchGotOrLostPointerCaptureEvent(
|
|
bool aIsGotCapture,
|
|
const WidgetPointerEvent* aPointerEvent,
|
|
nsIContent* aCaptureTarget)
|
|
{
|
|
nsIDocument* targetDoc = aCaptureTarget->OwnerDoc();
|
|
nsCOMPtr<nsIPresShell> shell = targetDoc->GetShell();
|
|
if (NS_WARN_IF(!shell)) {
|
|
return;
|
|
}
|
|
|
|
if (!aIsGotCapture && !aCaptureTarget->IsInUncomposedDoc()) {
|
|
// If the capturing element was removed from the DOM tree, fire
|
|
// ePointerLostCapture at the document.
|
|
PointerEventInit init;
|
|
init.mPointerId = aPointerEvent->pointerId;
|
|
init.mBubbles = true;
|
|
init.mComposed = true;
|
|
ConvertPointerTypeToString(aPointerEvent->inputSource, init.mPointerType);
|
|
init.mIsPrimary = aPointerEvent->mIsPrimary;
|
|
RefPtr<PointerEvent> event;
|
|
event = PointerEvent::Constructor(aCaptureTarget,
|
|
NS_LITERAL_STRING("lostpointercapture"),
|
|
init);
|
|
bool dummy;
|
|
targetDoc->DispatchEvent(event->InternalDOMEvent(), &dummy);
|
|
return;
|
|
}
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
|
WidgetPointerEvent localEvent(aPointerEvent->IsTrusted(),
|
|
aIsGotCapture ? ePointerGotCapture :
|
|
ePointerLostCapture,
|
|
aPointerEvent->mWidget);
|
|
|
|
localEvent.AssignPointerEventData(*aPointerEvent, true);
|
|
DebugOnly<nsresult> rv = shell->HandleEventWithTarget(
|
|
&localEvent,
|
|
aCaptureTarget->GetPrimaryFrame(),
|
|
aCaptureTarget, &status);
|
|
|
|
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
|
|
"DispatchGotOrLostPointerCaptureEvent failed");
|
|
}
|
|
|
|
} // namespace mozilla
|