| 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 324 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 335 // computing the sticky constraints. Any such ancestor is the first sticky | 335 // computing the sticky constraints. Any such ancestor is the first sticky |
| 336 // element between you and your containing block (exclusive). | 336 // element between you and your containing block (exclusive). |
| 337 // | 337 // |
| 338 // In most cases, this pointer should be null since your parent is normally your | 338 // In most cases, this pointer should be null since your parent is normally your |
| 339 // containing block. However there are cases where this is not true, including | 339 // containing block. However there are cases where this is not true, including |
| 340 // inline blocks and tables. The latter is currently irrelevant since only table | 340 // inline blocks and tables. The latter is currently irrelevant since only table |
| 341 // cells can be sticky in CSS2.1, but we can test the former. | 341 // cells can be sticky in CSS2.1, but we can test the former. |
| 342 TEST_F(LayoutBoxModelObjectTest, | 342 TEST_F(LayoutBoxModelObjectTest, |
| 343 StickyPositionFindsCorrectStickyBoxShiftingAncestor) { | 343 StickyPositionFindsCorrectStickyBoxShiftingAncestor) { |
| 344 SetBodyInnerHTML( | 344 SetBodyInnerHTML( |
| 345 "<style>#stickyOuterDiv { position: sticky; top: 0;}" | 345 "<style>#stickyOuterDiv { position: sticky; }" |
| 346 "#stickyOuterInline { position: sticky; top: 0; display: inline; }" | 346 "#stickyOuterInline { position: sticky; display: inline; }" |
| 347 "#unanchoredSticky { position: sticky; display: inline; }" | 347 "#stickyInnerInline { position: sticky; display: inline; }" |
| 348 ".inline { display: inline; }" | 348 ".inline { display: inline; }</style>" |
| 349 "#stickyInnerInline { position: sticky; top: 0; display: inline; " | |
| 350 "}</style>" | |
| 351 "<div id='stickyOuterDiv'>" | 349 "<div id='stickyOuterDiv'>" |
| 352 " <div id='stickyOuterInline'>" | 350 " <div id='stickyOuterInline'>" |
| 353 " <div id='unanchoredSticky'>" | 351 " <div class='inline'>" |
| 354 " <div class='inline'>" | 352 " <div id='stickyInnerInline'></div>" |
| 355 " <div id='stickyInnerInline'></div>" | |
| 356 " </div>" | |
| 357 " </div>" | 353 " </div>" |
| 358 " </div>" | 354 " </div>" |
| 359 "</div>"); | 355 "</div>"); |
| 360 | 356 |
| 361 LayoutBoxModelObject* sticky_outer_div = | 357 LayoutBoxModelObject* sticky_outer_div = |
| 362 ToLayoutBoxModelObject(GetLayoutObjectByElementId("stickyOuterDiv")); | 358 ToLayoutBoxModelObject(GetLayoutObjectByElementId("stickyOuterDiv")); |
| 363 LayoutBoxModelObject* sticky_outer_inline = | 359 LayoutBoxModelObject* sticky_outer_inline = |
| 364 ToLayoutBoxModelObject(GetLayoutObjectByElementId("stickyOuterInline")); | 360 ToLayoutBoxModelObject(GetLayoutObjectByElementId("stickyOuterInline")); |
| 365 LayoutBoxModelObject* unanchored_sticky = | |
| 366 ToLayoutBoxModelObject(GetLayoutObjectByElementId("unanchoredSticky")); | |
| 367 LayoutBoxModelObject* sticky_inner_inline = | 361 LayoutBoxModelObject* sticky_inner_inline = |
| 368 ToLayoutBoxModelObject(GetLayoutObjectByElementId("stickyInnerInline")); | 362 ToLayoutBoxModelObject(GetLayoutObjectByElementId("stickyInnerInline")); |
| 369 | 363 |
| 370 PaintLayerScrollableArea* scrollable_area = | 364 PaintLayerScrollableArea* scrollable_area = |
| 371 sticky_outer_div->Layer()->AncestorOverflowLayer()->GetScrollableArea(); | 365 sticky_outer_div->Layer()->AncestorOverflowLayer()->GetScrollableArea(); |
| 372 ASSERT_TRUE(scrollable_area); | 366 ASSERT_TRUE(scrollable_area); |
| 373 StickyConstraintsMap constraints_map = | 367 StickyConstraintsMap constraints_map = |
| 374 scrollable_area->GetStickyConstraintsMap(); | 368 scrollable_area->GetStickyConstraintsMap(); |
| 375 | 369 |
| 376 ASSERT_TRUE(constraints_map.Contains(sticky_outer_div->Layer())); | 370 ASSERT_TRUE(constraints_map.Contains(sticky_outer_div->Layer())); |
| 377 ASSERT_TRUE(constraints_map.Contains(sticky_outer_inline->Layer())); | 371 ASSERT_TRUE(constraints_map.Contains(sticky_outer_inline->Layer())); |
| 378 ASSERT_FALSE(constraints_map.Contains(unanchored_sticky->Layer())); | |
| 379 ASSERT_TRUE(constraints_map.Contains(sticky_inner_inline->Layer())); | 372 ASSERT_TRUE(constraints_map.Contains(sticky_inner_inline->Layer())); |
| 380 | 373 |
| 381 // The outer block element trivially has no sticky-box shifting ancestor. | 374 // The outer block element trivially has no sticky-box shifting ancestor. |
| 382 EXPECT_FALSE(constraints_map.at(sticky_outer_div->Layer()) | 375 EXPECT_FALSE(constraints_map.at(sticky_outer_div->Layer()) |
| 383 .NearestStickyBoxShiftingStickyBox()); | 376 .NearestStickyBoxShiftingStickyBox()); |
| 384 | 377 |
| 385 // Neither does the outer inline element, as its parent element is also its | 378 // Neither does the outer inline element, as its parent element is also its |
| 386 // containing block. | 379 // containing block. |
| 387 EXPECT_FALSE(constraints_map.at(sticky_outer_inline->Layer()) | 380 EXPECT_FALSE(constraints_map.at(sticky_outer_inline->Layer()) |
| 388 .NearestStickyBoxShiftingStickyBox()); | 381 .NearestStickyBoxShiftingStickyBox()); |
| 389 | 382 |
| 390 // However the inner inline element does have a sticky-box shifting ancestor, | 383 // However the inner inline element does have a sticky-box shifting ancestor, |
| 391 // as its containing block is the ancestor block element, above its ancestor | 384 // as its containing block is the ancestor block element, above its ancestor |
| 392 // sticky element. | 385 // sticky element. |
| 393 EXPECT_EQ(sticky_outer_inline, | 386 EXPECT_EQ(sticky_outer_inline, |
| 394 constraints_map.at(sticky_inner_inline->Layer()) | 387 constraints_map.at(sticky_inner_inline->Layer()) |
| 395 .NearestStickyBoxShiftingStickyBox()); | 388 .NearestStickyBoxShiftingStickyBox()); |
| 396 } | 389 } |
| 397 | 390 |
| 398 // Verifies that the correct containing-block shifting ancestor is found when | 391 // Verifies that the correct containing-block shifting ancestor is found when |
| 399 // computing the sticky constraints. Any such ancestor is the first sticky | 392 // computing the sticky constraints. Any such ancestor is the first sticky |
| 400 // element between your containing block (inclusive) and your ancestor overflow | 393 // element between your containing block (inclusive) and your ancestor overflow |
| 401 // layer (exclusive). | 394 // layer (exclusive). |
| 402 TEST_F(LayoutBoxModelObjectTest, | 395 TEST_F(LayoutBoxModelObjectTest, |
| 403 StickyPositionFindsCorrectContainingBlockShiftingAncestor) { | 396 StickyPositionFindsCorrectContainingBlockShiftingAncestor) { |
| 404 // We make the scroller itself sticky in order to check that elements do not | 397 // We make the scroller itself sticky in order to check that elements do not |
| 405 // detect it as their containing-block shifting ancestor. | 398 // detect it as their containing-block shifting ancestor. |
| 406 SetBodyInnerHTML( | 399 SetBodyInnerHTML( |
| 407 "<style>#scroller { overflow-y: scroll; position: sticky; top: 0;}" | 400 "<style>#scroller { overflow-y: scroll; position: sticky; }" |
| 408 "#stickyParent { position: sticky; top: 0;}" | 401 "#stickyParent { position: sticky; }" |
| 409 "#stickyChild { position: sticky; top: 0;}" | 402 "#stickyChild { position: sticky; }" |
| 410 "#unanchoredSticky { position: sticky; }" | 403 "#stickyNestedChild { position: sticky; }</style>" |
| 411 "#stickyNestedChild { position: sticky; top: 0;}</style>" | 404 "<div id='scroller'><div id='stickyParent'><div id='stickyChild'></div>" |
| 412 "<div id='scroller'>" | 405 "<div><div id='stickyNestedChild'></div></div></div></div>"); |
| 413 " <div id='stickyParent'>" | |
| 414 " <div id='unanchoredSticky'>" | |
| 415 " <div id='stickyChild'></div>" | |
| 416 " <div><div id='stickyNestedChild'></div></div>" | |
| 417 " </div>" | |
| 418 " </div>" | |
| 419 "</div>"); | |
| 420 | 406 |
| 421 LayoutBoxModelObject* scroller = | 407 LayoutBoxModelObject* scroller = |
| 422 ToLayoutBoxModelObject(GetLayoutObjectByElementId("scroller")); | 408 ToLayoutBoxModelObject(GetLayoutObjectByElementId("scroller")); |
| 423 LayoutBoxModelObject* sticky_parent = | 409 LayoutBoxModelObject* sticky_parent = |
| 424 ToLayoutBoxModelObject(GetLayoutObjectByElementId("stickyParent")); | 410 ToLayoutBoxModelObject(GetLayoutObjectByElementId("stickyParent")); |
| 425 LayoutBoxModelObject* sticky_child = | 411 LayoutBoxModelObject* sticky_child = |
| 426 ToLayoutBoxModelObject(GetLayoutObjectByElementId("stickyChild")); | 412 ToLayoutBoxModelObject(GetLayoutObjectByElementId("stickyChild")); |
| 427 LayoutBoxModelObject* sticky_nested_child = | 413 LayoutBoxModelObject* sticky_nested_child = |
| 428 ToLayoutBoxModelObject(GetLayoutObjectByElementId("stickyNestedChild")); | 414 ToLayoutBoxModelObject(GetLayoutObjectByElementId("stickyNestedChild")); |
| 429 | 415 |
| 430 PaintLayerScrollableArea* scrollable_area = | 416 PaintLayerScrollableArea* scrollable_area = |
| 431 scroller->Layer()->GetScrollableArea(); | 417 scroller->Layer()->GetScrollableArea(); |
| 432 ASSERT_TRUE(scrollable_area); | 418 ASSERT_TRUE(scrollable_area); |
| 433 StickyConstraintsMap constraints_map = | 419 StickyConstraintsMap constraints_map = |
| 434 scrollable_area->GetStickyConstraintsMap(); | 420 scrollable_area->GetStickyConstraintsMap(); |
| 435 | 421 |
| 436 ASSERT_FALSE(constraints_map.Contains(scroller->Layer())); | 422 ASSERT_FALSE(constraints_map.Contains(scroller->Layer())); |
| 437 ASSERT_TRUE(constraints_map.Contains(sticky_parent->Layer())); | 423 ASSERT_TRUE(constraints_map.Contains(sticky_parent->Layer())); |
| 438 ASSERT_TRUE(constraints_map.Contains(sticky_child->Layer())); | 424 ASSERT_TRUE(constraints_map.Contains(sticky_child->Layer())); |
| 439 ASSERT_TRUE(constraints_map.Contains(sticky_nested_child->Layer())); | 425 ASSERT_TRUE(constraints_map.Contains(sticky_nested_child->Layer())); |
| 440 | 426 |
| 441 // The outer <div> should not detect the scroller as its containing-block | 427 // The outer <div> should not detect the scroller as its containing-block |
| 442 // shifting ancestor. | 428 // shifting ancestor. |
| 443 EXPECT_FALSE(constraints_map.at(sticky_parent->Layer()) | 429 EXPECT_FALSE(constraints_map.at(sticky_parent->Layer()) |
| 444 .NearestStickyBoxShiftingContainingBlock()); | 430 .NearestStickyBoxShiftingContainingBlock()); |
| 445 | 431 |
| 446 // Both inner children should detect the parent <div> as their | 432 // Both inner children should detect the parent <div> as their |
| 447 // containing-block shifting ancestor. They skip past unanchored sticky | 433 // containing-block shifting ancestor. |
| 448 // because it will never have a non-zero offset. | |
| 449 EXPECT_EQ(sticky_parent, constraints_map.at(sticky_child->Layer()) | 434 EXPECT_EQ(sticky_parent, constraints_map.at(sticky_child->Layer()) |
| 450 .NearestStickyBoxShiftingContainingBlock()); | 435 .NearestStickyBoxShiftingContainingBlock()); |
| 451 EXPECT_EQ(sticky_parent, constraints_map.at(sticky_nested_child->Layer()) | 436 EXPECT_EQ(sticky_parent, constraints_map.at(sticky_nested_child->Layer()) |
| 452 .NearestStickyBoxShiftingContainingBlock()); | 437 .NearestStickyBoxShiftingContainingBlock()); |
| 453 } | 438 } |
| 454 | 439 |
| 455 // Verifies that the correct containing-block shifting ancestor is found when | 440 // Verifies that the correct containing-block shifting ancestor is found when |
| 456 // computing the sticky constraints, in the case where the overflow ancestor is | 441 // computing the sticky constraints, in the case where the overflow ancestor is |
| 457 // the page itself. This is a special-case version of the test above, as we | 442 // the page itself. This is a special-case version of the test above, as we |
| 458 // often treat the root page as special when it comes to scroll logic. It should | 443 // often treat the root page as special when it comes to scroll logic. It should |
| 459 // not make a difference for containing-block shifting ancestor calculations. | 444 // not make a difference for containing-block shifting ancestor calculations. |
| 460 TEST_F(LayoutBoxModelObjectTest, | 445 TEST_F(LayoutBoxModelObjectTest, |
| 461 StickyPositionFindsCorrectContainingBlockShiftingAncestorRoot) { | 446 StickyPositionFindsCorrectContainingBlockShiftingAncestorRoot) { |
| 462 SetBodyInnerHTML( | 447 SetBodyInnerHTML( |
| 463 "<style>#stickyParent { position: sticky; top: 0;}" | 448 "<style>#stickyParent { position: sticky; }" |
| 464 "#stickyGrandchild { position: sticky; top: 0;}</style>" | 449 "#stickyGrandchild { position: sticky; }</style>" |
| 465 "<div id='stickyParent'><div><div id='stickyGrandchild'></div></div>" | 450 "<div id='stickyParent'><div><div id='stickyGrandchild'></div></div>" |
| 466 "</div>"); | 451 "</div>"); |
| 467 | 452 |
| 468 LayoutBoxModelObject* sticky_parent = | 453 LayoutBoxModelObject* sticky_parent = |
| 469 ToLayoutBoxModelObject(GetLayoutObjectByElementId("stickyParent")); | 454 ToLayoutBoxModelObject(GetLayoutObjectByElementId("stickyParent")); |
| 470 LayoutBoxModelObject* sticky_grandchild = | 455 LayoutBoxModelObject* sticky_grandchild = |
| 471 ToLayoutBoxModelObject(GetLayoutObjectByElementId("stickyGrandchild")); | 456 ToLayoutBoxModelObject(GetLayoutObjectByElementId("stickyGrandchild")); |
| 472 | 457 |
| 473 PaintLayerScrollableArea* scrollable_area = | 458 PaintLayerScrollableArea* scrollable_area = |
| 474 sticky_parent->Layer()->AncestorOverflowLayer()->GetScrollableArea(); | 459 sticky_parent->Layer()->AncestorOverflowLayer()->GetScrollableArea(); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 486 } | 471 } |
| 487 | 472 |
| 488 // Verifies that the correct containing-block shifting ancestor is found when | 473 // Verifies that the correct containing-block shifting ancestor is found when |
| 489 // computing the sticky constraints, in the case of tables. Tables are unusual | 474 // computing the sticky constraints, in the case of tables. Tables are unusual |
| 490 // because the containing block for all table elements is the <table> itself, so | 475 // because the containing block for all table elements is the <table> itself, so |
| 491 // we have to skip over elements to find the correct ancestor. | 476 // we have to skip over elements to find the correct ancestor. |
| 492 TEST_F(LayoutBoxModelObjectTest, | 477 TEST_F(LayoutBoxModelObjectTest, |
| 493 StickyPositionFindsCorrectContainingBlockShiftingAncestorTable) { | 478 StickyPositionFindsCorrectContainingBlockShiftingAncestorTable) { |
| 494 SetBodyInnerHTML( | 479 SetBodyInnerHTML( |
| 495 "<style>#scroller { overflow-y: scroll; }" | 480 "<style>#scroller { overflow-y: scroll; }" |
| 496 "#stickyOuter { position: sticky; top: 0;}" | 481 "#stickyOuter { position: sticky; }" |
| 497 "#stickyTh { position: sticky; top: 0;}</style>" | 482 "#stickyTh { position: sticky; }</style>" |
| 498 "<div id='scroller'><div id='stickyOuter'><table><thead><tr>" | 483 "<div id='scroller'><div id='stickyOuter'><table><thead><tr>" |
| 499 "<th id='stickyTh'></th></tr></thead></table></div></div>"); | 484 "<th id='stickyTh'></th></tr></thead></table></div></div>"); |
| 500 | 485 |
| 501 LayoutBoxModelObject* scroller = | 486 LayoutBoxModelObject* scroller = |
| 502 ToLayoutBoxModelObject(GetLayoutObjectByElementId("scroller")); | 487 ToLayoutBoxModelObject(GetLayoutObjectByElementId("scroller")); |
| 503 LayoutBoxModelObject* sticky_outer = | 488 LayoutBoxModelObject* sticky_outer = |
| 504 ToLayoutBoxModelObject(GetLayoutObjectByElementId("stickyOuter")); | 489 ToLayoutBoxModelObject(GetLayoutObjectByElementId("stickyOuter")); |
| 505 LayoutBoxModelObject* sticky_th = | 490 LayoutBoxModelObject* sticky_th = |
| 506 ToLayoutBoxModelObject(GetLayoutObjectByElementId("stickyTh")); | 491 ToLayoutBoxModelObject(GetLayoutObjectByElementId("stickyTh")); |
| 507 | 492 |
| (...skipping 353 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 861 EXPECT_EQ(LayoutSize(0, 25), inner_sticky_th->StickyPositionOffset()); | 846 EXPECT_EQ(LayoutSize(0, 25), inner_sticky_th->StickyPositionOffset()); |
| 862 } | 847 } |
| 863 | 848 |
| 864 // Verifies that the calculated position:sticky offsets are correct in the case | 849 // Verifies that the calculated position:sticky offsets are correct in the case |
| 865 // of nested inline elements. | 850 // of nested inline elements. |
| 866 TEST_F(LayoutBoxModelObjectTest, StickyPositionNestedInlineElements) { | 851 TEST_F(LayoutBoxModelObjectTest, StickyPositionNestedInlineElements) { |
| 867 SetBodyInnerHTML( | 852 SetBodyInnerHTML( |
| 868 "<style>#scroller { width: 100px; height: 100px; overflow-y: scroll; }" | 853 "<style>#scroller { width: 100px; height: 100px; overflow-y: scroll; }" |
| 869 "#paddingBefore { height: 50px; }" | 854 "#paddingBefore { height: 50px; }" |
| 870 "#outerInline { display: inline; position: sticky; top: 0; }" | 855 "#outerInline { display: inline; position: sticky; top: 0; }" |
| 871 "#unanchoredSticky { position: sticky; display: inline; }" | |
| 872 ".inline {display: inline;}" | 856 ".inline {display: inline;}" |
| 873 "#innerInline { display: inline; position: sticky; top: 25px; }" | 857 "#innerInline { display: inline; position: sticky; top: 25px; }" |
| 874 "#paddingAfter { height: 200px; }</style>" | 858 "#paddingAfter { height: 200px; }</style>" |
| 875 "<div id='scroller'>" | 859 "<div id='scroller'>" |
| 876 " <div id='paddingBefore'></div>" | 860 " <div id='paddingBefore'></div>" |
| 877 " <div id='outerInline'>" | 861 " <div id='outerInline'>" |
| 878 " <div id='unanchoredSticky'>" | 862 " <div class='inline'>" |
| 879 " <div class='inline'>" | 863 " <div id='innerInline'></div>" |
| 880 " <div id='innerInline'></div>" | |
| 881 " </div>" | |
| 882 " </div>" | 864 " </div>" |
| 883 " </div>" | 865 " </div>" |
| 884 " <div id='paddingAfter'></div>" | 866 " <div id='paddingAfter'></div>" |
| 885 "</div>"); | 867 "</div>"); |
| 886 | 868 |
| 887 LayoutBoxModelObject* outer_inline = | 869 LayoutBoxModelObject* outer_inline = |
| 888 ToLayoutBoxModelObject(GetLayoutObjectByElementId("outerInline")); | 870 ToLayoutBoxModelObject(GetLayoutObjectByElementId("outerInline")); |
| 889 LayoutBoxModelObject* inner_inline = | 871 LayoutBoxModelObject* inner_inline = |
| 890 ToLayoutBoxModelObject(GetLayoutObjectByElementId("innerInline")); | 872 ToLayoutBoxModelObject(GetLayoutObjectByElementId("innerInline")); |
| 891 | 873 |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 942 | 924 |
| 943 // TODO(smcgruer): Until http://crbug.com/686164 is fixed, we need to update | 925 // TODO(smcgruer): Until http://crbug.com/686164 is fixed, we need to update |
| 944 // the constraints here before calculations will be correct. | 926 // the constraints here before calculations will be correct. |
| 945 inner_sticky->UpdateStickyPositionConstraints(); | 927 inner_sticky->UpdateStickyPositionConstraints(); |
| 946 | 928 |
| 947 EXPECT_EQ(LayoutSize(0, 100), outer_sticky->StickyPositionOffset()); | 929 EXPECT_EQ(LayoutSize(0, 100), outer_sticky->StickyPositionOffset()); |
| 948 EXPECT_EQ(LayoutSize(0, 25), inner_sticky->StickyPositionOffset()); | 930 EXPECT_EQ(LayoutSize(0, 25), inner_sticky->StickyPositionOffset()); |
| 949 } | 931 } |
| 950 | 932 |
| 951 } // namespace blink | 933 } // namespace blink |
| OLD | NEW |