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

Side by Side Diff: third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp

Issue 2636253002: Handle nested position:sticky elements (Closed)
Patch Set: Trying to add cc unittest, fails for unknown reason Created 3 years, 10 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 28 matching lines...) Expand all
39 #include "core/paint/ObjectPaintInvalidator.h" 39 #include "core/paint/ObjectPaintInvalidator.h"
40 #include "core/paint/PaintLayer.h" 40 #include "core/paint/PaintLayer.h"
41 #include "core/style/ShadowList.h" 41 #include "core/style/ShadowList.h"
42 #include "platform/LengthFunctions.h" 42 #include "platform/LengthFunctions.h"
43 #include "platform/geometry/TransformState.h" 43 #include "platform/geometry/TransformState.h"
44 #include "platform/scroll/MainThreadScrollingReason.h" 44 #include "platform/scroll/MainThreadScrollingReason.h"
45 #include "wtf/PtrUtil.h" 45 #include "wtf/PtrUtil.h"
46 46
47 namespace blink { 47 namespace blink {
48 48
49 namespace {
50 inline bool isOutOfFlowPositionedWithImplicitHeight(
51 const LayoutBoxModelObject* child) {
52 return child->isOutOfFlowPositioned() &&
53 !child->style()->logicalTop().isAuto() &&
54 !child->style()->logicalBottom().isAuto();
55 }
56
57 StickyPositionScrollingConstraints* stickyConstraintsForLayoutObject(
58 const LayoutBoxModelObject* o) {
59 if (!o || !o->layer()->ancestorOverflowLayer())
60 return nullptr;
61
62 PaintLayerScrollableArea* scrollableArea =
63 o->layer()->ancestorOverflowLayer()->getScrollableArea();
64 auto it = scrollableArea->stickyConstraintsMap().find(o->layer());
65 if (it == scrollableArea->stickyConstraintsMap().end())
66 return nullptr;
67
68 return &it->value;
69 }
70 } // namespace
71
49 class FloatStateForStyleChange { 72 class FloatStateForStyleChange {
50 public: 73 public:
51 static void setWasFloating(LayoutBoxModelObject* boxModelObject, 74 static void setWasFloating(LayoutBoxModelObject* boxModelObject,
52 bool wasFloating) { 75 bool wasFloating) {
53 s_wasFloating = wasFloating; 76 s_wasFloating = wasFloating;
54 s_boxModelObject = boxModelObject; 77 s_boxModelObject = boxModelObject;
55 } 78 }
56 79
57 static bool wasFloating(LayoutBoxModelObject* boxModelObject) { 80 static bool wasFloating(LayoutBoxModelObject* boxModelObject) {
58 ASSERT(boxModelObject == s_boxModelObject); 81 ASSERT(boxModelObject == s_boxModelObject);
(...skipping 582 matching lines...) Expand 10 before | Expand all | Expand 10 after
641 } 664 }
642 665
643 void LayoutBoxModelObject::updateFromStyle() { 666 void LayoutBoxModelObject::updateFromStyle() {
644 const ComputedStyle& styleToUse = styleRef(); 667 const ComputedStyle& styleToUse = styleRef();
645 setHasBoxDecorationBackground(styleToUse.hasBoxDecorationBackground()); 668 setHasBoxDecorationBackground(styleToUse.hasBoxDecorationBackground());
646 setInline(styleToUse.isDisplayInlineType()); 669 setInline(styleToUse.isDisplayInlineType());
647 setPositionState(styleToUse.position()); 670 setPositionState(styleToUse.position());
648 setHorizontalWritingMode(styleToUse.isHorizontalWritingMode()); 671 setHorizontalWritingMode(styleToUse.isHorizontalWritingMode());
649 } 672 }
650 673
651 static inline bool isOutOfFlowPositionedWithImplicitHeight(
652 const LayoutBoxModelObject* child) {
653 return child->isOutOfFlowPositioned() &&
654 !child->style()->logicalTop().isAuto() &&
655 !child->style()->logicalBottom().isAuto();
656 }
657
658 LayoutBlock* LayoutBoxModelObject::containingBlockForAutoHeightDetection( 674 LayoutBlock* LayoutBoxModelObject::containingBlockForAutoHeightDetection(
659 Length logicalHeight) const { 675 Length logicalHeight) const {
660 // For percentage heights: The percentage is calculated with respect to the 676 // For percentage heights: The percentage is calculated with respect to the
661 // height of the generated box's containing block. If the height of the 677 // height of the generated box's containing block. If the height of the
662 // containing block is not specified explicitly (i.e., it depends on content 678 // containing block is not specified explicitly (i.e., it depends on content
663 // height), and this element is not absolutely positioned, the used height is 679 // height), and this element is not absolutely positioned, the used height is
664 // calculated as if 'auto' was specified. 680 // calculated as if 'auto' was specified.
665 if (!logicalHeight.isPercentOrCalc() || isOutOfFlowPositioned()) 681 if (!logicalHeight.isPercentOrCalc() || isOutOfFlowPositioned())
666 return nullptr; 682 return nullptr;
667 683
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
764 (!containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight() || 780 (!containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight() ||
765 !style()->bottom().isPercentOrCalc() || 781 !style()->bottom().isPercentOrCalc() ||
766 containingBlock->stretchesToViewport())) 782 containingBlock->stretchesToViewport()))
767 offset.expand( 783 offset.expand(
768 LayoutUnit(), 784 LayoutUnit(),
769 -valueForLength(style()->bottom(), containingBlock->availableHeight())); 785 -valueForLength(style()->bottom(), containingBlock->availableHeight()));
770 786
771 return offset; 787 return offset;
772 } 788 }
773 789
790 // Inclusive of |from|, exclusive of |to|.
791 static LayoutBoxModelObject* findFirstStickyBetween(LayoutObject* from,
792 LayoutObject* to) {
793 LayoutObject* maybeStickyAncestor = from;
794 while (maybeStickyAncestor && maybeStickyAncestor != to) {
795 if (maybeStickyAncestor->isStickyPositioned()) {
796 return toLayoutBoxModelObject(maybeStickyAncestor);
797 }
798 maybeStickyAncestor =
799 maybeStickyAncestor->isLayoutInline()
800 ? maybeStickyAncestor->containingBlock()
801 : toLayoutBox(maybeStickyAncestor)->locationContainer();
802 }
803
804 return nullptr;
805 }
806
774 void LayoutBoxModelObject::updateStickyPositionConstraints() const { 807 void LayoutBoxModelObject::updateStickyPositionConstraints() const {
775 const FloatSize constrainingSize = computeStickyConstrainingRect().size(); 808 const FloatSize constrainingSize = computeStickyConstrainingRect().size();
776 809
777 PaintLayerScrollableArea* scrollableArea = 810 PaintLayerScrollableArea* scrollableArea =
778 layer()->ancestorOverflowLayer()->getScrollableArea(); 811 layer()->ancestorOverflowLayer()->getScrollableArea();
779 StickyPositionScrollingConstraints constraints; 812 StickyPositionScrollingConstraints constraints;
780 FloatSize skippedContainersOffset; 813 FloatSize skippedContainersOffset;
781 LayoutBlock* containingBlock = this->containingBlock(); 814 LayoutBlock* containingBlock = this->containingBlock();
782 // The location container for boxes is not always the containing block. 815 // The location container for boxes is not always the containing block.
783 LayoutBox* locationContainer = isLayoutInline() 816 LayoutObject* locationContainer =
784 ? containingBlock 817 isLayoutInline() ? container() : toLayoutBox(this)->locationContainer();
785 : toLayoutBox(this)->locationContainer();
786 // Skip anonymous containing blocks. 818 // Skip anonymous containing blocks.
787 while (containingBlock->isAnonymous()) { 819 while (containingBlock->isAnonymous()) {
788 containingBlock = containingBlock->containingBlock(); 820 containingBlock = containingBlock->containingBlock();
789 } 821 }
790 MapCoordinatesFlags flags = 0; 822 MapCoordinatesFlags flags = IgnoreStickyOffset;
791 skippedContainersOffset = 823 skippedContainersOffset =
792 toFloatSize(locationContainer 824 toFloatSize(locationContainer
793 ->localToAncestorQuadWithoutTransforms( 825 ->localToAncestorQuadWithoutTransforms(
794 FloatQuad(), containingBlock, flags) 826 FloatQuad(), containingBlock, flags)
795 .boundingBox() 827 .boundingBox()
796 .location()); 828 .location());
797 LayoutBox* scrollAncestor = 829 LayoutBox* scrollAncestor =
798 layer()->ancestorOverflowLayer()->isRootLayer() 830 layer()->ancestorOverflowLayer()->isRootLayer()
799 ? nullptr 831 ? nullptr
800 : toLayoutBox(layer()->ancestorOverflowLayer()->layoutObject()); 832 : toLayoutBox(layer()->ancestorOverflowLayer()->layoutObject());
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
860 minimumValueForLength(containingBlock->style()->paddingLeft(), 892 minimumValueForLength(containingBlock->style()->paddingLeft(),
861 maxContainerWidth) + 893 maxContainerWidth) +
862 minimumValueForLength(style()->marginLeft(), maxWidth)); 894 minimumValueForLength(style()->marginLeft(), maxWidth));
863 895
864 constraints.setScrollContainerRelativeContainingBlockRect( 896 constraints.setScrollContainerRelativeContainingBlockRect(
865 FloatRect(scrollContainerRelativeContainingBlockRect)); 897 FloatRect(scrollContainerRelativeContainingBlockRect));
866 898
867 FloatRect stickyBoxRect = 899 FloatRect stickyBoxRect =
868 isLayoutInline() ? FloatRect(toLayoutInline(this)->linesBoundingBox()) 900 isLayoutInline() ? FloatRect(toLayoutInline(this)->linesBoundingBox())
869 : FloatRect(toLayoutBox(this)->frameRect()); 901 : FloatRect(toLayoutBox(this)->frameRect());
902
903 // If we have an anonymous container, then |skippedContainersOffset| above can
904 // incorrectly exclude the scrollOffset if our non-anonymous containing block
905 // is the direct child of the scrollAncestor. Add it back here.
906 // TODO(smcgruer): I think this is not quite the right conditional check.
907 // TODO(smcgruer): Refactor into the code above.
smcgruer 2017/02/14 17:02:06 flackr : We should discuss this.
908 if (locationContainer != scrollAncestor &&
909 containingBlock == scrollAncestor) {
910 ScrollOffset scrollOffset(
911 scrollAncestor
912 ? toFloatSize(scrollAncestor->getScrollableArea()->scrollPosition())
913 : FloatSize());
914 stickyBoxRect.move(scrollOffset);
915 }
916
870 FloatRect flippedStickyBoxRect = stickyBoxRect; 917 FloatRect flippedStickyBoxRect = stickyBoxRect;
871 containingBlock->flipForWritingMode(flippedStickyBoxRect); 918 containingBlock->flipForWritingMode(flippedStickyBoxRect);
872 FloatPoint stickyLocation = 919 FloatPoint stickyLocation =
873 flippedStickyBoxRect.location() + skippedContainersOffset; 920 flippedStickyBoxRect.location() + skippedContainersOffset;
874 921
875 // The scrollContainerRelativePaddingBoxRect's position is the padding box so 922 // The scrollContainerRelativePaddingBoxRect's position is the padding box so
876 // we need to remove the border when finding the position of the sticky box 923 // we need to remove the border when finding the position of the sticky box
877 // within the scroll ancestor if the container is not our scroll ancestor. If 924 // within the scroll ancestor if the container is not our scroll ancestor. If
878 // the container is our scroll ancestor, we also need to remove the border 925 // the container is our scroll ancestor, we also need to remove the border
879 // box because we want the position from within the scroller border. 926 // box because we want the position from within the scroller border.
880 FloatSize containerBorderOffset(containingBlock->borderLeft(), 927 FloatSize containerBorderOffset(containingBlock->borderLeft(),
881 containingBlock->borderTop()); 928 containingBlock->borderTop());
882 stickyLocation -= containerBorderOffset; 929 stickyLocation -= containerBorderOffset;
883 constraints.setScrollContainerRelativeStickyBoxRect( 930 constraints.setScrollContainerRelativeStickyBoxRect(
884 FloatRect(scrollContainerRelativePaddingBoxRect.location() + 931 FloatRect(scrollContainerRelativePaddingBoxRect.location() +
885 toFloatSize(stickyLocation), 932 toFloatSize(stickyLocation),
886 flippedStickyBoxRect.size())); 933 flippedStickyBoxRect.size()));
887 934
935 // To correctly compute the offsets, the constraints need to know about any
936 // nested position:sticky elements between themselves and their
937 // containingBlock, and between the containingBlock and their scrollAncestor.
938 //
939 // The respective search ranges are [container, containingBlock) and
940 // [containingBlock, scrollAncestor).
941 constraints.setNearestStickyElementShiftingStickyBox(
942 findFirstStickyBetween(locationContainer, containingBlock));
943 // We cannot use |scrollAncestor| here as it disregards the root
944 // ancestorOverflowLayer(), which we should include.
945 LayoutObject* ancestorOverflowLayer =
946 layer()->ancestorOverflowLayer()->layoutObject();
947 constraints.setNearestStickyElementShiftingContainingBlock(
948 findFirstStickyBetween(containingBlock, ancestorOverflowLayer));
949
888 // We skip the right or top sticky offset if there is not enough space to 950 // We skip the right or top sticky offset if there is not enough space to
889 // honor both the left/right or top/bottom offsets. 951 // honor both the left/right or top/bottom offsets.
890 LayoutUnit horizontalOffsets = 952 LayoutUnit horizontalOffsets =
891 minimumValueForLength(style()->right(), 953 minimumValueForLength(style()->right(),
892 LayoutUnit(constrainingSize.width())) + 954 LayoutUnit(constrainingSize.width())) +
893 minimumValueForLength(style()->left(), 955 minimumValueForLength(style()->left(),
894 LayoutUnit(constrainingSize.width())); 956 LayoutUnit(constrainingSize.width()));
895 bool skipRight = false; 957 bool skipRight = false;
896 bool skipLeft = false; 958 bool skipLeft = false;
897 if (!style()->left().isAuto() && !style()->right().isAuto()) { 959 if (!style()->left().isAuto() && !style()->right().isAuto()) {
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
966 -enclosingClippingBox->borderTop() + enclosingClippingBox->paddingTop()); 1028 -enclosingClippingBox->borderTop() + enclosingClippingBox->paddingTop());
967 constrainingRect.contract( 1029 constrainingRect.contract(
968 FloatSize(enclosingClippingBox->paddingLeft() + 1030 FloatSize(enclosingClippingBox->paddingLeft() +
969 enclosingClippingBox->paddingRight(), 1031 enclosingClippingBox->paddingRight(),
970 enclosingClippingBox->paddingTop() + 1032 enclosingClippingBox->paddingTop() +
971 enclosingClippingBox->paddingBottom())); 1033 enclosingClippingBox->paddingBottom()));
972 return constrainingRect; 1034 return constrainingRect;
973 } 1035 }
974 1036
975 LayoutSize LayoutBoxModelObject::stickyPositionOffset() const { 1037 LayoutSize LayoutBoxModelObject::stickyPositionOffset() const {
976 const PaintLayer* ancestorOverflowLayer = layer()->ancestorOverflowLayer(); 1038 StickyPositionScrollingConstraints* constraints =
977 // TODO: Force compositing input update if we ask for offset before 1039 stickyConstraintsForLayoutObject(this);
978 // compositing inputs have been computed? 1040 if (!constraints)
979 if (!ancestorOverflowLayer)
980 return LayoutSize(); 1041 return LayoutSize();
981 FloatRect constrainingRect = computeStickyConstrainingRect(); 1042
982 PaintLayerScrollableArea* scrollableArea = 1043 StickyPositionScrollingConstraints* toContainingBlockConstraints =
983 ancestorOverflowLayer->getScrollableArea(); 1044 stickyConstraintsForLayoutObject(
1045 constraints->nearestStickyElementShiftingStickyBox());
1046
1047 StickyPositionScrollingConstraints* toViewportConstraints =
1048 stickyConstraintsForLayoutObject(
1049 constraints->nearestStickyElementShiftingContainingBlock());
984 1050
985 // The sticky offset is physical, so we can just return the delta computed in 1051 // The sticky offset is physical, so we can just return the delta computed in
986 // absolute coords (though it may be wrong with transforms). 1052 // absolute coords (though it may be wrong with transforms).
987 // TODO: Force compositing input update if we ask for offset with stale 1053 FloatRect constrainingRect = computeStickyConstrainingRect();
988 // compositing inputs. 1054 return LayoutSize(constraints->computeStickyOffset(
989 if (!scrollableArea->stickyConstraintsMap().contains(layer())) 1055 constrainingRect, toContainingBlockConstraints, toViewportConstraints));
990 return LayoutSize();
991 return LayoutSize(
992 scrollableArea->stickyConstraintsMap().get(layer()).computeStickyOffset(
993 constrainingRect));
994 } 1056 }
995 1057
996 LayoutPoint LayoutBoxModelObject::adjustedPositionRelativeTo( 1058 LayoutPoint LayoutBoxModelObject::adjustedPositionRelativeTo(
997 const LayoutPoint& startPoint, 1059 const LayoutPoint& startPoint,
998 const Element* offsetParent) const { 1060 const Element* offsetParent) const {
999 // If the element is the HTML body element or doesn't have a parent 1061 // If the element is the HTML body element or doesn't have a parent
1000 // return 0 and stop this algorithm. 1062 // return 0 and stop this algorithm.
1001 if (isBody() || !parent()) 1063 if (isBody() || !parent())
1002 return LayoutPoint(); 1064 return LayoutPoint();
1003 1065
(...skipping 348 matching lines...) Expand 10 before | Expand all | Expand 10 after
1352 if (rootElementStyle->hasBackground()) 1414 if (rootElementStyle->hasBackground())
1353 return false; 1415 return false;
1354 1416
1355 if (node() != document().firstBodyElement()) 1417 if (node() != document().firstBodyElement())
1356 return false; 1418 return false;
1357 1419
1358 return true; 1420 return true;
1359 } 1421 }
1360 1422
1361 } // namespace blink 1423 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698