Bug 1440118 - If the APZ content response timeout is set to zero, use the fallback (timeout) behavior unconditionally. r=kats draft
authorBotond Ballo <botond@mozilla.com>
Wed, 21 Feb 2018 17:55:03 -0500
changeset 759219 a75aa57831ec65f6390b445a24481900195e0441
parent 758066 5edc6d20150feac46ce1dd70030c677520b41ace
child 759220 f82947aa3cd259880ba1013566421113d574238f
push id100311
push userbballo@mozilla.com
push dateFri, 23 Feb 2018 21:53:16 +0000
reviewerskats
bugs1440118
milestone60.0a1
Bug 1440118 - If the APZ content response timeout is set to zero, use the fallback (timeout) behavior unconditionally. r=kats MozReview-Commit-ID: HrkUd3MJoxC
gfx/layers/apz/src/InputQueue.cpp
gfx/layers/apz/src/InputQueue.h
--- a/gfx/layers/apz/src/InputQueue.cpp
+++ b/gfx/layers/apz/src/InputQueue.cpp
@@ -30,16 +30,18 @@ InputQueue::~InputQueue() {
 
 nsEventStatus
 InputQueue::ReceiveInputEvent(const RefPtr<AsyncPanZoomController>& aTarget,
                               TargetConfirmationFlags aFlags,
                               const InputData& aEvent,
                               uint64_t* aOutInputBlockId) {
   APZThreadUtils::AssertOnControllerThread();
 
+  AutoRunImmediateTimeout timeoutRunner{this};
+
   switch (aEvent.mInputType) {
     case MULTITOUCH_INPUT: {
       const MultiTouchInput& event = aEvent.AsMultiTouchInput();
       return ReceiveTouchInput(aTarget, aFlags, event, aOutInputBlockId);
     }
 
     case SCROLLWHEEL_INPUT: {
       const ScrollWheelInput& event = aEvent.AsScrollWheelInput();
@@ -440,16 +442,17 @@ InputQueue::MaybeRequestContentResponse(
     // can run.
     ScheduleMainThreadTimeout(aTarget, aBlock);
   }
 }
 
 uint64_t
 InputQueue::InjectNewTouchBlock(AsyncPanZoomController* aTarget)
 {
+  AutoRunImmediateTimeout timeoutRunner{this};
   TouchBlockState* block = StartNewTouchBlock(aTarget,
     TargetConfirmationFlags{true},
     /* aCopyPropertiesFromCurrent = */ true);
   INPQ_LOG("injecting new touch block %p with id %" PRIu64 " and target %p\n",
     block, block->GetBlockId(), aTarget);
   ScheduleMainThreadTimeout(aTarget, block);
   return block->GetBlockId();
 }
@@ -562,22 +565,36 @@ InputQueue::IsDragOnScrollbar(bool aHitS
   return mDragTracker.IsOnScrollbar(aHitScrollbar);
 }
 
 void
 InputQueue::ScheduleMainThreadTimeout(const RefPtr<AsyncPanZoomController>& aTarget,
                                       CancelableBlockState* aBlock) {
   INPQ_LOG("scheduling main thread timeout for target %p\n", aTarget.get());
   aBlock->StartContentResponseTimer();
-  aTarget->PostDelayedTask(
-    NewRunnableMethod<uint64_t>("layers::InputQueue::MainThreadTimeout",
-                                this,
-                                &InputQueue::MainThreadTimeout,
-                                aBlock->GetBlockId()),
-    gfxPrefs::APZContentResponseTimeout());
+  RefPtr<Runnable> timeoutTask = NewRunnableMethod<uint64_t>("layers::InputQueue::MainThreadTimeout",
+      this,
+      &InputQueue::MainThreadTimeout,
+      aBlock->GetBlockId());
+  int32_t timeout = gfxPrefs::APZContentResponseTimeout();
+  if (timeout == 0) {
+    // If the timeout is zero, treat it as a request to ignore any main
+    // thread confirmation and unconditionally use fallback behaviour for
+    // when a timeout is reached. This codepath is used by tests that
+    // want to exercise the fallback behaviour.
+    // To ensure the fallback behaviour is used unconditionally, the timeout
+    // is run right away instead of using PostDelayedTask(). However,
+    // we can't run it right here, because MainThreadTimeout() expects that
+    // the input block has at least one input event in mQueuedInputs, and
+    // the event that triggered this call may not have been added to
+    // mQueuedInputs yet.
+    mImmediateTimeout = timeoutTask.forget();
+  } else {
+    aTarget->PostDelayedTask(timeoutTask.forget(), timeout);
+  }
 }
 
 InputBlockState*
 InputQueue::FindBlockForId(uint64_t aInputBlockId,
                            InputData** aOutFirstInput)
 {
   for (const auto& queuedInput : mQueuedInputs) {
     if (queuedInput->Block()->GetBlockId() == aInputBlockId) {
@@ -802,10 +819,24 @@ InputQueue::Clear()
   mActiveTouchBlock = nullptr;
   mActiveWheelBlock = nullptr;
   mActiveDragBlock = nullptr;
   mActivePanGestureBlock = nullptr;
   mActiveKeyboardBlock = nullptr;
   mLastActiveApzc = nullptr;
 }
 
+InputQueue::AutoRunImmediateTimeout::AutoRunImmediateTimeout(InputQueue* aQueue)
+  : mQueue(aQueue)
+{
+  MOZ_ASSERT(!mQueue->mImmediateTimeout);
+}
+
+InputQueue::AutoRunImmediateTimeout::~AutoRunImmediateTimeout()
+{
+  if (mQueue->mImmediateTimeout) {
+    mQueue->mImmediateTimeout->Run();
+    mQueue->mImmediateTimeout = nullptr;
+  }
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/apz/src/InputQueue.h
+++ b/gfx/layers/apz/src/InputQueue.h
@@ -134,16 +134,26 @@ public:
    * 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);
 
 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 {
+  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.
    */
@@ -213,14 +223,18 @@ private:
   // 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;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_InputQueue_h