Skip to content

Add winkin text backend - #1153

Open
nicoburns wants to merge 61 commits into
mainfrom
devin/1791548469-winkin-text-backends
Open

nicoburns wants to merge 61 commits into
mainfrom
devin/1791548469-winkin-text-backends

Conversation

@nicoburns

@nicoburns nicoburns commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

Summary

This is @dfrg's text-backends branch at feb319e5, rebased linearly onto main (7832c177), with four commits that are not his: one before his commits, three after. It is opened here to get CI and WPT results on the complete winkin integration; it is not ready to merge as is (see "Open before merging").

What the branch does

  • Text backend boundary. Text shaping, editing and fonts move behind blitz-dom/src/text/mod.rs: per-backend types (TextLayout, TextEditor, MarkerLayout, FontContext) and traits (InlineText, InlineBuilder, InlineLayoutEngine, LineExclusions, EditableText, EditEngine, MarkerEngine, DocumentText). The Parley path moves to text/parley/, and painting to blitz-paint/src/text/parley.rs.
  • Shared inline layout in Blitz. The DOM walk that builds an inline formatting context, line layout and float placement live in blitz-dom/src/layout/inline/{build,lines,floats}.rs and are written once for every backend.
  • winkin backend. text/winkin/ and blitz-paint/src/text/winkin.rs implement the same traits with winkin and fontwich, including hit testing, selection, innerText and text inputs.
  • Backend selection. parley (default) and winkin are cargo features, forwarded through blitz, blitz-paint, blitz-html, blitz-vibey-script, dioxus-native, the browser and the WPT runner. blitz-dom/build.rs turns them into one text_parley / text_winkin cfg; with both enabled Parley is used, with neither it is a compile error.
  • CI (from the branch). A "Check text backends" job in ci.yml checks Parley, winkin alone, both, and neither.

Commits that are not from the branch

In the rebased commits, the only conflicts were in Cargo.toml and Cargo.lock: parley / parlance come from crates.io (0.12 / 0.1) as in #1159, and the lockfile is main's with the winkin, fontwich and Stylo fork entries resolved on top.

Open before merging

  • Dependencies. winkin and fontwich are git dependencies pinned to a rev, and all Stylo crates are [patch]ed to dfrg/stylo branch style-more (unpinned).
  • General fixes riding along that are not part of the backend work: dir=auto direction from the first strong character, UA bidi rules for bdo / pre / textarea, text-autospace in the UA sheet, and the screenshot example changes.
  • Backend selection mechanism (build.rs cfgs vs plain feature cfgs vs runtime selection).
  • text-overflow. winkin handles ellipsis itself; Implement text-overflow (continues #893) #1015 implements it for Parley and would need rebasing onto the boundary.

Locally only cargo check of blitz-dom and blitz-paint was run on the final tree, with each backend; tests and WPT run in CI.

Link to Devin session: https://dioxus.staging.devinenterprise.com/sessions/93190c0235454579ac1505b20ed27dc8
Open in Devin Desktop: https://dioxus.staging.devinenterprise.com/desktop/session/93190c0235454579ac1505b20ed27dc8?variant=devin-insiders
Requested by: @nicoburns

WPT results

Subtests: 3323 newly passing, 348 newly failing (net +2975). Crashes: -1.

Subtest changes by area (139 areas)
+ css                       | +2977 (+3319 / -342) | 59.62% ->  61.12% ( +1.50%) | 118007 -> 120984 / 197936
+   CSS2                    |  +525 ( +577 /  -52) | 78.49% ->  84.44% ( +5.95%) |   6923 ->   7448 /   8820
+     abspos                |    +2 (   +3 /   -1) | 74.29% ->  80.00% ( +5.71%) |     26 ->     28 /     35
+     bidi-text             |   +42 (  +43 /   -1) | 43.81% ->  83.81% (+40.00%) |     46 ->     88 /    105
+     box                   |    +1 (   +1 /   -0) | 44.44% ->  55.56% (+11.11%) |      4 ->      5 /      9
+     cascade               |    +4 (   +5 /   -1) | 81.25% ->  93.75% (+12.50%) |     26 ->     30 /     32
+     css1                  |   +31 (  +31 /   -0) | 78.66% ->  97.56% (+18.90%) |    129 ->    160 /    164
+     floats                |   +12 (  +14 /   -2) | 74.89% ->  80.18% ( +5.29%) |    170 ->    182 /    227
+     floats-clear          |    +7 (   +7 /   -0) | 79.84% ->  82.56% ( +2.71%) |    206 ->    213 /    258
+     fonts                 |    +4 (   +4 /   -0) | 93.12% ->  95.62% ( +2.50%) |    149 ->    153 /    160
+     generated-content     |    +3 (   +3 /   -0) | 47.35% ->  48.67% ( +1.33%) |    107 ->    110 /    226
+     linebox               |   +15 (  +17 /   -2) | 81.96% ->  83.70% ( +1.75%) |    704 ->    719 /    859
-     lists                 |   -38 (   +0 /  -38) | 39.10% ->  14.74% (-24.36%) |     61 ->     23 /    156
+     margin-padding-clear  |    +4 (   +4 /   -0) | 96.48% ->  97.07% ( +0.59%) |    658 ->    662 /    682
+     normal-flow           |    +8 (  +10 /   -2) | 84.38% ->  85.26% ( +0.88%) |    767 ->    775 /    909
+     positioning           |    +3 (   +3 /   -0) | 94.18% ->  94.75% ( +0.56%) |    502 ->    505 /    533
+     sec5                  |    +1 (   +1 /   -0) | 95.65% -> 100.00% ( +4.35%) |     22 ->     23 /     23
+     selectors             |  +361 ( +365 /   -4) | 18.58% ->  93.95% (+75.37%) |     89 ->    450 /    479
+     text                  |   +57 (  +58 /   -1) | 82.00% ->  95.86% (+13.87%) |    337 ->    394 /    411
+     visudet               |    +3 (   +3 /   -0) | 48.72% ->  56.41% ( +7.69%) |     19 ->     22 /     39
+     visufx                |    +1 (   +1 /   -0) | 81.38% ->  81.91% ( +0.53%) |    153 ->    154 /    188
+   css-align               |   +34 (  +37 /   -3) | 93.10% ->  93.73% ( +0.63%) |   5002 ->   5036 /   5373
-     baseline-rules        |    -2 (   +0 /   -2) | 83.33% ->  75.00% ( -8.33%) |     20 ->     18 /     24
+     blocks                |   +33 (  +34 /   -1) | 89.51% ->  95.28% ( +5.77%) |    512 ->    545 /    572
+     self-alignment        |    +1 (   +1 /   -0) | 99.86% -> 100.00% ( +0.14%) |    739 ->    740 /    740
-   css-backgrounds         |    -1 (   +1 /   -2) | 73.47% ->  73.45% ( -0.01%) |   5114 ->   5113 /   6961
-     background-clip       |    -1 (   +0 /   -1) | 26.00% ->  24.00% ( -2.00%) |     13 ->     12 /     50
+   css-break               |   +13 (  +14 /   -1) | 16.64% ->  17.32% ( +0.68%) |    319 ->    332 /   1917
+     flexbox               |    +1 (   +1 /   -0) | 19.70% ->  20.00% ( +0.30%) |     65 ->     66 /    330
+   css-cascade             |   +20 (  +20 /   -0) | 71.35% ->  73.22% ( +1.87%) |    762 ->    782 /   1068
+   css-color               |  +185 ( +189 /   -4) | 88.13% ->  89.56% ( +1.43%) |  11422 ->  11607 /  12960
+     parsing               |  +184 ( +188 /   -4) | 89.02% ->  90.53% ( +1.51%) |  10816 ->  11000 /  12150
!   css-conditional         |    +0 (   +1 /   -1) | 81.09% ->  81.09% ( +0.00%) |   1870 ->   1870 /   2306
!     js                    |    +0 (   +1 /   -1) | 97.87% ->  97.87% ( +0.00%) |   1607 ->   1607 /   1642
-   css-contain             |    -2 (   +0 /   -2) | 47.22% ->  47.01% ( -0.21%) |    458 ->    456 /    970
+   css-content             |    +1 (   +1 /   -0) | 77.02% ->  77.33% ( +0.31%) |    248 ->    249 /    322
+   css-display             |    +9 (  +11 /   -2) | 54.29% ->  55.64% ( +1.35%) |    361 ->    370 /    665
-     run-in                |    -2 (   +0 /   -2) | 42.11% ->  40.60% ( -1.50%) |     56 ->     54 /    133
-   css-flexbox             |   -38 (  +24 /  -62) | 86.18% ->  85.53% ( -0.65%) |   5015 ->   4977 /   5819
-     alignment             |    -2 (   +1 /   -3) | 56.78% ->  56.31% ( -0.47%) |    243 ->    241 /    428
+   css-fonts               |  +275 ( +286 /  -11) | 74.28% ->  77.37% ( +3.08%) |   6623 ->   6898 /   8916
+     matching              |    +4 (   +4 /   -0) | 20.00% ->  60.00% (+40.00%) |      2 ->      6 /     10
+     parsing               |   +80 (  +80 /   -0) | 81.78% ->  86.29% ( +4.51%) |   1450 ->   1530 /   1773
+     variations            |    +9 (  +10 /   -1) | 78.53% ->  80.89% ( +2.36%) |    300 ->    309 /    382
-   css-grid                |   -44 (  +18 /  -62) | 77.89% ->  77.64% ( -0.25%) |  13685 ->  13641 /  17570
-     alignment             |   -22 (   +8 /  -30) | 83.34% ->  82.22% ( -1.12%) |   1631 ->   1609 /   1957
-       self-baseline       |    -2 (   +1 /   -3) | 27.78% ->  24.07% ( -3.70%) |     15 ->     13 /     54
-     grid-items            |   -30 (   +2 /  -32) | 98.38% ->  95.81% ( -2.56%) |   1151 ->   1121 /   1170
+     grid-model            |    +8 (   +8 /   -0) | 96.68% -> 100.00% ( +3.32%) |    233 ->    241 /    241
+   css-highlight-api       |    +1 (   +1 /   -0) | 10.95% ->  11.43% ( +0.48%) |     23 ->     24 /    210
+     painting              |    +1 (   +1 /   -0) | 10.99% ->  12.09% ( +1.10%) |     10 ->     11 /     91
+   css-inline              |  +333 ( +368 /  -35) | 39.75% ->  67.05% (+27.30%) |    485 ->    818 /   1220
+     animation             |   +70 (  +70 /   -0) |  9.62% ->  54.49% (+44.87%) |     15 ->     85 /    156
+     baseline-shift        |    +2 (   +2 /   -0) | 33.33% ->  50.00% (+16.67%) |      4 ->      6 /     12
+     baseline-source       |   +20 (  +21 /   -1) | 62.31% ->  70.00% ( +7.69%) |    162 ->    182 /    260
+     initial-letter        |   +60 (  +60 /   -0) | 18.56% ->  80.41% (+61.86%) |     18 ->     78 /     97
+     parsing               |   +18 (  +18 /   -0) | 75.47% ->  86.79% (+11.32%) |    120 ->    138 /    159
+     text-box-trim         |  +161 ( +195 /  -34) | 23.23% ->  62.59% (+39.36%) |     95 ->    256 /    409
+       parsing             |  +160 ( +160 /   -0) | 23.44% -> 100.00% (+76.56%) |     49 ->    209 /    209
-   css-lists               |    -1 (   +1 /   -2) | 69.54% ->  69.46% ( -0.09%) |    806 ->    805 /   1159
+   css-logical             |  +162 ( +162 /   -0) | 80.16% ->  92.24% (+12.08%) |   1075 ->   1237 /   1341
-   css-masking             |    -2 (   +0 /   -2) | 54.36% ->  54.33% ( -0.03%) |   3747 ->   3745 /   6893
-     clip-path             |    -2 (   +0 /   -2) | 54.48% ->  54.00% ( -0.48%) |    225 ->    223 /    413
+   css-multicol            |    +1 (   +1 /   -0) | 39.39% ->  39.44% ( +0.04%) |    884 ->    885 /   2244
+   css-overflow            |   +85 ( +102 /  -17) | 37.86% ->  42.69% ( +4.83%) |    666 ->    751 /   1759
+     line-clamp            |   +58 (  +64 /   -6) | 17.10% ->  35.81% (+18.71%) |     53 ->    111 /    310
+     parsing               |   +16 (  +16 /   -0) | 54.78% ->  58.91% ( +4.13%) |    212 ->    228 /    387
+   css-overscroll-behavior |   +11 (  +11 /   -0) | 75.53% ->  87.23% (+11.70%) |     71 ->     82 /     94
+     parsing               |   +11 (  +11 /   -0) | 84.51% -> 100.00% (+15.49%) |     60 ->     71 /     71
+   css-paint-api           |    +1 (   +1 /   -0) | 83.21% ->  83.94% ( +0.73%) |    114 ->    115 /    137
+   css-position            |    +2 (   +2 /   -0) | 66.09% ->  66.21% ( +0.12%) |   1146 ->   1148 /   1734
+     static-position       |    +2 (   +2 /   -0) | 16.67% ->  22.22% ( +5.56%) |      6 ->      8 /     36
+   css-pseudo              |   +85 (  +91 /   -6) | 17.53% ->  25.30% ( +7.76%) |    192 ->    277 /   1095
-     highlight-cascade     |    -1 (   +0 /   -1) | 19.55% ->  18.80% ( -0.75%) |     26 ->     25 /    133
+     parsing               |    +1 (   +1 /   -0) |  5.94% ->   6.29% ( +0.35%) |     17 ->     18 /    286
+   css-ruby                |   +35 (  +39 /   -4) | 36.84% ->  51.01% (+14.17%) |     91 ->    126 /    247
+     parsing               |    +9 (   +9 /   -0) | 66.67% ->  89.74% (+23.08%) |     26 ->     35 /     39
-   css-shapes              |   -12 (   +4 /  -16) |  9.24% ->   9.09% ( -0.15%) |    717 ->    705 /   7759
-     shape-outside         |   -12 (   +4 /  -16) |  8.72% ->   8.48% ( -0.24%) |    437 ->    425 /   5013
-       shape-box           |    -1 (   +0 /   -1) | 11.36% ->   9.09% ( -2.27%) |      5 ->      4 /     44
-       shape-image         |    -4 (   +1 /   -5) | 22.03% ->  15.25% ( -6.78%) |     13 ->      9 /     59
-         gradients         |    -1 (   +1 /   -2) | 16.67% ->  12.50% ( -4.17%) |      4 ->      3 /     24
-       supported-shapes    |    -7 (   +3 /  -10) | 14.62% ->   9.23% ( -5.38%) |     19 ->     12 /    130
+         circle            |    +1 (   +1 /   -0) |  5.00% ->   7.50% ( +2.50%) |      2 ->      3 /     40
+         ellipse           |    +2 (   +2 /   -0) |  5.13% ->  10.26% ( +5.13%) |      2 ->      4 /     39
-         inset             |    -3 (   +0 /   -3) | 19.05% ->   4.76% (-14.29%) |      4 ->      1 /     21
-         path              |    -1 (   +0 /   -1) | 50.00% ->  25.00% (-25.00%) |      2 ->      1 /      4
-         polygon           |    -5 (   +0 /   -5) | 28.57% ->   4.76% (-23.81%) |      6 ->      1 /     21
-         shape             |    -1 (   +0 /   -1) | 60.00% ->  40.00% (-20.00%) |      3 ->      2 /      5
+   css-sizing              |   +84 (  +86 /   -2) | 61.72% ->  63.02% ( +1.29%) |   4007 ->   4091 /   6492
+     stretch               |   +80 (  +80 /   -0) | 56.58% ->  64.27% ( +7.68%) |    589 ->    669 /   1041
+   css-tables              |    +1 (   +1 /   -0) | 52.77% ->  52.86% ( +0.09%) |    581 ->    582 /   1101
+     tentative             |    +1 (   +1 /   -0) | 28.69% ->  28.97% ( +0.28%) |    103 ->    104 /    359
+   css-text                |  +563 ( +587 /  -24) | 71.53% ->  80.49% ( +8.96%) |   4495 ->   5058 /   6284
+   css-text-decor          |  +170 ( +177 /   -7) | 66.06% ->  76.09% (+10.04%) |   1119 ->   1289 /   1694
+     animations            |   +70 (  +70 /   -0) | 70.55% ->  77.97% ( +7.42%) |    666 ->    736 /    944
+     parsing               |   +37 (  +37 /   -0) | 74.84% ->  86.62% (+11.78%) |    235 ->    272 /    314
+     text-shadow           |    +8 (   +8 /   -0) | 80.00% ->  96.00% (+16.00%) |     40 ->     48 /     50
+     animations            |  +188 ( +188 /   -0) | 43.25% ->  58.01% (+14.76%) |    551 ->    739 /   1274
+     bidi                  |    +2 (   +2 /   -0) | 25.00% ->  75.00% (+50.00%) |      1 ->      3 /      4
+     boundary-shaping      |    +3 (   +3 /   -0) | 20.00% ->  50.00% (+30.00%) |      2 ->      5 /     10
+     hanging-punctuation   |   +10 (  +10 /   -0) | 26.09% ->  69.57% (+43.48%) |      6 ->     16 /     23
+     hyphens               |   +19 (  +19 /   -0) | 29.51% ->  60.66% (+31.15%) |     18 ->     37 /     61
-     i18n                  |    -4 (   +0 /   -4) | 96.97% ->  96.73% ( -0.23%) |   1662 ->   1658 /   1714
+     letter-spacing        |    +7 (   +7 /   -0) | 22.22% ->  41.67% (+19.44%) |      8 ->     15 /     36
+     line-break            |    +4 (   +4 /   -0) | 91.76% ->  96.47% ( +4.71%) |     78 ->     82 /     85
+     line-breaking         |   +28 (  +28 /   -0) | 64.54% ->  84.40% (+19.86%) |     91 ->    119 /    141
+     overflow-wrap         |    +3 (   +3 /   -0) | 93.22% ->  98.31% ( +5.08%) |     55 ->     58 /     59
+     parsing               |  +142 ( +142 /   -0) | 59.22% ->  77.66% (+18.44%) |    456 ->    598 /    770
+     tab-size              |    +6 (   +6 /   -0) | 60.71% ->  82.14% (+21.43%) |     17 ->     23 /     28
+     text-align            |   +12 (  +14 /   -2) | 65.13% ->  69.73% ( +4.60%) |    170 ->    182 /    261
+     text-autospace        |    +4 (   +4 /   -0) | 60.61% ->  72.73% (+12.12%) |     20 ->     24 /     33
+     text-encoding         |    +1 (   +1 /   -0) | 88.89% -> 100.00% (+11.11%) |      8 ->      9 /      9
+     text-indent           |   +10 (  +10 /   -0) | 57.14% ->  92.86% (+35.71%) |     16 ->     26 /     28
+     text-justify          |    +2 (   +4 /   -2) | 78.85% ->  79.74% ( +0.88%) |    179 ->    181 /    227
+     text-spacing-trim     |   +39 (  +47 /   -8) | 28.26% ->  70.65% (+42.39%) |     26 ->     65 /     92
-     text-transform        |    -1 (   +1 /   -2) | 97.55% ->  97.20% ( -0.35%) |    279 ->    278 /    286
+     white-space           |   +71 (  +74 /   -3) | 78.97% ->  87.50% ( +8.53%) |    657 ->    728 /    832
+     word-break            |    +9 (  +10 /   -1) | 67.62% ->  76.19% ( +8.57%) |     71 ->     80 /    105
+     word-spacing          |    +1 (   +2 /   -1) | 76.19% ->  80.95% ( +4.76%) |     16 ->     17 /     21
-     writing-system        |    -1 (   +0 /   -1) | 40.00% ->  20.00% (-20.00%) |      2 ->      1 /      5
+   css-transforms          |    +1 (   +1 /   -0) | 65.57% ->  65.58% ( +0.02%) |   4153 ->   4154 /   6334
+   css-values              |   +14 (  +16 /   -2) | 54.85% ->  54.98% ( +0.13%) |   5774 ->   5788 /  10527
+   css-variables           |    +1 (   +1 /   -0) | 81.51% ->  81.65% ( +0.13%) |    604 ->    605 /    741
+   css-viewport            |    +1 (   +1 /   -0) | 74.58% ->  74.82% ( +0.24%) |    314 ->    315 /    421
+     zoom                  |    +1 (   +1 /   -0) | 75.18% ->  75.42% ( +0.24%) |    312 ->    313 /    415
+   css-writing-modes       |  +437 ( +449 /  -12) | 43.23% ->  72.66% (+29.43%) |    642 ->   1079 /   1485
+     astral-bidi           |    +2 (   +2 /   -0) | 50.00% -> 100.00% (+50.00%) |      2 ->      4 /      4
+     forms                 |   +20 (  +20 /   -0) | 25.35% ->  39.44% (+14.08%) |     36 ->     56 /    142
+     parsing               |    +4 (   +4 /   -0) | 90.48% -> 100.00% ( +9.52%) |     38 ->     42 /     42
!   cssom                   |    +0 (   +7 /   -7) | 79.06% ->  79.06% ( +0.00%) |   3429 ->   3429 /   4337
+   cssom-view              |   +18 (  +20 /   -2) | 41.80% ->  42.58% ( +0.79%) |    958 ->    976 /   2292
-   printing                |    -1 (   +1 /   -2) | 78.12% ->  75.00% ( -3.12%) |     25 ->     24 /     32
+   reference               |    +1 (   +1 /   -0) | 66.67% -> 100.00% (+33.33%) |      2 ->      3 /      3
+   selectors               |    +9 (   +9 /   -0) | 48.70% ->  48.88% ( +0.18%) |   2476 ->   2485 /   5084
+     old-tests             |    +5 (   +5 /   -0) | 90.25% ->  92.06% ( +1.81%) |    250 ->    255 /    277
- svg                       |    -2 (   +4 /   -6) | 38.22% ->  38.20% ( -0.01%) |   5970 ->   5968 /  15621
+   fonts                   |    +3 (   +3 /   -0) |  0.00% ->  75.00% (+75.00%) |      0 ->      3 /      4
-   styling                 |    -5 (   +1 /   -6) | 32.01% ->  30.91% ( -1.10%) |    145 ->    140 /    453
Full diff (2035 changed tests)
+ FAIL => PASS         [1/1]      +1  css/CSS2/abspos/hypothetical-inline-alone-on-second-line.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/abspos/static-inside-inline-001.html
- PASS => FAIL         [0/1]      -1  css/CSS2/abspos/static-inside-inline-002.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/abspos/static-inside-inline-003.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-005.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-006.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-007.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-010.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-002.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-005b.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-006b.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-007b.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-010b.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/bidi-text/bidi-011.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-003.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-006.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-009.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-010.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-011.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-012.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-013.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-014.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-015.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-019.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-020.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-021.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-022.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-023.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-024.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-028.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-029.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-030.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-031.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-032.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-033.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-037.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-038.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-039.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-040.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-041.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-box-model-042.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-breaking-002.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-glyph-mirroring-001.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-inline-001.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/bidi-inline-002.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/direction-applies-to-008.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/line-breaking-bidi-003.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/unicode-bidi-002.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/unicode-bidi-003.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/unicode-bidi-004.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/unicode-bidi-applies-to-008.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/bidi-text/unicode-bidi-applies-to-009.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/box/rtl-linebreak.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/cascade/cascade-009a.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/cascade/cascade-009b.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/cascade/inherit-computed-001.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/cascade/specificity-005.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/cascade/specificity-006.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/cascade/specificity-008.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c25-pseudo-elmnt-000.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c414-flt-fit-001.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c547-indent-001.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c548-leadin-000.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5502-imrgn-r-000.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5502-imrgn-r-002.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5502-imrgn-r-003.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5502-imrgn-r-004.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5502-imrgn-r-005.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5502-imrgn-r-006.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5504-imrgn-l-000.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5504-imrgn-l-001.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5504-imrgn-l-002.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5504-imrgn-l-003.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5504-imrgn-l-004.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5504-imrgn-l-006.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5505-imrgn-000.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5506-ipadn-t-000.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5506-ipadn-t-002.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5507-ipadn-r-000.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5507-ipadn-r-002.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5507-ipadn-r-003.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5507-ipadn-r-004.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5508-ipadn-b-002.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5509-ipadn-l-000.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5509-ipadn-l-001.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5509-ipadn-l-002.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5509-ipadn-l-003.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5509-ipadn-l-004.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5510-ipadn-000.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/css1/c5525-fltwidth-001.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/floats-clear/floats-031.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/floats-clear/floats-132.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/floats-clear/floats-153.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/floats-clear/floats-clear-multicol-000.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/floats-clear/floats-clear-multicol-001.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/floats-clear/floats-clear-multicol-balancing-000.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/floats-clear/floats-clear-multicol-balancing-001.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/floats/float-no-content-beside-001.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/floats/float-nowrap-1.html
- PASS => FAIL         [0/1]      -1  css/CSS2/floats/float-nowrap-7.html
- PASS => FAIL         [0/1]      -1  css/CSS2/floats/float-nowrap-8.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/floats/floats-line-wrap-shifted-001.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/floats/floats-placement-004.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/floats/floats-placement-vertical-003.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/floats/floats-placement-vertical-004.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/floats/floats-wrap-top-below-inline-001l.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/floats/floats-wrap-top-below-inline-001r.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/floats/floats-wrap-top-below-inline-002l.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/floats/floats-wrap-top-below-inline-002r.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/floats/floats-wrap-top-below-inline-003l.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/floats/floats-wrap-top-below-inline-003r.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/floats/floats-zero-height-wrap-001.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/floats/intrinsic-size-float-and-line.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/fonts/font-family-applies-to-005.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/fonts/font-family-invalid-characters-001.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/fonts/font-family-invalid-characters-003.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/fonts/font-family-rule-001.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/generated-content/content-082.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/generated-content/content-175.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/generated-content/content-white-space-002.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/linebox/baseline-block-with-overflow-001.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/linebox/border-padding-bleed-001.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/linebox/border-padding-bleed-002.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/linebox/border-padding-bleed-003.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/linebox/empty-inline-003.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/linebox/inline-formatting-context-003.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/linebox/inline-formatting-context-005.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/linebox/inline-formatting-context-007.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/linebox/inline-formatting-context-023.xht
! FAIL => FAIL        [9/13]   +3/-2  css/CSS2/linebox/inline-negative-margin-001.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/linebox/line-height-125.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/linebox/vertical-align-117a.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/linebox/vertical-align-118a.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/linebox/vertical-align-122.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/linebox/vertical-align-negative-leading-001.html
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-applies-to-001.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-applies-to-002.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-applies-to-003.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-applies-to-004.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-applies-to-007.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-applies-to-008.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-applies-to-009.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-applies-to-010.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-applies-to-012.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-applies-to-013.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-applies-to-014.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-applies-to-016.html
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-image-applies-to-001.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-image-applies-to-002.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-image-applies-to-003.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-image-applies-to-004.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-image-applies-to-005.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-image-applies-to-006.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-image-applies-to-007.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-image-applies-to-008.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-image-applies-to-009.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-image-applies-to-010.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-image-applies-to-012.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-image-applies-to-013.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-image-applies-to-014.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-image-applies-to-016.html
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-type-applies-to-001.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-type-applies-to-002.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-type-applies-to-003.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-type-applies-to-004.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-type-applies-to-007.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-type-applies-to-008.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-type-applies-to-009.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-type-applies-to-010.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-type-applies-to-012.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-type-applies-to-013.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-type-applies-to-014.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/lists/list-style-type-applies-to-016.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/margin-padding-clear/margin-bottom-applies-to-008.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/margin-padding-clear/margin-collapse-001.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/margin-padding-clear/margin-inline-001.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/margin-padding-clear/margin-top-applies-to-008.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/normal-flow/block-in-inline-first-line-001.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/normal-flow/block-in-inline-first-line-002.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/normal-flow/inline-box-border-line-break.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/normal-flow/inline-box-padding-line-break.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/normal-flow/inline-non-replaced-height-002.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/normal-flow/inline-non-replaced-height-003.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/normal-flow/inline-replaced-width-012.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/normal-flow/inline-replaced-width-013.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/normal-flow/inline-replaced-width-015.xht
- PASS => FAIL         [0/1]      -1  css/CSS2/normal-flow/inline-table-width-002b.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/normal-flow/inlines-016.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/normal-flow/inlines-017.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/positioning/abspos-block-level-001.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/positioning/line-break-after-leading-float.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/positioning/position-relative-033.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/sec5/first-line-000.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-001.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-abspos.html
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-nested-001.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-nested-002.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-nested-003.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-nested-004.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-nested-005.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-nested-006.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-nested-007.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-001.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-002.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-003.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-004.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-005.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-006.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-007.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-008.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-009.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-010.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-012.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-013.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-014.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-015.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-016.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-017.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-018.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-019.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-020.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-021.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-022.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-023.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-024.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-025.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-026.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-027.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-028.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-029.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-030.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-031.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-032.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-033.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-034.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-035.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-036.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-037.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-038.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-039.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-040.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-041.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-042.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-043.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-044.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-045.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-046.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-047.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-049.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-050.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-051.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-052.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-053.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-054.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-055.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-056.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-057.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-058.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-059.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-060.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-061.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-062.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-063.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-064.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-065.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-066.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-067.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-068.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-069.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-070.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-071.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-072.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-073.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-074.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-075.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-076.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-077.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-078.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-079.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-080.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-081.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-082.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-083.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-084.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-085.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-086.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-087.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-088.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-089.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-090.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-091.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-092.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-093.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-094.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-095.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-096.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-097.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-098.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-099.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-100.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-101.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-102.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-103.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-104.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-105.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-106.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-107.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-108.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-109.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-110.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-111.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-112.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-113.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-115.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-116.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-117.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-118.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-119.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-120.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-121.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-122.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-123.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-124.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-125.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-126.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-127.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-128.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-129.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-130.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-131.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-132.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-133.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-134.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-135.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-136.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-137.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-138.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-139.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-140.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-141.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-142.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-143.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-144.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-145.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-146.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-147.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-148.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-149.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-150.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-151.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-152.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-153.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-154.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-155.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-156.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-157.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-158.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-159.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-160.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-161.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-162.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-163.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-164.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-165.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-166.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-167.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-168.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-169.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-170.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-171.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-172.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-173.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-174.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-175.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-176.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-177.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-178.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-179.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-180.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-181.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-182.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-183.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-185.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-186.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-187.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-188.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-189.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-190.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-191.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-192.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-193.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-194.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-195.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-196.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-197.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-198.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-199.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-200.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-201.xht
+ FAIL => PASS         [1/1]      +1  css/CSS2/selectors/first-letter-punctuation-202.xht
# ... and 1635 more (see the workflow logs)

Generated by the WPT workflow.

@staging-devin-ai-integration

Copy link
Copy Markdown
Contributor

I'll fix CI failures and address comments from users with write access that start with 'Devin'.

  • Disable automatic comment, CI, and merge conflict monitoring

@staging-devin-ai-integration
staging-devin-ai-integration Bot force-pushed the devin/1791548469-winkin-text-backends branch 4 times, most recently from a4fcea2 to 9ea35f4 Compare October 9, 2026 21:22
@nicoburns nicoburns changed the title Text backend boundary with a winkin backend Add winkin text backend Oct 9, 2026
nicoburns and others added 23 commits October 10, 2026 14:32
… now

The text backend commits that follow move and replace layout/inline.rs and the
Parley TextLayout, which #1167 changed. Its blitz-dom part is taken out here so
that they apply as written, and is put back for the shared inline layout once
they have. The stylo_taffy part stays.
The fork relaxes the gates on properties stylo's servo build declines to
compute: sideways writing modes, ruby, text-emphasis,
text-combine-upright, line-clamp, ::first-line and the other text
properties winkin lays out. They are gated because servo's layout does
not implement them, not because they need gecko. It also parses
hanging-punctuation, text-spacing-trim, text-combine-upright's digits,
text-indent's hanging and each-line, and initial-letter's drop and
raise.

All eleven crates of the stylo family are patched together, so that
Blitz sees one Atom type.

The fork computes every font-variant-caps value and
white-space-collapse: preserve-spaces, so Parley maps them too.
The `winkin` feature of blitz-dom and blitz-paint lays out and paints
every inline formatting context with winkin in place of Parley. Text
inputs, selection and hit testing still use Parley, and with the feature
off nothing changes.

blitz-dom builds each inline formatting context's content from the box
tree and its computed styles (text_winkin), keeps the layout on the
node's TextLayout and breaks it in place, and sizes and places the
block, its atomic inlines, floats and the static positions of its
out-of-flow boxes from winkin's lines (layout/inline_winkin.rs). It
handles vertical and sideways writing modes, ruby, emphasis marks,
text-combine-upright, ::first-line, ::first-letter and initial-letter,
line clamping, hanging punctuation, text-autospace and the rest of the
text properties the stylo fork computes. Floats placed by Taffy narrow
the lines, and floats the text reaches are placed as the lines are
broken. @font-face faces are added to winkin's font collection with
every descriptor, and font-variant-alternates is resolved through
@font-feature-values. Inline elements answer their client rects from
winkin's box fragments.

blitz-paint paints winkin's runs, inline box backgrounds and borders,
decorations (with Blitz's decoration painting, including those a
block's ancestors propagate to its lines), emphasis marks, text
shadows and text-overflow marks (text_winkin.rs).

winkin and fontwich are path dependencies on the ifc workspace for now.
HTML's auto directionality: an element with dir=auto takes the direction
of the first character of its text with a strong bidi class, skipping
text inside bdi, script, style and textarea and inside descendants with
a valid dir of their own. With no strong character the inherited
direction stands. Blitz sets it as a presentational hint for
`direction`, since no selector can express it. Before, dir=auto kept
its isolation but always took the inherited direction.
The rules for these used Gecko's :-moz-has-dir-attr and
:-moz-dir-attr-like-auto, which Blitz's selector parser does not know,
so they were dropped. bdo is now isolate-override with or without a dir
attribute, "bdo[dir]" outweighing the "[dir]" rules as the Gecko
selector did, and pre and textarea with dir=auto are plaintext, by the
HTML spec's selector.
As CSS Text 4's default style sheet does.
The screenshot example renders the document's own height (up to 4000
CSS pixels) rather than at least the viewport's, and writes the PNG to
the path given as its third argument, where there is one.
It lays out and paints document text with winkin rather than Parley, so the suites can be run against either engine.
…atic positions from it

An absolutely positioned box in inline content goes to winkin through
LayoutBuilder::absolute, with its original display, in place of an empty
box that took part in line breaking. The static-position rectangle of
each OofCandidate comes from Layout::static_positions: an inline-level
box's is zero-wide where its anchor stands and spans its line box from
the top, facing the way the anchor's bidi level reads, as in Chrome; a
block-level box's spans the content box at the line's top, or below the
line where in-flow content comes before it. A box on no line, in an empty
block or after a final <br>, gets a static position too. In a vertical-rl
block the box's block-start edge is its right.

The painter no longer passes over placeholder boxes, since there are none.
Every document built its own fontwich::Collection::system(), which lists
the platform's fonts by reading every installed font file, and starts with
cold font and fallback caches. That cost about 200ms per document.

DocumentConfig::winkin_fonts now takes the collection, as font_ctx takes
Parley's. Clones share the layers, the fonts loaded through them and
fallback's answers, so an embedder lists the fonts once and hands each
document a clone. An iframe's document is handed its parent's. Without
one, a document lists the platform's fonts itself, through the new
text_winkin::system_fonts. blitz-dom re-exports fontwich for embedders.
The runner already hands every document a clone of one Parley
FontContext; it now does the same with winkin's font collection. The css
suites run in about 50s with the winkin feature, down from about 400s,
with the same results.
… pages

A tab's DocumentLoader lists winkin's fonts once, next to its Parley
FontContext, and hands a clone to each page, error page and view-source
document it builds. make_doc_config becomes a DocumentLoader method, since
everything but the base URL and abort signal came from the loader.
…ackend

compute_inline_layout_inner now resolves the container's sizes into a Frame
and hands it to compute_inline_layout_parley or compute_inline_layout_winkin.
The Parley half is no longer unreachable code behind a blanket allow, and
winkin no longer runs Parley's inline box measuring loop before its own.
winkin builds its own content and never breaks the Parley layout into lines,
so building and shaping it was unused work. The ids of the atomic inlines and
floats winkin measures now come from the inline root's layout children.
compute_inline_layout_winkin is a LayoutPassState method, as the
Parley path is, and builds winkin's content through the document it
borrows.
innerText walks the inline layout's content in logical order through
TextLayout::logical_content, and Selection maps a text node's offset
through TextLayout::source_offset where the backend maps source text,
so both work under winkin, which builds no Parley layout. Under Parley
they read the same clusters and follow the same collapsing as before.
…iting-mode

winkin turns an inline formatting context's lines onto the page itself,
so with the writing-mode feature such a box runs in physical axes
(Node::layout_frame_wm): Taffy's frames meet it only at its edges, where
a vertical parent's inline size reaches it transposed, and its atomic
inlines, floats and the boxes around it are read in physical axes.

With the frames, a vertical block finds definite room along its lines
far more often. A block in a block container whose lines run the same
way stretches into it; any other box fits its content into it, and
answers an intrinsic height query with its lines' intrinsic length. An
orthogonal flow fits into its parent's definite height, or else into
the initial containing block, which also caps a parent's max-height.
A link to Node::style, which does not exist, now names Node::primary_styles; offset_parent's docs no longer link to a private method; a bare URL becomes an automatic link, and two links drop a redundant target.
blitz-dom gains a text module that holds what Blitz asks of a text
backend: the fonts documents lay text out with (TextFonts), a document's
text context (DocumentText), an inline formatting context's layout and
readback (InlineText, InlineLayoutEngine), form-control editing
(EditableText, EditEngine, with an Edit enum of editing operations) and
outside list markers (MarkerEngine). Aliases name the backend's types:
FontContext, TextLayout, TextEditor, MarkerLayout and FaceDescriptors.
text::parley implements them with the code that was spread through the
crate: building the Parley layout, breaking and placing its lines,
PlainEditor, the font metrics provider, @font-face registration and the
Stylo conversions.

The rest of Blitz now goes through the boundary: the document keeps one
TextContext; deferred construction and inline layout call the backend;
the key, IME and pointer handling of text inputs speak Edit; hit testing,
selected text, innerText, Selection offsets and inline fragment rects
read InlineText. blitz-paint's text painting splits into the decorations
any backend's lines share and a Parley module behind paint_inline_layout,
paint_text_input and paint_marker, selected by blitz-dom's
cfg_text_backend! macro.

Behaviour is unchanged.

This is the fork's copy of the commit: it also carries the fork's winkin
hooks, so the winkin feature builds here. The fork's winkin feature lays
text out with winkin inside Parley's TextLayout, and those hooks move
behind the boundary with the Parley path: the Parley TextLayout carries
the winkin layout, skips the Parley build under winkin, and answers the
InlineText readback (text length, selected text, logical content, source
offsets, hit testing, selection and fragment rects) from winkin, with the
helpers that did this in node/text.rs moved to text_winkin.
InlineLayoutEngine says whether the backend sets vertical lines itself
(SETS_WRITING_MODES), which an inline root's layout writing mode reads.
InlineText::logical_content yields one item for each stretch of text one
node holds, as a byte range of text(), rather than one item per cluster
with a copy of its text, and innerText pushes each stretch at once. The
walk reads the cluster's style only where its style index changes, and
innerText of a long paragraph of spans takes less time than upstream's
loop over the clusters.

InlineText::selected_text returns the pieces of the laid-out text it is
made of, which get_selected_text appends without copying them first.
selection_rects becomes for_each_selection_rect, which hands each
rectangle to a callback, and blitz-paint's selection highlight goes
through it. hit_test takes the point and whether it must be exact, and
no longer the content box size and scale, which hit testing computed on
every call and Parley ignored; the winkin path records the content box
it places lines in, which its hit testing and selection rectangles map
through. first_baseline gives the first line's
baseline in CSS pixels, which places outside list markers.

EditableText::metrics gives the laid-out text's size and scale at once,
in place of size and scale, which each looked up the layout, and text
borrows the text unless an input method is composing. The setters of
EditEngine only record what changes, and the text is laid out once, by
the next refresh or edit: a single-line input at construction, a
textarea in the layout pass once its width is known, and a value set
from outside at once. Construction and scale changes no longer lay a
textarea out twice, and setting the same width again lays nothing out.
selectLine: and selectParagraph: are one Edit::Select of two motions.

FaceDescriptors holds parlance's FontWeight and FontStyle, the font value
types fontique shares, as the rule's lowest and highest values, read
from the @font-face rule in one place; Parley registers the face by the
lowest, with no conversion. blitz-dom takes parlance from Parley's
revision, which the lockfile already holds. TextFonts::add_fonts takes
Bytes, so fonts that need no decoding are not copied.

cfg_text_backend! takes an arm for any backend and expands the one in
use, so callers may list backends this build does not know. TextLayout
keeps only its Parley layout public; the never-read content widths cache
goes, with invalidate_content_widths. FontContext's documentation says
it is the backend's own type. The thin forwarding methods are #[inline].
…t backend

The walk that turns an inline formatting context's DOM into a text
backend's content moves out of the Parley backend into
layout::inline::build, and drives a new InlineBuilder trait: spans
(plain, display: contents, and ruby containers and annotations where the
backend sets ruby), text, atomic inlines, floats, absolutely positioned
boxes, forced line breaks, <wbr> opportunities and an inside list marker.
Blitz decides what each node is: hidden inputs, display: none, the
pseudo-elements, <br>, the elements that are atomic inlines, and
text-transform, which it applies for a backend that does not
(TRANSFORMS_TEXT), forcing word boundaries at atomic inlines and line
breaks as before. Parley's TreeBuilder implements the trait.

An element with no computed style is now skipped rather than laid out
as an unstyled inline; behaviour is otherwise unchanged.
dfrg and others added 29 commits October 10, 2026 14:32
PlainEditor marks the composing (preedit) range of an <input> or
<textarea> with an underline in its layout's styles, but the text painter
drew only CSS decorations, so composing text was not underlined. The
painter now draws it, solid, with the auto thickness and in the text's
colour, once per line across the composing runs.
With the winkin feature, text::winkin is the text backend in place of
text::parley, implementing the same traits: its TextLayout holds the
inline formatting context winkin builds, breaks and reads back; its
TextContext holds the fonts and the context winkin lays out in, with
@font-face faces in a document layer; FontContext wraps the fontwich
collection documents are handed. The Parley types no longer carry winkin
inside them, and the document builds no Parley state under winkin.

winkin's content is built by Blitz's walk of the DOM, through its own
InlineBuilder: spans with their ::first-line restyling, ruby containers
and annotations, the block's ::first-letter, emphasis marks, inside list
markers, atomic inlines and floats at the sizes Taffy measured, the
anchors of absolutely positioned boxes, and <br> with its clear. It sets
text-transform and white space itself, from the styles, so the walk
hands it each text node's text as the DOM holds it. The content is
built when it is laid out, once its boxes are measured, and again where
they measure differently.

Its lines are laid out by Blitz's inline driver, which takes what was
winkin's own copy of it: the driver measures the boxes, sizes the block,
keeps the lines clear of floats through the same room, places the boxes
and static positions, and reads winkin through the layout surface, which
grows what winkin needs: the room over the first line that ruby
annotations and emphasis marks may take, lent by the block before
(READS_ROOM_ABOVE, room_below), the content box's end that line-clamp
keeps the lines within, rewinding the floats a trial break placed,
lines that run down the page (line_flow, place_on_page, with the
driver sizing such a block in physical terms), static positions that
face the way their bidi level does, floats without a node of their own
(the initial letter), and position: relative on the inline boxes around
an atomic inline (inline_shift). Where winkin's copy differed from the
driver, the driver's rule holds: a display: contents element with
position: absolute is contents; the static positions of absolutely
positioned boxes move with align-content; only position: relative
offsets an atomic inline by its insets; the floats an inline
formatting context places hand on the absolutely positioned boxes they
hold; and the content's scrollable overflow reaches as far as its
longest line.

What winkin lacked before is filled in on its side of the boundary: Stylo's
font metrics (ex, ch, cap, ic) come from the fontwich collection the text
is laid out with, outside list markers are laid out and painted by winkin,
and <input> and <textarea> get a basic editor: the value laid out as plain
text, with a caret and a selection moved by winkin's selection motions.

DocumentConfig::winkin_fonts goes: winkin's fonts are DocumentConfig's
font_ctx, as Parley's are. blitz-paint paints winkin through the same
paint_inline_layout, paint_text_input and paint_marker functions.

FaceDescriptors gains the other descriptors fontwich declares a face by:
the width range, unicode-range, the feature and variation settings,
size-adjust and the metric overrides, which Parley leaves aside. winkin
converts them, with the weight and style, to fontwich's own.
…efault

blitz-dom's winkin feature now sits beside its parley feature as a text
backend of its own: build.rs reads the two into one cfg, text_parley or
text_winkin. With both features Parley is used, and with neither
blitz-dom stops with its compile error. system-fonts maps to fontwich's
system fonts as well as Parley's, and complex-scripts to winkin's ICU
dictionaries as well as Parley's complex scripts.

blitz-paint no longer depends on winkin itself: it takes winkin, as it
takes Parley, from blitz-dom, through cfg_text_backend! and blitz-dom's
re-exports, so the two never disagree. Every crate that forwards parley
forwards winkin too, with parley in its defaults: blitz, blitz-shell,
blitz-html, blitz-paint, blitz-vibey-script, blitz-test-harness,
dioxus-native, dioxus-native-dom, the browser, readme, the WPT runner,
the tests and the examples. The CI job checks the default build, winkin
alone, both, and that neither fails to compile.
A textarea's text was set as preserved white space, wrapping, whatever its
style said. Under break-spaces its spaces now wrap as break-spaces has
them, and it wraps as its text-wrap-mode does; only the collapsing modes,
which would move offsets away from the value's, are still preserved.
Text an input method is composing is laid out in a winkin <input> or
<textarea> in place of the selection, in a box of its own keyed by the
control's node with COMPOSE_KEY set, which the renderer underlines. The
selection follows the input method's cursor inside it, and the caret is
hidden where the input method gives none. Any other edit, a cancelled
composition and the input method being disabled return the text and the
selection to what they were before composing began; a commit then
replaces that selection. raw_text holds the composing text and text does
not, as with Parley; the selected text is none while composing.

The editor keeps the history the ifc editor example keeps: each change
to the text records the text and selection before it, up to a hundred,
and a new change drops what could be redone. Edit gains Undo and Redo,
which the key handling of text inputs sends for Ctrl+Z (Cmd+Z on macOS),
Ctrl+Shift+Z, Ctrl+Y off macOS, and the Undo and Redo keys, firing an
input event where the text changed. Parley's PlainEditor keeps no
history, so they do nothing under Parley. A value set from outside the
editor drops the history and any composition.

Deleting at the text's ends changes nothing and records nothing, and an
undone deletion returns the caret where it was.
Typing, line breaks, deletion by character, grapheme and word, word
motion, selection, select-all, copy, cut and paste through the shell's
clipboard, undo and redo, and composing text committed, cancelled and
composed over a selection, driven through key, IME and clipboard events.
Where the backends differ, each is held to its own: winkin's editor
undoes and Parley's does not, backspace after a combining mark takes the
grapheme under winkin and the mark under Parley, and only winkin returns
to a selection a cancelled composition replaced. Under winkin alone, the
composing text is checked to be laid out as one underlined box.
The inline driver measures the atomic inlines of an intrinsic
inline-size pass along the line only, with no block size or baseline,
so winkin found them changed and built its content again for every
such pass, and again for the next pass that sets the lines.

Content built with boxes as long now serves an inline-size pass, all
its intrinsic inline sizes depend on. Where it is built again there,
each box keeps the block size and baseline it was last built with,
which the next pass that sets the lines checks, and builds again with
where they differ.
The containing block's inline size is the basis winkin's styles resolve
percentage margins and padding of inline boxes against, and the build
was keyed on it. Taffy's intrinsic passes run with an indefinite width
(basis 0) and its layout pass with a definite one, so every inline
formatting context was built again for the layout pass even where no
percentage read the basis.

The build now records whether any percentage was resolved against the
basis, and keys on it only then. On a page of 510 nested inline-blocks
holding block children this cuts winkin builds per resolve from 4133 to
1654.
An intrinsic inline-size pass measures atomic inlines along the line
only, so content first built there has no block sizes or baselines for
them, and the pass that sets the lines found them changed and built the
content again. Where only atomic inlines' block sizes and baselines
differ, the content now takes them through winkin's
Layout::set_atomic_sizes, in one call for every box whose size changed,
which measures their extents again and keeps the analysis, fonts and
shaping. Anything else, and anything winkin refuses, still builds again;
a refusal changes nothing.

With the basis change before it, every inline formatting context on a
page of 510 nested inline-blocks is now built once per resolve.
Stylo renames the by-value longhand getters to `slow_clone_*`, takes a
mutable `ParserContext` in `parse_into` and `SupportsCondition::eval`,
renames `OpaqueElement::from_non_null_ptr` to `from_ptr`, drops the
`layout.grid.enabled` pref and the legacy masonry track list, adds
`display: grid-lanes`, computes shape() axis positions to
length-percentages, gives `mask-origin` the `background-origin` type,
names `-moz-none` text decorations `None` and stores font settings in a
`ThinVec`.
Stylo now quotes a font-family name only where it would not parse back as identifiers, as the CSSWG resolved and the font-family parsing WPTs expect.
Takes winkin and fontwich at cc23131, which adds text security.
Stylo's initial font-family is serif, which winkin resolved through the
serif setting. Chrome sets text with no font-family in the standard font,
and for Japanese, Chinese and Korean the two differ: on Windows lang=ja
takes Noto Sans JP as standard and Noto Serif JP as serif. Noto Serif JP
kerns katakana pairs such as テス, so emphasis tests whose ruby reference
splits the text drifted by 0.64px.

The initial value is told apart by its shared list: a serif a style sheet
names is parsed into a list of its own. With no family, winkin goes
straight to the language's standard font.
Both backends lay it out as word-break: normal with overflow-wrap: anywhere.
…nline layout

main computes, in layout/inline.rs, the out-of-flow positions an inline span
is a containing block for, and stores them on the Parley TextLayout. The text
backend commits replace both, so the same is done in layout/inline/lines.rs as
the inline boxes are placed, and each backend's TextLayout holds the flags,
which Node::layout_style reads as on main.
@staging-devin-ai-integration
staging-devin-ai-integration Bot force-pushed the devin/1791548469-winkin-text-backends branch from 7250db5 to b26a49a Compare October 10, 2026 14:33

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants