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

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

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

Powered by Google App Engine
This is Rietveld 408576698