Bug 504221 part 10. Switch the first-line frame list munging to nsFrameList. r=fantasai, r+sr=roc

This commit is contained in:
Boris Zbarsky 2009-07-28 08:53:19 -04:00
parent dca83c78dc
commit cddaf1831c
4 changed files with 159 additions and 120 deletions

View File

@ -689,7 +689,7 @@ nsFrameItems::AddChild(nsIFrame* aChild)
// parents (caption, I'm looking at you) on the same framelist, and // parents (caption, I'm looking at you) on the same framelist, and
// nsFrameList asserts if you try to do that. // nsFrameList asserts if you try to do that.
if (IsEmpty()) { if (IsEmpty()) {
AppendFrames(nsnull, aChild); nsFrameList::AppendFrames(nsnull, aChild);
} }
else else
{ {
@ -9558,7 +9558,8 @@ nsCSSFrameConstructor::ProcessChildren(nsFrameConstructorState& aState,
rv = WrapFramesInFirstLetterFrame(aContent, aFrame, aFrameItems); rv = WrapFramesInFirstLetterFrame(aContent, aFrame, aFrameItems);
} }
if (haveFirstLineStyle) { if (haveFirstLineStyle) {
rv = WrapFramesInFirstLineFrame(aState, aContent, aFrame, aFrameItems); rv = WrapFramesInFirstLineFrame(aState, aContent, aFrame, nsnull,
aFrameItems);
} }
// We might end up with first-line frames that change // We might end up with first-line frames that change
@ -9637,72 +9638,60 @@ nsCSSFrameConstructor::WrapFramesInFirstLineFrame(
nsFrameConstructorState& aState, nsFrameConstructorState& aState,
nsIContent* aBlockContent, nsIContent* aBlockContent,
nsIFrame* aBlockFrame, nsIFrame* aBlockFrame,
nsIFrame* aLineFrame,
nsFrameItems& aFrameItems) nsFrameItems& aFrameItems)
{ {
nsresult rv = NS_OK; nsresult rv = NS_OK;
// Find the first and last inline frame in aFrameItems // Find the part of aFrameItems that we want to put in the first-line
nsIFrame* kid = aFrameItems.FirstChild(); nsFrameList::FrameLinkEnumerator link(aFrameItems);
nsIFrame* firstInlineFrame = nsnull; while (!link.AtEnd() && IsInlineOutside(link.NextFrame())) {
nsIFrame* lastInlineFrame = nsnull; link.Next();
while (kid) {
if (IsInlineOutside(kid)) {
if (!firstInlineFrame) firstInlineFrame = kid;
lastInlineFrame = kid;
}
else {
break;
}
kid = kid->GetNextSibling();
} }
// If we don't find any inline frames, then there is nothing to do nsFrameItems firstLineChildren = aFrameItems.ExtractHead(link);
if (!firstInlineFrame) {
return rv; if (firstLineChildren.IsEmpty()) {
// Nothing is supposed to go into the first-line; nothing to do
return NS_OK;
} }
// Create line frame if (!aLineFrame) {
nsStyleContext* parentStyle = // Create line frame
nsFrame::CorrectStyleParentFrame(aBlockFrame, nsStyleContext* parentStyle =
nsCSSPseudoElements::firstLine)-> nsFrame::CorrectStyleParentFrame(aBlockFrame,
GetStyleContext(); nsCSSPseudoElements::firstLine)->
nsRefPtr<nsStyleContext> firstLineStyle = GetFirstLineStyle(aBlockContent, GetStyleContext();
parentStyle); nsRefPtr<nsStyleContext> firstLineStyle = GetFirstLineStyle(aBlockContent,
parentStyle);
nsIFrame* lineFrame = NS_NewFirstLineFrame(mPresShell, firstLineStyle); aLineFrame = NS_NewFirstLineFrame(mPresShell, firstLineStyle);
if (lineFrame) { if (aLineFrame) {
// Initialize the line frame // Initialize the line frame
rv = InitAndRestoreFrame(aState, aBlockContent, aBlockFrame, nsnull, rv = InitAndRestoreFrame(aState, aBlockContent, aBlockFrame, nsnull,
lineFrame); aLineFrame);
// Mangle the list of frames we are giving to the block: first // The lineFrame will be the block's first child; the rest of the
// chop the list in two after lastInlineFrame // frame list (after lastInlineFrame) will be the second and
nsIFrame* secondBlockFrame = lastInlineFrame->GetNextSibling(); // subsequent children; insert lineFrame into aFrameItems.
lastInlineFrame->SetNextSibling(nsnull); aFrameItems.InsertFrame(nsnull, nsnull, aLineFrame);
// The lineFrame will be the block's first child; the rest of the NS_ASSERTION(aLineFrame->GetStyleContext() == firstLineStyle,
// frame list (after lastInlineFrame) will be the second and "Bogus style context on line frame");
// subsequent children; join the list together and reset
// aFrameItems appropriately.
if (secondBlockFrame) {
lineFrame->SetNextSibling(secondBlockFrame);
} }
if (aFrameItems.FirstChild() == lastInlineFrame) { }
// Just in case the block had exactly one inline child
aFrameItems.lastChild = lineFrame;
}
aFrameItems.SetFrames(lineFrame);
if (aLineFrame) {
// Give the inline frames to the lineFrame <b>after</b> reparenting them // Give the inline frames to the lineFrame <b>after</b> reparenting them
kid = firstInlineFrame; ReparentFrames(aState.mFrameManager, aLineFrame, firstLineChildren);
NS_ASSERTION(lineFrame->GetStyleContext() == firstLineStyle, if (aLineFrame->GetChildList(nsnull).IsEmpty() &&
"Bogus style context on line frame"); (aLineFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
while (kid) { aLineFrame->SetInitialChildList(nsnull, firstLineChildren);
ReparentFrame(aState.mFrameManager, lineFrame, kid); } else {
kid = kid->GetNextSibling(); aState.mFrameManager->AppendFrames(aLineFrame, nsnull,
firstLineChildren.FirstChild());
} }
lineFrame->SetInitialChildList(nsnull, firstInlineFrame);
} }
else { else {
rv = NS_ERROR_OUT_OF_MEMORY; rv = NS_ERROR_OUT_OF_MEMORY;
@ -9723,66 +9712,25 @@ nsCSSFrameConstructor::AppendFirstLineFrames(
{ {
// It's possible that aBlockFrame needs to have a first-line frame // It's possible that aBlockFrame needs to have a first-line frame
// created because it doesn't currently have any children. // created because it doesn't currently have any children.
nsIFrame* blockKid = aBlockFrame->GetFirstChild(nsnull); const nsFrameList& blockKids = aBlockFrame->GetChildList(nsnull);
if (!blockKid) { if (blockKids.IsEmpty()) {
return WrapFramesInFirstLineFrame(aState, aBlockContent, return WrapFramesInFirstLineFrame(aState, aBlockContent,
aBlockFrame, aFrameItems); aBlockFrame, nsnull, aFrameItems);
} }
// Examine the last block child - if it's a first-line frame then // Examine the last block child - if it's a first-line frame then
// appended frames need special treatment. // appended frames need special treatment.
nsresult rv = NS_OK; nsIFrame* lastBlockKid = blockKids.LastChild();
nsFrameList blockFrames(blockKid);
nsIFrame* lastBlockKid = blockFrames.LastChild();
if (lastBlockKid->GetType() != nsGkAtoms::lineFrame) { if (lastBlockKid->GetType() != nsGkAtoms::lineFrame) {
// No first-line frame at the end of the list, therefore there is // No first-line frame at the end of the list, therefore there is
// an interveening block between any first-line frame the frames // an intervening block between any first-line frame the frames
// we are appending. Therefore, we don't need any special // we are appending. Therefore, we don't need any special
// treatment of the appended frames. // treatment of the appended frames.
return rv; return NS_OK;
}
nsIFrame* lineFrame = lastBlockKid;
// Find the first and last inline frame in aFrameItems
nsIFrame* kid = aFrameItems.FirstChild();
nsIFrame* firstInlineFrame = nsnull;
nsIFrame* lastInlineFrame = nsnull;
while (kid) {
if (IsInlineOutside(kid)) {
if (!firstInlineFrame) firstInlineFrame = kid;
lastInlineFrame = kid;
}
else {
break;
}
kid = kid->GetNextSibling();
} }
// If we don't find any inline frames, then there is nothing to do return WrapFramesInFirstLineFrame(aState, aBlockContent, aBlockFrame,
if (!firstInlineFrame) { lastBlockKid, aFrameItems);
return rv;
}
// The inline frames get appended to the lineFrame. Make sure they
// are reparented properly.
nsIFrame* remainingFrames = lastInlineFrame->GetNextSibling();
lastInlineFrame->SetNextSibling(nsnull);
kid = firstInlineFrame;
while (kid) {
ReparentFrame(aState.mFrameManager, lineFrame, kid);
kid = kid->GetNextSibling();
}
aState.mFrameManager->AppendFrames(lineFrame, nsnull, firstInlineFrame);
// The remaining frames get appended to the block frame
if (remainingFrames) {
aFrameItems.SetFrames(remainingFrames);
}
else {
aFrameItems.Clear();
}
return rv;
} }
// Special routine to handle inserting a new frame into a block // Special routine to handle inserting a new frame into a block
@ -10067,18 +10015,16 @@ nsCSSFrameConstructor::CreateFloatingLetterFrame(
} }
NS_ASSERTION(aResult.IsEmpty(), "aResult should be an empty nsFrameItems!"); NS_ASSERTION(aResult.IsEmpty(), "aResult should be an empty nsFrameItems!");
nsIFrame* insertAfter = nsnull;
// Put the new float before any of the floats in the block we're // Put the new float before any of the floats in the block we're
// doing first-letter for, that is, before any floats whose parent is aBlockFrame // doing first-letter for, that is, before any floats whose parent is aBlockFrame
for (nsFrameList::Enumerator e(aState.mFloatedItems); !e.AtEnd(); e.Next()) { nsFrameList::FrameLinkEnumerator link(aState.mFloatedItems);
if (e.get()->GetParent() == aBlockFrame) while (!link.AtEnd() && link.NextFrame()->GetParent() != aBlockFrame) {
break; link.Next();
insertAfter = e.get();
} }
rv = aState.AddChild(letterFrame, aResult, letterContent, aStyleContext, rv = aState.AddChild(letterFrame, aResult, letterContent, aStyleContext,
aParentFrame, PR_FALSE, PR_TRUE, PR_FALSE, PR_TRUE, aParentFrame, PR_FALSE, PR_TRUE, PR_FALSE, PR_TRUE,
insertAfter); link.PrevFrame());
if (nextTextFrame) { if (nextTextFrame) {
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {

View File

@ -87,6 +87,13 @@ struct nsFrameItems : public nsFrameList {
nsFrameItems(nsIFrame* aFrame = nsnull); nsFrameItems(nsIFrame* aFrame = nsnull);
nsFrameItems(const nsFrameList& aList, nsIFrame* aLastChild) :
nsFrameList(aList),
lastChild(aLastChild)
{
NS_ASSERTION(LastChild() == lastChild, "Bogus aLastChild");
}
// Appends the frame to the end of the list // Appends the frame to the end of the list
void AddChild(nsIFrame* aChild); void AddChild(nsIFrame* aChild);
@ -133,12 +140,32 @@ struct nsFrameItems : public nsFrameList {
return removed; return removed;
} }
nsFrameItems ExtractHead(FrameLinkEnumerator& aLink) {
nsIFrame* newLastChild = aLink.PrevFrame();
if (lastChild && aLink.NextFrame() == lastChild) {
lastChild = nsnull;
}
return nsFrameItems(nsFrameList::ExtractHead(aLink),
newLastChild);
}
void Clear() { void Clear() {
mFirstChild = lastChild = nsnull; mFirstChild = lastChild = nsnull;
} }
// For now, until we change some SetInitialChildList signatures // For now, until we change some SetInitialChildList signatures
operator nsIFrame* () { return FirstChild(); } operator nsIFrame* () { return FirstChild(); }
private:
// Not implemented; shouldn't be called
void SetFrames(nsIFrame* aFrameList);
void AppendFrames(nsIFrame* aParent, nsIFrame* aFrameList);
Slice AppendFrames(nsIFrame* aParent, nsFrameList& aFrameList);
void AppendFrame(nsIFrame* aParent, nsIFrame* aFrame);
PRBool RemoveFirstChild();
void InsertFrames(nsIFrame* aParent, nsIFrame* aPrevSibling,
nsIFrame* aFrameList);
void SortByContentOrder();
}; };
class nsCSSFrameConstructor class nsCSSFrameConstructor
@ -1590,11 +1617,21 @@ private:
// Methods support :first-line style // Methods support :first-line style
// This method chops the initial inline-outside frames out of aFrameItems.
// If aLineFrame is non-null, it appends them to that frame. Otherwise, it
// creates a new line frame, sets the inline frames as its initial child
// list, and inserts that line frame at the front of what's left of
// aFrameItems. In both cases, the kids are reparented to the line frame.
// After this call, aFrameItems holds the frames that need to become kids of
// the block (possibly including line frames).
nsresult WrapFramesInFirstLineFrame(nsFrameConstructorState& aState, nsresult WrapFramesInFirstLineFrame(nsFrameConstructorState& aState,
nsIContent* aBlockContent, nsIContent* aBlockContent,
nsIFrame* aBlockFrame, nsIFrame* aBlockFrame,
nsIFrame* aLineFrame,
nsFrameItems& aFrameItems); nsFrameItems& aFrameItems);
// Handle the case when a block with first-line style is appended to (by
// possibly calling WrapFramesInFirstLineFrame as needed).
nsresult AppendFirstLineFrames(nsFrameConstructorState& aState, nsresult AppendFirstLineFrames(nsFrameConstructorState& aState,
nsIContent* aContent, nsIContent* aContent,
nsIFrame* aBlockFrame, nsIFrame* aBlockFrame,

View File

@ -266,20 +266,29 @@ nsFrameList::InsertFrames(nsIFrame* aParent,
#endif #endif
} }
PRBool nsFrameList
nsFrameList::Split(nsIFrame* aAfterFrame, nsIFrame** aNextFrameResult) nsFrameList::ExtractHead(FrameLinkEnumerator& aLink)
{ {
NS_PRECONDITION(nsnull != aAfterFrame, "null ptr"); NS_PRECONDITION(&aLink.List() == this, "Unexpected list");
NS_PRECONDITION(nsnull != aNextFrameResult, "null ptr"); NS_PRECONDITION(!aLink.PrevFrame() ||
NS_ASSERTION(ContainsFrame(aAfterFrame), "split after unknown frame"); aLink.PrevFrame()->GetNextSibling() ==
aLink.NextFrame(),
"Unexpected PrevFrame()");
if (aNextFrameResult && aAfterFrame) { nsIFrame* prev = aLink.PrevFrame();
nsIFrame* nextFrame = aAfterFrame->GetNextSibling(); nsIFrame* newFirstFrame = nsnull;
aAfterFrame->SetNextSibling(nsnull); if (prev) {
*aNextFrameResult = nextFrame; // Truncate the list after |prev| and hand the first part to our new list.
return PR_TRUE; prev->SetNextSibling(nsnull);
newFirstFrame = mFirstChild;
mFirstChild = aLink.NextFrame();
// Now make sure aLink doesn't point to a frame we no longer have.
aLink.mPrev = nsnull;
} }
return PR_FALSE; // else aLink is pointing to before our first frame. Nothing to do.
return nsFrameList(newFirstFrame);
} }
nsIFrame* nsIFrame*

View File

@ -172,7 +172,13 @@ public:
inline Slice InsertFrames(nsIFrame* aParent, nsIFrame* aPrevSibling, inline Slice InsertFrames(nsIFrame* aParent, nsIFrame* aPrevSibling,
nsFrameList& aFrameList); nsFrameList& aFrameList);
PRBool Split(nsIFrame* aAfterFrame, nsIFrame** aNextFrameResult); class FrameLinkEnumerator;
/* Split this frame list such that all the frames before the link pointed to
* by aLink end up in the returned list, while the remaining frames stay in
* this list. After this call, aLink points to the beginning of this list.
*/
nsFrameList ExtractHead(FrameLinkEnumerator& aLink);
/** /**
* Sort the frames according to content order so that the first * Sort the frames according to content order so that the first
@ -310,7 +316,11 @@ public:
nsIFrame* get() const { return mFrame; } nsIFrame* get() const { return mFrame; }
private: #ifdef DEBUG
const nsFrameList& List() const { return mSlice.mList; }
#endif
protected:
#ifdef DEBUG #ifdef DEBUG
const Slice& mSlice; const Slice& mSlice;
#endif #endif
@ -319,6 +329,43 @@ public:
// May be null. // May be null.
}; };
/**
* A class that can be used to enumerate links between frames. When created
* from an nsFrameList, it points to the "link" immediately before the first
* frame. It can then be advanced until it points to the "link" immediately
* after the last frame. At any position, PrevFrame() and NextFrame() are
* the frames before and after the given link. This means PrevFrame() is
* null when the enumerator is at the beginning of the list and NextFrame()
* is null when it's AtEnd().
*/
class FrameLinkEnumerator : private Enumerator {
public:
friend class nsFrameList;
FrameLinkEnumerator(const nsFrameList& aList) :
Enumerator(aList),
mPrev(nsnull)
{}
FrameLinkEnumerator(const FrameLinkEnumerator& aOther) :
Enumerator(aOther),
mPrev(aOther.mPrev)
{}
void Next() {
mPrev = mFrame;
Enumerator::Next();
}
PRBool AtEnd() const { return Enumerator::AtEnd(); }
nsIFrame* PrevFrame() const { return mPrev; }
nsIFrame* NextFrame() const { return mFrame; }
protected:
nsIFrame* mPrev;
};
private: private:
#ifdef DEBUG #ifdef DEBUG
void CheckForLoops(); void CheckForLoops();