A single tap or a long press may cause compatibility mouse events, `mousemove`, `mousedown`, `mouseup`, then, `click` unless the target is changed during the sequence. Currently, we synthesize a mouse move event after a layout change or a scroll to dispatch mouse boundary events and update the `:hover` state. However, if the last mouse event is caused by a touch which does not support `:hover` state, it's odd to treat the new target as hovered. This patch prevents the synthesized mouse moves if the last mouse event is caused by a pointing device which does not support hover state unless the button state is active (e.g., the layout change at `mousedown` of a single tap should cause mouse boundary events before the following `mouseup`). I think that the compatibility `mouseup` event should reset the `:hover` state and dispatch mouse boundary events like `pointerup`. However, Chrome does not do that. Therefore, we should align the behavior to Chrome for the compatibility. Differential Revision: https://phabricator.services.mozilla.com/D309800
702 lines
29 KiB
C++
702 lines
29 KiB
C++
/* 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/. */
|
|
|
|
#ifndef mozilla_PointerEventHandler_h
|
|
#define mozilla_PointerEventHandler_h
|
|
|
|
#include "LayoutConstants.h"
|
|
#include "mozilla/EventForwards.h"
|
|
#include "mozilla/Maybe.h"
|
|
#include "mozilla/MouseEvents.h"
|
|
#include "mozilla/StaticPtr.h"
|
|
#include "mozilla/TouchEvents.h"
|
|
#include "mozilla/WeakPtr.h"
|
|
|
|
// XXX Avoid including this here by moving function bodies to the cpp file
|
|
#include "mozilla/dom/Document.h"
|
|
#include "mozilla/dom/Element.h"
|
|
#include "mozilla/layers/InputAPZContext.h"
|
|
|
|
class AutoWeakFrame;
|
|
class nsIFrame;
|
|
class nsIContent;
|
|
class nsPresContext;
|
|
|
|
namespace mozilla {
|
|
|
|
class PresShell;
|
|
|
|
namespace dom {
|
|
class BrowserParent;
|
|
class Document;
|
|
class Element;
|
|
}; // namespace dom
|
|
|
|
class PointerCaptureInfo final {
|
|
public:
|
|
RefPtr<dom::Element> mPendingElement;
|
|
RefPtr<dom::Element> mOverrideElement;
|
|
|
|
explicit PointerCaptureInfo(dom::Element* aPendingElement)
|
|
: mPendingElement(aPendingElement) {
|
|
MOZ_COUNT_CTOR(PointerCaptureInfo);
|
|
}
|
|
|
|
MOZ_COUNTED_DTOR(PointerCaptureInfo)
|
|
|
|
bool Empty() { return !(mPendingElement || mOverrideElement); }
|
|
};
|
|
|
|
/**
|
|
* PointerInfo stores the pointer's information and its last state (position,
|
|
* buttons, etc).
|
|
*/
|
|
struct PointerInfo final {
|
|
using Document = dom::Document;
|
|
enum class Active : bool { No, Yes };
|
|
enum class Primary : bool { No, Yes };
|
|
enum class FromTouchEvent : bool { No, Yes };
|
|
enum class SynthesizeForTests : bool { No, Yes };
|
|
PointerInfo()
|
|
: mIsActive(false),
|
|
mIsPrimary(false),
|
|
mFromTouchEvent(false),
|
|
mPreventMouseEventByContent(false),
|
|
mIsSynthesizedForTests(false) {}
|
|
PointerInfo(const PointerInfo&) = default;
|
|
explicit PointerInfo(
|
|
Active aActiveState, uint16_t aInputSource, Primary aPrimaryState,
|
|
FromTouchEvent aFromTouchEvent, Document* aActiveDocument,
|
|
const PointerInfo* aLastPointerInfo = nullptr,
|
|
SynthesizeForTests aIsSynthesizedForTests = SynthesizeForTests::No)
|
|
: mActiveDocument(aActiveDocument),
|
|
mInputSource(aInputSource),
|
|
mIsActive(static_cast<bool>(aActiveState)),
|
|
mIsPrimary(static_cast<bool>(aPrimaryState)),
|
|
mFromTouchEvent(static_cast<bool>(aFromTouchEvent)),
|
|
mPreventMouseEventByContent(false),
|
|
mIsSynthesizedForTests(static_cast<bool>(aIsSynthesizedForTests)) {
|
|
if (aLastPointerInfo) {
|
|
TakeOverLastState(*aLastPointerInfo);
|
|
}
|
|
}
|
|
explicit PointerInfo(Active aActiveState,
|
|
const WidgetPointerEvent& aPointerEvent,
|
|
Document* aActiveDocument,
|
|
const PointerInfo* aLastPointerInfo = nullptr)
|
|
: mActiveDocument(aActiveDocument),
|
|
mInputSource(aPointerEvent.mInputSource),
|
|
mIsActive(static_cast<bool>(aActiveState)),
|
|
mIsPrimary(aPointerEvent.mIsPrimary),
|
|
mFromTouchEvent(aPointerEvent.mFromTouchEvent),
|
|
mPreventMouseEventByContent(false),
|
|
mIsSynthesizedForTests(aPointerEvent.mFlags.mIsSynthesizedForTests) {
|
|
if (aLastPointerInfo) {
|
|
TakeOverLastState(*aLastPointerInfo);
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] bool InputSourceSupportsHover() const {
|
|
return WidgetMouseEventBase::InputSourceSupportsHover(mInputSource);
|
|
}
|
|
|
|
[[nodiscard]] bool HasLastState() const {
|
|
return mLastRefPointInRootDoc !=
|
|
nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
|
|
}
|
|
|
|
/**
|
|
* Make this store the last pointer state such as the position, buttons, etc,
|
|
* which should be used at dispatching a synthetic mouse/pointer move.
|
|
*/
|
|
void RecordLastState(const nsPoint& aRefPointInRootDoc,
|
|
const WidgetMouseEvent& aMouseOrPointerEvent) {
|
|
MOZ_ASSERT_IF(aMouseOrPointerEvent.mMessage == eMouseMove ||
|
|
aMouseOrPointerEvent.mMessage == ePointerMove,
|
|
aMouseOrPointerEvent.IsReal());
|
|
|
|
mLastRefPointInRootDoc = aRefPointInRootDoc;
|
|
mLastTargetGuid = layers::InputAPZContext::GetTargetLayerGuid();
|
|
// FIXME: DragEvent may not be initialized with the proper state. So,
|
|
// ignore the details of drag events for now.
|
|
if (aMouseOrPointerEvent.mClass != eDragEventClass) {
|
|
mLastTiltX = aMouseOrPointerEvent.ComputeTiltX();
|
|
mLastTiltY = aMouseOrPointerEvent.ComputeTiltY();
|
|
mLastButtons = aMouseOrPointerEvent.mButtons;
|
|
mLastPressure = aMouseOrPointerEvent.mPressure;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Take over the last pointer state from older PointerInfo.
|
|
*/
|
|
void TakeOverLastState(const PointerInfo& aPointerInfo) {
|
|
mLastRefPointInRootDoc = aPointerInfo.mLastRefPointInRootDoc;
|
|
mLastTargetGuid = aPointerInfo.mLastTargetGuid;
|
|
mLastTiltX = aPointerInfo.mLastTiltX;
|
|
mLastTiltY = aPointerInfo.mLastTiltY;
|
|
mLastButtons = aPointerInfo.mLastButtons;
|
|
mLastPressure = aPointerInfo.mLastPressure;
|
|
}
|
|
|
|
/**
|
|
* Clear the last pointer state to stop dispatching synthesized mouse/pointer
|
|
* move at the position.
|
|
*/
|
|
void ClearLastState() {
|
|
mLastRefPointInRootDoc =
|
|
nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
|
|
mLastTargetGuid = layers::ScrollableLayerGuid();
|
|
mLastTiltX = 0;
|
|
mLastTiltY = 0;
|
|
mLastButtons = 0;
|
|
mLastPressure = 0.0f;
|
|
}
|
|
|
|
[[nodiscard]] bool EqualsBasicPointerData(const PointerInfo& aOther) const {
|
|
return mInputSource == aOther.mInputSource &&
|
|
mIsActive == aOther.mIsActive && mIsPrimary == aOther.mIsPrimary &&
|
|
mFromTouchEvent == aOther.mFromTouchEvent &&
|
|
mIsSynthesizedForTests == aOther.mIsSynthesizedForTests;
|
|
}
|
|
|
|
// mLastRefPointInRootDoc stores the event point relative to the root
|
|
// PresShell. So, it's different from the WidgetEvent::mRefPoint.
|
|
nsPoint mLastRefPointInRootDoc =
|
|
nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
|
|
layers::ScrollableLayerGuid mLastTargetGuid;
|
|
WeakPtr<Document> mActiveDocument;
|
|
// mInputSource indicates which input source caused the last event. E.g.,
|
|
// if the last event is a compatibility mouse event, the input source is
|
|
// "touch".
|
|
uint16_t mInputSource = 0;
|
|
int32_t mLastTiltX = 0;
|
|
int32_t mLastTiltY = 0;
|
|
int16_t mLastButtons = 0;
|
|
float mLastPressure = 0.0f;
|
|
bool mIsActive : 1;
|
|
bool mIsPrimary : 1;
|
|
// mFromTouchEvent is set to true if the last event is a touch event or a
|
|
// pointer event caused by a touch event. If the last event is a
|
|
// compatibility mouse event, this is set to false even though the input
|
|
// source is "touch".
|
|
bool mFromTouchEvent : 1;
|
|
bool mPreventMouseEventByContent : 1;
|
|
// Set to true if the pointer is activated only by synthesized mouse events.
|
|
bool mIsSynthesizedForTests : 1;
|
|
};
|
|
|
|
class PointerEventHandler final {
|
|
public:
|
|
// Called in nsLayoutStatics::Initialize/Shutdown to initialize pointer event
|
|
// related static variables.
|
|
static void InitializeStatics();
|
|
static void ReleaseStatics();
|
|
|
|
// Return the preference value of implicit capture.
|
|
static bool IsPointerEventImplicitCaptureForTouchEnabled();
|
|
|
|
/**
|
|
* Dispatch a pointer event on aTargetWeakFrame if and only if the frame is
|
|
* available or aTargetContent if the target content does not have a frame.
|
|
*
|
|
* This follows the steps of "fire a pointer event" definition.
|
|
* https://w3c.github.io/pointerevents/#dfn-fire-a-pointer-event
|
|
*
|
|
* @param aPointerEventMessage The event message which you want to dispatch a
|
|
* pointer event.
|
|
* @param aMouseOrPointerEvent The source event which caused the dispatching
|
|
* pointer event. Must be a mouse or pointer event.
|
|
* @param aTargetWeakFrame If target frame is available, this should be set to
|
|
* non-nullptr.
|
|
* @param aTargetContent If target frame is not available for the content
|
|
* node, set this to the proper target node.
|
|
* @param aStatus [optional, out] The dispatched event status.
|
|
*/
|
|
MOZ_CAN_RUN_SCRIPT static nsresult DispatchPointerEventWithTarget(
|
|
EventMessage aPointerEventMessage,
|
|
const WidgetMouseEvent& aMouseOrPointerEvent,
|
|
const AutoWeakFrame& aTargetWeakFrame, nsIContent* aTargetContent,
|
|
nsEventStatus* aStatus = nullptr);
|
|
|
|
/**
|
|
* Dispatch a pointer event on aTargetWeakFrame if and only if the frame is
|
|
* available or aTargetContent if the target content does not have a frame.
|
|
*
|
|
* This follows the steps of "fire a pointer event" definition.
|
|
* https://w3c.github.io/pointerevents/#dfn-fire-a-pointer-event
|
|
*
|
|
* @param aPointerEventMessage The event message which you want to dispatch a
|
|
* pointer event.
|
|
* @param aTouchEvent The source touch event which caused the dispatching
|
|
* pointer event.
|
|
* @param aTouchIndex The source touch index in aTouchEvent.
|
|
* @param aTargetWeakFrame If target frame is available, this should be set to
|
|
* non-nullptr.
|
|
* @param aTargetContent If target frame is not available for the content
|
|
* node, set this to the proper target node.
|
|
* @param aStatus [optional, in/out] The dispatched event status.
|
|
*/
|
|
MOZ_CAN_RUN_SCRIPT static nsresult DispatchPointerEventWithTarget(
|
|
EventMessage aPointerEventMessage, const WidgetTouchEvent& aTouchEvent,
|
|
size_t aTouchIndex, const AutoWeakFrame& aTargetWeakFrame,
|
|
nsIContent* aTargetContent, nsEventStatus* aStatus = nullptr);
|
|
|
|
/**
|
|
* Dispatch a pointer event on aTargetWeakFrame if and only if the frame is
|
|
* available or aTargetContent if the target content does not have a frame.
|
|
*
|
|
* This follows the steps of "fire a pointer event" definition.
|
|
* https://w3c.github.io/pointerevents/#dfn-fire-a-pointer-event
|
|
*
|
|
* @param aPointerEvent The pointer event which should be dispatched.
|
|
* @param aTouchIndex The source touch index in aTouchEvent.
|
|
* @param aTargetWeakFrame If target frame is available, this should be set to
|
|
* non-nullptr.
|
|
* @param aTargetContent If target frame is not available for the content
|
|
* node, set this to the proper target node.
|
|
* @param aStatus [optional, in/out] The dispatched event status.
|
|
*/
|
|
MOZ_CAN_RUN_SCRIPT static nsresult DispatchPointerEventWithTarget(
|
|
WidgetPointerEvent& aPointerEvent, const AutoWeakFrame& aTargetWeakFrame,
|
|
nsIContent* aTargetContent, nsEventStatus* aStatus = nullptr);
|
|
|
|
/**
|
|
* Return true if click/auxclick/contextmenu event should be fired on
|
|
* an element which was capturing the pointer at dispatching ePointerUp.
|
|
*
|
|
* @param aSourceEvent [Optional] The source event which causes the
|
|
* `click`, `auxclick` or `contextmenu` event. I.e.,
|
|
* must be one of `mouseup`, `pointerup` or `touchend`.
|
|
* If specifying nullptr, this method checks only
|
|
* whether the behavior is enabled.
|
|
*/
|
|
[[nodiscard]] static bool ShouldDispatchClickEventOnCapturingElement(
|
|
const WidgetGUIEvent* aSourceEvent = nullptr);
|
|
|
|
// Called in ESM::PreHandleEvent to update current active pointers in a hash
|
|
// table.
|
|
static void UpdatePointerActiveState(WidgetMouseEvent* aEvent,
|
|
nsIContent* aTargetContent = nullptr);
|
|
|
|
/**
|
|
* Called when PresShell starts handling a mouse or subclass event. This will
|
|
* set PointerInfo for synthesizing pointer move at the position later.
|
|
*
|
|
* @param aRefPointInRootPresShell The event location in the root
|
|
* PresShell.
|
|
* @param aMouseEvent The event which will be handled.
|
|
*/
|
|
static void RecordPointerState(const nsPoint& aRefPointInRootPresShell,
|
|
const WidgetMouseEvent& aMouseEvent);
|
|
|
|
/**
|
|
* Called when PresShell starts handling a mouse event. The data will be used
|
|
* for synthesizing eMouseMove to dispatch mouse boundary events and updates
|
|
* `:hover` state.
|
|
*
|
|
* @param aRootPresShell Must be the root PresShell of the PresShell
|
|
* which starts handling the event.
|
|
* @param aMouseEvent The mouse event which the PresShell starts
|
|
* handling.
|
|
*/
|
|
static void RecordMouseState(PresShell& aRootPresShell,
|
|
const WidgetMouseEvent& aMouseEvent);
|
|
|
|
/**
|
|
* Called when PresShell dispatches aMouseEvent into the DOM.
|
|
*/
|
|
static void WillDispatchMouseEventToDOM(const WidgetMouseEvent& aMouseEvent);
|
|
|
|
/**
|
|
* Called when PresShell dispatches a mouse event to the DOM.
|
|
*/
|
|
static void RecordMouseButtons(const WidgetMouseEvent& aMouseEvent) {
|
|
// Buttons of mouse should be shared even if there are multiple mouse
|
|
// pointers which has different pointerIds for the backward compatibility.
|
|
// Thus, here does not check sLastMousePresShell nor pointerId.
|
|
if (sLastMouseInfo) {
|
|
sLastMouseInfo->mLastButtons = aMouseEvent.mButtons;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called when PresShell starts handling a mouse event or something which
|
|
* should make aRootPresShell should never dispatch synthetic eMouseMove
|
|
* events.
|
|
*
|
|
* @param aRootPresShell Must be the root PresShell of the PresShell
|
|
* which starts handling the event.
|
|
* @param aMouseEvent The mouse event which the PresShell starts
|
|
* handling.
|
|
*/
|
|
static void ClearMouseState(PresShell& aRootPresShell,
|
|
const WidgetMouseEvent& aMouseEvent);
|
|
|
|
// Request/release pointer capture of the specified pointer by the element.
|
|
static void RequestPointerCaptureById(uint32_t aPointerId,
|
|
dom::Element* aElement);
|
|
static void ReleasePointerCaptureById(uint32_t aPointerId);
|
|
static void ReleaseAllPointerCapture();
|
|
|
|
// Set/release pointer capture of the specified pointer by the remote target.
|
|
// Should only be called in parent process.
|
|
static bool SetPointerCaptureRemoteTarget(uint32_t aPointerId,
|
|
dom::BrowserParent* aBrowserParent);
|
|
static void ReleasePointerCaptureRemoteTarget(
|
|
dom::BrowserParent* aBrowserParent);
|
|
static void ReleasePointerCaptureRemoteTarget(uint32_t aPointerId);
|
|
static void ReleaseAllPointerCaptureRemoteTarget();
|
|
|
|
// Get the pointer capturing remote target of the specified pointer.
|
|
static dom::BrowserParent* GetPointerCapturingRemoteTarget(
|
|
uint32_t aPointerId);
|
|
|
|
// Get the pointer captured info of the specified pointer.
|
|
static PointerCaptureInfo* GetPointerCaptureInfo(uint32_t aPointerId);
|
|
|
|
// Return the PointerInfo if the pointer with aPointerId is situated in
|
|
// device, nullptr otherwise.
|
|
// Note that the result may be activated only by synthesized events for test.
|
|
// If you don't want it, check PointerInfo::mIsSynthesizedForTests.
|
|
static const PointerInfo* GetPointerInfo(uint32_t aPointerId);
|
|
|
|
/**
|
|
* Return the PointeInfo which stores the last mouse event state which should
|
|
* be used for dispatching a synthetic eMouseMove.
|
|
*
|
|
* @param aRootPresShell [optional] If specified, return non-nullptr if
|
|
* and only if the last mouse info was set by
|
|
* aRootPresShell. Otherwise, return the last
|
|
* mouse info which was set by any PresShell.
|
|
*/
|
|
[[nodiscard]] static const PointerInfo* GetLastMouseInfo(
|
|
const PresShell* aRootPresShell = nullptr);
|
|
|
|
/**
|
|
* Return the last pointerId which has not left from any documents managed in
|
|
* this process.
|
|
*/
|
|
[[nodiscard]] static Maybe<uint32_t> GetLastPointerId() {
|
|
return sLastPointerId;
|
|
}
|
|
|
|
/**
|
|
* If the static last-mouse state was set by a PresShell that has since been
|
|
* torn down (typically due to a same-tab navigation), rebind ownership to
|
|
* aRootPresShell and return the recorded pointerId. The caller can then
|
|
* use this to seed its own per-PresShell tracking so a synthetic mouse
|
|
* move can be dispatched against the cached position (bug 2038491).
|
|
*
|
|
* Returns Nothing() if there is no usable state, or if another live
|
|
* PresShell still owns the state (e.g. a popup): in that case the
|
|
* caller should leave the state alone.
|
|
*/
|
|
[[nodiscard]] static Maybe<uint32_t> TryClaimOrphanedLastMouseInfo(
|
|
PresShell& aRootPresShell);
|
|
|
|
/**
|
|
* Retrun true if aPointerId is the last pointerId.
|
|
*/
|
|
[[nodiscard]] static bool IsLastPointerId(uint32_t aPointerId) {
|
|
return sLastPointerId && *sLastPointerId == aPointerId;
|
|
}
|
|
|
|
// CheckPointerCaptureState checks cases, when got/lostpointercapture events
|
|
// should be fired.
|
|
MOZ_CAN_RUN_SCRIPT
|
|
static void MaybeProcessPointerCapture(WidgetGUIEvent* aEvent);
|
|
MOZ_CAN_RUN_SCRIPT
|
|
static void ProcessPointerCaptureForMouse(WidgetMouseEvent* aEvent);
|
|
MOZ_CAN_RUN_SCRIPT
|
|
static void ProcessPointerCaptureForTouch(WidgetTouchEvent* aEvent);
|
|
MOZ_CAN_RUN_SCRIPT
|
|
static void CheckPointerCaptureState(WidgetPointerEvent* aEvent);
|
|
|
|
// Implicitly get and release capture of current pointer for touch.
|
|
static void ImplicitlyCapturePointer(nsIFrame* aFrame,
|
|
const WidgetEvent& aEvent);
|
|
MOZ_CAN_RUN_SCRIPT
|
|
static void ImplicitlyReleasePointerCapture(WidgetEvent* aEvent);
|
|
MOZ_CAN_RUN_SCRIPT static void MaybeImplicitlyReleasePointerCapture(
|
|
WidgetGUIEvent* aEvent);
|
|
|
|
/**
|
|
* GetPointerCapturingContent returns a target element which captures the
|
|
* pointer. It's applied to mouse or pointer event (except mousedown and
|
|
* pointerdown). When capturing, return the element. Otherwise, nullptr.
|
|
*
|
|
* @param aEvent A mouse event or pointer event which may be
|
|
* captured.
|
|
*
|
|
* @return Target element for aEvent.
|
|
*/
|
|
static dom::Element* GetPointerCapturingElement(const WidgetGUIEvent* aEvent);
|
|
|
|
static dom::Element* GetPointerCapturingElement(uint32_t aPointerId);
|
|
|
|
/**
|
|
* Return pending capture element of for the pointerId (of the event).
|
|
* - If the element has already overriden the pointer capture and there is no
|
|
* new pending capture element, the result is what captures the pointer right
|
|
* now.
|
|
* - If the element has not overriden the pointer capture, the result will
|
|
* start capturing the pointer once the pending pointer capture is processed
|
|
* at dispatching a pointer event later.
|
|
*
|
|
* So, in other words, the result is the element which will capture the next
|
|
* pointer event for the pointerId.
|
|
*/
|
|
static dom::Element* GetPendingPointerCapturingElement(
|
|
const WidgetGUIEvent* aEvent);
|
|
static dom::Element* GetPendingPointerCapturingElement(uint32_t aPointerId);
|
|
|
|
/**
|
|
* Return an element which captured the pointer at dispatching the last
|
|
* ePointerUp event caused by eMouseUp except the compatibility mouse events
|
|
* of Touch Events or caused by eTouchEnd whose number of touches is one,
|
|
* i.e., the last touch release.
|
|
*/
|
|
[[nodiscard]] static RefPtr<dom::Element>
|
|
GetPointerCapturingElementAtLastPointerUp();
|
|
|
|
/**
|
|
* Forget the pointer capturing element at dispatching the last ePointerUp.
|
|
*/
|
|
static void ReleasePointerCapturingElementAtLastPointerUp();
|
|
|
|
// 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);
|
|
|
|
/*
|
|
* This function handles the case when content had called preventDefault on
|
|
* the active pointer. In that case we have to prevent firing subsequent mouse
|
|
* to content. We check the flag PointerInfo::mPreventMouseEventByContent and
|
|
* call PreventDefault(false) to stop default behaviors and stop firing mouse
|
|
* events to content and chrome.
|
|
*
|
|
* note: mouse transition events are excluded
|
|
* note: we have to clean mPreventMouseEventByContent on pointerup for those
|
|
* devices support hover
|
|
* note: we don't suppress firing mouse events to chrome and system group
|
|
* handlers because they may implement default behaviors
|
|
*/
|
|
static void PreHandlePointerEventsPreventDefault(
|
|
WidgetPointerEvent* aPointerEvent, WidgetGUIEvent* aMouseOrTouchEvent);
|
|
|
|
/*
|
|
* This function handles the preventDefault behavior of pointerdown. When user
|
|
* preventDefault on pointerdown, We have to mark the active pointer to
|
|
* prevent sebsequent mouse events (except mouse transition events) and
|
|
* default behaviors.
|
|
*
|
|
* 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(
|
|
PresShell* aPresShell, WidgetPointerEvent* aPointerEvent,
|
|
WidgetGUIEvent* aMouseOrTouchEvent);
|
|
|
|
/**
|
|
* Dispatch a pointer event for aMouseOrTouchEvent to aEventTargetContent.
|
|
*
|
|
* @param aShell The PresShell which is handling the event.
|
|
* @param aEventTargetFrame The frame for aEventTargetContent.
|
|
* @param aEventTargetContent The event target node.
|
|
* @param aPointerCapturingElement
|
|
* The pointer capturing element.
|
|
* @param aMouseOrTouchEvent A mouse or touch event.
|
|
* @param aDontRetargetEvents If true, this won't dispatch event with
|
|
* different PresShell from aShell. Otherwise,
|
|
* pointer events may be fired on different
|
|
* document if and only if aMouseOrTOuchEvent is a
|
|
* touch event except eTouchStart.
|
|
* @param aState [out] The result of the pointer event.
|
|
* @param aMouseOrTouchEventTarget
|
|
* [out] The event target for the following mouse
|
|
* or touch event. If aEventTargetContent has not
|
|
* been removed from the tree, this is always set
|
|
* to it. If aEventTargetContent is removed from
|
|
* the tree and aMouseOrTouchEvent is a mouse
|
|
* event, this is set to inclusive ancestor of
|
|
* aEventTargetContent which is still connected.
|
|
* If aEventTargetContent is removed from the tree
|
|
* and aMouseOrTouchEvent is a touch event, this is
|
|
* set to aEventTargetContent because touch event
|
|
* should be dispatched even on disconnected node.
|
|
* FIXME: If the event is a touch event but the
|
|
* message is not eTouchStart, this won't be set.
|
|
*/
|
|
MOZ_CAN_RUN_SCRIPT static void DispatchPointerFromMouseOrTouch(
|
|
PresShell* aShell, nsIFrame* aEventTargetFrame,
|
|
nsIContent* aEventTargetContent, dom::Element* aPointerCapturingElement,
|
|
WidgetGUIEvent* aMouseOrTouchEvent, bool aDontRetargetEvents,
|
|
nsEventStatus* aStatus, nsIContent** aMouseOrTouchEventTarget = nullptr);
|
|
|
|
/**
|
|
* Synthesize eMouseMove or ePointerMove to dispatch mouse/pointer boundary
|
|
* events if they are required. This dispatches the event on the widget.
|
|
* Therefore, this dispatches the event on correct document in the same
|
|
* process. However, if there is a popup under the pointer or a document in a
|
|
* different process, this does not work as you expected.
|
|
*/
|
|
MOZ_CAN_RUN_SCRIPT static void SynthesizeMoveToDispatchBoundaryEvents(
|
|
const WidgetMouseEvent* aEvent);
|
|
|
|
static void InitPointerEventFromMouse(WidgetPointerEvent* aPointerEvent,
|
|
const WidgetMouseEvent* aMouseEvent,
|
|
EventMessage aMessage);
|
|
|
|
static void InitPointerEventFromTouch(WidgetPointerEvent& aPointerEvent,
|
|
const WidgetTouchEvent& aTouchEvent,
|
|
const mozilla::dom::Touch& aTouch);
|
|
|
|
static void InitCoalescedEventFromPointerEvent(
|
|
WidgetPointerEvent& aCoalescedEvent,
|
|
const WidgetPointerEvent& aSourceEvent);
|
|
|
|
static bool ShouldGeneratePointerEventFromMouse(WidgetGUIEvent* aEvent) {
|
|
return aEvent->mMessage == eMouseRawUpdate ||
|
|
aEvent->mMessage == eMouseDown || aEvent->mMessage == eMouseUp ||
|
|
(aEvent->mMessage == eMouseMove &&
|
|
aEvent->AsMouseEvent()->IsReal()) ||
|
|
aEvent->mMessage == eMouseExitFromWidget;
|
|
}
|
|
|
|
static bool ShouldGeneratePointerEventFromTouch(WidgetGUIEvent* aEvent) {
|
|
return aEvent->mMessage == eTouchRawUpdate ||
|
|
aEvent->mMessage == eTouchStart || aEvent->mMessage == eTouchMove ||
|
|
aEvent->mMessage == eTouchEnd || aEvent->mMessage == eTouchCancel ||
|
|
aEvent->mMessage == eTouchPointerCancel;
|
|
}
|
|
|
|
static MOZ_ALWAYS_INLINE int32_t GetSpoofedPointerIdForRFP() {
|
|
return sSpoofedPointerId.valueOr(0);
|
|
}
|
|
|
|
static void NotifyDestroyPresContext(nsPresContext* aPresContext);
|
|
|
|
static bool IsDragAndDropEnabled(WidgetMouseEvent& aEvent);
|
|
|
|
// Get proper pointer event message for a mouse or touch event.
|
|
[[nodiscard]] static EventMessage ToPointerEventMessage(
|
|
const WidgetGUIEvent* aMouseOrTouchEvent);
|
|
|
|
/**
|
|
* Return true if the window containing aDocument has had a
|
|
* `pointerrawupdate` event listener.
|
|
*/
|
|
[[nodiscard]] static bool NeedToDispatchPointerRawUpdate(
|
|
const dom::Document* aDocument);
|
|
|
|
/**
|
|
* Return a log module reference for logging the mouse location.
|
|
*/
|
|
[[nodiscard]] static LazyLogModule& MouseLocationLogRef();
|
|
|
|
/**
|
|
* Return a log module reference for logging the pointer location.
|
|
*/
|
|
[[nodiscard]] static LazyLogModule& PointerLocationLogRef();
|
|
|
|
private:
|
|
// Set pointer capture of the specified pointer by the element.
|
|
static void SetPointerCaptureById(uint32_t aPointerId,
|
|
dom::Element* aElement);
|
|
|
|
// GetPointerType returns pointer type like mouse, pen or touch for pointer
|
|
// event with pointerId. The return value must be one of
|
|
// MouseEvent_Binding::MOZ_SOURCE_*
|
|
static uint16_t GetPointerType(uint32_t aPointerId);
|
|
|
|
// GetPointerPrimaryState returns state of attribute isPrimary for pointer
|
|
// event with pointerId
|
|
static bool GetPointerPrimaryState(uint32_t aPointerId);
|
|
|
|
// HasActiveTouchPointer returns true if there is active pointer event that is
|
|
// generated from touch event.
|
|
static bool HasActiveTouchPointer();
|
|
|
|
MOZ_CAN_RUN_SCRIPT
|
|
static void DispatchGotOrLostPointerCaptureEvent(
|
|
bool aIsGotCapture, const WidgetPointerEvent* aPointerEvent,
|
|
dom::Element* aCaptureTarget);
|
|
|
|
enum class CapturingState { Pending, Override };
|
|
static dom::Element* GetPointerCapturingElementInternal(
|
|
CapturingState aCapturingState, const WidgetGUIEvent* aEvent);
|
|
|
|
// The cached spoofed pointer ID for fingerprinting resistance. We will use a
|
|
// mouse pointer id for desktop. For mobile, we should use the touch pointer
|
|
// id as the spoofed one, and this work will be addressed in Bug 1492775.
|
|
static Maybe<int32_t> sSpoofedPointerId;
|
|
|
|
// A helper function to cache the pointer id of the spoofed interface, we
|
|
// would only cache the pointer id once. After that, we would always stick to
|
|
// that pointer id for fingerprinting resistance.
|
|
static void MaybeCacheSpoofedPointerID(uint16_t aInputSource,
|
|
uint32_t aPointerId);
|
|
|
|
/**
|
|
* Store the pointer capturing element.
|
|
*/
|
|
static void SetPointerCapturingElementAtLastPointerUp(
|
|
nsWeakPtr&& aPointerCapturingElement);
|
|
|
|
/**
|
|
* Insert/update a pointer to/in sActivePointerIds.
|
|
*/
|
|
static const UniquePtr<PointerInfo>& InsertOrUpdateActivePointer(
|
|
uint32_t aPointerId, UniquePtr<PointerInfo>&& aNewPointerInfo,
|
|
EventMessage aEventMessage, const char* aCallerName);
|
|
|
|
/**
|
|
* Remove a pointer from sActivePointerIds.
|
|
*/
|
|
static void RemoveActivePointer(uint32_t aPointerId,
|
|
EventMessage aEventMessage,
|
|
const char* aCallerName);
|
|
|
|
/**
|
|
* Called when a new pointer event is bing fired.
|
|
*/
|
|
static void UpdateLastPointerId(uint32_t aPointerId,
|
|
EventMessage aEventMessage);
|
|
|
|
/**
|
|
* Called when a pointer is leaving from this process.
|
|
*/
|
|
static void MaybeForgetLastPointerId(uint32_t aPointerId,
|
|
EventMessage aEventMessage);
|
|
|
|
// Stores the last mouse info to dispatch synthetic eMouseMove in root
|
|
// PresShells.
|
|
static StaticAutoPtr<PointerInfo> sLastMouseInfo;
|
|
|
|
// Stores the last mouse info setter.
|
|
static StaticRefPtr<nsIWeakReference> sLastMousePresShell;
|
|
|
|
// Weak reference to the widget that received the event recorded in
|
|
// sLastMouseInfo. Used to gate the orphaned-state claim in
|
|
// TryClaimOrphanedLastMouseInfo to a PresShell on the same top-level window
|
|
// as the one that originally recorded the state.
|
|
static StaticRefPtr<nsIWeakReference> sLastMouseWidget;
|
|
|
|
// pointerId of the last real mouse event that updated sLastMouseInfo.
|
|
// Distinct from sLastPointerId, which tracks the last pointerId of any
|
|
// kind (including touch).
|
|
static Maybe<uint32_t> sLastMousePointerId;
|
|
|
|
// Stores the last pointerId which has not left from all documents managed in
|
|
// this process.
|
|
static Maybe<uint32_t> sLastPointerId;
|
|
};
|
|
|
|
} // namespace mozilla
|
|
|
|
#endif // mozilla_PointerEventHandler_h
|