diff options
Diffstat (limited to 'gfx/layers/apz/src/APZCTreeManager.h')
-rw-r--r-- | gfx/layers/apz/src/APZCTreeManager.h | 531 |
1 files changed, 531 insertions, 0 deletions
diff --git a/gfx/layers/apz/src/APZCTreeManager.h b/gfx/layers/apz/src/APZCTreeManager.h new file mode 100644 index 000000000..c98e292ef --- /dev/null +++ b/gfx/layers/apz/src/APZCTreeManager.h @@ -0,0 +1,531 @@ +/* -*- Mode: C++; tab-width: 8; 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_layers_APZCTreeManager_h +#define mozilla_layers_APZCTreeManager_h + +#include <map> // for std::map + +#include "gfxPoint.h" // for gfxPoint +#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2 +#include "mozilla/gfx/Logging.h" // for gfx::TreeLog +#include "mozilla/gfx/Matrix.h" // for Matrix4x4 +#include "mozilla/layers/TouchCounter.h"// for TouchCounter +#include "mozilla/layers/IAPZCTreeManager.h" // for IAPZCTreeManager +#include "mozilla/Mutex.h" // for Mutex +#include "mozilla/RefPtr.h" // for RefPtr +#include "mozilla/TimeStamp.h" // for mozilla::TimeStamp +#include "nsCOMPtr.h" // for already_AddRefed + + +namespace mozilla { +class MultiTouchInput; + +namespace layers { + +class Layer; +class AsyncPanZoomController; +class APZCTreeManagerParent; +class CompositorBridgeParent; +class OverscrollHandoffChain; +struct OverscrollHandoffState; +struct FlingHandoffState; +class LayerMetricsWrapper; +class InputQueue; +class GeckoContentController; +class HitTestingTreeNode; + +/** + * ****************** NOTE ON LOCK ORDERING IN APZ ************************** + * + * There are two kinds of locks used by APZ: APZCTreeManager::mTreeLock + * ("the tree lock") and AsyncPanZoomController::mMonitor ("APZC locks"). + * + * To avoid deadlock, we impose a lock ordering between these locks, which is: + * + * tree lock -> APZC locks + * + * The interpretation of the lock ordering is that if lock A precedes lock B + * in the ordering sequence, then you must NOT wait on A while holding B. + * + * ************************************************************************** + */ + +/** + * This class manages the tree of AsyncPanZoomController instances. There is one + * instance of this class owned by each CompositorBridgeParent, and it contains as + * many AsyncPanZoomController instances as there are scrollable container layers. + * This class generally lives on the compositor thread, although some functions + * may be called from other threads as noted; thread safety is ensured internally. + * + * The bulk of the work of this class happens as part of the UpdateHitTestingTree + * function, which is when a layer tree update is received by the compositor. + * This function walks through the layer tree and creates a tree of + * HitTestingTreeNode instances to match the layer tree and for use in + * hit-testing on the controller thread. APZC instances may be preserved across + * calls to this function if the corresponding layers are still present in the layer + * tree. + * + * The other functions on this class are used by various pieces of client code to + * notify the APZC instances of events relevant to them. This includes, for example, + * user input events that drive panning and zooming, changes to the scroll viewport + * area, and changes to pan/zoom constraints. + * + * Note that the ClearTree function MUST be called when this class is no longer needed; + * see the method documentation for details. + * + * Behaviour of APZ is controlled by a number of preferences shown \ref APZCPrefs "here". + */ +class APZCTreeManager : public IAPZCTreeManager { + + typedef mozilla::layers::AllowedTouchBehavior AllowedTouchBehavior; + typedef mozilla::layers::AsyncDragMetrics AsyncDragMetrics; + + // Helper struct to hold some state while we build the hit-testing tree. The + // sole purpose of this struct is to shorten the argument list to + // UpdateHitTestingTree. All the state that we don't need to + // push on the stack during recursion and pop on unwind is stored here. + struct TreeBuildingState; + +public: + APZCTreeManager(); + + /** + * Initializes the global state used in AsyncPanZoomController. + * This is normally called when it is first needed in the constructor + * of APZCTreeManager, but can be called manually to force it to be + * initialized earlier. + */ + static void InitializeGlobalState(); + + /** + * Rebuild the hit-testing tree based on the layer update that just came up. + * Preserve nodes and APZC instances where possible, but retire those whose + * layers are no longer in the layer tree. + * + * This must be called on the compositor thread as it walks the layer tree. + * + * @param aRootLayerTreeId The layer tree ID of the root layer corresponding + * to this APZCTreeManager + * @param aRoot The root of the (full) layer tree + * @param aFirstPaintLayersId The layers id of the subtree to which aIsFirstPaint + * applies. + * @param aIsFirstPaint True if the layers update that this is called in response + * to included a first-paint. If this is true, the part of + * the tree that is affected by the first-paint flag is + * indicated by the aFirstPaintLayersId parameter. + * @param aPaintSequenceNumber The sequence number of the paint that triggered + * this layer update. Note that every layer child + * process' layer subtree has its own sequence + * numbers. + */ + void UpdateHitTestingTree(uint64_t aRootLayerTreeId, + Layer* aRoot, + bool aIsFirstPaint, + uint64_t aOriginatingLayersId, + uint32_t aPaintSequenceNumber); + + /** + * Walk the tree of APZCs and flushes the repaint requests for all the APZCS + * corresponding to the given layers id. Finally, sends a flush complete + * notification to the GeckoContentController for the layers id. + */ + void FlushApzRepaints(uint64_t aLayersId); + + /** + * General handler for incoming input events. Manipulates the frame metrics + * based on what type of input it is. For example, a PinchGestureEvent will + * cause scaling. This should only be called externally to this class, and + * must be called on the controller thread. + * + * This function transforms |aEvent| to have its coordinates in DOM space. + * This is so that the event can be passed through the DOM and content can + * handle them. The event may need to be converted to a WidgetInputEvent + * by the caller if it wants to do this. + * + * The following values may be returned by this function: + * nsEventStatus_eConsumeNoDefault is returned to indicate the + * APZ is consuming this event and the caller should discard the event with + * extreme prejudice. The exact scenarios under which this is returned is + * implementation-dependent and may vary. + * nsEventStatus_eIgnore is returned to indicate that the APZ code didn't + * use this event. This might be because it was directed at a point on + * the screen where there was no APZ, or because the thing the user was + * trying to do was not allowed. (For example, attempting to pan a + * non-pannable document). + * nsEventStatus_eConsumeDoDefault is returned to indicate that the APZ + * code may have used this event to do some user-visible thing. Note that + * in some cases CONSUMED is returned even if the event was NOT used. This + * is because we cannot always know at the time of event delivery whether + * the event will be used or not. So we err on the side of sending + * CONSUMED when we are uncertain. + * + * @param aEvent input event object; is modified in-place + * @param aOutTargetGuid returns the guid of the apzc this event was + * delivered to. May be null. + * @param aOutInputBlockId returns the id of the input block that this event + * was added to, if that was the case. May be null. + */ + nsEventStatus ReceiveInputEvent( + InputData& aEvent, + ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutInputBlockId) override; + + /** + * Kicks an animation to zoom to a rect. This may be either a zoom out or zoom + * in. The actual animation is done on the compositor thread after being set + * up. |aRect| must be given in CSS pixels, relative to the document. + * |aFlags| is a combination of the ZoomToRectBehavior enum values. + */ + void ZoomToRect( + const ScrollableLayerGuid& aGuid, + const CSSRect& aRect, + const uint32_t aFlags = DEFAULT_BEHAVIOR) override; + + /** + * If we have touch listeners, this should always be called when we know + * definitively whether or not content has preventDefaulted any touch events + * that have come in. If |aPreventDefault| is true, any touch events in the + * queue will be discarded. This function must be called on the controller + * thread. + */ + void ContentReceivedInputBlock( + uint64_t aInputBlockId, + bool aPreventDefault) override; + + /** + * When the event regions code is enabled, this function should be invoked to + * to confirm the target of the input block. This is only needed in cases + * where the initial input event of the block hit a dispatch-to-content region + * but is safe to call for all input blocks. This function should always be + * invoked on the controller thread. + * The different elements in the array of targets correspond to the targets + * for the different touch points. In the case where the touch point has no + * target, or the target is not a scrollable frame, the target's |mScrollId| + * should be set to FrameMetrics::NULL_SCROLL_ID. + */ + void SetTargetAPZC( + uint64_t aInputBlockId, + const nsTArray<ScrollableLayerGuid>& aTargets) override; + + /** + * Helper function for SetTargetAPZC when used with single-target events, + * such as mouse wheel events. + */ + void SetTargetAPZC(uint64_t aInputBlockId, const ScrollableLayerGuid& aTarget); + + /** + * Updates any zoom constraints contained in the <meta name="viewport"> tag. + * If the |aConstraints| is Nothing() then previously-provided constraints for + * the given |aGuid| are cleared. + */ + void UpdateZoomConstraints( + const ScrollableLayerGuid& aGuid, + const Maybe<ZoomConstraints>& aConstraints) override; + + /** + * Cancels any currently running animation. Note that all this does is set the + * state of the AsyncPanZoomController back to NOTHING, but it is the + * animation's responsibility to check this before advancing. + */ + void CancelAnimation(const ScrollableLayerGuid &aGuid) override; + + /** + * Adjusts the root APZC to compensate for a shift in the surface. See the + * documentation on AsyncPanZoomController::AdjustScrollForSurfaceShift for + * some more details. This is only currently needed due to surface shifts + * caused by the dynamic toolbar on Android. + */ + void AdjustScrollForSurfaceShift(const ScreenPoint& aShift) override; + + /** + * Calls Destroy() on all APZC instances attached to the tree, and resets the + * tree back to empty. This function must be called exactly once during the + * lifetime of this APZCTreeManager, when this APZCTreeManager is no longer + * needed. Failing to call this function may prevent objects from being freed + * properly. + */ + void ClearTree(); + + /** + * Tests if a screen point intersect an apz in the tree. + */ + bool HitTestAPZC(const ScreenIntPoint& aPoint); + + /** + * See AsyncPanZoomController::CalculatePendingDisplayPort. This + * function simply delegates to that one, so that non-layers code + * never needs to include AsyncPanZoomController.h + */ + static const ScreenMargin CalculatePendingDisplayPort( + const FrameMetrics& aFrameMetrics, + const ParentLayerPoint& aVelocity); + + /** + * Sets the dpi value used by all AsyncPanZoomControllers. + * DPI defaults to 72 if not set using SetDPI() at any point. + */ + void SetDPI(float aDpiValue) override { sDPI = aDpiValue; } + + /** + * Returns the current dpi value in use. + */ + static float GetDPI() { return sDPI; } + + /** + * Find the hit testing node for the scrollbar thumb that matches these + * drag metrics. + */ + RefPtr<HitTestingTreeNode> FindScrollNode(const AsyncDragMetrics& aDragMetrics); + + /** + * Sets allowed touch behavior values for current touch-session for specific + * input block (determined by aInputBlock). + * Should be invoked by the widget. Each value of the aValues arrays + * corresponds to the different touch point that is currently active. + * Must be called after receiving the TOUCH_START event that starts the + * touch-session. + * This must be called on the controller thread. + */ + void SetAllowedTouchBehavior( + uint64_t aInputBlockId, + const nsTArray<TouchBehaviorFlags>& aValues) override; + + /** + * This is a callback for AsyncPanZoomController to call when it wants to + * scroll in response to a touch-move event, or when it needs to hand off + * overscroll to the next APZC. Note that because of scroll grabbing, the + * first APZC to scroll may not be the one that is receiving the touch events. + * + * |aAPZC| is the APZC that received the touch events triggering the scroll + * (in the case of an initial scroll), or the last APZC to scroll (in the + * case of overscroll) + * |aStartPoint| and |aEndPoint| are in |aAPZC|'s transformed screen + * coordinates (i.e. the same coordinates in which touch points are given to + * APZCs). The amount of (over)scroll is represented by two points rather + * than a displacement because with certain 3D transforms, the same + * displacement between different points in transformed coordinates can + * represent different displacements in untransformed coordinates. + * |aOverscrollHandoffChain| is the overscroll handoff chain used for + * determining the order in which scroll should be handed off between + * APZCs + * |aOverscrollHandoffChainIndex| is the next position in the overscroll + * handoff chain that should be scrolled. + * + * aStartPoint and aEndPoint will be modified depending on how much of the + * scroll each APZC consumes. This is to allow the sending APZC to go into + * an overscrolled state if no APZC further up in the handoff chain accepted + * the entire scroll. + * + * The way this method works is best illustrated with an example. + * Consider three nested APZCs, A, B, and C, with C being the innermost one. + * Say B is scroll-grabbing. + * The touch events go to C because it's the innermost one (so e.g. taps + * should go through C), but the overscroll handoff chain is B -> C -> A + * because B is scroll-grabbing. + * For convenience I'll refer to the three APZC objects as A, B, and C, and + * to the tree manager object as TM. + * Here's what happens when C receives a touch-move event: + * - C.TrackTouch() calls TM.DispatchScroll() with index = 0. + * - TM.DispatchScroll() calls B.AttemptScroll() (since B is at index 0 in the chain). + * - B.AttemptScroll() scrolls B. If there is overscroll, it calls TM.DispatchScroll() with index = 1. + * - TM.DispatchScroll() calls C.AttemptScroll() (since C is at index 1 in the chain) + * - C.AttemptScroll() scrolls C. If there is overscroll, it calls TM.DispatchScroll() with index = 2. + * - TM.DispatchScroll() calls A.AttemptScroll() (since A is at index 2 in the chain) + * - A.AttemptScroll() scrolls A. If there is overscroll, it calls TM.DispatchScroll() with index = 3. + * - TM.DispatchScroll() discards the rest of the scroll as there are no more elements in the chain. + * + * Note: this should be used for panning only. For handing off overscroll for + * a fling, use DispatchFling(). + */ + void DispatchScroll(AsyncPanZoomController* aApzc, + ParentLayerPoint& aStartPoint, + ParentLayerPoint& aEndPoint, + OverscrollHandoffState& aOverscrollHandoffState); + + /** + * This is a callback for AsyncPanZoomController to call when it wants to + * start a fling in response to a touch-end event, or when it needs to hand + * off a fling to the next APZC. Note that because of scroll grabbing, the + * first APZC to fling may not be the one that is receiving the touch events. + * + * @param aApzc the APZC that wants to start or hand off the fling + * @param aHandoffState a collection of state about the operation, + * which contains the following: + * + * mVelocity the current velocity of the fling, in |aApzc|'s screen + * pixels per millisecond + * mChain the chain of APZCs along which the fling + * should be handed off + * mIsHandoff is true if |aApzc| is handing off an existing fling (in + * this case the fling is given to the next APZC in the + * handoff chain after |aApzc|), and false is |aApzc| wants + * start a fling (in this case the fling is given to the + * first APZC in the chain) + * + * aHandoffState.mVelocity will be modified depending on how much of that + * velocity has been consumed by APZCs in the overscroll hand-off chain. + * The caller can use this value to determine whether it should consume + * the excess velocity by going into an overscroll fling. + */ + void DispatchFling(AsyncPanZoomController* aApzc, + FlingHandoffState& aHandoffState); + + void StartScrollbarDrag( + const ScrollableLayerGuid& aGuid, + const AsyncDragMetrics& aDragMetrics) override; + + /* + * Build the chain of APZCs that will handle overscroll for a pan starting at |aInitialTarget|. + */ + RefPtr<const OverscrollHandoffChain> BuildOverscrollHandoffChain(const RefPtr<AsyncPanZoomController>& aInitialTarget); + + /** + * Function used to disable LongTap gestures. + * + * On slow running tests, drags and touch events can be misinterpreted + * as a long tap. This allows tests to disable long tap gesture detection. + */ + void SetLongTapEnabled(bool aTapGestureEnabled) override; + + // Methods to help process WidgetInputEvents (or manage conversion to/from InputData) + + void TransformEventRefPoint( + LayoutDeviceIntPoint* aRefPoint, + ScrollableLayerGuid* aOutTargetGuid) override; + + void UpdateWheelTransaction( + LayoutDeviceIntPoint aRefPoint, + EventMessage aEventMessage) override; + +protected: + // Protected destructor, to discourage deletion outside of Release(): + virtual ~APZCTreeManager(); + + // Protected hooks for gtests subclass + virtual AsyncPanZoomController* NewAPZCInstance(uint64_t aLayersId, + GeckoContentController* aController); +public: + // Public hooks for gtests subclass + virtual TimeStamp GetFrameTime(); + +public: + /* Some helper functions to find an APZC given some identifying input. These functions + lock the tree of APZCs while they find the right one, and then return an addref'd + pointer to it. This allows caller code to just use the target APZC without worrying + about it going away. These are public for testing code and generally should not be + used by other production code. + */ + RefPtr<HitTestingTreeNode> GetRootNode() const; + already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScreenPoint& aPoint, + HitTestResult* aOutHitResult, + bool* aOutHitScrollbar = nullptr); + ScreenToParentLayerMatrix4x4 GetScreenToApzcTransform(const AsyncPanZoomController *aApzc) const; + ParentLayerToScreenMatrix4x4 GetApzcToGeckoTransform(const AsyncPanZoomController *aApzc) const; + + /** + * Process touch velocity. + * Sometimes the touch move event will have a velocity even though no scrolling + * is occurring such as when the toolbar is being hidden/shown in Fennec. + * This function can be called to have the y axis' velocity queue updated. + */ + void ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY) override; +private: + typedef bool (*GuidComparator)(const ScrollableLayerGuid&, const ScrollableLayerGuid&); + + /* Helpers */ + void AttachNodeToTree(HitTestingTreeNode* aNode, + HitTestingTreeNode* aParent, + HitTestingTreeNode* aNextSibling); + already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScrollableLayerGuid& aGuid); + already_AddRefed<HitTestingTreeNode> GetTargetNode(const ScrollableLayerGuid& aGuid, + GuidComparator aComparator); + HitTestingTreeNode* FindTargetNode(HitTestingTreeNode* aNode, + const ScrollableLayerGuid& aGuid, + GuidComparator aComparator); + AsyncPanZoomController* GetTargetApzcForNode(HitTestingTreeNode* aNode); + AsyncPanZoomController* GetAPZCAtPoint(HitTestingTreeNode* aNode, + const ParentLayerPoint& aHitTestPoint, + HitTestResult* aOutHitResult, + bool* aOutHitScrollbar); + AsyncPanZoomController* FindRootApzcForLayersId(uint64_t aLayersId) const; + AsyncPanZoomController* FindRootContentApzcForLayersId(uint64_t aLayersId) const; + AsyncPanZoomController* FindRootContentOrRootApzc() const; + already_AddRefed<AsyncPanZoomController> GetMultitouchTarget(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const; + already_AddRefed<AsyncPanZoomController> CommonAncestor(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const; + already_AddRefed<AsyncPanZoomController> GetTouchInputBlockAPZC(const MultiTouchInput& aEvent, + nsTArray<TouchBehaviorFlags>* aOutTouchBehaviors, + HitTestResult* aOutHitResult); + nsEventStatus ProcessTouchInput(MultiTouchInput& aInput, + ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutInputBlockId); + void FlushRepaintsToClearScreenToGeckoTransform(); + + already_AddRefed<HitTestingTreeNode> RecycleOrCreateNode(TreeBuildingState& aState, + AsyncPanZoomController* aApzc, + uint64_t aLayersId); + HitTestingTreeNode* PrepareNodeForLayer(const LayerMetricsWrapper& aLayer, + const FrameMetrics& aMetrics, + uint64_t aLayersId, + const gfx::Matrix4x4& aAncestorTransform, + HitTestingTreeNode* aParent, + HitTestingTreeNode* aNextSibling, + TreeBuildingState& aState); + + void PrintAPZCInfo(const LayerMetricsWrapper& aLayer, + const AsyncPanZoomController* apzc); + +protected: + /* The input queue where input events are held until we know enough to + * figure out where they're going. Protected so gtests can access it. + */ + RefPtr<InputQueue> mInputQueue; + +private: + /* Whenever walking or mutating the tree rooted at mRootNode, mTreeLock must be held. + * This lock does not need to be held while manipulating a single APZC instance in + * isolation (that is, if its tree pointers are not being accessed or mutated). The + * lock also needs to be held when accessing the mRootNode instance variable, as that + * is considered part of the APZC tree management state. + * Finally, the lock needs to be held when accessing mZoomConstraints. + * IMPORTANT: See the note about lock ordering at the top of this file. */ + mutable mozilla::Mutex mTreeLock; + RefPtr<HitTestingTreeNode> mRootNode; + /* Holds the zoom constraints for scrollable layers, as determined by the + * the main-thread gecko code. */ + std::map<ScrollableLayerGuid, ZoomConstraints> mZoomConstraints; + /* This tracks the APZC that should receive all inputs for the current input event block. + * This allows touch points to move outside the thing they started on, but still have the + * touch events delivered to the same initial APZC. This will only ever be touched on the + * input delivery thread, and so does not require locking. + */ + RefPtr<AsyncPanZoomController> mApzcForInputBlock; + /* The hit result for the current input event block; this should always be in + * sync with mApzcForInputBlock. + */ + HitTestResult mHitResultForInputBlock; + /* Sometimes we want to ignore all touches except one. In such cases, this + * is set to the identifier of the touch we are not ignoring; in other cases, + * this is set to -1. + */ + int32_t mRetainedTouchIdentifier; + /* Tracks the number of touch points we are tracking that are currently on + * the screen. */ + TouchCounter mTouchCounter; + /* For logging the APZC tree for debugging (enabled by the apz.printtree + * pref). */ + gfx::TreeLog mApzcTreeLog; + + class CheckerboardFlushObserver; + friend class CheckerboardFlushObserver; + RefPtr<CheckerboardFlushObserver> mFlushObserver; + + static float sDPI; +}; + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_PanZoomController_h |