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

Side by Side Diff: third_party/WebKit/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: Make sticky vertical ref tests expectations not dependent on font size. Created 4 years, 8 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 unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org) 3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) 4 * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
5 * (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com) 5 * (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com)
6 * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 6 * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
7 * Copyright (C) 2010 Google Inc. All rights reserved. 7 * Copyright (C) 2010 Google Inc. All rights reserved.
8 * 8 *
9 * This library is free software; you can redistribute it and/or 9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public 10 * modify it under the terms of the GNU Library General Public
(...skipping 264 matching lines...) Expand 10 before | Expand all | Expand 10 after
275 bool newStoleBodyBackground = toLayoutBoxModelObject(bodyLayout)->ba ckgroundStolenForBeingBody(style()); 275 bool newStoleBodyBackground = toLayoutBoxModelObject(bodyLayout)->ba ckgroundStolenForBeingBody(style());
276 bool oldStoleBodyBackground = oldStyle && toLayoutBoxModelObject(bod yLayout)->backgroundStolenForBeingBody(oldStyle); 276 bool oldStoleBodyBackground = oldStyle && toLayoutBoxModelObject(bod yLayout)->backgroundStolenForBeingBody(oldStyle);
277 if (newStoleBodyBackground != oldStoleBodyBackground 277 if (newStoleBodyBackground != oldStoleBodyBackground
278 && bodyLayout->style() && bodyLayout->style()->hasBackground()) { 278 && bodyLayout->style() && bodyLayout->style()->hasBackground()) {
279 bodyLayout->setShouldDoFullPaintInvalidation(); 279 bodyLayout->setShouldDoFullPaintInvalidation();
280 } 280 }
281 } 281 }
282 } 282 }
283 283
284 if (FrameView *frameView = view()->frameView()) { 284 if (FrameView *frameView = view()->frameView()) {
285 bool newStyleIsViewportConstained = style()->hasViewportConstrainedPosit ion(); 285 bool newStyleIsViewportConstained = style()->position() == FixedPosition ;
286 bool oldStyleIsViewportConstrained = oldStyle && oldStyle->hasViewportCo nstrainedPosition(); 286 bool oldStyleIsViewportConstrained = oldStyle && oldStyle->position() == FixedPosition;
287 bool newStyleIsSticky = style()->position() == StickyPosition;
288 bool oldStyleIsSticky = oldStyle && oldStyle->position() == StickyPositi on;
289
290 if (newStyleIsSticky != oldStyleIsSticky) {
291 if (newStyleIsSticky) {
292 frameView->addStickyPositionObject();
293 // During compositing inputs update we'll have the scroll
294 // ancestor without having to walk up the tree and can compute
295 // the sticky position constraints then.
296 if (layer())
297 layer()->setNeedsCompositingInputsUpdate();
298 } else {
299 // This may get re-added to viewport constrained objects if the object went
300 // from sticky to fixed.
301 frameView->removeViewportConstrainedObject(this);
302 frameView->removeStickyPositionObject();
303
304 // Remove sticky constraints for this layer.
305 if (layer()) {
306 DisableCompositingQueryAsserts disabler;
307 if (const PaintLayer* ancestorOverflowLayer = layer()->ances torOverflowLayer())
308 ancestorOverflowLayer->getScrollableArea()->invalidateSt ickyConstraintsFor(layer());
309 }
310 }
311 }
312
287 if (newStyleIsViewportConstained != oldStyleIsViewportConstrained) { 313 if (newStyleIsViewportConstained != oldStyleIsViewportConstrained) {
288 if (newStyleIsViewportConstained && layer()) 314 if (newStyleIsViewportConstained && layer())
289 frameView->addViewportConstrainedObject(this); 315 frameView->addViewportConstrainedObject(this);
290 else 316 else
291 frameView->removeViewportConstrainedObject(this); 317 frameView->removeViewportConstrainedObject(this);
292 } 318 }
293 } 319 }
294 } 320 }
295 321
322 void LayoutBoxModelObject::invalidateStickyConstraints()
323 {
324 if (!layer())
325 return;
326
327 // This intentionally uses the stale ancestor overflow layer compositing
328 // input as if we have saved constraints for this layer they were saved
329 // in the previous frame.
330 DisableCompositingQueryAsserts disabler;
331 if (const PaintLayer* ancestorOverflowLayer = layer()->ancestorOverflowLayer ())
332 ancestorOverflowLayer->getScrollableArea()->invalidateAllStickyConstrain ts();
333 }
334
296 void LayoutBoxModelObject::createLayer(PaintLayerType type) 335 void LayoutBoxModelObject::createLayer(PaintLayerType type)
297 { 336 {
298 // If the current paint invalidation container is not a stacking context and this object is 337 // If the current paint invalidation container is not a stacking context and this object is
299 // stacked content, creating this layer may cause this object and its 338 // stacked content, creating this layer may cause this object and its
300 // descendants to change paint invalidation container. Therefore we must eag erly invalidate 339 // descendants to change paint invalidation container. Therefore we must eag erly invalidate
301 // them on the original paint invalidation container before creating the lay er. 340 // them on the original paint invalidation container before creating the lay er.
302 if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled() && isRooted() && style Ref().isStacked()) { 341 if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled() && isRooted() && style Ref().isStacked()) {
303 const LayoutBoxModelObject& currentPaintInvalidationContainer = containe rForPaintInvalidation(); 342 const LayoutBoxModelObject& currentPaintInvalidationContainer = containe rForPaintInvalidation();
304 if (!currentPaintInvalidationContainer.styleRef().isStackingContext()) 343 if (!currentPaintInvalidationContainer.styleRef().isStackingContext())
305 invalidatePaintIncludingNonSelfPaintingLayerDescendants(currentPaint InvalidationContainer); 344 invalidatePaintIncludingNonSelfPaintingLayerDescendants(currentPaint InvalidationContainer);
(...skipping 299 matching lines...) Expand 10 before | Expand all | Expand 10 after
605 644
606 else if (!style()->bottom().isAuto() 645 else if (!style()->bottom().isAuto()
607 && (!containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight() 646 && (!containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight()
608 || !style()->bottom().hasPercent() 647 || !style()->bottom().hasPercent()
609 || containingBlock->stretchesToViewport())) 648 || containingBlock->stretchesToViewport()))
610 offset.expand(LayoutUnit(), -valueForLength(style()->bottom(), containin gBlock->availableHeight())); 649 offset.expand(LayoutUnit(), -valueForLength(style()->bottom(), containin gBlock->availableHeight()));
611 650
612 return offset; 651 return offset;
613 } 652 }
614 653
654 void LayoutBoxModelObject::updateStickyPositionConstraints() const
655 {
656 // TODO(flackr): This method is reasonably complicated and should have some direct unit testing.
657 const FloatSize constrainingSize = computeStickyConstrainingRect().size();
658
659 PaintLayerScrollableArea* scrollableArea = layer()->ancestorOverflowLayer()- >getScrollableArea();
660 StickyPositionScrollingConstraints constraints;
661 FloatSize skippedContainersOffset;
662 LayoutBlock* containingBlock = this->containingBlock();
663 // Skip anonymous containing blocks.
664 while (containingBlock->isAnonymous()) {
665 skippedContainersOffset += toFloatSize(FloatPoint(containingBlock->frame Rect().location()));
666 containingBlock = containingBlock->containingBlock();
667 }
668 LayoutBox* scrollAncestor = layer()->ancestorOverflowLayer()->isRootLayer() ? nullptr : toLayoutBox(layer()->ancestorOverflowLayer()->layoutObject());
669
670 LayoutRect containerContentRect = containingBlock->contentBoxRect();
671 LayoutUnit maxWidth = containingBlock->availableLogicalWidth();
672
673 // Sticky positioned element ignore any override logical width on the contai ning block (as they don't call
674 // containingBlockLogicalWidthForContent). It's unclear whether this is tota lly fine.
675 // Compute the container-relative area within which the sticky element is al lowed to move.
676 containerContentRect.contractEdges(
677 minimumValueForLength(style()->marginTop(), maxWidth),
678 minimumValueForLength(style()->marginRight(), maxWidth),
679 minimumValueForLength(style()->marginBottom(), maxWidth),
680 minimumValueForLength(style()->marginLeft(), maxWidth));
681
682 // Map to the scroll ancestor.
683 constraints.setScrollContainerRelativeContainingBlockRect(containingBlock->l ocalToAncestorQuad(FloatRect(containerContentRect), scrollAncestor).boundingBox( ));
684
685 FloatRect stickyBoxRect = isLayoutInline()
686 ? FloatRect(toLayoutInline(this)->linesBoundingBox())
687 : FloatRect(toLayoutBox(this)->frameRect());
688 FloatRect flippedStickyBoxRect = stickyBoxRect;
689 containingBlock->flipForWritingMode(flippedStickyBoxRect);
690 FloatPoint stickyLocation = flippedStickyBoxRect.location() + skippedContain ersOffset;
691
692 // TODO(flackr): Unfortunate to call localToAncestorQuad again, but we can't just offset from the previously computed rect if there are transforms.
693 // Map to the scroll ancestor.
694 FloatRect scrollContainerRelativeContainerFrame = containingBlock->localToAn cestorQuad(FloatRect(FloatPoint(), FloatSize(containingBlock->size())), scrollAn cestor).boundingBox();
695
696 // If the containing block is our scroll ancestor, its location will not inc lude the scroll offset which we need to include as
697 // part of the sticky box rect so we include it here.
698 if (containingBlock->hasOverflowClip()) {
699 FloatSize scrollOffset(toFloatSize(containingBlock->layer()->getScrollab leArea()->adjustedScrollOffset()));
700 stickyLocation -= scrollOffset;
701 }
702
703 constraints.setScrollContainerRelativeStickyBoxRect(FloatRect(scrollContaine rRelativeContainerFrame.location() + toFloatSize(stickyLocation), flippedStickyB oxRect.size()));
704
705 // We skip the right or top sticky offset if there is not enough space to ho nor both the left/right or top/bottom offsets.
706 LayoutUnit horizontalOffsets = minimumValueForLength(style()->right(), Layou tUnit(constrainingSize.width())) +
707 minimumValueForLength(style()->left(), LayoutUnit(constrainingSize.width ()));
708 bool skipRight = false;
709 bool skipLeft = false;
710 if (!style()->left().isAuto() && !style()->right().isAuto()) {
711 if (horizontalOffsets > containerContentRect.width()
712 || horizontalOffsets + containerContentRect.width() > constrainingSi ze.width()) {
713 skipRight = style()->isLeftToRightDirection();
714 skipLeft = !skipRight;
715 }
716 }
717
718 if (!style()->left().isAuto() && !skipLeft) {
719 constraints.setLeftOffset(minimumValueForLength(style()->left(), LayoutU nit(constrainingSize.width())));
720 constraints.addAnchorEdge(StickyPositionScrollingConstraints::AnchorEdge Left);
721 }
722
723 if (!style()->right().isAuto() && !skipRight) {
724 constraints.setRightOffset(minimumValueForLength(style()->right(), Layou tUnit(constrainingSize.width())));
725 constraints.addAnchorEdge(StickyPositionScrollingConstraints::AnchorEdge Right);
726 }
727
728 bool skipBottom = false;
729 // TODO(flackr): Exclude top or bottom edge offset depending on the writing mode when related
730 // sections are fixed in spec: http://lists.w3.org/Archives/Public/www-style /2014May/0286.html
731 LayoutUnit verticalOffsets = minimumValueForLength(style()->top(), LayoutUni t(constrainingSize.height())) +
732 minimumValueForLength(style()->bottom(), LayoutUnit(constrainingSize.hei ght()));
733 if (!style()->top().isAuto() && !style()->bottom().isAuto()) {
734 if (verticalOffsets > containerContentRect.height()
735 || verticalOffsets + containerContentRect.height() > constrainingSiz e.height()) {
736 skipBottom = true;
737 }
738 }
739
740 if (!style()->top().isAuto()) {
741 constraints.setTopOffset(minimumValueForLength(style()->top(), LayoutUni t(constrainingSize.height())));
742 constraints.addAnchorEdge(StickyPositionScrollingConstraints::AnchorEdge Top);
743 }
744
745 if (!style()->bottom().isAuto() && !skipBottom) {
746 constraints.setBottomOffset(minimumValueForLength(style()->bottom(), Lay outUnit(constrainingSize.height())));
747 constraints.addAnchorEdge(StickyPositionScrollingConstraints::AnchorEdge Bottom);
748 }
749 scrollableArea->stickyConstraintsMap().set(layer(), constraints);
750 }
751
752 FloatRect LayoutBoxModelObject::computeStickyConstrainingRect() const
753 {
754 if (layer()->ancestorOverflowLayer()->isRootLayer())
755 return view()->frameView()->visibleContentRect();
756
757 LayoutBox* enclosingClippingBox = toLayoutBox(layer()->ancestorOverflowLayer ()->layoutObject());
758 FloatRect constrainingRect;
759 constrainingRect = FloatRect(enclosingClippingBox->overflowClipRect(LayoutPo int()));
760 constrainingRect.move(enclosingClippingBox->paddingLeft(), enclosingClipping Box->paddingTop());
761 constrainingRect.contract(FloatSize(enclosingClippingBox->paddingLeft() + en closingClippingBox->paddingRight(),
762 enclosingClippingBox->paddingTop() + enclosingClippingBox->paddingBottom ()));
763 return constrainingRect;
764 }
765
766 LayoutSize LayoutBoxModelObject::stickyPositionOffset() const
767 {
768 const PaintLayer* ancestorOverflowLayer = layer()->ancestorOverflowLayer();
769 // TODO: Force compositing input update if we ask for offset before composit ing inputs have been computed?
770 if (!ancestorOverflowLayer)
771 return LayoutSize();
772 FloatRect constrainingRect = computeStickyConstrainingRect();
773 PaintLayerScrollableArea* scrollableArea = ancestorOverflowLayer->getScrolla bleArea();
774
775 // The sticky offset is physical, so we can just return the delta computed i n absolute coords (though it may be wrong with transforms).
776 // TODO: Force compositing input update if we ask for offset with stale comp ositing inputs.
777 if (!scrollableArea->stickyConstraintsMap().contains(layer()))
778 return LayoutSize();
779 return LayoutSize(scrollableArea->stickyConstraintsMap().get(layer()).comput eStickyOffset(constrainingRect));
780 }
781
615 LayoutPoint LayoutBoxModelObject::adjustedPositionRelativeToOffsetParent(const L ayoutPoint& startPoint) const 782 LayoutPoint LayoutBoxModelObject::adjustedPositionRelativeToOffsetParent(const L ayoutPoint& startPoint) const
616 { 783 {
617 // If the element is the HTML body element or doesn't have a parent 784 // If the element is the HTML body element or doesn't have a parent
618 // return 0 and stop this algorithm. 785 // return 0 and stop this algorithm.
619 if (isBody() || !parent()) 786 if (isBody() || !parent())
620 return LayoutPoint(); 787 return LayoutPoint();
621 788
622 LayoutPoint referencePoint = startPoint; 789 LayoutPoint referencePoint = startPoint;
623 referencePoint.move(parent()->columnOffset(referencePoint)); 790 referencePoint.move(parent()->columnOffset(referencePoint));
624 791
625 // If the offsetParent of the element is null, or is the HTML body element, 792 // If the offsetParent of the element is null, or is the HTML body element,
626 // return the distance between the canvas origin and the left border edge 793 // return the distance between the canvas origin and the left border edge
627 // of the element and stop this algorithm. 794 // of the element and stop this algorithm.
628 Element* element = offsetParent(); 795 Element* element = offsetParent();
629 if (!element) 796 if (!element)
630 return referencePoint; 797 return referencePoint;
631 798
632 if (const LayoutBoxModelObject* offsetParent = element->layoutBoxModelObject ()) { 799 if (const LayoutBoxModelObject* offsetParent = element->layoutBoxModelObject ()) {
633 if (offsetParent->isBox() && !offsetParent->isBody()) 800 if (offsetParent->isBox() && !offsetParent->isBody())
634 referencePoint.move(-toLayoutBox(offsetParent)->borderLeft(), -toLay outBox(offsetParent)->borderTop()); 801 referencePoint.move(-toLayoutBox(offsetParent)->borderLeft(), -toLay outBox(offsetParent)->borderTop());
635 if (!isOutOfFlowPositioned() || flowThreadContainingBlock()) { 802 if (!isOutOfFlowPositioned() || flowThreadContainingBlock()) {
636 if (isInFlowPositioned()) 803 if (isInFlowPositioned())
637 referencePoint.move(relativePositionOffset()); 804 referencePoint.move(offsetForInFlowPosition());
638 805
639 LayoutObject* current; 806 LayoutObject* current;
640 for (current = parent(); current != offsetParent && current->parent( ); current = current->parent()) { 807 for (current = parent(); current != offsetParent && current->parent( ); current = current->parent()) {
641 // FIXME: What are we supposed to do inside SVG content? 808 // FIXME: What are we supposed to do inside SVG content?
642 if (!isOutOfFlowPositioned()) { 809 if (!isOutOfFlowPositioned()) {
643 if (current->isBox() && !current->isTableRow()) 810 if (current->isBox() && !current->isTableRow())
644 referencePoint.moveBy(toLayoutBox(current)->topLeftLocat ion()); 811 referencePoint.moveBy(toLayoutBox(current)->topLeftLocat ion());
645 referencePoint.move(current->parent()->columnOffset(referenc ePoint)); 812 referencePoint.move(current->parent()->columnOffset(referenc ePoint));
646 } 813 }
647 } 814 }
648 815
649 if (offsetParent->isBox() && offsetParent->isBody() && !offsetParent ->isPositioned()) 816 if (offsetParent->isBox() && offsetParent->isBody() && !offsetParent ->isPositioned())
650 referencePoint.moveBy(toLayoutBox(offsetParent)->topLeftLocation ()); 817 referencePoint.moveBy(toLayoutBox(offsetParent)->topLeftLocation ());
651 } 818 }
652 } 819 }
653 820
654 return referencePoint; 821 return referencePoint;
655 } 822 }
656 823
657 LayoutSize LayoutBoxModelObject::offsetForInFlowPosition() const 824 LayoutSize LayoutBoxModelObject::offsetForInFlowPosition() const
658 { 825 {
659 return isRelPositioned() ? relativePositionOffset() : LayoutSize(); 826 if (isRelPositioned())
827 return relativePositionOffset();
828
829 if (isStickyPositioned())
830 return stickyPositionOffset();
831
832 return LayoutSize();
660 } 833 }
661 834
662 LayoutUnit LayoutBoxModelObject::offsetLeft() const 835 LayoutUnit LayoutBoxModelObject::offsetLeft() const
663 { 836 {
664 // Note that LayoutInline and LayoutBox override this to pass a different 837 // Note that LayoutInline and LayoutBox override this to pass a different
665 // startPoint to adjustedPositionRelativeToOffsetParent. 838 // startPoint to adjustedPositionRelativeToOffsetParent.
666 return adjustedPositionRelativeToOffsetParent(LayoutPoint()).x(); 839 return adjustedPositionRelativeToOffsetParent(LayoutPoint()).x();
667 } 840 }
668 841
669 LayoutUnit LayoutBoxModelObject::offsetTop() const 842 LayoutUnit LayoutBoxModelObject::offsetTop() const
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after
882 if (offsetDependsOnPoint) 1055 if (offsetDependsOnPoint)
883 flags |= IsNonUniform; 1056 flags |= IsNonUniform;
884 if (isFixedPos) 1057 if (isFixedPos)
885 flags |= IsFixedPosition; 1058 flags |= IsFixedPosition;
886 if (hasTransform) 1059 if (hasTransform)
887 flags |= HasTransform; 1060 flags |= HasTransform;
888 if (shouldUseTransformFromContainer(container)) { 1061 if (shouldUseTransformFromContainer(container)) {
889 TransformationMatrix t; 1062 TransformationMatrix t;
890 getTransformFromContainer(container, containerOffset, t); 1063 getTransformFromContainer(container, containerOffset, t);
891 t.translateRight(adjustmentForSkippedAncestor.width().toFloat(), adjustm entForSkippedAncestor.height().toFloat()); 1064 t.translateRight(adjustmentForSkippedAncestor.width().toFloat(), adjustm entForSkippedAncestor.height().toFloat());
892 geometryMap.push(this, t, flags); 1065 geometryMap.push(this, t, flags, LayoutSize());
893 } else { 1066 } else {
894 containerOffset += adjustmentForSkippedAncestor; 1067 containerOffset += adjustmentForSkippedAncestor;
895 geometryMap.push(this, containerOffset, flags); 1068 geometryMap.push(this, containerOffset, flags, LayoutSize());
896 } 1069 }
897 1070
898 return ancestorSkipped ? ancestorToStopAt : container; 1071 return ancestorSkipped ? ancestorToStopAt : container;
899 } 1072 }
900 1073
901 void LayoutBoxModelObject::moveChildTo(LayoutBoxModelObject* toBoxModelObject, L ayoutObject* child, LayoutObject* beforeChild, bool fullRemoveInsert) 1074 void LayoutBoxModelObject::moveChildTo(LayoutBoxModelObject* toBoxModelObject, L ayoutObject* child, LayoutObject* beforeChild, bool fullRemoveInsert)
902 { 1075 {
903 // We assume that callers have cleared their positioned objects list for chi ld moves (!fullRemoveInsert) so the 1076 // We assume that callers have cleared their positioned objects list for chi ld moves (!fullRemoveInsert) so the
904 // positioned layoutObject maps don't become stale. It would be too slow to do the map lookup on each call. 1077 // positioned layoutObject maps don't become stale. It would be too slow to do the map lookup on each call.
905 ASSERT(!fullRemoveInsert || !isLayoutBlock() || !toLayoutBlock(this)->hasPos itionedObjects()); 1078 ASSERT(!fullRemoveInsert || !isLayoutBlock() || !toLayoutBlock(this)->hasPos itionedObjects());
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
967 if (rootElementStyle->hasBackground()) 1140 if (rootElementStyle->hasBackground())
968 return false; 1141 return false;
969 1142
970 if (node() != document().firstBodyElement()) 1143 if (node() != document().firstBodyElement())
971 return false; 1144 return false;
972 1145
973 return true; 1146 return true;
974 } 1147 }
975 1148
976 } // namespace blink 1149 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698