Skip to content

Commit b7d7fcf

Browse files
committed
Fix relative offset applied twice when resuming a box
Also match the z-index test's draw calls by color, so it can pass once stacking order is implemented.
1 parent 5cb23f6 commit b7d7fcf

3 files changed

Lines changed: 28 additions & 21 deletions

File tree

‎Source/HtmlRenderer/Core/Dom/CssBox.cs‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -898,8 +898,9 @@ protected virtual void PerformLayoutImp(RGraphics g)
898898
// Resuming this box's own interrupted child/content loop, not placing it fresh
899899
// - css-break-3 §2 gives a box one inline position across all its fragments, so
900900
// there is nothing to re-derive here; Location already holds it from the pass
901-
// that placed this box originally.
902-
top = Location.Y;
901+
// that placed this box originally. Back out the relative offset, it gets applied
902+
// again below.
903+
top = Location.Y - RelativeOffsetY;
903904
}
904905
else if (_resumeTopOverride.HasValue)
905906
{

‎Source/Test/HtmlRenderer.IntegrationTest/Painting/StackingContextTests.cs‎

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,36 +14,25 @@ namespace HtmlRenderer.IntegrationTest.Painting;
1414
[TestClass]
1515
public sealed class StackingContextTests
1616
{
17-
[Ignore("z-index/stacking-context paint order is not implemented on this fork: FragmentPainter.cs's own " +
18-
"class remarks explicitly document it as deferred (\"Stacking-context paint order and " +
19-
"box-decoration-break slicing are follow-on work\"), and CssBoxProperties has no ZIndex field at " +
20-
"all - the CSS-OM parses z-index (CssEngine/StyleProperties/Flow/ZIndexProperty.cs) but " +
21-
"CssUtils.SetPropertyValue never dispatches it onto a box, so it has zero effect on paint order. " +
22-
"This box tree currently paints in plain document order (normal flow, then absolute/fixed, per " +
23-
"FragmentPainter's child-iteration order) regardless of any z-index value - a position:relative " +
24-
"z-index:2 box painting over a LATER position:fixed sibling (this test's whole premise) is exactly " +
25-
"the case document order alone cannot produce, so this reliably fails rather than passing by " +
26-
"accident. Implementing real stacking-context ordering (a ZIndex box property, plus grouping/" +
27-
"sorting descendants by stacking context per CSS2.1 Appendix E) is a separate, larger feature port.")]
17+
[Ignore("z-index and stacking-context paint order aren't implemented yet, boxes paint in document order")]
2818
[TestMethod]
2919
public void PositionedZIndex_PaintsOverFixedPositionedContent()
3020
{
31-
// A black position:fixed bar declared AFTER (later in the box tree than) a white
32-
// position:relative;z-index:2 box must still be painted BEFORE it (i.e. underneath).
21+
// The white z-index:2 box must paint on top of the black fixed bar, even though the painter
22+
// currently draws the fixed bar after the normal flow.
3323
var html = LayoutHarness.Wrap(
3424
"<div id='fixedbar' style='position:fixed; top:0; left:0; width:50px; height:50px; background:rgb(0,0,0);'></div>"
3525
+ "<div id='intro' style='position:relative; z-index:2; width:50px; height:50px; background:rgb(255,255,255);'></div>");
3626

37-
var (root, container) = PaintHarness.Layout(html);
27+
var (_, container) = PaintHarness.Layout(html);
3828
var recorder = PaintHarness.PaintPage(container);
3929

40-
var fixedBar = PaintHarness.FindById(root, "fixedbar")!;
41-
var intro = PaintHarness.FindById(root, "intro")!;
42-
4330
var drawRectCalls = recorder.Log.OfType<RecordingGraphics.DrawRectCall>().ToList();
44-
var fixedBarPaintIndex = drawRectCalls.FindIndex(c => c.X == fixedBar.Location.X && c.Y == fixedBar.Location.Y);
45-
var introPaintIndex = drawRectCalls.FindIndex(c => c.X == intro.Location.X && c.Y == intro.Location.Y);
31+
var fixedBarPaintIndex = drawRectCalls.FindIndex(c => c.Color.R == 0 && c.Color.G == 0 && c.Color.B == 0);
32+
var introPaintIndex = drawRectCalls.FindIndex(c => c.Color.R == 255 && c.Color.G == 255 && c.Color.B == 255);
4633

34+
Assert.IsGreaterThanOrEqualTo(0, fixedBarPaintIndex);
35+
Assert.IsGreaterThanOrEqualTo(0, introPaintIndex);
4736
Assert.IsTrue(fixedBarPaintIndex < introPaintIndex,
4837
"the z-index:2 box must paint after (on top of) the fixed bar, regardless of document order");
4938
}

‎Source/Test/HtmlRenderer.IntegrationTest/Positioning/AbsolutePositioningIntegrationTests.cs‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,23 @@ public void PositionRelative_Offset_DoesNotAffectParentHeightOrFollowingSibling(
6060
Assert.AreEqual(parent.ActualBottom, after.Location.Y, Delta);
6161
}
6262

63+
[TestMethod]
64+
public void PositionRelative_WithAForcedBreakInside_AppliesTheOffsetOnce()
65+
{
66+
// The forced break makes a later layout pass resume inside the relative box, which must not add
67+
// its offset a second time.
68+
var (root, _) = PaintHarness.LayoutPaginated(PaintHarness.Wrap(
69+
"<p style='margin:0;height:50px'>a</p>"
70+
+ "<div id='rel' style='position:relative;top:20px'>"
71+
+ "<p style='margin:0;height:50px'>b</p>"
72+
+ "<p style='margin:0;height:50px;break-before:page'>c</p>"
73+
+ "</div>"),
74+
pageHeight: 200, margin: 0);
75+
var rel = LayoutHarness.FindById(root, "rel")!;
76+
77+
Assert.AreEqual(70, rel.Location.Y, Delta);
78+
}
79+
6380
[TestMethod]
6481
public void PositionRelative_OffsetOnBoxItself_DoesNotShiftFollowingSibling()
6582
{

0 commit comments

Comments
 (0)