1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef mozilla_layers_InputBlockState_h
8 #define mozilla_layers_InputBlockState_h
9 
10 #include "InputData.h"           // for MultiTouchInput
11 #include "mozilla/RefCounted.h"  // for RefCounted
12 #include "mozilla/RefPtr.h"      // for RefPtr
13 #include "mozilla/StaticPrefs_apz.h"
14 #include "mozilla/gfx/Matrix.h"  // for Matrix4x4
15 #include "mozilla/layers/APZUtils.h"
16 #include "mozilla/layers/LayersTypes.h"  // for TouchBehaviorFlags
17 #include "mozilla/layers/AsyncDragMetrics.h"
18 #include "mozilla/layers/TouchCounter.h"
19 #include "mozilla/TimeStamp.h"  // for TimeStamp
20 #include "nsTArray.h"           // for nsTArray
21 
22 namespace mozilla {
23 namespace layers {
24 
25 class AsyncPanZoomController;
26 class OverscrollHandoffChain;
27 class CancelableBlockState;
28 class TouchBlockState;
29 class WheelBlockState;
30 class DragBlockState;
31 class PanGestureBlockState;
32 class PinchGestureBlockState;
33 class KeyboardBlockState;
34 
35 /**
36  * A base class that stores state common to various input blocks.
37  * Note that the InputBlockState constructor acquires the tree lock, so callers
38  * from inside AsyncPanZoomController should ensure that the APZC lock is not
39  * held.
40  */
41 class InputBlockState : public RefCounted<InputBlockState> {
42  public:
43   MOZ_DECLARE_REFCOUNTED_TYPENAME(InputBlockState)
44 
45   static const uint64_t NO_BLOCK_ID = 0;
46 
47   enum class TargetConfirmationState : uint8_t {
48     eUnconfirmed,
49     eTimedOut,
50     eTimedOutAndMainThreadResponded,
51     eConfirmed
52   };
53 
54   InputBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
55                   TargetConfirmationFlags aFlags);
56   virtual ~InputBlockState() = default;
57 
AsCancelableBlock()58   virtual CancelableBlockState* AsCancelableBlock() { return nullptr; }
AsTouchBlock()59   virtual TouchBlockState* AsTouchBlock() { return nullptr; }
AsWheelBlock()60   virtual WheelBlockState* AsWheelBlock() { return nullptr; }
AsDragBlock()61   virtual DragBlockState* AsDragBlock() { return nullptr; }
AsPanGestureBlock()62   virtual PanGestureBlockState* AsPanGestureBlock() { return nullptr; }
AsPinchGestureBlock()63   virtual PinchGestureBlockState* AsPinchGestureBlock() { return nullptr; }
AsKeyboardBlock()64   virtual KeyboardBlockState* AsKeyboardBlock() { return nullptr; }
65 
66   virtual bool SetConfirmedTargetApzc(
67       const RefPtr<AsyncPanZoomController>& aTargetApzc,
68       TargetConfirmationState aState, InputData* aFirstInput,
69       bool aForScrollbarDrag);
70   const RefPtr<AsyncPanZoomController>& GetTargetApzc() const;
71   const RefPtr<const OverscrollHandoffChain>& GetOverscrollHandoffChain() const;
72   uint64_t GetBlockId() const;
73 
74   bool IsTargetConfirmed() const;
75   bool HasReceivedRealConfirmedTarget() const;
76 
77   virtual bool ShouldDropEvents() const;
78 
79   void SetScrolledApzc(AsyncPanZoomController* aApzc);
80   AsyncPanZoomController* GetScrolledApzc() const;
81   bool IsDownchainOfScrolledApzc(AsyncPanZoomController* aApzc) const;
82 
83   /**
84    * Dispatch the event to the target APZC. Mostly this is a hook for
85    * subclasses to do any per-event processing they need to.
86    */
87   virtual void DispatchEvent(const InputData& aEvent) const;
88 
89   /**
90    * Return true if this input block must stay active if it would otherwise
91    * be removed as the last item in the pending queue.
92    */
93   virtual bool MustStayActive() = 0;
94 
95  protected:
96   virtual void UpdateTargetApzc(
97       const RefPtr<AsyncPanZoomController>& aTargetApzc);
98 
99  private:
100   // Checks whether |aA| is an ancestor of |aB| (or the same as |aB|) in
101   // |mOverscrollHandoffChain|.
102   bool IsDownchainOf(AsyncPanZoomController* aA,
103                      AsyncPanZoomController* aB) const;
104 
105  private:
106   RefPtr<AsyncPanZoomController> mTargetApzc;
107   TargetConfirmationState mTargetConfirmed;
108   bool mRequiresTargetConfirmation;
109   const uint64_t mBlockId;
110 
111   // The APZC that was actually scrolled by events in this input block.
112   // This is used in configurations where a single input block is only
113   // allowed to scroll a single APZC (configurations where
114   // StaticPrefs::apz_allow_immediate_handoff() is false). Set the first time an
115   // input event in this block scrolls an APZC.
116   RefPtr<AsyncPanZoomController> mScrolledApzc;
117 
118  protected:
119   RefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain;
120 
121   // Used to transform events from global screen space to |mTargetApzc|'s
122   // screen space. It's cached at the beginning of the input block so that
123   // all events in the block are in the same coordinate space.
124   ScreenToParentLayerMatrix4x4 mTransformToApzc;
125 };
126 
127 /**
128  * This class represents a set of events that can be cancelled by web content
129  * via event listeners.
130  *
131  * Each cancelable input block can be cancelled by web content, and
132  * this information is stored in the mPreventDefault flag. Because web
133  * content runs on the Gecko main thread, we cannot always wait for web
134  * content's response. Instead, there is a timeout that sets this flag in the
135  * case where web content doesn't respond in time. The mContentResponded and
136  * mContentResponseTimerExpired flags indicate which of these scenarios
137  * occurred.
138  */
139 class CancelableBlockState : public InputBlockState {
140  public:
141   CancelableBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
142                        TargetConfirmationFlags aFlags);
143 
AsCancelableBlock()144   CancelableBlockState* AsCancelableBlock() override { return this; }
145 
146   /**
147    * Record whether or not content cancelled this block of events.
148    * @param aPreventDefault true iff the block is cancelled.
149    * @return false if this block has already received a response from
150    *         web content, true if not.
151    */
152   virtual bool SetContentResponse(bool aPreventDefault);
153 
154   /**
155    * Record that content didn't respond in time.
156    * @return false if this block already timed out, true if not.
157    */
158   bool TimeoutContentResponse();
159 
160   /**
161    * Checks if the content response timer has already expired.
162    */
163   bool IsContentResponseTimerExpired() const;
164 
165   /**
166    * @return true iff web content cancelled this block of events.
167    */
168   bool IsDefaultPrevented() const;
169 
170   /**
171    * @return true iff this block has received all the information it could
172    *         have gotten from the content thread.
173    */
174   virtual bool HasReceivedAllContentNotifications() const;
175 
176   /**
177    * @return true iff this block has received all the information needed
178    *         to properly dispatch the events in the block.
179    */
180   virtual bool IsReadyForHandling() const;
181 
182   /**
183    * Return a descriptive name for the block kind.
184    */
185   virtual const char* Type() = 0;
186 
187   bool ShouldDropEvents() const override;
188 
189  private:
190   bool mPreventDefault;
191   bool mContentResponded;
192   bool mContentResponseTimerExpired;
193 };
194 
195 /**
196  * A single block of wheel events.
197  */
198 class WheelBlockState : public CancelableBlockState {
199  public:
200   WheelBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
201                   TargetConfirmationFlags aFlags,
202                   const ScrollWheelInput& aEvent);
203 
204   bool SetContentResponse(bool aPreventDefault) override;
205   bool MustStayActive() override;
206   const char* Type() override;
207   bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
208                               TargetConfirmationState aState,
209                               InputData* aFirstInput,
210                               bool aForScrollbarDrag) override;
211 
AsWheelBlock()212   WheelBlockState* AsWheelBlock() override { return this; }
213 
214   /**
215    * Determine whether this wheel block is accepting new events.
216    */
217   bool ShouldAcceptNewEvent() const;
218 
219   /**
220    * Call to check whether a wheel event will cause the current transaction to
221    * timeout.
222    */
223   bool MaybeTimeout(const ScrollWheelInput& aEvent);
224 
225   /**
226    * Called from APZCTM when a mouse move or drag+drop event occurs, before
227    * the event has been processed.
228    */
229   void OnMouseMove(const ScreenIntPoint& aPoint,
230                    const Maybe<ScrollableLayerGuid>& aTargetGuid);
231 
232   /**
233    * Returns whether or not the block is participating in a wheel transaction.
234    * This means that the block is the most recent input block to be created,
235    * and no events have occurred that would require scrolling a different
236    * frame.
237    *
238    * @return True if in a transaction, false otherwise.
239    */
240   bool InTransaction() const;
241 
242   /**
243    * Mark the block as no longer participating in a wheel transaction. This
244    * will force future wheel events to begin a new input block.
245    */
246   void EndTransaction();
247 
248   /**
249    * @return Whether or not overscrolling is prevented for this wheel block.
250    */
251   bool AllowScrollHandoff() const;
252 
253   /**
254    * Called to check and possibly end the transaction due to a timeout.
255    *
256    * @return True if the transaction ended, false otherwise.
257    */
258   bool MaybeTimeout(const TimeStamp& aTimeStamp);
259 
260   /**
261    * Update the wheel transaction state for a new event.
262    */
263   void Update(ScrollWheelInput& aEvent);
264 
GetAllowedScrollDirections()265   ScrollDirections GetAllowedScrollDirections() const {
266     return mAllowedScrollDirections;
267   }
268 
269  protected:
270   void UpdateTargetApzc(
271       const RefPtr<AsyncPanZoomController>& aTargetApzc) override;
272 
273  private:
274   TimeStamp mLastEventTime;
275   TimeStamp mLastMouseMove;
276   uint32_t mScrollSeriesCounter;
277   bool mTransactionEnded;
278   bool mIsScrollable = true;
279   ScrollDirections mAllowedScrollDirections;
280 };
281 
282 /**
283  * A block of mouse events that are part of a drag
284  */
285 class DragBlockState : public CancelableBlockState {
286  public:
287   DragBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
288                  TargetConfirmationFlags aFlags, const MouseInput& aEvent);
289 
290   bool MustStayActive() override;
291   const char* Type() override;
292 
293   bool HasReceivedMouseUp();
294   void MarkMouseUpReceived();
295 
AsDragBlock()296   DragBlockState* AsDragBlock() override { return this; }
297 
298   void SetInitialThumbPos(CSSCoord aThumbPos);
299   void SetDragMetrics(const AsyncDragMetrics& aDragMetrics);
300 
301   void DispatchEvent(const InputData& aEvent) const override;
302 
303  private:
304   AsyncDragMetrics mDragMetrics;
305   CSSCoord mInitialThumbPos;
306   bool mReceivedMouseUp;
307 };
308 
309 /**
310  * A single block of pan gesture events.
311  */
312 class PanGestureBlockState : public CancelableBlockState {
313  public:
314   PanGestureBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
315                        TargetConfirmationFlags aFlags,
316                        const PanGestureInput& aEvent);
317 
318   bool SetContentResponse(bool aPreventDefault) override;
319   bool HasReceivedAllContentNotifications() const override;
320   bool IsReadyForHandling() const override;
321   bool MustStayActive() override;
322   const char* Type() override;
323   bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
324                               TargetConfirmationState aState,
325                               InputData* aFirstInput,
326                               bool aForScrollbarDrag) override;
327 
AsPanGestureBlock()328   PanGestureBlockState* AsPanGestureBlock() override { return this; }
329 
330   /**
331    * @return Whether or not overscrolling is prevented for this block.
332    */
333   bool AllowScrollHandoff() const;
334 
WasInterrupted()335   bool WasInterrupted() const { return mInterrupted; }
336 
337   void SetNeedsToWaitForContentResponse(bool aWaitForContentResponse);
338 
GetAllowedScrollDirections()339   ScrollDirections GetAllowedScrollDirections() const {
340     return mAllowedScrollDirections;
341   }
342 
343  private:
344   bool mInterrupted;
345   bool mWaitingForContentResponse;
346   ScrollDirections mAllowedScrollDirections;
347 };
348 
349 /**
350  * A single block of pinch gesture events.
351  */
352 class PinchGestureBlockState : public CancelableBlockState {
353  public:
354   PinchGestureBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
355                          TargetConfirmationFlags aFlags);
356 
357   bool SetContentResponse(bool aPreventDefault) override;
358   bool HasReceivedAllContentNotifications() const override;
359   bool IsReadyForHandling() const override;
360   bool MustStayActive() override;
361   const char* Type() override;
362 
AsPinchGestureBlock()363   PinchGestureBlockState* AsPinchGestureBlock() override { return this; }
364 
WasInterrupted()365   bool WasInterrupted() const { return mInterrupted; }
366 
367   void SetNeedsToWaitForContentResponse(bool aWaitForContentResponse);
368 
369  private:
370   bool mInterrupted;
371   bool mWaitingForContentResponse;
372 };
373 
374 /**
375  * This class represents a single touch block. A touch block is
376  * a set of touch events that can be cancelled by web content via
377  * touch event listeners.
378  *
379  * Every touch-start event creates a new touch block. In this case, the
380  * touch block consists of the touch-start, followed by all touch events
381  * up to but not including the next touch-start (except in the case where
382  * a long-tap happens, see below). Note that in particular we cannot know
383  * when a touch block ends until the next one is started. Most touch
384  * blocks are created by receipt of a touch-start event.
385  *
386  * Every long-tap event also creates a new touch block, since it can also
387  * be consumed by web content. In this case, when the long-tap event is
388  * dispatched to web content, a new touch block is started to hold the remaining
389  * touch events, up to but not including the next touch start (or long-tap).
390  *
391  * Additionally, if touch-action is enabled, each touch block should
392  * have a set of allowed touch behavior flags; one for each touch point.
393  * This also requires running code on the Gecko main thread, and so may
394  * be populated with some latency. The mAllowedTouchBehaviorSet and
395  * mAllowedTouchBehaviors variables track this information.
396  */
397 class TouchBlockState : public CancelableBlockState {
398  public:
399   explicit TouchBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
400                            TargetConfirmationFlags aFlags,
401                            TouchCounter& aTouchCounter);
402 
AsTouchBlock()403   TouchBlockState* AsTouchBlock() override { return this; }
404 
405   /**
406    * Set the allowed touch behavior flags for this block.
407    * @return false if this block already has these flags set, true if not.
408    */
409   bool SetAllowedTouchBehaviors(const nsTArray<TouchBehaviorFlags>& aBehaviors);
410   /**
411    * If the allowed touch behaviors have been set, populate them into
412    * |aOutBehaviors| and return true. Else, return false.
413    */
414   bool GetAllowedTouchBehaviors(
415       nsTArray<TouchBehaviorFlags>& aOutBehaviors) const;
416 
417   /**
418    * Returns true if the allowed touch behaviours have been set, or if touch
419    * action is disabled.
420    */
421   bool HasAllowedTouchBehaviors() const;
422 
423   /**
424    * Copy various properties from another block.
425    */
426   void CopyPropertiesFrom(const TouchBlockState& aOther);
427 
428   /*
429    * @return true iff this block has received all the information it could
430    *         have gotten from the content thread.
431    */
432   bool HasReceivedAllContentNotifications() const override;
433 
434   /**
435    * @return true iff this block has received all the information needed
436    *         to properly dispatch the events in the block.
437    */
438   bool IsReadyForHandling() const override;
439 
440   /**
441    * Sets a flag that indicates this input block occurred while the APZ was
442    * in a state of fast flinging. This affects gestures that may be produced
443    * from input events in this block.
444    */
445   void SetDuringFastFling();
446   /**
447    * @return true iff SetDuringFastFling was called on this block.
448    */
449   bool IsDuringFastFling() const;
450   /**
451    * Set the single-tap-occurred flag that indicates that this touch block
452    * triggered a single tap event.
453    */
454   void SetSingleTapOccurred();
455   /**
456    * @return true iff the single-tap-occurred flag is set on this block.
457    */
458   bool SingleTapOccurred() const;
459 
460   /**
461    * @return false iff touch-action is enabled and the allowed touch behaviors
462    * for this touch block do not allow pinch-zooming.
463    */
464   bool TouchActionAllowsPinchZoom() const;
465   /**
466    * @return false iff touch-action is enabled and the allowed touch behaviors
467    * for this touch block do not allow double-tap zooming.
468    */
469   bool TouchActionAllowsDoubleTapZoom() const;
470   /**
471    * @return false iff touch-action is enabled and the allowed touch behaviors
472    *         for the first touch point do not allow panning in the specified
473    *         direction(s).
474    */
475   bool TouchActionAllowsPanningX() const;
476   bool TouchActionAllowsPanningY() const;
477   bool TouchActionAllowsPanningXY() const;
478 
479   /**
480    * Notifies the input block of an incoming touch event so that the block can
481    * update its internal slop state. "Slop" refers to the area around the
482    * initial touchstart where we drop touchmove events so that content doesn't
483    * see them. The |aApzcCanConsumeEvents| parameter is factored into how large
484    * the slop area is - if this is true the slop area is larger.
485    * @return true iff the provided event is a touchmove in the slop area and
486    *         so should not be sent to content.
487    */
488   bool UpdateSlopState(const MultiTouchInput& aInput,
489                        bool aApzcCanConsumeEvents);
490   bool IsInSlop() const;
491 
492   /**
493    * Based on the slop origin and the given input event, return a best guess
494    * as to the pan direction of this touch block. Returns Nothing() if no guess
495    * can be made.
496    */
497   Maybe<ScrollDirection> GetBestGuessPanDirection(
498       const MultiTouchInput& aInput);
499 
500   /**
501    * Returns the number of touch points currently active.
502    */
503   uint32_t GetActiveTouchCount() const;
504 
505   void DispatchEvent(const InputData& aEvent) const override;
506   bool MustStayActive() override;
507   const char* Type() override;
508   TimeDuration GetTimeSinceBlockStart() const;
509 
510  private:
511   nsTArray<TouchBehaviorFlags> mAllowedTouchBehaviors;
512   bool mAllowedTouchBehaviorSet;
513   bool mDuringFastFling;
514   bool mSingleTapOccurred;
515   bool mInSlop;
516   ScreenIntPoint mSlopOrigin;
517   // A reference to the InputQueue's touch counter
518   TouchCounter& mTouchCounter;
519   TimeStamp mStartTime;
520 };
521 
522 /**
523  * This class represents a set of keyboard inputs targeted at the same Apzc.
524  */
525 class KeyboardBlockState : public InputBlockState {
526  public:
527   explicit KeyboardBlockState(
528       const RefPtr<AsyncPanZoomController>& aTargetApzc);
529 
AsKeyboardBlock()530   KeyboardBlockState* AsKeyboardBlock() override { return this; }
531 
MustStayActive()532   bool MustStayActive() override { return false; }
533 
534   /**
535    * @return Whether or not overscrolling is prevented for this keyboard block.
536    */
AllowScrollHandoff()537   bool AllowScrollHandoff() const { return false; }
538 };
539 
540 }  // namespace layers
541 }  // namespace mozilla
542 
543 #endif  // mozilla_layers_InputBlockState_h
544