diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 01fde114300..ddd7b6b08c0 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -689,7 +689,7 @@ nsFrameItems::AddChild(nsIFrame* aChild) // parents (caption, I'm looking at you) on the same framelist, and // nsFrameList asserts if you try to do that. if (IsEmpty()) { - AppendFrames(nsnull, aChild); + nsFrameList::AppendFrames(nsnull, aChild); } else { @@ -9558,7 +9558,8 @@ nsCSSFrameConstructor::ProcessChildren(nsFrameConstructorState& aState, rv = WrapFramesInFirstLetterFrame(aContent, aFrame, aFrameItems); } 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 @@ -9637,72 +9638,60 @@ nsCSSFrameConstructor::WrapFramesInFirstLineFrame( nsFrameConstructorState& aState, nsIContent* aBlockContent, nsIFrame* aBlockFrame, + nsIFrame* aLineFrame, nsFrameItems& aFrameItems) { nsresult rv = NS_OK; - // 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(); + // Find the part of aFrameItems that we want to put in the first-line + nsFrameList::FrameLinkEnumerator link(aFrameItems); + while (!link.AtEnd() && IsInlineOutside(link.NextFrame())) { + link.Next(); } - // If we don't find any inline frames, then there is nothing to do - if (!firstInlineFrame) { - return rv; + nsFrameItems firstLineChildren = aFrameItems.ExtractHead(link); + + if (firstLineChildren.IsEmpty()) { + // Nothing is supposed to go into the first-line; nothing to do + return NS_OK; } - // Create line frame - nsStyleContext* parentStyle = - nsFrame::CorrectStyleParentFrame(aBlockFrame, - nsCSSPseudoElements::firstLine)-> - GetStyleContext(); - nsRefPtr firstLineStyle = GetFirstLineStyle(aBlockContent, - parentStyle); + if (!aLineFrame) { + // Create line frame + nsStyleContext* parentStyle = + nsFrame::CorrectStyleParentFrame(aBlockFrame, + nsCSSPseudoElements::firstLine)-> + GetStyleContext(); + nsRefPtr firstLineStyle = GetFirstLineStyle(aBlockContent, + parentStyle); - nsIFrame* lineFrame = NS_NewFirstLineFrame(mPresShell, firstLineStyle); + aLineFrame = NS_NewFirstLineFrame(mPresShell, firstLineStyle); - if (lineFrame) { - // Initialize the line frame - rv = InitAndRestoreFrame(aState, aBlockContent, aBlockFrame, nsnull, - lineFrame); + if (aLineFrame) { + // Initialize the line frame + rv = InitAndRestoreFrame(aState, aBlockContent, aBlockFrame, nsnull, + aLineFrame); - // Mangle the list of frames we are giving to the block: first - // chop the list in two after lastInlineFrame - nsIFrame* secondBlockFrame = lastInlineFrame->GetNextSibling(); - lastInlineFrame->SetNextSibling(nsnull); + // The lineFrame will be the block's first child; the rest of the + // frame list (after lastInlineFrame) will be the second and + // subsequent children; insert lineFrame into aFrameItems. + aFrameItems.InsertFrame(nsnull, nsnull, aLineFrame); - // The lineFrame will be the block's first child; the rest of the - // frame list (after lastInlineFrame) will be the second and - // subsequent children; join the list together and reset - // aFrameItems appropriately. - if (secondBlockFrame) { - lineFrame->SetNextSibling(secondBlockFrame); + NS_ASSERTION(aLineFrame->GetStyleContext() == firstLineStyle, + "Bogus style context on line frame"); } - 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 after reparenting them - kid = firstInlineFrame; - NS_ASSERTION(lineFrame->GetStyleContext() == firstLineStyle, - "Bogus style context on line frame"); - while (kid) { - ReparentFrame(aState.mFrameManager, lineFrame, kid); - kid = kid->GetNextSibling(); + ReparentFrames(aState.mFrameManager, aLineFrame, firstLineChildren); + if (aLineFrame->GetChildList(nsnull).IsEmpty() && + (aLineFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) { + aLineFrame->SetInitialChildList(nsnull, firstLineChildren); + } else { + aState.mFrameManager->AppendFrames(aLineFrame, nsnull, + firstLineChildren.FirstChild()); } - lineFrame->SetInitialChildList(nsnull, firstInlineFrame); } else { rv = NS_ERROR_OUT_OF_MEMORY; @@ -9723,66 +9712,25 @@ nsCSSFrameConstructor::AppendFirstLineFrames( { // It's possible that aBlockFrame needs to have a first-line frame // created because it doesn't currently have any children. - nsIFrame* blockKid = aBlockFrame->GetFirstChild(nsnull); - if (!blockKid) { + const nsFrameList& blockKids = aBlockFrame->GetChildList(nsnull); + if (blockKids.IsEmpty()) { return WrapFramesInFirstLineFrame(aState, aBlockContent, - aBlockFrame, aFrameItems); + aBlockFrame, nsnull, aFrameItems); } // Examine the last block child - if it's a first-line frame then // appended frames need special treatment. - nsresult rv = NS_OK; - nsFrameList blockFrames(blockKid); - nsIFrame* lastBlockKid = blockFrames.LastChild(); + nsIFrame* lastBlockKid = blockKids.LastChild(); if (lastBlockKid->GetType() != nsGkAtoms::lineFrame) { // 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 // treatment of the appended frames. - return rv; - } - 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(); + return NS_OK; } - // If we don't find any inline frames, then there is nothing to do - if (!firstInlineFrame) { - 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; + return WrapFramesInFirstLineFrame(aState, aBlockContent, aBlockFrame, + lastBlockKid, aFrameItems); } // 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!"); - nsIFrame* insertAfter = nsnull; // 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 - for (nsFrameList::Enumerator e(aState.mFloatedItems); !e.AtEnd(); e.Next()) { - if (e.get()->GetParent() == aBlockFrame) - break; - insertAfter = e.get(); + nsFrameList::FrameLinkEnumerator link(aState.mFloatedItems); + while (!link.AtEnd() && link.NextFrame()->GetParent() != aBlockFrame) { + link.Next(); } rv = aState.AddChild(letterFrame, aResult, letterContent, aStyleContext, aParentFrame, PR_FALSE, PR_TRUE, PR_FALSE, PR_TRUE, - insertAfter); + link.PrevFrame()); if (nextTextFrame) { if (NS_FAILED(rv)) { diff --git a/layout/base/nsCSSFrameConstructor.h b/layout/base/nsCSSFrameConstructor.h index 76de987395d..40f7eeb8259 100644 --- a/layout/base/nsCSSFrameConstructor.h +++ b/layout/base/nsCSSFrameConstructor.h @@ -87,6 +87,13 @@ struct nsFrameItems : public nsFrameList { 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 void AddChild(nsIFrame* aChild); @@ -133,12 +140,32 @@ struct nsFrameItems : public nsFrameList { 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() { mFirstChild = lastChild = nsnull; } // For now, until we change some SetInitialChildList signatures 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 @@ -1590,11 +1617,21 @@ private: // 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, nsIContent* aBlockContent, nsIFrame* aBlockFrame, + nsIFrame* aLineFrame, 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, nsIContent* aContent, nsIFrame* aBlockFrame, diff --git a/layout/generic/nsFrameList.cpp b/layout/generic/nsFrameList.cpp index 16b523c175f..a27fc2abd7d 100644 --- a/layout/generic/nsFrameList.cpp +++ b/layout/generic/nsFrameList.cpp @@ -266,20 +266,29 @@ nsFrameList::InsertFrames(nsIFrame* aParent, #endif } -PRBool -nsFrameList::Split(nsIFrame* aAfterFrame, nsIFrame** aNextFrameResult) +nsFrameList +nsFrameList::ExtractHead(FrameLinkEnumerator& aLink) { - NS_PRECONDITION(nsnull != aAfterFrame, "null ptr"); - NS_PRECONDITION(nsnull != aNextFrameResult, "null ptr"); - NS_ASSERTION(ContainsFrame(aAfterFrame), "split after unknown frame"); + NS_PRECONDITION(&aLink.List() == this, "Unexpected list"); + NS_PRECONDITION(!aLink.PrevFrame() || + aLink.PrevFrame()->GetNextSibling() == + aLink.NextFrame(), + "Unexpected PrevFrame()"); - if (aNextFrameResult && aAfterFrame) { - nsIFrame* nextFrame = aAfterFrame->GetNextSibling(); - aAfterFrame->SetNextSibling(nsnull); - *aNextFrameResult = nextFrame; - return PR_TRUE; + nsIFrame* prev = aLink.PrevFrame(); + nsIFrame* newFirstFrame = nsnull; + if (prev) { + // Truncate the list after |prev| and hand the first part to our new list. + 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* diff --git a/layout/generic/nsFrameList.h b/layout/generic/nsFrameList.h index 41217d76534..ca61f96a5db 100644 --- a/layout/generic/nsFrameList.h +++ b/layout/generic/nsFrameList.h @@ -172,7 +172,13 @@ public: inline Slice InsertFrames(nsIFrame* aParent, nsIFrame* aPrevSibling, 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 @@ -310,7 +316,11 @@ public: nsIFrame* get() const { return mFrame; } - private: +#ifdef DEBUG + const nsFrameList& List() const { return mSlice.mList; } +#endif + + protected: #ifdef DEBUG const Slice& mSlice; #endif @@ -319,6 +329,43 @@ public: // 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: #ifdef DEBUG void CheckForLoops();