diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index ce76ae12258..ef9daf9e2a8 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -350,7 +350,16 @@ RestyleManager::RecomputePosition(nsIFrame* aFrame) // Move the frame if (display->mPosition == NS_STYLE_POSITION_STICKY) { - StickyScrollContainer::ComputeStickyOffsets(aFrame); + // Update sticky positioning for an entire element at once when + // RecomputePosition is called with the first continuation in a chain. + if (!aFrame->GetPrevContinuation()) { + StickyScrollContainer::ComputeStickyOffsets(aFrame); + StickyScrollContainer* ssc = + StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame); + if (ssc) { + ssc->PositionContinuations(aFrame); + } + } } else { MOZ_ASSERT(NS_STYLE_POSITION_RELATIVE == display->mPosition, "Unexpected type of positioning"); @@ -362,14 +371,14 @@ RestyleManager::RecomputePosition(nsIFrame* aFrame) NS_ASSERTION(newOffsets.left == -newOffsets.right && newOffsets.top == -newOffsets.bottom, "ComputeRelativeOffsets should return valid results"); + + // nsHTMLReflowState::ApplyRelativePositioning would work here, but + // since we've already checked mPosition and aren't changing the frame's + // normal position, go ahead and add the offsets directly. + aFrame->SetPosition(aFrame->GetNormalPosition() + + nsPoint(newOffsets.left, newOffsets.top)); } - nsPoint position = aFrame->GetNormalPosition(); - - // This handles both relative and sticky positioning. - nsHTMLReflowState::ApplyRelativePositioning(aFrame, newOffsets, &position); - aFrame->SetPosition(position); - return true; } diff --git a/layout/generic/StickyScrollContainer.cpp b/layout/generic/StickyScrollContainer.cpp index 8210ba893bd..6b2ca7acf5e 100644 --- a/layout/generic/StickyScrollContainer.cpp +++ b/layout/generic/StickyScrollContainer.cpp @@ -129,6 +129,9 @@ void StickyScrollContainer::ComputeStickyLimits(nsIFrame* aFrame, nsRect* aStick, nsRect* aContain) const { + NS_ASSERTION(!aFrame->GetPrevContinuation(), + "Can't sticky position individual continuations"); + aStick->SetRect(nscoord_MIN/2, nscoord_MIN/2, nscoord_MAX, nscoord_MAX); aContain->SetRect(nscoord_MIN/2, nscoord_MIN/2, nscoord_MAX, nscoord_MAX); @@ -146,17 +149,16 @@ StickyScrollContainer::ComputeStickyLimits(nsIFrame* aFrame, nsRect* aStick, nsLayoutUtils::IsProperAncestorFrame(scrolledFrame, cbFrame), "Scroll frame should be an ancestor of the containing block"); - nsRect rect = aFrame->GetRect(); - nsMargin margin = aFrame->GetUsedMargin(); + nsRect rect = + nsLayoutUtils::GetAllInFlowRectsUnion(aFrame, aFrame->GetParent()); // Containing block limits if (cbFrame != scrolledFrame) { - nsMargin cbBorderPadding = cbFrame->GetUsedBorderAndPadding(); - aContain->SetRect(nsPoint(cbBorderPadding.left, cbBorderPadding.top) - - aFrame->GetParent()->GetOffsetTo(cbFrame), - cbFrame->GetContentRectRelativeToSelf().Size() - - rect.Size()); - aContain->Deflate(margin); + *aContain = nsLayoutUtils::GetAllInFlowRectsUnion(cbFrame, cbFrame); + aContain->MoveBy(-aFrame->GetParent()->GetOffsetTo(cbFrame)); + aContain->Deflate(cbFrame->GetUsedBorderAndPadding()); + aContain->Deflate(aFrame->GetUsedMargin()); + aContain->Deflate(nsMargin(0, rect.width, rect.height, 0)); } nsMargin sfPadding = scrolledFrame->GetUsedPadding(); @@ -197,6 +199,10 @@ StickyScrollContainer::ComputeStickyLimits(nsIFrame* aFrame, nsRect* aStick, aStick->SetRightEdge(mScrollPosition.x + sfPadding.left + sfSize.width - computedOffsets->right - rect.width - sfOffset.x); } + + // These limits are for the bounding box of aFrame's continuations. Convert + // to limits for aFrame itself. + aStick->MoveBy(aFrame->GetPosition() - rect.TopLeft()); } nsPoint @@ -255,6 +261,22 @@ StickyScrollContainer::GetScrollRanges(nsIFrame* aFrame, nsRect* aOuter, } } +void +StickyScrollContainer::PositionContinuations(nsIFrame* aFrame) +{ + NS_ASSERTION(!aFrame->GetPrevContinuation(), + "Should be starting from the first continuation"); + nsPoint newPosition = ComputePosition(aFrame); + nsPoint translation = newPosition - aFrame->GetPosition(); + aFrame->SetPosition(newPosition); + + // Move all continuation frames by the same amount. + for (nsIFrame* cont = aFrame->GetNextContinuation(); cont; + cont = cont->GetNextContinuation()) { + cont->SetPosition(cont->GetPosition() + translation); + } +} + void StickyScrollContainer::UpdatePositions(nsPoint aScrollPosition, nsIFrame* aSubtreeRoot) @@ -272,12 +294,18 @@ StickyScrollContainer::UpdatePositions(nsPoint aScrollPosition, oct.SetSubtreeRoot(aSubtreeRoot); for (nsTArray::size_type i = 0; i < mFrames.Length(); i++) { nsIFrame* f = mFrames[i]; + if (aSubtreeRoot) { // Reflowing the scroll frame, so recompute offsets. ComputeStickyOffsets(f); } - f->SetPosition(ComputePosition(f)); - oct.AddFrame(f); + // mFrames will only contain first continuations, because we filter in + // nsIFrame::Init. + PositionContinuations(f); + + for (nsIFrame* cont = f; cont; cont = cont->GetNextContinuation()) { + oct.AddFrame(cont); + } } oct.Flush(); } diff --git a/layout/generic/StickyScrollContainer.h b/layout/generic/StickyScrollContainer.h index 3b29a8dca8e..e919ed8e92a 100644 --- a/layout/generic/StickyScrollContainer.h +++ b/layout/generic/StickyScrollContainer.h @@ -63,6 +63,11 @@ public: */ void GetScrollRanges(nsIFrame* aFrame, nsRect* aOuter, nsRect* aInner) const; + /** + * Compute and set the position of a frame and its following continuations. + */ + void PositionContinuations(nsIFrame* aFrame); + /** * Compute and set the position of all sticky frames, given the current * scroll position of the scroll frame. If not in reflow, aSubtreeRoot should diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 67a331942d2..4ba20a8dcf8 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -512,6 +512,7 @@ nsFrame::Init(nsIContent* aContent, mState |= NS_FRAME_MAY_BE_TRANSFORMED; } if (disp->mPosition == NS_STYLE_POSITION_STICKY && + !aPrevInFlow && !(mState & NS_FRAME_IS_NONDISPLAY)) { StickyScrollContainer* ssc = StickyScrollContainer::GetStickyScrollContainerForFrame(this); diff --git a/layout/generic/nsHTMLReflowState.cpp b/layout/generic/nsHTMLReflowState.cpp index 94020f5fc4a..fd1ff62c324 100644 --- a/layout/generic/nsHTMLReflowState.cpp +++ b/layout/generic/nsHTMLReflowState.cpp @@ -871,7 +871,14 @@ nsHTMLReflowState::ApplyRelativePositioning(nsIFrame* aFrame, const nsStyleDisplay* display = aFrame->StyleDisplay(); if (NS_STYLE_POSITION_RELATIVE == display->mPosition) { *aPosition += nsPoint(aComputedOffsets.left, aComputedOffsets.top); - } else if (NS_STYLE_POSITION_STICKY == display->mPosition) { + } else if (NS_STYLE_POSITION_STICKY == display->mPosition && + !aFrame->GetNextContinuation() && !aFrame->GetPrevContinuation()) { + // Sticky positioning for elements with multiple frames needs to be + // computed all at once. We can't safely do that here because we might be + // partway through (re)positioning the frames, so leave it until the scroll + // container reflows and calls StickyScrollContainer::UpdatePositions. + // For single-frame sticky positioned elements, though, go ahead and apply + // it now to avoid unnecessary overflow updates later. StickyScrollContainer* ssc = StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame); if (ssc) { diff --git a/layout/reftests/position-sticky/column-contain-1-ref.html b/layout/reftests/position-sticky/column-contain-1-ref.html new file mode 100644 index 00000000000..39ed2b1cb66 --- /dev/null +++ b/layout/reftests/position-sticky/column-contain-1-ref.html @@ -0,0 +1,42 @@ + + + + + + + + +
+
+
+
+
+
+ + diff --git a/layout/reftests/position-sticky/column-contain-1a.html b/layout/reftests/position-sticky/column-contain-1a.html new file mode 100644 index 00000000000..136851dc9d8 --- /dev/null +++ b/layout/reftests/position-sticky/column-contain-1a.html @@ -0,0 +1,45 @@ + + + + + CSS Test: Sticky Positioning - multiframe containing-block element + + + + + + +
+
+
+
+
+
+ + diff --git a/layout/reftests/position-sticky/column-contain-1b.html b/layout/reftests/position-sticky/column-contain-1b.html new file mode 100644 index 00000000000..f9e16681e25 --- /dev/null +++ b/layout/reftests/position-sticky/column-contain-1b.html @@ -0,0 +1,47 @@ + + + + + CSS Test: Sticky Positioning - multiframe containing-block element + + + + + + +
+
+
+
+
+
+
+
+ + diff --git a/layout/reftests/position-sticky/inline-1-ref.html b/layout/reftests/position-sticky/inline-1-ref.html new file mode 100644 index 00000000000..02cf1ee3348 --- /dev/null +++ b/layout/reftests/position-sticky/inline-1-ref.html @@ -0,0 +1,29 @@ + + + + + + + + +
+ hello
hello there
everyone
+
+ + diff --git a/layout/reftests/position-sticky/inline-1.html b/layout/reftests/position-sticky/inline-1.html new file mode 100644 index 00000000000..675dd074436 --- /dev/null +++ b/layout/reftests/position-sticky/inline-1.html @@ -0,0 +1,28 @@ + + + + + CSS Test: Sticky Positioning - inline + + + + + + +
+ hello there
everyone
+
+ + diff --git a/layout/reftests/position-sticky/inline-2-ref.html b/layout/reftests/position-sticky/inline-2-ref.html new file mode 100644 index 00000000000..2d881b860b7 --- /dev/null +++ b/layout/reftests/position-sticky/inline-2-ref.html @@ -0,0 +1,29 @@ + + + + + + + + +
+ hello
hello there
everyone
+
+ + diff --git a/layout/reftests/position-sticky/inline-2.html b/layout/reftests/position-sticky/inline-2.html new file mode 100644 index 00000000000..d41a42be92e --- /dev/null +++ b/layout/reftests/position-sticky/inline-2.html @@ -0,0 +1,36 @@ + + + + + CSS Test: Sticky Positioning - inline, dynamic changes + + + + + + + +
+ hello there
everyone
+
+ + + diff --git a/layout/reftests/position-sticky/inline-3-ref.html b/layout/reftests/position-sticky/inline-3-ref.html new file mode 100644 index 00000000000..59728a6ecc9 --- /dev/null +++ b/layout/reftests/position-sticky/inline-3-ref.html @@ -0,0 +1,35 @@ + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/position-sticky/inline-3.html b/layout/reftests/position-sticky/inline-3.html new file mode 100644 index 00000000000..329bd8599b0 --- /dev/null +++ b/layout/reftests/position-sticky/inline-3.html @@ -0,0 +1,41 @@ + + + + + CSS Test: Sticky Positioning - inline with margins + + + + + + +
+
+
+
+
+ + diff --git a/layout/reftests/position-sticky/reftest.list b/layout/reftests/position-sticky/reftest.list index 0d7fed3963b..9abd7ae355d 100644 --- a/layout/reftests/position-sticky/reftest.list +++ b/layout/reftests/position-sticky/reftest.list @@ -41,3 +41,8 @@ fuzzy-if(Android,4,1) == containing-block-1.html containing-block-1-ref.html == overconstrained-1.html overconstrained-1-ref.html == overconstrained-2.html overconstrained-2-ref.html == overconstrained-3.html overconstrained-3-ref.html +== inline-1.html inline-1-ref.html +== inline-2.html inline-2-ref.html +fails == inline-3.html inline-3-ref.html # bug 916302 +fails == column-contain-1a.html column-contain-1-ref.html +== column-contain-1b.html column-contain-1-ref.html