Add assertions to verify no-reentry invariants in layout. b=310985 r+sr=bzbarsky

This commit is contained in:
dbaron%dbaron.org 2006-04-18 05:44:02 +00:00
parent 755d917156
commit 637a3c6242
4 changed files with 142 additions and 3 deletions

View File

@ -4693,6 +4693,7 @@ nsresult
nsCSSFrameConstructor::ConstructRootFrame(nsIContent* aDocElement,
nsIFrame** aNewFrame)
{
AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
NS_PRECONDITION(aNewFrame, "null out param");
// how the root frame hierarchy should look
@ -8196,6 +8197,13 @@ IsRootBoxFrame(nsIFrame *aFrame)
nsresult
nsCSSFrameConstructor::ReconstructDocElementHierarchy()
{
AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
return ReconstructDocElementHierarchyInternal();
}
nsresult
nsCSSFrameConstructor::ReconstructDocElementHierarchyInternal()
{
#ifdef DEBUG
if (gNoisyContentUpdates) {
@ -8802,6 +8810,7 @@ nsresult
nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
PRInt32 aNewIndexInContainer)
{
AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
#ifdef DEBUG
if (gNoisyContentUpdates) {
printf("nsCSSFrameConstructor::ContentAppended container=%p index=%d\n",
@ -8891,11 +8900,13 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
for (ChildIterator::Init(insertionContent, &iter, &last);
iter != last;
++iter) {
LAYOUT_PHASE_TEMP_EXIT();
nsIContent* item = nsCOMPtr<nsIContent>(*iter);
if (item == child)
// Call ContentInserted with this index.
ContentInserted(aContainer, child,
iter.index(), mTempFrameTreeState, PR_FALSE);
LAYOUT_PHASE_TEMP_REENTER();
}
}
@ -9061,7 +9072,9 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
}
// We built some new frames. Initialize any newly-constructed bindings.
LAYOUT_PHASE_TEMP_EXIT();
mDocument->BindingManager()->ProcessAttachedQueue();
LAYOUT_PHASE_TEMP_REENTER();
// process the current pseudo frame state
if (!state.mPseudoFrames.IsEmpty()) {
@ -9312,6 +9325,7 @@ nsCSSFrameConstructor::ContentInserted(nsIContent* aContainer,
nsILayoutHistoryState* aFrameState,
PRBool aInReinsertContent)
{
AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
// XXXldb Do we need to re-resolve style to handle the CSS2 + combinator and
// the :empty pseudo-class?
#ifdef DEBUG
@ -9388,7 +9402,9 @@ nsCSSFrameConstructor::ContentInserted(nsIContent* aContainer,
#endif
}
LAYOUT_PHASE_TEMP_EXIT();
mDocument->BindingManager()->ProcessAttachedQueue();
LAYOUT_PHASE_TEMP_REENTER();
// otherwise this is not a child of the root element, and we
// won't let it have a frame.
@ -9607,7 +9623,9 @@ nsCSSFrameConstructor::ContentInserted(nsIContent* aContainer,
// Now that we've created frames, run the attach queue.
//XXXwaterson should we do this after we've processed pseudos, too?
LAYOUT_PHASE_TEMP_EXIT();
mDocument->BindingManager()->ProcessAttachedQueue();
LAYOUT_PHASE_TEMP_REENTER();
// process the current pseudo frame state
if (!state.mPseudoFrames.IsEmpty())
@ -9897,6 +9915,7 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
PRInt32 aIndexInContainer,
PRBool aInReinsertContent)
{
AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
// XXXldb Do we need to re-resolve style to handle the CSS2 + combinator and
// the :empty pseudo-class?
@ -10362,6 +10381,7 @@ nsresult
nsCSSFrameConstructor::CharacterDataChanged(nsIContent* aContent,
PRBool aAppend)
{
AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
nsresult rv = NS_OK;
// Find the child frame
@ -13243,7 +13263,7 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState,
ReinsertContent(parentContainer, blockContent);
}
else if (blockContent->GetCurrentDoc() == mDocument) {
ReconstructDocElementHierarchy();
ReconstructDocElementHierarchyInternal();
}
return PR_TRUE;
}
@ -13303,7 +13323,7 @@ nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame)
}
// If we get here, we're screwed!
return ReconstructDocElementHierarchy();
return ReconstructDocElementHierarchyInternal();
}
nsresult nsCSSFrameConstructor::RemoveFixedItems(const nsFrameConstructorState& aState)

View File

@ -193,6 +193,8 @@ public:
private:
nsresult ReconstructDocElementHierarchyInternal();
nsresult ReinsertContent(nsIContent* aContainer,
nsIContent* aChild);
@ -206,7 +208,6 @@ private:
void DoContentStateChanged(nsIContent* aContent,
PRInt32 aStateMask);
private:
/* aMinHint is the minimal change that should be made to the element */
void RestyleElement(nsIContent* aContent,
nsIFrame* aPrimaryFrame,

View File

@ -121,6 +121,17 @@ enum nsPresContext_CachedIntPrefType {
const PRUint8 kPresContext_DefaultVariableFont_ID = 0x00; // kGenericFont_moz_variable
const PRUint8 kPresContext_DefaultFixedFont_ID = 0x01; // kGenericFont_moz_fixed
#ifdef DEBUG
struct nsAutoLayoutPhase;
enum nsLayoutPhase {
eLayoutPhase_Paint,
eLayoutPhase_Reflow,
eLayoutPhase_FrameC,
eLayoutPhase_COUNT
};
#endif
// An interface for presentation contexts. Presentation contexts are
// objects that provide an outer context for a presentation shell.
@ -762,6 +773,16 @@ protected:
eDefaultFont_COUNT
};
#ifdef DEBUG
private:
friend struct nsAutoLayoutPhase;
PRUint32 mLayoutPhaseCount[eLayoutPhase_COUNT];
public:
PRUint32 LayoutPhaseCount(nsLayoutPhase aPhase) {
return mLayoutPhaseCount[aPhase];
}
#endif
};
// Bit values for StartLoadImage's aImageStatus
@ -769,6 +790,94 @@ protected:
#define NS_LOAD_IMAGE_STATUS_SIZE 0x2
#define NS_LOAD_IMAGE_STATUS_BITS 0x4
#ifdef DEBUG
struct nsAutoLayoutPhase {
nsAutoLayoutPhase(nsPresContext* aPresContext, nsLayoutPhase aPhase)
: mPresContext(aPresContext), mPhase(aPhase), mCount(0)
{
Enter();
}
~nsAutoLayoutPhase()
{
Exit();
NS_ASSERTION(mCount == 0, "imbalanced");
}
void Enter()
{
switch (mPhase) {
case eLayoutPhase_Paint:
NS_ASSERTION(mPresContext->mLayoutPhaseCount[eLayoutPhase_Paint] == 0,
"recurring into paint");
NS_ASSERTION(mPresContext->mLayoutPhaseCount[eLayoutPhase_Reflow] == 0,
"painting in the middle of reflow");
NS_ASSERTION(mPresContext->mLayoutPhaseCount[eLayoutPhase_FrameC] == 0,
"painting in the middle of frame construction");
break;
case eLayoutPhase_Reflow:
NS_ASSERTION(mPresContext->mLayoutPhaseCount[eLayoutPhase_Paint] == 0,
"reflowing in the middle of a paint");
NS_ASSERTION(mPresContext->mLayoutPhaseCount[eLayoutPhase_Reflow] == 0,
"recurring into reflow");
NS_ASSERTION(mPresContext->mLayoutPhaseCount[eLayoutPhase_FrameC] == 0,
"reflowing in the middle of frame construction");
break;
case eLayoutPhase_FrameC:
NS_ASSERTION(mPresContext->mLayoutPhaseCount[eLayoutPhase_Paint] == 0,
"constructing frames in the middle of a paint");
NS_ASSERTION(mPresContext->mLayoutPhaseCount[eLayoutPhase_Reflow] == 0,
"constructing frames in the middle of reflow");
// The nsXBLService::LoadBindings call in ConstructFrameInternal
// makes us hit this one too often to be an NS_ASSERTION,
// despite how scary it is.
NS_WARN_IF_FALSE(mPresContext->mLayoutPhaseCount[eLayoutPhase_FrameC] == 0,
"recurring into frame construction");
break;
default:
break;
}
++(mPresContext->mLayoutPhaseCount[mPhase]);
++mCount;
}
void Exit()
{
NS_ASSERTION(mCount > 0 && mPresContext->mLayoutPhaseCount[mPhase] > 0,
"imbalanced");
--(mPresContext->mLayoutPhaseCount[mPhase]);
--mCount;
}
private:
nsPresContext *mPresContext;
nsLayoutPhase mPhase;
PRUint32 mCount;
};
#define AUTO_LAYOUT_PHASE_ENTRY_POINT(pc_, phase_) \
nsAutoLayoutPhase autoLayoutPhase((pc_), (eLayoutPhase_##phase_))
#define LAYOUT_PHASE_TEMP_EXIT() \
PR_BEGIN_MACRO \
autoLayoutPhase.Exit(); \
PR_END_MACRO
#define LAYOUT_PHASE_TEMP_REENTER() \
PR_BEGIN_MACRO \
autoLayoutPhase.Enter(); \
PR_END_MACRO
#else
#define AUTO_LAYOUT_PHASE_ENTRY_POINT(pc_, phase_) \
PR_BEGIN_MACRO PR_END_MACRO
#define LAYOUT_PHASE_TEMP_EXIT() \
PR_BEGIN_MACRO PR_END_MACRO
#define LAYOUT_PHASE_TEMP_REENTER() \
PR_BEGIN_MACRO PR_END_MACRO
#endif
#ifdef MOZ_REFLOW_PERF
#define DO_GLOBAL_REFLOW_COUNT(_name, _type) \

View File

@ -2805,6 +2805,7 @@ PresShell::InitialReflow(nscoord aWidth, nscoord aHeight)
nsresult rv=CreateRenderingContext(rootFrame, &rcx);
if (NS_FAILED(rv)) return rv;
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
mIsReflowing = PR_TRUE;
nsHTMLReflowState reflowState(mPresContext, rootFrame,
@ -2952,6 +2953,9 @@ PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight)
nsresult rv=CreateRenderingContext(rootFrame, &rcx);
if (NS_FAILED(rv)) return rv;
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
// XXXldb Set mIsReflowing (and unset it later)?
nsHTMLReflowState reflowState(mPresContext, rootFrame,
eReflowReason_Resize, rcx, maxSize);
@ -3402,6 +3406,9 @@ PresShell::StyleChangeReflow()
nsresult rv=CreateRenderingContext(rootFrame, &rcx);
if (NS_FAILED(rv)) return rv;
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
// XXXldb Set mIsReflowing (and unset it later)?
nsHTMLReflowState reflowState(mPresContext, rootFrame,
eReflowReason_StyleChange, rcx, maxSize);
@ -5555,6 +5562,7 @@ PresShell::Paint(nsIView* aView,
nsIRenderingContext* aRenderingContext,
const nsRegion& aDirtyRegion)
{
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Paint);
nsIFrame* frame;
nsresult rv = NS_OK;
@ -6515,6 +6523,7 @@ PresShell::ProcessReflowCommands(PRBool aInterruptible)
mDocument->BeginUpdate(UPDATE_ALL);
mDocument->EndUpdate(UPDATE_ALL);
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
mIsReflowing = PR_TRUE;
do {