Backed out changeset 6a452e522e07 - Boris Zbarsky – Bug 67752. Implement interruptible reflow. r=roc,dbaron - because of apparent Tp hangs.
This commit is contained in:
@@ -934,7 +934,6 @@ public:
|
||||
nsIFrame** aPlaceholderFrame) const;
|
||||
NS_IMETHOD FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
|
||||
nsFrameState aBitToAdd);
|
||||
NS_IMETHOD_(void) FrameNeedsToContinueReflow(nsIFrame *aFrame);
|
||||
NS_IMETHOD CancelAllPendingReflows();
|
||||
NS_IMETHOD IsSafeToFlush(PRBool& aIsSafeToFlush);
|
||||
NS_IMETHOD FlushPendingNotifications(mozFlushType aType);
|
||||
@@ -958,7 +957,7 @@ public:
|
||||
|
||||
NS_IMETHOD ScrollContentIntoView(nsIContent* aContent,
|
||||
PRIntn aVPercent,
|
||||
PRIntn aHPercent);
|
||||
PRIntn aHPercent) const;
|
||||
|
||||
NS_IMETHOD SetIgnoreFrameDestruction(PRBool aIgnore);
|
||||
NS_IMETHOD NotifyDestroyingFrame(nsIFrame* aFrame);
|
||||
@@ -1134,31 +1133,23 @@ public:
|
||||
protected:
|
||||
virtual ~PresShell();
|
||||
|
||||
void HandlePostedReflowCallbacks(PRBool aInterruptible);
|
||||
void HandlePostedReflowCallbacks();
|
||||
void CancelPostedReflowCallbacks();
|
||||
|
||||
void UnsuppressAndInvalidate();
|
||||
|
||||
void WillDoReflow();
|
||||
void DidDoReflow(PRBool aInterruptible);
|
||||
// ProcessReflowCommands returns whether we processed all our dirty roots
|
||||
// without interruptions.
|
||||
PRBool ProcessReflowCommands(PRBool aInterruptible);
|
||||
void DidDoReflow();
|
||||
nsresult ProcessReflowCommands(PRBool aInterruptible);
|
||||
void ClearReflowEventStatus();
|
||||
void PostReflowEvent();
|
||||
|
||||
// DoReflow returns whether the reflow finished without interruption
|
||||
PRBool DoReflow(nsIFrame* aFrame, PRBool aInterruptible);
|
||||
|
||||
void DoReflow(nsIFrame* aFrame);
|
||||
#ifdef DEBUG
|
||||
void DoVerifyReflow();
|
||||
void VerifyHasDirtyRootAncestor(nsIFrame* aFrame);
|
||||
#endif
|
||||
|
||||
// Helper for ScrollContentIntoView
|
||||
nsresult DoScrollContentIntoView(nsIContent* aContent,
|
||||
PRIntn aVPercent,
|
||||
PRIntn aHPercent);
|
||||
|
||||
friend class nsPresShellEventCB;
|
||||
|
||||
class ReflowEvent;
|
||||
@@ -1242,6 +1233,11 @@ protected:
|
||||
// Utility method to restore the root scrollframe state
|
||||
void RestoreRootScrollPosition();
|
||||
|
||||
// Method to handle actually flushing. This allows the caller to control
|
||||
// whether the reflow flush (if any) should be interruptible.
|
||||
nsresult DoFlushPendingNotifications(mozFlushType aType,
|
||||
PRBool aInterruptibleReflow);
|
||||
|
||||
nsCOMPtr<nsICSSStyleSheet> mPrefStyleSheet; // mStyleSet owns it but we
|
||||
// maintain a ref, may be null
|
||||
#ifdef DEBUG
|
||||
@@ -1272,25 +1268,6 @@ protected:
|
||||
|
||||
nsRevocableEventPtr<ReflowEvent> mReflowEvent;
|
||||
|
||||
#ifdef DEBUG
|
||||
// The reflow root under which we're currently reflowing. Null when
|
||||
// not in reflow.
|
||||
nsIFrame* mCurrentReflowRoot;
|
||||
#endif
|
||||
|
||||
// Set of frames that we should mark with NS_FRAME_HAS_DIRTY_CHILDREN after
|
||||
// we finish reflowing mCurrentReflowRoot.
|
||||
nsTHashtable< nsPtrHashKey<nsIFrame> > mFramesToDirty;
|
||||
|
||||
// Information needed to properly handle scrolling content into view if the
|
||||
// pre-scroll reflow flush can be interrupted. mContentToScrollTo is
|
||||
// non-null between the initial scroll attempt and the first time we finish
|
||||
// processing all our dirty roots. mContentScrollVPosition and
|
||||
// mContentScrollHPosition are only used when it's non-null.
|
||||
nsCOMPtr<nsIContent> mContentToScrollTo;
|
||||
PRIntn mContentScrollVPosition;
|
||||
PRIntn mContentScrollHPosition;
|
||||
|
||||
struct nsBlurOrFocusTarget
|
||||
{
|
||||
nsBlurOrFocusTarget(nsPIDOMEventTarget* aTarget, PRUint32 aEventType)
|
||||
@@ -1307,8 +1284,6 @@ protected:
|
||||
nsCallbackEventRequest* mFirstCallbackEventRequest;
|
||||
nsCallbackEventRequest* mLastCallbackEventRequest;
|
||||
|
||||
PRPackedBool mSuppressInterruptibleReflows;
|
||||
|
||||
PRPackedBool mIsThemeSupportDisabled; // Whether or not form controls should use nsITheme in this shell.
|
||||
|
||||
PRPackedBool mIsDocumentGone; // We've been disconnected from the document.
|
||||
@@ -1643,10 +1618,6 @@ PresShell::Init(nsIDocument* aDocument,
|
||||
result = mStackArena.Init();
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
|
||||
if (!mFramesToDirty.Init()) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
mDocument = aDocument;
|
||||
NS_ADDREF(mDocument);
|
||||
mViewManager = aViewManager;
|
||||
@@ -1778,8 +1749,6 @@ PresShell::Destroy()
|
||||
if (mHaveShutDown)
|
||||
return NS_OK;
|
||||
|
||||
mContentToScrollTo = nsnull;
|
||||
|
||||
if (mPresContext) {
|
||||
// We need to notify the destroying the nsPresContext to ESM for
|
||||
// suppressing to use from ESM.
|
||||
@@ -2725,12 +2694,14 @@ PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight)
|
||||
|
||||
// Kick off a top-down reflow
|
||||
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
|
||||
mIsReflowing = PR_TRUE;
|
||||
|
||||
mDirtyRoots.RemoveElement(rootFrame);
|
||||
DoReflow(rootFrame, PR_TRUE);
|
||||
DoReflow(rootFrame);
|
||||
mIsReflowing = PR_FALSE;
|
||||
}
|
||||
|
||||
DidDoReflow(PR_TRUE);
|
||||
DidDoReflow();
|
||||
}
|
||||
|
||||
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
|
||||
@@ -3440,20 +3411,6 @@ PresShell::FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(void)
|
||||
PresShell::FrameNeedsToContinueReflow(nsIFrame *aFrame)
|
||||
{
|
||||
NS_ASSERTION(mIsReflowing, "Must be in reflow when marking path dirty.");
|
||||
NS_PRECONDITION(mCurrentReflowRoot, "Must have a current reflow root here");
|
||||
NS_ASSERTION(aFrame == mCurrentReflowRoot ||
|
||||
nsLayoutUtils::IsProperAncestorFrame(mCurrentReflowRoot, aFrame),
|
||||
"Frame passed in is not the descendant of mCurrentReflowRoot");
|
||||
NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW,
|
||||
"Frame passed in not in reflow?");
|
||||
|
||||
mFramesToDirty.PutEntry(aFrame);
|
||||
}
|
||||
|
||||
nsIScrollableView*
|
||||
PresShell::GetViewToScroll(nsLayoutUtils::Direction aDirection)
|
||||
{
|
||||
@@ -3587,8 +3544,6 @@ PresShell::ClearFrameRefs(nsIFrame* aFrame)
|
||||
}
|
||||
}
|
||||
|
||||
mFramesToDirty.RemoveEntry(aFrame);
|
||||
|
||||
nsWeakFrame* weakFrame = mWeakFrames;
|
||||
while (weakFrame) {
|
||||
nsWeakFrame* prev = weakFrame->GetPreviousWeakFrame();
|
||||
@@ -4127,21 +4082,17 @@ static void ScrollViewToShowRect(nsIScrollableView* aScrollingView,
|
||||
NS_IMETHODIMP
|
||||
PresShell::ScrollContentIntoView(nsIContent* aContent,
|
||||
PRIntn aVPercent,
|
||||
PRIntn aHPercent)
|
||||
PRIntn aHPercent) const
|
||||
{
|
||||
mContentToScrollTo = aContent;
|
||||
mContentScrollVPosition = aVPercent;
|
||||
mContentScrollHPosition = aHPercent;
|
||||
|
||||
nsCOMPtr<nsIContent> content = aContent; // Keep content alive while flushing.
|
||||
NS_ENSURE_TRUE(content, NS_ERROR_NULL_POINTER);
|
||||
nsCOMPtr<nsIDocument> currentDoc = content->GetCurrentDoc();
|
||||
NS_ENSURE_STATE(currentDoc);
|
||||
currentDoc->FlushPendingNotifications(Flush_InterruptibleLayout);
|
||||
|
||||
// If mContentToScrollTo is non-null, that means we interrupted the reflow
|
||||
// and won't necessarily get the position correct, but do a best-effort
|
||||
// scroll.
|
||||
currentDoc->FlushPendingNotifications(Flush_Layout);
|
||||
nsIFrame* frame = GetPrimaryFrameFor(content);
|
||||
if (!frame) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
// Before we scroll the frame into view, ask the command dispatcher
|
||||
// if we're resetting focus because a window just got an activate
|
||||
@@ -4158,26 +4109,11 @@ PresShell::ScrollContentIntoView(nsIContent* aContent,
|
||||
PRBool dontScroll = PR_FALSE;
|
||||
focusController->GetSuppressFocusScroll(&dontScroll);
|
||||
if (dontScroll) {
|
||||
mContentToScrollTo = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DoScrollContentIntoView(content, aVPercent, aHPercent);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresShell::DoScrollContentIntoView(nsIContent* aContent,
|
||||
PRIntn aVPercent,
|
||||
PRIntn aHPercent)
|
||||
{
|
||||
nsIFrame* frame = GetPrimaryFrameFor(aContent);
|
||||
if (!frame) {
|
||||
mContentToScrollTo = nsnull;
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
// This is a two-step process.
|
||||
// Step 1: Find the bounds of the rect we want to scroll into view. For
|
||||
// example, for an inline frame we may want to scroll in the whole
|
||||
@@ -4640,7 +4576,7 @@ PresShell::CancelPostedReflowCallbacks()
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::HandlePostedReflowCallbacks(PRBool aInterruptible)
|
||||
PresShell::HandlePostedReflowCallbacks()
|
||||
{
|
||||
PRBool shouldFlush = PR_FALSE;
|
||||
|
||||
@@ -4659,10 +4595,8 @@ PresShell::HandlePostedReflowCallbacks(PRBool aInterruptible)
|
||||
}
|
||||
}
|
||||
|
||||
mozFlushType flushType =
|
||||
aInterruptible ? Flush_InterruptibleLayout : Flush_Layout;
|
||||
if (shouldFlush)
|
||||
FlushPendingNotifications(flushType);
|
||||
FlushPendingNotifications(Flush_Layout);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@@ -4687,8 +4621,15 @@ PresShell::IsSafeToFlush(PRBool& aIsSafeToFlush)
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
NS_IMETHODIMP
|
||||
PresShell::FlushPendingNotifications(mozFlushType aType)
|
||||
{
|
||||
return DoFlushPendingNotifications(aType, PR_FALSE);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresShell::DoFlushPendingNotifications(mozFlushType aType,
|
||||
PRBool aInterruptibleReflow)
|
||||
{
|
||||
NS_ASSERTION(aType >= Flush_Frames, "Why did we get called?");
|
||||
|
||||
@@ -4757,17 +4698,11 @@ PresShell::FlushPendingNotifications(mozFlushType aType)
|
||||
// There might be more pending constructors now, but we're not going to
|
||||
// worry about them. They can't be triggered during reflow, so we should
|
||||
// be good.
|
||||
|
||||
if (aType >= (mSuppressInterruptibleReflows ? Flush_Layout : Flush_InterruptibleLayout) &&
|
||||
!mIsDestroying) {
|
||||
|
||||
if (aType >= Flush_Layout && !mIsDestroying) {
|
||||
mFrameConstructor->RecalcQuotesAndCounters();
|
||||
mViewManager->FlushDelayedResize();
|
||||
if (ProcessReflowCommands(aType < Flush_Layout) && mContentToScrollTo) {
|
||||
// We didn't get interrupted. Go ahead and scroll to our content
|
||||
DoScrollContentIntoView(mContentToScrollTo, mContentScrollVPosition,
|
||||
mContentScrollHPosition);
|
||||
mContentToScrollTo = nsnull;
|
||||
}
|
||||
ProcessReflowCommands(aInterruptibleReflow);
|
||||
}
|
||||
|
||||
PRUint32 updateFlags = NS_VMREFRESH_NO_SYNC;
|
||||
@@ -4776,7 +4711,7 @@ PresShell::FlushPendingNotifications(mozFlushType aType)
|
||||
// immediately
|
||||
updateFlags = NS_VMREFRESH_IMMEDIATE;
|
||||
}
|
||||
else if (aType < Flush_InterruptibleLayout) {
|
||||
else if (aType < Flush_Layout) {
|
||||
// Not flushing reflows, so do deferred invalidates. This will keep us
|
||||
// from possibly flushing out reflows due to invalidates being processed
|
||||
// at the end of this view batch.
|
||||
@@ -6610,7 +6545,7 @@ PresShell::WillPaint()
|
||||
// reflow being interspersed. Note that we _do_ allow this to be
|
||||
// interruptible; if we can't do all the reflows it's better to flicker a bit
|
||||
// than to freeze up.
|
||||
FlushPendingNotifications(Flush_InterruptibleLayout);
|
||||
DoFlushPendingNotifications(Flush_Layout, PR_TRUE);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@@ -6820,17 +6755,7 @@ PresShell::ReflowEvent::Run() {
|
||||
// before processing that pres shell's reflow commands. Fixes bug 54868.
|
||||
nsCOMPtr<nsIViewManager> viewManager = ps->GetViewManager();
|
||||
|
||||
ps->mSuppressInterruptibleReflows = PR_FALSE;
|
||||
|
||||
#ifdef NOISY_INTERRUPTIBLE_REFLOW
|
||||
printf("*** Entering reflow event (time=%lld)\n", PR_Now());
|
||||
#endif /* NOISY_INTERRUPTIBLE_REFLOW */
|
||||
|
||||
ps->FlushPendingNotifications(Flush_InterruptibleLayout);
|
||||
|
||||
#ifdef NOISY_INTERRUPTIBLE_REFLOW
|
||||
printf("*** Returning from reflow event (time=%lld)\n", PR_Now());
|
||||
#endif /* NOISY_INTERRUPTIBLE_REFLOW */
|
||||
ps->DoFlushPendingNotifications(Flush_Layout, PR_TRUE);
|
||||
|
||||
// Now, explicitly release the pres shell before the view manager
|
||||
ps = nsnull;
|
||||
@@ -6877,11 +6802,11 @@ PresShell::WillDoReflow()
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::DidDoReflow(PRBool aInterruptible)
|
||||
PresShell::DidDoReflow()
|
||||
{
|
||||
mFrameConstructor->EndUpdate();
|
||||
|
||||
HandlePostedReflowCallbacks(aInterruptible);
|
||||
HandlePostedReflowCallbacks();
|
||||
// Null-check mViewManager in case this happens during Destroy. See
|
||||
// bugs 244435 and 238546.
|
||||
if (!mPaintingSuppressed && mViewManager)
|
||||
@@ -6894,24 +6819,8 @@ PresShell::DidDoReflow(PRBool aInterruptible)
|
||||
}
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
MarkFramesDirtyToRoot(nsPtrHashKey<nsIFrame>* p, void* closure)
|
||||
{
|
||||
nsIFrame* target = static_cast<nsIFrame*>(closure);
|
||||
for (nsIFrame* f = p->GetKey(); f && !NS_SUBTREE_DIRTY(f);
|
||||
f = f->GetParent()) {
|
||||
f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
|
||||
|
||||
if (f == target) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
PRBool
|
||||
PresShell::DoReflow(nsIFrame* target, PRBool aInterruptible)
|
||||
void
|
||||
PresShell::DoReflow(nsIFrame* target)
|
||||
{
|
||||
nsIFrame* rootFrame = FrameManager()->GetRootFrame();
|
||||
|
||||
@@ -6920,13 +6829,9 @@ PresShell::DoReflow(nsIFrame* target, PRBool aInterruptible)
|
||||
// reflow; otherwise, it crashes on the mac (I'm not quite sure why)
|
||||
nsresult rv = CreateRenderingContext(rootFrame, getter_AddRefs(rcx));
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_NOTREACHED("CreateRenderingContext failure");
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
mCurrentReflowRoot = target;
|
||||
#endif
|
||||
NS_NOTREACHED("CreateRenderingContext failure");
|
||||
return;
|
||||
}
|
||||
|
||||
target->WillReflow(mPresContext);
|
||||
|
||||
@@ -6961,9 +6866,6 @@ PresShell::DoReflow(nsIFrame* target, PRBool aInterruptible)
|
||||
reflowState.mComputedBorderPadding.LeftRight(),
|
||||
"reflow state computed incorrect width");
|
||||
|
||||
mPresContext->ReflowStarted(aInterruptible);
|
||||
mIsReflowing = PR_TRUE;
|
||||
|
||||
nsReflowStatus status;
|
||||
nsHTMLReflowMetrics desiredSize;
|
||||
target->Reflow(mPresContext, desiredSize, reflowState, status);
|
||||
@@ -6995,38 +6897,6 @@ PresShell::DoReflow(nsIFrame* target, PRBool aInterruptible)
|
||||
mPresContext->SetVisibleArea(nsRect(0, 0, desiredSize.width,
|
||||
desiredSize.height));
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
mCurrentReflowRoot = nsnull;
|
||||
#endif
|
||||
|
||||
NS_ASSERTION(mPresContext->HasPendingInterrupt() ||
|
||||
mFramesToDirty.Count() == 0,
|
||||
"Why do we need to dirty anything if not interrupted?");
|
||||
|
||||
mIsReflowing = PR_FALSE;
|
||||
PRBool interrupted = mPresContext->HasPendingInterrupt();
|
||||
if (interrupted) {
|
||||
// Make sure target gets reflowed again.
|
||||
mFramesToDirty.EnumerateEntries(&MarkFramesDirtyToRoot, target);
|
||||
NS_ASSERTION(NS_SUBTREE_DIRTY(target), "Why is the target not dirty?");
|
||||
mDirtyRoots.AppendElement(target);
|
||||
|
||||
// Clear mFramesToDirty after we've done the NS_SUBTREE_DIRTY(target)
|
||||
// assertion so that if it fails it's easier to see what's going on.
|
||||
#ifdef NOISY_INTERRUPTIBLE_REFLOW
|
||||
printf("mFramesToDirty.Count() == %u\n", mFramesToDirty.Count());
|
||||
#endif /* NOISY_INTERRUPTIBLE_REFLOW */
|
||||
mFramesToDirty.Clear();
|
||||
|
||||
// Any FlushPendingNotifications with interruptible reflows
|
||||
// should be suppressed now. We don't want to do extra reflow work
|
||||
// before our reflow event happens.
|
||||
mSuppressInterruptibleReflows = PR_TRUE;
|
||||
PostReflowEvent();
|
||||
}
|
||||
|
||||
return !interrupted;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
@@ -7063,13 +6933,12 @@ PresShell::DoVerifyReflow()
|
||||
}
|
||||
#endif
|
||||
|
||||
PRBool
|
||||
nsresult
|
||||
PresShell::ProcessReflowCommands(PRBool aInterruptible)
|
||||
{
|
||||
MOZ_TIMER_DEBUGLOG(("Start: Reflow: PresShell::ProcessReflowCommands(), this=%p\n", this));
|
||||
MOZ_TIMER_START(mReflowWatch);
|
||||
|
||||
PRBool interrupted = PR_FALSE;
|
||||
if (0 != mDirtyRoots.Length()) {
|
||||
|
||||
#ifdef DEBUG
|
||||
@@ -7089,6 +6958,7 @@ PresShell::ProcessReflowCommands(PRBool aInterruptible)
|
||||
{
|
||||
nsAutoScriptBlocker scriptBlocker;
|
||||
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
|
||||
mIsReflowing = PR_TRUE;
|
||||
|
||||
do {
|
||||
// Send an incremental reflow notification to the target frame.
|
||||
@@ -7103,19 +6973,22 @@ PresShell::ProcessReflowCommands(PRBool aInterruptible)
|
||||
continue;
|
||||
}
|
||||
|
||||
interrupted = !DoReflow(target, aInterruptible);
|
||||
DoReflow(target);
|
||||
|
||||
// Keep going until we're out of reflow commands, or we've run
|
||||
// past our deadline, or we're interrupted.
|
||||
} while (!interrupted && mDirtyRoots.Length() &&
|
||||
// past our deadline.
|
||||
} while (mDirtyRoots.Length() &&
|
||||
(!aInterruptible || PR_IntervalNow() < deadline));
|
||||
|
||||
interrupted = mDirtyRoots.Length() != 0;
|
||||
// XXXwaterson for interruptible reflow, examine the tree and
|
||||
// re-enqueue any unflowed reflow targets.
|
||||
|
||||
mIsReflowing = PR_FALSE;
|
||||
}
|
||||
|
||||
// Exiting the scriptblocker might have killed us
|
||||
if (!mIsDestroying) {
|
||||
DidDoReflow(aInterruptible);
|
||||
DidDoReflow();
|
||||
}
|
||||
|
||||
// DidDoReflow might have killed us
|
||||
@@ -7151,7 +7024,7 @@ PresShell::ProcessReflowCommands(PRBool aInterruptible)
|
||||
UnsuppressAndInvalidate();
|
||||
}
|
||||
|
||||
return !interrupted;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
Reference in New Issue
Block a user