From eed697cb686a77e5bf117aafbecb3eb350d4185b Mon Sep 17 00:00:00 2001 From: uid502 Date: Wed, 15 Oct 2003 00:49:03 +0000 Subject: [PATCH] Bug 217604. Ensure out-of-flow frames are ordered in content order. Ensure views are maintained in frame/content order. git-svn-id: svn://10.0.0.236/trunk@147976 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/layout/base/nsCSSFrameConstructor.cpp | 212 +++++++----------- mozilla/layout/base/nsLayoutUtils.cpp | 124 ++++++++++ mozilla/layout/base/nsLayoutUtils.h | 37 +++ mozilla/layout/base/public/nsLayoutUtils.h | 37 +++ mozilla/layout/base/src/nsLayoutUtils.cpp | 124 ++++++++++ .../layout/generic/nsHTMLContainerFrame.cpp | 15 +- .../html/base/src/nsHTMLContainerFrame.cpp | 15 +- .../html/style/src/nsCSSFrameConstructor.cpp | 212 +++++++----------- 8 files changed, 494 insertions(+), 282 deletions(-) diff --git a/mozilla/layout/base/nsCSSFrameConstructor.cpp b/mozilla/layout/base/nsCSSFrameConstructor.cpp index 19084606d0e..291358ce15c 100644 --- a/mozilla/layout/base/nsCSSFrameConstructor.cpp +++ b/mozilla/layout/base/nsCSSFrameConstructor.cpp @@ -8053,6 +8053,74 @@ GetAdjustedParentFrame(nsIPresContext* aPresContext, return (newParent) ? newParent : aParentFrame; } +static nsresult InsertOutOfFlow(nsIPresContext* aPresContext, + const nsAbsoluteItems& aFrameItems, + nsIAtom* aChildListName) { + if (!aFrameItems.childList) { + return NS_OK; + } + + nsIFrame* firstChild; + aFrameItems.containingBlock->FirstChild(aPresContext, aChildListName, &firstChild); + + // Note that whether the frame construction context is doing an append or + // not is not helpful here, since it could be appending to some frame in + // the middle of the document, which means we're not necessarily appending + // to the children of the containing block. + // + // We need to make sure the 'append to the end of document' case is fast. + // So first test the last child of the containing block + nsIFrame* lastChild = nsLayoutUtils::GetLastSibling(firstChild); + if (lastChild) { + if (nsLayoutUtils::CompareTreePosition(lastChild->GetContent(), + aFrameItems.childList->GetContent(), + aFrameItems.containingBlock->GetContent()) < 0) { + // lastChild comes before the new children, so insert after lastChild + return aFrameItems.containingBlock-> + AppendFrames(aPresContext, *aPresContext->GetPresShell(), + aChildListName, aFrameItems.childList); + } + } + + nsIFrame* insertionPoint = nsnull; + // try the other children + for (nsIFrame* f = firstChild; f != lastChild; f = f->GetNextSibling()) { + if (nsLayoutUtils::CompareTreePosition(f->GetContent(), + aFrameItems.childList->GetContent(), + aFrameItems.containingBlock->GetContent()) > 0) { + // f comes after the new children, so stop here and insert after + // the previous frame + break; + } + insertionPoint = f; + } + + return aFrameItems.containingBlock-> + InsertFrames(aPresContext, *aPresContext->GetPresShell(), + aChildListName, insertionPoint, aFrameItems.childList); +} + +static nsresult +InsertOutOfFlowFrames(const nsFrameConstructorState& aState, + nsIPresContext* aPresContext) { + // If there are new absolutely positioned child frames, then notify + // the parent + nsresult rv = InsertOutOfFlow(aPresContext, aState.mAbsoluteItems, + nsLayoutAtoms::absoluteList); + NS_ENSURE_SUCCESS(rv, rv); + + // If there are new fixed positioned child frames, then notify + // the parent + rv = InsertOutOfFlow(aPresContext, aState.mFixedItems, + nsLayoutAtoms::fixedList); + NS_ENSURE_SUCCESS(rv, rv); + + // If there are new floating child frames, then notify + // the parent + return InsertOutOfFlow(aPresContext, aState.mFloatedItems, + nsLayoutAtoms::floatList); +} + NS_IMETHODIMP nsCSSFrameConstructor::ContentAppended(nsIPresContext* aPresContext, nsIContent* aContainer, @@ -8390,35 +8458,7 @@ nsCSSFrameConstructor::ContentAppended(nsIPresContext* aPresContext, parentFrame, firstAppendedFrame); } - // If there are new absolutely positioned child frames, then notify - // the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mAbsoluteItems.childList) { - state.mAbsoluteItems.containingBlock->AppendFrames(aPresContext, *shell, - nsLayoutAtoms::absoluteList, - state.mAbsoluteItems.childList); - } - - // If there are new fixed positioned child frames, then notify - // the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mFixedItems.childList) { - state.mFixedItems.containingBlock->AppendFrames(aPresContext, *shell, - nsLayoutAtoms::fixedList, - state.mFixedItems.childList); - } - - // If there are new floating child frames, then notify - // the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mFloatedItems.childList) { - state.mFloatedItems.containingBlock->AppendFrames(aPresContext, *shell, - nsLayoutAtoms::floatList, - state.mFloatedItems.childList); - } + InsertOutOfFlowFrames(state, aPresContext); // Recover first-letter frames if (haveFirstLetterStyle) { @@ -9077,36 +9117,8 @@ nsCSSFrameConstructor::ContentInserted(nsIPresContext* aPresContext, nsnull, prevSibling, newFrame); } } - - // If there are new absolutely positioned child frames, then notify - // the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mAbsoluteItems.childList) { - state.mAbsoluteItems.containingBlock->AppendFrames(aPresContext, *shell, - nsLayoutAtoms::absoluteList, - state.mAbsoluteItems.childList); - } - - // If there are new fixed positioned child frames, then notify - // the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mFixedItems.childList) { - rv = state.mFixedItems.containingBlock->AppendFrames(aPresContext, *shell, - nsLayoutAtoms::fixedList, - state.mFixedItems.childList); - } - - // If there are new floating child frames, then notify - // the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mFloatedItems.childList) { - state.mFloatedItems.containingBlock->AppendFrames(aPresContext, *shell, - nsLayoutAtoms::floatList, - state.mFloatedItems.childList); - } + + InsertOutOfFlowFrames(state, aPresContext); if (haveFirstLetterStyle) { // Recover the letter frames for the containing block when @@ -9156,23 +9168,6 @@ nsCSSFrameConstructor::ContentReplaced(nsIPresContext* aPresContext, return res; } -// Returns PR_TRUE if aAncestorFrame is an ancestor frame of aFrame -static PRBool -IsAncestorFrame(nsIFrame* aFrame, nsIFrame* aAncestorFrame) -{ - nsIFrame* parentFrame = aFrame->GetParent(); - - while (parentFrame) { - if (parentFrame == aAncestorFrame) { - return PR_TRUE; - } - - parentFrame = parentFrame->GetParent(); - } - - return PR_FALSE; -} - /** * Called when a frame subtree is about to be deleted. Two important * things happen: @@ -9244,7 +9239,8 @@ DoDeletingFrameSubtree(nsIPresContext* aPresContext, // If aRemovedFrame is an ancestor of the out-of-flow frame, then // the out-of-flow frame will be destroyed by aRemovedFrame. const nsStyleDisplay* display = outOfFlowFrame->GetStyleDisplay(); - if (display->mDisplay == NS_STYLE_DISPLAY_POPUP || !IsAncestorFrame(outOfFlowFrame, aRemovedFrame)) { + if (display->mDisplay == NS_STYLE_DISPLAY_POPUP || + !nsLayoutUtils::IsProperAncestorFrame(aRemovedFrame, outOfFlowFrame)) { if (aDestroyQueue.IndexOf(outOfFlowFrame) < 0) aDestroyQueue.AppendElement(outOfFlowFrame); } @@ -10720,37 +10716,8 @@ nsCSSFrameConstructor::CantRenderReplacedElement(nsIPresShell* aPresShell, // ConstructFrameByDisplayType() has done this state.mFrameManager->SetPrimaryFrameFor(content, newFrame); - // If there are new absolutely positioned child frames, then notify - // the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mAbsoluteItems.childList) { - rv = state.mAbsoluteItems.containingBlock->AppendFrames(aPresContext, *presShell, - nsLayoutAtoms::absoluteList, - state.mAbsoluteItems.childList); - } - - // If there are new fixed positioned child frames, then notify - // the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mFixedItems.childList) { - rv = state.mFixedItems.containingBlock->AppendFrames(aPresContext, *presShell, - nsLayoutAtoms::fixedList, - state.mFixedItems.childList); - } - - // If there are new floating child frames, then notify the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mFloatedItems.childList) { - rv = state.mFloatedItems.containingBlock->AppendFrames(aPresContext, - *presShell, - nsLayoutAtoms::floatList, - state.mFloatedItems.childList); - } + InsertOutOfFlowFrames(state, aPresContext); } - } else if (nsHTMLAtoms::input == tag.get()) { // XXX image INPUT elements are also image frames, but don't throw away the // image frame, because the frame class has extra logic that is specific to @@ -12785,35 +12752,8 @@ nsCSSFrameConstructor::CreateListBoxContent(nsIPresContext* aPresContext, rv = ((nsListBoxBodyFrame*)aParentFrame)->ListBoxAppendFrames(newFrame); else rv = ((nsListBoxBodyFrame*)aParentFrame)->ListBoxInsertFrames(aPrevFrame, newFrame); - // If there are new absolutely positioned child frames, then notify - // the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mAbsoluteItems.childList) { - rv = state.mAbsoluteItems.containingBlock->AppendFrames(aPresContext, *shell, - nsLayoutAtoms::absoluteList, - state.mAbsoluteItems.childList); - } - - // If there are new fixed positioned child frames, then notify - // the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mFixedItems.childList) { - rv = state.mFixedItems.containingBlock->AppendFrames(aPresContext, *shell, - nsLayoutAtoms::fixedList, - state.mFixedItems.childList); - } - - // If there are new floating child frames, then notify - // the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mFloatedItems.childList) { - rv = state.mFloatedItems.containingBlock->AppendFrames(aPresContext, *shell, - nsLayoutAtoms::floatList, - state.mFloatedItems.childList); - } + + InsertOutOfFlowFrames(state, aPresContext); } } diff --git a/mozilla/layout/base/nsLayoutUtils.cpp b/mozilla/layout/base/nsLayoutUtils.cpp index c958183f588..ff445a28866 100644 --- a/mozilla/layout/base/nsLayoutUtils.cpp +++ b/mozilla/layout/base/nsLayoutUtils.cpp @@ -43,6 +43,7 @@ #include "nsLayoutAtoms.h" #include "nsIAtom.h" #include "nsCSSPseudoElements.h" +#include "nsIView.h" /** * A namespace class for static layout utilities. @@ -199,3 +200,126 @@ nsLayoutUtils::IsGeneratedContentFor(nsIContent* aContent, return pseudoType == aPseudoElement; } +// static +PRBool +nsLayoutUtils::IsProperAncestorFrame(nsIFrame* aAncestorFrame, nsIFrame* aFrame, + nsIFrame* aCommonAncestor) +{ + if (aFrame == aCommonAncestor) { + return PR_FALSE; + } + + nsIFrame* parentFrame = aFrame->GetParent(); + + while (parentFrame != aCommonAncestor) { + if (parentFrame == aAncestorFrame) { + return PR_TRUE; + } + + parentFrame = parentFrame->GetParent(); + } + + return PR_FALSE; +} + +// static +PRInt32 +nsLayoutUtils::CompareTreePosition(nsIContent* aContent1, nsIContent* aContent2, + nsIContent* aCommonAncestor) { + NS_PRECONDITION(aContent1, "aContent1 must not be null"); + NS_PRECONDITION(aContent2, "aContent2 must not be null"); + + nsAutoVoidArray content1Ancestors; + nsAutoVoidArray content2Ancestors; + nsIContent* c; + + for (c = aContent1; c != aCommonAncestor; c = c->GetParent()) { + content1Ancestors.AppendElement(c); + } + for (c = aContent2; c != aCommonAncestor; c = c->GetParent()) { + content2Ancestors.AppendElement(c); + } + + int last1 = content1Ancestors.Count() - 1; + int last2 = content2Ancestors.Count() - 1; + nsIContent* content1Ancestor = nsnull; + nsIContent* content2Ancestor = nsnull; + while (last1 >= 0 && last2 >= 0 + && ((content1Ancestor = NS_STATIC_CAST(nsIContent*, content1Ancestors.ElementAt(last1))) + == (content2Ancestor = NS_STATIC_CAST(nsIContent*, content2Ancestors.ElementAt(last2))))) { + last1--; + last2--; + } + + if (last1 < 0) { + if (last2 < 0) { + NS_ASSERTION(aContent1 == aContent2, "internal error?"); + return 0; + } else { + // aContent1 is an ancestor of aContent2 + return -1; + } + } else { + if (last2 < 0) { + // aContent2 is an ancestor of aContent1 + return 1; + } else { + // content1Ancestor != content2Ancestor, so they must be siblings with the same parent + nsIContent* parent = content1Ancestor->GetParent(); + NS_ASSERTION(parent, "no common ancestor at all???"); + if (!parent) { // different documents?? + return 0; + } + + PRInt32 index1 = parent->IndexOf(content1Ancestor); + PRInt32 index2 = parent->IndexOf(content2Ancestor); + if (index1 < 0 || index2 < 0) { + // one of them must be anonymous; we can't determine the order + return 0; + } + + return index1 - index2; + } + } +} + +// static +nsIFrame* nsLayoutUtils::GetLastSibling(nsIFrame* aFrame) { + if (!aFrame) { + return nsnull; + } + + nsIFrame* next; + while ((next = aFrame->GetNextSibling()) != nsnull) { + aFrame = next; + } + return aFrame; +} + +// static +nsIView* +nsLayoutUtils::FindSiblingViewFor(nsIView* aParentView, nsIFrame* aFrame) { + nsIFrame* parentViewFrame = NS_STATIC_CAST(nsIFrame*, aParentView->GetClientData()); + nsIContent* parentViewContent = parentViewFrame ? parentViewFrame->GetContent() : nsnull; + for (nsIView* insertBefore = aParentView->GetFirstChild(); insertBefore; + insertBefore = insertBefore->GetNextSibling()) { + nsIFrame* f = NS_STATIC_CAST(nsIFrame*, insertBefore->GetClientData()); + if (!f) { + // this view could be some anonymous view attached to a meaningful parent + for (nsIView* searchView = insertBefore->GetParent(); searchView; + searchView = searchView->GetParent()) { + f = NS_STATIC_CAST(nsIFrame*, searchView->GetClientData()); + if (f) { + break; + } + } + NS_ASSERTION(f, "Can't find a frame anywhere!"); + } + if (f && CompareTreePosition(aFrame->GetContent(), + f->GetContent(), parentViewContent) > 0) { + // aFrame's content is after f's content, so put our view before f's view + return insertBefore; + } + } + return nsnull; +} diff --git a/mozilla/layout/base/nsLayoutUtils.h b/mozilla/layout/base/nsLayoutUtils.h index 5b65e44bf29..726aeaa4777 100644 --- a/mozilla/layout/base/nsLayoutUtils.h +++ b/mozilla/layout/base/nsLayoutUtils.h @@ -42,6 +42,7 @@ class nsIFrame; class nsIPresContext; class nsIContent; class nsIAtom; +class nsIView; #include "prtypes.h" #include "nsStyleContext.h" @@ -101,7 +102,43 @@ public: */ static PRBool IsGeneratedContentFor(nsIContent* aContent, nsIFrame* aFrame, nsIAtom* aPseudoElement); + + /** + * CompareTreePosition determines whether aContent1 comes before or after aContent2 in + * a preorder traversal of the content tree. + * + * @param aCommonAncestor either null, or a common ancestor of aContent1 and aContent2 + * @return < 0 if aContent1 is before aContent2, > 0 if aContent1 is after aContent2, + * 0 otherwise (meaning they're the same, or they're in different documents) + */ + static PRInt32 CompareTreePosition(nsIContent* aContent1, nsIContent* aContent2, + nsIContent* aCommonAncestor = nsnull); + /** + * GetLastSibling simply finds the last sibling of aFrame, or returns nsnull if + * aFrame is null. + */ + static nsIFrame* GetLastSibling(nsIFrame* aFrame); + + /** + * FindSiblingViewFor locates the child of aParentView that aFrame's + * view should be inserted 'above' (i.e., before in sibling view + * order). This is the first child view of aParentView whose + * corresponding content is before aFrame's content (view siblings + * are in reverse content order). + */ + static nsIView* FindSiblingViewFor(nsIView* aParentView, nsIFrame* aFrame); + + /** + * IsProperAncestorFrame checks whether aAncestorFrame is an ancestor + * of aFrame and not equal to aFrame. + * @param aCommonAncestor nsnull, or a common ancestor of aFrame and + * aAncestorFrame. If non-null, this can bound the search and speed up + * the function + */ + static PRBool IsProperAncestorFrame(nsIFrame* aAncestorFrame, nsIFrame* aFrame, + nsIFrame* aCommonAncestor = nsnull); + /** * HasPseudoStyle returns PR_TRUE if aContent (whose primary style * context is aStyleContext) has the aPseudoElement pseudo-style diff --git a/mozilla/layout/base/public/nsLayoutUtils.h b/mozilla/layout/base/public/nsLayoutUtils.h index 5b65e44bf29..726aeaa4777 100644 --- a/mozilla/layout/base/public/nsLayoutUtils.h +++ b/mozilla/layout/base/public/nsLayoutUtils.h @@ -42,6 +42,7 @@ class nsIFrame; class nsIPresContext; class nsIContent; class nsIAtom; +class nsIView; #include "prtypes.h" #include "nsStyleContext.h" @@ -101,7 +102,43 @@ public: */ static PRBool IsGeneratedContentFor(nsIContent* aContent, nsIFrame* aFrame, nsIAtom* aPseudoElement); + + /** + * CompareTreePosition determines whether aContent1 comes before or after aContent2 in + * a preorder traversal of the content tree. + * + * @param aCommonAncestor either null, or a common ancestor of aContent1 and aContent2 + * @return < 0 if aContent1 is before aContent2, > 0 if aContent1 is after aContent2, + * 0 otherwise (meaning they're the same, or they're in different documents) + */ + static PRInt32 CompareTreePosition(nsIContent* aContent1, nsIContent* aContent2, + nsIContent* aCommonAncestor = nsnull); + /** + * GetLastSibling simply finds the last sibling of aFrame, or returns nsnull if + * aFrame is null. + */ + static nsIFrame* GetLastSibling(nsIFrame* aFrame); + + /** + * FindSiblingViewFor locates the child of aParentView that aFrame's + * view should be inserted 'above' (i.e., before in sibling view + * order). This is the first child view of aParentView whose + * corresponding content is before aFrame's content (view siblings + * are in reverse content order). + */ + static nsIView* FindSiblingViewFor(nsIView* aParentView, nsIFrame* aFrame); + + /** + * IsProperAncestorFrame checks whether aAncestorFrame is an ancestor + * of aFrame and not equal to aFrame. + * @param aCommonAncestor nsnull, or a common ancestor of aFrame and + * aAncestorFrame. If non-null, this can bound the search and speed up + * the function + */ + static PRBool IsProperAncestorFrame(nsIFrame* aAncestorFrame, nsIFrame* aFrame, + nsIFrame* aCommonAncestor = nsnull); + /** * HasPseudoStyle returns PR_TRUE if aContent (whose primary style * context is aStyleContext) has the aPseudoElement pseudo-style diff --git a/mozilla/layout/base/src/nsLayoutUtils.cpp b/mozilla/layout/base/src/nsLayoutUtils.cpp index c958183f588..ff445a28866 100644 --- a/mozilla/layout/base/src/nsLayoutUtils.cpp +++ b/mozilla/layout/base/src/nsLayoutUtils.cpp @@ -43,6 +43,7 @@ #include "nsLayoutAtoms.h" #include "nsIAtom.h" #include "nsCSSPseudoElements.h" +#include "nsIView.h" /** * A namespace class for static layout utilities. @@ -199,3 +200,126 @@ nsLayoutUtils::IsGeneratedContentFor(nsIContent* aContent, return pseudoType == aPseudoElement; } +// static +PRBool +nsLayoutUtils::IsProperAncestorFrame(nsIFrame* aAncestorFrame, nsIFrame* aFrame, + nsIFrame* aCommonAncestor) +{ + if (aFrame == aCommonAncestor) { + return PR_FALSE; + } + + nsIFrame* parentFrame = aFrame->GetParent(); + + while (parentFrame != aCommonAncestor) { + if (parentFrame == aAncestorFrame) { + return PR_TRUE; + } + + parentFrame = parentFrame->GetParent(); + } + + return PR_FALSE; +} + +// static +PRInt32 +nsLayoutUtils::CompareTreePosition(nsIContent* aContent1, nsIContent* aContent2, + nsIContent* aCommonAncestor) { + NS_PRECONDITION(aContent1, "aContent1 must not be null"); + NS_PRECONDITION(aContent2, "aContent2 must not be null"); + + nsAutoVoidArray content1Ancestors; + nsAutoVoidArray content2Ancestors; + nsIContent* c; + + for (c = aContent1; c != aCommonAncestor; c = c->GetParent()) { + content1Ancestors.AppendElement(c); + } + for (c = aContent2; c != aCommonAncestor; c = c->GetParent()) { + content2Ancestors.AppendElement(c); + } + + int last1 = content1Ancestors.Count() - 1; + int last2 = content2Ancestors.Count() - 1; + nsIContent* content1Ancestor = nsnull; + nsIContent* content2Ancestor = nsnull; + while (last1 >= 0 && last2 >= 0 + && ((content1Ancestor = NS_STATIC_CAST(nsIContent*, content1Ancestors.ElementAt(last1))) + == (content2Ancestor = NS_STATIC_CAST(nsIContent*, content2Ancestors.ElementAt(last2))))) { + last1--; + last2--; + } + + if (last1 < 0) { + if (last2 < 0) { + NS_ASSERTION(aContent1 == aContent2, "internal error?"); + return 0; + } else { + // aContent1 is an ancestor of aContent2 + return -1; + } + } else { + if (last2 < 0) { + // aContent2 is an ancestor of aContent1 + return 1; + } else { + // content1Ancestor != content2Ancestor, so they must be siblings with the same parent + nsIContent* parent = content1Ancestor->GetParent(); + NS_ASSERTION(parent, "no common ancestor at all???"); + if (!parent) { // different documents?? + return 0; + } + + PRInt32 index1 = parent->IndexOf(content1Ancestor); + PRInt32 index2 = parent->IndexOf(content2Ancestor); + if (index1 < 0 || index2 < 0) { + // one of them must be anonymous; we can't determine the order + return 0; + } + + return index1 - index2; + } + } +} + +// static +nsIFrame* nsLayoutUtils::GetLastSibling(nsIFrame* aFrame) { + if (!aFrame) { + return nsnull; + } + + nsIFrame* next; + while ((next = aFrame->GetNextSibling()) != nsnull) { + aFrame = next; + } + return aFrame; +} + +// static +nsIView* +nsLayoutUtils::FindSiblingViewFor(nsIView* aParentView, nsIFrame* aFrame) { + nsIFrame* parentViewFrame = NS_STATIC_CAST(nsIFrame*, aParentView->GetClientData()); + nsIContent* parentViewContent = parentViewFrame ? parentViewFrame->GetContent() : nsnull; + for (nsIView* insertBefore = aParentView->GetFirstChild(); insertBefore; + insertBefore = insertBefore->GetNextSibling()) { + nsIFrame* f = NS_STATIC_CAST(nsIFrame*, insertBefore->GetClientData()); + if (!f) { + // this view could be some anonymous view attached to a meaningful parent + for (nsIView* searchView = insertBefore->GetParent(); searchView; + searchView = searchView->GetParent()) { + f = NS_STATIC_CAST(nsIFrame*, searchView->GetClientData()); + if (f) { + break; + } + } + NS_ASSERTION(f, "Can't find a frame anywhere!"); + } + if (f && CompareTreePosition(aFrame->GetContent(), + f->GetContent(), parentViewContent) > 0) { + // aFrame's content is after f's content, so put our view before f's view + return insertBefore; + } + } + return nsnull; +} diff --git a/mozilla/layout/generic/nsHTMLContainerFrame.cpp b/mozilla/layout/generic/nsHTMLContainerFrame.cpp index c3207649445..c8cb88396af 100644 --- a/mozilla/layout/generic/nsHTMLContainerFrame.cpp +++ b/mozilla/layout/generic/nsHTMLContainerFrame.cpp @@ -42,6 +42,7 @@ #include "nsStyleConsts.h" #include "nsIContent.h" #include "nsLayoutAtoms.h" +#include "nsLayoutUtils.h" #include "nsCSSAnonBoxes.h" #include "nsIWidget.h" #include "nsILinkHandler.h" @@ -376,8 +377,8 @@ ReparentFrameViewTo(nsIPresContext* aPresContext, aViewManager->RemoveChild(view); // The view will remember the Z-order and other attributes that have been set on it. - // XXX Pretend this view is last of the parent's views in document order - aViewManager->InsertChild(aNewParentView, view, nsnull, PR_TRUE); + nsIView* insertBefore = nsLayoutUtils::FindSiblingViewFor(aNewParentView, aFrame); + aViewManager->InsertChild(aNewParentView, view, insertBefore, insertBefore != nsnull); } else { PRInt32 listIndex = 0; nsCOMPtr listName; @@ -582,13 +583,17 @@ nsHTMLContainerFrame::CreateViewForFrame(nsIPresContext* aPresContext, if (NS_SUCCEEDED(CallQueryInterface(parentView, &scrollingView))) { scrollingView->SetScrolledView(view); } else { - // XXX Drop it at the end of the document order until we can do better - viewManager->InsertChild(parentView, view, nsnull, PR_TRUE); + nsIView* insertBefore = nsLayoutUtils::FindSiblingViewFor(parentView, aFrame); + // we insert this view 'above' the insertBefore view, unless insertBefore is null, + // in which case we want to call with aAbove == PR_FALSE to insert at the beginning + // in document order + viewManager->InsertChild(parentView, view, insertBefore, insertBefore != nsnull); if (nsnull != aContentParentFrame) { nsIView* zParentView = aContentParentFrame->GetClosestView(); if (zParentView != parentView) { - viewManager->InsertZPlaceholder(zParentView, view, nsnull, PR_TRUE); + insertBefore = nsLayoutUtils::FindSiblingViewFor(zParentView, aFrame); + viewManager->InsertZPlaceholder(zParentView, view, insertBefore, insertBefore != nsnull); } } } diff --git a/mozilla/layout/html/base/src/nsHTMLContainerFrame.cpp b/mozilla/layout/html/base/src/nsHTMLContainerFrame.cpp index c3207649445..c8cb88396af 100644 --- a/mozilla/layout/html/base/src/nsHTMLContainerFrame.cpp +++ b/mozilla/layout/html/base/src/nsHTMLContainerFrame.cpp @@ -42,6 +42,7 @@ #include "nsStyleConsts.h" #include "nsIContent.h" #include "nsLayoutAtoms.h" +#include "nsLayoutUtils.h" #include "nsCSSAnonBoxes.h" #include "nsIWidget.h" #include "nsILinkHandler.h" @@ -376,8 +377,8 @@ ReparentFrameViewTo(nsIPresContext* aPresContext, aViewManager->RemoveChild(view); // The view will remember the Z-order and other attributes that have been set on it. - // XXX Pretend this view is last of the parent's views in document order - aViewManager->InsertChild(aNewParentView, view, nsnull, PR_TRUE); + nsIView* insertBefore = nsLayoutUtils::FindSiblingViewFor(aNewParentView, aFrame); + aViewManager->InsertChild(aNewParentView, view, insertBefore, insertBefore != nsnull); } else { PRInt32 listIndex = 0; nsCOMPtr listName; @@ -582,13 +583,17 @@ nsHTMLContainerFrame::CreateViewForFrame(nsIPresContext* aPresContext, if (NS_SUCCEEDED(CallQueryInterface(parentView, &scrollingView))) { scrollingView->SetScrolledView(view); } else { - // XXX Drop it at the end of the document order until we can do better - viewManager->InsertChild(parentView, view, nsnull, PR_TRUE); + nsIView* insertBefore = nsLayoutUtils::FindSiblingViewFor(parentView, aFrame); + // we insert this view 'above' the insertBefore view, unless insertBefore is null, + // in which case we want to call with aAbove == PR_FALSE to insert at the beginning + // in document order + viewManager->InsertChild(parentView, view, insertBefore, insertBefore != nsnull); if (nsnull != aContentParentFrame) { nsIView* zParentView = aContentParentFrame->GetClosestView(); if (zParentView != parentView) { - viewManager->InsertZPlaceholder(zParentView, view, nsnull, PR_TRUE); + insertBefore = nsLayoutUtils::FindSiblingViewFor(zParentView, aFrame); + viewManager->InsertZPlaceholder(zParentView, view, insertBefore, insertBefore != nsnull); } } } diff --git a/mozilla/layout/html/style/src/nsCSSFrameConstructor.cpp b/mozilla/layout/html/style/src/nsCSSFrameConstructor.cpp index 19084606d0e..291358ce15c 100644 --- a/mozilla/layout/html/style/src/nsCSSFrameConstructor.cpp +++ b/mozilla/layout/html/style/src/nsCSSFrameConstructor.cpp @@ -8053,6 +8053,74 @@ GetAdjustedParentFrame(nsIPresContext* aPresContext, return (newParent) ? newParent : aParentFrame; } +static nsresult InsertOutOfFlow(nsIPresContext* aPresContext, + const nsAbsoluteItems& aFrameItems, + nsIAtom* aChildListName) { + if (!aFrameItems.childList) { + return NS_OK; + } + + nsIFrame* firstChild; + aFrameItems.containingBlock->FirstChild(aPresContext, aChildListName, &firstChild); + + // Note that whether the frame construction context is doing an append or + // not is not helpful here, since it could be appending to some frame in + // the middle of the document, which means we're not necessarily appending + // to the children of the containing block. + // + // We need to make sure the 'append to the end of document' case is fast. + // So first test the last child of the containing block + nsIFrame* lastChild = nsLayoutUtils::GetLastSibling(firstChild); + if (lastChild) { + if (nsLayoutUtils::CompareTreePosition(lastChild->GetContent(), + aFrameItems.childList->GetContent(), + aFrameItems.containingBlock->GetContent()) < 0) { + // lastChild comes before the new children, so insert after lastChild + return aFrameItems.containingBlock-> + AppendFrames(aPresContext, *aPresContext->GetPresShell(), + aChildListName, aFrameItems.childList); + } + } + + nsIFrame* insertionPoint = nsnull; + // try the other children + for (nsIFrame* f = firstChild; f != lastChild; f = f->GetNextSibling()) { + if (nsLayoutUtils::CompareTreePosition(f->GetContent(), + aFrameItems.childList->GetContent(), + aFrameItems.containingBlock->GetContent()) > 0) { + // f comes after the new children, so stop here and insert after + // the previous frame + break; + } + insertionPoint = f; + } + + return aFrameItems.containingBlock-> + InsertFrames(aPresContext, *aPresContext->GetPresShell(), + aChildListName, insertionPoint, aFrameItems.childList); +} + +static nsresult +InsertOutOfFlowFrames(const nsFrameConstructorState& aState, + nsIPresContext* aPresContext) { + // If there are new absolutely positioned child frames, then notify + // the parent + nsresult rv = InsertOutOfFlow(aPresContext, aState.mAbsoluteItems, + nsLayoutAtoms::absoluteList); + NS_ENSURE_SUCCESS(rv, rv); + + // If there are new fixed positioned child frames, then notify + // the parent + rv = InsertOutOfFlow(aPresContext, aState.mFixedItems, + nsLayoutAtoms::fixedList); + NS_ENSURE_SUCCESS(rv, rv); + + // If there are new floating child frames, then notify + // the parent + return InsertOutOfFlow(aPresContext, aState.mFloatedItems, + nsLayoutAtoms::floatList); +} + NS_IMETHODIMP nsCSSFrameConstructor::ContentAppended(nsIPresContext* aPresContext, nsIContent* aContainer, @@ -8390,35 +8458,7 @@ nsCSSFrameConstructor::ContentAppended(nsIPresContext* aPresContext, parentFrame, firstAppendedFrame); } - // If there are new absolutely positioned child frames, then notify - // the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mAbsoluteItems.childList) { - state.mAbsoluteItems.containingBlock->AppendFrames(aPresContext, *shell, - nsLayoutAtoms::absoluteList, - state.mAbsoluteItems.childList); - } - - // If there are new fixed positioned child frames, then notify - // the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mFixedItems.childList) { - state.mFixedItems.containingBlock->AppendFrames(aPresContext, *shell, - nsLayoutAtoms::fixedList, - state.mFixedItems.childList); - } - - // If there are new floating child frames, then notify - // the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mFloatedItems.childList) { - state.mFloatedItems.containingBlock->AppendFrames(aPresContext, *shell, - nsLayoutAtoms::floatList, - state.mFloatedItems.childList); - } + InsertOutOfFlowFrames(state, aPresContext); // Recover first-letter frames if (haveFirstLetterStyle) { @@ -9077,36 +9117,8 @@ nsCSSFrameConstructor::ContentInserted(nsIPresContext* aPresContext, nsnull, prevSibling, newFrame); } } - - // If there are new absolutely positioned child frames, then notify - // the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mAbsoluteItems.childList) { - state.mAbsoluteItems.containingBlock->AppendFrames(aPresContext, *shell, - nsLayoutAtoms::absoluteList, - state.mAbsoluteItems.childList); - } - - // If there are new fixed positioned child frames, then notify - // the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mFixedItems.childList) { - rv = state.mFixedItems.containingBlock->AppendFrames(aPresContext, *shell, - nsLayoutAtoms::fixedList, - state.mFixedItems.childList); - } - - // If there are new floating child frames, then notify - // the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mFloatedItems.childList) { - state.mFloatedItems.containingBlock->AppendFrames(aPresContext, *shell, - nsLayoutAtoms::floatList, - state.mFloatedItems.childList); - } + + InsertOutOfFlowFrames(state, aPresContext); if (haveFirstLetterStyle) { // Recover the letter frames for the containing block when @@ -9156,23 +9168,6 @@ nsCSSFrameConstructor::ContentReplaced(nsIPresContext* aPresContext, return res; } -// Returns PR_TRUE if aAncestorFrame is an ancestor frame of aFrame -static PRBool -IsAncestorFrame(nsIFrame* aFrame, nsIFrame* aAncestorFrame) -{ - nsIFrame* parentFrame = aFrame->GetParent(); - - while (parentFrame) { - if (parentFrame == aAncestorFrame) { - return PR_TRUE; - } - - parentFrame = parentFrame->GetParent(); - } - - return PR_FALSE; -} - /** * Called when a frame subtree is about to be deleted. Two important * things happen: @@ -9244,7 +9239,8 @@ DoDeletingFrameSubtree(nsIPresContext* aPresContext, // If aRemovedFrame is an ancestor of the out-of-flow frame, then // the out-of-flow frame will be destroyed by aRemovedFrame. const nsStyleDisplay* display = outOfFlowFrame->GetStyleDisplay(); - if (display->mDisplay == NS_STYLE_DISPLAY_POPUP || !IsAncestorFrame(outOfFlowFrame, aRemovedFrame)) { + if (display->mDisplay == NS_STYLE_DISPLAY_POPUP || + !nsLayoutUtils::IsProperAncestorFrame(aRemovedFrame, outOfFlowFrame)) { if (aDestroyQueue.IndexOf(outOfFlowFrame) < 0) aDestroyQueue.AppendElement(outOfFlowFrame); } @@ -10720,37 +10716,8 @@ nsCSSFrameConstructor::CantRenderReplacedElement(nsIPresShell* aPresShell, // ConstructFrameByDisplayType() has done this state.mFrameManager->SetPrimaryFrameFor(content, newFrame); - // If there are new absolutely positioned child frames, then notify - // the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mAbsoluteItems.childList) { - rv = state.mAbsoluteItems.containingBlock->AppendFrames(aPresContext, *presShell, - nsLayoutAtoms::absoluteList, - state.mAbsoluteItems.childList); - } - - // If there are new fixed positioned child frames, then notify - // the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mFixedItems.childList) { - rv = state.mFixedItems.containingBlock->AppendFrames(aPresContext, *presShell, - nsLayoutAtoms::fixedList, - state.mFixedItems.childList); - } - - // If there are new floating child frames, then notify the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mFloatedItems.childList) { - rv = state.mFloatedItems.containingBlock->AppendFrames(aPresContext, - *presShell, - nsLayoutAtoms::floatList, - state.mFloatedItems.childList); - } + InsertOutOfFlowFrames(state, aPresContext); } - } else if (nsHTMLAtoms::input == tag.get()) { // XXX image INPUT elements are also image frames, but don't throw away the // image frame, because the frame class has extra logic that is specific to @@ -12785,35 +12752,8 @@ nsCSSFrameConstructor::CreateListBoxContent(nsIPresContext* aPresContext, rv = ((nsListBoxBodyFrame*)aParentFrame)->ListBoxAppendFrames(newFrame); else rv = ((nsListBoxBodyFrame*)aParentFrame)->ListBoxInsertFrames(aPrevFrame, newFrame); - // If there are new absolutely positioned child frames, then notify - // the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mAbsoluteItems.childList) { - rv = state.mAbsoluteItems.containingBlock->AppendFrames(aPresContext, *shell, - nsLayoutAtoms::absoluteList, - state.mAbsoluteItems.childList); - } - - // If there are new fixed positioned child frames, then notify - // the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mFixedItems.childList) { - rv = state.mFixedItems.containingBlock->AppendFrames(aPresContext, *shell, - nsLayoutAtoms::fixedList, - state.mFixedItems.childList); - } - - // If there are new floating child frames, then notify - // the parent - // XXX We can't just assume these frames are being appended, we need to - // determine where in the list they should be inserted... - if (state.mFloatedItems.childList) { - rv = state.mFloatedItems.containingBlock->AppendFrames(aPresContext, *shell, - nsLayoutAtoms::floatList, - state.mFloatedItems.childList); - } + + InsertOutOfFlowFrames(state, aPresContext); } }