michael@0: /* -*- Mode: C++; tab-width: 8; 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_layers_APZCTreeManager_h michael@0: #define mozilla_layers_APZCTreeManager_h michael@0: michael@0: #include // for uint64_t, uint32_t michael@0: #include "FrameMetrics.h" // for FrameMetrics, etc michael@0: #include "Units.h" // for CSSPoint, CSSRect, etc michael@0: #include "gfxPoint.h" // for gfxPoint michael@0: #include "gfx3DMatrix.h" // for gfx3DMatrix michael@0: #include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2 michael@0: #include "mozilla/EventForwards.h" // for WidgetInputEvent, nsEventStatus michael@0: #include "mozilla/Monitor.h" // for Monitor michael@0: #include "nsAutoPtr.h" // for nsRefPtr michael@0: #include "nsCOMPtr.h" // for already_AddRefed michael@0: #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc michael@0: #include "mozilla/Vector.h" // for mozilla::Vector michael@0: #include "nsTArrayForwardDeclare.h" // for nsTArray, nsTArray_Impl, etc michael@0: #include "mozilla/gfx/Logging.h" // for gfx::TreeLog michael@0: michael@0: class gfx3DMatrix; michael@0: michael@0: namespace mozilla { michael@0: class InputData; michael@0: michael@0: namespace layers { michael@0: michael@0: enum AllowedTouchBehavior { michael@0: NONE = 0, michael@0: VERTICAL_PAN = 1 << 0, michael@0: HORIZONTAL_PAN = 1 << 1, michael@0: PINCH_ZOOM = 1 << 2, michael@0: DOUBLE_TAP_ZOOM = 1 << 3, michael@0: UNKNOWN = 1 << 4 michael@0: }; michael@0: michael@0: class Layer; michael@0: class AsyncPanZoomController; michael@0: class CompositorParent; michael@0: michael@0: /** michael@0: * ****************** NOTE ON LOCK ORDERING IN APZ ************************** michael@0: * michael@0: * There are two kinds of locks used by APZ: APZCTreeManager::mTreeLock michael@0: * ("the tree lock") and AsyncPanZoomController::mMonitor ("APZC locks"). michael@0: * michael@0: * To avoid deadlock, we impose a lock ordering between these locks, which is: michael@0: * michael@0: * tree lock -> APZC locks michael@0: * michael@0: * The interpretation of the lock ordering is that if lock A precedes lock B michael@0: * in the ordering sequence, then you must NOT wait on A while holding B. michael@0: * michael@0: * ************************************************************************** michael@0: */ michael@0: michael@0: /** michael@0: * This class manages the tree of AsyncPanZoomController instances. There is one michael@0: * instance of this class owned by each CompositorParent, and it contains as michael@0: * many AsyncPanZoomController instances as there are scrollable container layers. michael@0: * This class generally lives on the compositor thread, although some functions michael@0: * may be called from other threads as noted; thread safety is ensured internally. michael@0: * michael@0: * The bulk of the work of this class happens as part of the UpdatePanZoomControllerTree michael@0: * function, which is when a layer tree update is received by the compositor. michael@0: * This function walks through the layer tree and creates a tree of APZC instances michael@0: * to match the scrollable container layers. APZC instances may be preserved across michael@0: * calls to this function if the corresponding layers are still present in the layer michael@0: * tree. michael@0: * michael@0: * The other functions on this class are used by various pieces of client code to michael@0: * notify the APZC instances of events relevant to them. This includes, for example, michael@0: * user input events that drive panning and zooming, changes to the scroll viewport michael@0: * area, and changes to pan/zoom constraints. michael@0: * michael@0: * Note that the ClearTree function MUST be called when this class is no longer needed; michael@0: * see the method documentation for details. michael@0: */ michael@0: class APZCTreeManager { michael@0: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(APZCTreeManager) michael@0: michael@0: typedef mozilla::layers::AllowedTouchBehavior AllowedTouchBehavior; michael@0: typedef uint32_t TouchBehaviorFlags; michael@0: michael@0: public: michael@0: APZCTreeManager(); michael@0: michael@0: /** michael@0: * Rebuild the APZC tree based on the layer update that just came up. Preserve michael@0: * APZC instances where possible, but retire those whose layers are no longer michael@0: * in the layer tree. michael@0: * michael@0: * This must be called on the compositor thread as it walks the layer tree. michael@0: * michael@0: * @param aCompositor A pointer to the compositor parent instance that owns michael@0: * this APZCTreeManager michael@0: * @param aRoot The root of the (full) layer tree michael@0: * @param aFirstPaintLayersId The layers id of the subtree to which aIsFirstPaint michael@0: * applies. michael@0: * @param aIsFirstPaint True if the layers update that this is called in response michael@0: * to included a first-paint. If this is true, the part of michael@0: * the tree that is affected by the first-paint flag is michael@0: * indicated by the aFirstPaintLayersId parameter. michael@0: */ michael@0: void UpdatePanZoomControllerTree(CompositorParent* aCompositor, Layer* aRoot, michael@0: bool aIsFirstPaint, uint64_t aFirstPaintLayersId); michael@0: michael@0: /** michael@0: * General handler for incoming input events. Manipulates the frame metrics michael@0: * based on what type of input it is. For example, a PinchGestureEvent will michael@0: * cause scaling. This should only be called externally to this class. michael@0: * michael@0: * @param aEvent input event object, will not be modified michael@0: * @param aOutTargetGuid returns the guid of the apzc this event was michael@0: * delivered to. May be null. michael@0: */ michael@0: nsEventStatus ReceiveInputEvent(const InputData& aEvent, michael@0: ScrollableLayerGuid* aOutTargetGuid); michael@0: michael@0: /** michael@0: * WidgetInputEvent handler. Transforms |aEvent| (which is assumed to be an michael@0: * already-existing instance of an WidgetInputEvent which may be an michael@0: * WidgetTouchEvent) to have its coordinates in DOM space. This is so that the michael@0: * event can be passed through the DOM and content can handle them. michael@0: * michael@0: * NOTE: Be careful of invoking the WidgetInputEvent variant. This can only be michael@0: * called on the main thread. See widget/InputData.h for more information on michael@0: * why we have InputData and WidgetInputEvent separated. michael@0: * NOTE: On unix, mouse events are treated as touch and are forwarded michael@0: * to the appropriate apz as such. michael@0: * michael@0: * @param aEvent input event object; is modified in-place michael@0: * @param aOutTargetGuid returns the guid of the apzc this event was michael@0: * delivered to. May be null. michael@0: */ michael@0: nsEventStatus ReceiveInputEvent(WidgetInputEvent& aEvent, michael@0: ScrollableLayerGuid* aOutTargetGuid); michael@0: michael@0: /** michael@0: * A helper for transforming coordinates to gecko coordinate space. michael@0: * michael@0: * @param aPoint point to transform michael@0: * @param aOutTransformedPoint resulting transformed point michael@0: */ michael@0: void TransformCoordinateToGecko(const ScreenIntPoint& aPoint, michael@0: LayoutDeviceIntPoint* aOutTransformedPoint); michael@0: michael@0: /** michael@0: * Kicks an animation to zoom to a rect. This may be either a zoom out or zoom michael@0: * in. The actual animation is done on the compositor thread after being set michael@0: * up. |aRect| must be given in CSS pixels, relative to the document. michael@0: */ michael@0: void ZoomToRect(const ScrollableLayerGuid& aGuid, michael@0: const CSSRect& aRect); michael@0: michael@0: /** michael@0: * If we have touch listeners, this should always be called when we know michael@0: * definitively whether or not content has preventDefaulted any touch events michael@0: * that have come in. If |aPreventDefault| is true, any touch events in the michael@0: * queue will be discarded. michael@0: */ michael@0: void ContentReceivedTouch(const ScrollableLayerGuid& aGuid, michael@0: bool aPreventDefault); michael@0: michael@0: /** michael@0: * Updates any zoom constraints contained in the tag. michael@0: */ michael@0: void UpdateZoomConstraints(const ScrollableLayerGuid& aGuid, michael@0: const ZoomConstraints& aConstraints); michael@0: michael@0: /** michael@0: * Cancels any currently running animation. Note that all this does is set the michael@0: * state of the AsyncPanZoomController back to NOTHING, but it is the michael@0: * animation's responsibility to check this before advancing. michael@0: */ michael@0: void CancelAnimation(const ScrollableLayerGuid &aGuid); michael@0: michael@0: /** michael@0: * Calls Destroy() on all APZC instances attached to the tree, and resets the michael@0: * tree back to empty. This function may be called multiple times during the michael@0: * lifetime of this APZCTreeManager, but it must always be called at least once michael@0: * when this APZCTreeManager is no longer needed. Failing to call this function michael@0: * may prevent objects from being freed properly. michael@0: */ michael@0: void ClearTree(); michael@0: michael@0: /** michael@0: * Tests if a screen point intersect an apz in the tree. michael@0: */ michael@0: bool HitTestAPZC(const ScreenIntPoint& aPoint); michael@0: michael@0: /** michael@0: * Set the dpi value used by all AsyncPanZoomControllers. michael@0: * DPI defaults to 72 if not set using SetDPI() at any point. michael@0: */ michael@0: static void SetDPI(float aDpiValue) { sDPI = aDpiValue; } michael@0: michael@0: /** michael@0: * Returns the current dpi value in use. michael@0: */ michael@0: static float GetDPI() { return sDPI; } michael@0: michael@0: /** michael@0: * Returns values of allowed touch-behavior for the touches of aEvent via out parameter. michael@0: * Internally performs asks appropriate AsyncPanZoomController to perform michael@0: * hit testing on its own. michael@0: */ michael@0: void GetAllowedTouchBehavior(WidgetInputEvent* aEvent, michael@0: nsTArray& aOutValues); michael@0: michael@0: /** michael@0: * Sets allowed touch behavior values for current touch-session for specific apzc (determined by guid). michael@0: * Should be invoked by the widget. Each value of the aValues arrays corresponds to the different michael@0: * touch point that is currently active. michael@0: * Must be called after receiving the TOUCH_START event that starts the touch-session. michael@0: */ michael@0: void SetAllowedTouchBehavior(const ScrollableLayerGuid& aGuid, michael@0: const nsTArray& aValues); michael@0: michael@0: /** michael@0: * This is a callback for AsyncPanZoomController to call when it wants to michael@0: * scroll in response to a touch-move event, or when it needs to hand off michael@0: * overscroll to the next APZC. Note that because of scroll grabbing, the michael@0: * first APZC to scroll may not be the one that is receiving the touch events. michael@0: * michael@0: * |aAPZC| is the APZC that received the touch events triggering the scroll michael@0: * (in the case of an initial scroll), or the last APZC to scroll (in the michael@0: * case of overscroll) michael@0: * |aStartPoint| and |aEndPoint| are in |aAPZC|'s transformed screen michael@0: * coordinates (i.e. the same coordinates in which touch points are given to michael@0: * APZCs). The amount of (over)scroll is represented by two points rather michael@0: * than a displacement because with certain 3D transforms, the same michael@0: * displacement between different points in transformed coordinates can michael@0: * represent different displacements in untransformed coordinates. michael@0: * |aOverscrollHandoffChainIndex| is the next position in the overscroll michael@0: * handoff chain that should be scrolled. michael@0: * michael@0: * The way this method works is best illustrated with an example. michael@0: * Consider three nested APZCs, A, B, and C, with C being the innermost one. michael@0: * Say B is scroll-grabbing. michael@0: * The touch events go to C because it's the innermost one (so e.g. taps michael@0: * should go through C), but the overscroll handoff chain is B -> C -> A michael@0: * because B is scroll-grabbing. michael@0: * For convenience I'll refer to the three APZC objects as A, B, and C, and michael@0: * to the tree manager object as TM. michael@0: * Here's what happens when C receives a touch-move event: michael@0: * - C.TrackTouch() calls TM.DispatchScroll() with index = 0. michael@0: * - TM.DispatchScroll() calls B.AttemptScroll() (since B is at index 0 in the chain). michael@0: * - B.AttemptScroll() scrolls B. If there is overscroll, it calls TM.DispatchScroll() with index = 1. michael@0: * - TM.DispatchScroll() calls C.AttemptScroll() (since C is at index 1 in the chain) michael@0: * - C.AttemptScroll() scrolls C. If there is overscroll, it calls TM.DispatchScroll() with index = 2. michael@0: * - TM.DispatchScroll() calls A.AttemptScroll() (since A is at index 2 in the chain) michael@0: * - A.AttemptScroll() scrolls A. If there is overscroll, it calls TM.DispatchScroll() with index = 3. michael@0: * - TM.DispatchScroll() discards the rest of the scroll as there are no more elements in the chain. michael@0: * michael@0: * Note: this should be used for panning only. For handing off overscroll for michael@0: * a fling, use HandOffFling(). michael@0: */ michael@0: void DispatchScroll(AsyncPanZoomController* aAPZC, ScreenPoint aStartPoint, ScreenPoint aEndPoint, michael@0: uint32_t aOverscrollHandoffChainIndex); michael@0: michael@0: /** michael@0: * This is a callback for AsyncPanZoomController to call when it wants to michael@0: * hand off overscroll from a fling. michael@0: * @param aApzc the APZC that is handing off the fling michael@0: * @param aVelocity the current velocity of the fling, in |aApzc|'s screen michael@0: * pixels per millisecond michael@0: */ michael@0: void HandOffFling(AsyncPanZoomController* aApzc, ScreenPoint aVelocity); michael@0: michael@0: bool FlushRepaintsForOverscrollHandoffChain(); michael@0: michael@0: /** michael@0: * Determine whether |aApzc|, or any APZC along its overscroll handoff chain, michael@0: * has room to be panned. michael@0: * Expects the overscroll handoff chain to already be built. michael@0: */ michael@0: bool CanBePanned(AsyncPanZoomController* aApzc); michael@0: michael@0: protected: michael@0: // Protected destructor, to discourage deletion outside of Release(): michael@0: virtual ~APZCTreeManager(); michael@0: michael@0: /** michael@0: * Debug-build assertion that can be called to ensure code is running on the michael@0: * compositor thread. michael@0: */ michael@0: virtual void AssertOnCompositorThread(); michael@0: michael@0: /* michael@0: * Build the chain of APZCs that will handle overscroll for a pan starting at |aInitialTarget|. michael@0: */ michael@0: void BuildOverscrollHandoffChain(const nsRefPtr& aInitialTarget); michael@0: public: michael@0: /* Some helper functions to find an APZC given some identifying input. These functions michael@0: lock the tree of APZCs while they find the right one, and then return an addref'd michael@0: pointer to it. This allows caller code to just use the target APZC without worrying michael@0: about it going away. These are public for testing code and generally should not be michael@0: used by other production code. michael@0: */ michael@0: already_AddRefed GetTargetAPZC(const ScrollableLayerGuid& aGuid); michael@0: already_AddRefed GetTargetAPZC(const ScreenPoint& aPoint); michael@0: void GetInputTransforms(AsyncPanZoomController *aApzc, gfx3DMatrix& aTransformToApzcOut, michael@0: gfx3DMatrix& aTransformToGeckoOut); michael@0: private: michael@0: /* Helpers */ michael@0: AsyncPanZoomController* FindTargetAPZC(AsyncPanZoomController* aApzc, FrameMetrics::ViewID aScrollId); michael@0: AsyncPanZoomController* FindTargetAPZC(AsyncPanZoomController* aApzc, const ScrollableLayerGuid& aGuid); michael@0: AsyncPanZoomController* GetAPZCAtPoint(AsyncPanZoomController* aApzc, const gfxPoint& aHitTestPoint); michael@0: already_AddRefed CommonAncestor(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2); michael@0: already_AddRefed RootAPZCForLayersId(AsyncPanZoomController* aApzc); michael@0: already_AddRefed GetTouchInputBlockAPZC(const WidgetTouchEvent& aEvent); michael@0: nsEventStatus ProcessTouchEvent(WidgetTouchEvent& touchEvent, ScrollableLayerGuid* aOutTargetGuid); michael@0: nsEventStatus ProcessEvent(WidgetInputEvent& inputEvent, ScrollableLayerGuid* aOutTargetGuid); michael@0: void UpdateZoomConstraintsRecursively(AsyncPanZoomController* aApzc, michael@0: const ZoomConstraints& aConstraints); michael@0: void ClearOverscrollHandoffChain(); michael@0: michael@0: /** michael@0: * Recursive helper function to build the APZC tree. The tree of APZC instances has michael@0: * the same shape as the layer tree, but excludes all the layers that are not scrollable. michael@0: * Note that this means APZCs corresponding to layers at different depths in the tree michael@0: * may end up becoming siblings. It also means that the "root" APZC may have siblings. michael@0: * This function walks the layer tree backwards through siblings and constructs the APZC michael@0: * tree also as a last-child-prev-sibling tree because that simplifies the hit detection michael@0: * code. michael@0: */ michael@0: AsyncPanZoomController* UpdatePanZoomControllerTree(CompositorParent* aCompositor, michael@0: Layer* aLayer, uint64_t aLayersId, michael@0: gfx3DMatrix aTransform, michael@0: AsyncPanZoomController* aParent, michael@0: AsyncPanZoomController* aNextSibling, michael@0: bool aIsFirstPaint, michael@0: uint64_t aFirstPaintLayersId, michael@0: nsTArray< nsRefPtr >* aApzcsToDestroy); michael@0: michael@0: private: michael@0: /* Whenever walking or mutating the tree rooted at mRootApzc, mTreeLock must be held. michael@0: * This lock does not need to be held while manipulating a single APZC instance in michael@0: * isolation (that is, if its tree pointers are not being accessed or mutated). The michael@0: * lock also needs to be held when accessing the mRootApzc instance variable, as that michael@0: * is considered part of the APZC tree management state. michael@0: * Finally, the lock needs to be held when accessing mOverscrollHandoffChain. michael@0: * IMPORTANT: See the note about lock ordering at the top of this file. */ michael@0: mozilla::Monitor mTreeLock; michael@0: nsRefPtr mRootApzc; michael@0: /* This tracks the APZC that should receive all inputs for the current input event block. michael@0: * This allows touch points to move outside the thing they started on, but still have the michael@0: * touch events delivered to the same initial APZC. This will only ever be touched on the michael@0: * input delivery thread, and so does not require locking. michael@0: */ michael@0: nsRefPtr mApzcForInputBlock; michael@0: /* The number of touch points we are tracking that are currently on the screen. */ michael@0: uint32_t mTouchCount; michael@0: /* The transform from root screen coordinates into mApzcForInputBlock's michael@0: * screen coordinates, as returned through the 'aTransformToApzcOut' parameter michael@0: * of GetInputTransform(), at the start of the input block. This is cached michael@0: * because this transform can change over the course of the input block, michael@0: * but for some operations we need to use the initial transform. michael@0: * Meaningless if mApzcForInputBlock is nullptr. michael@0: */ michael@0: gfx3DMatrix mCachedTransformToApzcForInputBlock; michael@0: /* The chain of APZCs that will handle pans for the current touch input michael@0: * block, in the order in which they will be scrolled. When one APZC has michael@0: * been scrolled as far as it can, any overscroll will be handed off to michael@0: * the next APZC in the chain. michael@0: */ michael@0: Vector< nsRefPtr > mOverscrollHandoffChain; michael@0: /* For logging the APZC tree for debugging (enabled by the apz.printtree michael@0: * pref). */ michael@0: gfx::TreeLog mApzcTreeLog; michael@0: michael@0: static float sDPI; michael@0: }; michael@0: michael@0: } michael@0: } michael@0: michael@0: #endif // mozilla_layers_PanZoomController_h