mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1145439 (Part 1) - Throttle requestAnimationFrame for non-visible iframes. r=mstange,mchang
This commit is contained in:
parent
a7d6965c19
commit
91a61b3d2c
@ -3922,6 +3922,42 @@ nsIDocument::TakeFrameRequestCallbacks(FrameRequestCallbackList& aCallbacks)
|
||||
mFrameRequestCallbacks.Clear();
|
||||
}
|
||||
|
||||
bool
|
||||
nsIDocument::ShouldThrottleFrameRequests()
|
||||
{
|
||||
if (!mIsShowing) {
|
||||
// We're not showing (probably in a background tab or the bf cache).
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!mPresShell) {
|
||||
return false; // Can't do anything smarter.
|
||||
}
|
||||
|
||||
nsIFrame* frame = mPresShell->GetRootFrame();
|
||||
if (!frame) {
|
||||
return false; // Can't do anything smarter.
|
||||
}
|
||||
|
||||
nsIFrame* displayRootFrame = nsLayoutUtils::GetDisplayRootFrame(frame);
|
||||
if (!displayRootFrame) {
|
||||
return false; // Can't do anything smarter.
|
||||
}
|
||||
|
||||
if (!displayRootFrame->DidPaintPresShell(mPresShell)) {
|
||||
// We didn't get painted during the last paint, so we're not visible.
|
||||
// Throttle. Note that because we have to paint this document at least
|
||||
// once to unthrottle it, we will drop one requestAnimationFrame frame
|
||||
// when a document that previously wasn't visible scrolls into view. This
|
||||
// is acceptable since it would happen outside the viewport on APZ
|
||||
// platforms and is unlikely to be human-perceivable on non-APZ platforms.
|
||||
return true;
|
||||
}
|
||||
|
||||
// We got painted during the last paint, so run at full speed.
|
||||
return false;
|
||||
}
|
||||
|
||||
PLDHashOperator RequestDiscardEnumerator(imgIRequest* aKey,
|
||||
uint32_t aData,
|
||||
void* userArg)
|
||||
|
@ -2104,6 +2104,13 @@ public:
|
||||
*/
|
||||
void TakeFrameRequestCallbacks(FrameRequestCallbackList& aCallbacks);
|
||||
|
||||
/**
|
||||
* @return true if this document's frame request callbacks should be
|
||||
* throttled. We throttle requestAnimationFrame for documents which aren't
|
||||
* visible (e.g. scrolled out of the viewport).
|
||||
*/
|
||||
bool ShouldThrottleFrameRequests();
|
||||
|
||||
// This returns true when the document tree is being teared down.
|
||||
bool InUnlinkOrDeletion() { return mInUnlinkOrDeletion; }
|
||||
|
||||
|
@ -966,8 +966,8 @@ nsRefreshDriver::GetRegularTimerInterval(bool *outIsDefault) const
|
||||
return 1000.0 / rate;
|
||||
}
|
||||
|
||||
double
|
||||
nsRefreshDriver::GetThrottledTimerInterval() const
|
||||
/* static */ double
|
||||
nsRefreshDriver::GetThrottledTimerInterval()
|
||||
{
|
||||
int32_t rate = Preferences::GetInt("layout.throttled_frame_rate", -1);
|
||||
if (rate <= 0) {
|
||||
@ -1020,6 +1020,8 @@ nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
|
||||
mPendingTransaction(0),
|
||||
mCompletedTransaction(0),
|
||||
mFreezeCount(0),
|
||||
mThrottledFrameRequestInterval(TimeDuration::FromMilliseconds(
|
||||
GetThrottledTimerInterval())),
|
||||
mThrottled(false),
|
||||
mTestControllingRefreshes(false),
|
||||
mViewManagerFlushIsPending(false),
|
||||
@ -1031,6 +1033,7 @@ nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
|
||||
mMostRecentRefreshEpochTime = JS_Now();
|
||||
mMostRecentRefresh = TimeStamp::Now();
|
||||
mMostRecentTick = mMostRecentRefresh;
|
||||
mNextThrottledFrameRequestTick = mMostRecentTick;
|
||||
}
|
||||
|
||||
nsRefreshDriver::~nsRefreshDriver()
|
||||
@ -1254,13 +1257,15 @@ DisableHighPrecisionTimersCallback(nsITimer *aTimer, void *aClosure)
|
||||
void
|
||||
nsRefreshDriver::ConfigureHighPrecision()
|
||||
{
|
||||
bool haveFrameRequestCallbacks = mFrameRequestCallbackDocs.Length() > 0;
|
||||
bool haveUnthrottledFrameRequestCallbacks =
|
||||
mFrameRequestCallbackDocs.Length() > 0;
|
||||
|
||||
// if the only change that's needed is that we need high precision,
|
||||
// then just set that
|
||||
if (!mThrottled && !mRequestedHighPrecision && haveFrameRequestCallbacks) {
|
||||
if (!mThrottled && !mRequestedHighPrecision &&
|
||||
haveUnthrottledFrameRequestCallbacks) {
|
||||
SetHighPrecisionTimersEnabled(true);
|
||||
} else if (mRequestedHighPrecision && !haveFrameRequestCallbacks) {
|
||||
} else if (mRequestedHighPrecision && !haveUnthrottledFrameRequestCallbacks) {
|
||||
SetHighPrecisionTimersEnabled(false);
|
||||
}
|
||||
}
|
||||
@ -1328,6 +1333,7 @@ nsRefreshDriver::ObserverCount() const
|
||||
sum += mStyleFlushObservers.Length();
|
||||
sum += mLayoutFlushObservers.Length();
|
||||
sum += mFrameRequestCallbackDocs.Length();
|
||||
sum += mThrottledFrameRequestCallbackDocs.Length();
|
||||
sum += mViewManagerFlushIsPending;
|
||||
return sum;
|
||||
}
|
||||
@ -1453,6 +1459,105 @@ static void GetProfileTimelineSubDocShells(nsDocShell* aRootDocShell,
|
||||
};
|
||||
}
|
||||
|
||||
static void
|
||||
TakeFrameRequestCallbacksFrom(nsIDocument* aDocument,
|
||||
nsTArray<DocumentFrameCallbacks>& aTarget)
|
||||
{
|
||||
aTarget.AppendElement(aDocument);
|
||||
aDocument->TakeFrameRequestCallbacks(aTarget.LastElement().mCallbacks);
|
||||
}
|
||||
|
||||
void
|
||||
nsRefreshDriver::RunFrameRequestCallbacks(int64_t aNowEpoch, TimeStamp aNowTime)
|
||||
{
|
||||
// Grab all of our frame request callbacks up front.
|
||||
nsTArray<DocumentFrameCallbacks>
|
||||
frameRequestCallbacks(mFrameRequestCallbackDocs.Length() +
|
||||
mThrottledFrameRequestCallbackDocs.Length());
|
||||
|
||||
// First, grab throttled frame request callbacks.
|
||||
{
|
||||
nsTArray<nsIDocument*> docsToRemove;
|
||||
|
||||
// We always tick throttled frame requests if the entire refresh driver is
|
||||
// throttled, because in that situation throttled frame requests tick at the
|
||||
// same frequency as non-throttled frame requests.
|
||||
bool tickThrottledFrameRequests = mThrottled;
|
||||
|
||||
if (!tickThrottledFrameRequests &&
|
||||
aNowTime >= mNextThrottledFrameRequestTick) {
|
||||
mNextThrottledFrameRequestTick = aNowTime + mThrottledFrameRequestInterval;
|
||||
tickThrottledFrameRequests = true;
|
||||
}
|
||||
|
||||
for (nsIDocument* doc : mThrottledFrameRequestCallbackDocs) {
|
||||
if (tickThrottledFrameRequests) {
|
||||
// We're ticking throttled documents, so grab this document's requests.
|
||||
// We don't bother appending to docsToRemove because we're going to
|
||||
// clear mThrottledFrameRequestCallbackDocs anyway.
|
||||
TakeFrameRequestCallbacksFrom(doc, frameRequestCallbacks);
|
||||
} else if (!doc->ShouldThrottleFrameRequests()) {
|
||||
// This document is no longer throttled, so grab its requests even
|
||||
// though we're not ticking throttled frame requests right now. If
|
||||
// this is the first unthrottled document with frame requests, we'll
|
||||
// enter high precision mode the next time the callback is scheduled.
|
||||
TakeFrameRequestCallbacksFrom(doc, frameRequestCallbacks);
|
||||
docsToRemove.AppendElement(doc);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all the documents we're ticking from
|
||||
// mThrottledFrameRequestCallbackDocs so they can be readded as needed.
|
||||
if (tickThrottledFrameRequests) {
|
||||
mThrottledFrameRequestCallbackDocs.Clear();
|
||||
} else {
|
||||
// XXX(seth): We're using this approach to avoid concurrent modification
|
||||
// of mThrottledFrameRequestCallbackDocs. docsToRemove usually has either
|
||||
// zero elements or a very small number, so this should be OK in practice.
|
||||
for (nsIDocument* doc : docsToRemove) {
|
||||
mThrottledFrameRequestCallbackDocs.RemoveElement(doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now grab unthrottled frame request callbacks.
|
||||
for (nsIDocument* doc : mFrameRequestCallbackDocs) {
|
||||
TakeFrameRequestCallbacksFrom(doc, frameRequestCallbacks);
|
||||
}
|
||||
|
||||
// Reset mFrameRequestCallbackDocs so they can be readded as needed.
|
||||
mFrameRequestCallbackDocs.Clear();
|
||||
|
||||
profiler_tracing("Paint", "Scripts", TRACING_INTERVAL_START);
|
||||
int64_t eventTime = aNowEpoch / PR_USEC_PER_MSEC;
|
||||
for (uint32_t i = 0; i < frameRequestCallbacks.Length(); ++i) {
|
||||
const DocumentFrameCallbacks& docCallbacks = frameRequestCallbacks[i];
|
||||
// XXXbz Bug 863140: GetInnerWindow can return the outer
|
||||
// window in some cases.
|
||||
nsPIDOMWindow* innerWindow = docCallbacks.mDocument->GetInnerWindow();
|
||||
DOMHighResTimeStamp timeStamp = 0;
|
||||
if (innerWindow && innerWindow->IsInnerWindow()) {
|
||||
nsPerformance* perf = innerWindow->GetPerformance();
|
||||
if (perf) {
|
||||
timeStamp = perf->GetDOMTiming()->TimeStampToDOMHighRes(aNowTime);
|
||||
}
|
||||
// else window is partially torn down already
|
||||
}
|
||||
for (uint32_t j = 0; j < docCallbacks.mCallbacks.Length(); ++j) {
|
||||
const nsIDocument::FrameRequestCallbackHolder& holder =
|
||||
docCallbacks.mCallbacks[j];
|
||||
nsAutoMicroTask mt;
|
||||
if (holder.HasWebIDLCallback()) {
|
||||
ErrorResult ignored;
|
||||
holder.GetWebIDLCallback()->Call(timeStamp, ignored);
|
||||
} else {
|
||||
holder.GetXPCOMCallback()->Sample(eventTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
profiler_tracing("Paint", "Scripts", TRACING_INTERVAL_END);
|
||||
}
|
||||
|
||||
void
|
||||
nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
|
||||
{
|
||||
@ -1543,46 +1648,7 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
|
||||
if (i == 0) {
|
||||
// This is the Flush_Style case.
|
||||
|
||||
// Grab all of our frame request callbacks up front.
|
||||
nsTArray<DocumentFrameCallbacks>
|
||||
frameRequestCallbacks(mFrameRequestCallbackDocs.Length());
|
||||
for (uint32_t i = 0; i < mFrameRequestCallbackDocs.Length(); ++i) {
|
||||
frameRequestCallbacks.AppendElement(mFrameRequestCallbackDocs[i]);
|
||||
mFrameRequestCallbackDocs[i]->
|
||||
TakeFrameRequestCallbacks(frameRequestCallbacks.LastElement().mCallbacks);
|
||||
}
|
||||
// OK, now reset mFrameRequestCallbackDocs so they can be
|
||||
// readded as needed.
|
||||
mFrameRequestCallbackDocs.Clear();
|
||||
|
||||
profiler_tracing("Paint", "Scripts", TRACING_INTERVAL_START);
|
||||
int64_t eventTime = aNowEpoch / PR_USEC_PER_MSEC;
|
||||
for (uint32_t i = 0; i < frameRequestCallbacks.Length(); ++i) {
|
||||
const DocumentFrameCallbacks& docCallbacks = frameRequestCallbacks[i];
|
||||
// XXXbz Bug 863140: GetInnerWindow can return the outer
|
||||
// window in some cases.
|
||||
nsPIDOMWindow* innerWindow = docCallbacks.mDocument->GetInnerWindow();
|
||||
DOMHighResTimeStamp timeStamp = 0;
|
||||
if (innerWindow && innerWindow->IsInnerWindow()) {
|
||||
nsPerformance* perf = innerWindow->GetPerformance();
|
||||
if (perf) {
|
||||
timeStamp = perf->GetDOMTiming()->TimeStampToDOMHighRes(aNowTime);
|
||||
}
|
||||
// else window is partially torn down already
|
||||
}
|
||||
for (uint32_t j = 0; j < docCallbacks.mCallbacks.Length(); ++j) {
|
||||
const nsIDocument::FrameRequestCallbackHolder& holder =
|
||||
docCallbacks.mCallbacks[j];
|
||||
nsAutoMicroTask mt;
|
||||
if (holder.HasWebIDLCallback()) {
|
||||
ErrorResult ignored;
|
||||
holder.GetWebIDLCallback()->Call(timeStamp, ignored);
|
||||
} else {
|
||||
holder.GetXPCOMCallback()->Sample(eventTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
profiler_tracing("Paint", "Scripts", TRACING_INTERVAL_END);
|
||||
RunFrameRequestCallbacks(aNowEpoch, aNowTime);
|
||||
|
||||
if (mPresContext && mPresContext->GetPresShell()) {
|
||||
bool tracingStyleFlush = false;
|
||||
@ -2025,9 +2091,15 @@ void
|
||||
nsRefreshDriver::ScheduleFrameRequestCallbacks(nsIDocument* aDocument)
|
||||
{
|
||||
NS_ASSERTION(mFrameRequestCallbackDocs.IndexOf(aDocument) ==
|
||||
mFrameRequestCallbackDocs.NoIndex,
|
||||
mFrameRequestCallbackDocs.NoIndex &&
|
||||
mThrottledFrameRequestCallbackDocs.IndexOf(aDocument) ==
|
||||
mThrottledFrameRequestCallbackDocs.NoIndex,
|
||||
"Don't schedule the same document multiple times");
|
||||
mFrameRequestCallbackDocs.AppendElement(aDocument);
|
||||
if (aDocument->ShouldThrottleFrameRequests()) {
|
||||
mThrottledFrameRequestCallbackDocs.AppendElement(aDocument);
|
||||
} else {
|
||||
mFrameRequestCallbackDocs.AppendElement(aDocument);
|
||||
}
|
||||
|
||||
// make sure that the timer is running
|
||||
ConfigureHighPrecision();
|
||||
@ -2038,6 +2110,7 @@ void
|
||||
nsRefreshDriver::RevokeFrameRequestCallbacks(nsIDocument* aDocument)
|
||||
{
|
||||
mFrameRequestCallbackDocs.RemoveElement(aDocument);
|
||||
mThrottledFrameRequestCallbackDocs.RemoveElement(aDocument);
|
||||
ConfigureHighPrecision();
|
||||
// No need to worry about restarting our timer in slack mode if it's already
|
||||
// running; that will happen automatically when it fires.
|
||||
|
@ -308,6 +308,8 @@ private:
|
||||
};
|
||||
typedef nsClassHashtable<nsUint32HashKey, ImageStartData> ImageStartTable;
|
||||
|
||||
void RunFrameRequestCallbacks(int64_t aNowEpoch, mozilla::TimeStamp aNowTime);
|
||||
|
||||
void Tick(int64_t aNowEpoch, mozilla::TimeStamp aNowTime);
|
||||
|
||||
enum EnsureTimerStartedFlags {
|
||||
@ -336,7 +338,7 @@ private:
|
||||
|
||||
double GetRefreshTimerInterval() const;
|
||||
double GetRegularTimerInterval(bool *outIsDefault = nullptr) const;
|
||||
double GetThrottledTimerInterval() const;
|
||||
static double GetThrottledTimerInterval();
|
||||
|
||||
bool HaveFrameRequestCallbacks() const {
|
||||
return mFrameRequestCallbackDocs.Length() != 0;
|
||||
@ -361,6 +363,11 @@ private:
|
||||
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;
|
||||
|
||||
bool mThrottled;
|
||||
bool mTestControllingRefreshes;
|
||||
bool mViewManagerFlushIsPending;
|
||||
@ -379,6 +386,7 @@ private:
|
||||
mozilla::TimeStamp mMostRecentRefresh;
|
||||
mozilla::TimeStamp mMostRecentTick;
|
||||
mozilla::TimeStamp mTickStart;
|
||||
mozilla::TimeStamp mNextThrottledFrameRequestTick;
|
||||
|
||||
// separate arrays for each flush type we support
|
||||
ObserverArray mObservers[3];
|
||||
@ -390,6 +398,7 @@ private:
|
||||
nsAutoTArray<nsIPresShell*, 16> mPresShellsToInvalidateIfHidden;
|
||||
// nsTArray on purpose, because we want to be able to swap.
|
||||
nsTArray<nsIDocument*> mFrameRequestCallbackDocs;
|
||||
nsTArray<nsIDocument*> mThrottledFrameRequestCallbackDocs;
|
||||
nsTArray<nsAPostRefreshObserver*> mPostRefreshObservers;
|
||||
|
||||
// Helper struct for processing image requests
|
||||
|
@ -2806,16 +2806,28 @@ NS_PTR_TO_INT32(frame->Properties().Get(nsIFrame::ParagraphDepthProperty()))
|
||||
* the last repaint.
|
||||
*/
|
||||
void UpdatePaintCountForPaintedPresShells() {
|
||||
nsTArray<nsWeakPtr> * list = PaintedPresShellList();
|
||||
for (int i = 0, l = list->Length(); i < l; i++) {
|
||||
nsCOMPtr<nsIPresShell> shell = do_QueryReferent(list->ElementAt(i));
|
||||
|
||||
for (nsWeakPtr& item : *PaintedPresShellList()) {
|
||||
nsCOMPtr<nsIPresShell> shell = do_QueryReferent(item);
|
||||
if (shell) {
|
||||
shell->IncrementPaintCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if we painted @aShell during the last repaint.
|
||||
*/
|
||||
bool DidPaintPresShell(nsIPresShell* aShell)
|
||||
{
|
||||
for (nsWeakPtr& item : *PaintedPresShellList()) {
|
||||
nsCOMPtr<nsIPresShell> shell = do_QueryReferent(item);
|
||||
if (shell == aShell) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessors for the absolute containing block.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user