|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #ifndef mozilla_layers_APZCTreeManager_h |
|
7 #define mozilla_layers_APZCTreeManager_h |
|
8 |
|
9 #include <stdint.h> // for uint64_t, uint32_t |
|
10 #include "FrameMetrics.h" // for FrameMetrics, etc |
|
11 #include "Units.h" // for CSSPoint, CSSRect, etc |
|
12 #include "gfxPoint.h" // for gfxPoint |
|
13 #include "gfx3DMatrix.h" // for gfx3DMatrix |
|
14 #include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2 |
|
15 #include "mozilla/EventForwards.h" // for WidgetInputEvent, nsEventStatus |
|
16 #include "mozilla/Monitor.h" // for Monitor |
|
17 #include "nsAutoPtr.h" // for nsRefPtr |
|
18 #include "nsCOMPtr.h" // for already_AddRefed |
|
19 #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc |
|
20 #include "mozilla/Vector.h" // for mozilla::Vector |
|
21 #include "nsTArrayForwardDeclare.h" // for nsTArray, nsTArray_Impl, etc |
|
22 #include "mozilla/gfx/Logging.h" // for gfx::TreeLog |
|
23 |
|
24 class gfx3DMatrix; |
|
25 |
|
26 namespace mozilla { |
|
27 class InputData; |
|
28 |
|
29 namespace layers { |
|
30 |
|
31 enum AllowedTouchBehavior { |
|
32 NONE = 0, |
|
33 VERTICAL_PAN = 1 << 0, |
|
34 HORIZONTAL_PAN = 1 << 1, |
|
35 PINCH_ZOOM = 1 << 2, |
|
36 DOUBLE_TAP_ZOOM = 1 << 3, |
|
37 UNKNOWN = 1 << 4 |
|
38 }; |
|
39 |
|
40 class Layer; |
|
41 class AsyncPanZoomController; |
|
42 class CompositorParent; |
|
43 |
|
44 /** |
|
45 * ****************** NOTE ON LOCK ORDERING IN APZ ************************** |
|
46 * |
|
47 * There are two kinds of locks used by APZ: APZCTreeManager::mTreeLock |
|
48 * ("the tree lock") and AsyncPanZoomController::mMonitor ("APZC locks"). |
|
49 * |
|
50 * To avoid deadlock, we impose a lock ordering between these locks, which is: |
|
51 * |
|
52 * tree lock -> APZC locks |
|
53 * |
|
54 * The interpretation of the lock ordering is that if lock A precedes lock B |
|
55 * in the ordering sequence, then you must NOT wait on A while holding B. |
|
56 * |
|
57 * ************************************************************************** |
|
58 */ |
|
59 |
|
60 /** |
|
61 * This class manages the tree of AsyncPanZoomController instances. There is one |
|
62 * instance of this class owned by each CompositorParent, and it contains as |
|
63 * many AsyncPanZoomController instances as there are scrollable container layers. |
|
64 * This class generally lives on the compositor thread, although some functions |
|
65 * may be called from other threads as noted; thread safety is ensured internally. |
|
66 * |
|
67 * The bulk of the work of this class happens as part of the UpdatePanZoomControllerTree |
|
68 * function, which is when a layer tree update is received by the compositor. |
|
69 * This function walks through the layer tree and creates a tree of APZC instances |
|
70 * to match the scrollable container layers. APZC instances may be preserved across |
|
71 * calls to this function if the corresponding layers are still present in the layer |
|
72 * tree. |
|
73 * |
|
74 * The other functions on this class are used by various pieces of client code to |
|
75 * notify the APZC instances of events relevant to them. This includes, for example, |
|
76 * user input events that drive panning and zooming, changes to the scroll viewport |
|
77 * area, and changes to pan/zoom constraints. |
|
78 * |
|
79 * Note that the ClearTree function MUST be called when this class is no longer needed; |
|
80 * see the method documentation for details. |
|
81 */ |
|
82 class APZCTreeManager { |
|
83 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(APZCTreeManager) |
|
84 |
|
85 typedef mozilla::layers::AllowedTouchBehavior AllowedTouchBehavior; |
|
86 typedef uint32_t TouchBehaviorFlags; |
|
87 |
|
88 public: |
|
89 APZCTreeManager(); |
|
90 |
|
91 /** |
|
92 * Rebuild the APZC tree based on the layer update that just came up. Preserve |
|
93 * APZC instances where possible, but retire those whose layers are no longer |
|
94 * in the layer tree. |
|
95 * |
|
96 * This must be called on the compositor thread as it walks the layer tree. |
|
97 * |
|
98 * @param aCompositor A pointer to the compositor parent instance that owns |
|
99 * this APZCTreeManager |
|
100 * @param aRoot The root of the (full) layer tree |
|
101 * @param aFirstPaintLayersId The layers id of the subtree to which aIsFirstPaint |
|
102 * applies. |
|
103 * @param aIsFirstPaint True if the layers update that this is called in response |
|
104 * to included a first-paint. If this is true, the part of |
|
105 * the tree that is affected by the first-paint flag is |
|
106 * indicated by the aFirstPaintLayersId parameter. |
|
107 */ |
|
108 void UpdatePanZoomControllerTree(CompositorParent* aCompositor, Layer* aRoot, |
|
109 bool aIsFirstPaint, uint64_t aFirstPaintLayersId); |
|
110 |
|
111 /** |
|
112 * General handler for incoming input events. Manipulates the frame metrics |
|
113 * based on what type of input it is. For example, a PinchGestureEvent will |
|
114 * cause scaling. This should only be called externally to this class. |
|
115 * |
|
116 * @param aEvent input event object, will not be modified |
|
117 * @param aOutTargetGuid returns the guid of the apzc this event was |
|
118 * delivered to. May be null. |
|
119 */ |
|
120 nsEventStatus ReceiveInputEvent(const InputData& aEvent, |
|
121 ScrollableLayerGuid* aOutTargetGuid); |
|
122 |
|
123 /** |
|
124 * WidgetInputEvent handler. Transforms |aEvent| (which is assumed to be an |
|
125 * already-existing instance of an WidgetInputEvent which may be an |
|
126 * WidgetTouchEvent) to have its coordinates in DOM space. This is so that the |
|
127 * event can be passed through the DOM and content can handle them. |
|
128 * |
|
129 * NOTE: Be careful of invoking the WidgetInputEvent variant. This can only be |
|
130 * called on the main thread. See widget/InputData.h for more information on |
|
131 * why we have InputData and WidgetInputEvent separated. |
|
132 * NOTE: On unix, mouse events are treated as touch and are forwarded |
|
133 * to the appropriate apz as such. |
|
134 * |
|
135 * @param aEvent input event object; is modified in-place |
|
136 * @param aOutTargetGuid returns the guid of the apzc this event was |
|
137 * delivered to. May be null. |
|
138 */ |
|
139 nsEventStatus ReceiveInputEvent(WidgetInputEvent& aEvent, |
|
140 ScrollableLayerGuid* aOutTargetGuid); |
|
141 |
|
142 /** |
|
143 * A helper for transforming coordinates to gecko coordinate space. |
|
144 * |
|
145 * @param aPoint point to transform |
|
146 * @param aOutTransformedPoint resulting transformed point |
|
147 */ |
|
148 void TransformCoordinateToGecko(const ScreenIntPoint& aPoint, |
|
149 LayoutDeviceIntPoint* aOutTransformedPoint); |
|
150 |
|
151 /** |
|
152 * Kicks an animation to zoom to a rect. This may be either a zoom out or zoom |
|
153 * in. The actual animation is done on the compositor thread after being set |
|
154 * up. |aRect| must be given in CSS pixels, relative to the document. |
|
155 */ |
|
156 void ZoomToRect(const ScrollableLayerGuid& aGuid, |
|
157 const CSSRect& aRect); |
|
158 |
|
159 /** |
|
160 * If we have touch listeners, this should always be called when we know |
|
161 * definitively whether or not content has preventDefaulted any touch events |
|
162 * that have come in. If |aPreventDefault| is true, any touch events in the |
|
163 * queue will be discarded. |
|
164 */ |
|
165 void ContentReceivedTouch(const ScrollableLayerGuid& aGuid, |
|
166 bool aPreventDefault); |
|
167 |
|
168 /** |
|
169 * Updates any zoom constraints contained in the <meta name="viewport"> tag. |
|
170 */ |
|
171 void UpdateZoomConstraints(const ScrollableLayerGuid& aGuid, |
|
172 const ZoomConstraints& aConstraints); |
|
173 |
|
174 /** |
|
175 * Cancels any currently running animation. Note that all this does is set the |
|
176 * state of the AsyncPanZoomController back to NOTHING, but it is the |
|
177 * animation's responsibility to check this before advancing. |
|
178 */ |
|
179 void CancelAnimation(const ScrollableLayerGuid &aGuid); |
|
180 |
|
181 /** |
|
182 * Calls Destroy() on all APZC instances attached to the tree, and resets the |
|
183 * tree back to empty. This function may be called multiple times during the |
|
184 * lifetime of this APZCTreeManager, but it must always be called at least once |
|
185 * when this APZCTreeManager is no longer needed. Failing to call this function |
|
186 * may prevent objects from being freed properly. |
|
187 */ |
|
188 void ClearTree(); |
|
189 |
|
190 /** |
|
191 * Tests if a screen point intersect an apz in the tree. |
|
192 */ |
|
193 bool HitTestAPZC(const ScreenIntPoint& aPoint); |
|
194 |
|
195 /** |
|
196 * Set the dpi value used by all AsyncPanZoomControllers. |
|
197 * DPI defaults to 72 if not set using SetDPI() at any point. |
|
198 */ |
|
199 static void SetDPI(float aDpiValue) { sDPI = aDpiValue; } |
|
200 |
|
201 /** |
|
202 * Returns the current dpi value in use. |
|
203 */ |
|
204 static float GetDPI() { return sDPI; } |
|
205 |
|
206 /** |
|
207 * Returns values of allowed touch-behavior for the touches of aEvent via out parameter. |
|
208 * Internally performs asks appropriate AsyncPanZoomController to perform |
|
209 * hit testing on its own. |
|
210 */ |
|
211 void GetAllowedTouchBehavior(WidgetInputEvent* aEvent, |
|
212 nsTArray<TouchBehaviorFlags>& aOutValues); |
|
213 |
|
214 /** |
|
215 * Sets allowed touch behavior values for current touch-session for specific apzc (determined by guid). |
|
216 * Should be invoked by the widget. Each value of the aValues arrays corresponds to the different |
|
217 * touch point that is currently active. |
|
218 * Must be called after receiving the TOUCH_START event that starts the touch-session. |
|
219 */ |
|
220 void SetAllowedTouchBehavior(const ScrollableLayerGuid& aGuid, |
|
221 const nsTArray<TouchBehaviorFlags>& aValues); |
|
222 |
|
223 /** |
|
224 * This is a callback for AsyncPanZoomController to call when it wants to |
|
225 * scroll in response to a touch-move event, or when it needs to hand off |
|
226 * overscroll to the next APZC. Note that because of scroll grabbing, the |
|
227 * first APZC to scroll may not be the one that is receiving the touch events. |
|
228 * |
|
229 * |aAPZC| is the APZC that received the touch events triggering the scroll |
|
230 * (in the case of an initial scroll), or the last APZC to scroll (in the |
|
231 * case of overscroll) |
|
232 * |aStartPoint| and |aEndPoint| are in |aAPZC|'s transformed screen |
|
233 * coordinates (i.e. the same coordinates in which touch points are given to |
|
234 * APZCs). The amount of (over)scroll is represented by two points rather |
|
235 * than a displacement because with certain 3D transforms, the same |
|
236 * displacement between different points in transformed coordinates can |
|
237 * represent different displacements in untransformed coordinates. |
|
238 * |aOverscrollHandoffChainIndex| is the next position in the overscroll |
|
239 * handoff chain that should be scrolled. |
|
240 * |
|
241 * The way this method works is best illustrated with an example. |
|
242 * Consider three nested APZCs, A, B, and C, with C being the innermost one. |
|
243 * Say B is scroll-grabbing. |
|
244 * The touch events go to C because it's the innermost one (so e.g. taps |
|
245 * should go through C), but the overscroll handoff chain is B -> C -> A |
|
246 * because B is scroll-grabbing. |
|
247 * For convenience I'll refer to the three APZC objects as A, B, and C, and |
|
248 * to the tree manager object as TM. |
|
249 * Here's what happens when C receives a touch-move event: |
|
250 * - C.TrackTouch() calls TM.DispatchScroll() with index = 0. |
|
251 * - TM.DispatchScroll() calls B.AttemptScroll() (since B is at index 0 in the chain). |
|
252 * - B.AttemptScroll() scrolls B. If there is overscroll, it calls TM.DispatchScroll() with index = 1. |
|
253 * - TM.DispatchScroll() calls C.AttemptScroll() (since C is at index 1 in the chain) |
|
254 * - C.AttemptScroll() scrolls C. If there is overscroll, it calls TM.DispatchScroll() with index = 2. |
|
255 * - TM.DispatchScroll() calls A.AttemptScroll() (since A is at index 2 in the chain) |
|
256 * - A.AttemptScroll() scrolls A. If there is overscroll, it calls TM.DispatchScroll() with index = 3. |
|
257 * - TM.DispatchScroll() discards the rest of the scroll as there are no more elements in the chain. |
|
258 * |
|
259 * Note: this should be used for panning only. For handing off overscroll for |
|
260 * a fling, use HandOffFling(). |
|
261 */ |
|
262 void DispatchScroll(AsyncPanZoomController* aAPZC, ScreenPoint aStartPoint, ScreenPoint aEndPoint, |
|
263 uint32_t aOverscrollHandoffChainIndex); |
|
264 |
|
265 /** |
|
266 * This is a callback for AsyncPanZoomController to call when it wants to |
|
267 * hand off overscroll from a fling. |
|
268 * @param aApzc the APZC that is handing off the fling |
|
269 * @param aVelocity the current velocity of the fling, in |aApzc|'s screen |
|
270 * pixels per millisecond |
|
271 */ |
|
272 void HandOffFling(AsyncPanZoomController* aApzc, ScreenPoint aVelocity); |
|
273 |
|
274 bool FlushRepaintsForOverscrollHandoffChain(); |
|
275 |
|
276 /** |
|
277 * Determine whether |aApzc|, or any APZC along its overscroll handoff chain, |
|
278 * has room to be panned. |
|
279 * Expects the overscroll handoff chain to already be built. |
|
280 */ |
|
281 bool CanBePanned(AsyncPanZoomController* aApzc); |
|
282 |
|
283 protected: |
|
284 // Protected destructor, to discourage deletion outside of Release(): |
|
285 virtual ~APZCTreeManager(); |
|
286 |
|
287 /** |
|
288 * Debug-build assertion that can be called to ensure code is running on the |
|
289 * compositor thread. |
|
290 */ |
|
291 virtual void AssertOnCompositorThread(); |
|
292 |
|
293 /* |
|
294 * Build the chain of APZCs that will handle overscroll for a pan starting at |aInitialTarget|. |
|
295 */ |
|
296 void BuildOverscrollHandoffChain(const nsRefPtr<AsyncPanZoomController>& aInitialTarget); |
|
297 public: |
|
298 /* Some helper functions to find an APZC given some identifying input. These functions |
|
299 lock the tree of APZCs while they find the right one, and then return an addref'd |
|
300 pointer to it. This allows caller code to just use the target APZC without worrying |
|
301 about it going away. These are public for testing code and generally should not be |
|
302 used by other production code. |
|
303 */ |
|
304 already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScrollableLayerGuid& aGuid); |
|
305 already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScreenPoint& aPoint); |
|
306 void GetInputTransforms(AsyncPanZoomController *aApzc, gfx3DMatrix& aTransformToApzcOut, |
|
307 gfx3DMatrix& aTransformToGeckoOut); |
|
308 private: |
|
309 /* Helpers */ |
|
310 AsyncPanZoomController* FindTargetAPZC(AsyncPanZoomController* aApzc, FrameMetrics::ViewID aScrollId); |
|
311 AsyncPanZoomController* FindTargetAPZC(AsyncPanZoomController* aApzc, const ScrollableLayerGuid& aGuid); |
|
312 AsyncPanZoomController* GetAPZCAtPoint(AsyncPanZoomController* aApzc, const gfxPoint& aHitTestPoint); |
|
313 already_AddRefed<AsyncPanZoomController> CommonAncestor(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2); |
|
314 already_AddRefed<AsyncPanZoomController> RootAPZCForLayersId(AsyncPanZoomController* aApzc); |
|
315 already_AddRefed<AsyncPanZoomController> GetTouchInputBlockAPZC(const WidgetTouchEvent& aEvent); |
|
316 nsEventStatus ProcessTouchEvent(WidgetTouchEvent& touchEvent, ScrollableLayerGuid* aOutTargetGuid); |
|
317 nsEventStatus ProcessEvent(WidgetInputEvent& inputEvent, ScrollableLayerGuid* aOutTargetGuid); |
|
318 void UpdateZoomConstraintsRecursively(AsyncPanZoomController* aApzc, |
|
319 const ZoomConstraints& aConstraints); |
|
320 void ClearOverscrollHandoffChain(); |
|
321 |
|
322 /** |
|
323 * Recursive helper function to build the APZC tree. The tree of APZC instances has |
|
324 * the same shape as the layer tree, but excludes all the layers that are not scrollable. |
|
325 * Note that this means APZCs corresponding to layers at different depths in the tree |
|
326 * may end up becoming siblings. It also means that the "root" APZC may have siblings. |
|
327 * This function walks the layer tree backwards through siblings and constructs the APZC |
|
328 * tree also as a last-child-prev-sibling tree because that simplifies the hit detection |
|
329 * code. |
|
330 */ |
|
331 AsyncPanZoomController* UpdatePanZoomControllerTree(CompositorParent* aCompositor, |
|
332 Layer* aLayer, uint64_t aLayersId, |
|
333 gfx3DMatrix aTransform, |
|
334 AsyncPanZoomController* aParent, |
|
335 AsyncPanZoomController* aNextSibling, |
|
336 bool aIsFirstPaint, |
|
337 uint64_t aFirstPaintLayersId, |
|
338 nsTArray< nsRefPtr<AsyncPanZoomController> >* aApzcsToDestroy); |
|
339 |
|
340 private: |
|
341 /* Whenever walking or mutating the tree rooted at mRootApzc, mTreeLock must be held. |
|
342 * This lock does not need to be held while manipulating a single APZC instance in |
|
343 * isolation (that is, if its tree pointers are not being accessed or mutated). The |
|
344 * lock also needs to be held when accessing the mRootApzc instance variable, as that |
|
345 * is considered part of the APZC tree management state. |
|
346 * Finally, the lock needs to be held when accessing mOverscrollHandoffChain. |
|
347 * IMPORTANT: See the note about lock ordering at the top of this file. */ |
|
348 mozilla::Monitor mTreeLock; |
|
349 nsRefPtr<AsyncPanZoomController> mRootApzc; |
|
350 /* This tracks the APZC that should receive all inputs for the current input event block. |
|
351 * This allows touch points to move outside the thing they started on, but still have the |
|
352 * touch events delivered to the same initial APZC. This will only ever be touched on the |
|
353 * input delivery thread, and so does not require locking. |
|
354 */ |
|
355 nsRefPtr<AsyncPanZoomController> mApzcForInputBlock; |
|
356 /* The number of touch points we are tracking that are currently on the screen. */ |
|
357 uint32_t mTouchCount; |
|
358 /* The transform from root screen coordinates into mApzcForInputBlock's |
|
359 * screen coordinates, as returned through the 'aTransformToApzcOut' parameter |
|
360 * of GetInputTransform(), at the start of the input block. This is cached |
|
361 * because this transform can change over the course of the input block, |
|
362 * but for some operations we need to use the initial transform. |
|
363 * Meaningless if mApzcForInputBlock is nullptr. |
|
364 */ |
|
365 gfx3DMatrix mCachedTransformToApzcForInputBlock; |
|
366 /* The chain of APZCs that will handle pans for the current touch input |
|
367 * block, in the order in which they will be scrolled. When one APZC has |
|
368 * been scrolled as far as it can, any overscroll will be handed off to |
|
369 * the next APZC in the chain. |
|
370 */ |
|
371 Vector< nsRefPtr<AsyncPanZoomController> > mOverscrollHandoffChain; |
|
372 /* For logging the APZC tree for debugging (enabled by the apz.printtree |
|
373 * pref). */ |
|
374 gfx::TreeLog mApzcTreeLog; |
|
375 |
|
376 static float sDPI; |
|
377 }; |
|
378 |
|
379 } |
|
380 } |
|
381 |
|
382 #endif // mozilla_layers_PanZoomController_h |