/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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_MouseEvents_h__ #define mozilla_MouseEvents_h__ #include #include "mozilla/BasicEvents.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/dom/DataTransfer.h" #include "nsCOMPtr.h" namespace mozilla { namespace dom { class PBrowserParent; class PBrowserChild; } // namespace dom class WidgetPointerEvent; class WidgetPointerEventHolder final { public: nsTArray mEvents; NS_INLINE_DECL_REFCOUNTING(WidgetPointerEventHolder) private: virtual ~WidgetPointerEventHolder() {} }; /****************************************************************************** * mozilla::WidgetPointerHelper ******************************************************************************/ class WidgetPointerHelper { public: uint32_t pointerId; uint32_t tiltX; uint32_t tiltY; uint32_t twist; float tangentialPressure; bool convertToPointer; RefPtr mCoalescedWidgetEvents; WidgetPointerHelper() : pointerId(0), tiltX(0), tiltY(0), twist(0), tangentialPressure(0), convertToPointer(true) {} WidgetPointerHelper(uint32_t aPointerId, uint32_t aTiltX, uint32_t aTiltY, uint32_t aTwist = 0, float aTangentialPressure = 0) : pointerId(aPointerId), tiltX(aTiltX), tiltY(aTiltY), twist(aTwist), tangentialPressure(aTangentialPressure), convertToPointer(true) {} explicit WidgetPointerHelper(const WidgetPointerHelper& aHelper) : pointerId(aHelper.pointerId), tiltX(aHelper.tiltX), tiltY(aHelper.tiltY), twist(aHelper.twist), tangentialPressure(aHelper.tangentialPressure), convertToPointer(aHelper.convertToPointer), mCoalescedWidgetEvents(aHelper.mCoalescedWidgetEvents) {} void AssignPointerHelperData(const WidgetPointerHelper& aEvent, bool aCopyCoalescedEvents = false) { pointerId = aEvent.pointerId; tiltX = aEvent.tiltX; tiltY = aEvent.tiltY; twist = aEvent.twist; tangentialPressure = aEvent.tangentialPressure; convertToPointer = aEvent.convertToPointer; if (aCopyCoalescedEvents) { mCoalescedWidgetEvents = aEvent.mCoalescedWidgetEvents; } } }; /****************************************************************************** * mozilla::WidgetMouseEventBase ******************************************************************************/ class WidgetMouseEventBase : public WidgetInputEvent { private: friend class dom::PBrowserParent; friend class dom::PBrowserChild; protected: WidgetMouseEventBase() : mPressure(0), mButton(0), mButtons(0), mInputSource(/* MouseEvent_Binding::MOZ_SOURCE_MOUSE = */ 1), mHitCluster(false) {} // Including MouseEventBinding.h here leads to an include loop, so // we have to hardcode MouseEvent_Binding::MOZ_SOURCE_MOUSE. WidgetMouseEventBase(bool aIsTrusted, EventMessage aMessage, nsIWidget* aWidget, EventClassID aEventClassID) : WidgetInputEvent(aIsTrusted, aMessage, aWidget, aEventClassID), mPressure(0), mButton(0), mButtons(0), mInputSource(/* MouseEvent_Binding::MOZ_SOURCE_MOUSE = */ 1), mHitCluster(false) {} // Including MouseEventBinding.h here leads to an include loop, so // we have to hardcode MouseEvent_Binding::MOZ_SOURCE_MOUSE. public: virtual WidgetMouseEventBase* AsMouseEventBase() override { return this; } virtual WidgetEvent* Duplicate() const override { MOZ_CRASH("WidgetMouseEventBase must not be most-subclass"); } // ID of the canvas HitRegion nsString mRegion; // Finger or touch pressure of event. It ranges between 0.0 and 1.0. float mPressure; // Pressed button ID of mousedown or mouseup event. // This is set only when pressing a button causes the event. int16_t mButton; // Flags of all pressed buttons at the event fired. // This is set at any mouse event, don't be confused with |mButton|. int16_t mButtons; // Possible values a in MouseEvent uint16_t mInputSource; // Touch near a cluster of links (true) bool mHitCluster; bool IsLeftButtonPressed() const { return !!(mButtons & MouseButtonsFlag::eLeftFlag); } bool IsRightButtonPressed() const { return !!(mButtons & MouseButtonsFlag::eRightFlag); } bool IsMiddleButtonPressed() const { return !!(mButtons & MouseButtonsFlag::eMiddleFlag); } bool Is4thButtonPressed() const { return !!(mButtons & MouseButtonsFlag::e4thFlag); } bool Is5thButtonPressed() const { return !!(mButtons & MouseButtonsFlag::e5thFlag); } void AssignMouseEventBaseData(const WidgetMouseEventBase& aEvent, bool aCopyTargets) { AssignInputEventData(aEvent, aCopyTargets); mButton = aEvent.mButton; mButtons = aEvent.mButtons; mPressure = aEvent.mPressure; mHitCluster = aEvent.mHitCluster; mInputSource = aEvent.mInputSource; } /** * Returns true if left click event. */ bool IsLeftClickEvent() const { return mMessage == eMouseClick && mButton == MouseButton::eLeft; } }; /****************************************************************************** * mozilla::WidgetMouseEvent ******************************************************************************/ class WidgetMouseEvent : public WidgetMouseEventBase, public WidgetPointerHelper { private: friend class dom::PBrowserParent; friend class dom::PBrowserChild; public: typedef bool ReasonType; enum Reason : ReasonType { eReal, eSynthesized }; typedef bool ContextMenuTriggerType; enum ContextMenuTrigger : ContextMenuTriggerType { eNormal, eContextMenuKey }; typedef bool ExitFromType; enum ExitFrom : ExitFromType { eChild, eTopLevel }; protected: WidgetMouseEvent() : mReason(eReal), mContextMenuTrigger(eNormal), mExitFrom(eChild), mIgnoreRootScrollFrame(false), mClickCount(0), mUseLegacyNonPrimaryDispatch(false) {} WidgetMouseEvent(bool aIsTrusted, EventMessage aMessage, nsIWidget* aWidget, EventClassID aEventClassID, Reason aReason) : WidgetMouseEventBase(aIsTrusted, aMessage, aWidget, aEventClassID), mReason(aReason), mContextMenuTrigger(eNormal), mExitFrom(eChild), mIgnoreRootScrollFrame(false), mClickCount(0), mUseLegacyNonPrimaryDispatch(false) {} public: virtual WidgetMouseEvent* AsMouseEvent() override { return this; } WidgetMouseEvent(bool aIsTrusted, EventMessage aMessage, nsIWidget* aWidget, Reason aReason, ContextMenuTrigger aContextMenuTrigger = eNormal) : WidgetMouseEventBase(aIsTrusted, aMessage, aWidget, eMouseEventClass), mReason(aReason), mContextMenuTrigger(aContextMenuTrigger), mExitFrom(eChild), mIgnoreRootScrollFrame(false), mClickCount(0), mUseLegacyNonPrimaryDispatch(false) { if (aMessage == eContextMenu) { mButton = (mContextMenuTrigger == eNormal) ? MouseButton::eRight : MouseButton::eLeft; } } #ifdef DEBUG virtual ~WidgetMouseEvent() { NS_WARNING_ASSERTION( mMessage != eContextMenu || mButton == ((mContextMenuTrigger == eNormal) ? MouseButton::eRight : MouseButton::eLeft), "Wrong button set to eContextMenu event?"); } #endif virtual WidgetEvent* Duplicate() const override { MOZ_ASSERT(mClass == eMouseEventClass, "Duplicate() must be overridden by sub class"); // Not copying widget, it is a weak reference. WidgetMouseEvent* result = new WidgetMouseEvent( false, mMessage, nullptr, mReason, mContextMenuTrigger); result->AssignMouseEventData(*this, true); result->mFlags = mFlags; return result; } // If during mouseup handling we detect that click event might need to be // dispatched, this is setup to be the target of the click event. nsCOMPtr mClickTarget; // mReason indicates the reason why the event is fired: // - Representing mouse operation. // - Synthesized for emulating mousemove event when the content under the // mouse cursor is scrolled. Reason mReason; // mContextMenuTrigger is valid only when mMessage is eContextMenu. // This indicates if the context menu event is caused by context menu key or // other reasons (typically, a click of right mouse button). ContextMenuTrigger mContextMenuTrigger; // mExitFrom is valid only when mMessage is eMouseExitFromWidget. // This indicates if the mouse cursor exits from a top level widget or // a child widget. ExitFrom mExitFrom; // Whether the event should ignore scroll frame bounds during dispatch. bool mIgnoreRootScrollFrame; // mClickCount may be non-zero value when mMessage is eMouseDown, eMouseUp, // eMouseClick or eMouseDoubleClick. The number is count of mouse clicks. // Otherwise, this must be 0. uint32_t mClickCount; // Indicates whether the event should dispatch click events for non-primary // mouse buttons on window and document. bool mUseLegacyNonPrimaryDispatch; void AssignMouseEventData(const WidgetMouseEvent& aEvent, bool aCopyTargets) { AssignMouseEventBaseData(aEvent, aCopyTargets); AssignPointerHelperData(aEvent, /* aCopyCoalescedEvents */ true); mIgnoreRootScrollFrame = aEvent.mIgnoreRootScrollFrame; mClickCount = aEvent.mClickCount; mUseLegacyNonPrimaryDispatch = aEvent.mUseLegacyNonPrimaryDispatch; } /** * Returns true if the event is a context menu event caused by key. */ bool IsContextMenuKeyEvent() const { return mMessage == eContextMenu && mContextMenuTrigger == eContextMenuKey; } /** * Returns true if the event is a real mouse event. Otherwise, i.e., it's * a synthesized event by scroll or something, returns false. */ bool IsReal() const { return mReason == eReal; } /** * Returns true if middle click paste is enabled. */ static bool IsMiddleClickPasteEnabled(); }; /****************************************************************************** * mozilla::WidgetDragEvent ******************************************************************************/ class WidgetDragEvent : public WidgetMouseEvent { private: friend class mozilla::dom::PBrowserParent; friend class mozilla::dom::PBrowserChild; protected: WidgetDragEvent() : mUserCancelled(false), mDefaultPreventedOnContent(false) {} public: virtual WidgetDragEvent* AsDragEvent() override { return this; } WidgetDragEvent(bool aIsTrusted, EventMessage aMessage, nsIWidget* aWidget) : WidgetMouseEvent(aIsTrusted, aMessage, aWidget, eDragEventClass, eReal), mUserCancelled(false), mDefaultPreventedOnContent(false) {} virtual WidgetEvent* Duplicate() const override { MOZ_ASSERT(mClass == eDragEventClass, "Duplicate() must be overridden by sub class"); // Not copying widget, it is a weak reference. WidgetDragEvent* result = new WidgetDragEvent(false, mMessage, nullptr); result->AssignDragEventData(*this, true); result->mFlags = mFlags; return result; } // The dragging data. nsCOMPtr mDataTransfer; // If this is true, user has cancelled the drag operation. bool mUserCancelled; // If this is true, the drag event's preventDefault() is called on content. bool mDefaultPreventedOnContent; // XXX Not tested by test_assign_event_data.html void AssignDragEventData(const WidgetDragEvent& aEvent, bool aCopyTargets) { AssignMouseEventData(aEvent, aCopyTargets); mDataTransfer = aEvent.mDataTransfer; // XXX mUserCancelled isn't copied, is this intentionally? mUserCancelled = false; mDefaultPreventedOnContent = aEvent.mDefaultPreventedOnContent; } }; /****************************************************************************** * mozilla::WidgetMouseScrollEvent * * This is used for legacy DOM mouse scroll events, i.e., * DOMMouseScroll and MozMousePixelScroll event. These events are NOT hanbled * by ESM even if widget dispatches them. Use new WidgetWheelEvent instead. ******************************************************************************/ class WidgetMouseScrollEvent : public WidgetMouseEventBase { private: WidgetMouseScrollEvent() : mDelta(0), mIsHorizontal(false) {} public: virtual WidgetMouseScrollEvent* AsMouseScrollEvent() override { return this; } WidgetMouseScrollEvent(bool aIsTrusted, EventMessage aMessage, nsIWidget* aWidget) : WidgetMouseEventBase(aIsTrusted, aMessage, aWidget, eMouseScrollEventClass), mDelta(0), mIsHorizontal(false) {} virtual WidgetEvent* Duplicate() const override { MOZ_ASSERT(mClass == eMouseScrollEventClass, "Duplicate() must be overridden by sub class"); // Not copying widget, it is a weak reference. WidgetMouseScrollEvent* result = new WidgetMouseScrollEvent(false, mMessage, nullptr); result->AssignMouseScrollEventData(*this, true); result->mFlags = mFlags; return result; } // The delta value of mouse scroll event. // If the event message is eLegacyMouseLineOrPageScroll, the value indicates // scroll amount in lines. However, if the value is // UIEvent::SCROLL_PAGE_UP or UIEvent::SCROLL_PAGE_DOWN, the // value inducates one page scroll. If the event message is // eLegacyMousePixelScroll, the value indicates scroll amount in pixels. int32_t mDelta; // If this is true, it may cause to scroll horizontally. // Otherwise, vertically. bool mIsHorizontal; void AssignMouseScrollEventData(const WidgetMouseScrollEvent& aEvent, bool aCopyTargets) { AssignMouseEventBaseData(aEvent, aCopyTargets); mDelta = aEvent.mDelta; mIsHorizontal = aEvent.mIsHorizontal; } }; /****************************************************************************** * mozilla::WidgetWheelEvent ******************************************************************************/ class WidgetWheelEvent : public WidgetMouseEventBase { private: friend class mozilla::dom::PBrowserParent; friend class mozilla::dom::PBrowserChild; WidgetWheelEvent() : mDeltaX(0.0), mDeltaY(0.0), mDeltaZ(0.0), mOverflowDeltaX(0.0), mOverflowDeltaY(0.0) // Including WheelEventBinding.h here leads to an include loop, so // we have to hardcode WheelEvent_Binding::DOM_DELTA_PIXEL. , mDeltaMode(/* WheelEvent_Binding::DOM_DELTA_PIXEL = */ 0), mLineOrPageDeltaX(0), mLineOrPageDeltaY(0), mScrollType(SCROLL_DEFAULT), mCustomizedByUserPrefs(false), mMayHaveMomentum(false), mIsMomentum(false), mIsNoLineOrPageDelta(false), mViewPortIsOverscrolled(false), mCanTriggerSwipe(false), mAllowToOverrideSystemScrollSpeed(false), mDeltaValuesHorizontalizedForDefaultHandler(false) {} public: virtual WidgetWheelEvent* AsWheelEvent() override { return this; } WidgetWheelEvent(bool aIsTrusted, EventMessage aMessage, nsIWidget* aWidget) : WidgetMouseEventBase(aIsTrusted, aMessage, aWidget, eWheelEventClass), mDeltaX(0.0), mDeltaY(0.0), mDeltaZ(0.0), mOverflowDeltaX(0.0), mOverflowDeltaY(0.0) // Including WheelEventBinding.h here leads to an include loop, so // we have to hardcode WheelEvent_Binding::DOM_DELTA_PIXEL. , mDeltaMode(/* WheelEvent_Binding::DOM_DELTA_PIXEL = */ 0), mLineOrPageDeltaX(0), mLineOrPageDeltaY(0), mScrollType(SCROLL_DEFAULT), mCustomizedByUserPrefs(false), mMayHaveMomentum(false), mIsMomentum(false), mIsNoLineOrPageDelta(false), mViewPortIsOverscrolled(false), mCanTriggerSwipe(false), mAllowToOverrideSystemScrollSpeed(true), mDeltaValuesHorizontalizedForDefaultHandler(false) {} virtual WidgetEvent* Duplicate() const override { MOZ_ASSERT(mClass == eWheelEventClass, "Duplicate() must be overridden by sub class"); // Not copying widget, it is a weak reference. WidgetWheelEvent* result = new WidgetWheelEvent(false, mMessage, nullptr); result->AssignWheelEventData(*this, true); result->mFlags = mFlags; return result; } // On OS X, scroll gestures that start at the edge of the scrollable range // can result in a swipe gesture. For the first wheel event of such a // gesture, call TriggersSwipe() after the event has been processed // in order to find out whether a swipe should be started. bool TriggersSwipe() const { return mCanTriggerSwipe && mViewPortIsOverscrolled && this->mOverflowDeltaX != 0.0; } // NOTE: mDeltaX, mDeltaY and mDeltaZ may be customized by // mousewheel.*.delta_multiplier_* prefs which are applied by // EventStateManager. So, after widget dispatches this event, // these delta values may have different values than before. double mDeltaX; double mDeltaY; double mDeltaZ; // overflowed delta values for scroll, these values are set by // EventStateManger. If the default action of the wheel event isn't scroll, // these values are always zero. Otherwise, remaining delta values which are // not used by scroll are set. // NOTE: mDeltaX, mDeltaY and mDeltaZ may be modified by EventStateManager. // However, mOverflowDeltaX and mOverflowDeltaY indicate unused original // delta values which are not applied the delta_multiplier prefs. // So, if widget wanted to know the actual direction to be scrolled, // it would need to check the mDeltaX and mDeltaY. double mOverflowDeltaX; double mOverflowDeltaY; // Should be one of WheelEvent_Binding::DOM_DELTA_* uint32_t mDeltaMode; // If widget sets mLineOrPageDelta, EventStateManager will dispatch // eLegacyMouseLineOrPageScroll event for compatibility. Note that the delta // value means pages if the mDeltaMode is DOM_DELTA_PAGE, otherwise, lines. int32_t mLineOrPageDeltaX; int32_t mLineOrPageDeltaY; // When the default action for an wheel event is moving history or zooming, // need to chose a delta value for doing it. int32_t GetPreferredIntDelta() { if (!mLineOrPageDeltaX && !mLineOrPageDeltaY) { return 0; } if (mLineOrPageDeltaY && !mLineOrPageDeltaX) { return mLineOrPageDeltaY; } if (mLineOrPageDeltaX && !mLineOrPageDeltaY) { return mLineOrPageDeltaX; } if ((mLineOrPageDeltaX < 0 && mLineOrPageDeltaY > 0) || (mLineOrPageDeltaX > 0 && mLineOrPageDeltaY < 0)) { return 0; // We cannot guess the answer in this case. } return (Abs(mLineOrPageDeltaX) > Abs(mLineOrPageDeltaY)) ? mLineOrPageDeltaX : mLineOrPageDeltaY; } // Scroll type // The default value is SCROLL_DEFAULT, which means EventStateManager will // select preferred scroll type automatically. enum ScrollType : uint8_t { SCROLL_DEFAULT, SCROLL_SYNCHRONOUSLY, SCROLL_ASYNCHRONOUSELY, SCROLL_SMOOTHLY }; ScrollType mScrollType; // If the delta values are computed from prefs, this value is true. // Otherwise, i.e., they are computed from native events, false. bool mCustomizedByUserPrefs; // true if the momentum events directly tied to this event may follow it. bool mMayHaveMomentum; // true if the event is caused by momentum. bool mIsMomentum; // If device event handlers don't know when they should set mLineOrPageDeltaX // and mLineOrPageDeltaY, this is true. Otherwise, false. // If mIsNoLineOrPageDelta is true, ESM will generate // eLegacyMouseLineOrPageScroll events when accumulated delta values reach // a line height. bool mIsNoLineOrPageDelta; // Whether or not the parent of the currently overscrolled frame is the // ViewPort. This is false in situations when an element on the page is being // overscrolled (such as a text field), but true when the 'page' is being // overscrolled. bool mViewPortIsOverscrolled; // The wheel event can trigger a swipe to start if it's overscrolling the // viewport. bool mCanTriggerSwipe; // If mAllowToOverrideSystemScrollSpeed is true, the scroll speed may be // overridden. Otherwise, the scroll speed won't be overridden even if // it's enabled by the pref. bool mAllowToOverrideSystemScrollSpeed; // After the event's default action handler has adjusted its delta's values // for horizontalizing a vertical wheel scroll, this variable will be set to // true. bool mDeltaValuesHorizontalizedForDefaultHandler; void AssignWheelEventData(const WidgetWheelEvent& aEvent, bool aCopyTargets) { AssignMouseEventBaseData(aEvent, aCopyTargets); mDeltaX = aEvent.mDeltaX; mDeltaY = aEvent.mDeltaY; mDeltaZ = aEvent.mDeltaZ; mDeltaMode = aEvent.mDeltaMode; mCustomizedByUserPrefs = aEvent.mCustomizedByUserPrefs; mMayHaveMomentum = aEvent.mMayHaveMomentum; mIsMomentum = aEvent.mIsMomentum; mIsNoLineOrPageDelta = aEvent.mIsNoLineOrPageDelta; mLineOrPageDeltaX = aEvent.mLineOrPageDeltaX; mLineOrPageDeltaY = aEvent.mLineOrPageDeltaY; mScrollType = aEvent.mScrollType; mOverflowDeltaX = aEvent.mOverflowDeltaX; mOverflowDeltaY = aEvent.mOverflowDeltaY; mViewPortIsOverscrolled = aEvent.mViewPortIsOverscrolled; mCanTriggerSwipe = aEvent.mCanTriggerSwipe; mAllowToOverrideSystemScrollSpeed = aEvent.mAllowToOverrideSystemScrollSpeed; mDeltaValuesHorizontalizedForDefaultHandler = aEvent.mDeltaValuesHorizontalizedForDefaultHandler; } // System scroll speed settings may be too slow at using Gecko. In such // case, we should override the scroll speed computed with system settings. // Following methods return preferred delta values which are multiplied by // factors specified by prefs. If system scroll speed shouldn't be // overridden (e.g., this feature is disabled by pref), they return raw // delta values. double OverriddenDeltaX() const; double OverriddenDeltaY() const; // Compute the overridden delta value. This may be useful for suppressing // too fast scroll by system scroll speed overriding when widget sets // mAllowToOverrideSystemScrollSpeed. static double ComputeOverriddenDelta(double aDelta, bool aIsForVertical); private: static bool sInitialized; static bool sIsSystemScrollSpeedOverrideEnabled; static int32_t sOverrideFactorX; static int32_t sOverrideFactorY; static void Initialize(); }; /****************************************************************************** * mozilla::WidgetPointerEvent ******************************************************************************/ class WidgetPointerEvent : public WidgetMouseEvent { friend class mozilla::dom::PBrowserParent; friend class mozilla::dom::PBrowserChild; WidgetPointerEvent() : mWidth(1), mHeight(1), mIsPrimary(true) {} public: virtual WidgetPointerEvent* AsPointerEvent() override { return this; } WidgetPointerEvent(bool aIsTrusted, EventMessage aMsg, nsIWidget* w) : WidgetMouseEvent(aIsTrusted, aMsg, w, ePointerEventClass, eReal), mWidth(1), mHeight(1), mIsPrimary(true) {} explicit WidgetPointerEvent(const WidgetMouseEvent& aEvent) : WidgetMouseEvent(aEvent), mWidth(1), mHeight(1), mIsPrimary(true) { mClass = ePointerEventClass; } virtual WidgetEvent* Duplicate() const override { MOZ_ASSERT(mClass == ePointerEventClass, "Duplicate() must be overridden by sub class"); // Not copying widget, it is a weak reference. WidgetPointerEvent* result = new WidgetPointerEvent(false, mMessage, nullptr); result->AssignPointerEventData(*this, true); result->mFlags = mFlags; return result; } uint32_t mWidth; uint32_t mHeight; bool mIsPrimary; // XXX Not tested by test_assign_event_data.html void AssignPointerEventData(const WidgetPointerEvent& aEvent, bool aCopyTargets) { AssignMouseEventData(aEvent, aCopyTargets); mWidth = aEvent.mWidth; mHeight = aEvent.mHeight; mIsPrimary = aEvent.mIsPrimary; } }; } // namespace mozilla #endif // mozilla_MouseEvents_h__