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

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

Issue 2636253002: Handle nested position:sticky elements (Closed)
Patch Set: Use ancestorOverflowLayer instead of stickyAncestor + test fixing Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // 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/html/HTMLElement.h" 7 #include "core/html/HTMLElement.h"
8 #include "core/layout/ImageQualityController.h" 8 #include "core/layout/ImageQualityController.h"
9 #include "core/layout/LayoutTestHelper.h" 9 #include "core/layout/LayoutTestHelper.h"
10 #include "core/page/scrolling/StickyPositionScrollingConstraints.h" 10 #include "core/page/scrolling/StickyPositionScrollingConstraints.h"
(...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after
242 242
243 const StickyPositionScrollingConstraints& constraints = 243 const StickyPositionScrollingConstraints& constraints =
244 scrollableArea->stickyConstraintsMap().get(sticky->layer()); 244 scrollableArea->stickyConstraintsMap().get(sticky->layer());
245 ASSERT_EQ(IntRect(15, 115, 170, 370), 245 ASSERT_EQ(IntRect(15, 115, 170, 370),
246 enclosingIntRect( 246 enclosingIntRect(
247 getScrollContainerRelativeContainingBlockRect(constraints))); 247 getScrollContainerRelativeContainingBlockRect(constraints)));
248 ASSERT_EQ( 248 ASSERT_EQ(
249 IntRect(15, 165, 100, 100), 249 IntRect(15, 165, 100, 100),
250 enclosingIntRect(getScrollContainerRelativeStickyBoxRect(constraints))); 250 enclosingIntRect(getScrollContainerRelativeStickyBoxRect(constraints)));
251 } 251 }
252
253 // Verifies that the correct sticky-box shifting ancestor is found when
254 // computing the sticky constraints. Any such ancestor is the first sticky
255 // element between you and your containing block (exclusive).
256 //
257 // In most cases, this pointer should be null since your parent is normally your
258 // containing block. However there are a few cases where this is not true, most
259 // notably in tables where the root <table> is the containing block for the
260 // descendant elements.
261 TEST_F(LayoutBoxModelObjectTest,
262 StickyPositionFindsCorrectStickyBoxShiftingAncestor) {
263 setBodyInnerHTML(
264 "<style>#stickyOuterDiv { position: sticky; }"
265 "#stickyInnerDiv { position: sticky; }"
266 "#stickyThead { position: sticky; }"
267 "#stickyTr { position: sticky; }"
268 "#stickyTh { position: sticky; }</style>"
269 "<div id='stickyOuterDiv'><div id='stickyInnerDiv'></div><table>"
270 "<thead id='stickyThead'><tr id='stickyTr'><th id='stickyTh'></th></tr>"
271 "</thead></table></div>");
272
273 // Force the constraints to be calculated.
274 LayoutBoxModelObject* stickyOuterDiv =
275 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyOuterDiv"));
276 stickyOuterDiv->updateStickyPositionConstraints();
277
278 LayoutBoxModelObject* stickyInnerDiv =
279 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyInnerDiv"));
280 stickyInnerDiv->updateStickyPositionConstraints();
281
282 LayoutBoxModelObject* stickyThead =
283 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyThead"));
284 stickyThead->updateStickyPositionConstraints();
285
286 LayoutBoxModelObject* stickyTr =
287 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyTr"));
288 stickyTr->updateStickyPositionConstraints();
289
290 LayoutBoxModelObject* stickyTh =
291 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyTh"));
292 stickyTh->updateStickyPositionConstraints();
293
294 // There's only one scrollableArea, the main page.
295 PaintLayerScrollableArea* scrollableArea =
296 stickyOuterDiv->layer()->ancestorOverflowLayer()->getScrollableArea();
297 ASSERT_TRUE(scrollableArea);
298 StickyConstraintsMap constraintsMap = scrollableArea->stickyConstraintsMap();
299
300 ASSERT_TRUE(constraintsMap.contains(stickyOuterDiv->layer()));
301 ASSERT_TRUE(constraintsMap.contains(stickyInnerDiv->layer()));
302 ASSERT_TRUE(constraintsMap.contains(stickyThead->layer()));
303 ASSERT_TRUE(constraintsMap.contains(stickyTr->layer()));
304 ASSERT_TRUE(constraintsMap.contains(stickyTh->layer()));
305
306 // The outer <div> trivially has no sticky-box shifting ancestor.
307 EXPECT_FALSE(constraintsMap.get(stickyOuterDiv->layer())
308 .nearestStickyElementShiftingStickyBox());
309
310 // Neither does the inner <div>, as its parent is also its containing block.
311 EXPECT_FALSE(constraintsMap.get(stickyInnerDiv->layer())
312 .nearestStickyElementShiftingStickyBox());
313
314 // For tables, the containing block is always the <table> itself, so the <tr>
315 // and <th> both have sticky-box shifting ancestors.
316 EXPECT_FALSE(constraintsMap.get(stickyThead->layer())
317 .nearestStickyElementShiftingStickyBox());
318 EXPECT_EQ(stickyThead, constraintsMap.get(stickyTr->layer())
319 .nearestStickyElementShiftingStickyBox());
320 EXPECT_EQ(stickyTr, constraintsMap.get(stickyTh->layer())
321 .nearestStickyElementShiftingStickyBox());
322 }
323
324 // Verifies that the correct containing-block shifting ancestor is found when
325 // computing the sticky constraints. Any such ancestor is the first sticky
326 // element between your containing block (inclusive) and your ancestor overflow
327 // layer (exclusive).
328 TEST_F(LayoutBoxModelObjectTest,
329 StickyPositionFindsCorrectContainingBlockShiftingAncestor) {
330 // We make the scroller itself sticky in order to check that elements do not
331 // detect it as their containing-block shifting ancestor.
332 setBodyInnerHTML(
333 "<style>#scroller { overflow-y: scroll; position: sticky; }"
334 "#stickyParent { position: sticky; }"
335 "#stickyChild { position: sticky; }"
336 "#stickyNestedChild { position: sticky; }</style>"
337 "<div id='scroller'><div id='stickyParent'><div id='stickyChild'></div>"
338 "<div><div id='stickyNestedChild'></div></div></div></div>");
339
340 // Force the constraints to be calculated.
341 LayoutBoxModelObject* scroller =
342 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
343 scroller->updateStickyPositionConstraints();
344
345 LayoutBoxModelObject* stickyParent =
346 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent"));
347 stickyParent->updateStickyPositionConstraints();
348
349 LayoutBoxModelObject* stickyChild =
350 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyChild"));
351 stickyChild->updateStickyPositionConstraints();
352
353 LayoutBoxModelObject* stickyNestedChild =
354 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyNestedChild"));
355 stickyNestedChild->updateStickyPositionConstraints();
356
357 // There are two scrollable areas, but we only care about the scroller itself.
358 PaintLayerScrollableArea* scrollableArea =
359 scroller->layer()->getScrollableArea();
360 ASSERT_TRUE(scrollableArea);
361 StickyConstraintsMap constraintsMap = scrollableArea->stickyConstraintsMap();
362
363 ASSERT_FALSE(constraintsMap.contains(scroller->layer()));
364 ASSERT_TRUE(constraintsMap.contains(stickyParent->layer()));
365 ASSERT_TRUE(constraintsMap.contains(stickyChild->layer()));
366 ASSERT_TRUE(constraintsMap.contains(stickyNestedChild->layer()));
367
368 // The outer <div> should not detect the scroller as its containing-block
369 // shifting ancestor.
370 EXPECT_FALSE(constraintsMap.get(stickyParent->layer())
371 .nearestStickyElementShiftingContainingBlock());
372
373 // Both inner children should detect the parent <div> as their
374 // containing-block shifting ancestor.
375 EXPECT_EQ(stickyParent, constraintsMap.get(stickyChild->layer())
376 .nearestStickyElementShiftingContainingBlock());
377 EXPECT_EQ(stickyParent, constraintsMap.get(stickyNestedChild->layer())
378 .nearestStickyElementShiftingContainingBlock());
379 }
380
381 // Verifies that the correct containing-block shifting ancestor is found when
382 // computing the sticky constraints, in the case where the overflow ancestor is
383 // the page itself. This is a special-case version of the test above, as we
384 // often treat the root page as special when it comes to scroll logic. It should
385 // not make a difference for containing-block shifting ancestor calculations.
386 TEST_F(LayoutBoxModelObjectTest,
387 StickyPositionFindsCorrectContainingBlockShiftingAncestorRoot) {
388 setBodyInnerHTML(
389 "<style>#stickyParent { position: sticky; }"
390 "#stickyGrandchild { position: sticky; }</style>"
391 "<div id='stickyParent'><div><div id='stickyGrandchild'></div></div>"
392 "</div>");
393
394 // Force the constraints to be calculated.
395
396 LayoutBoxModelObject* stickyParent =
397 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent"));
398 stickyParent->updateStickyPositionConstraints();
399
400 LayoutBoxModelObject* stickyGrandchild =
401 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyGrandchild"));
402 stickyGrandchild->updateStickyPositionConstraints();
403
404 PaintLayerScrollableArea* scrollableArea =
405 stickyParent->layer()->ancestorOverflowLayer()->getScrollableArea();
406 ASSERT_TRUE(scrollableArea);
407 StickyConstraintsMap constraintsMap = scrollableArea->stickyConstraintsMap();
408
409 ASSERT_TRUE(constraintsMap.contains(stickyParent->layer()));
410 ASSERT_TRUE(constraintsMap.contains(stickyGrandchild->layer()));
411
412 // The grandchild sticky should detect the parent as its containing-block
413 // shifting ancestor.
414 EXPECT_EQ(stickyParent, constraintsMap.get(stickyGrandchild->layer())
415 .nearestStickyElementShiftingContainingBlock());
416 }
417
418 // Verifies that the correct containing-block shifting ancestor is found when
419 // computing the sticky constraints, in the case of tables. Tables are unusual
420 // because the containing block for all table elements is the <table> itself, so
421 // we have to skip over elements to find the correct ancestor.
422 TEST_F(LayoutBoxModelObjectTest,
423 StickyPositionFindsCorrectContainingBlockShiftingAncestorTable) {
424 setBodyInnerHTML(
425 "<style>#scroller { overflow-y: scroll; }"
426 "#stickyOuter { position: sticky; }"
427 "#stickyThead { position: sticky; }"
428 "#stickyTr { position: sticky; }"
429 "#stickyTh { position: sticky; }</style>"
430 "<div id='scroller'><div id='stickyOuter'><table><thead id='stickyThead'>"
431 "<tr id='stickyTr'><th id='stickyTh'></th></tr></thead></table></div>"
432 "</div>");
433
434 // Force the constraints to be calculated.
435 LayoutBoxModelObject* scroller =
436 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
437 scroller->updateStickyPositionConstraints();
438
439 LayoutBoxModelObject* stickyOuter =
440 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyOuter"));
441 stickyOuter->updateStickyPositionConstraints();
442
443 LayoutBoxModelObject* stickyThead =
444 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyThead"));
445 stickyThead->updateStickyPositionConstraints();
446
447 LayoutBoxModelObject* stickyTr =
448 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyTr"));
449 stickyTr->updateStickyPositionConstraints();
450
451 LayoutBoxModelObject* stickyTh =
452 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyTh"));
453 stickyTh->updateStickyPositionConstraints();
454
455 // There are two scrollable areas, but we only care about the scroller itself.
456 PaintLayerScrollableArea* scrollableArea =
457 scroller->layer()->getScrollableArea();
458 ASSERT_TRUE(scrollableArea);
459 StickyConstraintsMap constraintsMap = scrollableArea->stickyConstraintsMap();
460
461 ASSERT_FALSE(constraintsMap.contains(scroller->layer()));
462 ASSERT_TRUE(constraintsMap.contains(stickyOuter->layer()));
463 ASSERT_TRUE(constraintsMap.contains(stickyThead->layer()));
464 ASSERT_TRUE(constraintsMap.contains(stickyTr->layer()));
465 ASSERT_TRUE(constraintsMap.contains(stickyTh->layer()));
466
467 // All of the table elements should detect the outer <div> as their
468 // containing-block shifting ancestor.
469 EXPECT_EQ(stickyOuter, constraintsMap.get(stickyThead->layer())
470 .nearestStickyElementShiftingContainingBlock());
471 EXPECT_EQ(stickyOuter, constraintsMap.get(stickyTr->layer())
472 .nearestStickyElementShiftingContainingBlock());
473 EXPECT_EQ(stickyOuter, constraintsMap.get(stickyTh->layer())
474 .nearestStickyElementShiftingContainingBlock());
475 }
476
477 // Verifies that the calculated position:sticky offsets are correct when we have
478 // a simple case of nested sticky elements.
479 TEST_F(LayoutBoxModelObjectTest, StickyPositionNested) {
480 setBodyInnerHTML(
481 "<style>#scroller { height: 100px; width: 100px; overflow-y: auto; }"
482 "#prePadding { height: 50px }"
483 "#stickyParent { position: sticky; top: 0; height: 50px; }"
484 "#stickyChild { position: sticky; top: 0; height: 25px; }"
485 "#postPadding { height: 200px }</style>"
486 "<div id='scroller'><div id='prePadding'></div><div id='stickyParent'>"
487 "<div id='stickyChild'></div></div><div id='postPadding'></div></div>");
488
489 // Make sure that the constraints are cached before scrolling or they may be
490 // calculated post-scroll and thus not require offset correction.
491
492 LayoutBoxModelObject* stickyParent =
493 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent"));
494 stickyParent->updateStickyPositionConstraints();
495
496 LayoutBoxModelObject* stickyChild =
497 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyChild"));
498 stickyChild->updateStickyPositionConstraints();
499
500 // Scroll the page down.
501 LayoutBoxModelObject* scroller =
502 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
503 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
504 scrollableArea->scrollToAbsolutePosition(
505 FloatPoint(scrollableArea->scrollPosition().x(), 100));
506 ASSERT_EQ(100.0, scrollableArea->scrollPosition().y());
507
508 // Both the parent and child sticky divs are attempting to place themselves at
509 // the top of the scrollable area. To achieve this the parent must offset on
510 // the y-axis against its starting position. The child is offset relative to
511 // its parent so should not move at all.
512 EXPECT_EQ(LayoutSize(0, 50), stickyParent->stickyPositionOffset());
513 EXPECT_EQ(LayoutSize(0, 0), stickyChild->stickyPositionOffset());
514 }
515
516 // Verifies that the calculated position:sticky offsets are correct when the
517 // child has a larger edge constraint value than the parent.
518 TEST_F(LayoutBoxModelObjectTest, StickyPositionChildHasLargerTop) {
519 setBodyInnerHTML(
520 "<style>#scroller { height: 100px; width: 100px; overflow-y: auto; }"
521 "#prePadding { height: 50px }"
522 "#stickyParent { position: sticky; top: 0; height: 50px; }"
523 "#stickyChild { position: sticky; top: 25px; height: 25px; }"
524 "#postPadding { height: 200px }</style>"
525 "<div id='scroller'><div id='prePadding'></div><div id='stickyParent'>"
526 "<div id='stickyChild'></div></div><div id='postPadding'></div></div>");
527
528 // Make sure that the constraints are cached before scrolling or they may be
529 // calculated post-scroll and thus not require offset correction.
530
531 LayoutBoxModelObject* stickyParent =
532 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent"));
533 stickyParent->updateStickyPositionConstraints();
534
535 LayoutBoxModelObject* stickyChild =
536 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyChild"));
537 stickyChild->updateStickyPositionConstraints();
538
539 // Scroll the page down.
540 LayoutBoxModelObject* scroller =
541 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
542 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
543 scrollableArea->scrollToAbsolutePosition(
544 FloatPoint(scrollableArea->scrollPosition().x(), 100));
545 ASSERT_EQ(100.0, scrollableArea->scrollPosition().y());
546
547 // The parent is attempting to place itself at the top of the scrollable area,
548 // whilst the child is attempting to be 25 pixels from the top. To achieve
549 // this both must offset on the y-axis against their starting positions, but
550 // note the child is offset relative to the parent.
551 EXPECT_EQ(LayoutSize(0, 50), stickyParent->stickyPositionOffset());
552 EXPECT_EQ(LayoutSize(0, 25), stickyChild->stickyPositionOffset());
553 }
554
555 // Verifies that the calculated position:sticky offsets are correct when the
556 // child has a smaller edge constraint value than the parent.
557 TEST_F(LayoutBoxModelObjectTest, StickyPositionParentHasLargerTop) {
558 setBodyInnerHTML(
559 "<style>#scroller { height: 100px; width: 100px; overflow-y: auto; }"
560 "#prePadding { height: 50px }"
561 "#stickyParent { position: sticky; top: 25px; height: 50px; }"
562 "#stickyChild { position: sticky; top: 0; height: 25px; }"
563 "#postPadding { height: 200px }</style>"
564 "<div id='scroller'><div id='prePadding'></div><div id='stickyParent'>"
565 "<div id='stickyChild'></div></div><div id='postPadding'></div></div>");
566
567 // Make sure that the constraints are cached before scrolling or they may be
568 // calculated post-scroll and thus not require offset correction.
569
570 LayoutBoxModelObject* stickyParent =
571 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent"));
572 stickyParent->updateStickyPositionConstraints();
573
574 LayoutBoxModelObject* stickyChild =
575 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyChild"));
576 stickyChild->updateStickyPositionConstraints();
577
578 // Scroll the page down.
579 LayoutBoxModelObject* scroller =
580 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
581 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
582 scrollableArea->scrollToAbsolutePosition(
583 FloatPoint(scrollableArea->scrollPosition().x(), 100));
584 ASSERT_EQ(100.0, scrollableArea->scrollPosition().y());
585
586 // The parent is attempting to place itself 25 pixels from the top of the
587 // scrollable area, whilst the child is attempting to be at the top. However,
588 // the child must stay contained within the parent, so it should be pushed
589 // down to the same height. As always, the child offset is relative.
590 EXPECT_EQ(LayoutSize(0, 75), stickyParent->stickyPositionOffset());
591 EXPECT_EQ(LayoutSize(0, 0), stickyChild->stickyPositionOffset());
592 }
593
594 // Verifies that the calculated position:sticky offsets are correct when the
595 // child has a large enough edge constraint value to push outside of its parent.
596 TEST_F(LayoutBoxModelObjectTest, StickyPositionChildPushingOutsideParent) {
597 setBodyInnerHTML(
598 "<style> #scroller { height: 100px; width: 100px; overflow-y: auto; }"
599 "#prePadding { height: 50px; }"
600 "#stickyParent { position: sticky; top: 0; height: 50px; }"
601 "#stickyChild { position: sticky; top: 50px; height: 25px; }"
602 "#postPadding { height: 200px }</style>"
603 "<div id='scroller'><div id='prePadding'></div><div id='stickyParent'>"
604 "<div id='stickyChild'></div></div><div id='postPadding'></div></div>");
605
606 // Make sure that the constraints are cached before scrolling or they may be
607 // calculated post-scroll and thus not require offset correction.
608
609 LayoutBoxModelObject* stickyParent =
610 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent"));
611 stickyParent->updateStickyPositionConstraints();
612
613 LayoutBoxModelObject* stickyChild =
614 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyChild"));
615 stickyChild->updateStickyPositionConstraints();
616
617 // Scroll the page down.
618 LayoutBoxModelObject* scroller =
619 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
620 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
621 scrollableArea->scrollToAbsolutePosition(
622 FloatPoint(scrollableArea->scrollPosition().x(), 100));
623 ASSERT_EQ(100.0, scrollableArea->scrollPosition().y());
624
625 // The parent is attempting to place itself at the top of the scrollable area,
626 // whilst the child is attempting to be 50 pixels from the top. However, there
627 // is only 25 pixels of space for the child to move into, so it should be
628 // capped by that offset. As always, the child offset is relative.
629 EXPECT_EQ(LayoutSize(0, 50), stickyParent->stickyPositionOffset());
630 EXPECT_EQ(LayoutSize(0, 25), stickyChild->stickyPositionOffset());
631 }
632
633 // Verifies that the calculated position:sticky offsets are correct in the case
634 // of triple nesting. Triple (or more) nesting must be tested as the grandchild
635 // sticky must correct both its sticky box constraint rect and its containing
636 // block constaint rect.
637 TEST_F(LayoutBoxModelObjectTest, StickyPositionTripleNestedDiv) {
638 setBodyInnerHTML(
639 "<style>#scroller { height: 200px; width: 100px; overflow-y: auto; }"
640 "#prePadding { height: 50px; }"
641 "#outmostSticky { position: sticky; top: 0; height: 100px; }"
642 "#middleSticky { position: sticky; top: 0; height: 75px; }"
643 "#innerSticky { position: sticky; top: 25px; height: 25px; }"
644 "#postPadding { height: 400px }</style>"
645 "<div id='scroller'><div id='prePadding'></div><div id='outmostSticky'>"
646 "<div id='middleSticky'><div id='innerSticky'></div></div></div>"
647 "<div id='postPadding'></div></div>");
648
649 // Make sure that the constraints are cached before scrolling or they may be
650 // calculated post-scroll and thus not require offset correction.
651
652 LayoutBoxModelObject* outmostSticky =
653 toLayoutBoxModelObject(getLayoutObjectByElementId("outmostSticky"));
654 outmostSticky->updateStickyPositionConstraints();
655
656 LayoutBoxModelObject* middleSticky =
657 toLayoutBoxModelObject(getLayoutObjectByElementId("middleSticky"));
658 middleSticky->updateStickyPositionConstraints();
659
660 LayoutBoxModelObject* innerSticky =
661 toLayoutBoxModelObject(getLayoutObjectByElementId("innerSticky"));
662 innerSticky->updateStickyPositionConstraints();
663
664 // Scroll the page down.
665 LayoutBoxModelObject* scroller =
666 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
667 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
668 scrollableArea->scrollToAbsolutePosition(
669 FloatPoint(scrollableArea->scrollPosition().x(), 100));
670 ASSERT_EQ(100.0, scrollableArea->scrollPosition().y());
671
672 // The grandparent and parent divs are attempting to place themselves at the
673 // top of the scrollable area. The child div is attempting to place itself at
674 // an offset of 25 pixels to the top of the scrollable area. The result of
675 // this sticky offset calculation is quite simple, but internally the child
676 // offset has to offset both its sticky box constraint rect and its containing
677 // block constraint rect.
678 EXPECT_EQ(LayoutSize(0, 50), outmostSticky->stickyPositionOffset());
679 EXPECT_EQ(LayoutSize(0, 0), middleSticky->stickyPositionOffset());
680 EXPECT_EQ(LayoutSize(0, 25), innerSticky->stickyPositionOffset());
681 }
682
683 // Verifies that the calculated position:sticky offsets are correct in the case
684 // of tables. Tables are special as the containing block for all child elements
685 // is always the root level <table>.
686 TEST_F(LayoutBoxModelObjectTest, StickyPositionNestedStickyInTable) {
687 setBodyInnerHTML(
688 "<style>#scroller { height: 100px; width: 100px; overflow-y: auto; }"
689 "#prePadding { height: 50px; }"
690 "#stickyDiv { position: sticky; top: 0; height: 200px; }"
691 "#table { border-collapse: collapse; }"
692 "#stickyThead { position: sticky; top: 0; }"
693 "#stickyTr { position: sticky; top: 0; }"
694 "#stickyTh { position: sticky; top: 0; }"
695 "#postPadding { height: 200px; }</style>"
696 "<div id='scroller'><div id='prePadding'></div><div id='stickyDiv'>"
697 "<table id='table'><thead id='stickyThead'><tr id='stickyTr'>"
698 "<th id='stickyTh'>Header</th></tr></thread><tbody><tr><td>0</td></tr>"
699 "<tr><td>1</td></tr><tr><td>2</td></tr><tr><td>3</td></tr></tbody>"
700 "</table></div><div id='postPadding'></div></div>");
701
702 // Make sure that the constraints are cached before scrolling or they may be
703 // calculated post-scroll and thus not require offset correction.
704
705 LayoutBoxModelObject* stickyDiv =
706 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyDiv"));
707 stickyDiv->updateStickyPositionConstraints();
708
709 LayoutBoxModelObject* stickyThead =
710 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyThead"));
711 stickyThead->updateStickyPositionConstraints();
712
713 LayoutBoxModelObject* stickyTr =
714 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyTr"));
715 stickyTr->updateStickyPositionConstraints();
716
717 LayoutBoxModelObject* stickyTh =
718 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyTh"));
719 stickyTh->updateStickyPositionConstraints();
720
721 // Scroll the page down.
722 LayoutBoxModelObject* scroller =
723 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
724 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
725 scrollableArea->scrollToAbsolutePosition(
726 FloatPoint(scrollableArea->scrollPosition().x(), 150));
727 ASSERT_EQ(150.0, scrollableArea->scrollPosition().y());
728
729 // All sticky elements are attempting to stick to the top of the scrollable
730 // area. For the root sticky div, this requires an offset. All the other
731 // descendant sticky elements are positioned relatively so don't need offset.
732 EXPECT_EQ(LayoutSize(0, 100), stickyDiv->stickyPositionOffset());
733 EXPECT_EQ(LayoutSize(0, 0), stickyThead->stickyPositionOffset());
734 EXPECT_EQ(LayoutSize(0, 0), stickyTr->stickyPositionOffset());
735 EXPECT_EQ(LayoutSize(0, 0), stickyTh->stickyPositionOffset());
736
737 // If we now scroll to the point where the overall sticky div starts to move,
738 // the table headers should continue to stick to the top of the scrollable
739 // area until they run out of <table> space to move in.
740
741 scrollableArea->scrollToAbsolutePosition(
742 FloatPoint(scrollableArea->scrollPosition().x(), 275));
743 ASSERT_EQ(275.0, scrollableArea->scrollPosition().y());
744
745 // TODO(smcgruer): The value of 12 is due to http://crbug.com/673538 . Fix
746 // this test after the fix for that bug lands.
747 EXPECT_EQ(LayoutSize(0, 200), stickyDiv->stickyPositionOffset());
748 EXPECT_EQ(LayoutSize(0, 12), stickyThead->stickyPositionOffset());
749 EXPECT_EQ(LayoutSize(0, 0), stickyTr->stickyPositionOffset());
750 EXPECT_EQ(LayoutSize(0, 0), stickyTh->stickyPositionOffset());
751
752 // Finally, if we scroll so that the table is off the top of the page, the
753 // sticky header should go off the top with it.
754 scrollableArea->scrollToAbsolutePosition(
755 FloatPoint(scrollableArea->scrollPosition().x(), 350));
756 ASSERT_EQ(350.0, scrollableArea->scrollPosition().y());
757
758 // TODO(smcgruer): The value of 12 is due to http://crbug.com/673538 . Fix
759 // this test after the fix for that bug lands.
760 EXPECT_EQ(LayoutSize(0, 200), stickyDiv->stickyPositionOffset());
761 EXPECT_EQ(LayoutSize(0, 12), stickyThead->stickyPositionOffset());
762 EXPECT_EQ(LayoutSize(0, 0), stickyTr->stickyPositionOffset());
763 EXPECT_EQ(LayoutSize(0, 0), stickyTh->stickyPositionOffset());
764 }
765
766 // Verifies that the calculated position:sticky offsets are correct in the case
767 // where a particular position:sticky element is used both as a sticky-box
768 // shifting ancestor as well as a containing-block shifting ancestor.
769 //
770 // This is a rare case that can be replicated by nesting tables so that a sticky
771 // cell contains another table that has sticky elements. See the HTML below.
772 TEST_F(LayoutBoxModelObjectTest, StickyPositionComplexTableNesting) {
773 setBodyInnerHTML(
774 "<style>#scroller { height: 100px; width: 100px; overflow-y: auto; }"
775 "#prePadding { height: 50px; }"
776 "#outerStickyThead { position: sticky; top: 0; }"
777 "#outerStickyTr { position: sticky; top: 0; }"
778 "#outerStickyTh { position: sticky; top: 0; }"
779 "#innerStickyThead { position: sticky; top: 25px; }"
780 "#innerStickyTr { position: sticky; top: 25px; }"
781 "#innerStickyTh { position: sticky; top: 25px; }"
782 "#postPadding { height: 200px; }"
783 "table { border-collapse: collapse; }</style>"
784 "<div id='scroller'><div id='prePadding'></div>"
785 "<table><thead id='outerStickyThead'><tr id='outerStickyTr'>"
786 "<th id='outerStickyTh'><table><thead id='innerStickyThead'>"
787 "<tr id='innerStickyTr'><th id='innerStickyTh'>Nested</th></tr></thead>"
788 "</table></th></tr></thread><tbody><tr><td>0</td></tr><tr><td>1</td></tr>"
789 "<tr><td>2</td></tr><tr><td>3</td></tr></tbody></table>"
790 "<div id='postPadding'></div></div>");
791
792 // Make sure that the constraints are cached before scrolling or they may be
793 // calculated post-scroll and thus not require offset correction.
794
795 LayoutBoxModelObject* outerStickyThead =
796 toLayoutBoxModelObject(getLayoutObjectByElementId("outerStickyThead"));
797 outerStickyThead->updateStickyPositionConstraints();
798
799 LayoutBoxModelObject* outerStickyTr =
800 toLayoutBoxModelObject(getLayoutObjectByElementId("outerStickyTr"));
801 outerStickyTr->updateStickyPositionConstraints();
802
803 LayoutBoxModelObject* outerStickyTh =
804 toLayoutBoxModelObject(getLayoutObjectByElementId("outerStickyTh"));
805 outerStickyTh->updateStickyPositionConstraints();
806
807 LayoutBoxModelObject* innerStickyThead =
808 toLayoutBoxModelObject(getLayoutObjectByElementId("innerStickyThead"));
809 innerStickyThead->updateStickyPositionConstraints();
810
811 LayoutBoxModelObject* innerStickyTr =
812 toLayoutBoxModelObject(getLayoutObjectByElementId("innerStickyTr"));
813 innerStickyTr->updateStickyPositionConstraints();
814
815 LayoutBoxModelObject* innerStickyTh =
816 toLayoutBoxModelObject(getLayoutObjectByElementId("innerStickyTh"));
817 innerStickyTh->updateStickyPositionConstraints();
818
819 // Scroll the page down.
820 LayoutBoxModelObject* scroller =
821 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
822 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
823 scrollableArea->scrollToAbsolutePosition(
824 FloatPoint(scrollableArea->scrollPosition().x(), 150));
825 ASSERT_EQ(150.0, scrollableArea->scrollPosition().y());
826
827 // TODO(smcgruer): The value of 12 is due to http://crbug.com/673538 . Fix
828 // this test after the fix for that bug lands.
829 EXPECT_EQ(LayoutSize(0, 12), outerStickyThead->stickyPositionOffset());
830 EXPECT_EQ(LayoutSize(0, 0), outerStickyTr->stickyPositionOffset());
831 EXPECT_EQ(LayoutSize(0, 0), outerStickyTh->stickyPositionOffset());
832 EXPECT_EQ(LayoutSize(0, 0), innerStickyThead->stickyPositionOffset());
833 EXPECT_EQ(LayoutSize(0, 0), innerStickyTr->stickyPositionOffset());
834 EXPECT_EQ(LayoutSize(0, 0), innerStickyTh->stickyPositionOffset());
835
836 // TODO(smcgruer): More scrolling.
837 }
838
252 } // namespace blink 839 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698