Source/WebCore/ChangeLog

 12012-07-24 Simon Fraser <simon.fraser@apple.com>
 2
 3 Implement sticky positioning
 4 https://bugs.webkit.org/show_bug.cgi?id=90046
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Initial implementation of position: -webit-sticky, which
 9 constraints an element to be positioned inside the interection
 10 of its container box, and the viewport.
 11
 12 A stickily positioned element behaves like position:relative
 13 (space is reserved for it in-flow), but with an offset that is
 14 determined by the sticky position. Added isInFlowPositioned()
 15 to cover relative and sticky.
 16
 17 Tests: fast/css/sticky/inflow-sticky.html
 18 fast/css/sticky/inline-sticky.html
 19 fast/css/sticky/replaced-sticky.html
 20 fast/css/sticky/sticky-as-positioning-container.html
 21 fast/css/sticky/sticky-left-percentage.html
 22 fast/css/sticky/sticky-left.html
 23 fast/css/sticky/sticky-top.html
 24
 25 * page/FrameView.cpp:
 26 (WebCore::FrameView::scrollContentsFastPath): Need to consider sticky
 27 as well as fixed elements when scrolling.
 28 * page/FrameView.h: Comment to note that m_fixedObjects contains
 29 both fixed and sticky objects.
 30 * rendering/LayoutState.cpp:
 31 (WebCore::LayoutState::LayoutState): isRelPositioned() -> isInFlowPositioned().
 32 * rendering/RenderBlock.cpp:
 33 (WebCore::RenderBlock::isSelectionRoot): isRelPositioned() -> isInFlowPositioned().
 34 (WebCore::RenderBlock::blockSelectionGaps): Ditto. Also RenderLayer's relativePositionOffset()
 35 is renamed to offsetForInFlowPosition().
 36 (WebCore::positionForPointRespectingEditingBoundaries): Handle sticky.
 37 * rendering/RenderBox.cpp:
 38 (WebCore::RenderBox::styleWillChange): Take sticky into account when registering/unregistering
 39 fixed objects.
 40 (WebCore::RenderBox::mapLocalToContainer): Take sticky into account.
 41 (WebCore::RenderBox::mapAbsoluteToLocalPoint): This does now get called during layout, but
 42 there's no harm in it, so removing the assertion.
 43 (WebCore::RenderBox::offsetFromContainer): Take sticky into account.
 44 (WebCore::RenderBox::computeRectForRepaint):
 45 (WebCore::RenderBox::layoutOverflowRectForPropagation):
 46 * rendering/RenderBox.h: Sticky positioning forces a layer, like relative.
 47 Add virtual frameRectForStickyPositioning() method that RenderInline overrides to
 48 handle split inlines.
 49 * rendering/RenderBoxModelObject.cpp:
 50 (WebCore::RenderBoxModelObject::updateBoxModelInfoFromStyle): Call setStickyPositioned().
 51 (WebCore::RenderBoxModelObject::adjustedPositionRelativeToOffsetParent): Take any
 52 sticky offset into account.
 53 (WebCore::RenderBoxModelObject::stickyPositionOffset): Compute the sticky offset. This maps
 54 the viewport rect into the coordinate space of the containing block, and then computes
 55 the sticky offset using the viewport and containing block rects as constraints.
 56 (WebCore::RenderBoxModelObject::mapAbsoluteToLocalPoint): Remove the assertion about
 57 being called during layout. We use this to map the viewport rect to local coords when
 58 computing the sticky offset.
 59 * rendering/RenderBoxModelObject.h: Add some OVERRIDE.
 60 (WebCore::RenderBoxModelObject::stickyPositionLogicalOffset): New method.
 61 (WebCore::RenderBoxModelObject::requiresLayer): Use isInFlowPositioned().
 62 * rendering/RenderInline.cpp:
 63 (WebCore::updateStyleOfAnonymousBlockContinuations): Add a FIXME
 64 (WebCore::RenderInline::styleWillChange): Implement this here to handle
 65 sticky on inlines. RenderBox handles sticky on other renderers.
 66 (WebCore::RenderInline::styleDidChange): Some FIXMEs related to continutations.
 67 (WebCore::RenderInline::addChildIgnoringContinuation):
 68 (WebCore::RenderInline::clippedOverflowRectForRepaint):
 69 (WebCore::RenderInline::computeRectForRepaint):
 70 (WebCore::RenderInline::offsetFromContainer):
 71 (WebCore::RenderInline::mapLocalToContainer):
 72 * rendering/RenderInline.h:
 73 (WebCore::RenderInline::requiresLayer):
 74 * rendering/RenderLayer.cpp:
 75 (WebCore::RenderLayer::updateLayerPositionsAfterScroll): Need to look for both sticky
 76 and fixed elements when updating layer positions.
 77 (WebCore::RenderLayer::updateLayerPosition): Store the sticky position
 78 offset in the layer's 'offsetForInFlowPosition'.
 79 (WebCore::isPositionedContainer): Use isInFlowPositioned().
 80 (WebCore::RenderLayer::calculateClipRects): Sticky behaves like relative positioning
 81 for clipping.
 82 (WebCore::RenderLayer::shouldBeNormalFlowOnly):
 83 * rendering/RenderLayer.h:
 84 (WebCore::RenderLayer::offsetForInFlowPosition):
 85 (RenderLayer):
 86 * rendering/RenderObject.h:
 87 (RenderObject):
 88 (WebCore::RenderObject::isInFlowPositioned):
 89 (WebCore::RenderObject::isRelPositioned):
 90 (WebCore::RenderObject::isStickyPositioned):
 91 (WebCore::RenderObject::setStickyPositioned):
 92 (WebCore::RenderObject::RenderObjectBitfields::RenderObjectBitfields):
 93 (RenderObjectBitfields):
 94
1952012-07-24 Kent Tamura <tkent@chromium.org>
296
397 Unreviewed, rolling out r123191.

Source/WebCore/page/FrameView.cpp

@@bool FrameView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect
14651465 FixedObjectSet::const_iterator end = m_fixedObjects->end();
14661466 for (FixedObjectSet::const_iterator it = m_fixedObjects->begin(); it != end; ++it) {
14671467 RenderObject* renderer = *it;
1468  if (renderer->style()->position() != FixedPosition)
 1468 if (renderer->style()->position() != FixedPosition && renderer->style()->position() != StickyPosition)
14691469 continue;
14701470#if USE(ACCELERATED_COMPOSITING)
14711471 if (renderer->isComposited())

Source/WebCore/page/FrameView.h

@@public:
194194 void removeSlowRepaintObject();
195195 bool hasSlowRepaintObjects() const { return m_slowRepaintObjectCount; }
196196
 197 // This includes position:fixed and sticky objects.
197198 typedef HashSet<RenderObject*> FixedObjectSet;
198199 void addFixedObject(RenderObject*);
199200 void removeFixedObject(RenderObject*);

Source/WebCore/rendering/LayoutState.cpp

@@LayoutState::LayoutState(LayoutState* prev, RenderBox* renderer, const LayoutSiz
6262
6363 m_layoutOffset = m_paintOffset;
6464
65  if (renderer->isRelPositioned() && renderer->hasLayer())
66  m_paintOffset += renderer->layer()->relativePositionOffset();
 65 if (renderer->isInFlowPositioned() && renderer->hasLayer())
 66 m_paintOffset += renderer->layer()->offsetForInFlowPosition();
6767
6868 m_clipped = !fixed && prev->m_clipped;
6969 if (m_clipped)

Source/WebCore/rendering/RenderBlock.cpp

@@bool RenderBlock::isSelectionRoot() const
32033203 if (isTable())
32043204 return false;
32053205
3206  if (isBody() || isRoot() || hasOverflowClip() || isRelPositioned()
3207  || isFloatingOrOutOfFlowPositioned() || isTableCell() || isInlineBlockOrInlineTable() || hasTransform()
3208  || hasReflection() || hasMask() || isWritingModeRoot())
 3206 if (isBody() || isRoot() || hasOverflowClip()
 3207 || isInFlowPositioned() || isFloatingOrOutOfFlowPositioned()
 3208 || isTableCell() || isInlineBlockOrInlineTable()
 3209 || hasTransform() || hasReflection() || hasMask() || isWritingModeRoot())
32093210 return true;
32103211
32113212 if (view() && view()->selectionStart()) {

@@GapRects RenderBlock::blockSelectionGaps(RenderBlock* rootBlock, const LayoutPoi
34253426 if (curr->isFloatingOrOutOfFlowPositioned())
34263427 continue; // We must be a normal flow object in order to even be considered.
34273428
3428  if (curr->isRelPositioned() && curr->hasLayer()) {
 3429 if (curr->isInFlowPositioned() && curr->hasLayer()) {
34293430 // If the relposition offset is anything other than 0, then treat this just like an absolute positioned element.
34303431 // Just disregard it completely.
3431  LayoutSize relOffset = curr->layer()->relativePositionOffset();
 3432 LayoutSize relOffset = curr->layer()->offsetForInFlowPosition();
34323433 if (relOffset.width() || relOffset.height())
34333434 continue;
34343435 }

@@static VisiblePosition positionForPointRespectingEditingBoundaries(RenderBlock*
48894890 LayoutPoint childLocation = child->location();
48904891 if (child->isRelPositioned())
48914892 childLocation += child->relativePositionOffset();
 4893 else if (child->isStickyPositioned())
 4894 childLocation += child->stickyPositionOffset();
 4895
48924896 // FIXME: This is wrong if the child's writing-mode is different from the parent's.
48934897 LayoutPoint pointInChildCoordinates(toLayoutPoint(pointInParentCoordinates - childLocation));
48944898

Source/WebCore/rendering/RenderBox.cpp

@@void RenderBox::styleWillChange(StyleDifference diff, const RenderStyle* newStyl
210210 view()->repaint();
211211
212212 if (FrameView *frameView = view()->frameView()) {
213  bool newStyleIsFixed = newStyle && newStyle->position() == FixedPosition;
214  bool oldStyleIsFixed = oldStyle && oldStyle->position() == FixedPosition;
215  if (newStyleIsFixed != oldStyleIsFixed) {
216  if (newStyleIsFixed)
 213 bool newStyleIsFixedOrSticky = newStyle && (newStyle->position() == FixedPosition || newStyle->position() == StickyPosition);
 214 bool oldStyleIsFixedOrSticky = oldStyle && (oldStyle->position() == FixedPosition || oldStyle->position() == StickyPosition);
 215 if (newStyleIsFixedOrSticky != oldStyleIsFixedOrSticky) {
 216 if (newStyleIsFixedOrSticky)
217217 frameView->addFixedObject(this);
218218 else
219219 frameView->removeFixedObject(this);

@@void RenderBox::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool
12611261 if (v->layoutStateEnabled() && !repaintContainer) {
12621262 LayoutState* layoutState = v->layoutState();
12631263 LayoutSize offset = layoutState->m_paintOffset + locationOffset();
1264  if (style()->position() == RelativePosition && layer())
1265  offset += layer()->relativePositionOffset();
 1264 if ((style()->position() == RelativePosition || style()->position() == StickyPosition) && layer())
 1265 offset += layer()->offsetForInFlowPosition();
12661266 transformState.move(offset);
12671267 return;
12681268 }

@@const RenderObject* RenderBox::pushMappingToContainer(const RenderBoxModelObject
13561356
13571357void RenderBox::mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState& transformState) const
13581358{
1359  // We don't expect absoluteToLocal() to be called during layout (yet)
1360  ASSERT(!view() || !view()->layoutStateEnabled());
1361 
13621359 bool isFixedPos = style()->position() == FixedPosition;
13631360 bool hasTransform = hasLayer() && layer()->transform();
13641361 if (hasTransform) {

@@LayoutSize RenderBox::offsetFromContainer(RenderObject* o, const LayoutPoint& po
13781375 LayoutSize offset;
13791376 if (isRelPositioned())
13801377 offset += relativePositionOffset();
 1378 else if (isStickyPositioned())
 1379 offset += stickyPositionOffset();
13811380
13821381 if (!isInline() || isReplaced()) {
13831382 if (!style()->isOutOfFlowPositioned() && o->hasColumns()) {

@@void RenderBox::computeRectForRepaint(RenderBoxModelObject* repaintContainer, La
15131512 if (layer() && layer()->transform())
15141513 rect = layer()->transform()->mapRect(rect);
15151514
1516  if (styleToUse->position() == RelativePosition && layer())
1517  rect.move(layer()->relativePositionOffset());
 1515 // FIXME: why doesn't this use isRelPositioned?
 1516 if ((styleToUse->position() == RelativePosition || styleToUse->position() == StickyPosition) && layer())
 1517 rect.move(layer()->offsetForInFlowPosition());
15181518
15191519 rect.moveBy(location());
15201520 rect.move(layoutState->m_paintOffset);

@@void RenderBox::computeRectForRepaint(RenderBoxModelObject* repaintContainer, La
15581558
15591559 if (position == AbsolutePosition && o->isRelPositioned() && o->isRenderInline())
15601560 topLeft += toRenderInline(o)->relativePositionedInlineOffset(this);
1561  else if (position == RelativePosition && layer()) {
 1561 else if ((position == RelativePosition || position == StickyPosition) && layer()) {
15621562 // Apply the relative position offset when invalidating a rectangle. The layer
15631563 // is translated, but the render box isn't, so we need to do this to get the
15641564 // right dirty rect. Since this is called from RenderObject::setStyle, the relative position
15651565 // flag on the RenderObject has been cleared, so use the one on the style().
1566  topLeft += layer()->relativePositionOffset();
 1566 topLeft += layer()->offsetForInFlowPosition();
15671567 }
15681568
15691569 if (o->isBlockFlow() && position != AbsolutePosition && position != FixedPosition) {

@@LayoutRect RenderBox::layoutOverflowRectForPropagation(RenderStyle* parentStyle)
38193819 rect.unite(layoutOverflowRect());
38203820
38213821 bool hasTransform = hasLayer() && layer()->transform();
3822  if (isRelPositioned() || hasTransform) {
 3822 if (isInFlowPositioned() || hasTransform) {
38233823 // If we are relatively positioned or if we have a transform, then we have to convert
38243824 // this rectangle into physical coordinates, apply relative positioning and transforms
38253825 // to it, and then convert it back.

@@LayoutRect RenderBox::layoutOverflowRectForPropagation(RenderStyle* parentStyle)
38303830
38313831 if (isRelPositioned())
38323832 rect.move(relativePositionOffset());
 3833 else if (isStickyPositioned())
 3834 rect.move(stickyPositionOffset());
38333835
38343836 // Now we need to flip back.
38353837 flipForWritingMode(rect);

Source/WebCore/rendering/RenderBox.h

@@public:
4242 RenderBox(Node*);
4343 virtual ~RenderBox();
4444
45  virtual bool requiresLayer() const OVERRIDE { return isRoot() || isOutOfFlowPositioned() || isRelPositioned() || isTransparent() || hasOverflowClip() || hasTransform() || hasHiddenBackface() || hasMask() || hasReflection() || hasFilter() || style()->specifiesColumns(); }
 45 virtual bool requiresLayer() const OVERRIDE { return isRoot() || isOutOfFlowPositioned() || isInFlowPositioned() || isTransparent() || hasOverflowClip() || hasTransform() || hasHiddenBackface() || hasMask() || hasReflection() || hasFilter() || style()->specifiesColumns(); }
4646
4747 // Use this with caution! No type checking is done!
4848 RenderBox* firstChildBox() const;

@@private:
557557 // These include tables, positioned objects, floats and flexible boxes.
558558 virtual void computePreferredLogicalWidths() { setPreferredLogicalWidthsDirty(false); }
559559
 560 virtual LayoutRect frameRectForStickyPositioning() const OVERRIDE { return frameRect(); }
 561
560562private:
561563 // The width/height of the contents + borders + padding. The x/y location is relative to our container (which is not always our parent).
562564 LayoutRect m_frameRect;

Source/WebCore/rendering/RenderBoxModelObject.cpp

@@void RenderBoxModelObject::updateBoxModelInfoFromStyle()
456456 setHasBoxDecorations(hasBackground() || styleToUse->hasBorder() || styleToUse->hasAppearance() || styleToUse->boxShadow());
457457 setInline(styleToUse->isDisplayInlineType());
458458 setRelPositioned(styleToUse->position() == RelativePosition);
 459 setStickyPositioned(styleToUse->position() == StickyPosition);
459460 setHorizontalWritingMode(styleToUse->isHorizontalWritingMode());
460461}
461462

@@LayoutPoint RenderBoxModelObject::adjustedPositionRelativeToOffsetParent(const L
534535 if (!isOutOfFlowPositioned()) {
535536 if (isRelPositioned())
536537 referencePoint.move(relativePositionOffset());
 538 else if (isStickyPositioned())
 539 referencePoint.move(stickyPositionOffset());
537540 const RenderObject* curr = parent();
538541 while (curr != offsetParent) {
539542 // FIXME: What are we supposed to do inside SVG content?

@@LayoutPoint RenderBoxModelObject::adjustedPositionRelativeToOffsetParent(const L
550553 return referencePoint;
551554}
552555
 556LayoutSize RenderBoxModelObject::stickyPositionOffset() const
 557{
 558 RenderBlock* containingBlock = this->containingBlock();
 559
 560 // Do the math in the cooridinate space of the containing block.
 561 LayoutRect containingBlockRect = containingBlock->contentBoxRect();
 562 LayoutRect viewportRect = view()->frameView()->visibleContentRect();
 563
 564 // FIXME: no idea if these are right.
 565 LayoutUnit marginLeftLength = minimumValueForLength(style()->marginLeft(), containingBlockLogicalWidthForContent(), view());
 566 LayoutUnit marginTopLength = minimumValueForLength(style()->marginTop(), containingBlock->availableLogicalHeight(), view());
 567 LayoutUnit marginRightLength = minimumValueForLength(style()->marginRight(), containingBlockLogicalWidthForContent(), view());
 568 LayoutUnit marginBottomLength = minimumValueForLength(style()->marginBottom(), containingBlock->availableLogicalHeight(), view());
 569
 570 containingBlockRect.move(marginLeftLength, marginTopLength);
 571 containingBlockRect.contract(marginLeftLength + marginRightLength, marginTopLength + marginBottomLength);
 572
 573 FloatPoint containerViewportOffset;
 574 containerViewportOffset = containingBlock->absoluteToLocal(FloatPoint(), false, false); // FIXME: do something with transforms
 575 viewportRect.move(containerViewportOffset.x(), containerViewportOffset.y());
 576
 577 LayoutRect stickyBoxRect = frameRectForStickyPositioning();
 578 LayoutPoint inFlowLocation = stickyBoxRect.location();
 579
 580 // Horizontal postion.
 581 if (!style()->left().isAuto()) {
 582 LayoutUnit leftLimit = viewportRect.x() + valueForLength(style()->left(), viewportRect.width(), view());
 583 if (stickyBoxRect.x() < leftLimit)
 584 stickyBoxRect.setX(leftLimit);
 585
 586 if (stickyBoxRect.maxX() > containingBlockRect.maxX())
 587 stickyBoxRect.setX(containingBlockRect.maxX() - stickyBoxRect.width());
 588
 589 }
 590
 591 if (!style()->right().isAuto()) {
 592 LayoutUnit rightLimit = viewportRect.maxX() - valueForLength(style()->right(), viewportRect.width(), view());
 593 if (stickyBoxRect.maxX() > rightLimit)
 594 stickyBoxRect.setX(rightLimit - stickyBoxRect.width());
 595
 596 if (stickyBoxRect.x() < containingBlockRect.x())
 597 stickyBoxRect.setX(containingBlockRect.x());
 598 }
 599
 600 // Vertical position.
 601 if (!style()->top().isAuto()) {
 602 LayoutUnit topLimit = viewportRect.y() + valueForLength(style()->top(), viewportRect.height(), view());
 603 if (stickyBoxRect.y() < topLimit)
 604 stickyBoxRect.setY(topLimit);
 605
 606 if (stickyBoxRect.maxY() > containingBlockRect.maxY())
 607 stickyBoxRect.setY(containingBlockRect.maxY() - stickyBoxRect.height());
 608
 609 }
 610
 611 if (!style()->bottom().isAuto()) {
 612 LayoutUnit bottomLimit = viewportRect.maxY() - valueForLength(style()->bottom(), viewportRect.height(), view());
 613 if (stickyBoxRect.maxY() > bottomLimit)
 614 stickyBoxRect.setY(bottomLimit - stickyBoxRect.height());
 615
 616 if (stickyBoxRect.y() < containingBlockRect.y())
 617 stickyBoxRect.setY(containingBlockRect.y());
 618 }
 619
 620 LayoutUnit leftOffset = stickyBoxRect.x() - inFlowLocation.x();
 621 LayoutUnit topOffset = stickyBoxRect.y() - inFlowLocation.y();
 622
 623 return LayoutSize(leftOffset, topOffset);
 624}
 625
553626LayoutUnit RenderBoxModelObject::offsetLeft() const
554627{
555628 // Note that RenderInline and RenderBox override this to pass a different

@@bool RenderBoxModelObject::shouldAntialiasLines(GraphicsContext* context)
27202793
27212794void RenderBoxModelObject::mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState& transformState) const
27222795{
2723  // We don't expect absoluteToLocal() to be called during layout (yet)
2724  ASSERT(!view() || !view()->layoutStateEnabled());
2725 
27262796 RenderObject* o = container();
27272797 if (!o)
27282798 return;

Source/WebCore/rendering/RenderBoxModelObject.h

@@public:
6262 LayoutSize relativePositionOffset() const;
6363 LayoutSize relativePositionLogicalOffset() const { return style()->isHorizontalWritingMode() ? relativePositionOffset() : relativePositionOffset().transposedSize(); }
6464
 65 LayoutSize stickyPositionOffset() const;
 66 LayoutSize stickyPositionLogicalOffset() const { return style()->isHorizontalWritingMode() ? stickyPositionOffset() : stickyPositionOffset().transposedSize(); }
 67
6568 // IE extensions. Used to calculate offsetWidth/Height. Overridden by inlines (RenderFlow)
6669 // to return the remaining width on a given line (and the height of a single line).
6770 virtual LayoutUnit offsetLeft() const;

@@public:
7477 int pixelSnappedOffsetWidth() const;
7578 int pixelSnappedOffsetHeight() const;
7679
77  virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle);
78  virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
 80 virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle) OVERRIDE;
 81 virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle) OVERRIDE;
7982 virtual void updateBoxModelInfoFromStyle();
8083
8184 bool hasSelfPaintingLayer() const;
8285 RenderLayer* layer() const { return m_layer; }
83  virtual bool requiresLayer() const { return isRoot() || isOutOfFlowPositioned() || isRelPositioned() || isTransparent() || hasTransform() || hasHiddenBackface() || hasMask() || hasReflection() || hasFilter() || style()->specifiesColumns(); }
 86 virtual bool requiresLayer() const { return isRoot() || isOutOfFlowPositioned() || isInFlowPositioned() || isTransparent() || hasTransform() || hasHiddenBackface() || hasMask() || hasReflection() || hasFilter() || style()->specifiesColumns(); }
8487
8588 // This will work on inlines to return the bounding box of all of the lines' border boxes.
8689 virtual IntRect borderBoundingBox() const = 0;

@@public:
273276
274277private:
275278 virtual bool isBoxModelObject() const { return true; }
 279
 280 virtual LayoutRect frameRectForStickyPositioning() const = 0;
276281
277282 IntSize calculateFillTileSize(const FillLayer*, const IntSize& scaledPositioningAreaSize) const;
278283

Source/WebCore/rendering/RenderInline.cpp

@@static void updateStyleOfAnonymousBlockContinuations(RenderObject* block, const
146146 // If we are no longer relatively positioned but our descendant block(s) still have a relatively positioned ancestor then
147147 // their containing anonymous block should keep its relative positioning.
148148 RenderInline* cont = toRenderBlock(block)->inlineElementContinuation();
 149 // FIXME for sticky
149150 if (oldStyle->position() == RelativePosition && hasRelPositionedInlineAncestor(cont))
150151 continue;
151152 RefPtr<RenderStyle> blockStyle = RenderStyle::createAnonymousStyleWithDisplay(block->style(), BLOCK);

@@static void updateStyleOfAnonymousBlockContinuations(RenderObject* block, const
154155 }
155156}
156157
 158void RenderInline::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
 159{
 160 if (FrameView *frameView = view()->frameView()) {
 161 RenderStyle* oldStyle = style();
 162 bool newStyleIsSticky = newStyle && newStyle->position() == StickyPosition;
 163 bool oldStyleIsSticky = oldStyle && oldStyle->position() == StickyPosition;
 164 if (newStyleIsSticky != oldStyleIsSticky) {
 165 if (newStyleIsSticky)
 166 frameView->addFixedObject(this);
 167 else
 168 frameView->removeFixedObject(this);
 169 }
 170 }
 171
 172 RenderBoxModelObject::styleWillChange(diff, newStyle);
 173}
 174
157175void RenderInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
158176{
159177 RenderBoxModelObject::styleDidChange(diff, oldStyle);

@@void RenderInline::styleDidChange(StyleDifference diff, const RenderStyle* oldSt
175193
176194 // If an inline's relative positioning has changed then any descendant blocks will need to change their relative positioning accordingly.
177195 // Do this by updating the position of the descendant blocks' containing anonymous blocks - there may be more than one.
 196 // FIXME for sticky
178197 if (continuation && oldStyle && newStyle->position() != oldStyle->position()
179198 && (newStyle->position() == RelativePosition || (oldStyle->position() == RelativePosition))) {
180199 // If any descendant blocks exist then they will be in the next anonymous block and its siblings.

@@void RenderInline::addChildIgnoringContinuation(RenderObject* newChild, RenderOb
312331
313332 // If inside an inline affected by relative positioning the block needs to be affected by it too.
314333 // Giving the block a layer like this allows it to collect the x/y offsets from inline parents later.
 334 // FIXME for sticky
315335 if (hasRelPositionedInlineAncestor(this))
316336 newStyle->setPosition(RelativePosition);
317337

@@LayoutRect RenderInline::clippedOverflowRectForRepaint(RenderBoxModelObject* rep
977997 hitRepaintContainer = true;
978998 break;
979999 }
980  if (inlineFlow->style()->position() == RelativePosition && inlineFlow->hasLayer())
981  repaintRect.move(toRenderInline(inlineFlow)->layer()->relativePositionOffset());
 1000 if ((inlineFlow->style()->position() == RelativePosition || inlineFlow->style()->position() == StickyPosition) && inlineFlow->hasLayer())
 1001 repaintRect.move(toRenderInline(inlineFlow)->layer()->offsetForInFlowPosition());
9821002 }
9831003
9841004 LayoutUnit outlineSize = style()->outlineSize();

@@void RenderInline::computeRectForRepaint(RenderBoxModelObject* repaintContainer,
10311051 // LayoutState is only valid for root-relative repainting
10321052 if (v->layoutStateEnabled() && !repaintContainer) {
10331053 LayoutState* layoutState = v->layoutState();
1034  if (style()->position() == RelativePosition && layer())
1035  rect.move(layer()->relativePositionOffset());
 1054 if ((style()->position() == RelativePosition || style()->position() == StickyPosition) && layer())
 1055 rect.move(layer()->offsetForInFlowPosition());
10361056 rect.move(layoutState->m_paintOffset);
10371057 if (layoutState->m_clipped)
10381058 rect.intersect(layoutState->m_clipRect);

@@void RenderInline::computeRectForRepaint(RenderBoxModelObject* repaintContainer,
10601080 }
10611081 }
10621082
1063  if (style()->position() == RelativePosition && layer()) {
1064  // Apply the relative position offset when invalidating a rectangle. The layer
 1083 if ((style()->position() == RelativePosition || style()->position() == StickyPosition) && layer()) {
 1084 // Apply the relative position offset when invalidating a rectangle. The layer
10651085 // is translated, but the render box isn't, so we need to do this to get the
10661086 // right dirty rect. Since this is called from RenderObject::setStyle, the relative position
10671087 // flag on the RenderObject has been cleared, so use the one on the style().
1068  topLeft += layer()->relativePositionOffset();
 1088 topLeft += layer()->offsetForInFlowPosition();
10691089 }
10701090
10711091 // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout,

@@LayoutSize RenderInline::offsetFromContainer(RenderObject* container, const Layo
11031123 LayoutSize offset;
11041124 if (isRelPositioned())
11051125 offset += relativePositionOffset();
 1126 else if (isStickyPositioned())
 1127 offset += stickyPositionOffset();
11061128
11071129 container->adjustForColumns(offset, point);
11081130

@@void RenderInline::mapLocalToContainer(RenderBoxModelObject* repaintContainer, b
11241146 if (v->layoutStateEnabled() && !repaintContainer) {
11251147 LayoutState* layoutState = v->layoutState();
11261148 LayoutSize offset = layoutState->m_paintOffset;
1127  if (style()->position() == RelativePosition && layer())
1128  offset += layer()->relativePositionOffset();
 1149 if ((style()->position() == RelativePosition || style()->position() == StickyPosition) && layer())
 1150 offset += layer()->offsetForInFlowPosition();
11291151 transformState.move(offset);
11301152 return;
11311153 }

Source/WebCore/rendering/RenderInline.h

@@public:
8989protected:
9090 virtual void willBeDestroyed();
9191
92  virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
 92 virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle) OVERRIDE;
 93 virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle) OVERRIDE;
9394
9495private:
9596 virtual RenderObjectChildList* virtualChildren() { return children(); }

@@private:
124125
125126 virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestPoint& pointInContainer, const LayoutPoint& accumulatedOffset, HitTestAction) OVERRIDE;
126127
127  virtual bool requiresLayer() const { return isRelPositioned() || isTransparent() || hasMask() || hasFilter(); }
 128 virtual bool requiresLayer() const { return isInFlowPositioned() || isTransparent() || hasMask() || hasFilter(); }
128129
129130 virtual LayoutUnit offsetLeft() const;
130131 virtual LayoutUnit offsetTop() const;

@@private:
140141
141142 virtual VisiblePosition positionForPoint(const LayoutPoint&);
142143
 144 virtual LayoutRect frameRectForStickyPositioning() const OVERRIDE { return linesBoundingBox(); }
 145
143146 virtual IntRect borderBoundingBox() const
144147 {
145148 IntRect boundingBox = linesBoundingBox();

Source/WebCore/rendering/RenderLayer.cpp

@@void RenderLayer::updateLayerPositionsAfterScroll(UpdateLayerPositionsAfterScrol
505505
506506 updateLayerPosition();
507507
508  if ((flags & HasSeenFixedPositionedAncestor) || renderer()->style()->position() == FixedPosition) {
 508 if ((flags & HasSeenFixedOrStickyAncestor) || renderer()->style()->position() == FixedPosition || renderer()->style()->position() == StickyPosition) {
509509 // FIXME: Is it worth passing the offsetFromRoot around like in updateLayerPositions?
510510 computeRepaintRects();
511  flags |= HasSeenFixedPositionedAncestor;
 511 flags |= HasSeenFixedOrStickyAncestor;
512512 } else if ((flags & HasSeenAncestorWithOverflowClip) && !m_canSkipRepaintRectsUpdateOnScroll) {
513513 // If we have seen an overflow clip, we should update our repaint rects as clippedOverflowRectForRepaint
514514 // intersects it with our ancestor overflow clip that may have moved.

@@void RenderLayer::updateLayerPosition()
833833 LayoutSize offset = positionedParent->scrolledContentOffset();
834834 localPoint -= offset;
835835
836  if (renderer()->isOutOfFlowPositioned() && positionedParent->renderer()->isRelPositioned() && positionedParent->renderer()->isRenderInline()) {
 836 if (renderer()->isOutOfFlowPositioned() && positionedParent->renderer()->isInFlowPositioned() && positionedParent->renderer()->isRenderInline()) {
837837 LayoutSize offset = toRenderInline(positionedParent->renderer())->relativePositionedInlineOffset(toRenderBox(renderer()));
838838 localPoint += offset;
839839 }

@@void RenderLayer::updateLayerPosition()
855855 }
856856
857857 if (renderer()->isRelPositioned()) {
858  m_relativeOffset = renderer()->relativePositionOffset();
859  localPoint.move(m_relativeOffset);
 858 m_offsetForInFlowPosition = renderer()->relativePositionOffset();
 859 localPoint.move(m_offsetForInFlowPosition);
 860 } else if (renderer()->isStickyPositioned()) {
 861 m_offsetForInFlowPosition = renderer()->stickyPositionOffset();
 862 localPoint.move(m_offsetForInFlowPosition);
860863 } else {
861  m_relativeOffset = LayoutSize();
 864 m_offsetForInFlowPosition = LayoutSize();
862865 }
863866
864867 // FIXME: We'd really like to just get rid of the concept of a layer rectangle and rely on the renderers.

@@RenderLayer* RenderLayer::stackingContext() const
919922static inline bool isPositionedContainer(RenderLayer* layer)
920923{
921924 RenderBoxModelObject* layerRenderer = layer->renderer();
922  return layer->isRootLayer() || layerRenderer->isOutOfFlowPositioned() || layerRenderer->isRelPositioned() || layer->hasTransform();
 925 return layer->isRootLayer() || layerRenderer->isOutOfFlowPositioned() || layerRenderer->isInFlowPositioned() || layer->hasTransform();
923926}
924927
925928static inline bool isFixedPositionedContainer(RenderLayer* layer)

@@void RenderLayer::calculateClipRects(const RenderLayer* rootLayer, RenderRegion*
39123915 clipRects.setPosClipRect(clipRects.fixedClipRect());
39133916 clipRects.setOverflowClipRect(clipRects.fixedClipRect());
39143917 clipRects.setFixed(true);
3915  }
3916  else if (renderer()->style()->position() == RelativePosition)
 3918 } else if (renderer()->style()->position() == RelativePosition || renderer()->style()->position() == StickyPosition)
39173919 clipRects.setPosClipRect(clipRects.overflowClipRect());
39183920 else if (renderer()->style()->position() == AbsolutePosition)
39193921 clipRects.setOverflowClipRect(clipRects.posClipRect());

@@void RenderLayer::calculateClipRects(const RenderLayer* rootLayer, RenderRegion*
39383940 if (renderer()->style()->hasBorderRadius())
39393941 newOverflowClip.setHasRadius(true);
39403942 clipRects.setOverflowClipRect(intersection(newOverflowClip, clipRects.overflowClipRect()));
3941  if (renderer()->isOutOfFlowPositioned() || renderer()->isRelPositioned())
 3943 if (renderer()->isOutOfFlowPositioned() || renderer()->isInFlowPositioned())
39423944 clipRects.setPosClipRect(intersection(newOverflowClip, clipRects.posClipRect()));
39433945 }
39443946 if (renderer()->hasClip()) {

@@bool RenderLayer::shouldBeNormalFlowOnly() const
47354737 || renderer()->isRenderIFrame()
47364738 || renderer()->style()->specifiesColumns())
47374739 && !renderer()->isOutOfFlowPositioned()
4738  && !renderer()->isRelPositioned()
 4740 && !renderer()->isInFlowPositioned()
47394741 && !renderer()->hasTransform()
47404742#if ENABLE(CSS_FILTERS)
47414743 && !renderer()->hasFilter()

Source/WebCore/rendering/RenderLayer.h

@@public:
398398
399399 void updateTransform();
400400
401  const LayoutSize& relativePositionOffset() const { return m_relativeOffset; }
 401 const LayoutSize& offsetForInFlowPosition() const { return m_offsetForInFlowPosition; }
402402
403403 void clearClipRectsIncludingDescendants(ClipRectsType typeToClear = AllClipRectTypes);
404404 void clearClipRects(ClipRectsType typeToClear = AllClipRectTypes);

@@public:
562562
563563 enum UpdateLayerPositionsAfterScrollFlag {
564564 NoFlag = 0,
565  HasSeenFixedPositionedAncestor = 1 << 0,
 565 HasSeenFixedOrStickyAncestor = 1 << 0,
566566 HasSeenAncestorWithOverflowClip = 1 << 1
567567 };
568568

@@protected:
940940 LayoutRect m_outlineBox;
941941
942942 // Our current relative position offset.
943  LayoutSize m_relativeOffset;
 943 LayoutSize m_offsetForInFlowPosition;
944944
945945 // Our (x,y) coordinates are in our parent layer's coordinate space.
946946 LayoutPoint m_topLeft;

Source/WebCore/rendering/RenderObject.h

@@public:
500500 virtual RenderBoxModelObject* virtualContinuation() const { return 0; }
501501
502502 bool isFloating() const { return m_bitfields.floating(); }
 503
503504 bool isOutOfFlowPositioned() const { return m_bitfields.positioned(); } // absolute or fixed positioning
504  bool isInFlowPositioned() const { return m_bitfields.relPositioned(); } // relative positioning
505  bool isRelPositioned() const { return m_bitfields.relPositioned(); } // relative positioning
 505 bool isInFlowPositioned() const { return m_bitfields.relPositioned() || m_bitfields.stickyPositioned(); } // relative or sticky positioning
 506 bool isRelPositioned() const { return m_bitfields.relPositioned(); }
 507 bool isStickyPositioned() const { return m_bitfields.stickyPositioned(); }
 508
506509 bool isText() const { return m_bitfields.isText(); }
507510 bool isBox() const { return m_bitfields.isBox(); }
508511 bool isInline() const { return m_bitfields.isInline(); } // inline object

@@public:
608611
609612 void setPositioned(bool b = true) { m_bitfields.setPositioned(b); }
610613 void setRelPositioned(bool b = true) { m_bitfields.setRelPositioned(b); }
 614 void setStickyPositioned(bool b = true) { m_bitfields.setStickyPositioned(b); }
611615 void setFloating(bool b = true) { m_bitfields.setFloating(b); }
612616 void setInline(bool b = true) { m_bitfields.setIsInline(b); }
613617 void setHasBoxDecorations(bool b = true) { m_bitfields.setPaintBackground(b); }

@@private:
972976 , m_floating(false)
973977 , m_positioned(false)
974978 , m_relPositioned(false)
 979 , m_stickyPositioned(false)
975980 , m_paintBackground(false)
976981 , m_isAnonymous(node == node->document())
977982 , m_isText(false)

@@private:
10061011
10071012 ADD_BOOLEAN_BITFIELD(positioned, Positioned);
10081013 ADD_BOOLEAN_BITFIELD(relPositioned, RelPositioned);
 1014 ADD_BOOLEAN_BITFIELD(stickyPositioned, StickyPositioned);
10091015 ADD_BOOLEAN_BITFIELD(paintBackground, PaintBackground); // if the box has something to paint in the
10101016 // background painting phase (background, border, etc)
10111017

LayoutTests/ChangeLog

 12012-07-24 Simon Fraser <simon.fraser@apple.com>
 2
 3 Implement sticky positioning
 4 https://bugs.webkit.org/show_bug.cgi?id=90046
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Various ref tests for sticky positioning.
 9
 10 * fast/css/sticky/inflow-sticky-expected.html: Added.
 11 * fast/css/sticky/inflow-sticky.html: Added.
 12 * fast/css/sticky/inline-sticky-expected.html: Added.
 13 * fast/css/sticky/inline-sticky.html: Added.
 14 * fast/css/sticky/replaced-sticky-expected.html: Added.
 15 * fast/css/sticky/replaced-sticky.html: Added.
 16 * fast/css/sticky/sticky-as-positioning-container-expected.html: Added.
 17 * fast/css/sticky/sticky-as-positioning-container.html: Added.
 18 * fast/css/sticky/sticky-left-expected.html: Added.
 19 * fast/css/sticky/sticky-left-percentage-expected.html: Added.
 20 * fast/css/sticky/sticky-left-percentage.html: Added.
 21 * fast/css/sticky/sticky-left.html: Added.
 22 * fast/css/sticky/sticky-top-expected.html: Added.
 23 * fast/css/sticky/sticky-top.html: Added.
 24
1252012-07-24 Andrew Wilson <atwilson@chromium.org>
226
327 Unreviewed chromium expectations change stop ignoring fast/hidpi changes.

LayoutTests/fast/css/sticky/inflow-sticky-expected.html

 1<!DOCTYPE html>
 2
 3<html>
 4<head>
 5<style>
 6 body {
 7 margin: 0;
 8 }
 9
 10 .group {
 11 position: relative;
 12 width: 250px;
 13 height: 450px;
 14 }
 15
 16 .container {
 17 width: 200px;
 18 height: 600px;
 19 outline: 2px solid black;
 20 }
 21
 22 .box {
 23 width: 200px;
 24 height: 200px;
 25 }
 26
 27 .before {
 28 background-color: orange;
 29 height: 50px;
 30 }
 31
 32 .after {
 33 background-color: blue;
 34 height: 50px;
 35 }
 36 .sticky {
 37 background-color: green;
 38 position: relative;
 39 top: 250px;
 40 }
 41</style>
 42</head>
 43<body>
 44 <div class="group" style="top: 0">
 45 <div class="container">
 46 <div class="before box"></div>
 47 <div class="sticky box"></div>
 48 <div class="after box"></div>
 49 </div>
 50 </div>
 51
 52</body>
 53</html>

LayoutTests/fast/css/sticky/inflow-sticky.html

 1<!DOCTYPE html>
 2
 3<html>
 4<head>
 5<style>
 6 body {
 7 margin: 0;
 8 height: 2000px;
 9 overflow: hidden; /* hide scrollbars */
 10 }
 11
 12 .group {
 13 position: relative;
 14 width: 250px;
 15 height: 450px;
 16 }
 17
 18 .container {
 19 width: 200px;
 20 height: 600px;
 21 outline: 2px solid black;
 22 }
 23
 24 .box {
 25 width: 200px;
 26 height: 200px;
 27 }
 28
 29 .before {
 30 background-color: orange;
 31 height: 50px;
 32 }
 33
 34 .after {
 35 background-color: blue;
 36 height: 50px;
 37 }
 38 .sticky {
 39 background-color: green;
 40 position: -webkit-sticky;
 41 top: 300px;
 42 }
 43</style>
 44<script>
 45 function doTest()
 46 {
 47 window.scrollTo(0, 100);
 48 }
 49 window.addEventListener('load', doTest, false);
 50</script>
 51</head>
 52<body>
 53 <div class="group" style="top: 100px">
 54 <div class="container">
 55 <div class="before box"></div>
 56 <div class="sticky box"></div>
 57 <div class="after box"></div>
 58 </div>
 59 </div>
 60
 61</body>
 62</html>

LayoutTests/fast/css/sticky/inline-sticky-expected.html

 1<!DOCTYPE html>
 2
 3<html>
 4<head>
 5<style>
 6 body {
 7 margin: 0;
 8 font-family: 'Ahem';
 9 font-size: 24px;
 10 line-height: 2;
 11 }
 12
 13 .group {
 14 display: inline-block;
 15 position: relative;
 16 width: 220px;
 17 height: 500px;
 18 }
 19
 20 .container {
 21 width: 200px;
 22 height: 400px;
 23 outline: 2px solid black;
 24 }
 25
 26 .box {
 27 width: 200px;
 28 height: 200px;
 29 }
 30
 31 .sticky {
 32 width: 200px;
 33 height: 200px;
 34 color: blue;
 35 position: relative;
 36 }
 37
 38 .child {
 39 position: absolute;
 40 background-color: green;
 41 opacity: 0.8;
 42 top: 50px;
 43 left: 50px;
 44 }
 45
 46 .indicator {
 47 display: none;
 48 position: absolute;
 49 top: 250px;
 50 left: 50px;
 51 background-color: red;
 52 }
 53</style>
 54</head>
 55<body>
 56 <div class="group" style="top: -100px">
 57 <div class="indicator box"></div>
 58 <div class="container">
 59 XXX <span class="sticky" style="top: 172px;">XXXX</br>XXXX</br>XXXX</br>XXXX</br>XXXX</span> XXX
 60 </div>
 61 </div>
 62 <div class="group" style="top: 0">
 63 <div class="indicator box"></div>
 64 <div class="container">
 65 XXX <span class="sticky" style="top: 88px;">XXXX</br>XXXX</br>XXXX</br>XXXX</br>XXXX</span> XXX
 66 </div>
 67 </div>
 68 <div class="group" style="top: 100px">
 69 <div class="indicator box"></div>
 70 <div class="container">
 71 XXX <span class="sticky" style="top: 0;">XXXX</br>XXXX</br>XXXX</br>XXXX</br>XXXX</span> XXX
 72 </div>
 73 </div>
 74
 75</body>
 76</html>

LayoutTests/fast/css/sticky/inline-sticky.html

 1<!DOCTYPE html>
 2
 3<html>
 4<head>
 5<style>
 6 body {
 7 margin: 0;
 8 height: 2000px;
 9 overflow: hidden; /* hide scrollbars */
 10 font-family: 'Ahem';
 11 font-size: 24px;
 12 line-height: 2;
 13 }
 14
 15 .group {
 16 display: inline-block;
 17 position: relative;
 18 width: 220px;
 19 height: 500px;
 20 }
 21
 22 .container {
 23 width: 200px;
 24 height: 400px;
 25 outline: 2px solid black;
 26 }
 27
 28 .box {
 29 width: 200px;
 30 height: 200px;
 31 }
 32
 33 .sticky {
 34 width: 200px;
 35 height: 200px;
 36 color: blue;
 37 position: -webkit-sticky;
 38 top: 100px;
 39 }
 40
 41 .child {
 42 position: absolute;
 43 background-color: green;
 44 opacity: 0.8;
 45 top: 50px;
 46 left: 50px;
 47 }
 48
 49 .indicator {
 50 display: none;
 51 position: absolute;
 52 top: 250px;
 53 left: 50px;
 54 background-color: red;
 55 }
 56</style>
 57<script>
 58 function doTest()
 59 {
 60 window.scrollTo(0, 100);
 61 }
 62 window.addEventListener('load', doTest, false);
 63</script>
 64</head>
 65<body>
 66 <div class="group">
 67 <div class="indicator box"></div>
 68 <div class="container">
 69 XXX <span class="sticky">XXXX</br>XXXX</br>XXXX</br>XXXX</br>XXXX</span> XXX
 70 </div>
 71 </div>
 72 <div class="group" style="top: 100px">
 73 <div class="indicator box"></div>
 74 <div class="container">
 75 XXX <span class="sticky">XXXX</br>XXXX</br>XXXX</br>XXXX</br>XXXX</span> XXX
 76 </div>
 77 </div>
 78 <div class="group" style="top: 200px">
 79 <div class="indicator box"></div>
 80 <div class="container">
 81 XXX <span class="sticky">XXXX</br>XXXX</br>XXXX</br>XXXX</br>XXXX</span> XXX
 82 </div>
 83 </div>
 84
 85</body>
 86</html>

LayoutTests/fast/css/sticky/replaced-sticky-expected.html

 1<!DOCTYPE html>
 2
 3<html>
 4<head>
 5<style>
 6 body {
 7 margin: 0;
 8 }
 9
 10 .group {
 11 display: inline-block;
 12 position: relative;
 13 width: 250px;
 14 height: 500px;
 15 }
 16
 17 .container {
 18 width: 200px;
 19 height: 400px;
 20 outline: 2px solid black;
 21 }
 22
 23 .box {
 24 width: 200px;
 25 height: 200px;
 26 }
 27
 28 .sticky {
 29 background-color: silver;
 30 position: relative;
 31 top: 100px;
 32 }
 33
 34 .indicator {
 35 display: none;
 36 position: absolute;
 37 top: 250px;
 38 left: 50px;
 39 background-color: red;
 40 }
 41</style>
 42</head>
 43<body>
 44 <div class="group" style="top: -100px">
 45 <div class="indicator box"></div>
 46 <div class="container">
 47 <img src="../resources/greenbox.png" class="sticky box" style="top: 200px;">
 48 </div>
 49 </div>
 50
 51 <div class="group" style="top: 0">
 52 <div class="indicator box"></div>
 53 <div class="container">
 54 <img src="../resources/greenbox.png" class="sticky box" style="top: 100px;">
 55 </div>
 56 </div>
 57
 58</body>
 59</html>

LayoutTests/fast/css/sticky/replaced-sticky.html

 1<!DOCTYPE html>
 2
 3<html>
 4<head>
 5<style>
 6 body {
 7 margin: 0;
 8 height: 2000px;
 9 overflow: hidden; /* hide scrollbars */
 10 }
 11
 12 .group {
 13 display: inline-block;
 14 position: relative;
 15 width: 250px;
 16 height: 500px;
 17 }
 18
 19 .container {
 20 width: 200px;
 21 height: 400px;
 22 outline: 2px solid black;
 23 }
 24
 25 .box {
 26 width: 200px;
 27 height: 200px;
 28 }
 29
 30 .sticky {
 31 background-color: silver;
 32 position: -webkit-sticky;
 33 top: 100px;
 34 }
 35
 36 .indicator {
 37 display: none;
 38 position: absolute;
 39 top: 250px;
 40 left: 50px;
 41 background-color: red;
 42 }
 43</style>
 44<script>
 45 function doTest()
 46 {
 47 window.scrollTo(0, 100);
 48 }
 49 window.addEventListener('load', doTest, false);
 50</script>
 51</head>
 52<body>
 53 <div class="group">
 54 <div class="indicator box"></div>
 55 <div class="container">
 56 <img src="../resources/greenbox.png" class="sticky box">
 57 </div>
 58 </div>
 59
 60 <div class="group" style="top: 100px">
 61 <div class="indicator box"></div>
 62 <div class="container">
 63 <img src="../resources/greenbox.png" class="sticky box">
 64 </div>
 65 </div>
 66
 67</body>
 68</html>

LayoutTests/fast/css/sticky/sticky-as-positioning-container-expected.html

 1<!DOCTYPE html>
 2
 3<html>
 4<head>
 5<style>
 6 body {
 7 margin: 0;
 8 }
 9
 10 .group {
 11 display: inline-block;
 12 position: relative;
 13 width: 250px;
 14 height: 500px;
 15 }
 16
 17 .container {
 18 width: 200px;
 19 height: 400px;
 20 outline: 2px solid black;
 21 }
 22
 23 .box {
 24 width: 200px;
 25 height: 200px;
 26 }
 27
 28 .sticky {
 29 position: relative;
 30 top: 100px;
 31 background-color: silver;
 32 }
 33
 34 .child {
 35 position: absolute;
 36 background-color: green;
 37 top: 50px;
 38 left: 50px;
 39 }
 40
 41 .child:hover {
 42 color: orange;
 43 }
 44
 45 .indicator {
 46 position: absolute;
 47 top: 250px;
 48 left: 50px;
 49 background-color: red;
 50 }
 51</style>
 52<script>
 53</script>
 54</head>
 55<body>
 56 <div class="group" style="top: -100px;">
 57 <div class="indicator box"></div>
 58 <div class="container">
 59 <div class="sticky box" style="top: 200px;">
 60 <div class="child box"></div>
 61 </div>
 62 </div>
 63 </div>
 64
 65</body>
 66</html>

LayoutTests/fast/css/sticky/sticky-as-positioning-container.html

 1<!DOCTYPE html>
 2
 3<html>
 4<head>
 5<style>
 6 body {
 7 margin: 0;
 8 height: 2000px;
 9 overflow: hidden; /* hide scrollbars */
 10 }
 11
 12 .group {
 13 display: inline-block;
 14 position: relative;
 15 width: 250px;
 16 height: 500px;
 17 }
 18
 19 .container {
 20 width: 200px;
 21 height: 400px;
 22 outline: 2px solid black;
 23 }
 24
 25 .box {
 26 width: 200px;
 27 height: 200px;
 28 }
 29
 30 .sticky {
 31 position: -webkit-sticky;
 32 top: 100px;
 33 background-color: silver;
 34 }
 35
 36 .child {
 37 position: absolute;
 38 background-color: green;
 39 top: 50px;
 40 left: 50px;
 41 }
 42
 43 .indicator {
 44 position: absolute;
 45 top: 250px;
 46 left: 50px;
 47 background-color: red;
 48 }
 49</style>
 50<script>
 51 function doTest()
 52 {
 53 window.scrollTo(0, 100);
 54 }
 55 window.addEventListener('load', doTest, false);
 56</script>
 57</head>
 58<body>
 59 <div class="group">
 60 <div class="indicator box"></div>
 61 <div class="container">
 62 <div class="sticky box">
 63 <div class="child box"></div>
 64 </div>
 65 </div>
 66 </div>
 67
 68</body>
 69</html>

LayoutTests/fast/css/sticky/sticky-left-expected.html

 1<!DOCTYPE html>
 2
 3<html>
 4<head>
 5<style>
 6 body {
 7 margin: 0;
 8 }
 9
 10 .group {
 11 position: relative;
 12 width: 500px;
 13 height: 200px;
 14 }
 15
 16 .container {
 17 width: 400px;
 18 height: 180px;
 19 outline: 2px solid black;
 20 }
 21
 22 .box {
 23 width: 200px;
 24 height: 180px;
 25 }
 26
 27 .sticky {
 28 background-color: green;
 29 position: absolute;
 30 }
 31
 32 .indicator {
 33 position: absolute;
 34 top: 0;
 35 left: 0;
 36 background-color: red;
 37 }
 38</style>
 39</head>
 40<body>
 41 <div class="group" style="left: -100px;">
 42 <div class="indicator box" style="left: 200px;"></div>
 43 <div class="container">
 44 <div class="sticky box" style="left: 200px"></div>
 45 </div>
 46 </div>
 47
 48 <div class="group" style="left: 0">
 49 <div class="indicator box" style="left: 100px;"></div>
 50 <div class="container">
 51 <div class="sticky box" style="left: 100px"></div>
 52 </div>
 53 </div>
 54
 55 <div class="group" style="left: 100px">
 56 <div class="indicator box" style="left: 0;"></div>
 57 <div class="container">
 58 <div class="sticky box" style="left: 0"></div>
 59 </div>
 60 </div>
 61</body>
 62</html>

LayoutTests/fast/css/sticky/sticky-left-percentage-expected.html

 1<!DOCTYPE html>
 2
 3<html>
 4<head>
 5<style>
 6 body {
 7 margin: 0;
 8 }
 9
 10 .group {
 11 position: relative;
 12 width: 500px;
 13 height: 200px;
 14 }
 15
 16 .container {
 17 width: 400px;
 18 height: 180px;
 19 outline: 2px solid black;
 20 }
 21
 22 .box {
 23 width: 200px;
 24 height: 180px;
 25 }
 26
 27 .sticky {
 28 position: absolute;
 29 background-color: green;
 30 }
 31
 32 .indicator {
 33 position: absolute;
 34 top: 0;
 35 left: 0;
 36 background-color: red;
 37 }
 38</style>
 39</head>
 40<body>
 41 <div class="group" style="left: -100px;">
 42 <div class="indicator box" style="left: 200px;"></div>
 43 <div class="container">
 44 <div class="sticky box" style="left: 200px"></div>
 45 </div>
 46 </div>
 47
 48 <div class="group" style="left: 0">
 49 <div class="indicator box" style="left: 100px;"></div>
 50 <div class="container">
 51 <div class="sticky box" style="left: 12.5vw"></div>
 52 </div>
 53 </div>
 54
 55 <div class="group" style="left: 100px">
 56 <div class="indicator box" style="left: 0;"></div>
 57 <div class="container">
 58 <div class="sticky box" style="left: 0"></div>
 59 </div>
 60 </div>
 61</body>
 62</html>

LayoutTests/fast/css/sticky/sticky-left-percentage.html

 1<!DOCTYPE html>
 2
 3<html>
 4<head>
 5<style>
 6 body {
 7 margin: 0;
 8 width: 2000px;
 9 overflow: hidden; /* hide scrollbars */
 10 }
 11
 12 .group {
 13 position: relative;
 14 width: 500px;
 15 height: 200px;
 16 }
 17
 18 .container {
 19 width: 400px;
 20 height: 180px;
 21 outline: 2px solid black;
 22 }
 23
 24 .box {
 25 width: 200px;
 26 height: 180px;
 27 }
 28
 29 .sticky {
 30 position: -webkit-sticky;
 31 /* DRT window is 800px wide */
 32 left: 12.5%;
 33 background-color: green;
 34 }
 35
 36 .indicator {
 37 position: absolute;
 38 top: 0;
 39 left: 0;
 40 background-color: red;
 41 }
 42</style>
 43<script>
 44 function doTest()
 45 {
 46 window.scrollTo(100, 0);
 47 window.resizeTo(800, 700)
 48 }
 49 window.addEventListener('load', doTest, false);
 50</script>
 51</head>
 52<body>
 53 <div class="group">
 54 <div class="indicator box" style="left: 200px;"></div>
 55 <div class="container">
 56 <div class="sticky box"></div>
 57 </div>
 58 </div>
 59
 60 <div class="group" style="left: 100px">
 61 <div class="indicator box" style="left: 100px;"></div>
 62 <div class="container">
 63 <div class="sticky box"></div>
 64 </div>
 65 </div>
 66
 67 <div class="group" style="left: 200px">
 68 <div class="indicator box" style="left: 0;"></div>
 69 <div class="container">
 70 <div class="sticky box"></div>
 71 </div>
 72 </div>
 73</body>
 74</html>

LayoutTests/fast/css/sticky/sticky-left.html

 1<!DOCTYPE html>
 2
 3<html>
 4<head>
 5<style>
 6 body {
 7 margin: 0;
 8 width: 2000px;
 9 overflow: hidden; /* hide scrollbars */
 10 }
 11
 12 .group {
 13 position: relative;
 14 width: 500px;
 15 height: 200px;
 16 }
 17
 18 .container {
 19 width: 400px;
 20 height: 180px;
 21 outline: 2px solid black;
 22 }
 23
 24 .box {
 25 width: 200px;
 26 height: 180px;
 27 }
 28
 29 .sticky {
 30 position: -webkit-sticky;
 31 left: 100px;
 32 background-color: green;
 33 }
 34
 35 .indicator {
 36 position: absolute;
 37 top: 0;
 38 left: 0;
 39 background-color: red;
 40 }
 41</style>
 42<script>
 43 function doTest()
 44 {
 45 window.scrollTo(100, 0);
 46 }
 47 window.addEventListener('load', doTest, false);
 48</script>
 49</head>
 50<body>
 51 <div class="group">
 52 <div class="indicator box" style="left: 200px;"></div>
 53 <div class="container">
 54 <div class="sticky box"></div>
 55 </div>
 56 </div>
 57
 58 <div class="group" style="left: 100px">
 59 <div class="indicator box" style="left: 100px;"></div>
 60 <div class="container">
 61 <div class="sticky box"></div>
 62 </div>
 63 </div>
 64
 65 <div class="group" style="left: 200px">
 66 <div class="indicator box" style="left: 0;"></div>
 67 <div class="container">
 68 <div class="sticky box"></div>
 69 </div>
 70 </div>
 71</body>
 72</html>

LayoutTests/fast/css/sticky/sticky-top-expected.html

 1<!DOCTYPE html>
 2
 3<html>
 4<head>
 5<style>
 6 body {
 7 margin: 0;
 8 }
 9
 10 .group {
 11 display: inline-block;
 12 position: relative;
 13 width: 250px;
 14 height: 500px;
 15 }
 16
 17 .container {
 18 width: 200px;
 19 height: 400px;
 20 outline: 2px solid black;
 21 }
 22
 23 .box {
 24 width: 200px;
 25 height: 200px;
 26 }
 27
 28 .sticky {
 29 position: absolute;
 30 background-color: green;
 31 }
 32
 33 .indicator {
 34 position: absolute;
 35 top: 0;
 36 left: 0;
 37 background-color: red;
 38 }
 39</style>
 40</head>
 41<body>
 42 <div class="group" style="top: -100px;">
 43 <div class="indicator box" style="top: 200px;"></div>
 44 <div class="container">
 45 <div class="sticky box" style="top: 200px;"></div>
 46 </div>
 47 </div>
 48
 49 <div class="group" style="top: 0">
 50 <div class="indicator box" style="top: 100px;"></div>
 51 <div class="container">
 52 <div class="sticky box" style="top: 100px;"></div>
 53 </div>
 54 </div>
 55
 56 <div class="group" style="top: 100px">
 57 <div class="indicator box" style="top: 0;"></div>
 58 <div class="container">
 59 <div class="sticky box" style="top: 0;"></div>
 60 </div>
 61 </div>
 62</body>
 63</html>

LayoutTests/fast/css/sticky/sticky-top.html

 1<!DOCTYPE html>
 2
 3<html>
 4<head>
 5<style>
 6 body {
 7 margin: 0;
 8 height: 2000px;
 9 overflow: hidden; /* hide scrollbars */
 10 }
 11
 12 .group {
 13 display: inline-block;
 14 position: relative;
 15 width: 250px;
 16 height: 500px;
 17 }
 18
 19 .container {
 20 width: 200px;
 21 height: 400px;
 22 outline: 2px solid black;
 23 }
 24
 25 .box {
 26 width: 200px;
 27 height: 200px;
 28 }
 29
 30 .sticky {
 31 position: -webkit-sticky;
 32 top: 100px;
 33 background-color: green;
 34 }
 35
 36 .indicator {
 37 position: absolute;
 38 top: 0;
 39 left: 0;
 40 background-color: red;
 41 }
 42</style>
 43<script>
 44 function doTest()
 45 {
 46 window.scrollTo(0, 100);
 47 }
 48 window.addEventListener('load', doTest, false);
 49</script>
 50</head>
 51<body>
 52 <div class="group">
 53 <div class="indicator box" style="top: 200px;"></div>
 54 <div class="container">
 55 <div class="sticky box"></div>
 56 </div>
 57 </div>
 58
 59 <div class="group" style="top: 100px">
 60 <div class="indicator box" style="top: 100px;"></div>
 61 <div class="container">
 62 <div class="sticky box"></div>
 63 </div>
 64 </div>
 65
 66 <div class="group" style="top: 200px">
 67 <div class="indicator box" style="top: 0;"></div>
 68 <div class="container">
 69 <div class="sticky box"></div>
 70 </div>
 71 </div>
 72</body>
 73</html>