Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(148)

Side by Side Diff: third_party/WebKit/Source/core/layout/LayoutBoxModelObjectTest.cpp

Issue 2636253002: Handle nested position:sticky elements (Closed)
Patch Set: Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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/html/HTMLElement.h" 7 #include "core/html/HTMLElement.h"
8 #include "core/layout/ImageQualityController.h" 8 #include "core/layout/ImageQualityController.h"
9 #include "core/layout/LayoutTestHelper.h" 9 #include "core/layout/LayoutTestHelper.h"
10 #include "core/page/scrolling/StickyPositionScrollingConstraints.h" 10 #include "core/page/scrolling/StickyPositionScrollingConstraints.h"
(...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after
242 242
243 const StickyPositionScrollingConstraints& constraints = 243 const StickyPositionScrollingConstraints& constraints =
244 scrollableArea->stickyConstraintsMap().get(sticky->layer()); 244 scrollableArea->stickyConstraintsMap().get(sticky->layer());
245 ASSERT_EQ(IntRect(15, 115, 170, 370), 245 ASSERT_EQ(IntRect(15, 115, 170, 370),
246 enclosingIntRect( 246 enclosingIntRect(
247 getScrollContainerRelativeContainingBlockRect(constraints))); 247 getScrollContainerRelativeContainingBlockRect(constraints)));
248 ASSERT_EQ( 248 ASSERT_EQ(
249 IntRect(15, 165, 100, 100), 249 IntRect(15, 165, 100, 100),
250 enclosingIntRect(getScrollContainerRelativeStickyBoxRect(constraints))); 250 enclosingIntRect(getScrollContainerRelativeStickyBoxRect(constraints)));
251 } 251 }
252
253 // Verifies that the sticky constraints are correct when we have a simple case
254 // of nested position:sticky.
255 TEST_F(LayoutBoxModelObjectTest, StickyPositionNested) {
256 setBodyInnerHTML(
257 "<style>#scroller { height: 100px; width: 100px; overflow-y: auto; }"
258 "#prePadding { height: 50px }"
259 "#stickyParent { position: sticky; top: 0; height: 50px; }"
260 "#stickyChild { position: sticky; top: 0; height: 25px; }"
261 "#postPadding { height: 200px }</style>"
262 "<div id='scroller'><div id='prePadding'></div><div id='stickyParent'>"
263 "<div id='stickyChild'></div></div><div id='postPadding'></div></div>");
264
265 // Make sure that the constraints are cached before scrolling, or they will
266 // simply be calculated post-scroll and thus not require offset correction.
267
268 LayoutBoxModelObject* stickyParent =
269 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent"));
270 stickyParent->updateStickyPositionConstraints();
271
272 LayoutBoxModelObject* stickyChild =
273 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyChild"));
274 stickyChild->updateStickyPositionConstraints();
275
276 // Scroll the page down.
277 LayoutBoxModelObject* scroller =
278 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
279 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
280 scrollableArea->scrollToAbsolutePosition(
281 FloatPoint(scrollableArea->scrollPosition().x(), 100));
282 ASSERT_EQ(100.0, scrollableArea->scrollPosition().y());
283
284 // Both the parent and child sticky divs are attempting to place themselves at
285 // the top of the scrollable area. To achieve this the parent must offset on
286 // the y-axis against its starting position. The child is offset relative to
287 // its parent so the child should not move at all.
288
289 EXPECT_EQ(LayoutSize(0, 50), stickyParent->stickyPositionOffset());
290 EXPECT_EQ(LayoutSize(0, 0), stickyChild->stickyPositionOffset());
291 }
292
293 // Verifies that the sticky constraints are correct when the child has a
294 // larger edge constraint value than the parent.
295 TEST_F(LayoutBoxModelObjectTest, StickyPositionNestedChildLargerTop) {
296 setBodyInnerHTML(
297 "<style>#scroller { height: 100px; width: 100px; overflow-y: auto; }"
298 "#prePadding { height: 50px }"
299 "#stickyParent { position: sticky; top: 0; height: 50px; }"
300 "#stickyChild { position: sticky; top: 25px; height: 25px; }"
301 "#postPadding { height: 200px }</style>"
302 "<div id='scroller'><div id='prePadding'></div><div id='stickyParent'>"
303 "<div id='stickyChild'></div></div><div id='postPadding'></div></div>");
304
305 // Make sure that the constraints are cached before scrolling, or they will
306 // simply be calculated post-scroll and thus not require offset correction.
307
308 LayoutBoxModelObject* stickyParent =
309 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent"));
310 stickyParent->updateStickyPositionConstraints();
311
312 LayoutBoxModelObject* stickyChild =
313 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyChild"));
314 stickyChild->updateStickyPositionConstraints();
315
316 // Scroll the page down.
317 LayoutBoxModelObject* scroller =
318 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
319 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
320 scrollableArea->scrollToAbsolutePosition(
321 FloatPoint(scrollableArea->scrollPosition().x(), 100));
322 ASSERT_EQ(100.0, scrollableArea->scrollPosition().y());
323
324 // The parent is attempting to place itself at the top of the scrollable area,
325 // whilst the child is attempting to be 25 pixels from the top. To achieve
326 // this both must offset on the y-axis against their starting positions, but
327 // note the child is offset relative to the parent.
328 EXPECT_EQ(LayoutSize(0, 50), stickyParent->stickyPositionOffset());
329 EXPECT_EQ(LayoutSize(0, 25), stickyChild->stickyPositionOffset());
330 }
331
332 // Verifies that the sticky constraints are correct when the child has a
333 // smaller edge constraint value than the parent.
334 TEST_F(LayoutBoxModelObjectTest, StickyPositionNestedParentLargerTop) {
335 setBodyInnerHTML(
336 "<style>#scroller { height: 100px; width: 100px; overflow-y: auto; }"
337 "#prePadding { height: 50px }"
338 "#stickyParent { position: sticky; top: 25px; height: 50px; }"
339 "#stickyChild { position: sticky; top: 0; height: 25px; }"
340 "#postPadding { height: 200px }</style>"
341 "<div id='scroller'><div id='prePadding'></div><div id='stickyParent'>"
342 "<div id='stickyChild'></div></div><div id='postPadding'></div></div>");
343
344 // Make sure that the constraints are cached before scrolling, or they will
345 // simply be calculated post-scroll and thus not require offset correction.
346
347 LayoutBoxModelObject* stickyParent =
348 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent"));
349 stickyParent->updateStickyPositionConstraints();
350
351 LayoutBoxModelObject* stickyChild =
352 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyChild"));
353 stickyChild->updateStickyPositionConstraints();
354
355 // Scroll the page down.
356 LayoutBoxModelObject* scroller =
357 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
358 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
359 scrollableArea->scrollToAbsolutePosition(
360 FloatPoint(scrollableArea->scrollPosition().x(), 100));
361 ASSERT_EQ(100.0, scrollableArea->scrollPosition().y());
362
363 // The parent is attempting to place itself 25 pixels from the top of the
364 // scrollable area, whilst the child is attempting to be at the top. However,
365 // the child must stay contained within the parent, so it should be pushed
366 // down to the same height. As always, the child offset is relative.
367 EXPECT_EQ(LayoutSize(0, 75), stickyParent->stickyPositionOffset());
368 EXPECT_EQ(LayoutSize(0, 0), stickyChild->stickyPositionOffset());
369 }
370
371 // Verifies that the sticky constraints are correct when the child has a large
372 // enough edge constraint value to try and push outside of its parent.
373 TEST_F(LayoutBoxModelObjectTest,
374 StickyPositionNestedChildPushingOutsideParent) {
375 setBodyInnerHTML(
376 "<style> #scroller { height: 100px; width: 100px; overflow-y: auto; }"
377 "#prePadding { height: 50px; }"
378 "#stickyParent { position: sticky; top: 0; height: 50px; }"
379 "#stickyChild { position: sticky; top: 50px; height: 25px; }"
380 "#postPadding { height: 200px }</style>"
381 "<div id='scroller'><div id='prePadding'></div><div id='stickyParent'>"
382 "<div id='stickyChild'></div></div><div id='postPadding'></div></div>");
383
384 // Make sure that the constraints are cached before scrolling, or they will
385 // simply be calculated post-scroll and thus not require offset correction.
386
387 LayoutBoxModelObject* stickyParent =
388 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyParent"));
389 stickyParent->updateStickyPositionConstraints();
390
391 LayoutBoxModelObject* stickyChild =
392 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyChild"));
393 stickyChild->updateStickyPositionConstraints();
394
395 // Scroll the page down.
396 LayoutBoxModelObject* scroller =
397 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
398 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
399 scrollableArea->scrollToAbsolutePosition(
400 FloatPoint(scrollableArea->scrollPosition().x(), 100));
401 ASSERT_EQ(100.0, scrollableArea->scrollPosition().y());
402
403 // The parent is attempting to place itself at the top of the scrollable area,
404 // whilst the child is attempting to be 50 pixels from the top. However, there
405 // is only 25 pixels of space for the child to move into, so it should be
406 // capped by that offset. As always, the child offset is relative.
407 EXPECT_EQ(LayoutSize(0, 50), stickyParent->stickyPositionOffset());
408 EXPECT_EQ(LayoutSize(0, 25), stickyChild->stickyPositionOffset());
409 }
410
411 // Verifies that the sticky constraints are correct in the case of triple
412 // nesting. Triple (or more) nesting must be tested as the grandchild sticky
413 // offset must be corrected for both the containing block and the viewport.
414 TEST_F(LayoutBoxModelObjectTest, StickyPositionNestedTripleNestedDiv) {
415 setBodyInnerHTML(
416 "<style>#scroller { height: 200px; width: 100px; overflow-y: auto; }"
417 "#prePadding { height: 50px; }"
418 "#sticky1 { position: sticky; top: 0; height: 100px; }"
419 "#sticky2 { position: sticky; top: 0; height: 75px; }"
420 "#sticky3 { position: sticky; top: 25px; height: 25px; }"
421 "#postPadding { height: 400px }</style>"
422 "<div id='scroller'><div id='prePadding'></div><div id='sticky1'>"
423 "<div id='sticky2'><div id='sticky3'></div></div></div>"
424 "<div id='postPadding'></div></div>");
425
426 // Make sure that the constraints are cached before scrolling, or they will
427 // simply be calculated post-scroll and thus not require offset correction.
428
429 LayoutBoxModelObject* stickyOne =
430 toLayoutBoxModelObject(getLayoutObjectByElementId("sticky1"));
431 stickyOne->updateStickyPositionConstraints();
432
433 LayoutBoxModelObject* stickyTwo =
434 toLayoutBoxModelObject(getLayoutObjectByElementId("sticky2"));
435 stickyTwo->updateStickyPositionConstraints();
436
437 LayoutBoxModelObject* stickyThree =
438 toLayoutBoxModelObject(getLayoutObjectByElementId("sticky3"));
439 stickyThree->updateStickyPositionConstraints();
440
441 // Scroll the page down.
442 LayoutBoxModelObject* scroller =
443 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
444 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
445 scrollableArea->scrollToAbsolutePosition(
446 FloatPoint(scrollableArea->scrollPosition().x(), 100));
447 ASSERT_EQ(100.0, scrollableArea->scrollPosition().y());
448
449 // The grandparent and parent divs are attempting to place themselves at the
450 // top of the scrollable area. The child div is attempting to place itself at
451 // an offset of 25 pixels to the top of the scrollable area. The result of
452 // this sticky offset calculation is quite simple, but internally the child
453 // offset has to offset both against its containing block and against its
454 // viewport ancestor (the grandparent).
455 EXPECT_EQ(LayoutSize(0, 50), stickyOne->stickyPositionOffset());
456 EXPECT_EQ(LayoutSize(0, 0), stickyTwo->stickyPositionOffset());
457 EXPECT_EQ(LayoutSize(0, 25), stickyThree->stickyPositionOffset());
458 }
459
460 // Verifies that the sticky constraints are correct in the case of tables.
461 // Tables are special as the containing block for all child elements is always
462 // the root level <table>.
463 TEST_F(LayoutBoxModelObjectTest, StickyPositionNestedTableExample) {
464 setBodyInnerHTML(
465 "<style>#scroller { height: 100px; width: 100px; overflow-y: auto; }"
466 "#prePadding { height: 50px; }"
467 "#stickyDiv { position: sticky; top: 0; height: 200px; }"
468 "#table { border-collapse: collapse; }"
469 "#stickyThead { position: sticky; top: 0px; }"
470 "#stickyTr { position: sticky; top: 0px; }"
471 "#stickyTh { position: sticky; top: 0px; }"
472 "#postPadding { height: 200px; }</style>"
473 "<div id='scroller'><div id='prePadding'></div><div id='stickyDiv'>"
474 "<table id='table'><thead id='stickyThead'><tr id='stickyTr'>"
475 "<th id='stickyTh'>Header</th></tr></thread><tbody><tr><td>0</td></tr>"
476 "<tr><td>1</td></tr><tr><td>2</td></tr><tr><td>3</td></tr></tbody>"
477 "</table></div><div id='postPadding'></div></div>");
478
479 LOG(INFO) << document().body()->innerHTML();
480
481 // Make sure that the constraints are cached before scrolling, or they will
482 // simply be calculated post-scroll and thus not require offset correction.
483
484 LayoutBoxModelObject* stickyDiv =
485 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyDiv"));
486 stickyDiv->updateStickyPositionConstraints();
487
488 LayoutBoxModelObject* stickyThead =
489 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyThead"));
490 stickyThead->updateStickyPositionConstraints();
491
492 LayoutBoxModelObject* stickyTr =
493 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyTr"));
494 stickyTr->updateStickyPositionConstraints();
495
496 LayoutBoxModelObject* stickyTh =
497 toLayoutBoxModelObject(getLayoutObjectByElementId("stickyTh"));
498 stickyTh->updateStickyPositionConstraints();
499
500 // Scroll the page down.
501 LayoutBoxModelObject* scroller =
502 toLayoutBoxModelObject(getLayoutObjectByElementId("scroller"));
503 PaintLayerScrollableArea* scrollableArea = scroller->getScrollableArea();
504 scrollableArea->scrollToAbsolutePosition(
505 FloatPoint(scrollableArea->scrollPosition().x(), 150));
506 ASSERT_EQ(150.0, scrollableArea->scrollPosition().y());
507
508 // All sticky elements are attempting to stick to the top of the scrollable
509 // area. For the root sticky div, this requires an offset. All the other
510 // descendant sticky elements are positioned relatively so don't need offset.
511 EXPECT_EQ(LayoutSize(0, 100), stickyDiv->stickyPositionOffset());
512 EXPECT_EQ(LayoutSize(0, 0), stickyThead->stickyPositionOffset());
513 EXPECT_EQ(LayoutSize(0, 0), stickyTr->stickyPositionOffset());
514 EXPECT_EQ(LayoutSize(0, 0), stickyTh->stickyPositionOffset());
515
516 // If we now scroll to the point where the overall sticky div starts to move,
517 // the table headers should continue to stick to the top of the scrollable
518 // area until they run out of <table> space to move in.
519
520 scrollableArea->scrollToAbsolutePosition(
521 FloatPoint(scrollableArea->scrollPosition().x(), 275));
522 ASSERT_EQ(275.0, scrollableArea->scrollPosition().y());
523
524 // NOTE(smcgruer): Currently fails - the <th> also moves 12
525 // NOTE(smcgruer): I'm not sure that 12 is correct. My hankerchief math gets
526 // me something like (275 - 50 - 200) = 25? But it renders correct aisde from
527 // the <th>...
528 EXPECT_EQ(LayoutSize(0, 200), stickyDiv->stickyPositionOffset());
529 EXPECT_EQ(LayoutSize(0, 12), stickyThead->stickyPositionOffset());
530 EXPECT_EQ(LayoutSize(0, 0), stickyTr->stickyPositionOffset());
531 EXPECT_EQ(LayoutSize(0, 0), stickyTh->stickyPositionOffset());
532
533 // Finally, if we scroll so that the table is off the top of the page, the
534 // sticky header should go off the top with it.
535
536 scrollableArea->scrollToAbsolutePosition(
537 FloatPoint(scrollableArea->scrollPosition().x(), 350));
538 ASSERT_EQ(350.0, scrollableArea->scrollPosition().y());
539
540 // TODO(smcgruer): Do EXPECT_EQ for this scenario. It's currently broken
541 // anyway.
542 }
543
252 } // namespace blink 544 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698