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

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

Issue 2708883005: Handle nested position:sticky elements correctly (main thread) (Closed)
Patch Set: Rebase Created 3 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 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "core/layout/LayoutBoxModelObject.h" 5 #include "core/layout/LayoutBoxModelObject.h"
6 6
7 #include "core/dom/DOMTokenList.h" 7 #include "core/dom/DOMTokenList.h"
8 #include "core/dom/DocumentLifecycle.h" 8 #include "core/dom/DocumentLifecycle.h"
9 #include "core/html/HTMLElement.h" 9 #include "core/html/HTMLElement.h"
10 #include "core/layout/ImageQualityController.h" 10 #include "core/layout/ImageQualityController.h"
(...skipping 310 matching lines...) Expand 10 before | Expand all | Expand 10 after
321 321
322 // After updating compositing inputs we should have the updated position. 322 // After updating compositing inputs we should have the updated position.
323 document().view()->updateAllLifecyclePhases(); 323 document().view()->updateAllLifecyclePhases();
324 EXPECT_EQ(50.f, 324 EXPECT_EQ(50.f,
325 getScrollContainerRelativeStickyBoxRect( 325 getScrollContainerRelativeStickyBoxRect(
326 scrollableArea->stickyConstraintsMap().at(sticky->layer())) 326 scrollableArea->stickyConstraintsMap().at(sticky->layer()))
327 .location() 327 .location()
328 .x()); 328 .x());
329 } 329 }
330 330
331 // Verifies that the correct sticky-box shifting ancestor is found when
332 // computing the sticky constraints. Any such ancestor is the first sticky
333 // element between you and your containing block (exclusive).
334 //
335 // In most cases, this pointer should be null since your parent is normally your
336 // containing block. However there are cases where this is not true, including
337 // inline blocks and tables. The latter is currently irrelevant since only table
338 // cells can be sticky in CSS2.1, but we can test the former.
339 TEST_F(LayoutBoxModelObjectTest,
340 StickyPositionFindsCorrectStickyBoxShiftingAncestor) {
341 setBodyInnerHTML(
342 "<style>#stickyOuterDiv { position: sticky; }"
343 "#stickyOuterInline { position: sticky; display: inline; }"
344 "#stickyInnerInline { position: sticky; display: inline; }</style>"
345 "<div id='stickyOuterDiv'><div id='stickyOuterInline'>"
346 "<div id='stickyInnerInline'></div></div></div>");
347
348 LayoutBoxModelObject* stickyOuterDiv =
349 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyOuterDiv"));
350 LayoutBoxModelObject* stickyOuterInline =
351 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyOuterInline"));
352 LayoutBoxModelObject* stickyInnerInline =
353 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyInnerInline"));
354
355 PaintLayerScrollableArea* scrollableArea =
356 stickyOuterDiv->layer()->ancestorOverflowLayer()->getScrollableArea();
357 ASSERT_TRUE(scrollableArea);
358 StickyConstraintsMap constraintsMap = scrollableArea->stickyConstraintsMap();
359
360 ASSERT_TRUE(constraintsMap.contains(stickyOuterDiv->layer()));
361 ASSERT_TRUE(constraintsMap.contains(stickyOuterInline->layer()));
362 ASSERT_TRUE(constraintsMap.contains(stickyInnerInline->layer()));
363
364 // The outer block element trivially has no sticky-box shifting ancestor.
365 EXPECT_FALSE(constraintsMap.at(stickyOuterDiv->layer())
366 .nearestStickyBoxShiftingStickyBox());
367
368 // Neither does the outer inline element, as its parent element is also its
369 // containing block.
370 EXPECT_FALSE(constraintsMap.at(stickyOuterInline->layer())
371 .nearestStickyBoxShiftingStickyBox());
372
373 // However the inner inline element does have a sticky-box shifting ancestor,
374 // as its containing block is the ancestor block element, not its parent.
375 EXPECT_EQ(stickyOuterInline,
376 constraintsMap.at(stickyInnerInline->layer())
377 .nearestStickyBoxShiftingStickyBox());
378 }
379
380 // Verifies that the correct containing-block shifting ancestor is found when
381 // computing the sticky constraints. Any such ancestor is the first sticky
382 // element between your containing block (inclusive) and your ancestor overflow
383 // layer (exclusive).
384 TEST_F(LayoutBoxModelObjectTest,
385 StickyPositionFindsCorrectContainingBlockShiftingAncestor) {
386 // We make the scroller itself sticky in order to check that elements do not
387 // detect it as their containing-block shifting ancestor.
388 setBodyInnerHTML(
389 "<style>#scroller { overflow-y: scroll; position: sticky; }"
390 "#stickyParent { position: sticky; }"
391 "#stickyChild { position: sticky; }"
392 "#stickyNestedChild { position: sticky; }</style>"
393 "<div id='scroller'><div id='stickyParent'><div id='stickyChild'></div>"
394 "<div><div id='stickyNestedChild'></div></div></div></div>");
395
396 LayoutBoxModelObject* scroller =
397 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
398 LayoutBoxModelObject* stickyParent =
399 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent"));
400 LayoutBoxModelObject* stickyChild =
401 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyChild"));
402 LayoutBoxModelObject* stickyNestedChild =
403 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyNestedChild"));
404
405 PaintLayerScrollableArea* scrollableArea =
406 scroller->layer()->getScrollableArea();
407 ASSERT_TRUE(scrollableArea);
408 StickyConstraintsMap constraintsMap = scrollableArea->stickyConstraintsMap();
409
410 ASSERT_FALSE(constraintsMap.contains(scroller->layer()));
411 ASSERT_TRUE(constraintsMap.contains(stickyParent->layer()));
412 ASSERT_TRUE(constraintsMap.contains(stickyChild->layer()));
413 ASSERT_TRUE(constraintsMap.contains(stickyNestedChild->layer()));
414
415 // The outer <div> should not detect the scroller as its containing-block
416 // shifting ancestor.
417 EXPECT_FALSE(constraintsMap.at(stickyParent->layer())
418 .nearestStickyBoxShiftingContainingBlock());
419
420 // Both inner children should detect the parent <div> as their
421 // containing-block shifting ancestor.
422 EXPECT_EQ(stickyParent,
423 constraintsMap.at(stickyChild->layer())
424 .nearestStickyBoxShiftingContainingBlock());
425 EXPECT_EQ(stickyParent,
426 constraintsMap.at(stickyNestedChild->layer())
427 .nearestStickyBoxShiftingContainingBlock());
428 }
429
430 // Verifies that the correct containing-block shifting ancestor is found when
431 // computing the sticky constraints, in the case where the overflow ancestor is
432 // the page itself. This is a special-case version of the test above, as we
433 // often treat the root page as special when it comes to scroll logic. It should
434 // not make a difference for containing-block shifting ancestor calculations.
435 TEST_F(LayoutBoxModelObjectTest,
436 StickyPositionFindsCorrectContainingBlockShiftingAncestorRoot) {
437 setBodyInnerHTML(
438 "<style>#stickyParent { position: sticky; }"
439 "#stickyGrandchild { position: sticky; }</style>"
440 "<div id='stickyParent'><div><div id='stickyGrandchild'></div></div>"
441 "</div>");
442
443 LayoutBoxModelObject* stickyParent =
444 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent"));
445 LayoutBoxModelObject* stickyGrandchild =
446 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyGrandchild"));
447
448 PaintLayerScrollableArea* scrollableArea =
449 stickyParent->layer()->ancestorOverflowLayer()->getScrollableArea();
450 ASSERT_TRUE(scrollableArea);
451 StickyConstraintsMap constraintsMap = scrollableArea->stickyConstraintsMap();
452
453 ASSERT_TRUE(constraintsMap.contains(stickyParent->layer()));
454 ASSERT_TRUE(constraintsMap.contains(stickyGrandchild->layer()));
455
456 // The grandchild sticky should detect the parent as its containing-block
457 // shifting ancestor.
458 EXPECT_EQ(stickyParent,
459 constraintsMap.at(stickyGrandchild->layer())
460 .nearestStickyBoxShiftingContainingBlock());
461 }
462
463 // Verifies that the correct containing-block shifting ancestor is found when
464 // computing the sticky constraints, in the case of tables. Tables are unusual
465 // because the containing block for all table elements is the <table> itself, so
466 // we have to skip over elements to find the correct ancestor.
467 TEST_F(LayoutBoxModelObjectTest,
468 StickyPositionFindsCorrectContainingBlockShiftingAncestorTable) {
469 setBodyInnerHTML(
470 "<style>#scroller { overflow-y: scroll; }"
471 "#stickyOuter { position: sticky; }"
472 "#stickyTh { position: sticky; }</style>"
473 "<div id='scroller'><div id='stickyOuter'><table><thead><tr>"
474 "<th id='stickyTh'></th></tr></thead></table></div></div>");
475
476 LayoutBoxModelObject* scroller =
477 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
478 LayoutBoxModelObject* stickyOuter =
479 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyOuter"));
480 LayoutBoxModelObject* stickyTh =
481 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyTh"));
482
483 PaintLayerScrollableArea* scrollableArea =
484 scroller->layer()->getScrollableArea();
485 ASSERT_TRUE(scrollableArea);
486 StickyConstraintsMap constraintsMap = scrollableArea->stickyConstraintsMap();
487
488 ASSERT_FALSE(constraintsMap.contains(scroller->layer()));
489 ASSERT_TRUE(constraintsMap.contains(stickyOuter->layer()));
490 ASSERT_TRUE(constraintsMap.contains(stickyTh->layer()));
491
492 // The table cell should detect the outer <div> as its containing-block
493 // shifting ancestor.
494 EXPECT_EQ(stickyOuter,
495 constraintsMap.at(stickyTh->layer())
496 .nearestStickyBoxShiftingContainingBlock());
497 }
498
499 // Verifies that the calculated position:sticky offsets are correct when we have
500 // a simple case of nested sticky elements.
501 TEST_F(LayoutBoxModelObjectTest, StickyPositionNested) {
502 setBodyInnerHTML(
503 "<style>#scroller { height: 100px; width: 100px; overflow-y: auto; }"
504 "#prePadding { height: 50px }"
505 "#stickyParent { position: sticky; top: 0; height: 50px; }"
506 "#stickyChild { position: sticky; top: 0; height: 25px; }"
507 "#postPadding { height: 200px }</style>"
508 "<div id='scroller'><div id='prePadding'></div><div id='stickyParent'>"
509 "<div id='stickyChild'></div></div><div id='postPadding'></div></div>");
510
511 LayoutBoxModelObject* stickyParent =
512 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent"));
513 LayoutBoxModelObject* stickyChild =
514 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyChild"));
515
516 // Scroll the page down.
517 LayoutBoxModelObject* scroller =
518 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
519 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
520 scrollableArea->scrollToAbsolutePosition(
521 FloatPoint(scrollableArea->scrollPosition().x(), 100));
522 ASSERT_EQ(100.0, scrollableArea->scrollPosition().y());
523
524 // Both the parent and child sticky divs are attempting to place themselves at
525 // the top of the scrollable area. To achieve this the parent must offset on
526 // the y-axis against its starting position. The child is offset relative to
527 // its parent so should not move at all.
528 EXPECT_EQ(LayoutSize(0, 50), stickyParent->stickyPositionOffset());
529 EXPECT_EQ(LayoutSize(0, 0), stickyChild->stickyPositionOffset());
530
531 stickyParent->updateStickyPositionConstraints();
532
533 EXPECT_EQ(LayoutSize(0, 50), stickyParent->stickyPositionOffset());
534
535 stickyChild->updateStickyPositionConstraints();
536
537 EXPECT_EQ(LayoutSize(0, 50), stickyParent->stickyPositionOffset());
538 EXPECT_EQ(LayoutSize(0, 0), stickyChild->stickyPositionOffset());
539 }
540
541 // Verifies that the calculated position:sticky offsets are correct when the
542 // child has a larger edge constraint value than the parent.
543 TEST_F(LayoutBoxModelObjectTest, StickyPositionChildHasLargerTop) {
544 setBodyInnerHTML(
545 "<style>#scroller { height: 100px; width: 100px; overflow-y: auto; }"
546 "#prePadding { height: 50px }"
547 "#stickyParent { position: sticky; top: 0; height: 50px; }"
548 "#stickyChild { position: sticky; top: 25px; height: 25px; }"
549 "#postPadding { height: 200px }</style>"
550 "<div id='scroller'><div id='prePadding'></div><div id='stickyParent'>"
551 "<div id='stickyChild'></div></div><div id='postPadding'></div></div>");
552
553 LayoutBoxModelObject* stickyParent =
554 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent"));
555 LayoutBoxModelObject* stickyChild =
556 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyChild"));
557
558 // Scroll the page down.
559 LayoutBoxModelObject* scroller =
560 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
561 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
562 scrollableArea->scrollToAbsolutePosition(
563 FloatPoint(scrollableArea->scrollPosition().x(), 100));
564 ASSERT_EQ(100.0, scrollableArea->scrollPosition().y());
565
566 // The parent is attempting to place itself at the top of the scrollable area,
567 // whilst the child is attempting to be 25 pixels from the top. To achieve
568 // this both must offset on the y-axis against their starting positions, but
569 // note the child is offset relative to the parent.
570 EXPECT_EQ(LayoutSize(0, 50), stickyParent->stickyPositionOffset());
571 EXPECT_EQ(LayoutSize(0, 25), stickyChild->stickyPositionOffset());
572
573 stickyParent->updateStickyPositionConstraints();
574
575 EXPECT_EQ(LayoutSize(0, 50), stickyParent->stickyPositionOffset());
576
577 stickyChild->updateStickyPositionConstraints();
578
579 EXPECT_EQ(LayoutSize(0, 50), stickyParent->stickyPositionOffset());
580 EXPECT_EQ(LayoutSize(0, 25), stickyChild->stickyPositionOffset());
581 }
582
583 // Verifies that the calculated position:sticky offsets are correct when the
584 // child has a smaller edge constraint value than the parent.
585 TEST_F(LayoutBoxModelObjectTest, StickyPositionParentHasLargerTop) {
586 setBodyInnerHTML(
587 "<style>#scroller { height: 100px; width: 100px; overflow-y: auto; }"
588 "#prePadding { height: 50px }"
589 "#stickyParent { position: sticky; top: 25px; height: 50px; }"
590 "#stickyChild { position: sticky; top: 0; height: 25px; }"
591 "#postPadding { height: 200px }</style>"
592 "<div id='scroller'><div id='prePadding'></div><div id='stickyParent'>"
593 "<div id='stickyChild'></div></div><div id='postPadding'></div></div>");
594
595 LayoutBoxModelObject* stickyParent =
596 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent"));
597 LayoutBoxModelObject* stickyChild =
598 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyChild"));
599
600 // Scroll the page down.
601 LayoutBoxModelObject* scroller =
602 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
603 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
604 scrollableArea->scrollToAbsolutePosition(
605 FloatPoint(scrollableArea->scrollPosition().x(), 100));
606 ASSERT_EQ(100.0, scrollableArea->scrollPosition().y());
607
608 // The parent is attempting to place itself 25 pixels from the top of the
609 // scrollable area, whilst the child is attempting to be at the top. However,
610 // the child must stay contained within the parent, so it should be pushed
611 // down to the same height. As always, the child offset is relative.
612 EXPECT_EQ(LayoutSize(0, 75), stickyParent->stickyPositionOffset());
613 EXPECT_EQ(LayoutSize(0, 0), stickyChild->stickyPositionOffset());
614
615 stickyParent->updateStickyPositionConstraints();
616
617 EXPECT_EQ(LayoutSize(0, 75), stickyParent->stickyPositionOffset());
618
619 stickyChild->updateStickyPositionConstraints();
620
621 EXPECT_EQ(LayoutSize(0, 75), stickyParent->stickyPositionOffset());
622 EXPECT_EQ(LayoutSize(0, 0), stickyChild->stickyPositionOffset());
623 }
624
625 // Verifies that the calculated position:sticky offsets are correct when the
626 // child has a large enough edge constraint value to push outside of its parent.
627 TEST_F(LayoutBoxModelObjectTest, StickyPositionChildPushingOutsideParent) {
628 setBodyInnerHTML(
629 "<style> #scroller { height: 100px; width: 100px; overflow-y: auto; }"
630 "#prePadding { height: 50px; }"
631 "#stickyParent { position: sticky; top: 0; height: 50px; }"
632 "#stickyChild { position: sticky; top: 50px; height: 25px; }"
633 "#postPadding { height: 200px }</style>"
634 "<div id='scroller'><div id='prePadding'></div><div id='stickyParent'>"
635 "<div id='stickyChild'></div></div><div id='postPadding'></div></div>");
636
637 LayoutBoxModelObject* stickyParent =
638 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent"));
639 LayoutBoxModelObject* stickyChild =
640 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyChild"));
641
642 // Scroll the page down.
643 LayoutBoxModelObject* scroller =
644 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
645 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
646 scrollableArea->scrollToAbsolutePosition(
647 FloatPoint(scrollableArea->scrollPosition().x(), 100));
648 ASSERT_EQ(100.0, scrollableArea->scrollPosition().y());
649
650 // The parent is attempting to place itself at the top of the scrollable area,
651 // whilst the child is attempting to be 50 pixels from the top. However, there
652 // is only 25 pixels of space for the child to move into, so it should be
653 // capped by that offset. As always, the child offset is relative.
654 EXPECT_EQ(LayoutSize(0, 50), stickyParent->stickyPositionOffset());
655 EXPECT_EQ(LayoutSize(0, 25), stickyChild->stickyPositionOffset());
656
657 stickyParent->updateStickyPositionConstraints();
658
659 EXPECT_EQ(LayoutSize(0, 50), stickyParent->stickyPositionOffset());
660
661 stickyChild->updateStickyPositionConstraints();
662
663 EXPECT_EQ(LayoutSize(0, 50), stickyParent->stickyPositionOffset());
664 EXPECT_EQ(LayoutSize(0, 25), stickyChild->stickyPositionOffset());
665 }
666
667 // Verifies that the calculated position:sticky offsets are correct in the case
668 // of triple nesting. Triple (or more) nesting must be tested as the grandchild
669 // sticky must correct both its sticky box constraint rect and its containing
670 // block constaint rect.
671 TEST_F(LayoutBoxModelObjectTest, StickyPositionTripleNestedDiv) {
672 setBodyInnerHTML(
673 "<style>#scroller { height: 200px; width: 100px; overflow-y: auto; }"
674 "#prePadding { height: 50px; }"
675 "#outmostSticky { position: sticky; top: 0; height: 100px; }"
676 "#middleSticky { position: sticky; top: 0; height: 75px; }"
677 "#innerSticky { position: sticky; top: 25px; height: 25px; }"
678 "#postPadding { height: 400px }</style>"
679 "<div id='scroller'><div id='prePadding'></div><div id='outmostSticky'>"
680 "<div id='middleSticky'><div id='innerSticky'></div></div></div>"
681 "<div id='postPadding'></div></div>");
682
683 LayoutBoxModelObject* outmostSticky =
684 toLayoutBoxModelObject(getLayoutObjectByElementId("outmostSticky"));
685 LayoutBoxModelObject* middleSticky =
686 toLayoutBoxModelObject(getLayoutObjectByElementId("middleSticky"));
687 LayoutBoxModelObject* innerSticky =
688 toLayoutBoxModelObject(getLayoutObjectByElementId("innerSticky"));
689
690 // Scroll the page down.
691 LayoutBoxModelObject* scroller =
692 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
693 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
694 scrollableArea->scrollToAbsolutePosition(
695 FloatPoint(scrollableArea->scrollPosition().x(), 100));
696 ASSERT_EQ(100.0, scrollableArea->scrollPosition().y());
697
698 // The grandparent and parent divs are attempting to place themselves at the
699 // top of the scrollable area. The child div is attempting to place itself at
700 // an offset of 25 pixels to the top of the scrollable area. The result of
701 // this sticky offset calculation is quite simple, but internally the child
702 // offset has to offset both its sticky box constraint rect and its containing
703 // block constraint rect.
704 EXPECT_EQ(LayoutSize(0, 50), outmostSticky->stickyPositionOffset());
705 EXPECT_EQ(LayoutSize(0, 0), middleSticky->stickyPositionOffset());
706 EXPECT_EQ(LayoutSize(0, 25), innerSticky->stickyPositionOffset());
707
708 outmostSticky->updateStickyPositionConstraints();
709
710 EXPECT_EQ(LayoutSize(0, 50), outmostSticky->stickyPositionOffset());
711
712 middleSticky->updateStickyPositionConstraints();
713
714 EXPECT_EQ(LayoutSize(0, 50), outmostSticky->stickyPositionOffset());
715 EXPECT_EQ(LayoutSize(0, 0), middleSticky->stickyPositionOffset());
716
717 innerSticky->updateStickyPositionConstraints();
718
719 EXPECT_EQ(LayoutSize(0, 50), outmostSticky->stickyPositionOffset());
720 EXPECT_EQ(LayoutSize(0, 0), middleSticky->stickyPositionOffset());
721 EXPECT_EQ(LayoutSize(0, 25), innerSticky->stickyPositionOffset());
722 }
723
724 // Verifies that the calculated position:sticky offsets are correct in the case
725 // of tables. Tables are special as the containing block for table elements is
726 // always the root level <table>.
727 TEST_F(LayoutBoxModelObjectTest, StickyPositionNestedStickyTable) {
728 setBodyInnerHTML(
729 "<style>table { border-collapse: collapse; }"
730 "td, th { height: 25px; width: 25px; padding: 0; }"
731 "#scroller { height: 100px; width: 100px; overflow-y: auto; }"
732 "#prePadding { height: 50px; }"
733 "#stickyDiv { position: sticky; top: 0; height: 200px; }"
734 "#stickyTh { position: sticky; top: 0; }"
735 "#postPadding { height: 200px; }</style>"
736 "<div id='scroller'><div id='prePadding'></div><div id='stickyDiv'>"
737 "<table><thead><tr><th id='stickyTh'></th></tr></thead><tbody><tr><td>"
738 "</td></tr><tr><td></td></tr><tr><td></td></tr><tr><td></td></tr></tbody>"
739 "</table></div><div id='postPadding'></div></div>");
740
741 LayoutBoxModelObject* stickyDiv =
742 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyDiv"));
743 LayoutBoxModelObject* stickyTh =
744 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyTh"));
745
746 // Scroll the page down.
747 LayoutBoxModelObject* scroller =
748 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
749 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
750 scrollableArea->scrollToAbsolutePosition(
751 FloatPoint(scrollableArea->scrollPosition().x(), 150));
752 ASSERT_EQ(150.0, scrollableArea->scrollPosition().y());
753
754 // All sticky elements are attempting to stick to the top of the scrollable
755 // area. For the root sticky div, this requires an offset. All the other
756 // descendant sticky elements are positioned relatively so don't need offset.
757 EXPECT_EQ(LayoutSize(0, 100), stickyDiv->stickyPositionOffset());
758 EXPECT_EQ(LayoutSize(0, 0), stickyTh->stickyPositionOffset());
759
760 // If we now scroll to the point where the overall sticky div starts to move,
761 // the table headers should continue to stick to the top of the scrollable
762 // area until they run out of <table> space to move in.
763
764 scrollableArea->scrollToAbsolutePosition(
765 FloatPoint(scrollableArea->scrollPosition().x(), 275));
766 ASSERT_EQ(275.0, scrollableArea->scrollPosition().y());
767
768 EXPECT_EQ(LayoutSize(0, 200), stickyDiv->stickyPositionOffset());
769 EXPECT_EQ(LayoutSize(0, 25), stickyTh->stickyPositionOffset());
770
771 // Finally, if we scroll so that the table is off the top of the page, the
772 // sticky header should travel as far as it can (i.e. the table height) then
773 // move off the top with it.
774 scrollableArea->scrollToAbsolutePosition(
775 FloatPoint(scrollableArea->scrollPosition().x(), 350));
776 ASSERT_EQ(350.0, scrollableArea->scrollPosition().y());
777
778 EXPECT_EQ(LayoutSize(0, 200), stickyDiv->stickyPositionOffset());
779 EXPECT_EQ(LayoutSize(0, 100), stickyTh->stickyPositionOffset());
780
781 stickyDiv->updateStickyPositionConstraints();
782
783 EXPECT_EQ(LayoutSize(0, 200), stickyDiv->stickyPositionOffset());
784
785 stickyTh->updateStickyPositionConstraints();
786
787 EXPECT_EQ(LayoutSize(0, 200), stickyDiv->stickyPositionOffset());
788 EXPECT_EQ(LayoutSize(0, 100), stickyTh->stickyPositionOffset());
789 }
790
791 // Verifies that the calculated position:sticky offsets are correct in the case
792 // where a particular position:sticky element is used both as a sticky-box
793 // shifting ancestor as well as a containing-block shifting ancestor.
794 //
795 // This is a rare case that can be replicated by nesting tables so that a sticky
796 // cell contains another table that has sticky elements. See the HTML below.
797 TEST_F(LayoutBoxModelObjectTest, StickyPositionComplexTableNesting) {
798 setBodyInnerHTML(
799 "<style>table { border-collapse: collapse; }"
800 "td, th { height: 25px; width: 25px; padding: 0; }"
801 "#scroller { height: 100px; width: 100px; overflow-y: auto; }"
802 "#prePadding { height: 50px; }"
803 "#outerStickyTh { height: 50px; position: sticky; top: 0; }"
804 "#innerStickyTh { position: sticky; top: 25px; }"
805 "#postPadding { height: 200px; }</style>"
806 "<div id='scroller'><div id='prePadding'></div>"
807 "<table><thead><tr><th id='outerStickyTh'><table><thead><tr>"
808 "<th id='innerStickyTh'></th></tr></thead><tbody><tr><td></td></tr>"
809 "</tbody></table></th></tr></thead><tbody><tr><td></td></tr><tr><td></td>"
810 "</tr><tr><td></td></tr><tr><td></td></tr></tbody></table>"
811 "<div id='postPadding'></div></div>");
812
813 LayoutBoxModelObject* outerStickyTh =
814 toLayoutBoxModelObject(getLayoutObjectByElementId("outerStickyTh"));
815 LayoutBoxModelObject* innerStickyTh =
816 toLayoutBoxModelObject(getLayoutObjectByElementId("innerStickyTh"));
817
818 // Scroll the page down.
819 LayoutBoxModelObject* scroller =
820 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
821 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
822 scrollableArea->scrollToAbsolutePosition(
823 FloatPoint(scrollableArea->scrollPosition().x(), 150));
824 ASSERT_EQ(150.0, scrollableArea->scrollPosition().y());
825
826 EXPECT_EQ(LayoutSize(0, 100), outerStickyTh->stickyPositionOffset());
827 EXPECT_EQ(LayoutSize(0, 25), innerStickyTh->stickyPositionOffset());
828
829 outerStickyTh->updateStickyPositionConstraints();
830
831 EXPECT_EQ(LayoutSize(0, 100), outerStickyTh->stickyPositionOffset());
832
833 innerStickyTh->updateStickyPositionConstraints();
834
835 EXPECT_EQ(LayoutSize(0, 100), outerStickyTh->stickyPositionOffset());
836 EXPECT_EQ(LayoutSize(0, 25), innerStickyTh->stickyPositionOffset());
837 }
838
839 // Verifies that the calculated position:sticky offsets are correct in the case
840 // of nested inline elements.
841 TEST_F(LayoutBoxModelObjectTest, StickyPositionNestedInlineElements) {
842 setBodyInnerHTML(
843 "<style>#scroller { width: 100px; height: 100px; overflow-y: scroll; }"
844 "#paddingBefore { height: 50px; }"
845 "#outerInline { display: inline; position: sticky; top: 0; }"
846 "#innerInline { display: inline; position: sticky; top: 25px; }"
847 "#paddingAfter { height: 200px; }</style>"
848 "<div id='scroller'><div id='paddingBefore'></div><div id='outerInline'>"
849 "<div id='innerInline'></div></div><div id='paddingAfter'></div></div>");
850
851 LayoutBoxModelObject* outerInline =
852 toLayoutBoxModelObject(getLayoutObjectByElementId("outerInline"));
853 LayoutBoxModelObject* innerInline =
854 toLayoutBoxModelObject(getLayoutObjectByElementId("innerInline"));
855
856 // Scroll the page down.
857 LayoutBoxModelObject* scroller =
858 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
859 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
860 scrollableArea->scrollToAbsolutePosition(
861 FloatPoint(scrollableArea->scrollPosition().x(), 50));
862 ASSERT_EQ(50.0, scrollableArea->scrollPosition().y());
863
864 EXPECT_EQ(LayoutSize(0, 0), outerInline->stickyPositionOffset());
865 EXPECT_EQ(LayoutSize(0, 25), innerInline->stickyPositionOffset());
866 }
867
868 // Verifies that the calculated position:sticky offsets are correct in the case
869 // of an intermediate position:fixed element.
870 TEST_F(LayoutBoxModelObjectTest, StickyPositionNestedFixedPos) {
871 setBodyInnerHTML(
872 "<style>body { margin: 0; }"
873 "#scroller { height: 200px; width: 100px; overflow-y: auto; }"
874 "#outerSticky { position: sticky; top: 0; height: 50px; }"
875 "#fixedDiv { position: fixed; top: 0; left: 300px; height: 100px; "
876 "width: 100px; }"
877 "#innerSticky { position: sticky; top: 25px; height: 25px; }"
878 "#padding { height: 400px }</style>"
879 "<div id='scroller'><div id='outerSticky'><div id='fixedDiv'>"
880 "<div id='innerSticky'></div></div></div><div id='padding'></div></div>");
881
882 LayoutBoxModelObject* outerSticky =
883 toLayoutBoxModelObject(getLayoutObjectByElementId("outerSticky"));
884 LayoutBoxModelObject* innerSticky =
885 toLayoutBoxModelObject(getLayoutObjectByElementId("innerSticky"));
886
887 LayoutBoxModelObject* scroller =
888 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
889 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
890
891 StickyConstraintsMap constraintsMap = scrollableArea->stickyConstraintsMap();
892 ASSERT_TRUE(constraintsMap.contains(outerSticky->layer()));
893 ASSERT_TRUE(constraintsMap.contains(innerSticky->layer()));
894
895 // The inner sticky should not detect the outer one as any sort of ancestor.
896 EXPECT_FALSE(constraintsMap.at(innerSticky->layer())
897 .nearestStickyBoxShiftingStickyBox());
898 EXPECT_FALSE(constraintsMap.at(innerSticky->layer())
899 .nearestStickyBoxShiftingContainingBlock());
900
901 // Scroll the page down.
902 scrollableArea->scrollToAbsolutePosition(
903 FloatPoint(scrollableArea->scrollPosition().x(), 100));
904 ASSERT_EQ(100.0, scrollableArea->scrollPosition().y());
905
906 // TODO(smcgruer): Until http://crbug.com/686164 is fixed, we need to update
907 // the constraints here before calculations will be correct.
908 innerSticky->updateStickyPositionConstraints();
909
910 EXPECT_EQ(LayoutSize(0, 100), outerSticky->stickyPositionOffset());
911 EXPECT_EQ(LayoutSize(0, 25), innerSticky->stickyPositionOffset());
912 }
913
331 } // namespace blink 914 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp ('k') | third_party/WebKit/Source/core/layout/LayoutObject.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698