From f7d5a13f2b4a41fd1921579cf06fcb12e126a4cf Mon Sep 17 00:00:00 2001 From: kipp Date: Thu, 18 Jun 1998 23:16:00 +0000 Subject: [PATCH] snapshot --- layout/css/layout/src/makefile.win | 55 - layout/css/layout/src/nsCSSBlockFrame.cpp | 19 +- layout/css/layout/src/nsCSSBlockFrame.h | 2 - layout/css/layout/src/nsCSSInlineFrame.cpp | 1712 ++++++------------- layout/css/layout/src/nsCSSInlineFrame.h | 127 +- layout/css/layout/src/nsCSSInlineLayout.cpp | 422 +++++ layout/css/layout/src/nsCSSInlineLayout.h | 97 ++ layout/css/layout/src/nsCSSLineLayout.cpp | 379 +--- layout/css/layout/src/nsCSSLineLayout.h | 159 +- 9 files changed, 1070 insertions(+), 1902 deletions(-) create mode 100644 layout/css/layout/src/nsCSSInlineLayout.cpp create mode 100644 layout/css/layout/src/nsCSSInlineLayout.h diff --git a/layout/css/layout/src/makefile.win b/layout/css/layout/src/makefile.win index e7ef628ffaa..e69de29bb2d 100644 --- a/layout/css/layout/src/makefile.win +++ b/layout/css/layout/src/makefile.win @@ -1,55 +0,0 @@ -#!nmake -# -# The contents of this file are subject to the Netscape Public License -# Version 1.0 (the "NPL"); you may not use this file except in -# compliance with the NPL. You may obtain a copy of the NPL at -# http://www.mozilla.org/NPL/ -# -# Software distributed under the NPL is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL -# for the specific language governing rights and limitations under the -# NPL. -# -# The Initial Developer of this code under the NPL is Netscape -# Communications Corporation. Portions created by Netscape are -# Copyright (C) 1998 Netscape Communications Corporation. All Rights -# Reserved. - -DEPTH=..\..\..\.. - -LIBRARY_NAME=nglcsslay_s -MODULE=raptor -REQUIRES=xpcom raptor -DEFINES=-D_IMPL_NS_HTML -DWIN32_LEAN_AND_MEAN - -CPPSRCS= \ - nsCSSBlockFrame.cpp \ - nsCSSContainerFrame.cpp \ - nsCSSInlineFrame.cpp \ - nsCSSLineLayout.cpp \ - $(NULL) - -CPP_OBJS= \ - .\$(OBJDIR)\nsCSSBlockFrame.obj \ - .\$(OBJDIR)\nsCSSContainerFrame.obj \ - .\$(OBJDIR)\nsCSSInlineFrame.obj \ - .\$(OBJDIR)\nsCSSLineLayout.obj \ - $(NULL) - -LINCS = \ - -I$(PUBLIC)\xpcom -I$(PUBLIC)\raptor -I$(PUBLIC)\dom -I$(PUBLIC)\netlib \ - -I..\..\..\base\src \ - -I..\..\..\html\style\src -I..\..\..\html\base\src - -LCFLAGS = \ - $(LCFLAGS) \ - $(DEFINES) \ - $(NULL) - -include <$(DEPTH)\layout\config\rules.mak> - -libs:: $(LIBRARY) - $(MAKE_INSTALL) $(LIBRARY) $(DIST)\lib - -clobber:: - rm -f $(DIST)\lib\$(LIBRARY_NAME).lib diff --git a/layout/css/layout/src/nsCSSBlockFrame.cpp b/layout/css/layout/src/nsCSSBlockFrame.cpp index 32369b87b96..643d9f46a4c 100644 --- a/layout/css/layout/src/nsCSSBlockFrame.cpp +++ b/layout/css/layout/src/nsCSSBlockFrame.cpp @@ -18,9 +18,11 @@ */ #include "nsCSSBlockFrame.h" #include "nsCSSLineLayout.h" +#include "nsCSSInlineLayout.h" #include "nsCSSLayout.h" #include "nsPlaceholderFrame.h" #include "nsStyleConsts.h" +#include "nsHTMLIIDs.h" #include "nsIAnchoredItems.h" #include "nsIPresContext.h" @@ -35,11 +37,6 @@ #include "nsHTMLValue.h"// XXX list ordinal hack #include "nsIHTMLContent.h"// XXX list ordinal hack -static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID); -static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID); -static NS_DEFINE_IID(kIInlineReflowIID, NS_IINLINE_REFLOW_IID); -static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID); - // XXX mLastContentOffset, mFirstContentOffset, mLastContentIsComplete // XXX inline span frames // XXX IsFirstChild @@ -178,8 +175,6 @@ nsCSSBlockReflowState::nsCSSBlockReflowState(nsIPresContext* aPresContext, if (!mBlockIsPseudo) { const nsStyleSpacing* blockSpacing = (const nsStyleSpacing*) mBlockSC->GetStyleData(eStyleStruct_Spacing); - const nsStylePosition* blockPosition = (const nsStylePosition*) - mBlockSC->GetStyleData(eStyleStruct_Position); blockSpacing->CalcBorderPaddingFor(mBlock, mBorderPadding); mY = mBorderPadding.top; @@ -1145,8 +1140,8 @@ InlineFrameData::ReflowLine(nsCSSBlockReflowState& aState, // Prepare for reflowing this line aLineLayout.Prepare(aState.mX); - nsCSSInlineLayout inlineLayout(aLineLayout, aState.mBlock, aState.mBlockSC, - aState); + nsCSSInlineLayout inlineLayout(aLineLayout, aState.mBlock, aState.mBlockSC); + inlineLayout.Init(&aState); inlineLayout.Prepare(aState.mUnconstrainedWidth, aState.mNoWrap, aState.mMaxElementSize); inlineLayout.SetReflowSpace(aState.mCurrentBand.availSpace.x, @@ -1291,7 +1286,7 @@ InlineFrameData::ReflowLine(nsCSSBlockReflowState& aState, // See if speculative application of the margin should stick if (ild == &mLines) { - if (0 == inlineLayout.GetLineHeight()) { + if (0 == inlineLayout.mMaxAscent + inlineLayout.mMaxDescent) { // No, undo margin application when we get a zero height child. aState.mY -= bottomMargin; if (aState.mY + bottomMargin >= aState.mCurrentBand.availSpace.YMost()) { @@ -1351,7 +1346,7 @@ InlineFrameData::SplitLine(nsCSSBlockReflowState& aState, InlineLineData* ild, nsIFrame* aFrame) { - PRInt32 pushCount = ild->mChildCount - aInlineLayout.GetFrameNum(); + PRInt32 pushCount = ild->mChildCount - aInlineLayout.mFrameNum; NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW, ("LineLayout::SplitLine: pushing %d frames", pushCount)); @@ -1692,7 +1687,7 @@ nsCSSBlockFrame::Reflow(nsIPresContext* aPresContext, // Replace parent provided reflow state with our own significantly // more extensive version. nsCSSBlockReflowState state(aPresContext, aSpaceManager, this, aReflowState, - aMetrics.maxElementSize); + aMetrics.maxElementSize); nsresult rv = NS_OK; if (eReflowReason_Initial == state.reason) { diff --git a/layout/css/layout/src/nsCSSBlockFrame.h b/layout/css/layout/src/nsCSSBlockFrame.h index 4887302fa4d..02eb9f6c540 100644 --- a/layout/css/layout/src/nsCSSBlockFrame.h +++ b/layout/css/layout/src/nsCSSBlockFrame.h @@ -171,6 +171,4 @@ protected: nsVoidArray* mRunInFloaters; }; -#define IS_REFLOW_ERROR(_status) (PRInt32(_status) < 0) - #endif /* nsCSSBlockFrame_h___ */ diff --git a/layout/css/layout/src/nsCSSInlineFrame.cpp b/layout/css/layout/src/nsCSSInlineFrame.cpp index a8226a9d4e1..0cf792b4e97 100644 --- a/layout/css/layout/src/nsCSSInlineFrame.cpp +++ b/layout/css/layout/src/nsCSSInlineFrame.cpp @@ -16,134 +16,82 @@ * Reserved. */ #include "nsCSSInlineFrame.h" -#include "nsSize.h" -#include "nsIContent.h" -#include "nsIContentDelegate.h" -#include "nsIStyleContext.h" -#include "nsStyleConsts.h" -#include "nsIPresContext.h" -#include "nsIPresShell.h" #include "nsCSSLayout.h" -#include "nsPlaceholderFrame.h" + #include "nsIReflowCommand.h" -#include "nsHTMLAtoms.h" -#include "nsAbsoluteFrame.h" -#include "nsLeafFrame.h" -#include "nsIPtr.h" +#include "nsIStyleContext.h" -// XXX To Do: -// 2. horizontal child margins -// 3. borders/padding and splitting -// 4. child relative positioning -// 5. absolutely positioned children -// 6. direction support -// 7. CSS line-height property +#include "nsHTMLBase.h"// XXX rename to nsCSSBase? -/* XXX */ -#include "nsHTMLIIDs.h" +// XXX +static NS_DEFINE_IID(kIInlineReflowIID, NS_IINLINE_REFLOW_IID); -#define DEFAULT_ASCENT_LEN 10 - -NS_DEF_PTR(nsIStyleContext); -NS_DEF_PTR(nsIContent); -NS_DEF_PTR(nsIContentDelegate); - -class nsInlineState +nsCSSInlineReflowState::nsCSSInlineReflowState(nsCSSLineLayout& aLineLayout, + nsCSSInlineFrame* aInlineFrame, + nsIStyleContext* aInlineSC, + const nsReflowState& aRS, + nsSize* aMaxElementSize) + : nsReflowState(aInlineFrame, *aRS.parentReflowState, aRS.maxSize), + mInlineLayout(aLineLayout, aInlineFrame, aInlineSC) { -public: - const nsStyleFont* font; // style font - nsMargin borderPadding; // inner margin - nsSize mStyleSize; - PRIntn mStyleSizeFlags; - nsSize availSize; // available space in which to reflow (starts as max size minus insets) - nsSize* maxElementSize; // maximum element size (may be null) - nscoord x; // running x-offset (starts at left inner edge) - const nscoord y; // y-offset (top inner edge) - nscoord maxAscent; // max child ascent - nscoord maxDescent; // max child descent - nscoord* ascents; // ascent information for each child - PRBool unconstrainedWidth; - PRBool unconstrainedHeight; - nsCSSLineLayout* lineLayout; - PRBool atBreakPoint; - const nsReflowState& reflowState; - PRBool mNoWrap; + mPresContext = aLineLayout.mPresContext; + mLastChild = nsnull; + mKidContentIndex = aInlineFrame->GetFirstContentOffset(); - // Constructor - nsInlineState(nsIPresContext* aPresContext, - nsIFrame* aInlineFrame, - const nsStyleFont* aStyleFont, - const nsMargin& aBorderPadding, - const nsReflowState& aReflowState, - nsSize* aMaxElementSize, - nsCSSLineLayout* aLineLayout) - : x(aBorderPadding.left), // determined by inner edge - y(aBorderPadding.top), // determined by inner edge - reflowState(aReflowState) - { - lineLayout = aLineLayout; - atBreakPoint = PR_TRUE; + const nsStyleText* styleText = (const nsStyleText*) + aInlineSC->GetStyleData(eStyleStruct_Text); + mNoWrap = PRBool(NS_STYLE_WHITESPACE_NORMAL != styleText->mWhiteSpace); + const nsStyleSpacing* styleSpacing = (const nsStyleSpacing*) + aInlineSC->GetStyleData(eStyleStruct_Spacing); + styleSpacing->CalcBorderPaddingFor(aInlineFrame, mBorderPadding); - font = aStyleFont; - borderPadding = aBorderPadding; - - unconstrainedWidth = PRBool(reflowState.maxSize.width == NS_UNCONSTRAINEDSIZE); - unconstrainedHeight = PRBool(reflowState.maxSize.height == NS_UNCONSTRAINEDSIZE); - - // If we're constrained adjust the available size so it excludes space - // needed for border/padding - availSize.width = reflowState.maxSize.width; - if (PR_FALSE == unconstrainedWidth) { - availSize.width -= aBorderPadding.left + aBorderPadding.right; - } - availSize.height = reflowState.maxSize.height; - if (PR_FALSE == unconstrainedHeight) { - availSize.height -= aBorderPadding.top + aBorderPadding.bottom; - } - - // Initialize max element size - maxElementSize = aMaxElementSize; - if (nsnull != maxElementSize) { - maxElementSize->SizeTo(0, 0); - } - - ascents = ascentBuf; - maxAscent = 0; - maxDescent = 0; - mNoWrap = PR_FALSE; + if (nsnull != aMaxElementSize) { + aMaxElementSize->SizeTo(0, 0); } - void SetNumAscents(PRIntn aNumAscents) { - // We keep around ascent information so that we can vertically align - // children after we figure out how many children fit. - if (aNumAscents > DEFAULT_ASCENT_LEN) { - ascents = new nscoord[aNumAscents]; - } + // If we're constrained adjust the available size so it excludes space + // needed for border/padding + nscoord width = maxSize.width; + if (NS_UNCONSTRAINEDSIZE != width) { + width -= mBorderPadding.left + mBorderPadding.right; + } + // XXX I don't think we need this! + nscoord height = maxSize.height; + if (NS_UNCONSTRAINEDSIZE != height) { + height -= mBorderPadding.top + mBorderPadding.bottom; } - // Destructor - ~nsInlineState() { - if (ascents != ascentBuf) { - delete ascents; - } + PRIntn ss = mStyleSizeFlags = + nsCSSLayout::GetStyleSize(mPresContext, *this, mStyleSize); + if (0 != (ss & NS_SIZE_HAS_WIDTH)) { + // When we are given a width, change the reflow behavior to + // unconstrained. + // XXX is this right? + width = NS_UNCONSTRAINEDSIZE; } -private: - nscoord ascentBuf[DEFAULT_ASCENT_LEN]; -}; + mInlineLayout.Init(this); + mInlineLayout.Prepare(PRBool(NS_UNCONSTRAINEDSIZE == width), + mNoWrap, aMaxElementSize); + mInlineLayout.SetReflowSpace(mBorderPadding.left, mBorderPadding.top, + width, height/* XXX height??? */); +} -///////////////////////////////////////////////////////////////////////////// -// +nsCSSInlineReflowState::~nsCSSInlineReflowState() +{ +} -nsresult nsInlineFrame::NewFrame(nsIFrame** aInstancePtrResult, - nsIContent* aContent, - nsIFrame* aParent) +//---------------------------------------------------------------------- + +nsresult NS_NewCSSInlineFrame(nsIFrame** aInstancePtrResult, + nsIContent* aContent, + nsIFrame* aParent) { NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr"); if (nsnull == aInstancePtrResult) { return NS_ERROR_NULL_POINTER; } - nsIFrame* it = new nsInlineFrame(aContent, aParent); + nsIFrame* it = new nsCSSInlineFrame(aContent, aParent); if (nsnull == it) { return NS_ERROR_OUT_OF_MEMORY; } @@ -151,18 +99,17 @@ nsresult nsInlineFrame::NewFrame(nsIFrame** aInstancePtrResult, return NS_OK; } -nsInlineFrame::nsInlineFrame(nsIContent* aContent, nsIFrame* aParent) - : nsHTMLContainerFrame(aContent, aParent) +nsCSSInlineFrame::nsCSSInlineFrame(nsIContent* aContent, nsIFrame* aParent) + : nsCSSContainerFrame(aContent, aParent) { - NS_PRECONDITION(!IsPseudoFrame(), "can not be a pseudo frame"); } -nsInlineFrame::~nsInlineFrame() +nsCSSInlineFrame::~nsCSSInlineFrame() { } NS_IMETHODIMP -nsInlineFrame::QueryInterface(REFNSIID aIID, void** aInstancePtrResult) +nsCSSInlineFrame::QueryInterface(REFNSIID aIID, void** aInstancePtrResult) { NS_PRECONDITION(nsnull != aInstancePtrResult, "null pointer"); if (nsnull == aInstancePtrResult) { @@ -175,1017 +122,8 @@ nsInlineFrame::QueryInterface(REFNSIID aIID, void** aInstancePtrResult) return nsFrame::QueryInterface(aIID, aInstancePtrResult); } -nsInlineReflowStatus -nsInlineFrame::ReflowChild(nsInlineState& aState, - nsIFrame* aKidFrame, - nsIPresContext* aPresContext, - nsReflowMetrics& aDesiredSize, - const nsReflowState& aReflowState) -{ - NS_PRECONDITION(aReflowState.frame == aKidFrame, "bad reflow state"); -#ifdef NS_DEBUG - nsFrameState kidFrameState; - - aKidFrame->GetFrameState(kidFrameState); - NS_ASSERTION(kidFrameState & NS_FRAME_IN_REFLOW, "kid frame is not in reflow"); -#endif - nsIInlineReflow* iir; - nsresult rv; - if ((nsnull != aState.lineLayout) && - (NS_OK == aKidFrame->QueryInterface(kIInlineReflowIID, (void**)&iir))) { - rv = iir->InlineReflow(*aState.lineLayout, aDesiredSize, aReflowState); - } - else { - aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowState, rv); - rv = NS_FRAME_REFLOW_STATUS_2_INLINE_REFLOW_STATUS(rv); - } - - if (NS_FRAME_IS_COMPLETE(rv)) { - nsIFrame* kidNextInFlow; - - aKidFrame->GetNextInFlow(kidNextInFlow); - if (nsnull != kidNextInFlow) { - // Remove all of the childs next-in-flows. Make sure that we ask - // the right parent to do the removal (it's possible that the - // parent is not this because we are executing pullup code) - nsIFrame* parent; - - aKidFrame->GetGeometricParent(parent); - DeleteNextInFlowsFor((nsContainerFrame*)parent, aKidFrame); - } - } - return rv; -} - -void nsInlineFrame::PlaceChild(nsIFrame* aChild, - PRInt32 aIndex, - nsInlineState& aState, - const nsReflowMetrics& aChildSize, - const nsSize* aChildMaxElementSize) -{ - // Set the child's rect - aChild->SetRect(nsRect(aState.x, aState.y, aChildSize.width, aChildSize.height)); - - // Adjust the running x-offset - aState.x += aChildSize.width; - - // Update the array of ascents and the max ascent and descent - aState.ascents[aIndex] = aChildSize.ascent; - if (aChildSize.ascent > aState.maxAscent) { - aState.maxAscent = aChildSize.ascent; - } - if (aChildSize.descent > aState.maxDescent) { - aState.maxDescent = aChildSize.descent; - } - - // If we're constrained then update the available width - if (!aState.unconstrainedWidth) { - aState.availSize.width -= aChildSize.width; - } - - // Update the maximum element size - if (nsnull != aChildMaxElementSize) { - if (aChildMaxElementSize->width > aState.maxElementSize->width) { - aState.maxElementSize->width = aChildMaxElementSize->width; - } - if (aChildMaxElementSize->height > aState.maxElementSize->height) { - aState.maxElementSize->height = aChildMaxElementSize->height; - } - } -} - -// XXX an image shouldn't be allowed to continue a word but text -// should. hmmm. - -PRBool -nsInlineFrame::CanFitChild(nsIPresContext* aPresContext, - nsInlineState& aState, - nsIFrame* aChildFrame) -{ - PRBool result = PR_TRUE; -#if XXX_fix_me - if (nsnull != aState.lineLayout) { - aState.atBreakPoint = nsnull == aState.lineLayout->mBreakFrame; - if ((aState.availSize.width <= 0) && - (aChildFrame != mFirstChild) && (nsnull != mFirstChild)) { - // If we are at a breakable position then it's ok to be out of - // room. Otherwise the child is allowed to fit. - if (aState.atBreakPoint) { - result = PR_FALSE; - } - } - } - - NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW, - ("nsInlineFrame::CanFitChild: availSize=%d result=%s", - aState.availSize.width, result ? "true" : "false")); -#endif - return result; -} - -// XXX dup first-child check because of calling code not being nice! ick! -PRBool -nsInlineFrame::DidFitChild(nsIPresContext* aPresContext, - nsInlineState& aState, - nsIFrame* aChildFrame, - nsReflowMetrics& aChildMetrics) -{ -#if XXX_fix_me - if ((aChildMetrics.width > aState.availSize.width) && - (aChildFrame != mFirstChild) && (nsnull != mFirstChild)) { - // If we *were* at a breakable position then it's ok to be out of - // room. Otherwise the child is allowed to fit. - if (aState.atBreakPoint) { - return PR_FALSE; - } - } -#endif - return PR_TRUE; -} - -/** - * Reflow the frames we've already created - * - * @param aPresContext presentation context to use - * @param aState current inline state - * @param aChildFrame the first child frame to reflow - * @param aChildIndex the first child frame's index in the frame list - * @return true if we successfully reflowed all the mapped children and false - * otherwise, e.g. we pushed children to the next in flow - */ -PRBool -nsInlineFrame::ReflowMappedChildrenFrom(nsIPresContext* aPresContext, - nsInlineState& aState, - nsIFrame* aChildFrame, - PRInt32 aChildIndex) -{ -#ifdef NS_DEBUG - if (GetVerifyTreeEnable()) { - VerifyLastIsComplete(); - } -#endif - NS_PRECONDITION(nsnull != aChildFrame, "no children"); - - PRInt32 childCount = aChildIndex; - nsIFrame* prevKidFrame = nsnull; - - // Remember our original mLastContentIsComplete so that if we end up - // having to push children, we have the correct value to hand to - // PushChildren. - PRBool originalLastContentIsComplete = mLastContentIsComplete; - - nsSize kidMaxElementSize; - nsSize* pKidMaxElementSize = (nsnull != aState.maxElementSize) ? &kidMaxElementSize : nsnull; - PRBool result = PR_TRUE; - - for (nsIFrame* kidFrame = aChildFrame; nsnull != kidFrame; ) { - // If the previous child just reflowed has indicated a pending - // break, stop reflowing our remaining children. This can only - // happen when a prev-in-flow has pushed some junk into this - // frame. -#if XXX_fix_me - if (nsnull != aState.lineLayout) { - if (NS_STYLE_CLEAR_NONE != aState.lineLayout->mPendingBreak) { - PushChildren(kidFrame, prevKidFrame, originalLastContentIsComplete); - SetLastContentOffset(prevKidFrame); - - result = PR_FALSE; - break; - } - } -#endif - - // If the frame being reflowed is not appropriate (e.g. a block - // frame) then we should stop! This can only happen when a - // prev-in-flow has pushed some junk into this frame. - const nsStyleDisplay* kidDisplay; - kidFrame->GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&)kidDisplay); - if ((NS_STYLE_DISPLAY_BLOCK == kidDisplay->mDisplay) || - (NS_STYLE_DISPLAY_LIST_ITEM == kidDisplay->mDisplay)) { - if (mFirstChild != kidFrame) { - PushChildren(kidFrame, prevKidFrame, originalLastContentIsComplete); - SetLastContentOffset(prevKidFrame); - result = PR_FALSE; - break; - } - } - - nsReflowMetrics kidSize(pKidMaxElementSize); - nsReflowState kidReflowState(kidFrame, aState.reflowState, aState.availSize, - eReflowReason_Resize); - nsReflowStatus status; - - // Reflow the child into the available space - PRBool room = CanFitChild(aPresContext, aState, kidFrame); - if (room) { - kidFrame->WillReflow(*aPresContext); - status = ReflowChild(aState, kidFrame, aPresContext, kidSize, kidReflowState); - } - - // Did the child fit? - if (!room || !DidFitChild(aPresContext, aState, kidFrame, kidSize)) { - // The child is too wide to fit in the available space, and it's - // not our first child - - // Since we are giving the next-in-flow our last child, we - // give it our original mLastContentIsComplete too (in case we - // are pushing into an empty next-in-flow) - PushChildren(kidFrame, prevKidFrame, originalLastContentIsComplete); - SetLastContentOffset(prevKidFrame); - - result = PR_FALSE; - break; - } - - // Place and size the child. We'll deal with vertical alignment when - // we're all done - PlaceChild(kidFrame, childCount, aState, kidSize, pKidMaxElementSize); - childCount++; - - // Remember where we just were in case we end up pushing children - prevKidFrame = kidFrame; - - // Is the child complete? - mLastContentIsComplete = NS_FRAME_IS_COMPLETE(status); - if (NS_FRAME_IS_NOT_COMPLETE(status)) { - // No, the child isn't complete - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - PRBool lastContentIsComplete = mLastContentIsComplete; - if (nsnull == kidNextInFlow) { - // The child doesn't have a next-in-flow so create a continuing - // frame. This hooks the child into the flow - nsIFrame* continuingFrame; - - nsIStyleContextPtr kidSC; - kidFrame->GetStyleContext(aPresContext, kidSC.AssignRef()); - kidFrame->CreateContinuingFrame(aPresContext, this, kidSC, - continuingFrame); - NS_ASSERTION(nsnull != continuingFrame, "frame creation failed"); - - // Add the continuing frame to the sibling list - nsIFrame* nextSib; - - kidFrame->GetNextSibling(nextSib); - continuingFrame->SetNextSibling(nextSib); - kidFrame->SetNextSibling(continuingFrame); - if (nsnull == nextSib) { - // Assume that the continuation frame we just created is - // complete, for now. It will get reflowed by our - // next-in-flow (we are going to push it now) - lastContentIsComplete = PR_TRUE; - } - } - - // We've used up all of our available space so push the remaining - // children to the next-in-flow - nsIFrame* nextSibling; - - kidFrame->GetNextSibling(nextSibling); - if (nsnull != nextSibling) { - PushChildren(nextSibling, kidFrame, lastContentIsComplete); - SetLastContentOffset(prevKidFrame); - } - result = PR_FALSE; - break; - } - - // Get the next child frame - kidFrame->GetNextSibling(kidFrame); - } - - // Update the child count member data - mChildCount = childCount; -#ifdef NS_DEBUG - if (GetVerifyTreeEnable()) { - NS_POSTCONDITION(LengthOf(mFirstChild) == mChildCount, "bad child count"); - - nsIFrame* lastChild; - PRInt32 lastIndexInParent; - - LastChild(lastChild); - lastChild->GetContentIndex(lastIndexInParent); - NS_POSTCONDITION(lastIndexInParent == mLastContentOffset, "bad last content offset"); - VerifyLastIsComplete(); - } -#endif - return result; -} - -/** - * Try and pull-up frames from our next-in-flow - * - * @param aPresContext presentation context to use - * @param aState current inline state - * @return true if we successfully pulled-up all the children and false - * otherwise, e.g. child didn't fit - */ -PRBool nsInlineFrame::PullUpChildren(nsIPresContext* aPresContext, - nsInlineState& aState) -{ - NS_FRAME_LOG(NS_FRAME_TRACE_PUSH_PULL, - ("enter nsInlineFrame::PullUpChildren: [%d,%d,%c] childCount=%d", - mFirstContentOffset, - mLastContentOffset, - mLastContentIsComplete ? 'T' : 'F', - mChildCount)); -#ifdef NS_DEBUG - if (GetVerifyTreeEnable()) { - VerifyLastIsComplete(); - } -#endif - - nsInlineFrame* nextInFlow = (nsInlineFrame*)mNextInFlow; - nsSize kidMaxElementSize; - nsSize* pKidMaxElementSize = (nsnull != aState.maxElementSize) ? &kidMaxElementSize : nsnull; -#ifdef NS_DEBUG - PRInt32 kidIndex = NextChildOffset(); -#endif - nsIFrame* prevKidFrame; - - LastChild(prevKidFrame); - - PRBool result = PR_TRUE; - - while (nsnull != nextInFlow) { - nsReflowMetrics kidSize(pKidMaxElementSize); - nsReflowStatus status; - - // Get the next child - nsIFrame* kidFrame = nextInFlow->mFirstChild; - - // Any more child frames? - if (nsnull == kidFrame) { - // No. Any frames on its overflow list? - if (nsnull != nextInFlow->mOverflowList) { - // Move the overflow list to become the child list - NS_ABORT(); - nextInFlow->AppendChildren(nextInFlow->mOverflowList); - nextInFlow->mOverflowList = nsnull; - kidFrame = nextInFlow->mFirstChild; - } else { - // We've pulled up all the children, so move to the next-in-flow. - nextInFlow = (nsInlineFrame*)nextInFlow->mNextInFlow; - continue; - } - } - - // It's time to stop pulling up if our last child wants a break -#if XXX_fix_me - if (nsnull != aState.lineLayout) { - if (NS_STYLE_CLEAR_NONE != aState.lineLayout->mPendingBreak) { - result = PR_FALSE; - break; - } - } -#endif - - // If the frame being pulled up is not appropriate (e.g. a block - // frame) then we should stop! - const nsStyleDisplay* kidDisplay; - kidFrame->GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&)kidDisplay); - if ((NS_STYLE_DISPLAY_BLOCK == kidDisplay->mDisplay) || - (NS_STYLE_DISPLAY_LIST_ITEM == kidDisplay->mDisplay)) { - result = PR_FALSE; - break; - } - - // If there is no room, stop pulling up - if (!CanFitChild(aPresContext, aState, kidFrame)) { - result = PR_FALSE; - break; - } - -// XXX inline reflow avoidance will be done by nsCSSLineLayout -#if XXX - // See if the child fits in the available space. If it fits or - // it's splittable then reflow it. The reason we can't just move - // it is that we still need ascent/descent information - nsSize kidFrameSize; - nsSplittableType kidIsSplittable; - - kidFrame->GetSize(kidFrameSize); - kidFrame->IsSplittable(kidIsSplittable); - if ((kidFrameSize.width > aState.availSize.width) && - NS_FRAME_IS_NOT_SPLITTABLE(kidIsSplittable)) { - result = PR_FALSE; - break; - } -#endif - - nsReflowState kidReflowState(kidFrame, aState.reflowState, aState.availSize, - eReflowReason_Resize); - kidFrame->WillReflow(*aPresContext); - status = ReflowChild(aState, kidFrame, aPresContext, kidSize, kidReflowState); - - // Did the child fit? - if (!DidFitChild(aPresContext, aState, kidFrame, kidSize)) { - // The child is too wide to fit in the available space, and it's - // not our first child - result = PR_FALSE; - break; - } - - NS_FRAME_LOG(NS_FRAME_TRACE_PUSH_PULL, - ("nsInlineFrame::PullUpChildren: pulled frame=%p [%c][%x]", - kidFrame, - NS_FRAME_IS_COMPLETE(status) ? 'T' : 'F', - status)); - - // Place and size the child. We'll deal with vertical alignment when - // we're all done - PlaceChild(kidFrame, mChildCount, aState, kidSize, pKidMaxElementSize); - - // Remove the frame from its current parent - kidFrame->GetNextSibling(nextInFlow->mFirstChild); - nextInFlow->mChildCount--; - // Update the next-in-flows first content offset - if (nsnull != nextInFlow->mFirstChild) { - nextInFlow->SetFirstContentOffset(nextInFlow->mFirstChild); - } - - // Link the frame into our list of children - kidFrame->SetGeometricParent(this); - nsIFrame* kidContentParent; - - kidFrame->GetContentParent(kidContentParent); - if (nextInFlow == kidContentParent) { - kidFrame->SetContentParent(this); - } - if (nsnull == prevKidFrame) { - mFirstChild = kidFrame; - SetFirstContentOffset(kidFrame); - } else { - prevKidFrame->SetNextSibling(kidFrame); - } - kidFrame->SetNextSibling(nsnull); - mChildCount++; - prevKidFrame = kidFrame; - - // Is the child we just pulled up complete? - mLastContentIsComplete = NS_FRAME_IS_COMPLETE(status); - if (NS_FRAME_IS_NOT_COMPLETE(status)) { - // No the child isn't complete - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - if (nsnull == kidNextInFlow) { - // The child doesn't have a next-in-flow so create a - // continuing frame. The creation appends it to the flow and - // prepares it for reflow. - nsIFrame* continuingFrame; - - nsIStyleContextPtr kidSC; - kidFrame->GetStyleContext(aPresContext, kidSC.AssignRef()); - kidFrame->CreateContinuingFrame(aPresContext, this, kidSC, - continuingFrame); - NS_ASSERTION(nsnull != continuingFrame, "frame creation failed"); - - // Add the continuing frame to our sibling list and then push - // it to the next-in-flow. This ensures the next-in-flow's - // content offsets and child count are set properly. Note that - // we can safely assume that the continuation is complete so - // we pass PR_TRUE into PushChidren in case our next-in-flow - // was just drained and now needs to know it's - // mLastContentIsComplete state. - kidFrame->SetNextSibling(continuingFrame); - - NS_FRAME_LOG(NS_FRAME_TRACE_PUSH_PULL, - ("nsInlineFrame::PullUpChildren: pushing frame=%p", - continuingFrame)); - PushChildren(continuingFrame, kidFrame, PR_TRUE); - - // After we push the continuation frame we don't need to fuss - // with mLastContentIsComplete beause the continuation frame - // is no longer on *our* list. - } - - // If the child isn't complete then it means that we've used up - // all of our available space. - result = PR_FALSE; - break; - } - } - - // Update our last content offset - if (nsnull != prevKidFrame) { - NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child"); - SetLastContentOffset(prevKidFrame); - } - - // We need to make sure the first content offset is correct for any empty - // next-in-flow frames (frames where we pulled up all the child frames) - nextInFlow = (nsInlineFrame*)mNextInFlow; - if ((nsnull != nextInFlow) && (nsnull == nextInFlow->mFirstChild)) { - // We have at least one empty frame. Did we succesfully pull up all the - // child frames? - if (PR_FALSE == result) { - // No, so we need to adjust the first content offset of all the empty - // frames - AdjustOffsetOfEmptyNextInFlows(); -#ifdef NS_DEBUG - } else { - // Yes, we successfully pulled up all the child frames which means all - // the next-in-flows must be empty. Do a sanity check - if (GetVerifyTreeEnable()) { - while (nsnull != nextInFlow) { - NS_ASSERTION(nsnull == nextInFlow->mFirstChild, - "non-empty next-in-flow"); - nextInFlow = (nsInlineFrame*)nextInFlow->mNextInFlow; - } - } -#endif - } - } - -#ifdef NS_DEBUG - if (GetVerifyTreeEnable()) { - VerifyLastIsComplete(); - } -#endif - NS_FRAME_LOG(NS_FRAME_TRACE_PUSH_PULL, - ("exit nsInlineFrame::PullUpChildren: [%d,%d,%c] childCount=%d", - mFirstContentOffset, - mLastContentOffset, - mLastContentIsComplete ? 'T' : 'F', - mChildCount)); - return result; -} - -/** - * Create new frames for content we haven't yet mapped - * - * @param aPresContext presentation context to use - * @param aState current inline state - * @return NS_FRAME_COMPLETE if all content has been mapped and 0 if we - * should be continued - */ -nsReflowStatus -nsInlineFrame::ReflowUnmappedChildren(nsIPresContext* aPresContext, - nsInlineState& aState) -{ -#ifdef NS_DEBUG - if (GetVerifyTreeEnable()) { - VerifyLastIsComplete(); - } -#endif - nsIFrame* kidPrevInFlow = nsnull; - nsReflowStatus result = NS_FRAME_NOT_COMPLETE; - - // If we have no children and we have a prev-in-flow then we need to pick - // up where it left off. If we have children, e.g. we're being resized, then - // our content offset should already be set correctly... - if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) { - nsInlineFrame* prev = (nsInlineFrame*)mPrevInFlow; - NS_ASSERTION(prev->mLastContentOffset >= prev->mFirstContentOffset, - "bad prevInFlow"); - - mFirstContentOffset = prev->NextChildOffset(); - if (!prev->mLastContentIsComplete) { - // Our prev-in-flow's last child is not complete - prev->LastChild(kidPrevInFlow); - } - } - mLastContentIsComplete = PR_TRUE; - - // Place our children, one at a time until we are out of children - nsSize kidMaxElementSize; - nsSize* pKidMaxElementSize = - (nsnull != aState.maxElementSize) ? &kidMaxElementSize : nsnull; - PRInt32 kidIndex = NextChildOffset(); - nsIFrame* prevKidFrame; - - PRBool breakAfter = PR_FALSE; - LastChild(prevKidFrame); - for (;;) { - // Get the next content object - nsIContentPtr kid = mContent->ChildAt(kidIndex); - if (kid.IsNull()) { - result = NS_FRAME_COMPLETE; - break; - } - - // It's time to stop pulling up if our previous child wants a line - // break -#if XXX_fix_me - if (nsnull != aState.lineLayout) { - if (NS_STYLE_CLEAR_NONE != aState.lineLayout->mPendingBreak) { - break; - } - } -#endif - - // Make sure we still have room left - if (!CanFitChild(aPresContext, aState, nsnull)) { - // Note: return status was set to 0 above... - break; - } - - // Resolve style for the child - nsIStyleContextPtr kidStyleContext = - aPresContext->ResolveStyleContextFor(kid, this); - - // Figure out how we should treat the child - nsIFrame* kidFrame; - const nsStyleDisplay* kidDisplay = - (const nsStyleDisplay*)kidStyleContext->GetStyleData(eStyleStruct_Display); - const nsStylePosition* kidPosition = (const nsStylePosition*) - kidStyleContext->GetStyleData(eStyleStruct_Position); - - // Check whether it wants to floated or absolutely positioned - nsresult rv; - if (NS_STYLE_POSITION_ABSOLUTE == kidPosition->mPosition) { - rv = nsAbsoluteFrame::NewFrame(&kidFrame, kid, this); - if (NS_OK == rv) { - kidFrame->SetStyleContext(aPresContext, kidStyleContext); - } - } else if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) { - rv = nsPlaceholderFrame::NewFrame(&kidFrame, kid, this); - if (NS_OK == rv) { - kidFrame->SetStyleContext(aPresContext, kidStyleContext); - } - } else if (nsnull == kidPrevInFlow) { - nsIContentDelegatePtr kidDel; - switch (kidDisplay->mDisplay) { - case NS_STYLE_DISPLAY_BLOCK: - case NS_STYLE_DISPLAY_LIST_ITEM: - if (kidIndex != mFirstContentOffset) { - // We don't allow block elements to be placed in us anywhere - // other than at our left margin. - goto done; - } - // FALLTHROUGH - - case NS_STYLE_DISPLAY_INLINE: - kidDel = kid->GetDelegate(aPresContext); - rv = kidDel->CreateFrame(aPresContext, kid, this, - kidStyleContext, kidFrame); - break; - - default: - NS_ASSERTION(nsnull == kidPrevInFlow, "bad prev in flow"); - rv = nsFrame::NewFrame(&kidFrame, kid, this); - if (NS_OK == rv) { - kidFrame->SetStyleContext(aPresContext, kidStyleContext); - } - break; - } - } else { - rv = kidPrevInFlow->CreateContinuingFrame(aPresContext, this, - kidStyleContext, kidFrame); - } - - // Try to reflow the child into the available space. It might not - // fit or might need continuing. - nsReflowMetrics kidSize(pKidMaxElementSize); - nsReflowState kidReflowState(kidFrame, aState.reflowState, aState.availSize, - eReflowReason_Resize); - kidFrame->WillReflow(*aPresContext); - nsReflowStatus status = ReflowChild(aState, kidFrame, aPresContext, kidSize, - kidReflowState); - - // Did the child fit? - if (!DidFitChild(aPresContext, aState, kidFrame, kidSize)) { - // The child is too wide to fit in the available space, and it's - // not our first child. Add the frame to our overflow list - NS_ASSERTION(nsnull == mOverflowList, "bad overflow list"); - mOverflowList = kidFrame; - prevKidFrame->SetNextSibling(nsnull); - break; - } - - // Place and size the child. We'll deal with vertical alignment when - // we're all done - PlaceChild(kidFrame, mChildCount, aState, kidSize, pKidMaxElementSize); - - // Link child frame into the list of children - if (nsnull != prevKidFrame) { - prevKidFrame->SetNextSibling(kidFrame); - } else { - mFirstChild = kidFrame; // our first child - SetFirstContentOffset(kidFrame); - } - prevKidFrame = kidFrame; - mChildCount++; - kidIndex++; - - // Did the child complete? - if (NS_FRAME_IS_NOT_COMPLETE(status)) { - // If the child isn't complete then it means that we've used up - // all of our available space - mLastContentIsComplete = PR_FALSE; - break; - } - kidPrevInFlow = nsnull; - - // If we need to break after the kidFrame, then do so now - if (breakAfter) { - break; - } - } - -done:; - // Update the content mapping - NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child"); - SetLastContentOffset(prevKidFrame); -#ifdef NS_DEBUG - if (GetVerifyTreeEnable()) { - PRInt32 len = LengthOf(mFirstChild); - NS_ASSERTION(len == mChildCount, "bad child count"); - VerifyLastIsComplete(); - } -#endif - return result; -} - -void -nsInlineFrame::InitializeState(nsIPresContext* aPresContext, - const nsReflowState& aReflowState, - nsInlineState& aState) -{ - PRIntn ss = aState.mStyleSizeFlags = - nsCSSLayout::GetStyleSize(aPresContext, aReflowState, aState.mStyleSize); - if (0 != (ss & NS_SIZE_HAS_WIDTH)) { - // When we are given a width, change the reflow behavior to - // unconstrained. - aState.unconstrainedWidth = PR_TRUE; - aState.availSize.width = NS_UNCONSTRAINEDSIZE; - } -} - -NS_IMETHODIMP -nsInlineFrame::Reflow(nsIPresContext* aPresContext, - nsReflowMetrics& aDesiredSize, - const nsReflowState& aReflowState, - nsReflowStatus& aStatus) -{ - // XXX probably should create a line-layout object and use that - // directly so that some semblance of proper behavior exists - aStatus = Reflow2(aPresContext, nsnull, aDesiredSize, aReflowState); - return NS_OK;/* XXX wrong; fix later */ -} - -NS_IMETHODIMP -nsInlineFrame::FindTextRuns(nsCSSLineLayout& aLineLayout) -{ - return NS_OK; -} - -NS_IMETHODIMP -nsInlineFrame::InlineReflow(nsCSSLineLayout& aLineLayout, - nsReflowMetrics& aDesiredSize, - const nsReflowState& aReflowState) -{ - return Reflow2(aLineLayout.mPresContext, &aLineLayout, aDesiredSize, - aReflowState); -} - -nsresult -nsInlineFrame::Reflow2(nsIPresContext* aPresContext, - nsCSSLineLayout* aLineLayout, - nsReflowMetrics& aDesiredSize, - const nsReflowState& aReflowState) -{ - NS_FRAME_LOG(NS_FRAME_TRACE_CALLS, - ("enter nsInlineFrame::Reflow: this=%p: childCount=%d", - this, mChildCount)); -#ifdef NS_DEBUG - if (GetVerifyTreeEnable()) { - PreReflowCheck(); - } -#endif - - nsresult rv = NS_FRAME_COMPLETE; - - if (eReflowReason_Initial == aReflowState.reason) { - rv = ProcessInitialReflow(aPresContext); - if (NS_OK != rv) { - return rv; - } - } - - PRBool reflowMappedOK = PR_TRUE; - - // Get the style molecule - const nsStyleFont* styleFont = (const nsStyleFont*) - mStyleContext->GetStyleData(eStyleStruct_Font); - const nsStyleSpacing* styleSpacing = (const nsStyleSpacing*) - mStyleContext->GetStyleData(eStyleStruct_Spacing); - const nsStyleText* styleText = (const nsStyleText*) - mStyleContext->GetStyleData(eStyleStruct_Text); - - // Check for an overflow list - if (eReflowReason_Incremental != aReflowState.reason) { - MoveOverflowToChildList(); - } - - // Initialize our reflow state. We must wait until after we've processed - // the overflow list, because our first content offset might change - nsMargin borderPadding; - styleSpacing->CalcBorderPaddingFor(this, borderPadding); - nsInlineState state(aPresContext, this, styleFont, borderPadding, - aReflowState, aDesiredSize.maxElementSize, - aLineLayout); - InitializeState(aPresContext, aReflowState, state); - state.SetNumAscents(mContent->ChildCount() - mFirstContentOffset); - state.mNoWrap = NS_STYLE_WHITESPACE_NORMAL != styleText->mWhiteSpace; - - if (eReflowReason_Incremental == aReflowState.reason) { - NS_ASSERTION(nsnull != aReflowState.reflowCommand, "null reflow command"); - nsIFrame* target; - aReflowState.reflowCommand->GetTarget(target); - - if (target == this) { - nsIReflowCommand::ReflowType type; - aReflowState.reflowCommand->GetType(type); - - switch (type) { - case nsIReflowCommand::FrameAppended: - // Recover our state - RecoverState(aPresContext, state, nsnull); - rv = ReflowUnmappedChildren(aPresContext, state); - break; - - default: - NS_NOTYETIMPLEMENTED("unexpected reflow command"); - break; - } - - } else { - // The command is passing through us. Get the next frame in the reflow chain - nsIFrame* kidFrame; - aReflowState.reflowCommand->GetNext(kidFrame); - - nsReflowMetrics kidSize(aDesiredSize.maxElementSize); - nsReflowState kidReflowState(kidFrame, aReflowState, state.availSize); - - // Restore our state as if nextFrame is the next frame to reflow - PRInt32 kidIndex = RecoverState(aPresContext, state, kidFrame); - - // Reflow the child into the available space - kidFrame->WillReflow(*aPresContext); - rv = ReflowChild(state, kidFrame, aPresContext, kidSize, kidReflowState); - - // Did the child fit? - if (!DidFitChild(aPresContext, state, kidFrame, kidSize)) { - nsIFrame* prevFrame; - - // The child is too wide to fit in the available space, and - // it's not our first child - PrevChild(kidFrame, prevFrame); - PushChildren(kidFrame, prevFrame, mLastContentIsComplete); - SetLastContentOffset(prevFrame); - mChildCount = kidIndex - 1; - rv = NS_FRAME_NOT_COMPLETE; - - } else { - // Place and size the child - PlaceChild(kidFrame, kidIndex, state, kidSize, nsnull); - - nsIFrame* kidNextInFlow; - kidFrame->GetNextInFlow(kidNextInFlow); - - // Is the child complete? - if (NS_FRAME_IS_COMPLETE(rv)) { - // Check whether the frame has next-in-flow(s) that are no longer needed - if (nsnull != kidNextInFlow) { - // Remove the next-in-flow(s) - DeleteChildsNextInFlow(kidFrame); - } - - // Adjust the frames that follow - rv = IncrementalReflowAfter(aPresContext, state, kidFrame, kidIndex); - - } else { - nsIFrame* nextSibling; - - // No, the child isn't complete - if (nsnull == kidNextInFlow) { - // The child doesn't have a next-in-flow so create a continuing - // frame. - nsIFrame* continuingFrame; - - nsIStyleContextPtr kidSC; - kidFrame->GetStyleContext(aPresContext, kidSC.AssignRef()); - kidFrame->CreateContinuingFrame(aPresContext, this, kidSC, continuingFrame); - - // Link the child into the sibling list - kidFrame->GetNextSibling(nextSibling); - continuingFrame->SetNextSibling(nextSibling); - kidFrame->SetNextSibling(continuingFrame); - } - - // We've used up all of our available space, so push the remaining - // children to the next-in-flow - kidFrame->GetNextSibling(nextSibling); - if (nsnull != nextSibling) { - PushChildren(nextSibling, kidFrame, mLastContentIsComplete); - } - - SetLastContentOffset(kidFrame); - mChildCount = kidIndex; - } - } - } - - } else { - // Reflow any existing frames - if (nsnull != mFirstChild) { - reflowMappedOK = ReflowMappedChildrenFrom(aPresContext, state, mFirstChild, 0); - - if (PR_FALSE == reflowMappedOK) { - // We didn't successfully reflow our mapped frames; therefore, we're not complete - rv = NS_FRAME_NOT_COMPLETE; - } - } - - // Did we successfully relow our mapped children? - if (PR_TRUE == reflowMappedOK) { -#if XXX - // Any space left? - if (state.availSize.width <= 0) { - // No space left. Don't try to pull-up children or reflow unmapped - if (NextChildOffset() < mContent->ChildCount()) { - // No room left to map the remaining content; therefore, we're not complete - rv = NS_FRAME_NOT_COMPLETE; - } - } else -#endif - if (NextChildOffset() < mContent->ChildCount()) { - // Try and pull-up some children from a next-in-flow - if (PullUpChildren(aPresContext, state)) { - // If we still have unmapped children then create some new frames - if (NextChildOffset() < mContent->ChildCount()) { - rv = ReflowUnmappedChildren(aPresContext, state); - } - } else { - // We were unable to pull-up all the existing frames from the next in flow; - // therefore, we're not complete - rv = NS_FRAME_NOT_COMPLETE; - } - } - } - -#if 0 - // XXX I don't think our return size properly accounts for the lineHeight - // (which may not == state.maxAscent + state.maxDescent) - // Return our size and our status -#endif - } - - // Vertically align the children - nscoord lineHeight = - nsCSSLayout::VerticallyAlignChildren(aPresContext, this, styleFont, - borderPadding.top, - mFirstChild, mChildCount, - state.ascents, state.maxAscent); - - ComputeFinalSize(aPresContext, state, aDesiredSize); - -#ifdef NS_DEBUG - if (GetVerifyTreeEnable()) { - PostReflowCheck(rv); - } -#endif - NS_FRAME_LOG(NS_FRAME_TRACE_CALLS, - ("exit nsInlineFrame::Reflow: this=%p: childCount=%d status=%scomplete", - this, mChildCount, - NS_FRAME_IS_COMPLETE(rv) ? "" : "not ")); - return NS_OK; -} - -void -nsInlineFrame::ComputeFinalSize(nsIPresContext* aPresContext, - nsInlineState& aState, - nsReflowMetrics& aDesiredSize) -{ - // Compute default size - aDesiredSize.width = aState.x + aState.borderPadding.right; - aDesiredSize.ascent = aState.borderPadding.top + aState.maxAscent; - aDesiredSize.descent = aState.maxDescent + aState.borderPadding.bottom; - aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent; - - // Apply width/height style properties - PRIntn ss = aState.mStyleSizeFlags; - if (0 != (ss & NS_SIZE_HAS_WIDTH)) { - aDesiredSize.width = aState.borderPadding.left + aState.mStyleSize.width + - aState.borderPadding.right; - } - if (0 != (ss & NS_SIZE_HAS_HEIGHT)) { - aDesiredSize.height = aState.borderPadding.top + aState.mStyleSize.height + - aState.borderPadding.bottom; - } - - if ((nsnull != aDesiredSize.maxElementSize) && aState.mNoWrap) { - aDesiredSize.maxElementSize->width = aDesiredSize.width; - aDesiredSize.maxElementSize->height = aDesiredSize.height; - } -} - -///////////////////////////////////////////////////////////////////////////// - -PRIntn nsInlineFrame::GetSkipSides() const +PRIntn +nsCSSInlineFrame::GetSkipSides() const { PRIntn skip = 0; if (nsnull != mPrevInFlow) { @@ -1197,127 +135,431 @@ PRIntn nsInlineFrame::GetSkipSides() const return skip; } -///////////////////////////////////////////////////////////////////////////// - -// Incremental reflow support - -/** - * Setup aState to the state it would have had we just reflowed our - * children up to, but not including, aSkipChild. Return the index - * of aSkipChild in our list of children. - * If aSkipChild is nsnull then resets the state for appended content. - */ -PRInt32 nsInlineFrame::RecoverState(nsIPresContext* aPresContext, - nsInlineState& aState, - nsIFrame* aSkipChild) +NS_IMETHODIMP +nsCSSInlineFrame::FindTextRuns(nsCSSLineLayout& aLineLayout) { - // Get ascent & descent info for all the children up to but not - // including aSkipChild. Also compute the x coordinate for where - // aSkipChild will be place after it is reflowed. - PRInt32 i = 0; - nsIFrame* kid = mFirstChild; - nscoord x = aState.x; - nscoord maxAscent = 0; - nscoord maxDescent = 0; - while ((nsnull != kid) && (kid != aSkipChild)) { - nsReflowMetrics kidMetrics(nsnull); - kid->GetReflowMetrics(aPresContext, kidMetrics); - aState.ascents[i] = kidMetrics.ascent; - if (kidMetrics.ascent > maxAscent) maxAscent = kidMetrics.ascent; - if (kidMetrics.descent > maxDescent) maxDescent = kidMetrics.descent; - - // XXX Factor in left and right margins - x += kidMetrics.width; - - kid->GetNextSibling(kid); - i++; - } - aState.maxAscent = maxAscent; - aState.maxDescent = maxDescent; - aState.x = x; - return i; + // XXX write me + return NS_OK; } -// XXX We need to return information about whether our next-in-flow is -// dirty... -nsReflowStatus -nsInlineFrame::IncrementalReflowFrom(nsIPresContext* aPresContext, - nsInlineState& aState, - nsIFrame* aChildFrame, - PRInt32 aChildIndex) +NS_IMETHODIMP +nsCSSInlineFrame::InlineReflow(nsCSSLineLayout& aLineLayout, + nsReflowMetrics& aMetrics, + const nsReflowState& aReflowState) { - nsReflowStatus status = NS_FRAME_COMPLETE; + // If this is the initial reflow, generate any synthetic content + // that needs generating. + if (eReflowReason_Initial == aReflowState.reason) { + NS_ASSERTION(0 != (NS_FRAME_FIRST_REFLOW & mState), "bad mState"); + nsresult rv = ProcessInitialReflow(aLineLayout.mPresContext); + if (NS_OK != rv) { + return rv; + } + } + else { + NS_ASSERTION(0 == (NS_FRAME_FIRST_REFLOW & mState), "bad mState"); + } - // Just reflow all the mapped children starting with childFrame. - // XXX This isn't the optimal thing to do... - if (ReflowMappedChildrenFrom(aPresContext, aState, aChildFrame, aChildIndex)) { - if (NextChildOffset() < mContent->ChildCount()) { -#if XXX - // Any space left? - if (aState.availSize.width <= 0) { - // No space left. Don't try to pull-up children - status = NS_FRAME_NOT_COMPLETE; - } else -#endif - { - // Try and pull-up some children from a next-in-flow - if (!PullUpChildren(aPresContext, aState)) { - // We were not able to pull-up all the child frames from our - // next-in-flow - status = NS_FRAME_NOT_COMPLETE; - } + if (eReflowReason_Incremental != aReflowState.reason) { + MoveOverflowToChildList(); + } + + nsCSSInlineReflowState state(aLineLayout, this, mStyleContext, + aReflowState, aMetrics.maxElementSize); + nsresult rv = NS_OK; + if (eReflowReason_Initial == state.reason) { + rv = FrameAppendedReflow(state); + } + else if (eReflowReason_Incremental == state.reason) { + nsIFrame* target; + state.reflowCommand->GetTarget(target); + if (this == target) { + nsIReflowCommand::ReflowType type; + state.reflowCommand->GetType(type); + switch (type) { + case nsIReflowCommand::FrameAppended: + //XXX RecoverState(state); + rv = FrameAppendedReflow(state); + break; + + default: + NS_NOTYETIMPLEMENTED("XXX"); } } - } else { - // We were unable to reflow all our mapped frames - status = NS_FRAME_NOT_COMPLETE; - } - - return status; -} - -nsReflowStatus -nsInlineFrame::IncrementalReflowAfter(nsIPresContext* aPresContext, - nsInlineState& aState, - nsIFrame* aChildFrame, - PRInt32 aChildIndex) -{ - nsReflowStatus status = NS_FRAME_COMPLETE; - nsIFrame* nextFrame; - - aChildFrame->GetNextSibling(nextFrame); - - // Just reflow all the remaining mapped children - // XXX This isn't the optimal thing to do... - if ((nsnull == nextFrame) || - ReflowMappedChildrenFrom(aPresContext, aState, nextFrame, aChildIndex + 1)) { - if (NextChildOffset() < mContent->ChildCount()) { -#if XXX - // Any space left? - if (aState.availSize.width <= 0) { - // No space left. Don't try to pull-up children - status = NS_FRAME_NOT_COMPLETE; - } else -#endif - { - // Try and pull-up some children from a next-in-flow - if (!PullUpChildren(aPresContext, aState)) { - // We were not able to pull-up all the child frames from our - // next-in-flow - status = NS_FRAME_NOT_COMPLETE; - } - } + else { + rv = ChildIncrementalReflow(state); } - } else { - // We were unable to reflow all our mapped frames - status = NS_FRAME_NOT_COMPLETE; } + else if (eReflowReason_Resize == state.reason) { + rv = ResizeReflow(state); + } + ComputeFinalSize(state, aMetrics); - return status; +#ifdef NS_DEBUG + NS_ASSERTION(LengthOf(mFirstChild) == mChildCount, "bad child count"); +#endif + return rv; } -// In order to execute the vertical alignment code after incremental -// reflow of the inline frame, we need to reposition any child frames -// that were relatively positioned back to their computed x origin. -// This should probably be done as a pre-alignment computation (and it -// can be avoided if there are no relatively positioned children). +void +nsCSSInlineFrame::ComputeFinalSize(nsCSSInlineReflowState& aState, + nsReflowMetrics& aMetrics) +{ + // Compute default size + nsRect bounds; + nscoord lineHeight = + aState.mInlineLayout.AlignFrames(mFirstChild, mChildCount, bounds); + + aMetrics.width = aState.mBorderPadding.left + + (aState.mInlineLayout.mX - aState.mInlineLayout.mLeftEdge) + + aState.mBorderPadding.right; + aMetrics.ascent = aState.mBorderPadding.top + + aState.mInlineLayout.mMaxAscent; + aMetrics.descent = aState.mInlineLayout.mMaxDescent + + aState.mBorderPadding.bottom; + aMetrics.height = aMetrics.ascent + aMetrics.descent; + + // Apply width/height style properties + PRIntn ss = aState.mStyleSizeFlags; + if (0 != (ss & NS_SIZE_HAS_WIDTH)) { + aMetrics.width = aState.mBorderPadding.left + aState.mStyleSize.width + + aState.mBorderPadding.right; + } + if (0 != (ss & NS_SIZE_HAS_HEIGHT)) { + aMetrics.height = aState.mBorderPadding.top + aState.mStyleSize.height + + aState.mBorderPadding.bottom; + } + + if ((nsnull != aMetrics.maxElementSize) && aState.mNoWrap) { + aMetrics.maxElementSize->width = aMetrics.width; + aMetrics.maxElementSize->height = aMetrics.height; + } +} + +nsInlineReflowStatus +nsCSSInlineFrame::FrameAppendedReflow(nsCSSInlineReflowState& aState) +{ + // XXX Reflow existing children to recover our reflow state + nsInlineReflowStatus rv = NS_INLINE_REFLOW_COMPLETE; + if (0 != mChildCount) { + rv = ReflowMapped(aState); + if (IS_REFLOW_ERROR(rv)) { + return rv; + } + } + if (NS_INLINE_REFLOW_COMPLETE == (NS_INLINE_REFLOW_REFLOW_MASK & rv)) { + // Reflow new children + rv = ReflowUnmapped(aState); + // Note: There is nothing to pullup because this is a + // frame-appended reflow (therefore we are the last-in-flow) + } + return rv; +} + +nsInlineReflowStatus +nsCSSInlineFrame::ChildIncrementalReflow(nsCSSInlineReflowState& aState) +{ + // XXX we can do better SOMEDAY + return ResizeReflow(aState); +} + +nsInlineReflowStatus +nsCSSInlineFrame::ResizeReflow(nsCSSInlineReflowState& aState) +{ + nsInlineReflowStatus rv = NS_INLINE_REFLOW_COMPLETE; + if (0 != mChildCount) { + rv = ReflowMapped(aState); + if (IS_REFLOW_ERROR(rv)) { + return rv; + } + } + if (NS_INLINE_REFLOW_COMPLETE == (NS_INLINE_REFLOW_REFLOW_MASK & rv)) { + // Try to fit some more children from our next in flow + if (nsnull != mNextInFlow) { + rv = PullUpChildren(aState); + } + } + return rv; +} + +// XXX: this code is lazy about setting mLastContentIsComplete; it +// can be made more efficient after very careful thought; the initial +// value for mLastContentIsComplete upon entry is undefined (both +// values are legal). + +nsInlineReflowStatus +nsCSSInlineFrame::ReflowMapped(nsCSSInlineReflowState& aState) +{ + nsresult rv = NS_INLINE_REFLOW_NOT_COMPLETE; + PRInt32 n = mChildCount; + PRInt32 kidContentIndex = mFirstContentOffset; + nsIFrame* prevChild = nsnull; + nsIFrame* child = mFirstChild; + while (--n >= 0) { + nsInlineReflowStatus rs = aState.mInlineLayout.ReflowAndPlaceFrame(child); + if (IS_REFLOW_ERROR(rs)) { + rv = nsresult(rs); + goto done; + } + switch (NS_INLINE_REFLOW_REFLOW_MASK & rs) { + case NS_INLINE_REFLOW_COMPLETE: + prevChild = child; + child->GetNextSibling(child); + kidContentIndex++; + mLastContentIsComplete = PR_TRUE; + break; + + case NS_INLINE_REFLOW_NOT_COMPLETE: + prevChild = child; + child->GetNextSibling(child); + PushKids(aState, prevChild, child); + mLastContentOffset = kidContentIndex; + mLastContentIsComplete = PR_FALSE; + goto done; + + case NS_INLINE_REFLOW_BREAK_AFTER: + mLastContentOffset = kidContentIndex; + mLastContentIsComplete = PR_TRUE; + + // We might have children following this breaking child (for + // example, somebody inserted an HTML BR in the middle of + // us). If we do have more children they need to be pushed to a + // next-in-flow. + if (n != 0) { + prevChild = child; + child->GetNextSibling(child); + PushKids(aState, prevChild, child); + } + else { + rv = NS_INLINE_REFLOW_COMPLETE; + } + goto done; + + case NS_INLINE_REFLOW_BREAK_BEFORE: + mLastContentIsComplete = PR_TRUE; + if (nsnull != prevChild) { + mLastContentOffset = kidContentIndex - 1; + PushKids(aState, prevChild, child); + } + else { + // We are trying to push our only child. It must be a block + // that must be at the start of a line and we must not be at + // the start of a line. + NS_NOTYETIMPLEMENTED("XXX"); + } + goto done; + } + } + NS_ASSERTION(kidContentIndex > 0, "bad kid content index"); + mLastContentOffset = kidContentIndex - 1; + mLastContentIsComplete = PR_TRUE; + aState.mKidContentIndex = kidContentIndex; + aState.mLastChild = prevChild; + rv = NS_INLINE_REFLOW_COMPLETE; + +done:; + return rv; +} + +// XXX: this code is lazy about setting mLastContentIsComplete; it +// can be made more efficient after very careful thought; the initial +// value for mLastContentIsComplete upon entry is undefined (both +// values are legal). + +nsInlineReflowStatus +nsCSSInlineFrame::ReflowUnmapped(nsCSSInlineReflowState& aState) +{ + NS_PRECONDITION(mLastContentIsComplete == PR_TRUE, "huh?"); + + nsresult rv = NS_INLINE_REFLOW_NOT_COMPLETE; + PRInt32 kidContentIndex = aState.mKidContentIndex; + + // Get the childPrevInFlow for our eventual first child if we are a + // continuation and we have no children and the last child in our + // prev-in-flow is incomplete. + nsIFrame* childPrevInFlow = nsnull; + if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) { + nsCSSInlineFrame* prev = (nsCSSInlineFrame*)mPrevInFlow; + NS_ASSERTION(prev->mLastContentOffset >= prev->mFirstContentOffset, + "bad prevInFlow"); + + kidContentIndex = prev->NextChildOffset(); + if (!prev->mLastContentIsComplete) { + // Our prev-in-flow's last child is not complete + prev->LastChild(childPrevInFlow); + } + } + + nsIFrame* prevChild = aState.mLastChild; + PRInt32 lastContentIndex; + lastContentIndex = mContent->ChildCount(); + while (kidContentIndex < lastContentIndex) { + nsIContent* kid; + kid = mContent->ChildAt(kidContentIndex); + if (nsnull == kid) { + // Our content container is bad + break; + } + + // Create child + nsIFrame* child; + rv = nsHTMLBase::CreateFrame(aState.mPresContext, this, kid, + childPrevInFlow, child); + NS_RELEASE(kid); + if (NS_OK != rv) { + return rv; + } + if (nsnull == prevChild) { + mFirstChild = child; + mFirstContentOffset = kidContentIndex; + } + else { + prevChild->SetNextSibling(child); + } + mChildCount++; + + // Reflow the child + nsInlineReflowStatus rs = aState.mInlineLayout.ReflowAndPlaceFrame(child); + if (IS_REFLOW_ERROR(rs)) { + rv = nsresult(rs); + goto done; + } + switch (NS_INLINE_REFLOW_REFLOW_MASK & rs) { + case NS_INLINE_REFLOW_COMPLETE: + prevChild = child; + childPrevInFlow = nsnull; + kidContentIndex++; + mLastContentIsComplete = PR_TRUE; + break; + + case NS_INLINE_REFLOW_NOT_COMPLETE: + mLastContentOffset = kidContentIndex; + mLastContentIsComplete = PR_FALSE; + goto done; + + case NS_INLINE_REFLOW_BREAK_AFTER: + mLastContentOffset = kidContentIndex; + mLastContentIsComplete = PR_TRUE; + if (++kidContentIndex == lastContentIndex) { + rv = NS_INLINE_REFLOW_COMPLETE; + } + goto done; + + case NS_INLINE_REFLOW_BREAK_BEFORE: + // We created the child but it turns out we can't keep it + mLastContentIsComplete = PR_TRUE; + if (nsnull != prevChild) { + mLastContentOffset = kidContentIndex - 1; + PushKids(aState, prevChild, child); + } + else { + // We are trying to push our only child. It must be a block + // that must be at the start of a line and we must not be at + // the start of a line. + NS_NOTYETIMPLEMENTED("XXX"); + } + goto done; + } + } + NS_ASSERTION(kidContentIndex > 0, "bad kid content index"); + mLastContentOffset = kidContentIndex - 1; + mLastContentIsComplete = PR_TRUE; + rv = NS_INLINE_REFLOW_COMPLETE; + +done:; + return rv; +} + +// XXX: this code is lazy about setting mLastContentIsComplete; it +// can be made more efficient after very careful thought; the initial +// value for mLastContentIsComplete upon entry is undefined (both +// values are legal). + +nsInlineReflowStatus +nsCSSInlineFrame::PullUpChildren(nsCSSInlineReflowState& aState) +{ + nsresult rv = NS_INLINE_REFLOW_NOT_COMPLETE; + PRInt32 kidContentIndex = aState.mKidContentIndex; + PRInt32 lastContentIndex; + lastContentIndex = mContent->ChildCount(); + nsCSSInlineFrame* nextInFlow = (nsCSSInlineFrame*) mNextInFlow; + nsIFrame* prevChild = aState.mLastChild; + while (nsnull != nextInFlow) { + // Get child from our next-in-flow + nsIFrame* child = PullUpOneChild(nextInFlow, prevChild); + if (nsnull == child) { + nextInFlow = (nsCSSInlineFrame*) nextInFlow->mNextInFlow; + continue; + } + + // Reflow it + nsInlineReflowStatus rs = aState.mInlineLayout.ReflowAndPlaceFrame(child); + if (IS_REFLOW_ERROR(rs)) { + rv = nsresult(rs); + goto done; + } + switch (NS_INLINE_REFLOW_REFLOW_MASK & rs) { + case NS_INLINE_REFLOW_COMPLETE: + prevChild = child; + child->GetNextSibling(child); + kidContentIndex++; + mLastContentIsComplete = PR_TRUE; + break; + + case NS_INLINE_REFLOW_NOT_COMPLETE: + mLastContentOffset = kidContentIndex; + mLastContentIsComplete = PR_FALSE; + goto done; + + case NS_INLINE_REFLOW_BREAK_AFTER: + mLastContentOffset = kidContentIndex; + mLastContentIsComplete = PR_TRUE; + if (kidContentIndex == lastContentIndex) { + // All of the children have been pulled up + rv = NS_INLINE_REFLOW_COMPLETE; + } + goto done; + + case NS_INLINE_REFLOW_BREAK_BEFORE: + mLastContentIsComplete = PR_TRUE; + if (nsnull != prevChild) { + mLastContentOffset = kidContentIndex - 1; + PushKids(aState, prevChild, child); + } + else { + // We are trying to push our only child. It must be a block + // that must be at the start of a line and we must not be at + // the start of a line. + NS_NOTYETIMPLEMENTED("XXX"); + } + goto done; + } + } + mLastContentIsComplete = PR_TRUE; + rv = NS_INLINE_REFLOW_COMPLETE; + +done:; + return rv; +} + +void +nsCSSInlineFrame::PushKids(nsCSSInlineReflowState& aState, + nsIFrame* aPrevChild, nsIFrame* aPushedChild) +{ + // Count how many children are being pushed + PRInt32 pushCount = 0; + nsIFrame* child = aPushedChild; + while (nsnull != child) { + child->GetNextSibling(child); + pushCount++; + } + NS_ASSERTION(pushCount == mChildCount - aState.mInlineLayout.mFrameNum, + "whoops"); + + // Only ask container base class to push children if there is + // something to push... + if (0 != pushCount) { + PushChildren(aPushedChild, aPrevChild, PR_TRUE/* XXX ignored! */); + mChildCount -= pushCount; + } +} diff --git a/layout/css/layout/src/nsCSSInlineFrame.h b/layout/css/layout/src/nsCSSInlineFrame.h index 2a5484f5e7f..1b6e27ac18a 100644 --- a/layout/css/layout/src/nsCSSInlineFrame.h +++ b/layout/css/layout/src/nsCSSInlineFrame.h @@ -15,28 +15,61 @@ * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ -#ifndef nsInlineFrame_h___ -#define nsInlineFrame_h___ +#ifndef nsCSSInlineFrame_h___ +#define nsCSSInlineFrame_h___ -#include "nsHTMLContainerFrame.h" +#include "nsCSSContainerFrame.h" +#include "nsCSSInlineLayout.h" #include "nsCSSLineLayout.h" -class nsInlineState; -// Inline container class. Does not support being used as a pseudo frame -class nsInlineFrame : public nsHTMLContainerFrame, public nsIInlineReflow { +class nsCSSInlineFrame; + +/** + * Reflow state object for managing css inline layout. Most of the state + * is managed by the nsCSSInlineLayout object. + */ +struct nsCSSInlineReflowState : public nsReflowState { + nsCSSInlineReflowState(nsCSSLineLayout& aLineLayout, + nsCSSInlineFrame* aInlineFrame, + nsIStyleContext* aInlineSC, + const nsReflowState& aReflowState, + nsSize* aMaxElementSize); + ~nsCSSInlineReflowState(); + + nsIPresContext* mPresContext; + nsCSSInlineLayout mInlineLayout; + + nsIFrame* mLastChild; // last child we have reflowed (so far) + + PRInt32 mKidContentIndex; // content index of last child reflowed + + nsMargin mBorderPadding; + PRBool mNoWrap; + PRIntn mStyleSizeFlags; + nsSize mStyleSize; +}; + +//---------------------------------------------------------------------- + +/** + * CSS "inline" layout class. + * + * Note: This class does not support being used as a pseudo frame. + */ +class nsCSSInlineFrame : public nsCSSContainerFrame, + public nsIInlineReflow +{ public: - static nsresult NewFrame(nsIFrame** aInstancePtrResult, - nsIContent* aContent, - nsIFrame* aParent); - // nsISupports NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr); // nsIFrame +#if XXX_not_yet NS_IMETHOD Reflow(nsIPresContext* aPresContext, nsReflowMetrics& aDesiredSize, const nsReflowState& aReflowState, nsReflowStatus& aStatus); +#endif // nsIInlineReflow NS_IMETHOD FindTextRuns(nsCSSLineLayout& aLineLayout); @@ -45,70 +78,38 @@ public: const nsReflowState& aReflowState); protected: - nsInlineFrame(nsIContent* aContent, nsIFrame* aParent); + nsCSSInlineFrame(nsIContent* aContent, nsIFrame* aParent); - virtual ~nsInlineFrame(); + virtual ~nsCSSInlineFrame(); virtual PRIntn GetSkipSides() const; - void InitializeState(nsIPresContext* aPresContext, - const nsReflowState& aReflowState, - nsInlineState& aState); + nsresult FrameAppendedReflow(nsCSSInlineReflowState& aState); - PRBool DidFitChild(nsIPresContext* aPresContext, - nsInlineState& aState, - nsIFrame* aChildFrame, - nsReflowMetrics& aChildMetrics); + nsresult ChildIncrementalReflow(nsCSSInlineReflowState& aState); - PRBool CanFitChild(nsIPresContext* aPresContext, - nsInlineState& aState, - nsIFrame* aChildFrame); + nsresult ResizeReflow(nsCSSInlineReflowState& aState); - void ComputeFinalSize(nsIPresContext* aPresContext, - nsInlineState& aState, - nsReflowMetrics& aSize); + void ComputeFinalSize(nsCSSInlineReflowState& aState, + nsReflowMetrics& aMetrics); - PRBool ReflowMappedChildrenFrom(nsIPresContext* aPresContext, - nsInlineState& aState, - nsIFrame* aChildFrame, - PRInt32 aChildIndex); + nsInlineReflowStatus ReflowMapped(nsCSSInlineReflowState& aState); - PRBool PullUpChildren(nsIPresContext* aPresContext, - nsInlineState& aState); + nsInlineReflowStatus ReflowUnmapped(nsCSSInlineReflowState& aState); - nsReflowStatus ReflowUnmappedChildren(nsIPresContext* aPresContext, - nsInlineState& aState); + nsInlineReflowStatus PullUpChildren(nsCSSInlineReflowState& aState); - void PlaceChild(nsIFrame* aChild, - PRInt32 aIndex, // in the child frame list - nsInlineState& aState, - const nsReflowMetrics& aChildSize, - const nsSize* aChildMaxElementSize); + void PushKids(nsCSSInlineReflowState& aState, + nsIFrame* aPrevChild, nsIFrame* aPushedChild); - PRInt32 RecoverState(nsIPresContext* aCX, - nsInlineState& aState, - nsIFrame* aSkipChild); - - nsresult Reflow2(nsIPresContext* aPresContext, - nsCSSLineLayout* aLineLayout, - nsReflowMetrics& aDesiredSize, - const nsReflowState& aReflowState); - - nsReflowStatus IncrementalReflowFrom(nsIPresContext* aPresContext, - nsInlineState& aState, - nsIFrame* aChildFrame, - PRInt32 aChildIndex); - - nsReflowStatus IncrementalReflowAfter(nsIPresContext* aPresContext, - nsInlineState& aState, - nsIFrame* aChildFrame, - PRInt32 aChildIndex); - - nsInlineReflowStatus ReflowChild(nsInlineState& aState, - nsIFrame* aKidFrame, - nsIPresContext* aPresContext, - nsReflowMetrics& aDesiredSize, - const nsReflowState& aReflowState); + friend nsresult NS_NewCSSInlineFrame(nsIFrame** aInstancePtrResult, + nsIContent* aContent, + nsIFrame* aParent); }; -#endif /* nsInlineFrame_h___ */ +extern nsresult NS_NewCSSInlineFrame(nsIFrame** aInstancePtrResult, + nsIContent* aContent, + nsIFrame* aParent); + + +#endif /* nsCSSInlineFrame_h___ */ diff --git a/layout/css/layout/src/nsCSSInlineLayout.cpp b/layout/css/layout/src/nsCSSInlineLayout.cpp new file mode 100644 index 00000000000..f620a23ae08 --- /dev/null +++ b/layout/css/layout/src/nsCSSInlineLayout.cpp @@ -0,0 +1,422 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ +#include "nsCSSInlineLayout.h" +#include "nsCSSLineLayout.h" +#include "nsCSSLayout.h" +#include "nsHTMLIIDs.h" +#include "nsCSSContainerFrame.h" + +#include "nsIFontMetrics.h" +#include "nsIStyleContext.h" +#include "nsIPresContext.h" +#include "nsIRunaround.h" + +nsCSSInlineLayout::nsCSSInlineLayout(nsCSSLineLayout& aLineLayout, + nsIFrame* aContainerFrame, + nsIStyleContext* aContainerStyle) + : mLineLayout(aLineLayout) +{ + mContainerFrame = aContainerFrame; + mAscents = mAscentBuf; + mMaxAscents = sizeof(mAscentBuf) / sizeof(mAscentBuf[0]); + mMaxElementSize = nsnull; + + mContainerFont = (const nsStyleFont*) + aContainerStyle->GetStyleData(eStyleStruct_Font); + mContainerText = (const nsStyleText*) + aContainerStyle->GetStyleData(eStyleStruct_Text); + mContainerDisplay = (const nsStyleDisplay*) + aContainerStyle->GetStyleData(eStyleStruct_Display); + mDirection = mContainerDisplay->mDirection; +} + +nsCSSInlineLayout::~nsCSSInlineLayout() +{ + if (mAscents != mAscentBuf) { + delete [] mAscents; + } +} + +void +nsCSSInlineLayout::Init(const nsReflowState* aContainerReflowState) +{ + mContainerReflowState = aContainerReflowState; +} + +nsresult +nsCSSInlineLayout::SetAscent(nscoord aAscent) +{ + PRInt32 frameNum = mFrameNum; + if (frameNum == mMaxAscents) { + mMaxAscents *= 2; + nscoord* newAscents = new nscoord[mMaxAscents]; + if (nsnull == newAscents) { + return NS_ERROR_OUT_OF_MEMORY; + } + nsCRT::memcpy(newAscents, mAscents, sizeof(nscoord) * frameNum); + if (mAscents != mAscentBuf) { + delete [] mAscents; + } + mAscents = newAscents; + } + mAscents[frameNum] = aAscent; + return NS_OK; +} + +void +nsCSSInlineLayout::Prepare(PRBool aUnconstrainedWidth, PRBool aNoWrap, + nsSize* aMaxElementSize) +{ + mFrameNum = 0; + mUnconstrainedWidth = aUnconstrainedWidth; + mNoWrap = aNoWrap; + mMaxElementSize = aMaxElementSize; + mMaxAscent = 0; + mMaxDescent = 0; +} + +void +nsCSSInlineLayout::SetReflowSpace(nscoord aX, nscoord aY, + nscoord aAvailWidth, nscoord aAvailHeight) +{ + mAvailWidth = aAvailWidth; + mAvailHeight = aAvailHeight; + mX = aX; + mY = aY; + mLeftEdge = aX; + mRightEdge = aX + aAvailWidth; +} + +//XXX block children of inline frames needs handling *here* + +nsInlineReflowStatus +nsCSSInlineLayout::ReflowAndPlaceFrame(nsIFrame* aFrame) +{ + // Compute the maximum size of the frame. If there is no room at all + // for it, then trigger a line-break before the frame. + nsSize maxSize; + nsMargin margin; + if (!ComputeMaxSize(aFrame, margin, maxSize)) { + return NS_INLINE_REFLOW_LINE_BREAK_BEFORE; + } + + // Get reflow reason set correctly. It's possible that we created a + // child and then decided that we cannot reflow it (for example, a + // block frame that isn't at the start of a line). In this case the + // reason will be wrong so we need to check the frame state. + nsReflowReason reason = eReflowReason_Resize; + if (nsnull != mContainerReflowState->reflowCommand) { + reason = eReflowReason_Incremental; + } + else { + nsFrameState state; + aFrame->GetFrameState(state); + if (NS_FRAME_FIRST_REFLOW & state) { + reason = eReflowReason_Initial; + } + } + + // Setup reflow state for reflowing the frame + nsReflowState reflowState(aFrame, *mContainerReflowState, maxSize, reason); + nsInlineReflowStatus rs; + nsReflowMetrics metrics(mMaxElementSize); + PRBool isAware; + aFrame->WillReflow(*mLineLayout.mPresContext); + rs = ReflowFrame(aFrame, metrics, reflowState, isAware); + if (IS_REFLOW_ERROR(rs)) { + return rs; + } + if (NS_INLINE_REFLOW_BREAK_BEFORE == (rs & NS_INLINE_REFLOW_REFLOW_MASK)) { + return rs; + } + + // It's possible the frame didn't fit + if (metrics.width > maxSize.width) { + if (!IsFirstChild()) { + // We are out of room. + // XXX mKidPrevInFlow + NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW, + ("LineLayout::ReflowChild: !fit size=%d,%d", + metrics.width, metrics.height)); + return NS_INLINE_REFLOW_LINE_BREAK_BEFORE; + } + } + + nsRect frameRect(mX, mY, metrics.width, metrics.height); + return PlaceFrame(aFrame, frameRect, metrics, margin, rs); +} + +// XXX RTL +PRBool +nsCSSInlineLayout::IsFirstChild() +{ + return 0 == mFrameNum; +} + +PRBool +nsCSSInlineLayout::ComputeMaxSize(nsIFrame* aFrame, + nsMargin& aKidMargin, + nsSize& aResult) +{ + const nsStyleSpacing* kidSpacing; + aFrame->GetStyleData(eStyleStruct_Spacing, + (const nsStyleStruct*&)kidSpacing); + kidSpacing->CalcMarginFor(aFrame, aKidMargin); + if (mUnconstrainedWidth || mNoWrap) { + aResult.width = NS_UNCONSTRAINEDSIZE; + } + else { + aResult.width = mRightEdge - mX; + aResult.width -= aKidMargin.left + aKidMargin.right; + if (!IsFirstChild() && (aResult.width <= 0)) { + // XXX Make sure child is dirty for next time + aFrame->WillReflow(*mLineLayout.mPresContext); + NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW, + ("CSSLineLayout::ComputeMaxSize: !fit")); + return PR_FALSE; + } + } + aResult.height = mAvailHeight; + return PR_TRUE; +} + +nsInlineReflowStatus +nsCSSInlineLayout::ReflowFrame(nsIFrame* aKidFrame, + nsReflowMetrics& aMetrics, + const nsReflowState& aReflowState, + PRBool& aInlineAware) +{ + // There are 3 ways to reflow the child frame: using the nsIRunaround + // interface, using the nsIInlineReflow interface or using the default + // Reflow method in nsIFrame. The order of precedence is nsIRunaround, + // nsIInlineReflow, nsIFrame. For all three API's we map the reflow status + // into an nsInlineReflowStatus. + + nsresult rv; + nsIRunaround* runAround; + nsIInlineReflow* inlineReflow; + if ((nsnull != mLineLayout.mSpaceManager) && + (NS_OK == aKidFrame->QueryInterface(kIRunaroundIID, + (void**)&runAround))) { + nsRect r; + runAround->Reflow(mLineLayout.mPresContext, mLineLayout.mSpaceManager, + aMetrics, aReflowState, r, rv); + aMetrics.width = r.width; + aMetrics.height = r.height; + aMetrics.ascent = r.height; + aMetrics.descent = 0; + rv = NS_FRAME_REFLOW_STATUS_2_INLINE_REFLOW_STATUS(rv); + aInlineAware = PR_FALSE; + } + else if (NS_OK == aKidFrame->QueryInterface(kIInlineReflowIID, + (void**)&inlineReflow)) { + rv = inlineReflow->InlineReflow(mLineLayout, aMetrics, aReflowState); + aInlineAware = PR_TRUE; + } + else { + aKidFrame->Reflow(mLineLayout.mPresContext, aMetrics, aReflowState, rv); + rv = NS_FRAME_REFLOW_STATUS_2_INLINE_REFLOW_STATUS(rv); + aInlineAware = PR_FALSE; + } + + if (NS_FRAME_IS_COMPLETE(rv)) { + nsIFrame* kidNextInFlow; + aKidFrame->GetNextInFlow(kidNextInFlow); + if (nsnull != kidNextInFlow) { + // Remove all of the childs next-in-flows. Make sure that we ask + // the right parent to do the removal (it's possible that the + // parent is not this because we are executing pullup code) + nsCSSContainerFrame* parent; + aKidFrame->GetGeometricParent((nsIFrame*&)parent); + parent->DeleteChildsNextInFlow(aKidFrame); + } + } + + return rv; +} + +nsInlineReflowStatus +nsCSSInlineLayout::PlaceFrame(nsIFrame* aFrame, + nsRect& aFrameRect, + const nsReflowMetrics& aFrameMetrics, + const nsMargin& aFrameMargin, + nsInlineReflowStatus aFrameReflowStatus) +{ + nscoord horizontalMargins = 0; + + // Special case to position outside list bullets. + // XXX RTL bullets + PRBool isBullet = PR_FALSE; + if (mLineLayout.mListPositionOutside) { + PRBool isFirstChild = IsFirstChild(); + if (isFirstChild && (0 == mLineLayout.mLineNumber)) { + nsIFrame* containerPrevInFlow; + mContainerFrame->GetPrevInFlow(containerPrevInFlow); + if (nsnull == containerPrevInFlow) { + isBullet = PR_TRUE; + // We are placing the first child of the container and we have + // list-style-position of "outside" therefore this is the + // bullet that is being reflowed. The bullet is placed in the + // padding area of this block. Don't worry about getting the Y + // coordinate of the bullet right (vertical alignment will + // take care of that). + + // Compute gap between bullet and inner rect left edge + nsIFontMetrics* fm = + mLineLayout.mPresContext->GetMetricsFor(mContainerFont->mFont); + nscoord kidAscent = fm->GetMaxAscent(); + nscoord dx = fm->GetHeight() / 2; // from old layout engine + NS_RELEASE(fm); + + // XXX RTL bullets + aFrameRect.x = mX - aFrameRect.width - dx; + aFrame->SetRect(aFrameRect); + } + } + } + if (!isBullet) { + // Place normal in-flow child + aFrame->SetRect(aFrameRect); + + // XXX RTL + // Advance + const nsStyleDisplay* frameDisplay; + aFrame->GetStyleData(eStyleStruct_Display, + (const nsStyleStruct*&) frameDisplay); + switch (frameDisplay->mFloats) { + default: + NS_NOTYETIMPLEMENTED("Unsupported floater type"); + // FALL THROUGH + + case NS_STYLE_FLOAT_LEFT: + case NS_STYLE_FLOAT_RIGHT: + // When something is floated, it's margin's are applied there + // not here. + break; + + case NS_STYLE_FLOAT_NONE: + horizontalMargins = aFrameMargin.left + aFrameMargin.right; + break; + } + nscoord totalWidth = aFrameMetrics.width + horizontalMargins; + mX += totalWidth; + } + + NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW, + ("CSSLineLayout::PlaceChild: frame=%p {%d, %d, %d, %d}", + aFrame, + aFrameRect.x, aFrameRect.y, + aFrameRect.width, aFrameRect.height)); + +#if XXX_fix_me + // XXX this is not right; the max-element-size of a child depends on + // it's margins which it doesn't know how to add in + + if (nsnull != mMaxElementSize) { + // XXX I'm not certain that this is doing the right thing; rethink this + nscoord elementWidth = kidMaxElementSize->width + horizontalMargins; + if (elementWidth > mMaxElementSize->width) { + mMaxElementSize->width = elementWidth; + } + if (aFrameMetrics.height > mMaxElementSize->height) { + mMaxElementSize->height = aFrameMetrics.height; + } + } +#endif + + if (aFrameMetrics.ascent > mMaxAscent) { + mMaxAscent = aFrameMetrics.ascent; + } + if (aFrameMetrics.descent > mMaxDescent) { + mMaxDescent = aFrameMetrics.descent; + } + nsresult rv = SetAscent(aFrameMetrics.ascent); + if (NS_OK != rv) { + return nsInlineReflowStatus(rv); + } + mFrameNum++; + +#if XXX_fix_me + mLine->mLastContentOffset = mKidContentIndex; + switch (aFrameReflowStatus & NS_INLINE_REFLOW_REFLOW_MASK) { + case NS_INLINE_REFLOW_COMPLETE: + case NS_INLINE_REFLOW_BREAK_AFTER: + mLine->mLastContentIsComplete = PR_TRUE; + mKidPrevInFlow = nsnull; + break; + + case NS_INLINE_REFLOW_NOT_COMPLETE: + mLine->mLastContentIsComplete = PR_FALSE; + mKidPrevInFlow = mKidFrame; + break; + } +#endif + + NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW, + ("CSSLineLayout::PlaceChild: aFrameReflowStatus=%x", + aFrameReflowStatus)); + return aFrameReflowStatus; +} + +nscoord +nsCSSInlineLayout::AlignFrames(nsIFrame* aFrame, PRInt32 aFrameCount, + nsRect& aBounds) +{ + NS_PRECONDITION(aFrameCount == mFrameNum, "bogus reflow"); + + nscoord lineHeight; + + if (PR_TRUE /*XXX !mLine->mIsBlock*/) { + // Vertically align the children on the line; this will compute + // the actual line height for us. + lineHeight = + nsCSSLayout::VerticallyAlignChildren(mLineLayout.mPresContext, + mContainerFrame, mContainerFont, + mY, aFrame, aFrameCount, + mAscents, mMaxAscent); + } + else { + // The line height of a block is just the block's height + lineHeight = mMaxAscent; + } + + // Save away line bounds before other adjustments + aBounds.x = mLeftEdge; + aBounds.y = mY; + aBounds.width = mX - mLeftEdge; + aBounds.height = lineHeight; + + // Now horizontally place the children + if (!mUnconstrainedWidth) { + nsCSSLayout::HorizontallyPlaceChildren(mLineLayout.mPresContext, + mContainerFrame, + mContainerText->mTextAlign, + mDirection, + aFrame, aFrameCount, + mX - mLeftEdge, + mAvailWidth); + } + + // Last, apply relative positioning + nsCSSLayout::RelativePositionChildren(mLineLayout.mPresContext, + mContainerFrame, + aFrame, aFrameCount); + + return lineHeight; +} diff --git a/layout/css/layout/src/nsCSSInlineLayout.h b/layout/css/layout/src/nsCSSInlineLayout.h new file mode 100644 index 00000000000..d4608b5e418 --- /dev/null +++ b/layout/css/layout/src/nsCSSInlineLayout.h @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ +#ifndef nsCSSInlineLayout_h___ +#define nsCSSInlineLayout_h___ + +#include "nsIFrame.h" +#include "nsIInlineReflow.h" +class nsCSSLineLayout; +struct nsStyleDisplay; +struct nsStyleFont; +struct nsStyleText; + +/** + * This structure contains the horizontal layout state for line + * layout frame placement. This is factored out of nsCSSLineLayout so + * that the block layout code and the inline layout code can use + * nsCSSLineLayout to reflow and place frames. + */ +struct nsCSSInlineLayout { + nsCSSInlineLayout(nsCSSLineLayout& aLineLayout, + nsIFrame* aContainerFrame, + nsIStyleContext* aContainerStyle); + ~nsCSSInlineLayout(); + + void Init(const nsReflowState* aContainerReflowState); + + void Prepare(PRBool aUnconstrainedWidth, + PRBool aNoWrap, + nsSize* aMaxElementSize); + + void SetReflowSpace(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight); + + nsInlineReflowStatus ReflowAndPlaceFrame(nsIFrame* aFrame); + + nscoord AlignFrames(nsIFrame* aFrame, PRInt32 aFrameCount, nsRect& aBounds); + + PRBool IsFirstChild(); + + nsresult SetAscent(nscoord aAscent); + + PRBool ComputeMaxSize(nsIFrame* aFrame, + nsMargin& aKidMargin, + nsSize& aResult); + + nsInlineReflowStatus ReflowFrame(nsIFrame* aFrame, + nsReflowMetrics& aDesiredSize, + const nsReflowState& aReflowState, + PRBool& aInlineAware); + + nsInlineReflowStatus PlaceFrame(nsIFrame* aFrame, + nsRect& kidRect, + const nsReflowMetrics& kidMetrics, + const nsMargin& kidMargin, + nsInlineReflowStatus kidReflowStatus); + + nsCSSLineLayout& mLineLayout; + nsIFrame* mContainerFrame; + const nsStyleFont* mContainerFont; + const nsStyleText* mContainerText; + const nsStyleDisplay* mContainerDisplay; + const nsReflowState* mContainerReflowState; + PRUint8 mDirection; + + PRPackedBool mUnconstrainedWidth; + PRPackedBool mNoWrap; + nscoord mAvailWidth; + nscoord mAvailHeight; + nscoord mX, mY; + nscoord mLeftEdge, mRightEdge; + + PRInt32 mFrameNum; + nscoord* mAscents; + nscoord mAscentBuf[20]; + nscoord mMaxAscents; + + nscoord mMaxAscent; + nscoord mMaxDescent; + nsSize* mMaxElementSize; +}; + +#endif /* nsCSSInlineLayout_h___ */ diff --git a/layout/css/layout/src/nsCSSLineLayout.cpp b/layout/css/layout/src/nsCSSLineLayout.cpp index d0a20a19980..344a67eeda9 100644 --- a/layout/css/layout/src/nsCSSLineLayout.cpp +++ b/layout/css/layout/src/nsCSSLineLayout.cpp @@ -19,6 +19,7 @@ #include "nsCSSLineLayout.h" #include "nsCSSLayout.h" +#if 0 #include "nsIFontMetrics.h" #include "nsIPresContext.h" #include "nsIRunaround.h" @@ -27,6 +28,7 @@ // XXX nsCSSIIDs.[h,cpp] static NS_DEFINE_IID(kIInlineReflowIID, NS_IINLINE_REFLOW_IID); static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID); +#endif void nsCSSTextRun::List(FILE* out, PRInt32 aIndent) @@ -45,382 +47,6 @@ nsCSSTextRun::List(FILE* out, PRInt32 aIndent) //---------------------------------------------------------------------- -nsCSSInlineLayout::nsCSSInlineLayout(nsCSSLineLayout& aLineLayout, - nsIFrame* aContainerFrame, - nsIStyleContext* aContainerStyle, - const nsReflowState& aContainerRS) - : mLineLayout(aLineLayout), - mContainerReflowState(aContainerRS) -{ - mContainerFrame = aContainerFrame; - mAscents = mAscentBuf; - mMaxAscents = sizeof(mAscentBuf) / sizeof(mAscentBuf[0]); - mMaxElementSize = nsnull; - - mContainerFont = (const nsStyleFont*) - aContainerStyle->GetStyleData(eStyleStruct_Font); - mContainerText = (const nsStyleText*) - aContainerStyle->GetStyleData(eStyleStruct_Text); - mContainerDisplay = (const nsStyleDisplay*) - aContainerStyle->GetStyleData(eStyleStruct_Display); - mDirection = mContainerDisplay->mDirection; -} - -nsCSSInlineLayout::~nsCSSInlineLayout() -{ - if (mAscents != mAscentBuf) { - delete [] mAscents; - } -} - -nsresult -nsCSSInlineLayout::SetAscent(nscoord aAscent) -{ - PRInt32 frameNum = mFrameNum; - if (frameNum == mMaxAscents) { - mMaxAscents *= 2; - nscoord* newAscents = new nscoord[mMaxAscents]; - if (nsnull == newAscents) { - return NS_ERROR_OUT_OF_MEMORY; - } - nsCRT::memcpy(newAscents, mAscents, sizeof(nscoord) * frameNum); - if (mAscents != mAscentBuf) { - delete [] mAscents; - } - mAscents = newAscents; - } - mAscents[frameNum] = aAscent; - return NS_OK; -} - -void -nsCSSInlineLayout::Prepare(PRBool aUnconstrainedWidth, PRBool aNoWrap, - nsSize* aMaxElementSize) -{ - mFrameNum = 0; - mUnconstrainedWidth = aUnconstrainedWidth; - mNoWrap = aNoWrap; - mMaxElementSize = aMaxElementSize; - - // mKidPrevInFlow = ???; -} - -void -nsCSSInlineLayout::SetReflowSpace(nscoord aX, nscoord aY, - nscoord aAvailWidth, nscoord aAvailHeight) -{ - mAvailWidth = aAvailWidth; - mAvailHeight = aAvailHeight; - mX = aX; - mY = aY; - mLeftEdge = aX; - mRightEdge = aX + aAvailWidth; -} - -//XXX block children of inline frames needs handling *here* - -nsInlineReflowStatus -nsCSSInlineLayout::ReflowAndPlaceFrame(nsIFrame* aFrame) -{ - // Compute the maximum size of the frame. If there is no room at all - // for it, then trigger a line-break before the frame. - nsSize maxSize; - nsMargin margin; - if (!ComputeMaxSize(aFrame, margin, maxSize)) { - return NS_INLINE_REFLOW_LINE_BREAK_BEFORE; - } - - // Setup reflow state for reflowing the frame - nsReflowState reflowState(aFrame, mContainerReflowState, maxSize); - nsInlineReflowStatus rs; - nsReflowMetrics metrics(mMaxElementSize); - PRBool isAware; - aFrame->WillReflow(*mLineLayout.mPresContext); - rs = ReflowFrame(aFrame, metrics, reflowState, isAware); - if (IS_REFLOW_ERROR(rs)) { - return rs; - } - if (NS_INLINE_REFLOW_BREAK_BEFORE == (rs & NS_INLINE_REFLOW_REFLOW_MASK)) { - return rs; - } - - // It's possible the frame didn't fit - if (metrics.width > maxSize.width) { - if (!IsFirstChild()) { - // We are out of room. - // XXX mKidPrevInFlow - NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW, - ("LineLayout::ReflowChild: !fit size=%d,%d", - metrics.width, metrics.height)); - return NS_INLINE_REFLOW_LINE_BREAK_BEFORE; - } - } - - nsRect frameRect(mX, mY, metrics.width, metrics.height); - return PlaceFrame(aFrame, frameRect, metrics, margin, rs); -} - -// XXX RTL -PRBool -nsCSSInlineLayout::IsFirstChild() -{ - return 0 == mFrameNum; -} - -PRBool -nsCSSInlineLayout::ComputeMaxSize(nsIFrame* aFrame, - nsMargin& aKidMargin, - nsSize& aResult) -{ - const nsStyleSpacing* kidSpacing; - aFrame->GetStyleData(eStyleStruct_Spacing, - (const nsStyleStruct*&)kidSpacing); - kidSpacing->CalcMarginFor(aFrame, aKidMargin); - if (mUnconstrainedWidth || mNoWrap) { - aResult.width = NS_UNCONSTRAINEDSIZE; - } - else { - aResult.width = mRightEdge - mX; - aResult.width -= aKidMargin.left + aKidMargin.right; - if (!IsFirstChild() && (aResult.width <= 0)) { - // XXX Make sure child is dirty for next time - aFrame->WillReflow(*mLineLayout.mPresContext); - NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW, - ("CSSLineLayout::ComputeMaxSize: !fit")); - return PR_FALSE; - } - } - aResult.height = mAvailHeight; - return PR_TRUE; -} - -nsInlineReflowStatus -nsCSSInlineLayout::ReflowFrame(nsIFrame* aKidFrame, - nsReflowMetrics& aMetrics, - const nsReflowState& aReflowState, - PRBool& aInlineAware) -{ - // There are 3 ways to reflow the child frame: using the nsIRunaround - // interface, using the nsIInlineReflow interface or using the default - // Reflow method in nsIFrame. The order of precedence is nsIRunaround, - // nsIInlineReflow, nsIFrame. For all three API's we map the reflow status - // into an nsInlineReflowStatus. - - nsresult rv; - nsIRunaround* runAround; - nsIInlineReflow* inlineReflow; - if ((nsnull != mLineLayout.mSpaceManager) && - (NS_OK == aKidFrame->QueryInterface(kIRunaroundIID, - (void**)&runAround))) { - nsRect r; - runAround->Reflow(mLineLayout.mPresContext, mLineLayout.mSpaceManager, - aMetrics, aReflowState, r, rv); - aMetrics.width = r.width; - aMetrics.height = r.height; - aMetrics.ascent = r.height; - aMetrics.descent = 0; - rv = NS_FRAME_REFLOW_STATUS_2_INLINE_REFLOW_STATUS(rv); - aInlineAware = PR_FALSE; - } - else if (NS_OK == aKidFrame->QueryInterface(kIInlineReflowIID, - (void**)&inlineReflow)) { - rv = inlineReflow->InlineReflow(mLineLayout, aMetrics, aReflowState); - aInlineAware = PR_TRUE; - } - else { - aKidFrame->Reflow(mLineLayout.mPresContext, aMetrics, aReflowState, rv); - rv = NS_FRAME_REFLOW_STATUS_2_INLINE_REFLOW_STATUS(rv); - aInlineAware = PR_FALSE; - } - - if (NS_FRAME_IS_COMPLETE(rv)) { - nsIFrame* kidNextInFlow; - aKidFrame->GetNextInFlow(kidNextInFlow); - if (nsnull != kidNextInFlow) { - // Remove all of the childs next-in-flows. Make sure that we ask - // the right parent to do the removal (it's possible that the - // parent is not this because we are executing pullup code) - nsCSSContainerFrame* parent; - aKidFrame->GetGeometricParent((nsIFrame*&)parent); - parent->DeleteChildsNextInFlow(aKidFrame); - } - } - - return rv; -} - -nsInlineReflowStatus -nsCSSInlineLayout::PlaceFrame(nsIFrame* aFrame, - nsRect& aFrameRect, - const nsReflowMetrics& aFrameMetrics, - const nsMargin& aFrameMargin, - nsInlineReflowStatus aFrameReflowStatus) -{ - nscoord horizontalMargins = 0; - - // Special case to position outside list bullets. - // XXX RTL bullets - PRBool isBullet = PR_FALSE; - if (mLineLayout.mListPositionOutside) { - PRBool isFirstChild = IsFirstChild(); - if (isFirstChild && (0 == mLineLayout.mLineNumber)) { - nsIFrame* containerPrevInFlow; - mContainerFrame->GetPrevInFlow(containerPrevInFlow); - if (nsnull == containerPrevInFlow) { - isBullet = PR_TRUE; - // We are placing the first child of the container and we have - // list-style-position of "outside" therefore this is the - // bullet that is being reflowed. The bullet is placed in the - // padding area of this block. Don't worry about getting the Y - // coordinate of the bullet right (vertical alignment will - // take care of that). - - // Compute gap between bullet and inner rect left edge - nsIFontMetrics* fm = - mLineLayout.mPresContext->GetMetricsFor(mContainerFont->mFont); - nscoord kidAscent = fm->GetMaxAscent(); - nscoord dx = fm->GetHeight() / 2; // from old layout engine - NS_RELEASE(fm); - - // XXX RTL bullets - aFrameRect.x = mX - aFrameRect.width - dx; - aFrame->SetRect(aFrameRect); - } - } - } - if (!isBullet) { - // Place normal in-flow child - aFrame->SetRect(aFrameRect); - - // XXX RTL - // Advance - const nsStyleDisplay* frameDisplay; - aFrame->GetStyleData(eStyleStruct_Display, - (const nsStyleStruct*&) frameDisplay); - switch (frameDisplay->mFloats) { - default: - NS_NOTYETIMPLEMENTED("Unsupported floater type"); - // FALL THROUGH - - case NS_STYLE_FLOAT_LEFT: - case NS_STYLE_FLOAT_RIGHT: - // When something is floated, it's margin's are applied there - // not here. - break; - - case NS_STYLE_FLOAT_NONE: - horizontalMargins = aFrameMargin.left + aFrameMargin.right; - break; - } - nscoord totalWidth = aFrameMetrics.width + horizontalMargins; - mX += totalWidth; - } - - NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW, - ("CSSLineLayout::PlaceChild: frame=%p {%d, %d, %d, %d}", - aFrame, - aFrameRect.x, aFrameRect.y, - aFrameRect.width, aFrameRect.height)); - -#if XXX_fix_me - // XXX this is not right; the max-element-size of a child depends on - // it's margins which it doesn't know how to add in - - if (nsnull != mMaxElementSize) { - // XXX I'm not certain that this is doing the right thing; rethink this - nscoord elementWidth = kidMaxElementSize->width + horizontalMargins; - if (elementWidth > mMaxElementSize->width) { - mMaxElementSize->width = elementWidth; - } - if (aFrameMetrics.height > mMaxElementSize->height) { - mMaxElementSize->height = aFrameMetrics.height; - } - } -#endif - - if (aFrameMetrics.ascent > mMaxAscent) { - mMaxAscent = aFrameMetrics.ascent; - } - if (aFrameMetrics.descent > mMaxDescent) { - mMaxDescent = aFrameMetrics.descent; - } - nsresult rv = SetAscent(aFrameMetrics.ascent); - if (NS_OK != rv) { - return nsInlineReflowStatus(rv); - } - mFrameNum++; - -#if XXX_fix_me - mLine->mLastContentOffset = mKidContentIndex; - switch (aFrameReflowStatus & NS_INLINE_REFLOW_REFLOW_MASK) { - case NS_INLINE_REFLOW_COMPLETE: - case NS_INLINE_REFLOW_BREAK_AFTER: - mLine->mLastContentIsComplete = PR_TRUE; - mKidPrevInFlow = nsnull; - break; - - case NS_INLINE_REFLOW_NOT_COMPLETE: - mLine->mLastContentIsComplete = PR_FALSE; - mKidPrevInFlow = mKidFrame; - break; - } -#endif - - NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW, - ("CSSLineLayout::PlaceChild: aFrameReflowStatus=%x", - aFrameReflowStatus)); - return aFrameReflowStatus; -} - -nscoord -nsCSSInlineLayout::AlignFrames(nsIFrame* aFrame, PRInt32 aFrameCount, - nsRect& aBounds) -{ - NS_PRECONDITION(aFrameCount == mFrameNum, "bogus reflow"); - - nscoord lineHeight; - - if (PR_TRUE /*XXX !mLine->mIsBlock*/) { - // Vertically align the children on the line; this will compute - // the actual line height for us. - lineHeight = - nsCSSLayout::VerticallyAlignChildren(mLineLayout.mPresContext, - mContainerFrame, mContainerFont, - mY, aFrame, aFrameCount, - mAscents, mMaxAscent); - } - else { - // The line height of a block is just the block's height - lineHeight = mMaxAscent; - } - - // Save away line bounds before other adjustments - aBounds.x = mLeftEdge; - aBounds.y = mY; - aBounds.width = mX - mLeftEdge; - aBounds.height = lineHeight; - - // Now horizontally place the children - if (!mUnconstrainedWidth) { - nsCSSLayout::HorizontallyPlaceChildren(mLineLayout.mPresContext, - mContainerFrame, - mContainerText->mTextAlign, - mDirection, - aFrame, aFrameCount, - mX - mLeftEdge, - mAvailWidth); - } - - // Last, apply relative positioning - nsCSSLayout::RelativePositionChildren(mLineLayout.mPresContext, - mContainerFrame, - aFrame, aFrameCount); - - return lineHeight; -} - -//---------------------------------------------------------------------- - nsCSSLineLayout::nsCSSLineLayout(nsIPresContext* aPresContext, nsISpaceManager* aSpaceManager) { @@ -467,4 +93,3 @@ nsCSSLineLayout::AddText(nsIFrame* aTextFrame) mCurrentTextRun->mArray.AppendElement(aTextFrame); return NS_OK;/* XXX */ } - diff --git a/layout/css/layout/src/nsCSSLineLayout.h b/layout/css/layout/src/nsCSSLineLayout.h index cab992021b0..df77b2aa66f 100644 --- a/layout/css/layout/src/nsCSSLineLayout.h +++ b/layout/css/layout/src/nsCSSLineLayout.h @@ -19,84 +19,8 @@ #ifndef nsCSSLineLayout_h___ #define nsCSSLineLayout_h___ -#include "nsCSSBlockFrame.h" +#include "nsIFrame.h" #include "nsVoidArray.h" -#include "nsStyleConsts.h" - -class nsCSSLineLayout; -struct nsStyleDisplay; -struct nsStyleFont; -struct nsStyleText; - -/* d76e29b0-ff56-11d1-89e7-006008911b81 */ -#define NS_IINLINE_REFLOW_IID \ -{0xd76e29b0, 0xff56, 0x11d1, {0x89, 0xe7, 0x00, 0x60, 0x08, 0x91, 0x1b, 0x81}} - -class nsIInlineReflow { -public: - /** - * Recursively find all of the text runs contained in an outer - * block container. Inline frames implement this by recursing over - * their children; note that inlines frames may need to create - * missing child frames before proceeding (e.g. when a tree - * containing inlines is appended/inserted into a block container - */ - NS_IMETHOD FindTextRuns(nsCSSLineLayout& aLineLayout) = 0; - - /** - * InlineReflow method. See below for how to interpret the return value. - */ - NS_IMETHOD InlineReflow(nsCSSLineLayout& aLineLayout, - nsReflowMetrics& aDesiredSize, - const nsReflowState& aReflowState) = 0; -}; - -/** - * For InlineReflow the return value (an nsresult) indicates the - * status of the reflow operation. If the return value is negative - * then some sort of catastrophic error has occured (e.g. out of memory). - * If the return value is non-negative then the macros below can be - * used to interpret it. - */ -typedef nsresult nsInlineReflowStatus; - -// The low 3 bits of the nsInlineReflowStatus indicate what happened -// to the child during it's reflow. -#define NS_INLINE_REFLOW_COMPLETE 0 // note: not a bit! -#define NS_INLINE_REFLOW_NOT_COMPLETE 1 -#define NS_INLINE_REFLOW_BREAK_BEFORE 2 -#define NS_INLINE_REFLOW_BREAK_AFTER 3 -#define NS_INLINE_REFLOW_REFLOW_MASK 0x3 - -// The inline reflow status may need to indicate that the next-in-flow -// must be reflowed. This bit is or'd in in that case. -#define NS_INLINE_REFLOW_NEXT_IN_FLOW 0x4 - -// When a break is indicated (break-before/break-after) the type of -// break requested is indicate in bits 4-7. -#define NS_INLINE_REFLOW_BREAK_MASK 0xF0 -#define NS_INLINE_REFLOW_GET_BREAK_TYPE(_status) (((_status) >> 4) & 0xF) -#define NS_INLINE_REFLOW_MAKE_BREAK_TYPE(_type) ((_type) << 4) - -// This macro maps an nsIFrame nsReflowStatus value into an -// nsInlineReflowStatus value. -#define NS_FRAME_REFLOW_STATUS_2_INLINE_REFLOW_STATUS(_status) \ - (((_status) & 0x1) | (((_status) & NS_FRAME_REFLOW_NEXTINFLOW) << 2)) - -// Convenience macro's -#define NS_INLINE_REFLOW_LINE_BREAK_BEFORE \ - (NS_INLINE_REFLOW_BREAK_BEFORE | \ - NS_INLINE_REFLOW_MAKE_BREAK_TYPE(NS_STYLE_CLEAR_LINE)) - -#define NS_INLINE_REFLOW_LINE_BREAK_AFTER \ - (NS_INLINE_REFLOW_BREAK_AFTER | \ - NS_INLINE_REFLOW_MAKE_BREAK_TYPE(NS_STYLE_CLEAR_LINE)) - -// This macro tests to see if an nsInlineReflowStatus is an error value -// or just a regular return value -#define NS_INLINE_REFLOW_ERROR(_status) (PRInt32(_status) < 0) - -//---------------------------------------------------------------------- // This structure represents a run of text. In mText are the // nsIFrame's that are considered text frames. @@ -122,87 +46,6 @@ struct nsCSSTextRun { //---------------------------------------------------------------------- -/** - * This structure contains the horizontal layout state for line - * layout frame placement. This is factored out of nsCSSLineLayout so - * that the block layout code and the inline layout code can use - * nsCSSLineLayout to reflow and place frames. - */ -struct nsCSSInlineLayout { - nsCSSInlineLayout(nsCSSLineLayout& aLineLayout, - nsIFrame* aContainerFrame, - nsIStyleContext* aContainerStyle, - const nsReflowState& aContainerReflowState); - ~nsCSSInlineLayout(); - - void Prepare(PRBool aUnconstrainedWidth, - PRBool aNoWrap, - nsSize* aMaxElementSize); - - void SetReflowSpace(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight); - - nsInlineReflowStatus ReflowAndPlaceFrame(nsIFrame* aFrame); - - nscoord AlignFrames(nsIFrame* aFrame, PRInt32 aFrameCount, - nsRect& aBounds); - - PRInt32 GetFrameNum() { return mFrameNum; } - - /** - * Return an approximation of the line height for the line as - * reflowed so far. Note that this value doesn't take into account - * any vertical alignment properties on the frames in the line so - * it may be smaller than it should be. - */ - nscoord GetLineHeight() { return mMaxAscent + mMaxDescent; } - -protected: - PRBool IsFirstChild(); - - nsresult SetAscent(nscoord aAscent); - - PRBool ComputeMaxSize(nsIFrame* aFrame, - nsMargin& aKidMargin, - nsSize& aResult); - - nsInlineReflowStatus ReflowFrame(nsIFrame* aFrame, - nsReflowMetrics& aDesiredSize, - const nsReflowState& aReflowState, - PRBool& aInlineAware); - - nsInlineReflowStatus PlaceFrame(nsIFrame* aFrame, - nsRect& kidRect, - const nsReflowMetrics& kidMetrics, - const nsMargin& kidMargin, - nsInlineReflowStatus kidReflowStatus); - - nsCSSLineLayout& mLineLayout; - nsIFrame* mContainerFrame; - const nsStyleFont* mContainerFont; - const nsStyleText* mContainerText; - const nsStyleDisplay* mContainerDisplay; - const nsReflowState& mContainerReflowState; - PRUint8 mDirection; - - PRPackedBool mUnconstrainedWidth; - PRPackedBool mNoWrap; - nscoord mAvailWidth; - nscoord mAvailHeight; - nscoord mX, mY; - nscoord mLeftEdge, mRightEdge; - - PRInt32 mFrameNum; - nscoord* mAscents; - nscoord mAscentBuf[20]; - nscoord mMaxAscents; - - nscoord mMaxAscent; - nscoord mMaxDescent; - nsSize* mMaxElementSize; -}; - -//---------------------------------------------------------------------- - // 1) collecting frames for text-runs // 2) horizontal layout of frames for block // 3) horizontal layout of frames for inline