summaryrefslogtreecommitdiffstats
path: root/gfx/layers/apz/src/InputQueue.h
blob: 8a015c24f3b1853c1cdbc6f15b02a71ffc37a836 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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_InputQueue_h
#define mozilla_layers_InputQueue_h

#include "APZUtils.h"
#include "DragTracker.h"
#include "InputData.h"
#include "mozilla/EventForwards.h"
#include "mozilla/layers/TouchCounter.h"
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
#include "nsTArray.h"

#include <unordered_map>

namespace mozilla {

class InputData;
class MultiTouchInput;
class ScrollWheelInput;

namespace layers {

class AsyncPanZoomController;
class InputBlockState;
class CancelableBlockState;
class TouchBlockState;
class WheelBlockState;
class DragBlockState;
class PanGestureBlockState;
class PinchGestureBlockState;
class KeyboardBlockState;
class AsyncDragMetrics;
class QueuedInput;
struct APZEventResult;
struct APZHandledResult;
enum class BrowserGestureResponse : bool;

using InputBlockCallback = std::function<void(uint64_t aInputBlockId,
                                              APZHandledResult aHandledResult)>;

struct InputBlockCallbackInfo {
  nsEventStatus mEagerStatus;
  InputBlockCallback mCallback;
};

/**
 * This class stores incoming input events, associated with "input blocks",
 * until they are ready for handling.
 */
class InputQueue {
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InputQueue)

 public:
  InputQueue();

  /**
   * Notifies the InputQueue of a new incoming input event. The APZC that the
   * input event was targeted to should be provided in the |aTarget| parameter.
   * See the documentation on APZCTreeManager::ReceiveInputEvent for info on
   * return values from this function.
   */
  APZEventResult ReceiveInputEvent(
      const RefPtr<AsyncPanZoomController>& aTarget,
      TargetConfirmationFlags aFlags, InputData& aEvent,
      const Maybe<nsTArray<TouchBehaviorFlags>>& aTouchBehaviors = Nothing());
  /**
   * This function should be invoked to notify the InputQueue when web content
   * decides whether or not it wants to cancel a block of events. The block
   * id to which this applies should be provided in |aInputBlockId|.
   */
  void ContentReceivedInputBlock(uint64_t aInputBlockId, bool aPreventDefault);
  /**
   * This function should be invoked to notify the InputQueue once the target
   * APZC to handle an input block has been confirmed. In practice this should
   * generally be decidable upon receipt of the input event, but in some cases
   * we may need to query the layout engine to know for sure. The input block
   * this applies to should be specified via the |aInputBlockId| parameter.
   */
  void SetConfirmedTargetApzc(
      uint64_t aInputBlockId,
      const RefPtr<AsyncPanZoomController>& aTargetApzc);
  /**
   * This function is invoked to confirm that the drag block should be handled
   * by the APZ.
   */
  void ConfirmDragBlock(uint64_t aInputBlockId,
                        const RefPtr<AsyncPanZoomController>& aTargetApzc,
                        const AsyncDragMetrics& aDragMetrics);
  /**
   * This function should be invoked to notify the InputQueue of the touch-
   * action properties for the different touch points in an input block. The
   * input block this applies to should be specified by the |aInputBlockId|
   * parameter. If touch-action is not enabled on the platform, this function
   * does nothing and need not be called.
   */
  void SetAllowedTouchBehavior(uint64_t aInputBlockId,
                               const nsTArray<TouchBehaviorFlags>& aBehaviors);
  /**
   * Adds a new touch block at the end of the input queue that has the same
   * allowed touch behaviour flags as the the touch block currently being
   * processed. This should only be called when processing of a touch block
   * triggers the creation of a new touch block. Returns the input block id
   * of the the newly-created block.
   */
  uint64_t InjectNewTouchBlock(AsyncPanZoomController* aTarget);
  /**
   * Returns the pending input block at the head of the queue, if there is one.
   * This may return null if there all input events have been processed.
   */
  InputBlockState* GetCurrentBlock() const;
  /*
   * Returns the current pending input block as a specific kind of block. If
   * GetCurrentBlock() returns null, these functions additionally check the
   * mActiveXXXBlock field of the corresponding input type to see if there is
   * a depleted but still active input block, and returns that if found. These
   * functions may return null if no block is found.
   */
  TouchBlockState* GetCurrentTouchBlock() const;
  WheelBlockState* GetCurrentWheelBlock() const;
  DragBlockState* GetCurrentDragBlock() const;
  PanGestureBlockState* GetCurrentPanGestureBlock() const;
  PinchGestureBlockState* GetCurrentPinchGestureBlock() const;
  KeyboardBlockState* GetCurrentKeyboardBlock() const;
  /**
   * Returns true iff the pending block at the head of the queue is a touch
   * block and is ready for handling.
   */
  bool HasReadyTouchBlock() const;
  /**
   * If there is an active wheel transaction, returns the WheelBlockState
   * representing the transaction. Otherwise, returns null. "Active" in this
   * function name is the same kind of "active" as in mActiveWheelBlock - that
   * is, new incoming wheel events will go into the "active" block.
   */
  WheelBlockState* GetActiveWheelTransaction() const;
  /**
   * Remove all input blocks from the input queue.
   */
  void Clear();
  /**
   * Whether the current pending block allows scroll handoff.
   */
  bool AllowScrollHandoff() const;
  /**
   * If there is currently a drag in progress, return whether or not it was
   * targeted at a scrollbar. If the drag was newly-created and doesn't know,
   * use the provided |aOnScrollbar| to populate that information.
   */
  bool IsDragOnScrollbar(bool aOnScrollbar);

  InputBlockState* GetBlockForId(uint64_t aInputBlockId);

  void AddInputBlockCallback(uint64_t aInputBlockId,
                             InputBlockCallbackInfo&& aCallback);

  void SetBrowserGestureResponse(uint64_t aInputBlockId,
                                 BrowserGestureResponse aResponse);

 private:
  ~InputQueue();

  // RAII class for automatically running a timeout task that may
  // need to be run immediately after an event has been queued.
  class AutoRunImmediateTimeout final {
   public:
    explicit AutoRunImmediateTimeout(InputQueue* aQueue);
    ~AutoRunImmediateTimeout();

   private:
    InputQueue* mQueue;
  };

  TouchBlockState* StartNewTouchBlock(
      const RefPtr<AsyncPanZoomController>& aTarget,
      TargetConfirmationFlags aFlags, bool aCopyPropertiesFromCurrent);

  /**
   * If animations are present for the current pending input block, cancel
   * them as soon as possible.
   */
  void CancelAnimationsForNewBlock(InputBlockState* aBlock,
                                   CancelAnimationFlags aExtraFlags = Default);

  /**
   * If we need to wait for a content response, schedule that now. Returns true
   * if the timeout was scheduled, false otherwise.
   */
  bool MaybeRequestContentResponse(
      const RefPtr<AsyncPanZoomController>& aTarget,
      CancelableBlockState* aBlock);

  APZEventResult ReceiveTouchInput(
      const RefPtr<AsyncPanZoomController>& aTarget,
      TargetConfirmationFlags aFlags, const MultiTouchInput& aEvent,
      const Maybe<nsTArray<TouchBehaviorFlags>>& aTouchBehaviors);
  APZEventResult ReceiveMouseInput(
      const RefPtr<AsyncPanZoomController>& aTarget,
      TargetConfirmationFlags aFlags, MouseInput& aEvent);
  APZEventResult ReceiveScrollWheelInput(
      const RefPtr<AsyncPanZoomController>& aTarget,
      TargetConfirmationFlags aFlags, const ScrollWheelInput& aEvent);
  APZEventResult ReceivePanGestureInput(
      const RefPtr<AsyncPanZoomController>& aTarget,
      TargetConfirmationFlags aFlags, const PanGestureInput& aEvent);
  APZEventResult ReceivePinchGestureInput(
      const RefPtr<AsyncPanZoomController>& aTarget,
      TargetConfirmationFlags aFlags, const PinchGestureInput& aEvent);
  APZEventResult ReceiveKeyboardInput(
      const RefPtr<AsyncPanZoomController>& aTarget,
      TargetConfirmationFlags aFlags, const KeyboardInput& aEvent);

  /**
   * Helper function that searches mQueuedInputs for the first block matching
   * the given id, and returns it. If |aOutFirstInput| is non-null, it is
   * populated with a pointer to the first input in mQueuedInputs that
   * corresponds to the block, or null if no such input was found. Note that
   * even if there are no inputs in mQueuedInputs, this function can return
   * non-null if the block id provided matches one of the depleted-but-still-
   * active blocks (mActiveTouchBlock, mActiveWheelBlock, etc.).
   */
  InputBlockState* FindBlockForId(uint64_t aInputBlockId,
                                  InputData** aOutFirstInput);
  void ScheduleMainThreadTimeout(const RefPtr<AsyncPanZoomController>& aTarget,
                                 CancelableBlockState* aBlock);
  void MainThreadTimeout(uint64_t aInputBlockId);
  void MaybeLongTapTimeout(uint64_t aInputBlockId);
  void ProcessQueue();
  bool CanDiscardBlock(InputBlockState* aBlock);
  void UpdateActiveApzc(const RefPtr<AsyncPanZoomController>& aNewActive);

 private:
  // The queue of input events that have not yet been fully processed.
  // This member must only be accessed on the controller/UI thread.
  nsTArray<UniquePtr<QueuedInput>> mQueuedInputs;

  // These are the most recently created blocks of each input type. They are
  // "active" in the sense that new inputs of that type are associated with
  // them. Note that these pointers may be null if no inputs of the type have
  // arrived, or if the inputs for the type formed a complete block that was
  // then discarded.
  RefPtr<TouchBlockState> mActiveTouchBlock;
  RefPtr<WheelBlockState> mActiveWheelBlock;
  RefPtr<DragBlockState> mActiveDragBlock;
  RefPtr<PanGestureBlockState> mActivePanGestureBlock;
  RefPtr<PinchGestureBlockState> mActivePinchGestureBlock;
  RefPtr<KeyboardBlockState> mActiveKeyboardBlock;

  // The APZC to which the last event was delivered
  RefPtr<AsyncPanZoomController> mLastActiveApzc;

  // Track touches so we know when to clear mLastActiveApzc
  TouchCounter mTouchCounter;

  // Track mouse inputs so we know if we're in a drag or not
  DragTracker mDragTracker;

  // Temporarily stores a timeout task that needs to be run as soon as
  // as the event that triggered it has been queued.
  RefPtr<Runnable> mImmediateTimeout;

  // Maps input block ids to callbacks that will be invoked when the input block
  // is ready for handling.
  using InputBlockCallbackMap =
      std::unordered_map<uint64_t, InputBlockCallbackInfo>;
  InputBlockCallbackMap mInputBlockCallbacks;
};

}  // namespace layers
}  // namespace mozilla

#endif  // mozilla_layers_InputQueue_h