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
--- 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;