Chromium Code Reviews| Index: third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp |
| diff --git a/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp b/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp |
| index 9324ca4d64e938cca2c04a75145635c478ba62e4..45ed7d68c84b30b4ac8867dcb71596089a9d0d7d 100644 |
| --- a/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp |
| +++ b/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp |
| @@ -583,6 +583,130 @@ 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()); |
| + constraints.setAbsoluteStickyBoxRect(absoluteStickyBoxRect); |
| + |
| + LayoutUnit horizontalOffsets = constraints.rightOffset() + constraints.leftOffset(); |
|
chrishtr
2015/10/06 17:08:33
You're reading leftOffset() here and then setting
flackr
2015/10/07 20:38:12
This is strange, I had lifted this from the old st
|
| + 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 |
|
chrishtr
2015/10/06 17:08:33
Is this part of implementing the 6-step algorithm
flackr
2015/10/07 20:38:12
No, this rectangle is the "viewport" used to deter
|
| +{ |
| + 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()); |
| + } else { |
| + constrainingRect = LayoutRect(view()->frameView()->visibleContentRect()); |
| + } |
| + |
| + 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 |
| @@ -605,7 +729,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()) { |
| @@ -627,7 +751,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 |
| @@ -949,7 +1079,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; |
| @@ -967,10 +1097,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; |