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