Backed out changeset 6a452e522e07 - Boris Zbarsky – Bug 67752. Implement interruptible reflow. r=roc,dbaron - because of apparent Tp hangs.

This commit is contained in:
Joe Drew
2009-04-21 23:02:17 -04:00
parent 600528e80b
commit 659abece3b
37 changed files with 147 additions and 823 deletions

View File

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