/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_layers_InputQueue_h #define mozilla_layers_InputQueue_h #include "APZUtils.h" #include "DragTracker.h" #include "InputData.h" #include "mozilla/EventForwards.h" #include "mozilla/layers/TouchCounter.h" #include "mozilla/RefPtr.h" #include "mozilla/UniquePtr.h" #include "nsTArray.h" #include namespace mozilla { class InputData; class MultiTouchInput; class ScrollWheelInput; namespace layers { class AsyncPanZoomController; class InputBlockState; class CancelableBlockState; class TouchBlockState; class WheelBlockState; class DragBlockState; class PanGestureBlockState; class PinchGestureBlockState; class KeyboardBlockState; class AsyncDragMetrics; class QueuedInput; struct APZEventResult; struct APZHandledResult; enum class BrowserGestureResponse : bool; using InputBlockCallback = std::function; struct InputBlockCallbackInfo { nsEventStatus mEagerStatus; InputBlockCallback mCallback; }; /** * This class stores incoming input events, associated with "input blocks", * until they are ready for handling. */ class InputQueue { NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InputQueue) public: InputQueue(); /** * Notifies the InputQueue of a new incoming input event. The APZC that the * input event was targeted to should be provided in the |aTarget| parameter. * See the documentation on APZCTreeManager::ReceiveInputEvent for info on * return values from this function. */ APZEventResult ReceiveInputEvent( const RefPtr& aTarget, TargetConfirmationFlags aFlags, InputData& aEvent, const Maybe>& aTouchBehaviors = Nothing()); /** * This function should be invoked to notify the InputQueue when web content * decides whether or not it wants to cancel a block of events. The block * id to which this applies should be provided in |aInputBlockId|. */ void ContentReceivedInputBlock(uint64_t aInputBlockId, bool aPreventDefault); /** * This function should be invoked to notify the InputQueue once the target * APZC to handle an input block has been confirmed. In practice this should * generally be decidable upon receipt of the input event, but in some cases * we may need to query the layout engine to know for sure. The input block * this applies to should be specified via the |aInputBlockId| parameter. */ void SetConfirmedTargetApzc( uint64_t aInputBlockId, const RefPtr& aTargetApzc); /** * This function is invoked to confirm that the drag block should be handled * by the APZ. */ void ConfirmDragBlock(uint64_t aInputBlockId, const RefPtr& aTargetApzc, const AsyncDragMetrics& aDragMetrics); /** * This function should be invoked to notify the InputQueue of the touch- * action properties for the different touch points in an input block. The * input block this applies to should be specified by the |aInputBlockId| * parameter. If touch-action is not enabled on the platform, this function * does nothing and need not be called. */ void SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray& aBehaviors); /** * Adds a new touch block at the end of the input queue that has the same * allowed touch behaviour flags as the the touch block currently being * processed. This should only be called when processing of a touch block * triggers the creation of a new touch block. Returns the input block id * of the the newly-created block. */ uint64_t InjectNewTouchBlock(AsyncPanZoomController* aTarget); /** * Returns the pending input block at the head of the queue, if there is one. * This may return null if there all input events have been processed. */ InputBlockState* GetCurrentBlock() const; /* * Returns the current pending input block as a specific kind of block. If * GetCurrentBlock() returns null, these functions additionally check the * mActiveXXXBlock field of the corresponding input type to see if there is * a depleted but still active input block, and returns that if found. These * functions may return null if no block is found. */ TouchBlockState* GetCurrentTouchBlock() const; WheelBlockState* GetCurrentWheelBlock() const; DragBlockState* GetCurrentDragBlock() const; PanGestureBlockState* GetCurrentPanGestureBlock() const; PinchGestureBlockState* GetCurrentPinchGestureBlock() const; KeyboardBlockState* GetCurrentKeyboardBlock() const; /** * Returns true iff the pending block at the head of the queue is a touch * block and is ready for handling. */ bool HasReadyTouchBlock() const; /** * If there is an active wheel transaction, returns the WheelBlockState * representing the transaction. Otherwise, returns null. "Active" in this * function name is the same kind of "active" as in mActiveWheelBlock - that * is, new incoming wheel events will go into the "active" block. */ WheelBlockState* GetActiveWheelTransaction() const; /** * Remove all input blocks from the input queue. */ void Clear(); /** * Whether the current pending block allows scroll handoff. */ bool AllowScrollHandoff() const; /** * If there is currently a drag in progress, return whether or not it was * targeted at a scrollbar. If the drag was newly-created and doesn't know, * use the provided |aOnScrollbar| to populate that information. */ bool IsDragOnScrollbar(bool aOnScrollbar); InputBlockState* GetBlockForId(uint64_t aInputBlockId); void AddInputBlockCallback(uint64_t aInputBlockId, InputBlockCallbackInfo&& aCallback); void SetBrowserGestureResponse(uint64_t aInputBlockId, BrowserGestureResponse aResponse); private: ~InputQueue(); // RAII class for automatically running a timeout task that may // need to be run immediately after an event has been queued. class AutoRunImmediateTimeout final { public: explicit AutoRunImmediateTimeout(InputQueue* aQueue); ~AutoRunImmediateTimeout(); private: InputQueue* mQueue; }; TouchBlockState* StartNewTouchBlock( const RefPtr& aTarget, TargetConfirmationFlags aFlags); TouchBlockState* StartNewTouchBlockForLongTap( const RefPtr& aTarget); /** * If animations are present for the current pending input block, cancel * them as soon as possible. */ void CancelAnimationsForNewBlock(InputBlockState* aBlock, CancelAnimationFlags aExtraFlags = Default); /** * If we need to wait for a content response, schedule that now. Returns true * if the timeout was scheduled, false otherwise. */ bool MaybeRequestContentResponse( const RefPtr& aTarget, CancelableBlockState* aBlock); APZEventResult ReceiveTouchInput( const RefPtr& aTarget, TargetConfirmationFlags aFlags, const MultiTouchInput& aEvent, const Maybe>& aTouchBehaviors); APZEventResult ReceiveMouseInput( const RefPtr& aTarget, TargetConfirmationFlags aFlags, MouseInput& aEvent); APZEventResult ReceiveScrollWheelInput( const RefPtr& aTarget, TargetConfirmationFlags aFlags, const ScrollWheelInput& aEvent); APZEventResult ReceivePanGestureInput( const RefPtr& aTarget, TargetConfirmationFlags aFlags, const PanGestureInput& aEvent); APZEventResult ReceivePinchGestureInput( const RefPtr& aTarget, TargetConfirmationFlags aFlags, const PinchGestureInput& aEvent); APZEventResult ReceiveKeyboardInput( const RefPtr& aTarget, TargetConfirmationFlags aFlags, const KeyboardInput& aEvent); /** * Helper function that searches mQueuedInputs for the first block matching * the given id, and returns it. If |aOutFirstInput| is non-null, it is * populated with a pointer to the first input in mQueuedInputs that * corresponds to the block, or null if no such input was found. Note that * even if there are no inputs in mQueuedInputs, this function can return * non-null if the block id provided matches one of the depleted-but-still- * active blocks (mActiveTouchBlock, mActiveWheelBlock, etc.). */ InputBlockState* FindBlockForId(uint64_t aInputBlockId, InputData** aOutFirstInput); void ScheduleMainThreadTimeout(const RefPtr& aTarget, CancelableBlockState* aBlock); void MainThreadTimeout(uint64_t aInputBlockId); void MaybeLongTapTimeout(uint64_t aInputBlockId); // Returns true if there's one more queued event we need to process as a // result of switching the active block back to the original touch block from // the touch block for long-tap. bool ProcessQueue(); bool CanDiscardBlock(InputBlockState* aBlock); void UpdateActiveApzc(const RefPtr& aNewActive); private: // The queue of input events that have not yet been fully processed. // This member must only be accessed on the controller/UI thread. nsTArray> mQueuedInputs; // These are the most recently created blocks of each input type. They are // "active" in the sense that new inputs of that type are associated with // them. Note that these pointers may be null if no inputs of the type have // arrived, or if the inputs for the type formed a complete block that was // then discarded. RefPtr mActiveTouchBlock; RefPtr mActiveWheelBlock; RefPtr mActiveDragBlock; RefPtr mActivePanGestureBlock; RefPtr mActivePinchGestureBlock; RefPtr mActiveKeyboardBlock; // In the case where a long-tap event triggered by keeping touching happens // we need to keep both the touch block for the long-tap and the original // touch block started with `touch-start`. This value holds the original block // until the long-tap block is processed. RefPtr mPrevActiveTouchBlock; // The APZC to which the last event was delivered RefPtr mLastActiveApzc; // Track touches so we know when to clear mLastActiveApzc TouchCounter mTouchCounter; // Track mouse inputs so we know if we're in a drag or not DragTracker mDragTracker; // Temporarily stores a timeout task that needs to be run as soon as // as the event that triggered it has been queued. RefPtr mImmediateTimeout; // Maps input block ids to callbacks that will be invoked when the input block // is ready for handling. using InputBlockCallbackMap = std::unordered_map; InputBlockCallbackMap mInputBlockCallbacks; }; } // namespace layers } // namespace mozilla #endif // mozilla_layers_InputQueue_h