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

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

Issue 1870663002: Reland main thread position sticky implementation. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Only descend into children which have an ancestor overflow layer. 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 ASSERT(!m_layer); 337 ASSERT(!m_layer);
299 m_layer = adoptPtr(new PaintLayer(this, type)); 338 m_layer = adoptPtr(new PaintLayer(this, type));
300 setHasLayer(true); 339 setHasLayer(true);
301 m_layer->insertOnlyThisLayerAfterStyleChange(); 340 m_layer->insertOnlyThisLayerAfterStyleChange();
302 } 341 }
303 342
304 void LayoutBoxModelObject::destroyLayer() 343 void LayoutBoxModelObject::destroyLayer()
305 { 344 {
(...skipping 285 matching lines...) Expand 10 before | Expand all | Expand 10 after
591 630
592 else if (!style()->bottom().isAuto() 631 else if (!style()->bottom().isAuto()
593 && (!containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight() 632 && (!containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight()
594 || !style()->bottom().hasPercent() 633 || !style()->bottom().hasPercent()
595 || containingBlock->stretchesToViewport())) 634 || containingBlock->stretchesToViewport()))
596 offset.expand(LayoutUnit(), -valueForLength(style()->bottom(), containin gBlock->availableHeight())); 635 offset.expand(LayoutUnit(), -valueForLength(style()->bottom(), containin gBlock->availableHeight()));
597 636
598 return offset; 637 return offset;
599 } 638 }
600 639
640 void LayoutBoxModelObject::updateStickyPositionConstraints() const
641 {
642 // TODO(flackr): This method is reasonably complicated and should have some direct unit testing.
643 const FloatSize constrainingSize = computeStickyConstrainingRect().size();
644
645 PaintLayerScrollableArea* scrollableArea = layer()->ancestorOverflowLayer()- >getScrollableArea();
646 StickyPositionScrollingConstraints constraints;
647 FloatSize skippedContainersOffset;
648 LayoutBlock* containingBlock = this->containingBlock();
649 // Skip anonymous containing blocks.
650 while (containingBlock->isAnonymous()) {
651 skippedContainersOffset += toFloatSize(FloatPoint(containingBlock->frame Rect().location()));
652 containingBlock = containingBlock->containingBlock();
653 }
654 LayoutBox* scrollAncestor = layer()->ancestorOverflowLayer()->isRootLayer() ? nullptr : toLayoutBox(layer()->ancestorOverflowLayer()->layoutObject());
655
656 LayoutRect containerContentRect = containingBlock->contentBoxRect();
657 LayoutUnit maxWidth = containingBlock->availableLogicalWidth();
658
659 // Sticky positioned element ignore any override logical width on the contai ning block (as they don't call
660 // containingBlockLogicalWidthForContent). It's unclear whether this is tota lly fine.
661 // Compute the container-relative area within which the sticky element is al lowed to move.
662 containerContentRect.contractEdges(
663 minimumValueForLength(style()->marginTop(), maxWidth),
664 minimumValueForLength(style()->marginRight(), maxWidth),
665 minimumValueForLength(style()->marginBottom(), maxWidth),
666 minimumValueForLength(style()->marginLeft(), maxWidth));
667
668 // Map to the scroll ancestor.
669 constraints.setScrollContainerRelativeContainingBlockRect(containingBlock->l ocalToAncestorQuad(FloatRect(containerContentRect), scrollAncestor).boundingBox( ));
670
671 FloatRect stickyBoxRect = isLayoutInline()
672 ? FloatRect(toLayoutInline(this)->linesBoundingBox())
673 : FloatRect(toLayoutBox(this)->frameRect());
674 FloatRect flippedStickyBoxRect = stickyBoxRect;
675 containingBlock->flipForWritingMode(flippedStickyBoxRect);
676 FloatPoint stickyLocation = flippedStickyBoxRect.location() + skippedContain ersOffset;
677
678 // TODO(flackr): Unfortunate to call localToAncestorQuad again, but we can't just offset from the previously computed rect if there are transforms.
679 // Map to the scroll ancestor.
680 FloatRect scrollContainerRelativeContainerFrame = containingBlock->localToAn cestorQuad(FloatRect(FloatPoint(), FloatSize(containingBlock->size())), scrollAn cestor).boundingBox();
681
682 // If the containing block is our scroll ancestor, its location will not inc lude the scroll offset which we need to include as
683 // part of the sticky box rect so we include it here.
684 if (containingBlock->hasOverflowClip()) {
685 FloatSize scrollOffset(toFloatSize(containingBlock->layer()->getScrollab leArea()->adjustedScrollOffset()));
686 stickyLocation -= scrollOffset;
687 }
688
689 constraints.setScrollContainerRelativeStickyBoxRect(FloatRect(scrollContaine rRelativeContainerFrame.location() + toFloatSize(stickyLocation), flippedStickyB oxRect.size()));
690
691 // 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.
692 LayoutUnit horizontalOffsets = minimumValueForLength(style()->right(), Layou tUnit(constrainingSize.width())) +
693 minimumValueForLength(style()->left(), LayoutUnit(constrainingSize.width ()));
694 bool skipRight = false;
695 bool skipLeft = false;
696 if (!style()->left().isAuto() && !style()->right().isAuto()) {
697 if (horizontalOffsets > containerContentRect.width()
698 || horizontalOffsets + containerContentRect.width() > constrainingSi ze.width()) {
699 skipRight = style()->isLeftToRightDirection();
700 skipLeft = !skipRight;
701 }
702 }
703
704 if (!style()->left().isAuto() && !skipLeft) {
705 constraints.setLeftOffset(minimumValueForLength(style()->left(), LayoutU nit(constrainingSize.width())));
706 constraints.addAnchorEdge(StickyPositionScrollingConstraints::AnchorEdge Left);
707 }
708
709 if (!style()->right().isAuto() && !skipRight) {
710 constraints.setRightOffset(minimumValueForLength(style()->right(), Layou tUnit(constrainingSize.width())));
711 constraints.addAnchorEdge(StickyPositionScrollingConstraints::AnchorEdge Right);
712 }
713
714 bool skipBottom = false;
715 // TODO(flackr): Exclude top or bottom edge offset depending on the writing mode when related
716 // sections are fixed in spec: http://lists.w3.org/Archives/Public/www-style /2014May/0286.html
717 LayoutUnit verticalOffsets = minimumValueForLength(style()->top(), LayoutUni t(constrainingSize.height())) +
718 minimumValueForLength(style()->bottom(), LayoutUnit(constrainingSize.hei ght()));
719 if (!style()->top().isAuto() && !style()->bottom().isAuto()) {
720 if (verticalOffsets > containerContentRect.height()
721 || verticalOffsets + containerContentRect.height() > constrainingSiz e.height()) {
722 skipBottom = true;
723 }
724 }
725
726 if (!style()->top().isAuto()) {
727 constraints.setTopOffset(minimumValueForLength(style()->top(), LayoutUni t(constrainingSize.height())));
728 constraints.addAnchorEdge(StickyPositionScrollingConstraints::AnchorEdge Top);
729 }
730
731 if (!style()->bottom().isAuto() && !skipBottom) {
732 constraints.setBottomOffset(minimumValueForLength(style()->bottom(), Lay outUnit(constrainingSize.height())));
733 constraints.addAnchorEdge(StickyPositionScrollingConstraints::AnchorEdge Bottom);
734 }
735 scrollableArea->stickyConstraintsMap().set(layer(), constraints);
736 }
737
738 FloatRect LayoutBoxModelObject::computeStickyConstrainingRect() const
739 {
740 if (layer()->ancestorOverflowLayer()->isRootLayer())
741 return view()->frameView()->visibleContentRect();
742
743 LayoutBox* enclosingClippingBox = toLayoutBox(layer()->ancestorOverflowLayer ()->layoutObject());
744 FloatRect constrainingRect;
745 constrainingRect = FloatRect(enclosingClippingBox->overflowClipRect(LayoutPo int()));
746 constrainingRect.move(enclosingClippingBox->paddingLeft(), enclosingClipping Box->paddingTop());
747 constrainingRect.contract(FloatSize(enclosingClippingBox->paddingLeft() + en closingClippingBox->paddingRight(),
748 enclosingClippingBox->paddingTop() + enclosingClippingBox->paddingBottom ()));
749 return constrainingRect;
750 }
751
752 LayoutSize LayoutBoxModelObject::stickyPositionOffset() const
753 {
754 const PaintLayer* ancestorOverflowLayer = layer()->ancestorOverflowLayer();
755 // TODO: Force compositing input update if we ask for offset before composit ing inputs have been computed?
756 if (!ancestorOverflowLayer)
757 return LayoutSize();
758 FloatRect constrainingRect = computeStickyConstrainingRect();
759 PaintLayerScrollableArea* scrollableArea = ancestorOverflowLayer->getScrolla bleArea();
760
761 // The sticky offset is physical, so we can just return the delta computed i n absolute coords (though it may be wrong with transforms).
762 // TODO: Force compositing input update if we ask for offset with stale comp ositing inputs.
763 if (!scrollableArea->stickyConstraintsMap().contains(layer()))
764 return LayoutSize();
765 return LayoutSize(scrollableArea->stickyConstraintsMap().get(layer()).comput eStickyOffset(constrainingRect));
766 }
767
601 LayoutPoint LayoutBoxModelObject::adjustedPositionRelativeToOffsetParent(const L ayoutPoint& startPoint) const 768 LayoutPoint LayoutBoxModelObject::adjustedPositionRelativeToOffsetParent(const L ayoutPoint& startPoint) const
602 { 769 {
603 // If the element is the HTML body element or doesn't have a parent 770 // If the element is the HTML body element or doesn't have a parent
604 // return 0 and stop this algorithm. 771 // return 0 and stop this algorithm.
605 if (isBody() || !parent()) 772 if (isBody() || !parent())
606 return LayoutPoint(); 773 return LayoutPoint();
607 774
608 LayoutPoint referencePoint = startPoint; 775 LayoutPoint referencePoint = startPoint;
609 referencePoint.move(parent()->columnOffset(referencePoint)); 776 referencePoint.move(parent()->columnOffset(referencePoint));
610 777
611 // If the offsetParent of the element is null, or is the HTML body element, 778 // If the offsetParent of the element is null, or is the HTML body element,
612 // return the distance between the canvas origin and the left border edge 779 // return the distance between the canvas origin and the left border edge
613 // of the element and stop this algorithm. 780 // of the element and stop this algorithm.
614 Element* element = offsetParent(); 781 Element* element = offsetParent();
615 if (!element) 782 if (!element)
616 return referencePoint; 783 return referencePoint;
617 784
618 if (const LayoutBoxModelObject* offsetParent = element->layoutBoxModelObject ()) { 785 if (const LayoutBoxModelObject* offsetParent = element->layoutBoxModelObject ()) {
619 if (offsetParent->isBox() && !offsetParent->isBody()) 786 if (offsetParent->isBox() && !offsetParent->isBody())
620 referencePoint.move(-toLayoutBox(offsetParent)->borderLeft(), -toLay outBox(offsetParent)->borderTop()); 787 referencePoint.move(-toLayoutBox(offsetParent)->borderLeft(), -toLay outBox(offsetParent)->borderTop());
621 if (!isOutOfFlowPositioned() || flowThreadContainingBlock()) { 788 if (!isOutOfFlowPositioned() || flowThreadContainingBlock()) {
622 if (isInFlowPositioned()) 789 if (isInFlowPositioned())
623 referencePoint.move(relativePositionOffset()); 790 referencePoint.move(offsetForInFlowPosition());
624 791
625 LayoutObject* current; 792 LayoutObject* current;
626 for (current = parent(); current != offsetParent && current->parent( ); current = current->parent()) { 793 for (current = parent(); current != offsetParent && current->parent( ); current = current->parent()) {
627 // FIXME: What are we supposed to do inside SVG content? 794 // FIXME: What are we supposed to do inside SVG content?
628 if (!isOutOfFlowPositioned()) { 795 if (!isOutOfFlowPositioned()) {
629 if (current->isBox() && !current->isTableRow()) 796 if (current->isBox() && !current->isTableRow())
630 referencePoint.moveBy(toLayoutBox(current)->topLeftLocat ion()); 797 referencePoint.moveBy(toLayoutBox(current)->topLeftLocat ion());
631 referencePoint.move(current->parent()->columnOffset(referenc ePoint)); 798 referencePoint.move(current->parent()->columnOffset(referenc ePoint));
632 } 799 }
633 } 800 }
634 801
635 if (offsetParent->isBox() && offsetParent->isBody() && !offsetParent ->isPositioned()) 802 if (offsetParent->isBox() && offsetParent->isBody() && !offsetParent ->isPositioned())
636 referencePoint.moveBy(toLayoutBox(offsetParent)->topLeftLocation ()); 803 referencePoint.moveBy(toLayoutBox(offsetParent)->topLeftLocation ());
637 } 804 }
638 } 805 }
639 806
640 return referencePoint; 807 return referencePoint;
641 } 808 }
642 809
643 LayoutSize LayoutBoxModelObject::offsetForInFlowPosition() const 810 LayoutSize LayoutBoxModelObject::offsetForInFlowPosition() const
644 { 811 {
645 return isRelPositioned() ? relativePositionOffset() : LayoutSize(); 812 if (isRelPositioned())
813 return relativePositionOffset();
814
815 if (isStickyPositioned())
816 return stickyPositionOffset();
817
818 return LayoutSize();
646 } 819 }
647 820
648 LayoutUnit LayoutBoxModelObject::offsetLeft() const 821 LayoutUnit LayoutBoxModelObject::offsetLeft() const
649 { 822 {
650 // Note that LayoutInline and LayoutBox override this to pass a different 823 // Note that LayoutInline and LayoutBox override this to pass a different
651 // startPoint to adjustedPositionRelativeToOffsetParent. 824 // startPoint to adjustedPositionRelativeToOffsetParent.
652 return adjustedPositionRelativeToOffsetParent(LayoutPoint()).x(); 825 return adjustedPositionRelativeToOffsetParent(LayoutPoint()).x();
653 } 826 }
654 827
655 LayoutUnit LayoutBoxModelObject::offsetTop() const 828 LayoutUnit LayoutBoxModelObject::offsetTop() const
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after
874 if (offsetDependsOnPoint) 1047 if (offsetDependsOnPoint)
875 flags |= IsNonUniform; 1048 flags |= IsNonUniform;
876 if (isFixedPos) 1049 if (isFixedPos)
877 flags |= IsFixedPosition; 1050 flags |= IsFixedPosition;
878 if (hasTransform) 1051 if (hasTransform)
879 flags |= HasTransform; 1052 flags |= HasTransform;
880 if (shouldUseTransformFromContainer(container)) { 1053 if (shouldUseTransformFromContainer(container)) {
881 TransformationMatrix t; 1054 TransformationMatrix t;
882 getTransformFromContainer(container, containerOffset, t); 1055 getTransformFromContainer(container, containerOffset, t);
883 t.translateRight(adjustmentForSkippedAncestor.width().toFloat(), adjustm entForSkippedAncestor.height().toFloat()); 1056 t.translateRight(adjustmentForSkippedAncestor.width().toFloat(), adjustm entForSkippedAncestor.height().toFloat());
884 geometryMap.push(this, t, flags); 1057 geometryMap.push(this, t, flags, LayoutSize());
885 } else { 1058 } else {
886 containerOffset += adjustmentForSkippedAncestor; 1059 containerOffset += adjustmentForSkippedAncestor;
887 geometryMap.push(this, containerOffset, flags); 1060 geometryMap.push(this, containerOffset, flags, LayoutSize());
888 } 1061 }
889 1062
890 return ancestorSkipped ? ancestorToStopAt : container; 1063 return ancestorSkipped ? ancestorToStopAt : container;
891 } 1064 }
892 1065
893 void LayoutBoxModelObject::moveChildTo(LayoutBoxModelObject* toBoxModelObject, L ayoutObject* child, LayoutObject* beforeChild, bool fullRemoveInsert) 1066 void LayoutBoxModelObject::moveChildTo(LayoutBoxModelObject* toBoxModelObject, L ayoutObject* child, LayoutObject* beforeChild, bool fullRemoveInsert)
894 { 1067 {
895 // We assume that callers have cleared their positioned objects list for chi ld moves (!fullRemoveInsert) so the 1068 // We assume that callers have cleared their positioned objects list for chi ld moves (!fullRemoveInsert) so the
896 // positioned layoutObject maps don't become stale. It would be too slow to do the map lookup on each call. 1069 // positioned layoutObject maps don't become stale. It would be too slow to do the map lookup on each call.
897 ASSERT(!fullRemoveInsert || !isLayoutBlock() || !toLayoutBlock(this)->hasPos itionedObjects()); 1070 ASSERT(!fullRemoveInsert || !isLayoutBlock() || !toLayoutBlock(this)->hasPos itionedObjects());
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
959 if (rootElementStyle->hasBackground()) 1132 if (rootElementStyle->hasBackground())
960 return false; 1133 return false;
961 1134
962 if (node() != document().firstBodyElement()) 1135 if (node() != document().firstBodyElement())
963 return false; 1136 return false;
964 1137
965 return true; 1138 return true;
966 } 1139 }
967 1140
968 } // namespace blink 1141 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698