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

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: Remove redundant invalidation in LayoutBlock::tryLayoutDoingPositionedMovementOnly Created 4 years, 9 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) {
chrishtr 2016/03/24 17:36:02 Do you need to invalidate sticky constraints here?
flackr 2016/03/29 16:02:32 Yes, but we're currently invalidating sticky const
chrishtr 2016/03/29 16:28:19 Does the code do that? Which line of code implemen
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 }
305
287 if (newStyleIsViewportConstained != oldStyleIsViewportConstrained) { 306 if (newStyleIsViewportConstained != oldStyleIsViewportConstrained) {
288 if (newStyleIsViewportConstained && layer()) 307 if (newStyleIsViewportConstained && layer())
289 frameView->addViewportConstrainedObject(this); 308 frameView->addViewportConstrainedObject(this);
290 else 309 else
291 frameView->removeViewportConstrainedObject(this); 310 frameView->removeViewportConstrainedObject(this);
292 } 311 }
293 } 312 }
294 } 313 }
295 314
315 void LayoutBoxModelObject::invalidateStickyConstraints()
316 {
317 if (!layer())
318 return;
319
320 // This intentionally uses the stale ancestor overflow layer compositing
321 // input as if we have saved constraints for this layer they were saved
322 // in the previous frame.
323 DisableCompositingQueryAsserts disabler;
324 if (const PaintLayer* ancestorOverflowLayer = layer()->ancestorOverflowLayer ())
325 ancestorOverflowLayer->getScrollableArea()->invalidateAllStickyConstrain ts();
326 }
327
296 void LayoutBoxModelObject::createLayer(PaintLayerType type) 328 void LayoutBoxModelObject::createLayer(PaintLayerType type)
297 { 329 {
298 // If the current paint invalidation container is not a stacking context and this object is 330 // 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 331 // stacked content, creating this layer may cause this object and its
300 // descendants to change paint invalidation container. Therefore we must eag erly invalidate 332 // 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. 333 // them on the original paint invalidation container before creating the lay er.
302 if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled() && isRooted() && style Ref().isStacked()) { 334 if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled() && isRooted() && style Ref().isStacked()) {
303 const LayoutBoxModelObject& currentPaintInvalidationContainer = containe rForPaintInvalidation(); 335 const LayoutBoxModelObject& currentPaintInvalidationContainer = containe rForPaintInvalidation();
304 if (!currentPaintInvalidationContainer.styleRef().isStackingContext()) 336 if (!currentPaintInvalidationContainer.styleRef().isStackingContext())
305 invalidatePaintIncludingNonSelfPaintingLayerDescendants(currentPaint InvalidationContainer); 337 invalidatePaintIncludingNonSelfPaintingLayerDescendants(currentPaint InvalidationContainer);
(...skipping 297 matching lines...) Expand 10 before | Expand all | Expand 10 after
603 635
604 else if (!style()->bottom().isAuto() 636 else if (!style()->bottom().isAuto()
605 && (!containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight() 637 && (!containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight()
606 || !style()->bottom().hasPercent() 638 || !style()->bottom().hasPercent()
607 || containingBlock->stretchesToViewport())) 639 || containingBlock->stretchesToViewport()))
608 offset.expand(LayoutUnit(), -valueForLength(style()->bottom(), containin gBlock->availableHeight())); 640 offset.expand(LayoutUnit(), -valueForLength(style()->bottom(), containin gBlock->availableHeight()));
609 641
610 return offset; 642 return offset;
611 } 643 }
612 644
645 void LayoutBoxModelObject::updateStickyPositionConstraints() const
chrishtr 2016/03/24 17:36:02 It would be good to directly unittest this a bit.
flackr 2016/03/29 16:02:32 Done.
646 {
647 const FloatSize constrainingSize = computeStickyConstrainingRect().size();
648
649 PaintLayerScrollableArea* scrollableArea = layer()->ancestorOverflowLayer()- >getScrollableArea();
650 StickyPositionScrollingConstraints constraints;
651 FloatSize skippedContainersOffset;
652 LayoutBlock* containingBlock = this->containingBlock();
653 // Skip anonymous containing blocks.
654 while (containingBlock->isAnonymous()) {
655 skippedContainersOffset += toFloatSize(FloatPoint(containingBlock->frame Rect().location()));
656 containingBlock = containingBlock->containingBlock();
657 }
658 LayoutBox* scrollAncestor = layer()->ancestorOverflowLayer()->isRootLayer() ? nullptr : toLayoutBox(layer()->ancestorOverflowLayer()->layoutObject());
659
660 LayoutRect containerContentRect = containingBlock->contentBoxRect();
661 LayoutUnit maxWidth = containingBlock->availableLogicalWidth();
662
663 // Sticky positioned element ignore any override logical width on the contai ning block (as they don't call
664 // containingBlockLogicalWidthForContent). It's unclear whether this is tota lly fine.
665 // Compute the container-relative area within which the sticky element is al lowed to move.
666 containerContentRect.contractEdges(
667 minimumValueForLength(style()->marginTop(), maxWidth),
668 minimumValueForLength(style()->marginRight(), maxWidth),
669 minimumValueForLength(style()->marginBottom(), maxWidth),
670 minimumValueForLength(style()->marginLeft(), maxWidth));
671
672 // Map to the scroll ancestor.
673 constraints.setScrollContainerRelativeContainingBlockRect(containingBlock->l ocalToAncestorQuad(FloatRect(containerContentRect), scrollAncestor).boundingBox( ));
674
675 FloatRect stickyBoxRect = isLayoutInline()
676 ? FloatRect(toLayoutInline(this)->linesBoundingBox())
677 : FloatRect(toLayoutBox(this)->frameRect());
678 FloatRect flippedStickyBoxRect = stickyBoxRect;
679 containingBlock->flipForWritingMode(flippedStickyBoxRect);
680 FloatPoint stickyLocation = flippedStickyBoxRect.location() + skippedContain ersOffset;
681
682 // TODO(flackr): Unfortunate to call localToAncestorQuad again, but we can't just offset from the previously computed rect if there are transforms.
683 // Map to the scroll ancestor.
684 FloatRect scrollContainerRelativeContainerFrame = containingBlock->localToAn cestorQuad(FloatRect(FloatPoint(), FloatSize(containingBlock->size())), scrollAn cestor).boundingBox();
685
686 // If the containing block is our scroll ancestor, its location will not inc lude the scroll offset which we need to include as
687 // part of the sticky box rect so we include it here.
688 if (containingBlock->hasOverflowClip()) {
689 FloatSize scrollOffset(toFloatSize(containingBlock->layer()->getScrollab leArea()->adjustedScrollOffset()));
690 stickyLocation -= scrollOffset;
691 }
692
693 constraints.setScrollContainerRelativeStickyBoxRect(FloatRect(scrollContaine rRelativeContainerFrame.location() + toFloatSize(stickyLocation), flippedStickyB oxRect.size()));
694
695 // 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.
696 LayoutUnit horizontalOffsets = minimumValueForLength(style()->right(), Layou tUnit(constrainingSize.width())) +
697 minimumValueForLength(style()->left(), LayoutUnit(constrainingSize.width ()));
698 bool skipRight = false;
699 bool skipLeft = false;
700 if (!style()->left().isAuto() && !style()->right().isAuto()) {
701 if (horizontalOffsets > containerContentRect.width()
702 || horizontalOffsets + containerContentRect.width() > constrainingSi ze.width()) {
703 skipRight = style()->isLeftToRightDirection();
704 skipLeft = !skipRight;
705 }
706 }
707
708 if (!style()->left().isAuto() && !skipLeft) {
709 constraints.setLeftOffset(minimumValueForLength(style()->left(), LayoutU nit(constrainingSize.width())));
710 constraints.addAnchorEdge(StickyPositionScrollingConstraints::AnchorEdge Left);
711 }
712
713 if (!style()->right().isAuto() && !skipRight) {
714 constraints.setRightOffset(minimumValueForLength(style()->right(), Layou tUnit(constrainingSize.width())));
715 constraints.addAnchorEdge(StickyPositionScrollingConstraints::AnchorEdge Right);
716 }
717
718 bool skipBottom = false;
719 // TODO(flackr): Exclude top or bottom edge offset depending on the writing mode when related
720 // sections are fixed in spec: http://lists.w3.org/Archives/Public/www-style /2014May/0286.html
721 LayoutUnit verticalOffsets = minimumValueForLength(style()->top(), LayoutUni t(constrainingSize.height())) +
722 minimumValueForLength(style()->bottom(), LayoutUnit(constrainingSize.hei ght()));
723 if (!style()->top().isAuto() && !style()->bottom().isAuto()) {
724 if (verticalOffsets > containerContentRect.height()
725 || verticalOffsets + containerContentRect.height() > constrainingSiz e.height()) {
726 skipBottom = true;
727 }
728 }
729
730 if (!style()->top().isAuto()) {
731 constraints.setTopOffset(minimumValueForLength(style()->top(), LayoutUni t(constrainingSize.height())));
732 constraints.addAnchorEdge(StickyPositionScrollingConstraints::AnchorEdge Top);
733 }
734
735 if (!style()->bottom().isAuto() && !skipBottom) {
736 constraints.setBottomOffset(minimumValueForLength(style()->bottom(), Lay outUnit(constrainingSize.height())));
737 constraints.addAnchorEdge(StickyPositionScrollingConstraints::AnchorEdge Bottom);
738 }
739 scrollableArea->stickyConstraintsMap().set(layer(), constraints);
740 }
741
742 FloatRect LayoutBoxModelObject::computeStickyConstrainingRect() const
743 {
744 if (layer()->ancestorOverflowLayer()->isRootLayer())
745 return view()->frameView()->visibleContentRect();
746
747 LayoutBox* enclosingClippingBox = toLayoutBox(layer()->ancestorOverflowLayer ()->layoutObject());
748 FloatRect constrainingRect;
749 constrainingRect = FloatRect(enclosingClippingBox->overflowClipRect(LayoutPo int()));
750 constrainingRect.move(enclosingClippingBox->paddingLeft(), enclosingClipping Box->paddingTop());
751 constrainingRect.contract(FloatSize(enclosingClippingBox->paddingLeft() + en closingClippingBox->paddingRight(),
752 enclosingClippingBox->paddingTop() + enclosingClippingBox->paddingBottom ()));
753 return constrainingRect;
754 }
755
756 LayoutSize LayoutBoxModelObject::stickyPositionOffset() const
757 {
758 const PaintLayer* ancestorOverflowLayer = layer()->ancestorOverflowLayer();
759 // TODO: Force compositing input update if we ask for offset before composit ing inputs have been computed?
760 if (!ancestorOverflowLayer)
761 return LayoutSize();
762 FloatRect constrainingRect = computeStickyConstrainingRect();
763 PaintLayerScrollableArea* scrollableArea = ancestorOverflowLayer->getScrolla bleArea();
764
765 // The sticky offset is physical, so we can just return the delta computed i n absolute coords (though it may be wrong with transforms).
766 // TODO: Force compositing input update if we ask for offset with stale comp ositing inputs.
767 if (!scrollableArea->stickyConstraintsMap().contains(layer()))
768 return LayoutSize();
769 return LayoutSize(scrollableArea->stickyConstraintsMap().get(layer()).comput eStickyOffset(constrainingRect));
770 }
771
613 LayoutPoint LayoutBoxModelObject::adjustedPositionRelativeToOffsetParent(const L ayoutPoint& startPoint) const 772 LayoutPoint LayoutBoxModelObject::adjustedPositionRelativeToOffsetParent(const L ayoutPoint& startPoint) const
614 { 773 {
615 // If the element is the HTML body element or doesn't have a parent 774 // If the element is the HTML body element or doesn't have a parent
616 // return 0 and stop this algorithm. 775 // return 0 and stop this algorithm.
617 if (isBody() || !parent()) 776 if (isBody() || !parent())
618 return LayoutPoint(); 777 return LayoutPoint();
619 778
620 LayoutPoint referencePoint = startPoint; 779 LayoutPoint referencePoint = startPoint;
621 referencePoint.move(parent()->columnOffset(referencePoint)); 780 referencePoint.move(parent()->columnOffset(referencePoint));
622 781
623 // If the offsetParent of the element is null, or is the HTML body element, 782 // If the offsetParent of the element is null, or is the HTML body element,
624 // return the distance between the canvas origin and the left border edge 783 // return the distance between the canvas origin and the left border edge
625 // of the element and stop this algorithm. 784 // of the element and stop this algorithm.
626 Element* element = offsetParent(); 785 Element* element = offsetParent();
627 if (!element) 786 if (!element)
628 return referencePoint; 787 return referencePoint;
629 788
630 if (const LayoutBoxModelObject* offsetParent = element->layoutBoxModelObject ()) { 789 if (const LayoutBoxModelObject* offsetParent = element->layoutBoxModelObject ()) {
631 if (offsetParent->isBox() && !offsetParent->isBody()) 790 if (offsetParent->isBox() && !offsetParent->isBody())
632 referencePoint.move(-toLayoutBox(offsetParent)->borderLeft(), -toLay outBox(offsetParent)->borderTop()); 791 referencePoint.move(-toLayoutBox(offsetParent)->borderLeft(), -toLay outBox(offsetParent)->borderTop());
633 if (!isOutOfFlowPositioned() || flowThreadContainingBlock()) { 792 if (!isOutOfFlowPositioned() || flowThreadContainingBlock()) {
634 if (isInFlowPositioned()) 793 if (isInFlowPositioned())
635 referencePoint.move(relativePositionOffset()); 794 referencePoint.move(offsetForInFlowPosition());
636 795
637 LayoutObject* current; 796 LayoutObject* current;
638 for (current = parent(); current != offsetParent && current->parent( ); current = current->parent()) { 797 for (current = parent(); current != offsetParent && current->parent( ); current = current->parent()) {
639 // FIXME: What are we supposed to do inside SVG content? 798 // FIXME: What are we supposed to do inside SVG content?
640 if (!isOutOfFlowPositioned()) { 799 if (!isOutOfFlowPositioned()) {
641 if (current->isBox() && !current->isTableRow()) 800 if (current->isBox() && !current->isTableRow())
642 referencePoint.moveBy(toLayoutBox(current)->topLeftLocat ion()); 801 referencePoint.moveBy(toLayoutBox(current)->topLeftLocat ion());
643 referencePoint.move(current->parent()->columnOffset(referenc ePoint)); 802 referencePoint.move(current->parent()->columnOffset(referenc ePoint));
644 } 803 }
645 } 804 }
646 805
647 if (offsetParent->isBox() && offsetParent->isBody() && !offsetParent ->isPositioned()) 806 if (offsetParent->isBox() && offsetParent->isBody() && !offsetParent ->isPositioned())
648 referencePoint.moveBy(toLayoutBox(offsetParent)->topLeftLocation ()); 807 referencePoint.moveBy(toLayoutBox(offsetParent)->topLeftLocation ());
649 } 808 }
650 } 809 }
651 810
652 return referencePoint; 811 return referencePoint;
653 } 812 }
654 813
655 LayoutSize LayoutBoxModelObject::offsetForInFlowPosition() const 814 LayoutSize LayoutBoxModelObject::offsetForInFlowPosition() const
656 { 815 {
657 return isRelPositioned() ? relativePositionOffset() : LayoutSize(); 816 if (isRelPositioned())
817 return relativePositionOffset();
818
819 if (isStickyPositioned())
820 return stickyPositionOffset();
821
822 return LayoutSize();
658 } 823 }
659 824
660 LayoutUnit LayoutBoxModelObject::offsetLeft() const 825 LayoutUnit LayoutBoxModelObject::offsetLeft() const
661 { 826 {
662 // Note that LayoutInline and LayoutBox override this to pass a different 827 // Note that LayoutInline and LayoutBox override this to pass a different
663 // startPoint to adjustedPositionRelativeToOffsetParent. 828 // startPoint to adjustedPositionRelativeToOffsetParent.
664 return adjustedPositionRelativeToOffsetParent(LayoutPoint()).x(); 829 return adjustedPositionRelativeToOffsetParent(LayoutPoint()).x();
665 } 830 }
666 831
667 LayoutUnit LayoutBoxModelObject::offsetTop() const 832 LayoutUnit LayoutBoxModelObject::offsetTop() const
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after
880 if (offsetDependsOnPoint) 1045 if (offsetDependsOnPoint)
881 flags |= IsNonUniform; 1046 flags |= IsNonUniform;
882 if (isFixedPos) 1047 if (isFixedPos)
883 flags |= IsFixedPosition; 1048 flags |= IsFixedPosition;
884 if (hasTransform) 1049 if (hasTransform)
885 flags |= HasTransform; 1050 flags |= HasTransform;
886 if (shouldUseTransformFromContainer(container)) { 1051 if (shouldUseTransformFromContainer(container)) {
887 TransformationMatrix t; 1052 TransformationMatrix t;
888 getTransformFromContainer(container, containerOffset, t); 1053 getTransformFromContainer(container, containerOffset, t);
889 t.translateRight(adjustmentForSkippedAncestor.width().toFloat(), adjustm entForSkippedAncestor.height().toFloat()); 1054 t.translateRight(adjustmentForSkippedAncestor.width().toFloat(), adjustm entForSkippedAncestor.height().toFloat());
890 geometryMap.push(this, t, flags); 1055 geometryMap.push(this, t, flags, LayoutSize());
891 } else { 1056 } else {
892 containerOffset += adjustmentForSkippedAncestor; 1057 containerOffset += adjustmentForSkippedAncestor;
893 geometryMap.push(this, containerOffset, flags); 1058 geometryMap.push(this, containerOffset, flags, LayoutSize());
894 } 1059 }
895 1060
896 return ancestorSkipped ? ancestorToStopAt : container; 1061 return ancestorSkipped ? ancestorToStopAt : container;
897 } 1062 }
898 1063
899 void LayoutBoxModelObject::moveChildTo(LayoutBoxModelObject* toBoxModelObject, L ayoutObject* child, LayoutObject* beforeChild, bool fullRemoveInsert) 1064 void LayoutBoxModelObject::moveChildTo(LayoutBoxModelObject* toBoxModelObject, L ayoutObject* child, LayoutObject* beforeChild, bool fullRemoveInsert)
900 { 1065 {
901 // We assume that callers have cleared their positioned objects list for chi ld moves (!fullRemoveInsert) so the 1066 // We assume that callers have cleared their positioned objects list for chi ld moves (!fullRemoveInsert) so the
902 // positioned layoutObject maps don't become stale. It would be too slow to do the map lookup on each call. 1067 // positioned layoutObject maps don't become stale. It would be too slow to do the map lookup on each call.
903 ASSERT(!fullRemoveInsert || !isLayoutBlock() || !toLayoutBlock(this)->hasPos itionedObjects()); 1068 ASSERT(!fullRemoveInsert || !isLayoutBlock() || !toLayoutBlock(this)->hasPos itionedObjects());
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
965 if (rootElementStyle->hasBackground()) 1130 if (rootElementStyle->hasBackground())
966 return false; 1131 return false;
967 1132
968 if (node() != document().firstBodyElement()) 1133 if (node() != document().firstBodyElement())
969 return false; 1134 return false;
970 1135
971 return true; 1136 return true;
972 } 1137 }
973 1138
974 } // namespace blink 1139 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698