Bug 1419226 - Part 4. Fire MozAfterPaint when revoking transactions. r?mattwoodrow draft
authorMantaroh Yoshinaga <mantaroh@gmail.com>
Mon, 05 Feb 2018 14:33:36 +0900
changeset 751076 c657bcd7fa215e0e5a22bcf0b0ec36e2c314ea70
parent 751063 0f3b6362cf8199162799841118008051f6259340
child 751077 d347bb8754fa9e0b35ddaf06bd6c378914bdee70
push id97840
push userbmo:mantaroh@gmail.com
push dateMon, 05 Feb 2018 05:34:00 +0000
reviewersmattwoodrow
bugs1419226
milestone60.0a1
Bug 1419226 - Part 4. Fire MozAfterPaint when revoking transactions. r?mattwoodrow This patch will fire a MozAfterPaint event when revoking transaction. However, Gecko will not fire in the following cases: * If there is a transaction which waiting for a reply from compositor. In this case, we wait to fire the MozAfterPaint of revoking transaction until a previous transaction is finished. And refresh driver ignores issuing duplicate transaction id when revoking transaction. MozReview-Commit-ID: 8GuofJd6HMP
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/base/nsRefreshDriver.cpp
layout/base/nsRefreshDriver.h
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -2606,17 +2606,16 @@ nsPresContext::NotifyInvalidation(uint64
               DevPixelsToAppUnits(clampedRect.height));
   NotifyInvalidation(aTransactionId, rect);
 }
 
 void
 nsPresContext::NotifyInvalidation(uint64_t aTransactionId, const nsRect& aRect)
 {
   MOZ_ASSERT(GetContainerWeak(), "Invalidation in detached pres context");
-
   nsPresContext* pc;
   for (pc = this; pc; pc = pc->GetParentPresContext()) {
     if (pc->mFireAfterPaintEvents)
       break;
     pc->mFireAfterPaintEvents = true;
   }
 
   TransactionInvalidations* transaction = nullptr;
@@ -2731,24 +2730,57 @@ public:
 
   RefPtr<nsPresContext> mPresContext;
   uint64_t mTransactionId;
   const mozilla::TimeStamp mTimeStamp;
   nsTArray<nsRect> mList;
 };
 
 void
+nsPresContext::NotifyRevokingDidPaint(uint64_t aTransactionId)
+ {
+   if ((IsRoot() && !mFireAfterPaintEvents) ||
+       (!PresShell()->IsVisible() && !mFireAfterPaintEvents)) {
+     return;
+   }
+
+  TransactionInvalidations* transaction = nullptr;
+  for (auto& t : mTransactions) {
+    if (t.mTransactionId == aTransactionId) {
+      transaction = &t;
+      break;
+    }
+  }
+  // If we don't have a transaction of revoking transaction id,
+  // we don't need to fire a MozAfterPaint.
+  if (!transaction) {
+    return;
+  }
+
+  // if there are not waiting for the transaction before this transaction,
+  // fire MozAfterPaint event of this transaction.
+  // Or mark as 'fire MozAfterPaint after firing the previous transaction'
+  // if there is a waiting transaction.
+  if (mTransactions.Length() == 1) {
+    nsCOMPtr<nsIRunnable> ev =
+      new DelayedFireDOMPaintEvent(this, &transaction->mInvalidations,
+                                   transaction->mTransactionId, mozilla::TimeStamp());
+    nsContentUtils::AddScriptRunner(ev);
+    mTransactions.RemoveElementAt(0);
+  } else {
+    transaction->mIsWaitingForPreviousTransaction = true;
+  }
+}
+
+void
 nsPresContext::NotifyDidPaintForSubtree(uint64_t aTransactionId,
                                         const mozilla::TimeStamp& aTimeStamp)
 {
-  if (IsRoot() && !mFireAfterPaintEvents) {
-    return;
-  }
-
-  if (!PresShell()->IsVisible() && !mFireAfterPaintEvents) {
+  if ((IsRoot() && !mFireAfterPaintEvents) ||
+      (!PresShell()->IsVisible() && !mFireAfterPaintEvents)) {
     return;
   }
 
   // Non-root prescontexts fire MozAfterPaint to all their descendants
   // unconditionally, even if no invalidations have been collected. This is
   // because we don't want to eat the cost of collecting invalidations for
   // every subdocument (which would require putting every subdocument in its
   // own layer).
@@ -2759,16 +2791,27 @@ nsPresContext::NotifyDidPaintForSubtree(
     if (mTransactions[i].mTransactionId <= aTransactionId) {
       nsCOMPtr<nsIRunnable> ev =
         new DelayedFireDOMPaintEvent(this, &mTransactions[i].mInvalidations,
                                      mTransactions[i].mTransactionId, aTimeStamp);
       nsContentUtils::AddScriptRunner(ev);
       sent = true;
       mTransactions.RemoveElementAt(i);
     } else {
+      // If there are transaction which is waiting for this transaction,
+      // we should fire a MozAfterPaint immediately.
+      if (sent && mTransactions[i].mIsWaitingForPreviousTransaction) {
+        nsCOMPtr<nsIRunnable> ev =
+          new DelayedFireDOMPaintEvent(this, &mTransactions[i].mInvalidations,
+                                       mTransactions[i].mTransactionId, aTimeStamp);
+        nsContentUtils::AddScriptRunner(ev);
+        sent = true;
+        mTransactions.RemoveElementAt(i);
+        continue;
+      }
       i++;
     }
   }
 
   if (!sent) {
     nsTArray<nsRect> dummy;
     nsCOMPtr<nsIRunnable> ev =
       new DelayedFireDOMPaintEvent(this, &dummy,
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -1028,16 +1028,17 @@ public:
   // by nsRefreshDriver::GetTransactionId).
   // Invalidated regions will be dispatched to MozAfterPaint events when
   // NotifyDidPaintForSubtree is called for the transaction id (or any higher id).
   void NotifyInvalidation(uint64_t aTransactionId, const nsRect& aRect);
   // aRect is in device pixels
   void NotifyInvalidation(uint64_t aTransactionId, const nsIntRect& aRect);
   void NotifyDidPaintForSubtree(uint64_t aTransactionId = 0,
                                 const mozilla::TimeStamp& aTimeStamp = mozilla::TimeStamp());
+  void NotifyRevokingDidPaint(uint64_t aTransactionId = 0);
   void FireDOMPaintEvent(nsTArray<nsRect>* aList, uint64_t aTransactionId,
                          mozilla::TimeStamp aTimeStamp = mozilla::TimeStamp());
 
   // Callback for catching invalidations in ContainerLayers
   // Passed to LayerProperties::ComputeDifference
   static void NotifySubDocInvalidation(mozilla::layers::ContainerLayer* aContainer,
                                        const nsIntRegion* aRegion);
   void SetNotifySubDocInvalidationData(mozilla::layers::ContainerLayer* aContainer);
@@ -1345,16 +1346,17 @@ protected:
   nsCOMPtr<nsIPrintSettings> mPrintSettings;
   nsCOMPtr<nsITimer>    mPrefChangedTimer;
 
   mozilla::UniquePtr<nsBidi> mBidiEngine;
 
   struct TransactionInvalidations {
     uint64_t mTransactionId;
     nsTArray<nsRect> mInvalidations;
+    bool mIsWaitingForPreviousTransaction;
   };
   AutoTArray<TransactionInvalidations, 4> mTransactions;
 
   // text performance metrics
   nsAutoPtr<gfxTextPerfMetrics>   mTextPerf;
 
   nsAutoPtr<gfxMissingFontRecorder> mMissingFonts;
 
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -1146,17 +1146,18 @@ nsRefreshDriver::ChooseTimer() const
   }
   return sRegularRateTimer;
 }
 
 nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
   : mActiveTimer(nullptr),
     mPresContext(aPresContext),
     mRootRefresh(nullptr),
-    mPendingTransaction(0),
+    mDistanceFromCompletedTransaction(0),
+    mNextTransactionId(0),
     mCompletedTransaction(0),
     mFreezeCount(0),
     mThrottledFrameRequestInterval(TimeDuration::FromMilliseconds(
                                      GetThrottledTimerInterval())),
     mMinRecomputeVisibilityInterval(GetMinRecomputeVisibilityInterval()),
     mThrottled(false),
     mNeedToRecomputeVisibility(false),
     mTestControllingRefreshes(false),
@@ -1226,17 +1227,17 @@ nsRefreshDriver::AdvanceTimeAndRefresh(i
   DoTick();
 }
 
 void
 nsRefreshDriver::RestoreNormalRefresh()
 {
   mTestControllingRefreshes = false;
   EnsureTimerStarted(eAllowTimeToGoBackwards);
-  mCompletedTransaction = mPendingTransaction;
+  mCompletedTransaction = mDistanceFromCompletedTransaction = mNextTransactionId;
 }
 
 TimeStamp
 nsRefreshDriver::MostRecentRefresh() const
 {
   // In case of stylo traversal, we have already activated the refresh driver in
   // ServoRestyleManager::ProcessPendingRestyles().
   if (!ServoStyleSet::IsInServoTraversal()) {
@@ -2178,79 +2179,90 @@ nsRefreshDriver::FinishedWaitingForTrans
   }
   mSkippedPaints = false;
   mWarningThreshold = 1;
 }
 
 uint64_t
 nsRefreshDriver::GetTransactionId(bool aThrottle)
 {
-  ++mPendingTransaction;
+  ++mDistanceFromCompletedTransaction;
+  ++mNextTransactionId;
 
   if (aThrottle &&
-      mPendingTransaction >= mCompletedTransaction + 2 &&
+      mDistanceFromCompletedTransaction >= mCompletedTransaction + 2 &&
       !mWaitingForTransaction &&
       !mTestControllingRefreshes) {
     mWaitingForTransaction = true;
     mSkippedPaints = false;
     mWarningThreshold = 1;
   }
 
-  return mPendingTransaction;
+  return mNextTransactionId;
 }
 
 uint64_t
 nsRefreshDriver::LastTransactionId() const
 {
-  return mPendingTransaction;
+  return mNextTransactionId;
 }
 
 void
 nsRefreshDriver::RevokeTransactionId(uint64_t aTransactionId)
 {
-  MOZ_ASSERT(aTransactionId == mPendingTransaction);
-  if (mPendingTransaction == mCompletedTransaction + 2 &&
+  MOZ_ASSERT(aTransactionId == mNextTransactionId);
+  if (mDistanceFromCompletedTransaction == mCompletedTransaction + 2 &&
       mWaitingForTransaction) {
     MOZ_ASSERT(!mSkippedPaints, "How did we skip a paint when we're in the middle of one?");
     FinishedWaitingForTransaction();
   }
-  mPendingTransaction--;
+
+  nsPresContext* pc = GetPresContext();
+  if (pc) {
+    pc->NotifyRevokingDidPaint(aTransactionId);
+  }
+  mDistanceFromCompletedTransaction--;
 }
 
 void
 nsRefreshDriver::ClearPendingTransactions()
 {
-  mCompletedTransaction = mPendingTransaction;
+  mCompletedTransaction = mDistanceFromCompletedTransaction = mNextTransactionId;
   mWaitingForTransaction = false;
 }
 
 void
 nsRefreshDriver::ResetInitialTransactionId(uint64_t aTransactionId)
 {
-  mCompletedTransaction = mPendingTransaction = aTransactionId;
+  mCompletedTransaction = mDistanceFromCompletedTransaction = mNextTransactionId = aTransactionId;
 }
 
 mozilla::TimeStamp
 nsRefreshDriver::GetTransactionStart()
 {
   return mTickStart;
 }
 
 void
 nsRefreshDriver::NotifyTransactionCompleted(uint64_t aTransactionId)
 {
   if (aTransactionId > mCompletedTransaction) {
-    if (mPendingTransaction > mCompletedTransaction + 1 &&
+    if (mDistanceFromCompletedTransaction > mCompletedTransaction + 1 &&
         mWaitingForTransaction) {
       mCompletedTransaction = aTransactionId;
       FinishedWaitingForTransaction();
     } else {
       mCompletedTransaction = aTransactionId;
     }
   }
+
+  // If completed transaction id get ahead of distance id, reset to distance id.
+  if (mCompletedTransaction > mDistanceFromCompletedTransaction) {
+    mDistanceFromCompletedTransaction = mCompletedTransaction;
+  }
 }
 
 void
 nsRefreshDriver::WillRefresh(mozilla::TimeStamp aTime)
 {
   mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
   mRootRefresh = nullptr;
   if (mSkippedPaints) {
--- a/layout/base/nsRefreshDriver.h
+++ b/layout/base/nsRefreshDriver.h
@@ -439,17 +439,19 @@ private:
   mozilla::RefreshDriverTimer* mActiveTimer;
 
   // nsPresContext passed in constructor and unset in Disconnect.
   mozilla::WeakPtr<nsPresContext> mPresContext;
 
   RefPtr<nsRefreshDriver> mRootRefresh;
 
   // The most recently allocated transaction id.
-  uint64_t mPendingTransaction;
+  uint64_t mNextTransactionId;
+  // This number is mCompletedTransaction + (pending transaction count).
+  uint64_t mDistanceFromCompletedTransaction;
   // The most recently completed transaction id.
   uint64_t mCompletedTransaction;
 
   uint32_t mFreezeCount;
 
   // How long we wait between ticks for throttled (which generally means
   // non-visible) documents registered with a non-throttled refresh driver.
   const mozilla::TimeDuration mThrottledFrameRequestInterval;