Bug 1806905 - Tweak reflow callback set-up to deal with container query reentrancy. r=smaug
Differential Revision: https://phabricator.services.mozilla.com/D165611
This commit is contained in:
@@ -853,7 +853,9 @@ PresShell::PresShell(Document* aDocument)
|
||||
mForceUseLegacyNonPrimaryDispatch(false),
|
||||
mInitializedWithClickEventDispatchingBlacklist(false),
|
||||
mMouseLocationWasSetBySynthesizedMouseEventForTests(false),
|
||||
mHasTriedFastUnsuppress(false) {
|
||||
mHasTriedFastUnsuppress(false),
|
||||
mProcessingReflowCommands(false),
|
||||
mPendingDidDoReflow(false) {
|
||||
MOZ_LOG(gLog, LogLevel::Debug, ("PresShell::PresShell this=%p", this));
|
||||
MOZ_ASSERT(aDocument);
|
||||
|
||||
@@ -2151,6 +2153,7 @@ bool PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight,
|
||||
// Now, we may have been destroyed by the destructor of
|
||||
// `nsAutoCauseReflowNotifier`.
|
||||
|
||||
mPendingDidDoReflow = true;
|
||||
DidDoReflow(true);
|
||||
|
||||
// the reflow above should've set our bsize if it was NS_UNCONSTRAINEDSIZE,
|
||||
@@ -4200,10 +4203,7 @@ void PresShell::HandlePostedReflowCallbacks(bool aInterruptible) {
|
||||
|
||||
FlushType flushType =
|
||||
aInterruptible ? FlushType::InterruptibleLayout : FlushType::Layout;
|
||||
if (shouldFlush && !mIsDestroying && nsContentUtils::IsSafeToRunScript()) {
|
||||
// We don't want to flush when not allowed to run script (e.g., like when
|
||||
// running container query updates), since that trivially executes script.
|
||||
// We'll flush layout again at the end of that process if necessary.
|
||||
if (shouldFlush && !mIsDestroying) {
|
||||
FlushPendingNotifications(flushType);
|
||||
}
|
||||
}
|
||||
@@ -9485,9 +9485,22 @@ void PresShell::WillDoReflow() {
|
||||
}
|
||||
|
||||
void PresShell::DidDoReflow(bool aInterruptible) {
|
||||
MOZ_ASSERT(mPendingDidDoReflow);
|
||||
if (!nsContentUtils::IsSafeToRunScript()) {
|
||||
// If we're reflowing while script-blocked (e.g. from container query
|
||||
// updates), defer our reflow callbacks until the end of our next layout
|
||||
// flush.
|
||||
SetNeedLayoutFlush();
|
||||
return;
|
||||
}
|
||||
|
||||
auto clearPendingDidDoReflow =
|
||||
MakeScopeExit([&] { mPendingDidDoReflow = false; });
|
||||
|
||||
mHiddenContentInForcedLayout.Clear();
|
||||
|
||||
HandlePostedReflowCallbacks(aInterruptible);
|
||||
|
||||
if (mIsDestroying) {
|
||||
return;
|
||||
}
|
||||
@@ -9825,12 +9838,18 @@ void PresShell::DoVerifyReflow() {
|
||||
#define NS_LONG_REFLOW_TIME_MS 5000
|
||||
|
||||
bool PresShell::ProcessReflowCommands(bool aInterruptible) {
|
||||
if (mDirtyRoots.IsEmpty() && !mShouldUnsuppressPainting) {
|
||||
if (mDirtyRoots.IsEmpty() && !mShouldUnsuppressPainting &&
|
||||
!mPendingDidDoReflow) {
|
||||
// Nothing to do; bail out
|
||||
return true;
|
||||
}
|
||||
|
||||
mozilla::TimeStamp timerStart = mozilla::TimeStamp::Now();
|
||||
const bool wasProcessingReflowCommands = mProcessingReflowCommands;
|
||||
auto restoreProcessingReflowCommands = MakeScopeExit(
|
||||
[&] { mProcessingReflowCommands = wasProcessingReflowCommands; });
|
||||
mProcessingReflowCommands = true;
|
||||
|
||||
auto timerStart = mozilla::TimeStamp::Now();
|
||||
bool interrupted = false;
|
||||
if (!mDirtyRoots.IsEmpty()) {
|
||||
#ifdef DEBUG
|
||||
@@ -9846,68 +9865,68 @@ bool PresShell::ProcessReflowCommands(bool aInterruptible) {
|
||||
: (PRIntervalTime)0;
|
||||
|
||||
// Scope for the reflow entry point
|
||||
{
|
||||
nsAutoScriptBlocker scriptBlocker;
|
||||
WillDoReflow();
|
||||
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
|
||||
nsViewManager::AutoDisableRefresh refreshBlocker(mViewManager);
|
||||
nsAutoScriptBlocker scriptBlocker;
|
||||
WillDoReflow();
|
||||
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
|
||||
nsViewManager::AutoDisableRefresh refreshBlocker(mViewManager);
|
||||
|
||||
OverflowChangedTracker overflowTracker;
|
||||
OverflowChangedTracker overflowTracker;
|
||||
|
||||
do {
|
||||
// Send an incremental reflow notification to the target frame.
|
||||
nsIFrame* target = mDirtyRoots.PopShallowestRoot();
|
||||
do {
|
||||
// Send an incremental reflow notification to the target frame.
|
||||
nsIFrame* target = mDirtyRoots.PopShallowestRoot();
|
||||
|
||||
if (!target->IsSubtreeDirty()) {
|
||||
// It's not dirty anymore, which probably means the notification
|
||||
// was posted in the middle of a reflow (perhaps with a reflow
|
||||
// root in the middle). Don't do anything.
|
||||
continue;
|
||||
}
|
||||
|
||||
interrupted = !DoReflow(target, aInterruptible, &overflowTracker);
|
||||
|
||||
// Keep going until we're out of reflow commands, or we've run
|
||||
// past our deadline, or we're interrupted.
|
||||
} while (!interrupted && !mDirtyRoots.IsEmpty() &&
|
||||
(!aInterruptible || PR_IntervalNow() < deadline));
|
||||
|
||||
interrupted = !mDirtyRoots.IsEmpty();
|
||||
|
||||
overflowTracker.Flush();
|
||||
|
||||
if (!interrupted) {
|
||||
// We didn't get interrupted. Go ahead and perform scroll anchor
|
||||
// adjustments.
|
||||
FlushPendingScrollAnchorAdjustments();
|
||||
if (!target->IsSubtreeDirty()) {
|
||||
// It's not dirty anymore, which probably means the notification
|
||||
// was posted in the middle of a reflow (perhaps with a reflow
|
||||
// root in the middle). Don't do anything.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Exiting the scriptblocker might have killed us
|
||||
if (!mIsDestroying) {
|
||||
DidDoReflow(aInterruptible);
|
||||
}
|
||||
interrupted = !DoReflow(target, aInterruptible, &overflowTracker);
|
||||
|
||||
// DidDoReflow might have killed us
|
||||
if (!mIsDestroying) {
|
||||
// Keep going until we're out of reflow commands, or we've run
|
||||
// past our deadline, or we're interrupted.
|
||||
} while (!interrupted && !mDirtyRoots.IsEmpty() &&
|
||||
(!aInterruptible || PR_IntervalNow() < deadline));
|
||||
|
||||
interrupted = !mDirtyRoots.IsEmpty();
|
||||
|
||||
overflowTracker.Flush();
|
||||
|
||||
if (!interrupted) {
|
||||
// We didn't get interrupted. Go ahead and perform scroll anchor
|
||||
// adjustments.
|
||||
FlushPendingScrollAnchorAdjustments();
|
||||
}
|
||||
mPendingDidDoReflow = true;
|
||||
}
|
||||
|
||||
// Exiting the scriptblocker might have killed us. If we were processing
|
||||
// scroll commands, let the outermost call deal with it.
|
||||
if (!mIsDestroying && mPendingDidDoReflow && !wasProcessingReflowCommands) {
|
||||
DidDoReflow(aInterruptible);
|
||||
}
|
||||
|
||||
// DidDoReflow might have killed us
|
||||
if (!mIsDestroying) {
|
||||
#ifdef DEBUG
|
||||
if (VerifyReflowFlags::DumpCommands & gVerifyReflowFlags) {
|
||||
printf("\nPresShell::ProcessReflowCommands() finished: this=%p\n",
|
||||
(void*)this);
|
||||
}
|
||||
DoVerifyReflow();
|
||||
if (VerifyReflowFlags::DumpCommands & gVerifyReflowFlags) {
|
||||
printf("\nPresShell::ProcessReflowCommands() finished: this=%p\n",
|
||||
(void*)this);
|
||||
}
|
||||
DoVerifyReflow();
|
||||
#endif
|
||||
|
||||
// If any new reflow commands were enqueued during the reflow, schedule
|
||||
// another reflow event to process them. Note that we want to do this
|
||||
// after DidDoReflow(), since that method can change whether there are
|
||||
// dirty roots around by flushing, and there's no point in posting a
|
||||
// reflow event just to have the flush revoke it.
|
||||
if (!mDirtyRoots.IsEmpty()) {
|
||||
MaybeScheduleReflow();
|
||||
// And record that we might need flushing
|
||||
SetNeedLayoutFlush();
|
||||
}
|
||||
// If any new reflow commands were enqueued during the reflow, schedule
|
||||
// another reflow event to process them. Note that we want to do this
|
||||
// after DidDoReflow(), since that method can change whether there are
|
||||
// dirty roots around by flushing, and there's no point in posting a
|
||||
// reflow event just to have the flush revoke it.
|
||||
if (!mDirtyRoots.IsEmpty()) {
|
||||
MaybeScheduleReflow();
|
||||
// And record that we might need flushing
|
||||
SetNeedLayoutFlush();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user