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 
231   /**
232    * Returns whether or not the block is participating in a wheel transaction.
233    * This means that the block is the most recent input block to be created,
234    * and no events have occurred that would require scrolling a different
235    * frame.
236    *
237    * @return True if in a transaction, false otherwise.
238    */
239   bool InTransaction() const;
240 
241   /**
242    * Mark the block as no longer participating in a wheel transaction. This
243    * will force future wheel events to begin a new input block.
244    */
245   void EndTransaction();
246 
247   /**
248    * @return Whether or not overscrolling is prevented for this wheel block.
249    */
250   bool AllowScrollHandoff() const;
251 
252   /**
253    * Called to check and possibly end the transaction due to a timeout.
254    *
255    * @return True if the transaction ended, false otherwise.
256    */
257   bool MaybeTimeout(const TimeStamp& aTimeStamp);
258 
259   /**
260    * Update the wheel transaction state for a new event.
261    */
262   void Update(ScrollWheelInput& aEvent);
263 
GetAllowedScrollDirections()264   ScrollDirections GetAllowedScrollDirections() const {
265     return mAllowedScrollDirections;
266   }
267 
268  protected:
269   void UpdateTargetApzc(
270       const RefPtr<AsyncPanZoomController>& aTargetApzc) override;
271 
272  private:
273   TimeStamp mLastEventTime;
274   TimeStamp mLastMouseMove;
275   uint32_t mScrollSeriesCounter;
276   bool mTransactionEnded;
277   bool mIsScrollable = true;
278   ScrollDirections mAllowedScrollDirections;
279 };
280 
281 /**
282  * A block of mouse events that are part of a drag
283  */
284 class DragBlockState : public CancelableBlockState {
285  public:
286   DragBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
287                  TargetConfirmationFlags aFlags, const MouseInput& aEvent);
288 
289   bool MustStayActive() override;
290   const char* Type() override;
291 
292   bool HasReceivedMouseUp();
293   void MarkMouseUpReceived();
294 
AsDragBlock()295   DragBlockState* AsDragBlock() override { return this; }
296 
297   void SetInitialThumbPos(CSSCoord aThumbPos);
298   void SetDragMetrics(const AsyncDragMetrics& aDragMetrics);
299 
300   void DispatchEvent(const InputData& aEvent) const override;
301 
302  private:
303   AsyncDragMetrics mDragMetrics;
304   CSSCoord mInitialThumbPos;
305   bool mReceivedMouseUp;
306 };
307 
308 /**
309  * A single block of pan gesture events.
310  */
311 class PanGestureBlockState : public CancelableBlockState {
312  public:
313   PanGestureBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
314                        TargetConfirmationFlags aFlags,
315                        const PanGestureInput& aEvent);
316 
317   bool SetContentResponse(bool aPreventDefault) override;
318   bool HasReceivedAllContentNotifications() const override;
319   bool IsReadyForHandling() const override;
320   bool MustStayActive() override;
321   const char* Type() override;
322   bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
323                               TargetConfirmationState aState,
324                               InputData* aFirstInput,
325                               bool aForScrollbarDrag) override;
326 
AsPanGestureBlock()327   PanGestureBlockState* AsPanGestureBlock() override { return this; }
328 
329   /**
330    * @return Whether or not overscrolling is prevented for this block.
331    */
332   bool AllowScrollHandoff() const;
333 
WasInterrupted()334   bool WasInterrupted() const { return mInterrupted; }
335 
336   void SetNeedsToWaitForContentResponse(bool aWaitForContentResponse);
337 
GetAllowedScrollDirections()338   ScrollDirections GetAllowedScrollDirections() const {
339     return mAllowedScrollDirections;
340   }
341 
342  private:
343   bool mInterrupted;
344   bool mWaitingForContentResponse;
345   ScrollDirections mAllowedScrollDirections;
346 };
347 
348 /**
349  * A single block of pinch gesture events.
350  */
351 class PinchGestureBlockState : public CancelableBlockState {
352  public:
353   PinchGestureBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
354                          TargetConfirmationFlags aFlags);
355 
356   bool SetContentResponse(bool aPreventDefault) override;
357   bool HasReceivedAllContentNotifications() const override;
358   bool IsReadyForHandling() const override;
359   bool MustStayActive() override;
360   const char* Type() override;
361 
AsPinchGestureBlock()362   PinchGestureBlockState* AsPinchGestureBlock() override { return this; }
363 
WasInterrupted()364   bool WasInterrupted() const { return mInterrupted; }
365 
366   void SetNeedsToWaitForContentResponse(bool aWaitForContentResponse);
367 
368  private:
369   bool mInterrupted;
370   bool mWaitingForContentResponse;
371 };
372 
373 /**
374  * This class represents a single touch block. A touch block is
375  * a set of touch events that can be cancelled by web content via
376  * touch event listeners.
377  *
378  * Every touch-start event creates a new touch block. In this case, the
379  * touch block consists of the touch-start, followed by all touch events
380  * up to but not including the next touch-start (except in the case where
381  * a long-tap happens, see below). Note that in particular we cannot know
382  * when a touch block ends until the next one is started. Most touch
383  * blocks are created by receipt of a touch-start event.
384  *
385  * Every long-tap event also creates a new touch block, since it can also
386  * be consumed by web content. In this case, when the long-tap event is
387  * dispatched to web content, a new touch block is started to hold the remaining
388  * touch events, up to but not including the next touch start (or long-tap).
389  *
390  * Additionally, if touch-action is enabled, each touch block should
391  * have a set of allowed touch behavior flags; one for each touch point.
392  * This also requires running code on the Gecko main thread, and so may
393  * be populated with some latency. The mAllowedTouchBehaviorSet and
394  * mAllowedTouchBehaviors variables track this information.
395  */
396 class TouchBlockState : public CancelableBlockState {
397  public:
398   explicit TouchBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
399                            TargetConfirmationFlags aFlags,
400                            TouchCounter& aTouchCounter);
401 
AsTouchBlock()402   TouchBlockState* AsTouchBlock() override { return this; }
403 
404   /**
405    * Set the allowed touch behavior flags for this block.
406    * @return false if this block already has these flags set, true if not.
407    */
408   bool SetAllowedTouchBehaviors(const nsTArray<TouchBehaviorFlags>& aBehaviors);
409   /**
410    * If the allowed touch behaviors have been set, populate them into
411    * |aOutBehaviors| and return true. Else, return false.
412    */
413   bool GetAllowedTouchBehaviors(
414       nsTArray<TouchBehaviorFlags>& aOutBehaviors) const;
415 
416   /**
417    * Returns true if the allowed touch behaviours have been set, or if touch
418    * action is disabled.
419    */
420   bool HasAllowedTouchBehaviors() const;
421 
422   /**
423    * Copy various properties from another block.
424    */
425   void CopyPropertiesFrom(const TouchBlockState& aOther);
426 
427   /*
428    * @return true iff this block has received all the information it could
429    *         have gotten from the content thread.
430    */
431   bool HasReceivedAllContentNotifications() const override;
432 
433   /**
434    * @return true iff this block has received all the information needed
435    *         to properly dispatch the events in the block.
436    */
437   bool IsReadyForHandling() const override;
438 
439   /**
440    * Sets a flag that indicates this input block occurred while the APZ was
441    * in a state of fast flinging. This affects gestures that may be produced
442    * from input events in this block.
443    */
444   void SetDuringFastFling();
445   /**
446    * @return true iff SetDuringFastFling was called on this block.
447    */
448   bool IsDuringFastFling() const;
449   /**
450    * Set the single-tap-occurred flag that indicates that this touch block
451    * triggered a single tap event.
452    */
453   void SetSingleTapOccurred();
454   /**
455    * @return true iff the single-tap-occurred flag is set on this block.
456    */
457   bool SingleTapOccurred() const;
458 
459   /**
460    * @return false iff touch-action is enabled and the allowed touch behaviors
461    * for this touch block do not allow pinch-zooming.
462    */
463   bool TouchActionAllowsPinchZoom() const;
464   /**
465    * @return false iff touch-action is enabled and the allowed touch behaviors
466    * for this touch block do not allow double-tap zooming.
467    */
468   bool TouchActionAllowsDoubleTapZoom() const;
469   /**
470    * @return false iff touch-action is enabled and the allowed touch behaviors
471    *         for the first touch point do not allow panning in the specified
472    *         direction(s).
473    */
474   bool TouchActionAllowsPanningX() const;
475   bool TouchActionAllowsPanningY() const;
476   bool TouchActionAllowsPanningXY() const;
477 
478   /**
479    * Notifies the input block of an incoming touch event so that the block can
480    * update its internal slop state. "Slop" refers to the area around the
481    * initial touchstart where we drop touchmove events so that content doesn't
482    * see them. The |aApzcCanConsumeEvents| parameter is factored into how large
483    * the slop area is - if this is true the slop area is larger.
484    * @return true iff the provided event is a touchmove in the slop area and
485    *         so should not be sent to content.
486    */
487   bool UpdateSlopState(const MultiTouchInput& aInput,
488                        bool aApzcCanConsumeEvents);
489   bool IsInSlop() const;
490 
491   /**
492    * Based on the slop origin and the given input event, return a best guess
493    * as to the pan direction of this touch block. Returns Nothing() if no guess
494    * can be made.
495    */
496   Maybe<ScrollDirection> GetBestGuessPanDirection(
497       const MultiTouchInput& aInput);
498 
499   /**
500    * Returns the number of touch points currently active.
501    */
502   uint32_t GetActiveTouchCount() const;
503 
504   void DispatchEvent(const InputData& aEvent) const override;
505   bool MustStayActive() override;
506   const char* Type() override;
507   TimeDuration GetTimeSinceBlockStart() const;
508 
509  private:
510   nsTArray<TouchBehaviorFlags> mAllowedTouchBehaviors;
511   bool mAllowedTouchBehaviorSet;
512   bool mDuringFastFling;
513   bool mSingleTapOccurred;
514   bool mInSlop;
515   ScreenIntPoint mSlopOrigin;
516   // A reference to the InputQueue's touch counter
517   TouchCounter& mTouchCounter;
518   TimeStamp mStartTime;
519 };
520 
521 /**
522  * This class represents a set of keyboard inputs targeted at the same Apzc.
523  */
524 class KeyboardBlockState : public InputBlockState {
525  public:
526   explicit KeyboardBlockState(
527       const RefPtr<AsyncPanZoomController>& aTargetApzc);
528 
AsKeyboardBlock()529   KeyboardBlockState* AsKeyboardBlock() override { return this; }
530 
MustStayActive()531   bool MustStayActive() override { return false; }
532 
533   /**
534    * @return Whether or not overscrolling is prevented for this keyboard block.
535    */
AllowScrollHandoff()536   bool AllowScrollHandoff() const { return false; }
537 };
538 
539 }  // namespace layers
540 }  // namespace mozilla
541 
542 #endif  // mozilla_layers_InputBlockState_h
543