/* -*- 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_BasicEvents_h__ #define mozilla_BasicEvents_h__ #include <stdint.h> #include <type_traits> #include "mozilla/EventForwards.h" #include "mozilla/TimeStamp.h" #include "mozilla/dom/EventTarget.h" #include "mozilla/layers/LayersTypes.h" #include "nsCOMPtr.h" #include "nsAtom.h" #include "nsISupportsImpl.h" #include "nsIWidget.h" #include "nsString.h" #include "Units.h" #ifdef DEBUG # include "nsXULAppAPI.h" #endif // #ifdef DEBUG class nsIPrincipal; namespace IPC { template <typename T> struct ParamTraits; } // namespace IPC namespace mozilla { class EventTargetChainItem; enum class CrossProcessForwarding { // eStop prevents the event to be sent to remote process. eStop, // eAllow keeps current state of the event whether it's sent to remote // process. In other words, eAllow does NOT mean that making the event // sent to remote process when IsCrossProcessForwardingStopped() returns // true. eAllow, }; /****************************************************************************** * mozilla::BaseEventFlags * * BaseEventFlags must be a POD struct for safe to use memcpy (including * in ParamTraits<BaseEventFlags>). So don't make virtual methods, constructor, * destructor and operators. * This is necessary for VC which is NOT C++0x compiler. ******************************************************************************/ struct BaseEventFlags { public: // If mIsTrusted is true, the event is a trusted event. Otherwise, it's // an untrusted event. bool mIsTrusted : 1; // If mInBubblingPhase is true, the event is in bubbling phase or target // phase. bool mInBubblingPhase : 1; // If mInCapturePhase is true, the event is in capture phase or target phase. bool mInCapturePhase : 1; // If mInTargetPhase is true, the event is in target phase. bool mInTargetPhase : 1; // If mInSystemGroup is true, the event is being dispatched in system group. bool mInSystemGroup : 1; // If mCancelable is true, the event can be consumed. I.e., calling // dom::Event::PreventDefault() can prevent the default action. bool mCancelable : 1; // If mBubbles is true, the event can bubble. Otherwise, cannot be handled // in bubbling phase. bool mBubbles : 1; // If mPropagationStopped is true, dom::Event::StopPropagation() or // dom::Event::StopImmediatePropagation() has been called. bool mPropagationStopped : 1; // If mImmediatePropagationStopped is true, // dom::Event::StopImmediatePropagation() has been called. // Note that mPropagationStopped must be true when this is true. bool mImmediatePropagationStopped : 1; // If mDefaultPrevented is true, the event has been consumed. // E.g., dom::Event::PreventDefault() has been called or // the default action has been performed. bool mDefaultPrevented : 1; // If mDefaultPreventedByContent is true, the event has been // consumed by content. // Note that mDefaultPrevented must be true when this is true. bool mDefaultPreventedByContent : 1; // If mDefaultPreventedByChrome is true, the event has been // consumed by chrome. // Note that mDefaultPrevented must be true when this is true. bool mDefaultPreventedByChrome : 1; // mMultipleActionsPrevented may be used when default handling don't want to // be prevented, but only one of the event targets should handle the event. // For example, when a <label> element is in another <label> element and // the first <label> element is clicked, that one may set this true. // Then, the second <label> element won't handle the event. bool mMultipleActionsPrevented : 1; // If mIsBeingDispatched is true, the DOM event created from the event is // dispatching into the DOM tree and not completed. bool mIsBeingDispatched : 1; // If mDispatchedAtLeastOnce is true, the event has been dispatched // as a DOM event and the dispatch has been completed in the process. // So, this is false even if the event has already been dispatched // in another process. bool mDispatchedAtLeastOnce : 1; // If mIsSynthesizedForTests is true, the event has been synthesized for // automated tests or something hacky approach of an add-on. bool mIsSynthesizedForTests : 1; // If mExceptionWasRaised is true, one of the event handlers has raised an // exception. bool mExceptionWasRaised : 1; // If mRetargetToNonNativeAnonymous is true and the target is in a non-native // native anonymous subtree, the event target is set to mOriginalTarget. bool mRetargetToNonNativeAnonymous : 1; // If mNoContentDispatch is true, the event is never dispatched to the // event handlers which are added to the contents, onfoo attributes and // properties. Note that this flag is ignored when // EventChainPreVisitor::mForceContentDispatch is set true. For exapmle, // window and document object sets it true. Therefore, web applications // can handle the event if they add event listeners to the window or the // document. // XXX This is an ancient and broken feature, don't use this for new bug // as far as possible. bool mNoContentDispatch : 1; // If mOnlyChromeDispatch is true, the event is dispatched to only chrome. bool mOnlyChromeDispatch : 1; // Indicates if the key combination is reserved by chrome. This is set by // MarkAsReservedByChrome(). bool mIsReservedByChrome : 1; // If mOnlySystemGroupDispatchInContent is true, event listeners added to // the default group for non-chrome EventTarget won't be called. // Be aware, if this is true, EventDispatcher needs to check if each event // listener is added to chrome node, so, don't set this to true for the // events which are fired a lot of times like eMouseMove. bool mOnlySystemGroupDispatchInContent : 1; // If mOnlySystemGroupDispatch is true, the event will be dispatched only to // event listeners added in the system group. bool mOnlySystemGroupDispatch : 1; // The event's action will be handled by APZ. The main thread should not // perform its associated action. bool mHandledByAPZ : 1; // True if the event is currently being handled by an event listener that // was registered as a passive listener. bool mInPassiveListener : 1; // If mComposed is true, the event fired by nodes in shadow DOM can cross the // boundary of shadow DOM and light DOM. bool mComposed : 1; // Similar to mComposed. Set it to true to allow events cross the boundary // between native non-anonymous content and native anonymouse content bool mComposedInNativeAnonymousContent : 1; // Set to true for events which are suppressed or delayed so that later a // DelayedEvent of it is dispatched. This is used when parent side process // the key event after content side, and may drop the event if the event // was suppressed or delayed in contents side. // It is also set to true for the events (in a DelayedInputEvent), which will // be dispatched afterwards. bool mIsSuppressedOrDelayed : 1; // Certain mouse events can be marked as positionless to return 0 from // coordinate related getters. bool mIsPositionless : 1; // Flags managing state of propagation between processes. // Note the the following flags shouldn't be referred directly. Use utility // methods instead. // If mNoRemoteProcessDispatch is true, the event is not allowed to be sent // to remote process. bool mNoRemoteProcessDispatch : 1; // If mWantReplyFromContentProcess is true, the event will be redispatched // in the parent process after the content process has handled it. Useful // for when the parent process need the know first how the event was used // by content before handling it itself. bool mWantReplyFromContentProcess : 1; // If mPostedToRemoteProcess is true, the event has been posted to the // remote process (but it's not handled yet if it's not a duplicated event // instance). bool mPostedToRemoteProcess : 1; // If mCameFromAnotherProcess is true, the event came from another process. bool mCameFromAnotherProcess : 1; /** * Helper methods for methods of DOM Event. */ inline void StopPropagation() { mPropagationStopped = true; } inline void StopImmediatePropagation() { StopPropagation(); mImmediatePropagationStopped = true; } inline void PreventDefault(bool aCalledByDefaultHandler = true) { if (!mCancelable) { return; } mDefaultPrevented = true; // Note that even if preventDefault() has already been called by chrome, // a call of preventDefault() by content needs to overwrite // mDefaultPreventedByContent to true because in such case, defaultPrevented // must be true when web apps check it after they call preventDefault(). if (aCalledByDefaultHandler) { StopCrossProcessForwarding(); mDefaultPreventedByChrome = true; } else { mDefaultPreventedByContent = true; } } // This should be used only before dispatching events into the DOM tree. inline void PreventDefaultBeforeDispatch( CrossProcessForwarding aCrossProcessForwarding) { if (!mCancelable) { return; } mDefaultPrevented = true; if (aCrossProcessForwarding == CrossProcessForwarding::eStop) { StopCrossProcessForwarding(); } } inline bool DefaultPrevented() const { return mDefaultPrevented; } inline bool DefaultPreventedByContent() const { MOZ_ASSERT(!mDefaultPreventedByContent || DefaultPrevented()); return mDefaultPreventedByContent; } inline bool IsTrusted() const { return mIsTrusted; } inline bool PropagationStopped() const { return mPropagationStopped; } // Helper methods to access flags managing state of propagation between // processes. /** * Prevent to be dispatched to remote process. */ inline void StopCrossProcessForwarding() { MOZ_ASSERT(!mPostedToRemoteProcess); mNoRemoteProcessDispatch = true; mWantReplyFromContentProcess = false; } /** * Return true if the event shouldn't be dispatched to remote process. */ inline bool IsCrossProcessForwardingStopped() const { return mNoRemoteProcessDispatch; } /** * Mark the event as waiting reply from remote process. * If the caller needs to win other keyboard event handlers in chrome, * the caller should call StopPropagation() too. * Otherwise, if the caller just needs to know if the event is consumed by * either content or chrome, it should just call this because the event * may be reserved by chrome and it needs to be dispatched into the DOM * tree in chrome for checking if it's reserved before being sent to any * remote processes. */ inline void MarkAsWaitingReplyFromRemoteProcess() { MOZ_ASSERT(!mPostedToRemoteProcess); mNoRemoteProcessDispatch = false; mWantReplyFromContentProcess = true; } /** * Reset "waiting reply from remote process" state. This is useful when * you dispatch a copy of an event coming from different process. */ inline void ResetWaitingReplyFromRemoteProcessState() { if (IsWaitingReplyFromRemoteProcess()) { // FYI: mWantReplyFromContentProcess is also used for indicating // "handled in remote process" state. Therefore, only when // IsWaitingReplyFromRemoteProcess() returns true, this should // reset the flag. mWantReplyFromContentProcess = false; } } /** * Return true if the event handler should wait reply event. I.e., if this * returns true, any event handler should do nothing with the event. */ inline bool IsWaitingReplyFromRemoteProcess() const { return !mNoRemoteProcessDispatch && mWantReplyFromContentProcess; } /** * Mark the event as already handled in the remote process. This should be * called when initializing reply events. */ inline void MarkAsHandledInRemoteProcess() { mNoRemoteProcessDispatch = true; mWantReplyFromContentProcess = true; mPostedToRemoteProcess = false; } /** * Return true if the event has already been handled in the remote process. */ inline bool IsHandledInRemoteProcess() const { return mNoRemoteProcessDispatch && mWantReplyFromContentProcess; } /** * Return true if the event should be sent back to its parent process. */ inline bool WantReplyFromContentProcess() const { MOZ_ASSERT(!XRE_IsParentProcess()); return IsWaitingReplyFromRemoteProcess(); } /** * Mark the event has already posted to a remote process. */ inline void MarkAsPostedToRemoteProcess() { MOZ_ASSERT(!IsCrossProcessForwardingStopped()); mPostedToRemoteProcess = true; } /** * Reset the cross process dispatching state. This should be used when a * process receives the event because the state is in the sender. */ inline void ResetCrossProcessDispatchingState() { MOZ_ASSERT(!IsCrossProcessForwardingStopped()); mPostedToRemoteProcess = false; // Ignore propagation state in the remote process if it's marked as // "waiting reply from remote process" because the process needs to // stop propagation in the process until receiving a reply event. // Note that the propagation stopped flag is important for the reply event // handler in the main process because it's used for making whether it's // ignored by the remote process or not. if (!XRE_IsParentProcess() && IsWaitingReplyFromRemoteProcess()) { mPropagationStopped = mImmediatePropagationStopped = false; } // mDispatchedAtLeastOnce indicates the state in current process. mDispatchedAtLeastOnce = false; } /** * Return true if the event has been posted to a remote process. * Note that MarkAsPostedToRemoteProcess() is called by * ParamTraits<mozilla::WidgetEvent>. Therefore, it *might* be possible * that posting the event failed even if this returns true. But that must * really rare. If that'd be problem for you, you should unmark this in * BrowserParent or somewhere. */ inline bool HasBeenPostedToRemoteProcess() const { return mPostedToRemoteProcess; } /** * Return true if the event came from another process. */ inline bool CameFromAnotherProcess() const { return mCameFromAnotherProcess; } /** * Mark the event as coming from another process. */ inline void MarkAsComingFromAnotherProcess() { mCameFromAnotherProcess = true; } /** * Mark the event is reserved by chrome. I.e., shouldn't be dispatched to * content because it shouldn't be cancelable. */ inline void MarkAsReservedByChrome() { MOZ_ASSERT(!mPostedToRemoteProcess); mIsReservedByChrome = true; // For reserved commands (such as Open New Tab), we don't need to wait for // the content to answer, neither to give a chance for content to override // its behavior. StopCrossProcessForwarding(); // If the event is reserved by chrome, we shouldn't expose the event to // web contents because such events shouldn't be cancelable. So, it's not // good behavior to fire such events but to ignore the defaultPrevented // attribute value in chrome. mOnlySystemGroupDispatchInContent = true; } /** * Return true if the event is reserved by chrome. */ inline bool IsReservedByChrome() const { MOZ_ASSERT(!mIsReservedByChrome || (IsCrossProcessForwardingStopped() && mOnlySystemGroupDispatchInContent)); return mIsReservedByChrome; } inline void Clear() { SetRawFlags(0); } // Get if either the instance's bit or the aOther's bit is true, the // instance's bit becomes true. In other words, this works like: // eventFlags |= aOther; inline void Union(const BaseEventFlags& aOther) { RawFlags rawFlags = GetRawFlags() | aOther.GetRawFlags(); SetRawFlags(rawFlags); } private: typedef uint64_t RawFlags; inline void SetRawFlags(RawFlags aRawFlags) { static_assert(sizeof(BaseEventFlags) <= sizeof(RawFlags), "mozilla::EventFlags must not be bigger than the RawFlags"); memcpy(this, &aRawFlags, sizeof(BaseEventFlags)); } inline RawFlags GetRawFlags() const { RawFlags result = 0; memcpy(&result, this, sizeof(BaseEventFlags)); return result; } }; /****************************************************************************** * mozilla::EventFlags ******************************************************************************/ struct EventFlags : public BaseEventFlags { EventFlags() { Clear(); } }; /****************************************************************************** * mozilla::WidgetEventTime ******************************************************************************/ class WidgetEventTime { public: // Timestamp when the message was created. TimeStamp mTimeStamp; WidgetEventTime() : mTimeStamp(TimeStamp::Now()) {} explicit WidgetEventTime(const WidgetEventTime* aTime) : mTimeStamp(aTime ? aTime->mTimeStamp : TimeStamp::Now()) { MOZ_ASSERT(aTime != this); MOZ_ASSERT_IF(aTime, !aTime->mTimeStamp.IsNull()); } explicit WidgetEventTime(TimeStamp aTimeStamp) : mTimeStamp(aTimeStamp) {} void AssignEventTime(const WidgetEventTime& aOther) { mTimeStamp = aOther.mTimeStamp; } }; /****************************************************************************** * mozilla::WidgetEvent ******************************************************************************/ class WidgetEvent : public WidgetEventTime { private: void SetDefaultCancelableAndBubbles() { switch (mClass) { case eEditorInputEventClass: mFlags.mCancelable = false; mFlags.mBubbles = mFlags.mIsTrusted; break; case eLegacyTextEventClass: mFlags.mCancelable = mFlags.mIsTrusted && mMessage == eLegacyTextInput; mFlags.mBubbles = mFlags.mIsTrusted && mMessage == eLegacyTextInput; break; case eMouseEventClass: mFlags.mCancelable = (mMessage != eMouseEnter && mMessage != eMouseLeave); mFlags.mBubbles = (mMessage != eMouseEnter && mMessage != eMouseLeave); break; case ePointerEventClass: mFlags.mCancelable = (mMessage != ePointerEnter && mMessage != ePointerLeave && mMessage != ePointerCancel && mMessage != ePointerGotCapture && mMessage != ePointerLostCapture); mFlags.mBubbles = (mMessage != ePointerEnter && mMessage != ePointerLeave); break; case eDragEventClass: mFlags.mCancelable = (mMessage != eDragExit && mMessage != eDragLeave && mMessage != eDragEnd); mFlags.mBubbles = true; break; case eSMILTimeEventClass: mFlags.mCancelable = false; mFlags.mBubbles = false; break; case eTransitionEventClass: case eAnimationEventClass: mFlags.mCancelable = false; mFlags.mBubbles = true; break; case eCompositionEventClass: // XXX compositionstart is cancelable in draft of DOM3 Events. // However, it doesn't make sense for us, we cannot cancel // composition when we send compositionstart event. mFlags.mCancelable = false; mFlags.mBubbles = true; break; default: if (mMessage == eResize || mMessage == eMozVisualResize || mMessage == eMozVisualScroll || mMessage == eEditorInput || mMessage == eFormSelect) { mFlags.mCancelable = false; } else { mFlags.mCancelable = true; } mFlags.mBubbles = true; break; } } protected: WidgetEvent(bool aIsTrusted, EventMessage aMessage, EventClassID aEventClassID, const WidgetEventTime* aTime = nullptr) : WidgetEventTime(aTime), mClass(aEventClassID), mMessage(aMessage), mRefPoint(0, 0), mLastRefPoint(0, 0), mFocusSequenceNumber(0), mSpecifiedEventType(nullptr), mPath(nullptr), mLayersId(layers::LayersId{0}) { MOZ_COUNT_CTOR(WidgetEvent); mFlags.Clear(); mFlags.mIsTrusted = aIsTrusted; SetDefaultCancelableAndBubbles(); SetDefaultComposed(); SetDefaultComposedInNativeAnonymousContent(); } WidgetEvent() : mPath(nullptr) { MOZ_COUNT_CTOR(WidgetEvent); } public: WidgetEvent(bool aIsTrusted, EventMessage aMessage, const WidgetEventTime* aTime = nullptr) : WidgetEvent(aIsTrusted, aMessage, eBasicEventClass, aTime) {} MOZ_COUNTED_DTOR_VIRTUAL(WidgetEvent) WidgetEvent(const WidgetEvent& aOther) : WidgetEventTime(aOther) { MOZ_COUNT_CTOR(WidgetEvent); *this = aOther; } WidgetEvent& operator=(const WidgetEvent& aOther) = default; WidgetEvent(WidgetEvent&& aOther) : WidgetEventTime(std::move(aOther)), mClass(aOther.mClass), mMessage(aOther.mMessage), mRefPoint(std::move(aOther.mRefPoint)), mLastRefPoint(std::move(aOther.mLastRefPoint)), mFocusSequenceNumber(aOther.mFocusSequenceNumber), mFlags(std::move(aOther.mFlags)), mSpecifiedEventType(std::move(aOther.mSpecifiedEventType)), mSpecifiedEventTypeString(std::move(aOther.mSpecifiedEventTypeString)), mTarget(std::move(aOther.mTarget)), mCurrentTarget(std::move(aOther.mCurrentTarget)), mOriginalTarget(std::move(aOther.mOriginalTarget)), mRelatedTarget(std::move(aOther.mRelatedTarget)), mOriginalRelatedTarget(std::move(aOther.mOriginalRelatedTarget)), mPath(std::move(aOther.mPath)) { MOZ_COUNT_CTOR(WidgetEvent); } WidgetEvent& operator=(WidgetEvent&& aOther) = default; virtual WidgetEvent* Duplicate() const { MOZ_ASSERT(mClass == eBasicEventClass, "Duplicate() must be overridden by sub class"); WidgetEvent* result = new WidgetEvent(false, mMessage, this); result->AssignEventData(*this, true); result->mFlags = mFlags; return result; } EventClassID mClass; EventMessage mMessage; // Relative to the widget of the event, or if there is no widget then it is // in screen coordinates. Not modified by layout code. // This is in visual coordinates, i.e. the correct RelativeTo value that // expresses what this is relative to is `{viewportFrame, Visual}`, where // `viewportFrame` is the viewport frame of the widget's root document. LayoutDeviceIntPoint mRefPoint; // The previous mRefPoint, if known, used to calculate mouse movement deltas. LayoutDeviceIntPoint mLastRefPoint; // The sequence number of the last potentially focus changing event handled // by APZ. This is used to track when that event has been processed by // content, and focus can be reconfirmed for async keyboard scrolling. uint64_t mFocusSequenceNumber; // See BaseEventFlags definition for the detail. BaseEventFlags mFlags; // If JS creates an event with unknown event type or known event type but // for different event interface, the event type is stored to this. // NOTE: This is always used if the instance is a WidgetCommandEvent instance // or "input" event is dispatched with dom::Event class. RefPtr<nsAtom> mSpecifiedEventType; // nsAtom isn't available on non-main thread due to unsafe. Therefore, // mSpecifiedEventTypeString is used instead of mSpecifiedEventType if // the event is created in non-main thread. nsString mSpecifiedEventTypeString; // Event targets, needed by DOM Events // Note that when you need event target for DOM event, you should use // Get*DOMEventTarget() instead of accessing these members directly. nsCOMPtr<dom::EventTarget> mTarget; nsCOMPtr<dom::EventTarget> mCurrentTarget; nsCOMPtr<dom::EventTarget> mOriginalTarget; /// The possible related target nsCOMPtr<dom::EventTarget> mRelatedTarget; nsCOMPtr<dom::EventTarget> mOriginalRelatedTarget; nsTArray<EventTargetChainItem>* mPath; // The LayersId of the content process that this event should be // dispatched to. This field is only used in the chrome process // and doesn't get remoted to child processes. layers::LayersId mLayersId; dom::EventTarget* GetDOMEventTarget() const; dom::EventTarget* GetCurrentDOMEventTarget() const; dom::EventTarget* GetOriginalDOMEventTarget() const; void AssignEventData(const WidgetEvent& aEvent, bool aCopyTargets) { // mClass should be initialized with the constructor. // mMessage should be initialized with the constructor. mRefPoint = aEvent.mRefPoint; // mLastRefPoint doesn't need to be copied. mFocusSequenceNumber = aEvent.mFocusSequenceNumber; // mLayersId intentionally not copied, since it's not used within content AssignEventTime(aEvent); // mFlags should be copied manually if it's necessary. mSpecifiedEventType = aEvent.mSpecifiedEventType; // mSpecifiedEventTypeString should be copied manually if it's necessary. mTarget = aCopyTargets ? aEvent.mTarget : nullptr; mCurrentTarget = aCopyTargets ? aEvent.mCurrentTarget : nullptr; mOriginalTarget = aCopyTargets ? aEvent.mOriginalTarget : nullptr; mRelatedTarget = aCopyTargets ? aEvent.mRelatedTarget : nullptr; mOriginalRelatedTarget = aCopyTargets ? aEvent.mOriginalRelatedTarget : nullptr; } /** * Helper methods for methods of DOM Event. */ void StopPropagation() { mFlags.StopPropagation(); } void StopImmediatePropagation() { mFlags.StopImmediatePropagation(); } void PreventDefault(bool aCalledByDefaultHandler = true, nsIPrincipal* aPrincipal = nullptr); void PreventDefaultBeforeDispatch( CrossProcessForwarding aCrossProcessForwarding) { mFlags.PreventDefaultBeforeDispatch(aCrossProcessForwarding); } bool DefaultPrevented() const { return mFlags.DefaultPrevented(); } bool DefaultPreventedByContent() const { return mFlags.DefaultPreventedByContent(); } bool IsTrusted() const { return mFlags.IsTrusted(); } bool PropagationStopped() const { return mFlags.PropagationStopped(); } /** * Prevent to be dispatched to remote process. */ inline void StopCrossProcessForwarding() { mFlags.StopCrossProcessForwarding(); } /** * Return true if the event shouldn't be dispatched to remote process. */ inline bool IsCrossProcessForwardingStopped() const { return mFlags.IsCrossProcessForwardingStopped(); } /** * Mark the event as waiting reply from remote process. * Note that this also stops immediate propagation in current process. */ inline void MarkAsWaitingReplyFromRemoteProcess() { mFlags.MarkAsWaitingReplyFromRemoteProcess(); } /** * Reset "waiting reply from remote process" state. This is useful when * you dispatch a copy of an event coming from different process. */ inline void ResetWaitingReplyFromRemoteProcessState() { mFlags.ResetWaitingReplyFromRemoteProcessState(); } /** * Return true if the event handler should wait reply event. I.e., if this * returns true, any event handler should do nothing with the event. */ inline bool IsWaitingReplyFromRemoteProcess() const { return mFlags.IsWaitingReplyFromRemoteProcess(); } /** * Mark the event as already handled in the remote process. This should be * called when initializing reply events. */ inline void MarkAsHandledInRemoteProcess() { mFlags.MarkAsHandledInRemoteProcess(); } /** * Return true if the event has already been handled in the remote process. * I.e., if this returns true, the event is a reply event. */ inline bool IsHandledInRemoteProcess() const { return mFlags.IsHandledInRemoteProcess(); } /** * Return true if the event should be sent back to its parent process. * So, usual event handlers shouldn't call this. */ inline bool WantReplyFromContentProcess() const { return mFlags.WantReplyFromContentProcess(); } /** * Mark the event has already posted to a remote process. */ inline void MarkAsPostedToRemoteProcess() { mFlags.MarkAsPostedToRemoteProcess(); } /** * Reset the cross process dispatching state. This should be used when a * process receives the event because the state is in the sender. */ inline void ResetCrossProcessDispatchingState() { mFlags.ResetCrossProcessDispatchingState(); } /** * Return true if the event has been posted to a remote process. */ inline bool HasBeenPostedToRemoteProcess() const { return mFlags.HasBeenPostedToRemoteProcess(); } /** * Return true if the event came from another process. */ inline bool CameFromAnotherProcess() const { return mFlags.CameFromAnotherProcess(); } /** * Mark the event as coming from another process. */ inline void MarkAsComingFromAnotherProcess() { mFlags.MarkAsComingFromAnotherProcess(); } /** * Mark the event is reserved by chrome. I.e., shouldn't be dispatched to * content because it shouldn't be cancelable. */ inline void MarkAsReservedByChrome() { mFlags.MarkAsReservedByChrome(); } /** * Return true if the event is reserved by chrome. */ inline bool IsReservedByChrome() const { return mFlags.IsReservedByChrome(); } /** * Utils for checking event types */ /** * As*Event() returns the pointer of the instance only when the instance is * the class or one of its derived class. */ #define NS_ROOT_EVENT_CLASS(aPrefix, aName) #define NS_EVENT_CLASS(aPrefix, aName) \ virtual aPrefix##aName* As##aName(); \ const aPrefix##aName* As##aName() const; #include "mozilla/EventClassList.h" #undef NS_EVENT_CLASS #undef NS_ROOT_EVENT_CLASS /** * Returns true if the event is a query content event. */ bool IsQueryContentEvent() const; /** * Returns true if the event is a selection event. */ bool IsSelectionEvent() const; /** * Returns true if the event is a content command event. */ bool IsContentCommandEvent() const; /** * Returns true if the event mMessage is one of mouse events. */ bool HasMouseEventMessage() const; /** * Returns true if the event mMessage is one of drag events. */ bool HasDragEventMessage() const; /** * Returns true if aMessage or mMessage is one of key events. */ static bool IsKeyEventMessage(EventMessage aMessage); bool HasKeyEventMessage() const { return IsKeyEventMessage(mMessage); } /** * Returns true if the event mMessage is one of composition events or text * event. */ bool HasIMEEventMessage() const; /** * Returns true if the event can be sent to remote process. */ bool CanBeSentToRemoteProcess() const; /** * Returns true if the original target is a remote process and the event * will be posted to the remote process later. */ bool WillBeSentToRemoteProcess() const; /** * Returns true if the event is related to IME handling. It includes * IME events, query content events and selection events. * Be careful when you use this. */ bool IsIMERelatedEvent() const; /** * Whether the event should be handled by the frame of the mouse cursor * position or not. When it should be handled there (e.g., the mouse events), * this returns true. */ bool IsUsingCoordinates() const; /** * Whether the event should be handled by the focused DOM window in the * same top level window's or not. E.g., key events, IME related events * (including the query content events, they are used in IME transaction) * should be handled by the (last) focused window rather than the dispatched * window. * * NOTE: Even if this returns true, the event isn't going to be handled by the * application level active DOM window which is on another top level window. * So, when the event is fired on a deactive window, the event is going to be * handled by the last focused DOM window in the last focused window. */ bool IsTargetedAtFocusedWindow() const; /** * Whether the event should be handled by the focused content or not. E.g., * key events, IME related events and other input events which are not handled * by the frame of the mouse cursor position. * * NOTE: Even if this returns true, the event isn't going to be handled by the * application level active DOM window which is on another top level window. * So, when the event is fired on a deactive window, the event is going to be * handled by the last focused DOM element of the last focused DOM window in * the last focused window. */ bool IsTargetedAtFocusedContent() const; /** * Whether the event should cause a DOM event. */ bool IsAllowedToDispatchDOMEvent() const; /** * Whether the event should be dispatched in system group. */ bool IsAllowedToDispatchInSystemGroup() const; /** * Whether the event should be blocked for fingerprinting resistance. */ bool IsBlockedForFingerprintingResistance() const; /** * Whether the event handler can flush pending notifications or not. */ bool AllowFlushingPendingNotifications() const; /** * Initialize mComposed */ void SetDefaultComposed() { switch (mClass) { case eClipboardEventClass: mFlags.mComposed = true; break; case eCompositionEventClass: mFlags.mComposed = mMessage == eCompositionStart || mMessage == eCompositionUpdate || mMessage == eCompositionChange || mMessage == eCompositionEnd; break; case eDragEventClass: // All drag & drop events are composed mFlags.mComposed = mMessage == eDrag || mMessage == eDragEnd || mMessage == eDragEnter || mMessage == eDragExit || mMessage == eDragLeave || mMessage == eDragOver || mMessage == eDragStart || mMessage == eDrop; break; case eEditorInputEventClass: mFlags.mComposed = mMessage == eEditorInput || mMessage == eEditorBeforeInput; break; case eFocusEventClass: mFlags.mComposed = mMessage == eBlur || mMessage == eFocus || mMessage == eFocusOut || mMessage == eFocusIn; break; case eKeyboardEventClass: mFlags.mComposed = mMessage == eKeyDown || mMessage == eKeyUp || mMessage == eKeyPress; break; case eMouseEventClass: mFlags.mComposed = mMessage == eMouseClick || mMessage == eMouseDoubleClick || mMessage == eMouseAuxClick || mMessage == eMouseDown || mMessage == eMouseUp || mMessage == eMouseOver || mMessage == eMouseOut || mMessage == eMouseMove || mMessage == eContextMenu || mMessage == eXULPopupShowing || mMessage == eXULPopupHiding || mMessage == eXULPopupShown || mMessage == eXULPopupHidden; break; case ePointerEventClass: // All pointer events are composed mFlags.mComposed = mMessage == ePointerDown || mMessage == ePointerMove || mMessage == ePointerUp || mMessage == ePointerCancel || mMessage == ePointerOver || mMessage == ePointerOut || mMessage == ePointerGotCapture || mMessage == ePointerLostCapture; break; case eTouchEventClass: // All touch events are composed mFlags.mComposed = mMessage == eTouchStart || mMessage == eTouchEnd || mMessage == eTouchMove || mMessage == eTouchCancel; break; case eUIEventClass: mFlags.mComposed = mMessage == eLegacyDOMFocusIn || mMessage == eLegacyDOMFocusOut || mMessage == eLegacyDOMActivate; break; case eWheelEventClass: // All wheel events are composed mFlags.mComposed = mMessage == eWheel; break; case eMouseScrollEventClass: // Legacy mouse scroll events are composed too, for consistency with // wheel. mFlags.mComposed = mMessage == eLegacyMouseLineOrPageScroll || mMessage == eLegacyMousePixelScroll; break; default: mFlags.mComposed = false; break; } } void SetComposed(const nsAString& aEventTypeArg) { mFlags.mComposed = // composition events aEventTypeArg.EqualsLiteral("compositionstart") || aEventTypeArg.EqualsLiteral("compositionupdate") || aEventTypeArg.EqualsLiteral("compositionend") || aEventTypeArg.EqualsLiteral("text") || // drag and drop events aEventTypeArg.EqualsLiteral("dragstart") || aEventTypeArg.EqualsLiteral("drag") || aEventTypeArg.EqualsLiteral("dragenter") || aEventTypeArg.EqualsLiteral("dragexit") || aEventTypeArg.EqualsLiteral("dragleave") || aEventTypeArg.EqualsLiteral("dragover") || aEventTypeArg.EqualsLiteral("drop") || aEventTypeArg.EqualsLiteral("dropend") || // editor input events aEventTypeArg.EqualsLiteral("input") || aEventTypeArg.EqualsLiteral("beforeinput") || // focus events aEventTypeArg.EqualsLiteral("blur") || aEventTypeArg.EqualsLiteral("focus") || aEventTypeArg.EqualsLiteral("focusin") || aEventTypeArg.EqualsLiteral("focusout") || // keyboard events aEventTypeArg.EqualsLiteral("keydown") || aEventTypeArg.EqualsLiteral("keyup") || aEventTypeArg.EqualsLiteral("keypress") || // mouse events aEventTypeArg.EqualsLiteral("click") || aEventTypeArg.EqualsLiteral("dblclick") || aEventTypeArg.EqualsLiteral("mousedown") || aEventTypeArg.EqualsLiteral("mouseup") || aEventTypeArg.EqualsLiteral("mouseenter") || aEventTypeArg.EqualsLiteral("mouseleave") || aEventTypeArg.EqualsLiteral("mouseover") || aEventTypeArg.EqualsLiteral("mouseout") || aEventTypeArg.EqualsLiteral("mousemove") || aEventTypeArg.EqualsLiteral("contextmenu") || // pointer events aEventTypeArg.EqualsLiteral("pointerdown") || aEventTypeArg.EqualsLiteral("pointermove") || aEventTypeArg.EqualsLiteral("pointerup") || aEventTypeArg.EqualsLiteral("pointercancel") || aEventTypeArg.EqualsLiteral("pointerover") || aEventTypeArg.EqualsLiteral("pointerout") || aEventTypeArg.EqualsLiteral("pointerenter") || aEventTypeArg.EqualsLiteral("pointerleave") || aEventTypeArg.EqualsLiteral("gotpointercapture") || aEventTypeArg.EqualsLiteral("lostpointercapture") || // touch events aEventTypeArg.EqualsLiteral("touchstart") || aEventTypeArg.EqualsLiteral("touchend") || aEventTypeArg.EqualsLiteral("touchmove") || aEventTypeArg.EqualsLiteral("touchcancel") || // UI legacy events aEventTypeArg.EqualsLiteral("DOMFocusIn") || aEventTypeArg.EqualsLiteral("DOMFocusOut") || aEventTypeArg.EqualsLiteral("DOMActivate") || // wheel events aEventTypeArg.EqualsLiteral("wheel"); } void SetComposed(bool aComposed) { mFlags.mComposed = aComposed; } void SetDefaultComposedInNativeAnonymousContent() { // For compatibility concerns, we set mComposedInNativeAnonymousContent to // false for those events we want to stop propagation. // // nsVideoFrame may create anonymous image element which fires eLoad, // eLoadStart, eLoadEnd, eLoadError. We don't want these events cross // the boundary of NAC mFlags.mComposedInNativeAnonymousContent = mMessage != eLoad && mMessage != eLoadStart && mMessage != eLoadEnd && mMessage != eLoadError; } bool IsUserAction() const; }; /****************************************************************************** * mozilla::WidgetGUIEvent ******************************************************************************/ class WidgetGUIEvent : public WidgetEvent { protected: WidgetGUIEvent(bool aIsTrusted, EventMessage aMessage, nsIWidget* aWidget, EventClassID aEventClassID, const WidgetEventTime* aTime = nullptr) : WidgetEvent(aIsTrusted, aMessage, aEventClassID, aTime), mWidget(aWidget) {} WidgetGUIEvent() = default; public: virtual WidgetGUIEvent* AsGUIEvent() override { return this; } WidgetGUIEvent(bool aIsTrusted, EventMessage aMessage, nsIWidget* aWidget, const WidgetEventTime* aTime = nullptr) : WidgetEvent(aIsTrusted, aMessage, eGUIEventClass, aTime), mWidget(aWidget) {} virtual WidgetEvent* Duplicate() const override { MOZ_ASSERT(mClass == eGUIEventClass, "Duplicate() must be overridden by sub class"); // Not copying widget, it is a weak reference. WidgetGUIEvent* result = new WidgetGUIEvent(false, mMessage, nullptr, this); result->AssignGUIEventData(*this, true); result->mFlags = mFlags; return result; } // Originator of the event nsCOMPtr<nsIWidget> mWidget; void AssignGUIEventData(const WidgetGUIEvent& aEvent, bool aCopyTargets) { AssignEventData(aEvent, aCopyTargets); // widget should be initialized with the constructor. } }; /****************************************************************************** * mozilla::Modifier * * All modifier keys should be defined here. This is used for managing * modifier states for DOM Level 3 or later. ******************************************************************************/ enum Modifier { MODIFIER_NONE = 0x0000, MODIFIER_ALT = 0x0001, MODIFIER_ALTGRAPH = 0x0002, MODIFIER_CAPSLOCK = 0x0004, MODIFIER_CONTROL = 0x0008, MODIFIER_FN = 0x0010, MODIFIER_FNLOCK = 0x0020, MODIFIER_META = 0x0040, MODIFIER_NUMLOCK = 0x0080, MODIFIER_SCROLLLOCK = 0x0100, MODIFIER_SHIFT = 0x0200, MODIFIER_SYMBOL = 0x0400, MODIFIER_SYMBOLLOCK = 0x0800, }; /****************************************************************************** * Modifier key names. ******************************************************************************/ #define NS_DOM_KEYNAME_ALT "Alt" #define NS_DOM_KEYNAME_ALTGRAPH "AltGraph" #define NS_DOM_KEYNAME_CAPSLOCK "CapsLock" #define NS_DOM_KEYNAME_CONTROL "Control" #define NS_DOM_KEYNAME_FN "Fn" #define NS_DOM_KEYNAME_FNLOCK "FnLock" #define NS_DOM_KEYNAME_META "Meta" #define NS_DOM_KEYNAME_NUMLOCK "NumLock" #define NS_DOM_KEYNAME_SCROLLLOCK "ScrollLock" #define NS_DOM_KEYNAME_SHIFT "Shift" #define NS_DOM_KEYNAME_SYMBOL "Symbol" #define NS_DOM_KEYNAME_SYMBOLLOCK "SymbolLock" #define NS_DOM_KEYNAME_OS "OS" /****************************************************************************** * mozilla::Modifiers ******************************************************************************/ typedef uint16_t Modifiers; class MOZ_STACK_CLASS GetModifiersName final : public nsAutoCString { public: explicit GetModifiersName(Modifiers aModifiers) { if (aModifiers & MODIFIER_ALT) { AssignLiteral(NS_DOM_KEYNAME_ALT); } if (aModifiers & MODIFIER_ALTGRAPH) { MaybeAppendSeparator(); AppendLiteral(NS_DOM_KEYNAME_ALTGRAPH); } if (aModifiers & MODIFIER_CAPSLOCK) { MaybeAppendSeparator(); AppendLiteral(NS_DOM_KEYNAME_CAPSLOCK); } if (aModifiers & MODIFIER_CONTROL) { MaybeAppendSeparator(); AppendLiteral(NS_DOM_KEYNAME_CONTROL); } if (aModifiers & MODIFIER_FN) { MaybeAppendSeparator(); AppendLiteral(NS_DOM_KEYNAME_FN); } if (aModifiers & MODIFIER_FNLOCK) { MaybeAppendSeparator(); AppendLiteral(NS_DOM_KEYNAME_FNLOCK); } if (aModifiers & MODIFIER_META) { MaybeAppendSeparator(); AppendLiteral(NS_DOM_KEYNAME_META); } if (aModifiers & MODIFIER_NUMLOCK) { MaybeAppendSeparator(); AppendLiteral(NS_DOM_KEYNAME_NUMLOCK); } if (aModifiers & MODIFIER_SCROLLLOCK) { MaybeAppendSeparator(); AppendLiteral(NS_DOM_KEYNAME_SCROLLLOCK); } if (aModifiers & MODIFIER_SHIFT) { MaybeAppendSeparator(); AppendLiteral(NS_DOM_KEYNAME_SHIFT); } if (aModifiers & MODIFIER_SYMBOL) { MaybeAppendSeparator(); AppendLiteral(NS_DOM_KEYNAME_SYMBOL); } if (aModifiers & MODIFIER_SYMBOLLOCK) { MaybeAppendSeparator(); AppendLiteral(NS_DOM_KEYNAME_SYMBOLLOCK); } if (IsEmpty()) { AssignLiteral("none"); } } private: void MaybeAppendSeparator() { if (!IsEmpty()) { AppendLiteral(" | "); } } }; /****************************************************************************** * mozilla::WidgetInputEvent ******************************************************************************/ class WidgetInputEvent : public WidgetGUIEvent { protected: WidgetInputEvent(bool aIsTrusted, EventMessage aMessage, nsIWidget* aWidget, EventClassID aEventClassID, const WidgetEventTime* aTime = nullptr) : WidgetGUIEvent(aIsTrusted, aMessage, aWidget, aEventClassID, aTime), mModifiers(0) {} WidgetInputEvent() : mModifiers(0) {} public: virtual WidgetInputEvent* AsInputEvent() override { return this; } WidgetInputEvent(bool aIsTrusted, EventMessage aMessage, nsIWidget* aWidget, const WidgetEventTime* aTime = nullptr) : WidgetGUIEvent(aIsTrusted, aMessage, aWidget, eInputEventClass, aTime), mModifiers(0) {} virtual WidgetEvent* Duplicate() const override { MOZ_ASSERT(mClass == eInputEventClass, "Duplicate() must be overridden by sub class"); // Not copying widget, it is a weak reference. WidgetInputEvent* result = new WidgetInputEvent(false, mMessage, nullptr, this); result->AssignInputEventData(*this, true); result->mFlags = mFlags; return result; } /** * Returns a modifier of "Accel" virtual modifier which is used for shortcut * key. */ static Modifier AccelModifier(); /** * GetModifier() returns a modifier flag which is activated by aDOMKeyName. */ static Modifier GetModifier(const nsAString& aDOMKeyName); // true indicates the accel key on the environment is down bool IsAccel() const { return ((mModifiers & AccelModifier()) != 0); } // true indicates the shift key is down bool IsShift() const { return ((mModifiers & MODIFIER_SHIFT) != 0); } // true indicates the control key is down bool IsControl() const { return ((mModifiers & MODIFIER_CONTROL) != 0); } // true indicates the alt key is down bool IsAlt() const { return ((mModifiers & MODIFIER_ALT) != 0); } // true indicates the meta key is down (Command key on macOS, Windows logo key // on Windows, Super/Hyper key on Linux, Meta key on Android). bool IsMeta() const { return ((mModifiers & MODIFIER_META) != 0); } // true indicates the alt graph key is down // NOTE: on Mac, the option key press causes both IsAlt() and IsAltGrpah() // return true. bool IsAltGraph() const { return ((mModifiers & MODIFIER_ALTGRAPH) != 0); } // true indicates the CapLock LED is turn on. bool IsCapsLocked() const { return ((mModifiers & MODIFIER_CAPSLOCK) != 0); } // true indicates the NumLock LED is turn on. bool IsNumLocked() const { return ((mModifiers & MODIFIER_NUMLOCK) != 0); } // true indicates the ScrollLock LED is turn on. bool IsScrollLocked() const { return ((mModifiers & MODIFIER_SCROLLLOCK) != 0); } // true indicates the Fn key is down, but this is not supported by native // key event on any platform. bool IsFn() const { return ((mModifiers & MODIFIER_FN) != 0); } // true indicates the FnLock LED is turn on, but we don't know such // keyboards nor platforms. bool IsFnLocked() const { return ((mModifiers & MODIFIER_FNLOCK) != 0); } // true indicates the Symbol is down, but this is not supported by native // key event on any platforms. bool IsSymbol() const { return ((mModifiers & MODIFIER_SYMBOL) != 0); } // true indicates the SymbolLock LED is turn on, but we don't know such // keyboards nor platforms. bool IsSymbolLocked() const { return ((mModifiers & MODIFIER_SYMBOLLOCK) != 0); } void InitBasicModifiers(bool aCtrlKey, bool aAltKey, bool aShiftKey, bool aMetaKey) { mModifiers = 0; if (aCtrlKey) { mModifiers |= MODIFIER_CONTROL; } if (aAltKey) { mModifiers |= MODIFIER_ALT; } if (aShiftKey) { mModifiers |= MODIFIER_SHIFT; } if (aMetaKey) { mModifiers |= MODIFIER_META; } } Modifiers mModifiers; void AssignInputEventData(const WidgetInputEvent& aEvent, bool aCopyTargets) { AssignGUIEventData(aEvent, aCopyTargets); mModifiers = aEvent.mModifiers; } }; /****************************************************************************** * mozilla::InternalUIEvent * * XXX Why this inherits WidgetGUIEvent rather than WidgetEvent? ******************************************************************************/ class InternalUIEvent : public WidgetGUIEvent { protected: InternalUIEvent() : mDetail(0), mCausedByUntrustedEvent(false) {} InternalUIEvent(bool aIsTrusted, EventMessage aMessage, nsIWidget* aWidget, EventClassID aEventClassID, const WidgetEventTime* aTime = nullptr) : WidgetGUIEvent(aIsTrusted, aMessage, aWidget, aEventClassID, aTime), mDetail(0), mCausedByUntrustedEvent(false) {} InternalUIEvent(bool aIsTrusted, EventMessage aMessage, EventClassID aEventClassID, const WidgetEventTime* aTime = nullptr) : WidgetGUIEvent(aIsTrusted, aMessage, nullptr, aEventClassID, aTime), mDetail(0), mCausedByUntrustedEvent(false) {} public: virtual InternalUIEvent* AsUIEvent() override { return this; } /** * If the UIEvent is caused by another event (e.g., click event), * aEventCausesThisEvent should be the event. If there is no such event, * this should be nullptr. */ InternalUIEvent(bool aIsTrusted, EventMessage aMessage, const WidgetEvent* aEventCausesThisEvent, const WidgetEventTime* aTime = nullptr) : WidgetGUIEvent(aIsTrusted, aMessage, nullptr, eUIEventClass, aTime), mDetail(0), mCausedByUntrustedEvent(aEventCausesThisEvent && !aEventCausesThisEvent->IsTrusted()) {} virtual WidgetEvent* Duplicate() const override { MOZ_ASSERT(mClass == eUIEventClass, "Duplicate() must be overridden by sub class"); InternalUIEvent* result = new InternalUIEvent(false, mMessage, nullptr, this); result->AssignUIEventData(*this, true); result->mFlags = mFlags; return result; } int32_t mDetail; // mCausedByUntrustedEvent is true if the event is caused by untrusted event. bool mCausedByUntrustedEvent; // If you check the event is a trusted event and NOT caused by an untrusted // event, IsTrustable() returns what you expected. bool IsTrustable() const { return IsTrusted() && !mCausedByUntrustedEvent; } void AssignUIEventData(const InternalUIEvent& aEvent, bool aCopyTargets) { AssignGUIEventData(aEvent, aCopyTargets); mDetail = aEvent.mDetail; mCausedByUntrustedEvent = aEvent.mCausedByUntrustedEvent; } }; } // namespace mozilla #endif // mozilla_BasicEvents_h__