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 |