michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef mozilla_EventStateManager_h_ michael@0: #define mozilla_EventStateManager_h_ michael@0: michael@0: #include "mozilla/EventForwards.h" michael@0: #include "mozilla/TypedEnum.h" michael@0: michael@0: #include "nsIObserver.h" michael@0: #include "nsWeakReference.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsCOMArray.h" michael@0: #include "nsCycleCollectionParticipant.h" michael@0: #include "mozilla/TimeStamp.h" michael@0: #include "nsIFrame.h" michael@0: #include "Units.h" michael@0: michael@0: class nsFrameLoader; michael@0: class nsIContent; michael@0: class nsIDocument; michael@0: class nsIDocShell; michael@0: class nsIDocShellTreeItem; michael@0: class imgIContainer; michael@0: class EnterLeaveDispatcher; michael@0: class nsIMarkupDocumentViewer; michael@0: class nsIScrollableFrame; michael@0: class nsITimer; michael@0: class nsPresContext; michael@0: michael@0: namespace mozilla { michael@0: michael@0: class EnterLeaveDispatcher; michael@0: class EventStates; michael@0: class IMEContentObserver; michael@0: class ScrollbarsForWheel; michael@0: class WheelTransaction; michael@0: michael@0: namespace dom { michael@0: class DataTransfer; michael@0: class TabParent; michael@0: } // namespace dom michael@0: michael@0: class OverOutElementsWrapper MOZ_FINAL : public nsISupports michael@0: { michael@0: public: michael@0: OverOutElementsWrapper(); michael@0: ~OverOutElementsWrapper(); michael@0: michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: NS_DECL_CYCLE_COLLECTION_CLASS(OverOutElementsWrapper) michael@0: michael@0: nsWeakFrame mLastOverFrame; michael@0: michael@0: nsCOMPtr mLastOverElement; michael@0: michael@0: // The last element on which we fired a over event, or null if michael@0: // the last over event we fired has finished processing. michael@0: nsCOMPtr mFirstOverEventElement; michael@0: michael@0: // The last element on which we fired a out event, or null if michael@0: // the last out event we fired has finished processing. michael@0: nsCOMPtr mFirstOutEventElement; michael@0: }; michael@0: michael@0: class EventStateManager : public nsSupportsWeakReference, michael@0: public nsIObserver michael@0: { michael@0: friend class mozilla::EnterLeaveDispatcher; michael@0: friend class mozilla::ScrollbarsForWheel; michael@0: friend class mozilla::WheelTransaction; michael@0: michael@0: public: michael@0: EventStateManager(); michael@0: virtual ~EventStateManager(); michael@0: michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: NS_DECL_NSIOBSERVER michael@0: michael@0: nsresult Init(); michael@0: nsresult Shutdown(); michael@0: michael@0: /* The PreHandleEvent method is called before event dispatch to either michael@0: * the DOM or frames. Any processing which must not be prevented or michael@0: * cancelled should occur here. Any processing which is intended to michael@0: * be conditional based on either DOM or frame processing should occur in michael@0: * PostHandleEvent. Any centralized event processing which must occur before michael@0: * DOM or frame event handling should occur here as well. michael@0: */ michael@0: nsresult PreHandleEvent(nsPresContext* aPresContext, michael@0: WidgetEvent* aEvent, michael@0: nsIFrame* aTargetFrame, michael@0: nsEventStatus* aStatus); michael@0: michael@0: /* The PostHandleEvent method should contain all system processing which michael@0: * should occur conditionally based on DOM or frame processing. It should michael@0: * also contain any centralized event processing which must occur after michael@0: * DOM and frame processing. michael@0: */ michael@0: nsresult PostHandleEvent(nsPresContext* aPresContext, michael@0: WidgetEvent* aEvent, michael@0: nsIFrame* aTargetFrame, michael@0: nsEventStatus* aStatus); michael@0: michael@0: /** michael@0: * DispatchLegacyMouseScrollEvents() dispatches NS_MOUSE_SCROLL event and michael@0: * NS_MOUSE_PIXEL_SCROLL event for compatiblity with old Gecko. michael@0: */ michael@0: void DispatchLegacyMouseScrollEvents(nsIFrame* aTargetFrame, michael@0: WidgetWheelEvent* aEvent, michael@0: nsEventStatus* aStatus); michael@0: michael@0: void NotifyDestroyPresContext(nsPresContext* aPresContext); michael@0: void SetPresContext(nsPresContext* aPresContext); michael@0: void ClearFrameRefs(nsIFrame* aFrame); michael@0: michael@0: nsIFrame* GetEventTarget(); michael@0: already_AddRefed GetEventTargetContent(WidgetEvent* aEvent); michael@0: michael@0: /** michael@0: * Notify that the given NS_EVENT_STATE_* bit has changed for this content. michael@0: * @param aContent Content which has changed states michael@0: * @param aState Corresponding state flags such as NS_EVENT_STATE_FOCUS michael@0: * @return Whether the content was able to change all states. Returns false michael@0: * if a resulting DOM event causes the content node passed in michael@0: * to not change states. Note, the frame for the content may michael@0: * change as a result of the content state change, because of michael@0: * frame reconstructions that may occur, but this does not michael@0: * affect the return value. michael@0: */ michael@0: bool SetContentState(nsIContent* aContent, EventStates aState); michael@0: void ContentRemoved(nsIDocument* aDocument, nsIContent* aContent); michael@0: bool EventStatusOK(WidgetGUIEvent* aEvent); michael@0: michael@0: /** michael@0: * EventStateManager stores IMEContentObserver while it's observing contents. michael@0: * Following mehtods are called by IMEContentObserver when it starts to michael@0: * observe or stops observing the content. michael@0: */ michael@0: void OnStartToObserveContent(IMEContentObserver* aIMEContentObserver); michael@0: void OnStopObservingContent(IMEContentObserver* aIMEContentObserver); michael@0: michael@0: /** michael@0: * Register accesskey on the given element. When accesskey is activated then michael@0: * the element will be notified via nsIContent::PerformAccesskey() method. michael@0: * michael@0: * @param aContent the given element michael@0: * @param aKey accesskey michael@0: */ michael@0: void RegisterAccessKey(nsIContent* aContent, uint32_t aKey); michael@0: michael@0: /** michael@0: * Unregister accesskey for the given element. michael@0: * michael@0: * @param aContent the given element michael@0: * @param aKey accesskey michael@0: */ michael@0: void UnregisterAccessKey(nsIContent* aContent, uint32_t aKey); michael@0: michael@0: /** michael@0: * Get accesskey registered on the given element or 0 if there is none. michael@0: * michael@0: * @param aContent the given element (must not be null) michael@0: * @return registered accesskey michael@0: */ michael@0: uint32_t GetRegisteredAccessKey(nsIContent* aContent); michael@0: michael@0: bool GetAccessKeyLabelPrefix(nsAString& aPrefix); michael@0: michael@0: nsresult SetCursor(int32_t aCursor, imgIContainer* aContainer, michael@0: bool aHaveHotspot, float aHotspotX, float aHotspotY, michael@0: nsIWidget* aWidget, bool aLockCursor); michael@0: michael@0: static void StartHandlingUserInput() michael@0: { michael@0: ++sUserInputEventDepth; michael@0: if (sUserInputEventDepth == 1) { michael@0: sHandlingInputStart = TimeStamp::Now(); michael@0: } michael@0: } michael@0: michael@0: static void StopHandlingUserInput() michael@0: { michael@0: --sUserInputEventDepth; michael@0: if (sUserInputEventDepth == 0) { michael@0: sHandlingInputStart = TimeStamp(); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Returns true if the current code is being executed as a result of user input. michael@0: * This includes timers or anything else that is initiated from user input. michael@0: * However, mouse over events are not counted as user input, nor are michael@0: * page load events. If this method is called from asynchronously executed code, michael@0: * such as during layout reflows, it will return false. If more time has elapsed michael@0: * since the user input than is specified by the michael@0: * dom.event.handling-user-input-time-limit pref (default 1 second), this michael@0: * function also returns false. michael@0: */ michael@0: static bool IsHandlingUserInput(); michael@0: michael@0: nsPresContext* GetPresContext() { return mPresContext; } michael@0: michael@0: NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(EventStateManager, michael@0: nsIObserver) michael@0: michael@0: static nsIDocument* sMouseOverDocument; michael@0: michael@0: static EventStateManager* GetActiveEventStateManager() { return sActiveESM; } michael@0: michael@0: // Sets aNewESM to be the active event state manager, and michael@0: // if aContent is non-null, marks the object as active. michael@0: static void SetActiveManager(EventStateManager* aNewESM, michael@0: nsIContent* aContent); michael@0: michael@0: // Sets the full-screen event state on aElement to aIsFullScreen. michael@0: static void SetFullScreenState(dom::Element* aElement, bool aIsFullScreen); michael@0: michael@0: static bool IsRemoteTarget(nsIContent* aTarget); michael@0: static LayoutDeviceIntPoint GetChildProcessOffset(nsFrameLoader* aFrameLoader, michael@0: const WidgetEvent& aEvent); michael@0: michael@0: // Holds the point in screen coords that a mouse event was dispatched to, michael@0: // before we went into pointer lock mode. This is constantly updated while michael@0: // the pointer is not locked, but we don't update it while the pointer is michael@0: // locked. This is used by dom::Event::GetScreenCoords() to make mouse michael@0: // events' screen coord appear frozen at the last mouse position while michael@0: // the pointer is locked. michael@0: static nsIntPoint sLastScreenPoint; michael@0: michael@0: // Holds the point in client coords of the last mouse event. Used by michael@0: // dom::Event::GetClientCoords() to make mouse events' client coords appear michael@0: // frozen at the last mouse position while the pointer is locked. michael@0: static CSSIntPoint sLastClientPoint; michael@0: michael@0: static bool sIsPointerLocked; michael@0: static nsWeakPtr sPointerLockedElement; michael@0: static nsWeakPtr sPointerLockedDoc; michael@0: michael@0: protected: michael@0: /** michael@0: * Prefs class capsules preference management. michael@0: */ michael@0: class Prefs michael@0: { michael@0: public: michael@0: static bool KeyCausesActivation() { return sKeyCausesActivation; } michael@0: static bool ClickHoldContextMenu() { return sClickHoldContextMenu; } michael@0: static int32_t ChromeAccessModifierMask(); michael@0: static int32_t ContentAccessModifierMask(); michael@0: michael@0: static void Init(); michael@0: static void OnChange(const char* aPrefName, void*); michael@0: static void Shutdown(); michael@0: michael@0: private: michael@0: static bool sKeyCausesActivation; michael@0: static bool sClickHoldContextMenu; michael@0: static int32_t sGenericAccessModifierKey; michael@0: static int32_t sChromeAccessModifierMask; michael@0: static int32_t sContentAccessModifierMask; michael@0: michael@0: static int32_t GetAccessModifierMask(int32_t aItemType); michael@0: }; michael@0: michael@0: /** michael@0: * Get appropriate access modifier mask for the aDocShell. Returns -1 if michael@0: * access key isn't available. michael@0: */ michael@0: static int32_t GetAccessModifierMaskFor(nsISupports* aDocShell); michael@0: michael@0: void UpdateCursor(nsPresContext* aPresContext, michael@0: WidgetEvent* aEvent, michael@0: nsIFrame* aTargetFrame, michael@0: nsEventStatus* aStatus); michael@0: /** michael@0: * Turn a GUI mouse/pointer event into a mouse/pointer event targeted at the specified michael@0: * content. This returns the primary frame for the content (or null michael@0: * if it goes away during the event). michael@0: */ michael@0: nsIFrame* DispatchMouseOrPointerEvent(WidgetMouseEvent* aMouseEvent, michael@0: uint32_t aMessage, michael@0: nsIContent* aTargetContent, michael@0: nsIContent* aRelatedContent); michael@0: /** michael@0: * Synthesize DOM and frame mouseover and mouseout events from this michael@0: * MOUSE_MOVE or MOUSE_EXIT event. michael@0: */ michael@0: void GenerateMouseEnterExit(WidgetMouseEvent* aMouseEvent); michael@0: /** michael@0: * Tell this ESM and ESMs in parent documents that the mouse is michael@0: * over some content in this document. michael@0: */ michael@0: void NotifyMouseOver(WidgetMouseEvent* aMouseEvent, michael@0: nsIContent* aContent); michael@0: /** michael@0: * Tell this ESM and ESMs in affected child documents that the mouse michael@0: * has exited this document's currently hovered content. michael@0: * @param aMouseEvent the event that triggered the mouseout michael@0: * @param aMovingInto the content node we've moved into. This is used to set michael@0: * the relatedTarget for mouseout events. Also, if it's non-null michael@0: * NotifyMouseOut will NOT change the current hover content to null; michael@0: * in that case the caller is responsible for updating hover state. michael@0: */ michael@0: void NotifyMouseOut(WidgetMouseEvent* aMouseEvent, michael@0: nsIContent* aMovingInto); michael@0: void GenerateDragDropEnterExit(nsPresContext* aPresContext, michael@0: WidgetDragEvent* aDragEvent); michael@0: michael@0: /** michael@0: * Return mMouseEnterLeaveHelper or relevant mPointersEnterLeaveHelper elements wrapper. michael@0: * If mPointersEnterLeaveHelper does not contain wrapper for pointerId it create new one michael@0: */ michael@0: OverOutElementsWrapper* GetWrapperByEventID(WidgetMouseEvent* aMouseEvent); michael@0: michael@0: /** michael@0: * Fire the dragenter and dragexit/dragleave events when the mouse moves to a michael@0: * new target. michael@0: * michael@0: * @param aRelatedTarget relatedTarget to set for the event michael@0: * @param aTargetContent target to set for the event michael@0: * @param aTargetFrame target frame for the event michael@0: */ michael@0: void FireDragEnterOrExit(nsPresContext* aPresContext, michael@0: WidgetDragEvent* aDragEvent, michael@0: uint32_t aMsg, michael@0: nsIContent* aRelatedTarget, michael@0: nsIContent* aTargetContent, michael@0: nsWeakFrame& aTargetFrame); michael@0: /** michael@0: * Update the initial drag session data transfer with any changes that occur michael@0: * on cloned data transfer objects used for events. michael@0: */ michael@0: void UpdateDragDataTransfer(WidgetDragEvent* dragEvent); michael@0: michael@0: nsresult SetClickCount(nsPresContext* aPresContext, michael@0: WidgetMouseEvent* aEvent, michael@0: nsEventStatus* aStatus); michael@0: nsresult CheckForAndDispatchClick(nsPresContext* aPresContext, michael@0: WidgetMouseEvent* aEvent, michael@0: nsEventStatus* aStatus); michael@0: void EnsureDocument(nsPresContext* aPresContext); michael@0: void FlushPendingEvents(nsPresContext* aPresContext); michael@0: michael@0: /** michael@0: * The phases of HandleAccessKey processing. See below. michael@0: */ michael@0: typedef enum { michael@0: eAccessKeyProcessingNormal = 0, michael@0: eAccessKeyProcessingUp, michael@0: eAccessKeyProcessingDown michael@0: } ProcessingAccessKeyState; michael@0: michael@0: /** michael@0: * Access key handling. If there is registered content for the accesskey michael@0: * given by the key event and modifier mask then call michael@0: * content.PerformAccesskey(), otherwise call HandleAccessKey() recursively, michael@0: * on descendant docshells first, then on the ancestor (with |aBubbledFrom| michael@0: * set to the docshell associated with |this|), until something matches. michael@0: * michael@0: * @param aPresContext the presentation context michael@0: * @param aEvent the key event michael@0: * @param aStatus the event status michael@0: * @param aBubbledFrom is used by an ancestor to avoid calling HandleAccessKey() michael@0: * on the child the call originally came from, i.e. this is the child michael@0: * that recursively called us in its Up phase. The initial caller michael@0: * passes |nullptr| here. This is to avoid an infinite loop. michael@0: * @param aAccessKeyState Normal, Down or Up processing phase (see enums michael@0: * above). The initial event receiver uses 'normal', then 'down' when michael@0: * processing children and Up when recursively calling its ancestor. michael@0: * @param aModifierMask modifier mask for the key event michael@0: */ michael@0: void HandleAccessKey(nsPresContext* aPresContext, michael@0: WidgetKeyboardEvent* aEvent, michael@0: nsEventStatus* aStatus, michael@0: nsIDocShellTreeItem* aBubbledFrom, michael@0: ProcessingAccessKeyState aAccessKeyState, michael@0: int32_t aModifierMask); michael@0: michael@0: bool ExecuteAccessKey(nsTArray& aAccessCharCodes, michael@0: bool aIsTrustedEvent); michael@0: michael@0: //--------------------------------------------- michael@0: // DocShell Focus Traversal Methods michael@0: //--------------------------------------------- michael@0: michael@0: nsIContent* GetFocusedContent(); michael@0: bool IsShellVisible(nsIDocShell* aShell); michael@0: michael@0: // These functions are for mousewheel and pixel scrolling michael@0: michael@0: class WheelPrefs michael@0: { michael@0: public: michael@0: static WheelPrefs* GetInstance(); michael@0: static void Shutdown(); michael@0: michael@0: /** michael@0: * ApplyUserPrefsToDelta() overrides the wheel event's delta values with michael@0: * user prefs. michael@0: */ michael@0: void ApplyUserPrefsToDelta(WidgetWheelEvent* aEvent); michael@0: michael@0: /** michael@0: * If ApplyUserPrefsToDelta() changed the delta values with customized michael@0: * prefs, the overflowDelta values would be inflated. michael@0: * CancelApplyingUserPrefsFromOverflowDelta() cancels the inflation. michael@0: */ michael@0: void CancelApplyingUserPrefsFromOverflowDelta(WidgetWheelEvent* aEvent); michael@0: michael@0: /** michael@0: * Computes the default action for the aEvent with the prefs. michael@0: */ michael@0: enum Action MOZ_ENUM_TYPE(uint8_t) michael@0: { michael@0: ACTION_NONE = 0, michael@0: ACTION_SCROLL, michael@0: ACTION_HISTORY, michael@0: ACTION_ZOOM, michael@0: ACTION_LAST = ACTION_ZOOM michael@0: }; michael@0: Action ComputeActionFor(WidgetWheelEvent* aEvent); michael@0: michael@0: /** michael@0: * NeedToComputeLineOrPageDelta() returns if the aEvent needs to be michael@0: * computed the lineOrPageDelta values. michael@0: */ michael@0: bool NeedToComputeLineOrPageDelta(WidgetWheelEvent* aEvent); michael@0: michael@0: /** michael@0: * IsOverOnePageScrollAllowed*() checks whether wheel scroll amount should michael@0: * be rounded down to the page width/height (false) or not (true). michael@0: */ michael@0: bool IsOverOnePageScrollAllowedX(WidgetWheelEvent* aEvent); michael@0: bool IsOverOnePageScrollAllowedY(WidgetWheelEvent* aEvent); michael@0: michael@0: private: michael@0: WheelPrefs(); michael@0: ~WheelPrefs(); michael@0: michael@0: static void OnPrefChanged(const char* aPrefName, void* aClosure); michael@0: michael@0: enum Index michael@0: { michael@0: INDEX_DEFAULT = 0, michael@0: INDEX_ALT, michael@0: INDEX_CONTROL, michael@0: INDEX_META, michael@0: INDEX_SHIFT, michael@0: INDEX_OS, michael@0: COUNT_OF_MULTIPLIERS michael@0: }; michael@0: michael@0: /** michael@0: * GetIndexFor() returns the index of the members which should be used for michael@0: * the aEvent. When only one modifier key of MODIFIER_ALT, michael@0: * MODIFIER_CONTROL, MODIFIER_META, MODIFIER_SHIFT or MODIFIER_OS is michael@0: * pressed, returns the index for the modifier. Otherwise, this return the michael@0: * default index which is used at either no modifier key is pressed or michael@0: * two or modifier keys are pressed. michael@0: */ michael@0: Index GetIndexFor(WidgetWheelEvent* aEvent); michael@0: michael@0: /** michael@0: * GetPrefNameBase() returns the base pref name for aEvent. michael@0: * It's decided by GetModifierForPref() which modifier should be used for michael@0: * the aEvent. michael@0: * michael@0: * @param aBasePrefName The result, must be "mousewheel.with_*." or michael@0: * "mousewheel.default.". michael@0: */ michael@0: void GetBasePrefName(Index aIndex, nsACString& aBasePrefName); michael@0: michael@0: void Init(Index aIndex); michael@0: michael@0: void Reset(); michael@0: michael@0: /** michael@0: * If the abosolute values of mMultiplierX and/or mMultiplierY are equals or michael@0: * larger than this value, the computed scroll amount isn't rounded down to michael@0: * the page width or height. michael@0: */ michael@0: enum { michael@0: MIN_MULTIPLIER_VALUE_ALLOWING_OVER_ONE_PAGE_SCROLL = 1000 michael@0: }; michael@0: michael@0: bool mInit[COUNT_OF_MULTIPLIERS]; michael@0: double mMultiplierX[COUNT_OF_MULTIPLIERS]; michael@0: double mMultiplierY[COUNT_OF_MULTIPLIERS]; michael@0: double mMultiplierZ[COUNT_OF_MULTIPLIERS]; michael@0: Action mActions[COUNT_OF_MULTIPLIERS]; michael@0: /** michael@0: * action values overridden by .override_x pref. michael@0: * If an .override_x value is -1, same as the michael@0: * corresponding mActions value. michael@0: */ michael@0: Action mOverriddenActionsX[COUNT_OF_MULTIPLIERS]; michael@0: michael@0: static WheelPrefs* sInstance; michael@0: }; michael@0: michael@0: /** michael@0: * DeltaDirection is used for specifying whether the called method should michael@0: * handle vertical delta or horizontal delta. michael@0: * This is clearer than using bool. michael@0: */ michael@0: enum DeltaDirection michael@0: { michael@0: DELTA_DIRECTION_X = 0, michael@0: DELTA_DIRECTION_Y michael@0: }; michael@0: michael@0: struct MOZ_STACK_CLASS EventState michael@0: { michael@0: bool mDefaultPrevented; michael@0: bool mDefaultPreventedByContent; michael@0: michael@0: EventState() : michael@0: mDefaultPrevented(false), mDefaultPreventedByContent(false) michael@0: { michael@0: } michael@0: }; michael@0: michael@0: /** michael@0: * SendLineScrollEvent() dispatches a DOMMouseScroll event for the michael@0: * WidgetWheelEvent. This method shouldn't be called for non-trusted michael@0: * wheel event because it's not necessary for compatiblity. michael@0: * michael@0: * @param aTargetFrame The event target of wheel event. michael@0: * @param aEvent The original Wheel event. michael@0: * @param aState The event which should be set to the dispatching michael@0: * event. This also returns the dispatched event michael@0: * state. michael@0: * @param aDelta The delta value of the event. michael@0: * @param aDeltaDirection The X/Y direction of dispatching event. michael@0: */ michael@0: void SendLineScrollEvent(nsIFrame* aTargetFrame, michael@0: WidgetWheelEvent* aEvent, michael@0: EventState& aState, michael@0: int32_t aDelta, michael@0: DeltaDirection aDeltaDirection); michael@0: michael@0: /** michael@0: * SendPixelScrollEvent() dispatches a MozMousePixelScroll event for the michael@0: * WidgetWheelEvent. This method shouldn't be called for non-trusted michael@0: * wheel event because it's not necessary for compatiblity. michael@0: * michael@0: * @param aTargetFrame The event target of wheel event. michael@0: * @param aEvent The original Wheel event. michael@0: * @param aState The event which should be set to the dispatching michael@0: * event. This also returns the dispatched event michael@0: * state. michael@0: * @param aPixelDelta The delta value of the event. michael@0: * @param aDeltaDirection The X/Y direction of dispatching event. michael@0: */ michael@0: void SendPixelScrollEvent(nsIFrame* aTargetFrame, michael@0: WidgetWheelEvent* aEvent, michael@0: EventState& aState, michael@0: int32_t aPixelDelta, michael@0: DeltaDirection aDeltaDirection); michael@0: michael@0: /** michael@0: * ComputeScrollTarget() returns the scrollable frame which should be michael@0: * scrolled. michael@0: * michael@0: * @param aTargetFrame The event target of the wheel event. michael@0: * @param aEvent The handling mouse wheel event. michael@0: * @param aOptions The options for finding the scroll target. michael@0: * Callers should use COMPUTE_*. michael@0: * @return The scrollable frame which should be scrolled. michael@0: */ michael@0: // These flags are used in ComputeScrollTarget(). Callers should use michael@0: // COMPUTE_*. michael@0: enum michael@0: { michael@0: PREFER_MOUSE_WHEEL_TRANSACTION = 1, michael@0: PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_X_AXIS = 2, michael@0: PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS = 4, michael@0: START_FROM_PARENT = 8 michael@0: }; michael@0: enum ComputeScrollTargetOptions michael@0: { michael@0: // At computing scroll target for legacy mouse events, we should return michael@0: // first scrollable element even when it's not scrollable to the direction. michael@0: COMPUTE_LEGACY_MOUSE_SCROLL_EVENT_TARGET = 0, michael@0: // Default action prefers the scrolled element immediately before if it's michael@0: // still under the mouse cursor. Otherwise, it prefers the nearest michael@0: // scrollable ancestor which will be scrolled actually. michael@0: COMPUTE_DEFAULT_ACTION_TARGET = michael@0: (PREFER_MOUSE_WHEEL_TRANSACTION | michael@0: PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_X_AXIS | michael@0: PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS), michael@0: // Look for the nearest scrollable ancestor which can be scrollable with michael@0: // aEvent. michael@0: COMPUTE_SCROLLABLE_ANCESTOR_ALONG_X_AXIS = michael@0: (PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_X_AXIS | START_FROM_PARENT), michael@0: COMPUTE_SCROLLABLE_ANCESTOR_ALONG_Y_AXIS = michael@0: (PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS | START_FROM_PARENT) michael@0: }; michael@0: nsIScrollableFrame* ComputeScrollTarget(nsIFrame* aTargetFrame, michael@0: WidgetWheelEvent* aEvent, michael@0: ComputeScrollTargetOptions aOptions); michael@0: michael@0: nsIScrollableFrame* ComputeScrollTarget(nsIFrame* aTargetFrame, michael@0: double aDirectionX, michael@0: double aDirectionY, michael@0: WidgetWheelEvent* aEvent, michael@0: ComputeScrollTargetOptions aOptions); michael@0: michael@0: /** michael@0: * GetScrollAmount() returns the scroll amount in app uints of one line or michael@0: * one page. If the wheel event scrolls a page, returns the page width and michael@0: * height. Otherwise, returns line height for both its width and height. michael@0: * michael@0: * @param aScrollableFrame A frame which will be scrolled by the event. michael@0: * The result of ComputeScrollTarget() is michael@0: * expected for this value. michael@0: * This can be nullptr if there is no scrollable michael@0: * frame. Then, this method uses root frame's michael@0: * line height or visible area's width and height. michael@0: */ michael@0: nsSize GetScrollAmount(nsPresContext* aPresContext, michael@0: WidgetWheelEvent* aEvent, michael@0: nsIScrollableFrame* aScrollableFrame); michael@0: michael@0: /** michael@0: * DoScrollText() scrolls the scrollable frame for aEvent. michael@0: */ michael@0: void DoScrollText(nsIScrollableFrame* aScrollableFrame, michael@0: WidgetWheelEvent* aEvent); michael@0: michael@0: void DoScrollHistory(int32_t direction); michael@0: void DoScrollZoom(nsIFrame *aTargetFrame, int32_t adjustment); michael@0: nsresult GetMarkupDocumentViewer(nsIMarkupDocumentViewer** aMv); michael@0: nsresult ChangeTextSize(int32_t change); michael@0: nsresult ChangeFullZoom(int32_t change); michael@0: michael@0: /** michael@0: * DeltaAccumulator class manages delta values for dispatching DOMMouseScroll michael@0: * event. If wheel events are caused by pixel scroll only devices or michael@0: * the delta values are customized by prefs, this class stores the delta michael@0: * values and set lineOrPageDelta values. michael@0: */ michael@0: class DeltaAccumulator michael@0: { michael@0: public: michael@0: static DeltaAccumulator* GetInstance() michael@0: { michael@0: if (!sInstance) { michael@0: sInstance = new DeltaAccumulator; michael@0: } michael@0: return sInstance; michael@0: } michael@0: michael@0: static void Shutdown() michael@0: { michael@0: delete sInstance; michael@0: sInstance = nullptr; michael@0: } michael@0: michael@0: bool IsInTransaction() { return mHandlingDeltaMode != UINT32_MAX; } michael@0: michael@0: /** michael@0: * InitLineOrPageDelta() stores pixel delta values of WidgetWheelEvents michael@0: * which are caused if it's needed. And if the accumulated delta becomes a michael@0: * line height, sets lineOrPageDeltaX and lineOrPageDeltaY automatically. michael@0: */ michael@0: void InitLineOrPageDelta(nsIFrame* aTargetFrame, michael@0: EventStateManager* aESM, michael@0: WidgetWheelEvent* aEvent); michael@0: michael@0: /** michael@0: * Reset() resets all members. michael@0: */ michael@0: void Reset(); michael@0: michael@0: /** michael@0: * ComputeScrollAmountForDefaultAction() computes the default action's michael@0: * scroll amount in device pixels with mPendingScrollAmount*. michael@0: */ michael@0: nsIntPoint ComputeScrollAmountForDefaultAction( michael@0: WidgetWheelEvent* aEvent, michael@0: const nsIntSize& aScrollAmountInDevPixels); michael@0: michael@0: private: michael@0: DeltaAccumulator() : michael@0: mX(0.0), mY(0.0), mPendingScrollAmountX(0.0), mPendingScrollAmountY(0.0), michael@0: mHandlingDeltaMode(UINT32_MAX), mHandlingPixelOnlyDevice(false) michael@0: { michael@0: } michael@0: michael@0: double mX; michael@0: double mY; michael@0: michael@0: // When default action of a wheel event is scroll but some delta values michael@0: // are ignored because the computed amount values are not integer, the michael@0: // fractional values are saved by these members. michael@0: double mPendingScrollAmountX; michael@0: double mPendingScrollAmountY; michael@0: michael@0: TimeStamp mLastTime; michael@0: michael@0: uint32_t mHandlingDeltaMode; michael@0: bool mHandlingPixelOnlyDevice; michael@0: michael@0: static DeltaAccumulator* sInstance; michael@0: }; michael@0: michael@0: // end mousewheel functions michael@0: michael@0: /* michael@0: * When a touch gesture is about to start, this function determines what michael@0: * kind of gesture interaction we will want to use, based on what is michael@0: * underneath the initial touch point. michael@0: * Currently it decides between panning (finger scrolling) or dragging michael@0: * the target element, as well as the orientation to trigger panning and michael@0: * display visual boundary feedback. The decision is stored back in aEvent. michael@0: */ michael@0: void DecideGestureEvent(WidgetGestureNotifyEvent* aEvent, michael@0: nsIFrame* targetFrame); michael@0: michael@0: // routines for the d&d gesture tracking state machine michael@0: void BeginTrackingDragGesture(nsPresContext* aPresContext, michael@0: WidgetMouseEvent* aDownEvent, michael@0: nsIFrame* aDownFrame); michael@0: void StopTrackingDragGesture(); michael@0: void GenerateDragGesture(nsPresContext* aPresContext, michael@0: WidgetMouseEvent* aEvent); michael@0: michael@0: /** michael@0: * Determine which node the drag should be targeted at. michael@0: * This is either the node clicked when there is a selection, or, for HTML, michael@0: * the element with a draggable property set to true. michael@0: * michael@0: * aSelectionTarget - target to check for selection michael@0: * aDataTransfer - data transfer object that will contain the data to drag michael@0: * aSelection - [out] set to the selection to be dragged michael@0: * aTargetNode - [out] the draggable node, or null if there isn't one michael@0: */ michael@0: void DetermineDragTarget(nsPIDOMWindow* aWindow, michael@0: nsIContent* aSelectionTarget, michael@0: dom::DataTransfer* aDataTransfer, michael@0: nsISelection** aSelection, michael@0: nsIContent** aTargetNode); michael@0: michael@0: /* michael@0: * Perform the default handling for the dragstart/draggesture event and set up a michael@0: * drag for aDataTransfer if it contains any data. Returns true if a drag has michael@0: * started. michael@0: * michael@0: * aDragEvent - the dragstart/draggesture event michael@0: * aDataTransfer - the data transfer that holds the data to be dragged michael@0: * aDragTarget - the target of the drag michael@0: * aSelection - the selection to be dragged michael@0: */ michael@0: bool DoDefaultDragStart(nsPresContext* aPresContext, michael@0: WidgetDragEvent* aDragEvent, michael@0: dom::DataTransfer* aDataTransfer, michael@0: nsIContent* aDragTarget, michael@0: nsISelection* aSelection); michael@0: michael@0: bool IsTrackingDragGesture ( ) const { return mGestureDownContent != nullptr; } michael@0: /** michael@0: * Set the fields of aEvent to reflect the mouse position and modifier keys michael@0: * that were set when the user first pressed the mouse button (stored by michael@0: * BeginTrackingDragGesture). aEvent->widget must be michael@0: * mCurrentTarget->GetNearestWidget(). michael@0: */ michael@0: void FillInEventFromGestureDown(WidgetMouseEvent* aEvent); michael@0: michael@0: nsresult DoContentCommandEvent(WidgetContentCommandEvent* aEvent); michael@0: nsresult DoContentCommandScrollEvent(WidgetContentCommandEvent* aEvent); michael@0: michael@0: void DoQuerySelectedText(WidgetQueryContentEvent* aEvent); michael@0: michael@0: bool RemoteQueryContentEvent(WidgetEvent* aEvent); michael@0: dom::TabParent *GetCrossProcessTarget(); michael@0: bool IsTargetCrossProcess(WidgetGUIEvent* aEvent); michael@0: michael@0: bool DispatchCrossProcessEvent(WidgetEvent* aEvent, michael@0: nsFrameLoader* aRemote, michael@0: nsEventStatus *aStatus); michael@0: bool HandleCrossProcessEvent(WidgetEvent* aEvent, michael@0: nsIFrame* aTargetFrame, michael@0: nsEventStatus* aStatus); michael@0: michael@0: void ReleaseCurrentIMEContentObserver(); michael@0: michael@0: private: michael@0: static inline void DoStateChange(dom::Element* aElement, michael@0: EventStates aState, bool aAddState); michael@0: static inline void DoStateChange(nsIContent* aContent, EventStates aState, michael@0: bool aAddState); michael@0: static void UpdateAncestorState(nsIContent* aStartNode, michael@0: nsIContent* aStopBefore, michael@0: EventStates aState, michael@0: bool aAddState); michael@0: static PLDHashOperator ResetLastOverForContent(const uint32_t& aIdx, michael@0: nsRefPtr& aChunk, michael@0: void* aClosure); michael@0: michael@0: int32_t mLockCursor; michael@0: michael@0: // Last mouse event refPoint (the offset from the widget's origin in michael@0: // device pixels) when mouse was locked, used to restore mouse position michael@0: // after unlocking. michael@0: LayoutDeviceIntPoint mPreLockPoint; michael@0: michael@0: // Stores the refPoint of the last synthetic mouse move we dispatched michael@0: // to re-center the mouse when we were pointer locked. If this is (-1,-1) it michael@0: // means we've not recently dispatched a centering event. We use this to michael@0: // detect when we receive the synth event, so we can cancel and not send it michael@0: // to content. michael@0: static LayoutDeviceIntPoint sSynthCenteringPoint; michael@0: michael@0: nsWeakFrame mCurrentTarget; michael@0: nsCOMPtr mCurrentTargetContent; michael@0: static nsWeakFrame sLastDragOverFrame; michael@0: michael@0: // Stores the refPoint (the offset from the widget's origin in device michael@0: // pixels) of the last mouse event. michael@0: static LayoutDeviceIntPoint sLastRefPoint; michael@0: michael@0: // member variables for the d&d gesture state machine michael@0: LayoutDeviceIntPoint mGestureDownPoint; // screen coordinates michael@0: // The content to use as target if we start a d&d (what we drag). michael@0: nsCOMPtr mGestureDownContent; michael@0: // The content of the frame where the mouse-down event occurred. It's the same michael@0: // as the target in most cases but not always - for example when dragging michael@0: // an of an image map this is the image. (bug 289667) michael@0: nsCOMPtr mGestureDownFrameOwner; michael@0: // State of keys when the original gesture-down happened michael@0: Modifiers mGestureModifiers; michael@0: uint16_t mGestureDownButtons; michael@0: michael@0: nsCOMPtr mLastLeftMouseDownContent; michael@0: nsCOMPtr mLastLeftMouseDownContentParent; michael@0: nsCOMPtr mLastMiddleMouseDownContent; michael@0: nsCOMPtr mLastMiddleMouseDownContentParent; michael@0: nsCOMPtr mLastRightMouseDownContent; michael@0: nsCOMPtr mLastRightMouseDownContentParent; michael@0: michael@0: nsCOMPtr mActiveContent; michael@0: nsCOMPtr mHoverContent; michael@0: static nsCOMPtr sDragOverContent; michael@0: nsCOMPtr mURLTargetContent; michael@0: michael@0: nsPresContext* mPresContext; // Not refcnted michael@0: nsCOMPtr mDocument; // Doesn't necessarily need to be owner michael@0: michael@0: nsRefPtr mIMEContentObserver; michael@0: michael@0: uint32_t mLClickCount; michael@0: uint32_t mMClickCount; michael@0: uint32_t mRClickCount; michael@0: michael@0: bool m_haveShutdown; michael@0: michael@0: // Time at which we began handling user input. michael@0: static TimeStamp sHandlingInputStart; michael@0: michael@0: nsRefPtr mMouseEnterLeaveHelper; michael@0: nsRefPtrHashtable mPointersEnterLeaveHelper; michael@0: michael@0: public: michael@0: static nsresult UpdateUserActivityTimer(void); michael@0: // Array for accesskey support michael@0: nsCOMArray mAccessKeys; michael@0: michael@0: static int32_t sUserInputEventDepth; michael@0: michael@0: static bool sNormalLMouseEventInProcess; michael@0: michael@0: static EventStateManager* sActiveESM; michael@0: michael@0: static void ClearGlobalActiveContent(EventStateManager* aClearer); michael@0: michael@0: // Functions used for click hold context menus michael@0: nsCOMPtr mClickHoldTimer; michael@0: void CreateClickHoldTimer(nsPresContext* aPresContext, michael@0: nsIFrame* aDownFrame, michael@0: WidgetGUIEvent* aMouseDownEvent); michael@0: void KillClickHoldTimer(); michael@0: void FireContextClick(); michael@0: michael@0: void SetPointerLock(nsIWidget* aWidget, nsIContent* aElement) ; michael@0: static void sClickHoldCallback ( nsITimer* aTimer, void* aESM ) ; michael@0: }; michael@0: michael@0: /** michael@0: * This class is used while processing real user input. During this time, popups michael@0: * are allowed. For mousedown events, mouse capturing is also permitted. michael@0: */ michael@0: class AutoHandlingUserInputStatePusher michael@0: { michael@0: public: michael@0: AutoHandlingUserInputStatePusher(bool aIsHandlingUserInput, michael@0: WidgetEvent* aEvent, michael@0: nsIDocument* aDocument); michael@0: ~AutoHandlingUserInputStatePusher(); michael@0: michael@0: protected: michael@0: bool mIsHandlingUserInput; michael@0: bool mIsMouseDown; michael@0: bool mResetFMMouseDownState; michael@0: michael@0: private: michael@0: // Hide so that this class can only be stack-allocated michael@0: static void* operator new(size_t /*size*/) CPP_THROW_NEW { return nullptr; } michael@0: static void operator delete(void* /*memory*/) {} michael@0: }; michael@0: michael@0: } // namespace mozilla michael@0: michael@0: // Click and double-click events need to be handled even for content that michael@0: // has no frame. This is required for Web compatibility. michael@0: #define NS_EVENT_NEEDS_FRAME(event) \ michael@0: (!(event)->HasPluginActivationEventMessage() && \ michael@0: (event)->message != NS_MOUSE_CLICK && \ michael@0: (event)->message != NS_MOUSE_DOUBLECLICK) michael@0: michael@0: #endif // mozilla_EventStateManager_h_