LCOV - code coverage report
Current view: top level - gfx/layers/apz/src - InputQueue.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 19 352 5.4 %
Date: 2017-07-14 16:53:18 Functions: 4 34 11.8 %
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.13