Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(953)

Unified Diff: Source/core/layout/LayoutBoxModelObject.cpp

Issue 1308273010: Adapt and reland old position sticky implementation (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Fix style errors and remaining layout tests. Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: Source/core/layout/LayoutBoxModelObject.cpp
diff --git a/Source/core/layout/LayoutBoxModelObject.cpp b/Source/core/layout/LayoutBoxModelObject.cpp
index 1d7a2fb5b029473ab51240726de94e6fc2b0a991..66564d918698d9b315c89078945f1dca1b6b9e6d 100644
--- a/Source/core/layout/LayoutBoxModelObject.cpp
+++ b/Source/core/layout/LayoutBoxModelObject.cpp
@@ -569,6 +569,131 @@ LayoutSize LayoutBoxModelObject::relativePositionOffset() const
return offset;
}
+void LayoutBoxModelObject::computeStickyPositionConstraints(StickyPositionViewportConstraints& constraints, const LayoutRect& constrainingRect) const
+{
+ LayoutBlock* containingBlock = this->containingBlock();
+
+ LayoutRect containerContentRect = containingBlock->contentBoxRect();
+ LayoutUnit maxWidth = containingBlock->availableLogicalWidth();
+
+ // Sticky positioned element ignore any override logical width on the containing block (as they don't call
+ // containingBlockLogicalWidthForContent). It's unclear whether this is totally fine.
+ // Compute the container-relative area within which the sticky element is allowed to move.
+ containerContentRect.contractEdges(
+ minimumValueForLength(style()->marginTop(), maxWidth),
+ minimumValueForLength(style()->marginRight(), maxWidth),
+ minimumValueForLength(style()->marginBottom(), maxWidth),
+ minimumValueForLength(style()->marginLeft(), maxWidth));
+
+ // Map to the view to avoid including page scale factor.
+ constraints.setAbsoluteContainingBlockRect(LayoutRect(containingBlock->localToContainerQuad(FloatRect(containerContentRect), view()).boundingBox()));
+
+ LayoutRect stickyBoxRect = frameRectForStickyPositioning();
+ LayoutRect flippedStickyBoxRect = stickyBoxRect;
+ containingBlock->flipForWritingMode(flippedStickyBoxRect);
+ LayoutPoint stickyLocation = flippedStickyBoxRect.location();
+
+ // FIXME: sucks to call localToAbsolute again, but we can't just offset from the previously computed rect if there are transforms.
+ // Map to the view to avoid including page scale factor.
+ LayoutRect absContainerFrame = LayoutRect(containingBlock->localToContainerQuad(FloatRect(FloatPoint(), FloatSize(containingBlock->size())), view()).boundingBox());
+
+ if (containingBlock->hasOverflowClip()) {
+ LayoutSize scrollOffset(containingBlock->layer()->scrollableArea()->adjustedScrollOffset());
+ stickyLocation -= scrollOffset;
+ }
+
+ // We can't call localToAbsolute on |this| because that will recur. FIXME: For now, assume that |this| is not transformed.
+ LayoutRect absoluteStickyBoxRect(LayoutPoint(absContainerFrame.location()) + stickyLocation, flippedStickyBoxRect.size());
chrishtr 2015/09/17 01:18:56 This is bad for performance. Do we really need to
flackr 2015/10/07 20:38:11 We need to save this rect because sticky objects o
+ constraints.setAbsoluteStickyBoxRect(absoluteStickyBoxRect);
+
+ LayoutUnit horizontalOffsets = constraints.rightOffset() + constraints.leftOffset();
+ bool skipRight = false;
+ bool skipLeft = false;
+ if (!style()->left().isAuto() && !style()->right().isAuto()) {
+ if (horizontalOffsets > containerContentRect.width()
+ || horizontalOffsets + containerContentRect.width() > constrainingRect.width()) {
+ skipRight = style()->isLeftToRightDirection();
+ skipLeft = !skipRight;
+ }
+ }
+
+ if (!style()->left().isAuto() && !skipLeft) {
+ constraints.setLeftOffset(minimumValueForLength(style()->left(), constrainingRect.width()));
+ constraints.addAnchorEdge(ViewportConstraints::AnchorEdgeLeft);
+ }
+
+ if (!style()->right().isAuto() && !skipRight) {
+ constraints.setRightOffset(minimumValueForLength(style()->right(), constrainingRect.width()));
+ constraints.addAnchorEdge(ViewportConstraints::AnchorEdgeRight);
+ }
+
+ bool skipBottom = false;
+ // FIXME(ostap): Exclude top or bottom edge offset depending on the writing mode when related
+ // sections are fixed in spec: http://lists.w3.org/Archives/Public/www-style/2014May/0286.html
+ LayoutUnit verticalOffsets = constraints.topOffset() + constraints.bottomOffset();
+ if (!style()->top().isAuto() && !style()->bottom().isAuto()) {
+ if (verticalOffsets > containerContentRect.height()
+ || verticalOffsets + containerContentRect.height() > constrainingRect.height()) {
+ skipBottom = true;
+ }
+ }
+
+ if (!style()->top().isAuto()) {
+ constraints.setTopOffset(minimumValueForLength(style()->top(), constrainingRect.height()));
+ constraints.addAnchorEdge(ViewportConstraints::AnchorEdgeTop);
+ }
+
+ if (!style()->bottom().isAuto() && !skipBottom) {
+ constraints.setBottomOffset(minimumValueForLength(style()->bottom(), constrainingRect.height()));
+ constraints.addAnchorEdge(ViewportConstraints::AnchorEdgeBottom);
+ }
+}
+
+static inline LayoutBox* findScrollAncestor(const Node* startNode)
+{
+ ASSERT(startNode->layoutObject());
+ LayoutBox* curBox = startNode->layoutObject()->containingBlock();
+
+ // Scrolling propagates along the containing block chain.
+ while (curBox && !curBox->isLayoutView()) {
+ if (curBox->hasOverflowClip())
+ return curBox;
+ curBox = curBox->containingBlock();
+ }
+
+ return nullptr;
+}
+
+LayoutRect LayoutBoxModelObject::computeStickyConstrainingRect() const
+{
+ LayoutRect constrainingRect;
+
+ ASSERT(hasLayer());
+ LayoutBox* enclosingClippingBox = findScrollAncestor(node());
+ if (enclosingClippingBox) {
+ LayoutRect clipRect = enclosingClippingBox->overflowClipRect(LayoutPoint());
+ clipRect.move(enclosingClippingBox->paddingLeft(), enclosingClippingBox->paddingTop());
+ clipRect.contract(LayoutSize(enclosingClippingBox->paddingLeft() + enclosingClippingBox->paddingRight(),
+ enclosingClippingBox->paddingTop() + enclosingClippingBox->paddingBottom()));
+ constrainingRect = LayoutRect(enclosingClippingBox->localToContainerQuad(FloatRect(clipRect), view()).boundingBox());
chrishtr 2015/09/17 01:18:56 Does clippedOverflowRectForPaintInvalidation do th
flackr 2015/10/07 20:38:11 Unless I'm not understanding / using this correctl
+ } else {
+ LayoutRect viewportRect(view()->frameView()->visibleContentRect());
+ constrainingRect = viewportRect;
+ }
+
+ return constrainingRect;
+}
+
+LayoutSize LayoutBoxModelObject::stickyPositionOffset() const
+{
+ LayoutRect constrainingRect = computeStickyConstrainingRect();
+ StickyPositionViewportConstraints constraints;
+ computeStickyPositionConstraints(constraints, constrainingRect);
+
+ // The sticky offset is physical, so we can just return the delta computed in absolute coords (though it may be wrong with transforms).
+ return LayoutSize(constraints.computeStickyOffset(constrainingRect));
+}
+
LayoutPoint LayoutBoxModelObject::adjustedPositionRelativeToOffsetParent(const LayoutPoint& startPoint) const
{
// If the element is the HTML body element or doesn't have a parent
@@ -591,7 +716,7 @@ LayoutPoint LayoutBoxModelObject::adjustedPositionRelativeToOffsetParent(const L
referencePoint.move(-toLayoutBox(offsetParent)->borderLeft(), -toLayoutBox(offsetParent)->borderTop());
if (!isOutOfFlowPositioned() || flowThreadContainingBlock()) {
if (isInFlowPositioned())
- referencePoint.move(relativePositionOffset());
+ referencePoint.move(offsetForInFlowPosition());
LayoutObject* current;
for (current = parent(); current != offsetParent && current->parent(); current = current->parent()) {
@@ -613,7 +738,13 @@ LayoutPoint LayoutBoxModelObject::adjustedPositionRelativeToOffsetParent(const L
LayoutSize LayoutBoxModelObject::offsetForInFlowPosition() const
{
- return isRelPositioned() ? relativePositionOffset() : LayoutSize();
+ if (isRelPositioned())
+ return relativePositionOffset();
+
+ if (isStickyPositioned())
+ return stickyPositionOffset();
+
+ return LayoutSize();
}
LayoutUnit LayoutBoxModelObject::offsetLeft() const
@@ -935,7 +1066,7 @@ const LayoutObject* LayoutBoxModelObject::pushMappingToContainer(const LayoutBox
return nullptr;
bool isInline = isLayoutInline();
- bool isFixedPos = !isInline && style()->position() == FixedPosition;
+ bool isViewportConstrained = !isInline && (style()->hasViewportConstrainedPosition());
bool hasTransform = !isInline && hasLayer() && layer()->transform();
LayoutSize adjustmentForSkippedAncestor;
@@ -953,10 +1084,10 @@ const LayoutObject* LayoutBoxModelObject::pushMappingToContainer(const LayoutBox
TransformationMatrix t;
getTransformFromContainer(container, containerOffset, t);
t.translateRight(adjustmentForSkippedAncestor.width().toFloat(), adjustmentForSkippedAncestor.height().toFloat());
- geometryMap.push(this, t, preserve3D, offsetDependsOnPoint, isFixedPos, hasTransform);
+ geometryMap.push(this, t, preserve3D, offsetDependsOnPoint, isViewportConstrained, hasTransform);
} else {
containerOffset += adjustmentForSkippedAncestor;
- geometryMap.push(this, containerOffset, preserve3D, offsetDependsOnPoint, isFixedPos, hasTransform);
+ geometryMap.push(this, containerOffset, preserve3D, offsetDependsOnPoint, isViewportConstrained, hasTransform);
}
return ancestorSkipped ? ancestorToStopAt : container;

Powered by Google App Engine
This is Rietveld 408576698