Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set sw=2 ts=8 et 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 : #include "InputQueue.h"
8 :
9 : #include "AsyncPanZoomController.h"
10 : #include "gfxPrefs.h"
11 : #include "InputBlockState.h"
12 : #include "LayersLogging.h"
13 : #include "mozilla/layers/APZThreadUtils.h"
14 : #include "OverscrollHandoffState.h"
15 : #include "QueuedInput.h"
16 :
17 : #define INPQ_LOG(...)
18 : // #define INPQ_LOG(...) printf_stderr("INPQ: " __VA_ARGS__)
19 :
20 : namespace mozilla {
21 : namespace layers {
22 :
23 1 : InputQueue::InputQueue()
24 : {
25 1 : }
26 :
27 0 : InputQueue::~InputQueue() {
28 0 : mQueuedInputs.Clear();
29 0 : }
30 :
31 : nsEventStatus
32 4 : InputQueue::ReceiveInputEvent(const RefPtr<AsyncPanZoomController>& aTarget,
33 : bool aTargetConfirmed,
34 : const InputData& aEvent,
35 : uint64_t* aOutInputBlockId) {
36 4 : APZThreadUtils::AssertOnControllerThread();
37 :
38 4 : switch (aEvent.mInputType) {
39 : case MULTITOUCH_INPUT: {
40 0 : const MultiTouchInput& event = aEvent.AsMultiTouchInput();
41 0 : return ReceiveTouchInput(aTarget, aTargetConfirmed, event, aOutInputBlockId);
42 : }
43 :
44 : case SCROLLWHEEL_INPUT: {
45 0 : const ScrollWheelInput& event = aEvent.AsScrollWheelInput();
46 0 : return ReceiveScrollWheelInput(aTarget, aTargetConfirmed, event, aOutInputBlockId);
47 : }
48 :
49 : case PANGESTURE_INPUT: {
50 0 : const PanGestureInput& event = aEvent.AsPanGestureInput();
51 0 : return ReceivePanGestureInput(aTarget, aTargetConfirmed, event, aOutInputBlockId);
52 : }
53 :
54 : case MOUSE_INPUT: {
55 4 : const MouseInput& event = aEvent.AsMouseInput();
56 4 : return ReceiveMouseInput(aTarget, aTargetConfirmed, event, aOutInputBlockId);
57 : }
58 :
59 : case KEYBOARD_INPUT: {
60 : // Every keyboard input must have a confirmed target
61 0 : MOZ_ASSERT(aTarget && aTargetConfirmed);
62 :
63 0 : const KeyboardInput& event = aEvent.AsKeyboardInput();
64 0 : return ReceiveKeyboardInput(aTarget, event, aOutInputBlockId);
65 : }
66 :
67 : default:
68 : // The return value for non-touch input is only used by tests, so just pass
69 : // through the return value for now. This can be changed later if needed.
70 : // TODO (bug 1098430): we will eventually need to have smarter handling for
71 : // non-touch events as well.
72 0 : return aTarget->HandleInputEvent(aEvent, aTarget->GetTransformToThis());
73 : }
74 : }
75 :
76 : nsEventStatus
77 0 : InputQueue::ReceiveTouchInput(const RefPtr<AsyncPanZoomController>& aTarget,
78 : bool aTargetConfirmed,
79 : const MultiTouchInput& aEvent,
80 : uint64_t* aOutInputBlockId) {
81 0 : TouchBlockState* block = nullptr;
82 0 : if (aEvent.mType == MultiTouchInput::MULTITOUCH_START) {
83 0 : nsTArray<TouchBehaviorFlags> currentBehaviors;
84 0 : bool haveBehaviors = false;
85 0 : if (!gfxPrefs::TouchActionEnabled()) {
86 0 : haveBehaviors = true;
87 0 : } else if (mActiveTouchBlock) {
88 0 : haveBehaviors = mActiveTouchBlock->GetAllowedTouchBehaviors(currentBehaviors);
89 : // If the behaviours aren't set, but the main-thread response timer on
90 : // the block is expired we still treat it as though it has behaviors,
91 : // because in that case we still want to interrupt the fast-fling and
92 : // use the default behaviours.
93 0 : haveBehaviors |= mActiveTouchBlock->IsContentResponseTimerExpired();
94 : }
95 :
96 0 : block = StartNewTouchBlock(aTarget, aTargetConfirmed, false);
97 : INPQ_LOG("started new touch block %p id %" PRIu64 " for target %p\n",
98 : block, block->GetBlockId(), aTarget.get());
99 :
100 : // XXX using the chain from |block| here may be wrong in cases where the
101 : // target isn't confirmed and the real target turns out to be something
102 : // else. For now assume this is rare enough that it's not an issue.
103 0 : if (mQueuedInputs.IsEmpty() &&
104 0 : aEvent.mTouches.Length() == 1 &&
105 0 : block->GetOverscrollHandoffChain()->HasFastFlungApzc() &&
106 : haveBehaviors) {
107 : // If we're already in a fast fling, and a single finger goes down, then
108 : // we want special handling for the touch event, because it shouldn't get
109 : // delivered to content. Note that we don't set this flag when going
110 : // from a fast fling to a pinch state (i.e. second finger goes down while
111 : // the first finger is moving).
112 0 : block->SetDuringFastFling();
113 0 : block->SetConfirmedTargetApzc(aTarget,
114 : InputBlockState::TargetConfirmationState::eConfirmed,
115 0 : nullptr /* the block was just created so it has no events */);
116 0 : if (gfxPrefs::TouchActionEnabled()) {
117 0 : block->SetAllowedTouchBehaviors(currentBehaviors);
118 : }
119 : INPQ_LOG("block %p tagged as fast-motion\n", block);
120 : }
121 :
122 0 : CancelAnimationsForNewBlock(block);
123 :
124 0 : MaybeRequestContentResponse(aTarget, block);
125 : } else {
126 0 : block = mActiveTouchBlock.get();
127 0 : if (!block) {
128 0 : NS_WARNING("Received a non-start touch event while no touch blocks active!");
129 0 : return nsEventStatus_eIgnore;
130 : }
131 :
132 : INPQ_LOG("received new event in block %p\n", block);
133 : }
134 :
135 0 : if (aOutInputBlockId) {
136 0 : *aOutInputBlockId = block->GetBlockId();
137 : }
138 :
139 : // Note that the |aTarget| the APZCTM sent us may contradict the confirmed
140 : // target set on the block. In this case the confirmed target (which may be
141 : // null) should take priority. This is equivalent to just always using the
142 : // target (confirmed or not) from the block.
143 0 : RefPtr<AsyncPanZoomController> target = block->GetTargetApzc();
144 :
145 0 : nsEventStatus result = nsEventStatus_eIgnore;
146 :
147 : // XXX calling ArePointerEventsConsumable on |target| may be wrong here if
148 : // the target isn't confirmed and the real target turns out to be something
149 : // else. For now assume this is rare enough that it's not an issue.
150 0 : if (block->IsDuringFastFling()) {
151 : INPQ_LOG("dropping event due to block %p being in fast motion\n", block);
152 0 : result = nsEventStatus_eConsumeNoDefault;
153 0 : } else if (target && target->ArePointerEventsConsumable(block, aEvent.mTouches.Length())) {
154 0 : if (block->UpdateSlopState(aEvent, true)) {
155 : INPQ_LOG("dropping event due to block %p being in slop\n", block);
156 0 : result = nsEventStatus_eConsumeNoDefault;
157 : } else {
158 0 : result = nsEventStatus_eConsumeDoDefault;
159 : }
160 0 : } else if (block->UpdateSlopState(aEvent, false)) {
161 : INPQ_LOG("dropping event due to block %p being in mini-slop\n", block);
162 0 : result = nsEventStatus_eConsumeNoDefault;
163 : }
164 0 : mQueuedInputs.AppendElement(MakeUnique<QueuedInput>(aEvent, *block));
165 0 : ProcessQueue();
166 0 : return result;
167 : }
168 :
169 : nsEventStatus
170 4 : InputQueue::ReceiveMouseInput(const RefPtr<AsyncPanZoomController>& aTarget,
171 : bool aTargetConfirmed,
172 : const MouseInput& aEvent,
173 : uint64_t* aOutInputBlockId) {
174 : // On a new mouse down we can have a new target so we must force a new block
175 : // with a new target.
176 4 : bool newBlock = DragTracker::StartsDrag(aEvent);
177 :
178 4 : DragBlockState* block = newBlock ? nullptr : mActiveDragBlock.get();
179 4 : if (block && block->HasReceivedMouseUp()) {
180 0 : block = nullptr;
181 : }
182 :
183 4 : if (!block && mDragTracker.InDrag()) {
184 : // If there's no current drag block, but we're getting a move with a button
185 : // down, we need to start a new drag block because we're obviously already
186 : // in the middle of a drag (it probably got interrupted by something else).
187 : INPQ_LOG("got a drag event outside a drag block, need to create a block to hold it\n");
188 0 : newBlock = true;
189 : }
190 :
191 4 : mDragTracker.Update(aEvent);
192 :
193 4 : if (!newBlock && !block) {
194 : // This input event is not in a drag block, so we're not doing anything
195 : // with it, return eIgnore.
196 4 : return nsEventStatus_eIgnore;
197 : }
198 :
199 0 : if (!block) {
200 0 : MOZ_ASSERT(newBlock);
201 0 : block = new DragBlockState(aTarget, aTargetConfirmed, aEvent);
202 :
203 : INPQ_LOG("started new drag block %p id %" PRIu64 " for %sconfirmed target %p\n",
204 : block, block->GetBlockId(), aTargetConfirmed ? "" : "un", aTarget.get());
205 :
206 0 : mActiveDragBlock = block;
207 :
208 0 : CancelAnimationsForNewBlock(block);
209 0 : MaybeRequestContentResponse(aTarget, block);
210 : }
211 :
212 0 : if (aOutInputBlockId) {
213 0 : *aOutInputBlockId = block->GetBlockId();
214 : }
215 :
216 0 : mQueuedInputs.AppendElement(MakeUnique<QueuedInput>(aEvent, *block));
217 0 : ProcessQueue();
218 :
219 0 : if (DragTracker::EndsDrag(aEvent)) {
220 0 : block->MarkMouseUpReceived();
221 : }
222 :
223 : // The event is part of a drag block and could potentially cause
224 : // scrolling, so return DoDefault.
225 0 : return nsEventStatus_eConsumeDoDefault;
226 : }
227 :
228 : nsEventStatus
229 0 : InputQueue::ReceiveScrollWheelInput(const RefPtr<AsyncPanZoomController>& aTarget,
230 : bool aTargetConfirmed,
231 : const ScrollWheelInput& aEvent,
232 : uint64_t* aOutInputBlockId) {
233 0 : WheelBlockState* block = mActiveWheelBlock.get();
234 : // If the block is not accepting new events we'll create a new input block
235 : // (and therefore a new wheel transaction).
236 0 : if (block &&
237 0 : (!block->ShouldAcceptNewEvent() ||
238 0 : block->MaybeTimeout(aEvent)))
239 : {
240 0 : block = nullptr;
241 : }
242 :
243 0 : MOZ_ASSERT(!block || block->InTransaction());
244 :
245 0 : if (!block) {
246 0 : block = new WheelBlockState(aTarget, aTargetConfirmed, aEvent);
247 : INPQ_LOG("started new scroll wheel block %p id %" PRIu64 " for target %p\n",
248 : block, block->GetBlockId(), aTarget.get());
249 :
250 0 : mActiveWheelBlock = block;
251 :
252 0 : CancelAnimationsForNewBlock(block, ExcludeWheel);
253 0 : MaybeRequestContentResponse(aTarget, block);
254 : } else {
255 : INPQ_LOG("received new event in block %p\n", block);
256 : }
257 :
258 0 : if (aOutInputBlockId) {
259 0 : *aOutInputBlockId = block->GetBlockId();
260 : }
261 :
262 : // Note that the |aTarget| the APZCTM sent us may contradict the confirmed
263 : // target set on the block. In this case the confirmed target (which may be
264 : // null) should take priority. This is equivalent to just always using the
265 : // target (confirmed or not) from the block, which is what
266 : // ProcessQueue() does.
267 0 : mQueuedInputs.AppendElement(MakeUnique<QueuedInput>(aEvent, *block));
268 :
269 : // The WheelBlockState needs to affix a counter to the event before we process
270 : // it. Note that the counter is affixed to the copy in the queue rather than
271 : // |aEvent|.
272 0 : block->Update(mQueuedInputs.LastElement()->Input()->AsScrollWheelInput());
273 :
274 0 : ProcessQueue();
275 :
276 0 : return nsEventStatus_eConsumeDoDefault;
277 : }
278 :
279 : nsEventStatus
280 0 : InputQueue::ReceiveKeyboardInput(const RefPtr<AsyncPanZoomController>& aTarget,
281 : const KeyboardInput& aEvent,
282 : uint64_t* aOutInputBlockId) {
283 0 : KeyboardBlockState* block = mActiveKeyboardBlock.get();
284 :
285 : // If the block is targeting a different Apzc than this keyboard event then
286 : // we'll create a new input block
287 0 : if (block && block->GetTargetApzc() != aTarget) {
288 0 : block = nullptr;
289 : }
290 :
291 0 : if (!block) {
292 0 : block = new KeyboardBlockState(aTarget);
293 : INPQ_LOG("started new keyboard block %p id %" PRIu64 " for target %p\n",
294 : block, block->GetBlockId(), aTarget.get());
295 :
296 0 : mActiveKeyboardBlock = block;
297 : } else {
298 : INPQ_LOG("received new event in block %p\n", block);
299 : }
300 :
301 0 : if (aOutInputBlockId) {
302 0 : *aOutInputBlockId = block->GetBlockId();
303 : }
304 :
305 0 : mQueuedInputs.AppendElement(MakeUnique<QueuedInput>(aEvent, *block));
306 :
307 0 : ProcessQueue();
308 :
309 0 : return nsEventStatus_eConsumeNoDefault;
310 : }
311 :
312 : static bool
313 0 : CanScrollTargetHorizontally(const PanGestureInput& aInitialEvent,
314 : PanGestureBlockState* aBlock)
315 : {
316 0 : PanGestureInput horizontalComponent = aInitialEvent;
317 0 : horizontalComponent.mPanDisplacement.y = 0;
318 : RefPtr<AsyncPanZoomController> horizontallyScrollableAPZC =
319 0 : aBlock->GetOverscrollHandoffChain()->FindFirstScrollable(horizontalComponent);
320 0 : return horizontallyScrollableAPZC && horizontallyScrollableAPZC == aBlock->GetTargetApzc();
321 : }
322 :
323 : nsEventStatus
324 0 : InputQueue::ReceivePanGestureInput(const RefPtr<AsyncPanZoomController>& aTarget,
325 : bool aTargetConfirmed,
326 : const PanGestureInput& aEvent,
327 : uint64_t* aOutInputBlockId) {
328 0 : if (aEvent.mType == PanGestureInput::PANGESTURE_MAYSTART ||
329 0 : aEvent.mType == PanGestureInput::PANGESTURE_CANCELLED) {
330 : // Ignore these events for now.
331 0 : return nsEventStatus_eConsumeDoDefault;
332 : }
333 :
334 0 : PanGestureBlockState* block = nullptr;
335 0 : if (aEvent.mType != PanGestureInput::PANGESTURE_START) {
336 0 : block = mActivePanGestureBlock.get();
337 : }
338 :
339 0 : PanGestureInput event = aEvent;
340 0 : nsEventStatus result = nsEventStatus_eConsumeDoDefault;
341 :
342 0 : if (!block || block->WasInterrupted()) {
343 0 : if (event.mType != PanGestureInput::PANGESTURE_START) {
344 : // Only PANGESTURE_START events are allowed to start a new pan gesture
345 : // block, but we really want to start a new block here, so we magically
346 : // turn this input into a PANGESTURE_START.
347 : INPQ_LOG("transmogrifying pan input %d to PANGESTURE_START for new block\n",
348 : event.mType);
349 0 : event.mType = PanGestureInput::PANGESTURE_START;
350 : }
351 0 : block = new PanGestureBlockState(aTarget, aTargetConfirmed, event);
352 : INPQ_LOG("started new pan gesture block %p id %" PRIu64 " for target %p\n",
353 : block, block->GetBlockId(), aTarget.get());
354 :
355 0 : if (aTargetConfirmed &&
356 0 : event.mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection &&
357 0 : !CanScrollTargetHorizontally(event, block)) {
358 : // This event may trigger a swipe gesture, depending on what our caller
359 : // wants to do it. We need to suspend handling of this block until we get
360 : // a content response which will tell us whether to proceed or abort the
361 : // block.
362 0 : block->SetNeedsToWaitForContentResponse(true);
363 :
364 : // Inform our caller that we haven't scrolled in response to the event
365 : // and that a swipe can be started from this event if desired.
366 0 : result = nsEventStatus_eIgnore;
367 : }
368 :
369 0 : mActivePanGestureBlock = block;
370 :
371 0 : CancelAnimationsForNewBlock(block);
372 0 : MaybeRequestContentResponse(aTarget, block);
373 : } else {
374 : INPQ_LOG("received new event in block %p\n", block);
375 : }
376 :
377 0 : if (aOutInputBlockId) {
378 0 : *aOutInputBlockId = block->GetBlockId();
379 : }
380 :
381 : // Note that the |aTarget| the APZCTM sent us may contradict the confirmed
382 : // target set on the block. In this case the confirmed target (which may be
383 : // null) should take priority. This is equivalent to just always using the
384 : // target (confirmed or not) from the block, which is what
385 : // ProcessQueue() does.
386 0 : mQueuedInputs.AppendElement(MakeUnique<QueuedInput>(event, *block));
387 0 : ProcessQueue();
388 :
389 0 : return result;
390 : }
391 :
392 : void
393 0 : InputQueue::CancelAnimationsForNewBlock(InputBlockState* aBlock,
394 : CancelAnimationFlags aExtraFlags)
395 : {
396 : // We want to cancel animations here as soon as possible (i.e. without waiting for
397 : // content responses) because a finger has gone down and we don't want to keep moving
398 : // the content under the finger. However, to prevent "future" touchstart events from
399 : // interfering with "past" animations (i.e. from a previous touch block that is still
400 : // being processed) we only do this animation-cancellation if there are no older
401 : // touch blocks still in the queue.
402 0 : if (mQueuedInputs.IsEmpty()) {
403 0 : aBlock->GetOverscrollHandoffChain()->CancelAnimations(
404 0 : aExtraFlags | ExcludeOverscroll | ScrollSnap);
405 : }
406 0 : }
407 :
408 : void
409 0 : InputQueue::MaybeRequestContentResponse(const RefPtr<AsyncPanZoomController>& aTarget,
410 : CancelableBlockState* aBlock)
411 : {
412 0 : bool waitForMainThread = false;
413 0 : if (aBlock->IsTargetConfirmed()) {
414 : // Content won't prevent-default this, so we can just set the flag directly.
415 : INPQ_LOG("not waiting for content response on block %p\n", aBlock);
416 0 : aBlock->SetContentResponse(false);
417 : } else {
418 0 : waitForMainThread = true;
419 : }
420 0 : if (aBlock->AsTouchBlock() && gfxPrefs::TouchActionEnabled()) {
421 : // waitForMainThread is set to true unconditionally here, but if the APZCTM
422 : // has the touch-action behaviours for this block, it will set it
423 : // immediately after we unwind out of this ReceiveInputEvent call. So even
424 : // though we are scheduling the main-thread timeout, we might end up not
425 : // waiting.
426 : INPQ_LOG("waiting for main thread touch-action info on block %p\n", aBlock);
427 0 : waitForMainThread = true;
428 : }
429 0 : if (waitForMainThread) {
430 : // We either don't know for sure if aTarget is the right APZC, or we may
431 : // need to wait to give content the opportunity to prevent-default the
432 : // touch events. Either way we schedule a timeout so the main thread stuff
433 : // can run.
434 0 : ScheduleMainThreadTimeout(aTarget, aBlock);
435 : }
436 0 : }
437 :
438 : uint64_t
439 0 : InputQueue::InjectNewTouchBlock(AsyncPanZoomController* aTarget)
440 : {
441 0 : TouchBlockState* block = StartNewTouchBlock(aTarget,
442 : /* aTargetConfirmed = */ true,
443 0 : /* aCopyPropertiesFromCurrent = */ true);
444 : INPQ_LOG("injecting new touch block %p with id %" PRIu64 " and target %p\n",
445 : block, block->GetBlockId(), aTarget);
446 0 : ScheduleMainThreadTimeout(aTarget, block);
447 0 : return block->GetBlockId();
448 : }
449 :
450 : TouchBlockState*
451 0 : InputQueue::StartNewTouchBlock(const RefPtr<AsyncPanZoomController>& aTarget,
452 : bool aTargetConfirmed,
453 : bool aCopyPropertiesFromCurrent)
454 : {
455 : TouchBlockState* newBlock = new TouchBlockState(aTarget, aTargetConfirmed,
456 0 : mTouchCounter);
457 0 : if (aCopyPropertiesFromCurrent) {
458 : // We should never enter here without a current touch block, because this
459 : // codepath is invoked from the OnLongPress handler in
460 : // AsyncPanZoomController, which should bail out if there is no current
461 : // touch block.
462 0 : MOZ_ASSERT(GetCurrentTouchBlock());
463 0 : newBlock->CopyPropertiesFrom(*GetCurrentTouchBlock());
464 : }
465 :
466 0 : mActiveTouchBlock = newBlock;
467 0 : return newBlock;
468 : }
469 :
470 : InputBlockState*
471 0 : InputQueue::GetCurrentBlock() const
472 : {
473 0 : APZThreadUtils::AssertOnControllerThread();
474 0 : return mQueuedInputs.IsEmpty() ? nullptr : mQueuedInputs[0]->Block();
475 : }
476 :
477 : TouchBlockState*
478 0 : InputQueue::GetCurrentTouchBlock() const
479 : {
480 0 : InputBlockState* block = GetCurrentBlock();
481 0 : return block ? block->AsTouchBlock() : mActiveTouchBlock.get();
482 : }
483 :
484 : WheelBlockState*
485 0 : InputQueue::GetCurrentWheelBlock() const
486 : {
487 0 : InputBlockState* block = GetCurrentBlock();
488 0 : return block ? block->AsWheelBlock() : mActiveWheelBlock.get();
489 : }
490 :
491 : DragBlockState*
492 0 : InputQueue::GetCurrentDragBlock() const
493 : {
494 0 : InputBlockState* block = GetCurrentBlock();
495 0 : return block ? block->AsDragBlock() : mActiveDragBlock.get();
496 : }
497 :
498 : PanGestureBlockState*
499 0 : InputQueue::GetCurrentPanGestureBlock() const
500 : {
501 0 : InputBlockState* block = GetCurrentBlock();
502 0 : return block ? block->AsPanGestureBlock() : mActivePanGestureBlock.get();
503 : }
504 :
505 : KeyboardBlockState*
506 0 : InputQueue::GetCurrentKeyboardBlock() const
507 : {
508 0 : InputBlockState* block = GetCurrentBlock();
509 0 : return block ? block->AsKeyboardBlock() : mActiveKeyboardBlock.get();
510 : }
511 :
512 : WheelBlockState*
513 6 : InputQueue::GetActiveWheelTransaction() const
514 : {
515 6 : WheelBlockState* block = mActiveWheelBlock.get();
516 6 : if (!block || !block->InTransaction()) {
517 6 : return nullptr;
518 : }
519 0 : return block;
520 : }
521 :
522 : bool
523 0 : InputQueue::HasReadyTouchBlock() const
524 : {
525 0 : return !mQueuedInputs.IsEmpty() &&
526 0 : mQueuedInputs[0]->Block()->AsTouchBlock() &&
527 0 : mQueuedInputs[0]->Block()->AsTouchBlock()->IsReadyForHandling();
528 : }
529 :
530 : bool
531 0 : InputQueue::AllowScrollHandoff() const
532 : {
533 0 : if (GetCurrentWheelBlock()) {
534 0 : return GetCurrentWheelBlock()->AllowScrollHandoff();
535 : }
536 0 : if (GetCurrentPanGestureBlock()) {
537 0 : return GetCurrentPanGestureBlock()->AllowScrollHandoff();
538 : }
539 0 : return true;
540 : }
541 :
542 : bool
543 0 : InputQueue::IsDragOnScrollbar(bool aHitScrollbar)
544 : {
545 0 : if (!mDragTracker.InDrag()) {
546 0 : return false;
547 : }
548 : // Now that we know we are in a drag, get the info from the drag tracker.
549 : // We keep it in the tracker rather than the block because the block can get
550 : // interrupted by something else (like a wheel event) and then a new block
551 : // will get created without the info we want. The tracker will persist though.
552 0 : return mDragTracker.IsOnScrollbar(aHitScrollbar);
553 : }
554 :
555 : void
556 0 : InputQueue::ScheduleMainThreadTimeout(const RefPtr<AsyncPanZoomController>& aTarget,
557 : CancelableBlockState* aBlock) {
558 : INPQ_LOG("scheduling main thread timeout for target %p\n", aTarget.get());
559 0 : aBlock->StartContentResponseTimer();
560 0 : aTarget->PostDelayedTask(
561 0 : NewRunnableMethod<uint64_t>("layers::InputQueue::MainThreadTimeout",
562 : this,
563 : &InputQueue::MainThreadTimeout,
564 0 : aBlock->GetBlockId()),
565 0 : gfxPrefs::APZContentResponseTimeout());
566 0 : }
567 :
568 : InputBlockState*
569 0 : InputQueue::FindBlockForId(uint64_t aInputBlockId,
570 : InputData** aOutFirstInput)
571 : {
572 0 : for (const auto& queuedInput : mQueuedInputs) {
573 0 : if (queuedInput->Block()->GetBlockId() == aInputBlockId) {
574 0 : if (aOutFirstInput) {
575 0 : *aOutFirstInput = queuedInput->Input();
576 : }
577 0 : return queuedInput->Block();
578 : }
579 : }
580 :
581 0 : CancelableBlockState* block = nullptr;
582 0 : if (mActiveTouchBlock && mActiveTouchBlock->GetBlockId() == aInputBlockId) {
583 0 : block = mActiveTouchBlock.get();
584 0 : } else if (mActiveWheelBlock && mActiveWheelBlock->GetBlockId() == aInputBlockId) {
585 0 : block = mActiveWheelBlock.get();
586 0 : } else if (mActiveDragBlock && mActiveDragBlock->GetBlockId() == aInputBlockId) {
587 0 : block = mActiveDragBlock.get();
588 0 : } else if (mActivePanGestureBlock && mActivePanGestureBlock->GetBlockId() == aInputBlockId) {
589 0 : block = mActivePanGestureBlock.get();
590 : }
591 : // Since we didn't encounter this block while iterating through mQueuedInputs,
592 : // it must have no events associated with it at the moment.
593 0 : if (aOutFirstInput) {
594 0 : *aOutFirstInput = nullptr;
595 : }
596 0 : return block;
597 : }
598 :
599 : void
600 0 : InputQueue::MainThreadTimeout(uint64_t aInputBlockId) {
601 0 : APZThreadUtils::AssertOnControllerThread();
602 :
603 : INPQ_LOG("got a main thread timeout; block=%" PRIu64 "\n", aInputBlockId);
604 0 : bool success = false;
605 0 : InputData* firstInput = nullptr;
606 0 : InputBlockState* inputBlock = FindBlockForId(aInputBlockId, &firstInput);
607 0 : if (inputBlock && inputBlock->AsCancelableBlock()) {
608 0 : CancelableBlockState* block = inputBlock->AsCancelableBlock();
609 : // time out the touch-listener response and also confirm the existing
610 : // target apzc in the case where the main thread doesn't get back to us
611 : // fast enough.
612 0 : success = block->TimeoutContentResponse();
613 0 : success |= block->SetConfirmedTargetApzc(
614 : block->GetTargetApzc(),
615 : InputBlockState::TargetConfirmationState::eTimedOut,
616 0 : firstInput);
617 0 : } else if (inputBlock) {
618 0 : NS_WARNING("input block is not a cancelable block");
619 : }
620 0 : if (success) {
621 0 : ProcessQueue();
622 : }
623 0 : }
624 :
625 : void
626 0 : InputQueue::ContentReceivedInputBlock(uint64_t aInputBlockId, bool aPreventDefault) {
627 0 : APZThreadUtils::AssertOnControllerThread();
628 :
629 : INPQ_LOG("got a content response; block=%" PRIu64 "\n", aInputBlockId);
630 0 : bool success = false;
631 0 : InputBlockState* inputBlock = FindBlockForId(aInputBlockId, nullptr);
632 0 : if (inputBlock && inputBlock->AsCancelableBlock()) {
633 0 : CancelableBlockState* block = inputBlock->AsCancelableBlock();
634 0 : success = block->SetContentResponse(aPreventDefault);
635 0 : block->RecordContentResponseTime();
636 0 : } else if (inputBlock) {
637 0 : NS_WARNING("input block is not a cancelable block");
638 : }
639 0 : if (success) {
640 0 : ProcessQueue();
641 : }
642 0 : }
643 :
644 : void
645 0 : InputQueue::SetConfirmedTargetApzc(uint64_t aInputBlockId, const RefPtr<AsyncPanZoomController>& aTargetApzc) {
646 0 : APZThreadUtils::AssertOnControllerThread();
647 :
648 : INPQ_LOG("got a target apzc; block=%" PRIu64 " guid=%s\n",
649 : aInputBlockId, aTargetApzc ? Stringify(aTargetApzc->GetGuid()).c_str() : "");
650 0 : bool success = false;
651 0 : InputData* firstInput = nullptr;
652 0 : InputBlockState* inputBlock = FindBlockForId(aInputBlockId, &firstInput);
653 0 : if (inputBlock && inputBlock->AsCancelableBlock()) {
654 0 : CancelableBlockState* block = inputBlock->AsCancelableBlock();
655 0 : success = block->SetConfirmedTargetApzc(aTargetApzc,
656 : InputBlockState::TargetConfirmationState::eConfirmed,
657 0 : firstInput);
658 0 : block->RecordContentResponseTime();
659 0 : } else if (inputBlock) {
660 0 : NS_WARNING("input block is not a cancelable block");
661 : }
662 0 : if (success) {
663 0 : ProcessQueue();
664 : }
665 0 : }
666 :
667 : void
668 0 : InputQueue::ConfirmDragBlock(uint64_t aInputBlockId, const RefPtr<AsyncPanZoomController>& aTargetApzc,
669 : const AsyncDragMetrics& aDragMetrics)
670 : {
671 0 : APZThreadUtils::AssertOnControllerThread();
672 :
673 : INPQ_LOG("got a target apzc; block=%" PRIu64 " guid=%s\n",
674 : aInputBlockId, aTargetApzc ? Stringify(aTargetApzc->GetGuid()).c_str() : "");
675 0 : bool success = false;
676 0 : InputData* firstInput = nullptr;
677 0 : InputBlockState* inputBlock = FindBlockForId(aInputBlockId, &firstInput);
678 0 : if (inputBlock && inputBlock->AsDragBlock()) {
679 0 : DragBlockState* block = inputBlock->AsDragBlock();
680 0 : block->SetDragMetrics(aDragMetrics);
681 0 : success = block->SetConfirmedTargetApzc(aTargetApzc,
682 : InputBlockState::TargetConfirmationState::eConfirmed,
683 0 : firstInput);
684 0 : block->RecordContentResponseTime();
685 : }
686 0 : if (success) {
687 0 : ProcessQueue();
688 : }
689 0 : }
690 :
691 : void
692 0 : InputQueue::SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aBehaviors) {
693 0 : APZThreadUtils::AssertOnControllerThread();
694 :
695 : INPQ_LOG("got allowed touch behaviours; block=%" PRIu64 "\n", aInputBlockId);
696 0 : bool success = false;
697 0 : InputBlockState* inputBlock = FindBlockForId(aInputBlockId, nullptr);
698 0 : if (inputBlock && inputBlock->AsTouchBlock()) {
699 0 : TouchBlockState* block = inputBlock->AsTouchBlock();
700 0 : success = block->SetAllowedTouchBehaviors(aBehaviors);
701 0 : block->RecordContentResponseTime();
702 0 : } else if (inputBlock) {
703 0 : NS_WARNING("input block is not a touch block");
704 : }
705 0 : if (success) {
706 0 : ProcessQueue();
707 : }
708 0 : }
709 :
710 : void
711 0 : InputQueue::ProcessQueue() {
712 0 : APZThreadUtils::AssertOnControllerThread();
713 :
714 0 : while (!mQueuedInputs.IsEmpty()) {
715 0 : InputBlockState* curBlock = mQueuedInputs[0]->Block();
716 0 : CancelableBlockState* cancelable = curBlock->AsCancelableBlock();
717 0 : if (cancelable && !cancelable->IsReadyForHandling()) {
718 0 : break;
719 : }
720 :
721 : INPQ_LOG("processing input from block %p; preventDefault %d target %p\n",
722 : curBlock, cancelable && cancelable->IsDefaultPrevented(),
723 : curBlock->GetTargetApzc().get());
724 0 : RefPtr<AsyncPanZoomController> target = curBlock->GetTargetApzc();
725 : // target may be null here if the initial target was unconfirmed and then
726 : // we later got a confirmed null target. in that case drop the events.
727 0 : if (target) {
728 0 : if (cancelable && cancelable->IsDefaultPrevented()) {
729 0 : if (curBlock->AsTouchBlock()) {
730 0 : target->ResetTouchInputState();
731 : }
732 : } else {
733 0 : UpdateActiveApzc(target);
734 0 : curBlock->DispatchEvent(*(mQueuedInputs[0]->Input()));
735 : }
736 : }
737 0 : mQueuedInputs.RemoveElementAt(0);
738 : }
739 :
740 0 : if (CanDiscardBlock(mActiveTouchBlock)) {
741 0 : mActiveTouchBlock = nullptr;
742 : }
743 0 : if (CanDiscardBlock(mActiveWheelBlock)) {
744 0 : mActiveWheelBlock = nullptr;
745 : }
746 0 : if (CanDiscardBlock(mActiveDragBlock)) {
747 0 : mActiveDragBlock = nullptr;
748 : }
749 0 : if (CanDiscardBlock(mActivePanGestureBlock)) {
750 0 : mActivePanGestureBlock = nullptr;
751 : }
752 0 : }
753 :
754 : bool
755 0 : InputQueue::CanDiscardBlock(InputBlockState* aBlock)
756 : {
757 0 : if (!aBlock ||
758 0 : (aBlock->AsCancelableBlock() && !aBlock->AsCancelableBlock()->IsReadyForHandling()) ||
759 0 : aBlock->MustStayActive()) {
760 0 : return false;
761 : }
762 0 : InputData* firstInput = nullptr;
763 0 : FindBlockForId(aBlock->GetBlockId(), &firstInput);
764 0 : if (firstInput) {
765 : // The block has at least one input event still in the queue, so it's
766 : // not depleted
767 0 : return false;
768 : }
769 0 : return true;
770 : }
771 :
772 : void
773 0 : InputQueue::UpdateActiveApzc(const RefPtr<AsyncPanZoomController>& aNewActive) {
774 0 : if (mLastActiveApzc && mLastActiveApzc != aNewActive
775 0 : && mTouchCounter.GetActiveTouchCount() > 0) {
776 0 : mLastActiveApzc->ResetTouchInputState();
777 : }
778 0 : mLastActiveApzc = aNewActive;
779 0 : }
780 :
781 : void
782 0 : InputQueue::Clear()
783 : {
784 0 : APZThreadUtils::AssertOnControllerThread();
785 :
786 0 : mQueuedInputs.Clear();
787 0 : mActiveTouchBlock = nullptr;
788 0 : mActiveWheelBlock = nullptr;
789 0 : mActiveDragBlock = nullptr;
790 0 : mActivePanGestureBlock = nullptr;
791 0 : mLastActiveApzc = nullptr;
792 0 : }
793 :
794 : } // namespace layers
795 : } // namespace mozilla
|