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 |