|
| 1 | +using HtmlRenderer.IntegrationTest.TestSupport; |
| 2 | + |
| 3 | +namespace HtmlRenderer.IntegrationTest.BoxModel; |
| 4 | + |
| 5 | +/// <summary> |
| 6 | +/// DomParser's "block inside inline" DOM-correction pass (CorrectBlockInsideInline) should never look through an |
| 7 | +/// inline-level box that establishes its own independent formatting context (inline-block/inline-table both do) |
| 8 | +/// when deciding whether an ANCESTOR box needs splitting - DomUtils.ContainsInlinesOnly is meant to make such a |
| 9 | +/// box opaque for that purpose. A child whose own resolved display is "none" (not "inline") should not make the |
| 10 | +/// correction pass misclassify the box's content as containing a "block", incorrectly splitting the child out of |
| 11 | +/// its real parent entirely. |
| 12 | +/// </summary> |
| 13 | +/// <remarks> |
| 14 | +/// The triage note for this file claimed both cases already pass on this fork. Direct testing shows otherwise |
| 15 | +/// for the inline-block case: dumping the box tree for the <select>/<option> markup below shows |
| 16 | +/// <c>opt1</c> (the FIRST display:none <option>) gets hoisted out from under <select> entirely and |
| 17 | +/// becomes a sibling of the anonymous block box CorrectInlineBoxesParent wraps <select> in, while |
| 18 | +/// <c>opt2</c> (the second, otherwise-identical <option>) correctly stays nested. That first-child-only |
| 19 | +/// asymmetry is exactly the atomic-inline-level gap the class doc describes - it just was not caught by the |
| 20 | +/// triage pass. The inline-table case genuinely does pass, so only the inline-block test method is ignored |
| 21 | +/// (with the real, faithfully-ported assertions kept, to document the target behavior). |
| 22 | +/// </remarks> |
| 23 | +[DoNotParallelize] |
| 24 | +[TestClass] |
| 25 | +public sealed class AtomicInlineLevelBoxCorrectionTests |
| 26 | +{ |
| 27 | + [Ignore("Confirmed real bug on this fork, not a porting mistake: dumping the box tree shows opt1 (the " + |
| 28 | + "FIRST display:none <option> child of an inline-block <select>) gets hoisted out from under " + |
| 29 | + "<select> and becomes a sibling of the anonymous block wrapper CorrectInlineBoxesParent creates, " + |
| 30 | + "while opt2 (second, otherwise-identical child) stays correctly nested. DomUtils.ContainsInlinesOnly " + |
| 31 | + "does not treat inline-block as opaque the way an atomic-inline-level check should.")] |
| 32 | + [TestMethod] |
| 33 | + public void DisplayNoneChildOfInlineBlockBox_StaysNestedUnderItsRealParent() |
| 34 | + { |
| 35 | + var html = LayoutHarness.Wrap( |
| 36 | + "<select id='sel'>" + |
| 37 | + "<option id='opt1' style='display:none'>Red</option>" + |
| 38 | + "<option id='opt2' style='display:none'>Green</option>" + |
| 39 | + "</select>"); |
| 40 | + |
| 41 | + var (root, _) = LayoutHarness.Layout(html); |
| 42 | + var select = LayoutHarness.FindById(root, "sel")!; |
| 43 | + var opt1 = LayoutHarness.FindById(root, "opt1")!; |
| 44 | + var opt2 = LayoutHarness.FindById(root, "opt2")!; |
| 45 | + |
| 46 | + Assert.IsTrue(IsDescendantOf(opt1, select), "opt1 (display:none) must stay nested under its real <select> parent."); |
| 47 | + Assert.IsTrue(IsDescendantOf(opt2, select), "opt2 must stay nested under its real <select> parent."); |
| 48 | + } |
| 49 | + |
| 50 | + [TestMethod] |
| 51 | + public void DisplayNoneChildOfInlineTableBox_StaysNestedUnderItsRealParent() |
| 52 | + { |
| 53 | + var html = LayoutHarness.Wrap( |
| 54 | + "<div id='wrap'>" + |
| 55 | + "<div id='it' style='display:inline-table'>" + |
| 56 | + "<div style='display:table-row'><div id='cell' style='display:table-cell'>cell</div></div>" + |
| 57 | + "<div id='hidden' style='display:none'>hidden</div>" + |
| 58 | + "</div>" + |
| 59 | + "</div>"); |
| 60 | + |
| 61 | + var (root, _) = LayoutHarness.Layout(html); |
| 62 | + var inlineTable = LayoutHarness.FindById(root, "it")!; |
| 63 | + var hidden = LayoutHarness.FindById(root, "hidden")!; |
| 64 | + |
| 65 | + Assert.IsTrue(IsDescendantOf(hidden, inlineTable), "a display:none sibling inside an inline-table must stay nested under it."); |
| 66 | + } |
| 67 | + |
| 68 | + // PeachPDF's source file also has a "DisplayNoneChildOfInlineFlexBox_StillStaysNestedUnderItsRealParent" |
| 69 | + // regression case, guarding pre-existing display:inline-flex handling. HTML-Renderer (this fork) implements |
| 70 | + // no flexbox layout at all - "flex"/"inline-flex" are not recognized display values anywhere in |
| 71 | + // TheArtOfDev.HtmlRenderer.Core (confirmed: no "flex" hits in the Core source tree) - so there is no such |
| 72 | + // pre-existing handling to guard, and porting that case would document a feature this fork doesn't have. |
| 73 | + // Intentionally dropped rather than ported under a false premise. |
| 74 | + |
| 75 | + private static bool IsDescendantOf(TheArtOfDev.HtmlRenderer.Core.Dom.CssBox box, TheArtOfDev.HtmlRenderer.Core.Dom.CssBox ancestor) |
| 76 | + { |
| 77 | + var current = box.ParentBox; |
| 78 | + while (current != null) |
| 79 | + { |
| 80 | + if (ReferenceEquals(current, ancestor)) return true; |
| 81 | + current = current.ParentBox; |
| 82 | + } |
| 83 | + return false; |
| 84 | + } |
| 85 | +} |
0 commit comments