Bug 1194876. Reset the input state on an APZC if a new touch block targets a new APZC while there were touches still active. r=botond
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 25 Aug 2015 07:50:01 -0400
changeset 259253 15947e668f645450e913ec287e808865d350a38c
parent 259252 27008b7bd3621f098929e8c5d35c0d001f56c805
child 259254 e1b8ef55a4ec4ffec411898e106a5dea3415173a
push id29275
push userryanvm@gmail.com
push dateTue, 25 Aug 2015 20:52:52 +0000
treeherdermozilla-central@c46370eea81a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbotond
bugs1194876
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1194876. Reset the input state on an APZC if a new touch block targets a new APZC while there were touches still active. r=botond
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/InputBlockState.cpp
gfx/layers/apz/src/InputBlockState.h
gfx/layers/apz/src/InputQueue.cpp
gfx/layers/apz/src/InputQueue.h
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -725,34 +725,17 @@ APZCTreeManager::ProcessTouchInput(Multi
     if (mApzcForInputBlock && BuildOverscrollHandoffChain(mApzcForInputBlock)->HasApzcPannedIntoOverscroll()) {
       if (mRetainedTouchIdentifier == -1) {
         mRetainedTouchIdentifier = mApzcForInputBlock->GetLastTouchIdentifier();
       }
       return nsEventStatus_eConsumeNoDefault;
     }
 
     mHitResultForInputBlock = HitNothing;
-    nsRefPtr<AsyncPanZoomController> apzc = GetTouchInputBlockAPZC(aInput, &mHitResultForInputBlock);
-    // XXX the following check assumes mHitResultForInputBlock == HitLayer
-    // (and that mApzcForInputBlock was the confirmed target of the previous
-    // input block). Eventually it would be better to move this into InputQueue
-    // and have it auto-generated when we start processing events in a new
-    // event block.
-    if (apzc != mApzcForInputBlock) {
-      // If we're moving to a different APZC as our input target, then send a cancel event
-      // to the old one so that it clears its internal state. Otherwise it could get left
-      // in the middle of a panning touch block (for example) and not clean up properly.
-      if (mApzcForInputBlock) {
-        MultiTouchInput cancel(MultiTouchInput::MULTITOUCH_CANCEL, 0, TimeStamp::Now(), 0);
-        mInputQueue->ReceiveInputEvent(mApzcForInputBlock,
-            /* aTargetConfirmed = */ true, cancel, nullptr);
-      }
-      mApzcForInputBlock = apzc;
-    }
-
+    mApzcForInputBlock = GetTouchInputBlockAPZC(aInput, &mHitResultForInputBlock);
   } else if (mApzcForInputBlock) {
     APZCTM_LOG("Re-using APZC %p as continuation of event block\n", mApzcForInputBlock.get());
   }
 
   // If we receive a touch-cancel, it means all touches are finished, so we
   // can stop ignoring any that we were ignoring.
   if (aInput.mType == MultiTouchInput::MULTITOUCH_CANCEL) {
     mRetainedTouchIdentifier = -1;
--- a/gfx/layers/apz/src/InputBlockState.cpp
+++ b/gfx/layers/apz/src/InputBlockState.cpp
@@ -146,16 +146,22 @@ CancelableBlockState::IsReadyForHandling
   return mContentResponded || mContentResponseTimerExpired;
 }
 
 void
 CancelableBlockState::DispatchImmediate(const InputData& aEvent) const
 {
   MOZ_ASSERT(!HasEvents());
   MOZ_ASSERT(GetTargetApzc());
+  DispatchEvent(aEvent);
+}
+
+void
+CancelableBlockState::DispatchEvent(const InputData& aEvent) const
+{
   GetTargetApzc()->HandleInputEvent(aEvent, mTransformToApzc);
 }
 
 // This is used to track the current wheel transaction.
 static uint64_t sLastWheelBlockId = InputBlockState::NO_BLOCK_ID;
 
 WheelBlockState::WheelBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc,
                                  bool aTargetConfirmed,
@@ -268,17 +274,17 @@ WheelBlockState::DropEvents()
 
 void
 WheelBlockState::HandleEvents()
 {
   while (HasEvents()) {
     TBS_LOG("%p returning first of %" PRIuSIZE " events\n", this, mEvents.Length());
     ScrollWheelInput event = mEvents[0];
     mEvents.RemoveElementAt(0);
-    GetTargetApzc()->HandleInputEvent(event, mTransformToApzc);
+    DispatchEvent(event);
   }
 }
 
 bool
 WheelBlockState::MustStayActive()
 {
   return !mTransactionEnded;
 }
@@ -411,21 +417,22 @@ WheelBlockState::AllowScrollHandoff() co
 void
 WheelBlockState::EndTransaction()
 {
   TBS_LOG("%p ending wheel transaction\n", this);
   mTransactionEnded = true;
 }
 
 TouchBlockState::TouchBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc,
-                                 bool aTargetConfirmed)
+                                 bool aTargetConfirmed, TouchCounter& aCounter)
   : CancelableBlockState(aTargetApzc, aTargetConfirmed)
   , mAllowedTouchBehaviorSet(false)
   , mDuringFastFling(false)
   , mSingleTapOccurred(false)
+  , mTouchCounter(aCounter)
 {
   TBS_LOG("Creating %p\n", this);
 }
 
 bool
 TouchBlockState::SetAllowedTouchBehaviors(const nsTArray<TouchBehaviorFlags>& aBehaviors)
 {
   if (mAllowedTouchBehaviorSet) {
@@ -537,20 +544,28 @@ TouchBlockState::DropEvents()
 
 void
 TouchBlockState::HandleEvents()
 {
   while (HasEvents()) {
     TBS_LOG("%p returning first of %" PRIuSIZE " events\n", this, mEvents.Length());
     MultiTouchInput event = mEvents[0];
     mEvents.RemoveElementAt(0);
-    GetTargetApzc()->HandleInputEvent(event, mTransformToApzc);
+    DispatchEvent(event);
   }
 }
 
+void
+TouchBlockState::DispatchEvent(const InputData& aEvent) const
+{
+  MOZ_ASSERT(aEvent.mInputType == MULTITOUCH_INPUT);
+  mTouchCounter.Update(aEvent.AsMultiTouchInput());
+  CancelableBlockState::DispatchEvent(aEvent);
+}
+
 bool
 TouchBlockState::TouchActionAllowsPinchZoom() const
 {
   if (!gfxPrefs::TouchActionEnabled()) {
     return true;
   }
   // Pointer events specification requires that all touch points allow zoom.
   for (size_t i = 0; i < mAllowedTouchBehaviors.Length(); i++) {
--- a/gfx/layers/apz/src/InputBlockState.h
+++ b/gfx/layers/apz/src/InputBlockState.h
@@ -113,16 +113,22 @@ public:
   /**
    * Process the given event using this input block's target apzc.
    * This input block must not have pending events, and its apzc must not be
    * nullptr.
    */
   void DispatchImmediate(const InputData& aEvent) const;
 
   /**
+   * Dispatch the event to the target APZC. Mostly this is a hook for
+   * subclasses to do any per-event processing they need to.
+   */
+  virtual void DispatchEvent(const InputData& aEvent) const;
+
+  /**
    * @return true iff this block has received all the information needed
    *         to properly dispatch the events in the block.
    */
   virtual bool IsReadyForHandling() const;
 
   /**
    * Returns whether or not this block has pending events.
    */
@@ -263,17 +269,17 @@ private:
  * This also requires running code on the Gecko main thread, and so may
  * be populated with some latency. The mAllowedTouchBehaviorSet and
  * mAllowedTouchBehaviors variables track this information.
  */
 class TouchBlockState : public CancelableBlockState
 {
 public:
   explicit TouchBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc,
-                           bool aTargetConfirmed);
+                           bool aTargetConfirmed, TouchCounter& aTouchCounter);
 
   TouchBlockState *AsTouchBlock() override {
     return this;
   }
 
   /**
    * Set the allowed touch behavior flags for this block.
    * @return false if this block already has these flags set, true if not.
@@ -339,23 +345,26 @@ public:
    */
   bool TouchActionAllowsPanningX() const;
   bool TouchActionAllowsPanningY() const;
   bool TouchActionAllowsPanningXY() const;
 
   bool HasEvents() const override;
   void DropEvents() override;
   void HandleEvents() override;
+  void DispatchEvent(const InputData& aEvent) const override;
   bool MustStayActive() override;
   const char* Type() override;
 
 private:
   nsTArray<TouchBehaviorFlags> mAllowedTouchBehaviors;
   bool mAllowedTouchBehaviorSet;
   bool mDuringFastFling;
   bool mSingleTapOccurred;
   nsTArray<MultiTouchInput> mEvents;
+  // A reference to the InputQueue's touch counter
+  TouchCounter& mTouchCounter;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_InputBlockState_h
--- a/gfx/layers/apz/src/InputQueue.cpp
+++ b/gfx/layers/apz/src/InputQueue.cpp
@@ -59,16 +59,17 @@ InputQueue::MaybeHandleCurrentBlock(Canc
                                     const InputData& aEvent) {
   if (block == CurrentBlock() && block->IsReadyForHandling()) {
     const nsRefPtr<AsyncPanZoomController>& target = block->GetTargetApzc();
     INPQ_LOG("current block is ready with target %p preventdefault %d\n",
         target.get(), block->IsDefaultPrevented());
     if (!target || block->IsDefaultPrevented()) {
       return true;
     }
+    UpdateActiveApzc(block->GetTargetApzc());
     block->DispatchImmediate(aEvent);
     return true;
   }
   return false;
 }
 
 nsEventStatus
 InputQueue::ReceiveTouchInput(const nsRefPtr<AsyncPanZoomController>& aTarget,
@@ -277,17 +278,18 @@ InputQueue::SweepDepletedBlocks()
   }
 }
 
 TouchBlockState*
 InputQueue::StartNewTouchBlock(const nsRefPtr<AsyncPanZoomController>& aTarget,
                                bool aTargetConfirmed,
                                bool aCopyPropertiesFromCurrent)
 {
-  TouchBlockState* newBlock = new TouchBlockState(aTarget, aTargetConfirmed);
+  TouchBlockState* newBlock = new TouchBlockState(aTarget, aTargetConfirmed,
+      mTouchCounter);
   if (aCopyPropertiesFromCurrent) {
     newBlock->CopyPropertiesFrom(*CurrentTouchBlock());
   }
 
   SweepDepletedBlocks();
 
   // Add the new block to the queue.
   mInputBlockQueue.AppendElement(newBlock);
@@ -444,16 +446,17 @@ InputQueue::ProcessInputBlocks() {
     // target may be null here if the initial target was unconfirmed and then
     // we later got a confirmed null target. in that case drop the events.
     if (!target) {
       curBlock->DropEvents();
     } else if (curBlock->IsDefaultPrevented()) {
       curBlock->DropEvents();
       target->ResetInputState();
     } else {
+      UpdateActiveApzc(curBlock->GetTargetApzc());
       curBlock->HandleEvents();
     }
     MOZ_ASSERT(!curBlock->HasEvents());
 
     if (mInputBlockQueue.Length() == 1 && curBlock->MustStayActive()) {
       // Some types of blocks (e.g. touch blocks) accumulate events until the
       // next input block is started. Therefore we cannot remove the block from
       // the queue until we have started another block. This block will be
@@ -464,17 +467,27 @@ InputQueue::ProcessInputBlocks() {
     // If we get here, we know there are more touch blocks in the queue after
     // |curBlock|, so we can remove |curBlock| and try to process the next one.
     INPQ_LOG("discarding processed %s block %p\n", curBlock->Type(), curBlock);
     mInputBlockQueue.RemoveElementAt(0);
   } while (!mInputBlockQueue.IsEmpty());
 }
 
 void
+InputQueue::UpdateActiveApzc(const nsRefPtr<AsyncPanZoomController>& aNewActive) {
+  if (mLastActiveApzc && mLastActiveApzc != aNewActive
+      && mTouchCounter.GetActiveTouchCount() > 0) {
+    mLastActiveApzc->ResetInputState();
+  }
+  mLastActiveApzc = aNewActive;
+}
+
+void
 InputQueue::Clear()
 {
   APZThreadUtils::AssertOnControllerThread();
 
   mInputBlockQueue.Clear();
+  mLastActiveApzc = nullptr;
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/apz/src/InputQueue.h
+++ b/gfx/layers/apz/src/InputQueue.h
@@ -142,19 +142,26 @@ private:
    * target APZC.
    */
   bool MaybeHandleCurrentBlock(CancelableBlockState* block,
                                const InputData& aEvent);
 
   void ScheduleMainThreadTimeout(const nsRefPtr<AsyncPanZoomController>& aTarget, uint64_t aInputBlockId);
   void MainThreadTimeout(const uint64_t& aInputBlockId);
   void ProcessInputBlocks();
+  void UpdateActiveApzc(const nsRefPtr<AsyncPanZoomController>& aNewActive);
 
 private:
   // The queue of touch blocks that have not yet been fully processed.
   // This member must only be accessed on the controller/UI thread.
   nsTArray<UniquePtr<CancelableBlockState>> mInputBlockQueue;
+
+  // The APZC to which the last event was delivered
+  nsRefPtr<AsyncPanZoomController> mLastActiveApzc;
+
+  // Track touches so we know when to clear mLastActiveApzc
+  TouchCounter mTouchCounter;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_InputQueue_h