diff --git a/mozilla/layout/base/nsCSSFrameConstructor.cpp b/mozilla/layout/base/nsCSSFrameConstructor.cpp index 879b1c4979b..68617e143a5 100644 --- a/mozilla/layout/base/nsCSSFrameConstructor.cpp +++ b/mozilla/layout/base/nsCSSFrameConstructor.cpp @@ -1232,9 +1232,17 @@ nsFrameConstructorState::nsFrameConstructorState(nsIPresShell* aPresShell, nsFrameConstructorState::~nsFrameConstructorState() { + // Frame order comparison functions only work properly when the placeholders + // have been inserted into the frame tree. So for example if we have a new float + // containing the placeholder for a new abs-pos frame, and we process the abs-pos + // insertion first, then we won't be able to find the right place to insert in + // in the abs-pos list. So put floats in first, because they can contain placeholders + // for abs-pos and fixed-pos items whose containing blocks are outside the floats. + // Then put abs-pos frames in, because they can contain placeholders for fixed-pos + // items whose containing block is outside the abs-pos frames. + ProcessFrameInsertions(mFloatedItems, nsGkAtoms::floatList); ProcessFrameInsertions(mAbsoluteItems, nsGkAtoms::absoluteList); ProcessFrameInsertions(mFixedItems, nsGkAtoms::fixedList); - ProcessFrameInsertions(mFloatedItems, nsGkAtoms::floatList); } static nsIFrame* @@ -1473,19 +1481,20 @@ nsFrameConstructorState::ProcessFrameInsertions(nsAbsoluteItems& aFrameItems, // So first test the last child of the containing block nsIFrame* lastChild = nsLayoutUtils::GetLastSibling(firstChild); + // CompareTreePosition uses placeholder hierarchy for out of flow frames, + // so this will make out-of-flows respect the ordering of placeholders, + // which is great because it takes care of anonymous content. if (!lastChild || - nsLayoutUtils::CompareTreePosition(lastChild->GetContent(), - firstNewFrame->GetContent(), - containingBlock->GetContent()) < 0) { + nsLayoutUtils::CompareTreePosition(lastChild, firstNewFrame, containingBlock) < 0) { // no lastChild, or lastChild comes before the new children, so just append rv = containingBlock->AppendFrames(aChildListName, firstNewFrame); } else { nsIFrame* insertionPoint = nsnull; // try the other children for (nsIFrame* f = firstChild; f != lastChild; f = f->GetNextSibling()) { - if (nsLayoutUtils::CompareTreePosition(f->GetContent(), - firstNewFrame->GetContent(), - containingBlock->GetContent()) > 0) { + PRInt32 compare = + nsLayoutUtils::CompareTreePosition(f, firstNewFrame, containingBlock); + if (compare > 0) { // f comes after the new children, so stop here and insert after // the previous frame break; diff --git a/mozilla/layout/base/nsLayoutUtils.cpp b/mozilla/layout/base/nsLayoutUtils.cpp index e4a1c33da0e..f9d86682987 100644 --- a/mozilla/layout/base/nsLayoutUtils.cpp +++ b/mozilla/layout/base/nsLayoutUtils.cpp @@ -360,6 +360,96 @@ nsLayoutUtils::DoCompareTreePosition(nsIContent* aContent1, return index1 - index2; } +static nsIFrame* FillAncestors(nsIFrame* aFrame, + nsIFrame* aStopAtAncestor, nsFrameManager* aFrameManager, + nsTArray* aAncestors) +{ + while (aFrame && aFrame != aStopAtAncestor) { + aAncestors->AppendElement(aFrame); + aFrame = nsLayoutUtils::GetParentOrPlaceholderFor(aFrameManager, aFrame); + } + return aFrame; +} + +// Return true if aFrame1 is after aFrame2 +static PRBool IsFrameAfter(nsIFrame* aFrame1, nsIFrame* aFrame2) +{ + nsIFrame* f = aFrame2; + do { + f = f->GetNextSibling(); + if (f == aFrame1) + return PR_TRUE; + } while (f); + return PR_FALSE; +} + +// static +PRInt32 +nsLayoutUtils::DoCompareTreePosition(nsIFrame* aFrame1, + nsIFrame* aFrame2, + PRInt32 aIf1Ancestor, + PRInt32 aIf2Ancestor, + nsIFrame* aCommonAncestor) +{ + NS_PRECONDITION(aFrame1, "aFrame1 must not be null"); + NS_PRECONDITION(aFrame2, "aFrame2 must not be null"); + + nsPresContext* presContext = aFrame1->GetPresContext(); + if (presContext != aFrame2->GetPresContext()) { + NS_ERROR("no common ancestor at all, different documents"); + return 0; + } + nsFrameManager* frameManager = presContext->PresShell()->FrameManager(); + + nsAutoTArray frame1Ancestors; + if (!FillAncestors(aFrame1, aCommonAncestor, frameManager, &frame1Ancestors)) { + // We reached the root of the frame tree ... if aCommonAncestor was set, + // it is wrong + aCommonAncestor = nsnull; + } + + nsAutoTArray frame2Ancestors; + if (!FillAncestors(aFrame2, aCommonAncestor, frameManager, &frame2Ancestors) && + aCommonAncestor) { + // We reached the root of the frame tree ... aCommonAncestor was wrong. + // Try again with no hint. + return DoCompareTreePosition(aFrame1, aFrame2, + aIf1Ancestor, aIf2Ancestor, nsnull); + } + + PRInt32 last1 = PRInt32(frame1Ancestors.Length()) - 1; + PRInt32 last2 = PRInt32(frame2Ancestors.Length()) - 1; + while (last1 >= 0 && last2 >= 0 && + frame1Ancestors[last1] == frame2Ancestors[last2]) { + last1--; + last2--; + } + + if (last1 < 0) { + if (last2 < 0) { + NS_ASSERTION(aFrame1 == aFrame2, "internal error?"); + return 0; + } + // aFrame1 is an ancestor of aFrame2 + return aIf1Ancestor; + } + + if (last2 < 0) { + // aFrame2 is an ancestor of aFrame1 + return aIf2Ancestor; + } + + nsIFrame* ancestor1 = frame1Ancestors[last1]; + nsIFrame* ancestor2 = frame2Ancestors[last2]; + // Now we should be able to walk sibling chains to find which one is first + if (IsFrameAfter(ancestor2, ancestor1)) + return -1; + if (IsFrameAfter(ancestor1, ancestor2)) + return 1; + NS_WARNING("Frames were in different child lists???"); + return 0; +} + // static nsIFrame* nsLayoutUtils::GetLastSibling(nsIFrame* aFrame) { if (!aFrame) { diff --git a/mozilla/layout/base/nsLayoutUtils.h b/mozilla/layout/base/nsLayoutUtils.h index 58311336f5b..0f1a77612ae 100644 --- a/mozilla/layout/base/nsLayoutUtils.h +++ b/mozilla/layout/base/nsLayoutUtils.h @@ -144,7 +144,45 @@ public: PRInt32 aIf1Ancestor, PRInt32 aIf2Ancestor, nsIContent* aCommonAncestor = nsnull); - + + /** + * CompareTreePosition determines whether aFrame1 comes before or + * after aFrame2 in a preorder traversal of the frame tree, where out + * of flow frames are treated as children of their placeholders. This is + * basically the same ordering as DoCompareTreePosition(nsIContent*) except + * that it handles anonymous content properly and there are subtleties with + * continuations. + * + * @param aCommonAncestor either null, or a common ancestor of + * aContent1 and aContent2. Actually this is + * only a hint; if it's not an ancestor of + * aContent1 or aContent2, this function will + * still work, but it will be slower than + * normal. + * @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 frame trees) + */ + static PRInt32 CompareTreePosition(nsIFrame* aFrame1, + nsIFrame* aFrame2, + nsIFrame* aCommonAncestor = nsnull) + { + return DoCompareTreePosition(aFrame1, aFrame2, -1, 1, aCommonAncestor); + } + + /* + * More generic version of |CompareTreePosition|. |aIf1Ancestor| + * gives the value to return when 1 is an ancestor of 2, and likewise + * for |aIf2Ancestor|. Passing (-1, 1) gives preorder traversal + * order, and (1, -1) gives postorder traversal order. + */ + static PRInt32 DoCompareTreePosition(nsIFrame* aFrame1, + nsIFrame* aFrame2, + PRInt32 aIf1Ancestor, + PRInt32 aIf2Ancestor, + nsIFrame* aCommonAncestor = nsnull); + /** * GetLastSibling simply finds the last sibling of aFrame, or returns nsnull if * aFrame is null.