Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 321 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 332 // computing the sticky constraints. Any such ancestor is the first sticky | 332 // computing the sticky constraints. Any such ancestor is the first sticky |
| 333 // element between you and your containing block (exclusive). | 333 // element between you and your containing block (exclusive). |
| 334 // | 334 // |
| 335 // In most cases, this pointer should be null since your parent is normally your | 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 | 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 | 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. | 338 // cells can be sticky in CSS2.1, but we can test the former. |
| 339 TEST_F(LayoutBoxModelObjectTest, | 339 TEST_F(LayoutBoxModelObjectTest, |
| 340 StickyPositionFindsCorrectStickyBoxShiftingAncestor) { | 340 StickyPositionFindsCorrectStickyBoxShiftingAncestor) { |
| 341 setBodyInnerHTML( | 341 setBodyInnerHTML( |
| 342 "<style>#stickyOuterDiv { position: sticky; }" | 342 "<style>#stickyOuterDiv { position: sticky; top: 0;}" |
| 343 "#stickyOuterInline { position: sticky; display: inline; }" | 343 "#stickyOuterInline { position: sticky; top: 0; display: inline; }" |
| 344 "#stickyInnerInline { position: sticky; display: inline; }</style>" | 344 "#unanchoredSticky { position: sticky; display: inline; }" |
| 345 "<div id='stickyOuterDiv'><div id='stickyOuterInline'>" | 345 "#stickyInnerInline { position: sticky; top: 0; display: inline; " |
| 346 "<div id='stickyInnerInline'></div></div></div>"); | 346 "}</style>" |
| 347 "<div id='stickyOuterDiv'>" | |
| 348 " <div id='stickyOuterInline'>" | |
| 349 " <div id='unanchoredSticky'>" | |
| 350 " <div id='stickyInnerInline'></div>" | |
| 351 " </div>" | |
| 352 " </div>" | |
| 353 "</div>"); | |
| 347 | 354 |
| 348 LayoutBoxModelObject* stickyOuterDiv = | 355 LayoutBoxModelObject* stickyOuterDiv = |
| 349 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyOuterDiv")); | 356 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyOuterDiv")); |
| 350 LayoutBoxModelObject* stickyOuterInline = | 357 LayoutBoxModelObject* stickyOuterInline = |
| 351 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyOuterInline")); | 358 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyOuterInline")); |
| 352 LayoutBoxModelObject* stickyInnerInline = | 359 LayoutBoxModelObject* stickyInnerInline = |
| 353 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyInnerInline")); | 360 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyInnerInline")); |
| 354 | 361 |
| 355 PaintLayerScrollableArea* scrollableArea = | 362 PaintLayerScrollableArea* scrollableArea = |
| 356 stickyOuterDiv->layer()->ancestorOverflowLayer()->getScrollableArea(); | 363 stickyOuterDiv->layer()->ancestorOverflowLayer()->getScrollableArea(); |
| 357 ASSERT_TRUE(scrollableArea); | 364 ASSERT_TRUE(scrollableArea); |
| 358 StickyConstraintsMap constraintsMap = scrollableArea->stickyConstraintsMap(); | 365 StickyConstraintsMap constraintsMap = scrollableArea->stickyConstraintsMap(); |
| 359 | 366 |
| 360 ASSERT_TRUE(constraintsMap.contains(stickyOuterDiv->layer())); | 367 ASSERT_TRUE(constraintsMap.contains(stickyOuterDiv->layer())); |
| 361 ASSERT_TRUE(constraintsMap.contains(stickyOuterInline->layer())); | 368 ASSERT_TRUE(constraintsMap.contains(stickyOuterInline->layer())); |
| 362 ASSERT_TRUE(constraintsMap.contains(stickyInnerInline->layer())); | 369 ASSERT_TRUE(constraintsMap.contains(stickyInnerInline->layer())); |
|
chrishtr
2017/03/27 16:28:07
ASSERT_FALSE for unanchoredSticky?
flackr
2017/04/07 18:15:45
Done.
| |
| 363 | 370 |
| 364 // The outer block element trivially has no sticky-box shifting ancestor. | 371 // The outer block element trivially has no sticky-box shifting ancestor. |
| 365 EXPECT_FALSE(constraintsMap.at(stickyOuterDiv->layer()) | 372 EXPECT_FALSE(constraintsMap.at(stickyOuterDiv->layer()) |
| 366 .nearestStickyBoxShiftingStickyBox()); | 373 .nearestStickyBoxShiftingStickyBox()); |
| 367 | 374 |
| 368 // Neither does the outer inline element, as its parent element is also its | 375 // Neither does the outer inline element, as its parent element is also its |
| 369 // containing block. | 376 // containing block. |
| 370 EXPECT_FALSE(constraintsMap.at(stickyOuterInline->layer()) | 377 EXPECT_FALSE(constraintsMap.at(stickyOuterInline->layer()) |
| 371 .nearestStickyBoxShiftingStickyBox()); | 378 .nearestStickyBoxShiftingStickyBox()); |
| 372 | 379 |
| 373 // However the inner inline element does have a sticky-box shifting ancestor, | 380 // 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. | 381 // as its containing block is the ancestor block element, not its parent. |
| 375 EXPECT_EQ(stickyOuterInline, | 382 EXPECT_EQ(stickyOuterInline, |
| 376 constraintsMap.at(stickyInnerInline->layer()) | 383 constraintsMap.at(stickyInnerInline->layer()) |
| 377 .nearestStickyBoxShiftingStickyBox()); | 384 .nearestStickyBoxShiftingStickyBox()); |
| 378 } | 385 } |
| 379 | 386 |
| 380 // Verifies that the correct containing-block shifting ancestor is found when | 387 // Verifies that the correct containing-block shifting ancestor is found when |
| 381 // computing the sticky constraints. Any such ancestor is the first sticky | 388 // computing the sticky constraints. Any such ancestor is the first sticky |
| 382 // element between your containing block (inclusive) and your ancestor overflow | 389 // element between your containing block (inclusive) and your ancestor overflow |
| 383 // layer (exclusive). | 390 // layer (exclusive). |
| 384 TEST_F(LayoutBoxModelObjectTest, | 391 TEST_F(LayoutBoxModelObjectTest, |
| 385 StickyPositionFindsCorrectContainingBlockShiftingAncestor) { | 392 StickyPositionFindsCorrectContainingBlockShiftingAncestor) { |
| 386 // We make the scroller itself sticky in order to check that elements do not | 393 // We make the scroller itself sticky in order to check that elements do not |
| 387 // detect it as their containing-block shifting ancestor. | 394 // detect it as their containing-block shifting ancestor. |
| 388 setBodyInnerHTML( | 395 setBodyInnerHTML( |
| 389 "<style>#scroller { overflow-y: scroll; position: sticky; }" | 396 "<style>#scroller { overflow-y: scroll; position: sticky; top: 0;}" |
| 390 "#stickyParent { position: sticky; }" | 397 "#stickyParent { position: sticky; top: 0;}" |
| 391 "#stickyChild { position: sticky; }" | 398 "#stickyChild { position: sticky; top: 0;}" |
| 392 "#stickyNestedChild { position: sticky; }</style>" | 399 "#unanchoredSticky { position: sticky; }" |
| 393 "<div id='scroller'><div id='stickyParent'><div id='stickyChild'></div>" | 400 "#stickyNestedChild { position: sticky; top: 0;}</style>" |
| 394 "<div><div id='stickyNestedChild'></div></div></div></div>"); | 401 "<div id='scroller'>" |
| 402 " <div id='stickyParent'>" | |
| 403 " <div id='unanchoredSticky'>" | |
| 404 " <div id='stickyChild'></div>" | |
| 405 " <div><div id='stickyNestedChild'></div></div>" | |
| 406 " </div>" | |
| 407 " </div>" | |
| 408 "</div>"); | |
| 395 | 409 |
| 396 LayoutBoxModelObject* scroller = | 410 LayoutBoxModelObject* scroller = |
| 397 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller")); | 411 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller")); |
| 398 LayoutBoxModelObject* stickyParent = | 412 LayoutBoxModelObject* stickyParent = |
| 399 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent")); | 413 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent")); |
| 400 LayoutBoxModelObject* stickyChild = | 414 LayoutBoxModelObject* stickyChild = |
| 401 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyChild")); | 415 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyChild")); |
| 402 LayoutBoxModelObject* stickyNestedChild = | 416 LayoutBoxModelObject* stickyNestedChild = |
| 403 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyNestedChild")); | 417 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyNestedChild")); |
| 404 | 418 |
| 405 PaintLayerScrollableArea* scrollableArea = | 419 PaintLayerScrollableArea* scrollableArea = |
| 406 scroller->layer()->getScrollableArea(); | 420 scroller->layer()->getScrollableArea(); |
| 407 ASSERT_TRUE(scrollableArea); | 421 ASSERT_TRUE(scrollableArea); |
| 408 StickyConstraintsMap constraintsMap = scrollableArea->stickyConstraintsMap(); | 422 StickyConstraintsMap constraintsMap = scrollableArea->stickyConstraintsMap(); |
| 409 | 423 |
| 410 ASSERT_FALSE(constraintsMap.contains(scroller->layer())); | 424 ASSERT_FALSE(constraintsMap.contains(scroller->layer())); |
| 411 ASSERT_TRUE(constraintsMap.contains(stickyParent->layer())); | 425 ASSERT_TRUE(constraintsMap.contains(stickyParent->layer())); |
| 412 ASSERT_TRUE(constraintsMap.contains(stickyChild->layer())); | 426 ASSERT_TRUE(constraintsMap.contains(stickyChild->layer())); |
| 413 ASSERT_TRUE(constraintsMap.contains(stickyNestedChild->layer())); | 427 ASSERT_TRUE(constraintsMap.contains(stickyNestedChild->layer())); |
| 414 | 428 |
| 415 // The outer <div> should not detect the scroller as its containing-block | 429 // The outer <div> should not detect the scroller as its containing-block |
| 416 // shifting ancestor. | 430 // shifting ancestor. |
| 417 EXPECT_FALSE(constraintsMap.at(stickyParent->layer()) | 431 EXPECT_FALSE(constraintsMap.at(stickyParent->layer()) |
| 418 .nearestStickyBoxShiftingContainingBlock()); | 432 .nearestStickyBoxShiftingContainingBlock()); |
| 419 | 433 |
| 420 // Both inner children should detect the parent <div> as their | 434 // Both inner children should detect the parent <div> as their |
| 421 // containing-block shifting ancestor. | 435 // containing-block shifting ancestor. They skip past unanchored sticky |
| 436 // because it will never have a non-zero offset. | |
| 422 EXPECT_EQ(stickyParent, | 437 EXPECT_EQ(stickyParent, |
| 423 constraintsMap.at(stickyChild->layer()) | 438 constraintsMap.at(stickyChild->layer()) |
| 424 .nearestStickyBoxShiftingContainingBlock()); | 439 .nearestStickyBoxShiftingContainingBlock()); |
| 425 EXPECT_EQ(stickyParent, | 440 EXPECT_EQ(stickyParent, |
| 426 constraintsMap.at(stickyNestedChild->layer()) | 441 constraintsMap.at(stickyNestedChild->layer()) |
| 427 .nearestStickyBoxShiftingContainingBlock()); | 442 .nearestStickyBoxShiftingContainingBlock()); |
| 428 } | 443 } |
| 429 | 444 |
| 430 // Verifies that the correct containing-block shifting ancestor is found when | 445 // Verifies that the correct containing-block shifting ancestor is found when |
| 431 // computing the sticky constraints, in the case where the overflow ancestor is | 446 // 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 | 447 // 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 | 448 // 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. | 449 // not make a difference for containing-block shifting ancestor calculations. |
| 435 TEST_F(LayoutBoxModelObjectTest, | 450 TEST_F(LayoutBoxModelObjectTest, |
| 436 StickyPositionFindsCorrectContainingBlockShiftingAncestorRoot) { | 451 StickyPositionFindsCorrectContainingBlockShiftingAncestorRoot) { |
| 437 setBodyInnerHTML( | 452 setBodyInnerHTML( |
| 438 "<style>#stickyParent { position: sticky; }" | 453 "<style>#stickyParent { position: sticky; top: 0;}" |
| 439 "#stickyGrandchild { position: sticky; }</style>" | 454 "#stickyGrandchild { position: sticky; top: 0;}</style>" |
| 440 "<div id='stickyParent'><div><div id='stickyGrandchild'></div></div>" | 455 "<div id='stickyParent'><div><div id='stickyGrandchild'></div></div>" |
| 441 "</div>"); | 456 "</div>"); |
| 442 | 457 |
| 443 LayoutBoxModelObject* stickyParent = | 458 LayoutBoxModelObject* stickyParent = |
| 444 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent")); | 459 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent")); |
| 445 LayoutBoxModelObject* stickyGrandchild = | 460 LayoutBoxModelObject* stickyGrandchild = |
| 446 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyGrandchild")); | 461 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyGrandchild")); |
| 447 | 462 |
| 448 PaintLayerScrollableArea* scrollableArea = | 463 PaintLayerScrollableArea* scrollableArea = |
| 449 stickyParent->layer()->ancestorOverflowLayer()->getScrollableArea(); | 464 stickyParent->layer()->ancestorOverflowLayer()->getScrollableArea(); |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 461 } | 476 } |
| 462 | 477 |
| 463 // Verifies that the correct containing-block shifting ancestor is found when | 478 // Verifies that the correct containing-block shifting ancestor is found when |
| 464 // computing the sticky constraints, in the case of tables. Tables are unusual | 479 // 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 | 480 // 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. | 481 // we have to skip over elements to find the correct ancestor. |
| 467 TEST_F(LayoutBoxModelObjectTest, | 482 TEST_F(LayoutBoxModelObjectTest, |
| 468 StickyPositionFindsCorrectContainingBlockShiftingAncestorTable) { | 483 StickyPositionFindsCorrectContainingBlockShiftingAncestorTable) { |
| 469 setBodyInnerHTML( | 484 setBodyInnerHTML( |
| 470 "<style>#scroller { overflow-y: scroll; }" | 485 "<style>#scroller { overflow-y: scroll; }" |
| 471 "#stickyOuter { position: sticky; }" | 486 "#stickyOuter { position: sticky; top: 0;}" |
| 472 "#stickyTh { position: sticky; }</style>" | 487 "#stickyTh { position: sticky; top: 0;}</style>" |
| 473 "<div id='scroller'><div id='stickyOuter'><table><thead><tr>" | 488 "<div id='scroller'><div id='stickyOuter'><table><thead><tr>" |
| 474 "<th id='stickyTh'></th></tr></thead></table></div></div>"); | 489 "<th id='stickyTh'></th></tr></thead></table></div></div>"); |
| 475 | 490 |
| 476 LayoutBoxModelObject* scroller = | 491 LayoutBoxModelObject* scroller = |
| 477 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller")); | 492 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller")); |
| 478 LayoutBoxModelObject* stickyOuter = | 493 LayoutBoxModelObject* stickyOuter = |
| 479 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyOuter")); | 494 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyOuter")); |
| 480 LayoutBoxModelObject* stickyTh = | 495 LayoutBoxModelObject* stickyTh = |
| 481 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyTh")); | 496 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyTh")); |
| 482 | 497 |
| (...skipping 353 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 836 EXPECT_EQ(LayoutSize(0, 25), innerStickyTh->stickyPositionOffset()); | 851 EXPECT_EQ(LayoutSize(0, 25), innerStickyTh->stickyPositionOffset()); |
| 837 } | 852 } |
| 838 | 853 |
| 839 // Verifies that the calculated position:sticky offsets are correct in the case | 854 // Verifies that the calculated position:sticky offsets are correct in the case |
| 840 // of nested inline elements. | 855 // of nested inline elements. |
| 841 TEST_F(LayoutBoxModelObjectTest, StickyPositionNestedInlineElements) { | 856 TEST_F(LayoutBoxModelObjectTest, StickyPositionNestedInlineElements) { |
| 842 setBodyInnerHTML( | 857 setBodyInnerHTML( |
| 843 "<style>#scroller { width: 100px; height: 100px; overflow-y: scroll; }" | 858 "<style>#scroller { width: 100px; height: 100px; overflow-y: scroll; }" |
| 844 "#paddingBefore { height: 50px; }" | 859 "#paddingBefore { height: 50px; }" |
| 845 "#outerInline { display: inline; position: sticky; top: 0; }" | 860 "#outerInline { display: inline; position: sticky; top: 0; }" |
| 861 "#unanchoredSticky { position: sticky; display: inline; }" | |
| 846 "#innerInline { display: inline; position: sticky; top: 25px; }" | 862 "#innerInline { display: inline; position: sticky; top: 25px; }" |
| 847 "#paddingAfter { height: 200px; }</style>" | 863 "#paddingAfter { height: 200px; }</style>" |
| 848 "<div id='scroller'><div id='paddingBefore'></div><div id='outerInline'>" | 864 "<div id='scroller'>" |
| 849 "<div id='innerInline'></div></div><div id='paddingAfter'></div></div>"); | 865 " <div id='paddingBefore'></div>" |
| 866 " <div id='outerInline'>" | |
| 867 " <div id='unanchoredSticky'>" | |
| 868 " <div id='innerInline'></div>" | |
| 869 " </div>" | |
| 870 " </div>" | |
| 871 " <div id='paddingAfter'></div>" | |
| 872 "</div>"); | |
| 850 | 873 |
| 851 LayoutBoxModelObject* outerInline = | 874 LayoutBoxModelObject* outerInline = |
| 852 toLayoutBoxModelObject(getLayoutObjectByElementId("outerInline")); | 875 toLayoutBoxModelObject(getLayoutObjectByElementId("outerInline")); |
| 853 LayoutBoxModelObject* innerInline = | 876 LayoutBoxModelObject* innerInline = |
| 854 toLayoutBoxModelObject(getLayoutObjectByElementId("innerInline")); | 877 toLayoutBoxModelObject(getLayoutObjectByElementId("innerInline")); |
| 855 | 878 |
| 856 // Scroll the page down. | 879 // Scroll the page down. |
| 857 LayoutBoxModelObject* scroller = | 880 LayoutBoxModelObject* scroller = |
| 858 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller")); | 881 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller")); |
| 859 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea(); | 882 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea(); |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 905 | 928 |
| 906 // TODO(smcgruer): Until http://crbug.com/686164 is fixed, we need to update | 929 // TODO(smcgruer): Until http://crbug.com/686164 is fixed, we need to update |
| 907 // the constraints here before calculations will be correct. | 930 // the constraints here before calculations will be correct. |
| 908 innerSticky->updateStickyPositionConstraints(); | 931 innerSticky->updateStickyPositionConstraints(); |
| 909 | 932 |
| 910 EXPECT_EQ(LayoutSize(0, 100), outerSticky->stickyPositionOffset()); | 933 EXPECT_EQ(LayoutSize(0, 100), outerSticky->stickyPositionOffset()); |
| 911 EXPECT_EQ(LayoutSize(0, 25), innerSticky->stickyPositionOffset()); | 934 EXPECT_EQ(LayoutSize(0, 25), innerSticky->stickyPositionOffset()); |
| 912 } | 935 } |
| 913 | 936 |
| 914 } // namespace blink | 937 } // namespace blink |
| OLD | NEW |