summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/mathml
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:43:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:43:14 +0000
commit8dd16259287f58f9273002717ec4d27e97127719 (patch)
tree3863e62a53829a84037444beab3abd4ed9dfc7d0 /testing/web-platform/tests/mathml
parentReleasing progress-linux version 126.0.1-1~progress7.99u1. (diff)
downloadfirefox-8dd16259287f58f9273002717ec4d27e97127719.tar.xz
firefox-8dd16259287f58f9273002717ec4d27e97127719.zip
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/mathml')
-rw-r--r--testing/web-platform/tests/mathml/presentation-markup/operators/mo-axis-height-1.html169
-rw-r--r--testing/web-platform/tests/mathml/presentation-markup/operators/mo-minsize-maxsize-001.html60
-rw-r--r--testing/web-platform/tests/mathml/presentation-markup/operators/mo-stretch-properties-dynamic-001.html3
-rw-r--r--testing/web-platform/tests/mathml/presentation-markup/operators/size-and-position-of-stretchy-fences-with-default-font-001.html115
-rw-r--r--testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-001-ref.html16
-rw-r--r--testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-001.html23
-rw-r--r--testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-002-ref.html30
-rw-r--r--testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-002.html33
-rw-r--r--testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-003-ref.html31
-rw-r--r--testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-003.html38
-rw-r--r--testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-004-ref.html19
-rw-r--r--testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-004.html22
-rwxr-xr-xtesting/web-platform/tests/mathml/tools/stretchy.py11
13 files changed, 536 insertions, 34 deletions
diff --git a/testing/web-platform/tests/mathml/presentation-markup/operators/mo-axis-height-1.html b/testing/web-platform/tests/mathml/presentation-markup/operators/mo-axis-height-1.html
index 6de6284188..10ea4dcffc 100644
--- a/testing/web-platform/tests/mathml/presentation-markup/operators/mo-axis-height-1.html
+++ b/testing/web-platform/tests/mathml/presentation-markup/operators/mo-axis-height-1.html
@@ -29,24 +29,100 @@
window.addEventListener("load", () => { loadAllFonts().then(runTests); });
function runTests() {
+ const AxisHeight = 5000 * emToPx;
+
test(function() {
- var v1 = 5000 * emToPx;
var moMiddle = (getBox("mo1").bottom + getBox("mo1").top) / 2;
assert_approx_equals(getBox("mo1").height,
14000 * emToPx, epsilon, "mo: size");
assert_approx_equals(getBox("baseline1").bottom - moMiddle,
- v1, epsilon, "mo: axis height");
- }, "AxisHeight (size variant)");
+ AxisHeight, epsilon, "mo: axis height");
+ }, "symmetric stretching with respect to the math axis (size variant)");
test(function() {
- var v1 = 5000 * emToPx;
var moMiddle = (getBox("mo2").bottom + getBox("mo2").top) / 2;
assert_approx_equals(getBox("mo2").height,
- 2 * (getBox("target2").height - v1),
+ 2 * (getBox("target2").height - AxisHeight),
epsilon, "mo: size");
assert_approx_equals(getBox("baseline2").bottom - moMiddle,
- v1, epsilon, "mo: axis height");
- }, "AxisHeight (glyph assembly)");
+ AxisHeight, epsilon, "mo: axis height");
+ }, "symmetric stretching with respect to the math axis (glyph assembly)");
+
+ test(function() {
+ const minsize = 14000 * emToPx;
+ const Tascent = minsize / 2 + AxisHeight;
+ const Tdescent = minsize - Tascent;
+ assert_approx_equals(getBox("baseline3").bottom - getBox("mo3").top, Tascent, epsilon, "mo ascent");
+ assert_approx_equals(getBox("mo3").bottom - getBox("baseline3").bottom, Tdescent, epsilon, "mo descent");
+ }, "Tascent = Tdescent = 0, minsize = 14em");
+
+ test(function() {
+ const minsize = 14000 * emToPx;
+ var Tascent = getBox("baseline4").bottom - getBox("target4").top;
+ assert_greater_than(Tascent, AxisHeight);
+ var Tdescent = getBox("target4").bottom - getBox("baseline4").bottom;
+ const T = Tascent + Tdescent;
+ Tascent = Math.max(0, Tascent - AxisHeight) * minsize / T + AxisHeight;
+ Tdescent = minsize - Tascent;
+ assert_approx_equals(getBox("baseline4").bottom - getBox("mo4").top, Tascent, epsilon, "mo ascent");
+ assert_approx_equals(getBox("mo4").bottom - getBox("baseline4").bottom, Tdescent, epsilon, "mo descent");
+ }, "Tascent = 6em > AxisHeight, Tdescent = 1em, symmetric = false, minsize = 14em");
+
+ test(function() {
+ const minsize = 14000 * emToPx;
+ var Tascent = getBox("baseline5").bottom - getBox("target5").top;
+ assert_less_than(Tascent, AxisHeight);
+ var Tdescent = getBox("target5").bottom - getBox("baseline5").bottom;
+ const T = Tascent + Tdescent;
+ Tascent = Math.max(0, Tascent - AxisHeight) * minsize / T + AxisHeight;
+ Tdescent = minsize - Tascent;
+ assert_approx_equals(getBox("baseline5").bottom - getBox("mo5").top, Tascent, epsilon, "mo ascent");
+ assert_approx_equals(getBox("mo5").bottom - getBox("baseline5").bottom, Tdescent, epsilon, "mo descent");
+ }, "Tascent = 4em < AxisHeight, Tdescent = 3em, symmetric = false, minsize = 14em");
+
+ test(function() {
+ const maxsize = 14000 * emToPx;
+ var Tascent = getBox("baseline6").bottom - getBox("target6").top;
+ assert_greater_than(Tascent, AxisHeight);
+ var Tdescent = getBox("target6").bottom - getBox("baseline6").bottom;
+ const T = Tascent + Tdescent;
+ Tascent = Math.max(0, Tascent - AxisHeight) * maxsize / T + AxisHeight;
+ Tdescent = maxsize - Tascent;
+ assert_approx_equals(getBox("baseline6").bottom - getBox("mo6").top, Tascent, epsilon, "mo ascent");
+ assert_approx_equals(getBox("mo6").bottom - getBox("baseline6").bottom, Tdescent, epsilon, "mo descent");
+ }, "Tascent = 6em > AxisHeight, Tdescent = 22em, symmetric = false, maxsize = 14em");
+
+ test(function() {
+ const maxsize = 14000 * emToPx;
+ var Tascent = getBox("baseline7").bottom - getBox("target7").top;
+ assert_less_than(Tascent, AxisHeight);
+ var Tdescent = getBox("target7").bottom - getBox("baseline7").bottom;
+ var T = Tascent + Tdescent;
+ Tascent = Math.max(0, Tascent - AxisHeight) * maxsize / T + AxisHeight;
+ Tdescent = maxsize - Tascent;
+ assert_approx_equals(getBox("baseline7").bottom - getBox("mo7").top, Tascent, epsilon, "mo ascent");
+ assert_approx_equals(getBox("mo7").bottom - getBox("baseline7").bottom, Tdescent, epsilon, "mo descent");
+ }, "Tascent = 4em < AxisHeight, Tdescent = 24em, symmetric = false, maxsize = 14em");
+
+ test(function() {
+ const minsize = 14000 * emToPx;
+ const Uascent = getBox("baseline8").bottom - getBox("target8").top;
+ const Udescent = getBox("target8").bottom - getBox("baseline8").bottom;
+ assert_less_than(2 * Math.max(Uascent - AxisHeight, Udescent + AxisHeight), minsize, "Sascent + Sdescent < minsize");
+ assert_approx_equals(getBox("mo8").height, minsize, epsilon, "mo size");
+ const MathAxis = getBox("baseline8").bottom - AxisHeight;
+ assert_approx_equals(MathAxis - getBox("mo8").top, getBox("mo8").bottom - MathAxis, epsilon, "mo is symmetric");
+ }, "symmetric stretching with respect to the math axis (minsize = 14em)");
+
+ test(function() {
+ const maxsize = 14000 * emToPx;
+ const Uascent = getBox("baseline9").bottom - getBox("target9").top;
+ const Udescent = getBox("target9").bottom - getBox("baseline9").bottom;
+ assert_greater_than(2 * Math.max(Uascent - AxisHeight, Udescent + AxisHeight), maxsize, "Sascent + Sdescent > maxsize");
+ assert_approx_equals(getBox("mo9").height, maxsize, epsilon, "mo size");
+ const MathAxis = getBox("baseline9").bottom - AxisHeight;
+ assert_approx_equals(MathAxis - getBox("mo9").top, getBox("mo9").bottom - MathAxis, epsilon, "mo is symmetric");
+ }, "symmetric stretching with respect to the math axis (maxsize = 14em)");
done();
}
@@ -56,20 +132,87 @@
<div id="log"></div>
<p>
<math style="font-family: axisheight5000-verticalarrow14000;">
+ <mspace id="baseline1" style="background: blue" width="50px" height="1px"/>
+ <mpadded voffset="50px"><mspace style="background: cyan" width="50px" height="1px"/></mpadded>
<mrow>
- <mspace id="baseline1" style="background: blue" width="50px" height="1px"/>
- <mpadded voffset="50px"><mspace style="background: cyan" width="50px" height="1px"/></mpadded>
<mo id="mo1" symmetric="true" style="color: green">&#x21A8;</mo>
- <mspace style="background: gray" width="10px" height="50px"/>
+ <mpadded style="background: gray" width="10px" height="50px"><mn>1</mn></mpadded>
</mrow>
</math>
<math style="font-family: axisheight5000-verticalarrow14000;">
+ <mspace id="baseline2" style="background: blue" width="50px" height="1px"/>
+ <mpadded voffset="50px"><mspace style="background: cyan" width="50px" height="1px"/></mpadded>
<mrow>
- <mspace id="baseline2" style="background: blue" width="50px" height="1px"/>
- <mpadded voffset="50px"><mspace style="background: cyan" width="50px" height="1px"/></mpadded>
<mo id="mo2" symmetric="true" style="color: green">&#x21A8;</mo>
- <mspace id="target2" style="background: gray" width="10px" height="200px"/>
+ <mpadded id="target2" style="background: gray" width="10px" height="200px"><mn>2</mn></mpadded>
+ </mrow>
+ </math>
+ </p>
+ <p>
+ <math style="font-family: axisheight5000-verticalarrow14000;">
+ <mspace id="baseline3" style="background: blue" width="50px" height="1px"/>
+ <mpadded voffset="50px"><mspace style="background: cyan" width="50px" height="1px"/></mpadded>
+ <mrow>
+ <mo id="mo3" minsize="14em" style="color: green">&#x21A8;</mo>
+ <mpadded id="target3" style="background: gray" width="10px" height="0px" depth="0px"><mn>3</mn></mpadded>
+ </mrow>
+ </math>
+ </p>
+ <p>
+ <math style="font-family: axisheight5000-verticalarrow14000;">
+ <mspace id="baseline4" style="background: blue" width="50px" height="1px"/>
+ <mpadded voffset="50px"><mspace style="background: cyan" width="50px" height="1px"/></mpadded>
+ <mrow>
+ <mo id="mo4" minsize="14em" style="color: green">&#x21A8;</mo>
+ <mpadded id="target4" style="background: gray" width="10px" height="6em" depth="1em"><mn>4</mn></mpadded>
+ </mrow>
+ </math>
+ <math style="font-family: axisheight5000-verticalarrow14000;">
+ <mspace id="baseline5" style="background: blue" width="50px" height="1px"/>
+ <mpadded voffset="50px"><mspace style="background: cyan" width="50px" height="1px"/></mpadded>
+ <mrow>
+ <mo id="mo5" minsize="14em" style="color: green">&#x21A8;</mo>
+ <mpadded id="target5" style="background: gray" width="10px" height="4em" depth="3em"><mn>5</mn></mpadded>
+ </mrow>
+ </math>
+ </p>
+ <p>
+ <math style="font-family: axisheight5000-verticalarrow14000;">
+ <mspace id="baseline6" style="background: blue" width="50px" height="1px"/>
+ <mpadded voffset="50px"><mspace style="background: cyan" width="50px" height="1px"/></mpadded>
+ <mrow>
+ <mo id="mo6" maxsize="14em" style="color: green">&#x21A8;</mo>
+ <mpadded id="target6" style="background: gray" width="10px" height="6em" depth="22em"><mn>6</mn></mpadded>
+ </mrow>
+ </math>
+ <math style="font-family: axisheight5000-verticalarrow14000;">
+ <mspace id="baseline7" style="background: blue" width="50px" height="1px"/>
+ <mpadded voffset="50px"><mspace style="background: cyan" width="50px" height="1px"/></mpadded>
+ <mrow>
+ <mo id="mo7" maxsize="14em" style="color: green">&#x21A8;</mo>
+ <mpadded id="target7" style="background: gray" width="10px" height="4em" depth="24em"><mn>7</mn></mpadded>
+ </mrow>
+ </math>
+ </p>
+
+
+ <p>
+ <math style="font-family: axisheight5000-verticalarrow14000;">
+ <mspace id="baseline8" style="background: blue" width="50px" height="1px"/>
+ <mpadded voffset="50px"><mspace style="background: cyan" width="50px" height="1px"/></mpadded>
+ <mrow>
+ <mo id="mo8" symmetric="true" minsize="14em" style="color: green">&#x21A8;</mo>
+ <mpadded id="target8" style="background: gray" width="10px" height="6em" depth="1em"><mn>8</mn></mpadded>
+ </mrow>
+ </math>
+ <math style="font-family: axisheight5000-verticalarrow14000;">
+ <mspace id="baseline9" style="background: blue" width="50px" height="1px"/>
+ <mpadded voffset="50px"><mspace style="background: cyan" width="50px" height="1px"/></mpadded>
+ <mrow>
+ <mo id="mo9" symmetric="true" maxsize="14em" style="color: green">&#x21A8;</mo>
+ <mpadded id="target9" style="background: gray" width="10px" height="6em" depth="24em"><mn>9</mn></mpadded>
</mrow>
</math>
+ </p>
</body>
</html>
diff --git a/testing/web-platform/tests/mathml/presentation-markup/operators/mo-minsize-maxsize-001.html b/testing/web-platform/tests/mathml/presentation-markup/operators/mo-minsize-maxsize-001.html
index 3e7e5c9bcc..c07f64327a 100644
--- a/testing/web-platform/tests/mathml/presentation-markup/operators/mo-minsize-maxsize-001.html
+++ b/testing/web-platform/tests/mathml/presentation-markup/operators/mo-minsize-maxsize-001.html
@@ -20,6 +20,10 @@
mo {
font-family: operators;
}
+ @font-face {
+ font-family: stretchy;
+ src: url("/fonts/math/stretchy.woff");
+ }
</style>
<script>
setup({ explicit_done: true });
@@ -48,17 +52,27 @@
test(function() {
assert_approx_equals(document.getElementById("percent_minsize").getBoundingClientRect().height, 12 * emToPx, epsilon, "percent minsize");
assert_approx_equals(document.getElementById("percent_maxsize").getBoundingClientRect().height, 3 * emToPx, epsilon, "percent maxsize");
- }, `minsize/maxsize percentages are relative to the target size`);
+ }, `minsize/maxsize percentages are relative to the unstretched size`);
test(function() {
- // These tests are not really strong:
- // - The smallest glyph for this stretchy operator is a 1em square so
- // it can't go under a minsize of 1em anyway.
- // - The maxsize is theorically infinite, this only tests that a large
- // value of 300em is clamped.
- assert_approx_equals(document.getElementById("default_minsize").getBoundingClientRect().height, 1 * emToPx, epsilon, "default minsize is 1em");
+ // - The unstretched size is a lower bound for the stretched size, so
+ // specifying a lower minsize has no effect. This test only verifies
+ // that the default minsize is at most 100% the unstretched size.
+ const unstretched_size = 1 * emToPx;
+ assert_approx_equals(document.getElementById("default_minsize").getBoundingClientRect().height, unstretched_size, epsilon, "default minsize is 100%");
+
+ // Previous version of MathML Core were defining minsize as 1em rather
+ // than 100% the unstretched size. So try the same test with a .5em
+ // unstretched size.
+ const unstretched_size_2 = .5 * emToPx;
+ assert_approx_equals(document.getElementById("default_minsize_2").getBoundingClientRect().height, unstretched_size_2, epsilon, "default minsize is not 1em");
+
+ // - The target size is an upper bound for the stretched size, so
+ // specifying a larger maxsize has no effect. This test only
+ // verifies that the default maxsize is at least 300 times the
+ // unstretched size.
assert_approx_equals(document.getElementById("default_maxsize").getBoundingClientRect().height, 300 * emToPx, epsilon, "default maxsize is infinity");
- }, `default minsize/maxsize percentages`);
+ }, `default minsize/maxsize values`);
done();
}
@@ -71,7 +85,7 @@
<mrow>
<mspace width="1em" height="5em" style="background: blue"/>
<mo id="negative_minsize" minsize="-10em" stretchy="true" symmetric="false">⥯</mo>
- <mn><!-- not space like --></mn>
+ <mpadded height="0" depth="0"><mn><!-- not space like --></mn></mpadded>
</mrow>
</math>
</p>
@@ -80,7 +94,7 @@
<mrow>
<mspace width="1em" height="5em" style="background: blue"/>
<mo id="maxsize_less_than_minsize" minsize="7em" maxsize="2em" stretchy="true" symmetric="false">⥯</mo>
- <mn><!-- not space like --></mn>
+ <mpadded height="0" depth="0"><mn><!-- not space like --></mn></mpadded>
</mrow>
</math>
</p>
@@ -89,7 +103,7 @@
<mrow>
<mspace width="1em" height="5em" style="background: blue"/>
<mo id="minsize_less_than_negative_maxsize" minsize="-2em" maxsize="-1em" stretchy="true" symmetric="false">⥯</mo>
- <mn><!-- not space like --></mn>
+ <mpadded height="0" depth="0"><mn><!-- not space like --></mn></mpadded>
</mrow>
</math>
</p>
@@ -98,7 +112,7 @@
<mrow>
<mspace id="zero_target_size_with_minsize_math_axis" width="1em" height="0em" style="background: blue"/>
<mo id="zero_target_size_with_minsize" minsize="2em" stretchy="true" symmetric="true">⥯</mo>
- <mn><!-- not space like --></mn>
+ <mpadded height="0" depth="0"><mn><!-- not space like --></mn></mpadded>
</mrow>
</math>
</p>
@@ -106,8 +120,8 @@
<math>
<mrow>
<mspace width="1em" height="6em" style="background: blue"/>
- <mo id="percent_minsize" minsize="200%" stretchy="true" symmetric="false">⥯</mo>
- <mn><!-- not space like --></mn>
+ <mo id="percent_minsize" minsize="1200%" stretchy="true" symmetric="false">⥯</mo>
+ <mpadded height="0" depth="0"><mn><!-- not space like --></mn></mpadded>
</mrow>
</math>
</p>
@@ -115,8 +129,8 @@
<math>
<mrow>
<mspace width="1em" height="6em" style="background: blue"/>
- <mo id="percent_maxsize" maxsize="50%" stretchy="true" symmetric="false">⥯</mo>
- <mn><!-- not space like --></mn>
+ <mo id="percent_maxsize" maxsize="300%" stretchy="true" symmetric="false">⥯</mo>
+ <mpadded height="0" depth="0"><mn><!-- not space like --></mn></mpadded>
</mrow>
</math>
</p>
@@ -125,7 +139,16 @@
<mrow>
<mspace width="1em" height=".5em" style="background: blue"/>
<mo id="default_minsize" stretchy="true" symmetric="false">⥯</mo>
- <mn><!-- not space like --></mn>
+ <mpadded height="0" depth="0"><mn><!-- not space like --></mn></mpadded>
+ </mrow>
+ </math>
+ </p>
+ <p>
+ <math>
+ <mrow>
+ <mspace width="1em" height=".25em" style="background: blue"/>
+ <mo style="font-family: stretchy" id="default_minsize_2" stretchy="true" symmetric="false">↨</mo>
+ <mpadded height="0" depth="0"><mn><!-- not space like --></mn></mpadded>
</mrow>
</math>
</p>
@@ -134,10 +157,9 @@
<mrow>
<mspace width="1em" height="300em" style="background: blue"/>
<mo id="default_maxsize" stretchy="true" symmetric="false">⥯</mo>
- <mn><!-- not space like --></mn>
+ <mpadded height="0" depth="0"><mn><!-- not space like --></mn></mpadded>
</mrow>
</math>
</p>
-
</body>
</html>
diff --git a/testing/web-platform/tests/mathml/presentation-markup/operators/mo-stretch-properties-dynamic-001.html b/testing/web-platform/tests/mathml/presentation-markup/operators/mo-stretch-properties-dynamic-001.html
index 5d447aa1d2..1cb3c90bcb 100644
--- a/testing/web-platform/tests/mathml/presentation-markup/operators/mo-stretch-properties-dynamic-001.html
+++ b/testing/web-platform/tests/mathml/presentation-markup/operators/mo-stretch-properties-dynamic-001.html
@@ -42,7 +42,8 @@
element = document.getElementById("minsize_remove");
element.removeAttribute("minsize");
- assert_approx_equals(element.getBoundingClientRect().height, 1 * emToPx, epsilon, "remove");
+ const unstretched_size = 1 * emToPx;
+ assert_approx_equals(element.getBoundingClientRect().height, unstretched_size, epsilon, "remove");
}, `minsize`);
test(function() {
diff --git a/testing/web-platform/tests/mathml/presentation-markup/operators/size-and-position-of-stretchy-fences-with-default-font-001.html b/testing/web-platform/tests/mathml/presentation-markup/operators/size-and-position-of-stretchy-fences-with-default-font-001.html
new file mode 100644
index 0000000000..a5eb267c61
--- /dev/null
+++ b/testing/web-platform/tests/mathml/presentation-markup/operators/size-and-position-of-stretchy-fences-with-default-font-001.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Size of nested stretchy fences with inner mo</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=40066018">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=40068339">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=40856331">
+
+<div id="log"></div>
+
+<p>
+ <math>
+ <mrow>
+ <mo id="left1">(</mo>
+ <mrow>
+ <mo>(</mo>
+ <mrow>
+ <mi>x</mi>
+ <mo>+</mo>
+ <mi>y</mi>
+ </mrow>
+ <mo>)</mo>
+ </mrow>
+ <mo id="right1">)</mo>
+ </mrow>
+ </math>
+ <math>
+ <mrow>
+ <mo id="left2">(</mo>
+ <mrow>
+ <mo>(</mo>
+ <mrow>
+ <mi>x</mi>
+ </mrow>
+ <mo>)</mo>
+ </mrow>
+ <mo id="right2">)</mo>
+ </mrow>
+ </math>
+</p>
+
+<p>
+ <math>
+ <mn id="plus3">+</mn>
+ <mrow>
+ <mo id="left3" fence="true" form="prefix">(</mo>
+ <mi>x</mi>
+ <mo id="right3" fence="true" form="postfix">)</mo>
+ </mrow>
+ </math>
+</p>
+
+<p>
+ <math display="block">
+ <mrow>
+ <mo id="left4" fence="false" symmetric="true" minsize="2.4em" maxsize="2.4em">(</mo>
+ <mfrac>
+ <msup>
+ <mi>∂</mi>
+ <mn>2</mn>
+ </msup>
+ <mrow>
+ <mi>∂</mi>
+ <msup>
+ <mi>x</mi>
+ <mn>2</mn>
+ </msup>
+ </mrow>
+ </mfrac>
+ <mo id="plus4">+</mo>
+ <mfrac>
+ <msup>
+ <mi>∂</mi>
+ <mn>2</mn>
+ </msup>
+ <mrow>
+ <mi>∂</mi>
+ <msup>
+ <mi>y</mi>
+ <mn>2</mn>
+ </msup>
+ </mrow>
+ </mfrac>
+ <mo id="right4" fence="false" symmetric="true" minsize="2.4em" maxsize="2.4em">)</mo>
+ </mrow>
+ </math>
+</p>
+
+<script>
+ function getBox(id) {
+ return document.getElementById(id).getBoundingClientRect();
+ }
+ function middleOf(id) {
+ let box = getBox(id);
+ return (box.top + box.bottom) / 2;
+ }
+ const epsilon = 2;
+
+ test(t => {
+ assert_approx_equals(getBox("left1").top, getBox("left2").top, epsilon);
+ assert_approx_equals(getBox("left1").bottom, getBox("left2").bottom, epsilon);
+ assert_approx_equals(getBox("right1").top, getBox("right2").top, epsilon);
+ assert_approx_equals(getBox("right1").bottom, getBox("right2").bottom, epsilon);
+ }, "Inner binary operator should not affect position and size of outer fences.");
+
+ test(t => {
+ const math_axis_3 = middleOf("plus3")
+ assert_approx_equals(middleOf("left3"), math_axis_3, epsilon);
+ assert_approx_equals(middleOf("right3"), math_axis_3, epsilon);
+ const math_axis_4 = middleOf("plus4")
+ assert_approx_equals(middleOf("left4"), math_axis_4, epsilon);
+ assert_approx_equals(middleOf("right4"), math_axis_4, epsilon);
+ }, "Fences are stretched symmetrically with respect to the math axis");
+</script>
diff --git a/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-001-ref.html b/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-001-ref.html
new file mode 100644
index 0000000000..5d4e6b7dca
--- /dev/null
+++ b/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-001-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>MathML and ::first-line/::first-letter pseudo-elements (reference)</title>
+<p>PASS if there is no red.</p>
+<ol>
+ <li>
+ <math>
+ <mtext class="firstline"><span>Hello,<br/>World!</span></mtext>
+ </math>
+ </li>
+ <li>
+ <math>
+ <mtext class="firstletter">Hello, World!</mtext>
+ </math>
+ </li>
+</ol>
diff --git a/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-001.html b/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-001.html
new file mode 100644
index 0000000000..42d0f04ed8
--- /dev/null
+++ b/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-001.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>MathML and ::first-line/::first-letter pseudo-elements</title>
+<link rel="help" href="https://w3c.github.io/mathml-core/#legacy-mathml-style-attributes">
+<link rel="match" href="first-line-first-letter-pseudo-elements-001-ref.html"/>
+<meta name="assert" content="::first-line and ::first-letter do not apply to MathML elements.">
+<style>
+ .firstline::first-line { background: red; }
+ .firstletter::first-letter { background: red; }
+</style>
+<p>PASS if there is no red.</p>
+<ol>
+ <li>
+ <math>
+ <mtext class="firstline"><span>Hello,<br/>World!</span></mtext>
+ </math>
+ </li>
+ <li>
+ <math>
+ <mtext class="firstletter">Hello, World!</mtext>
+ </math>
+ </li>
+</ol>
diff --git a/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-002-ref.html b/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-002-ref.html
new file mode 100644
index 0000000000..d4c79c7218
--- /dev/null
+++ b/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-002-ref.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>MathML and ::first-line/::first-letter pseudo-elements (reference)</title>
+<style>
+ .firstline > span { background: lime; }
+ .firstletter > span { background: lime; }
+</style>
+<p>PASS if the first line or letter is green.</p>
+<ol>
+ <li>
+ <div style="display: inline math" class="firstline">
+ <span>Hello,</span><br/>World!
+ </div>
+ </li>
+ <li>
+ <div style="display: inline math" class="firstletter">
+ <span>H</span>ello, World!
+ </div>
+ </li>
+ <li>
+ <div style="display: block math" class="firstline">
+ <span>Hello,</span><br/>World!
+ </div>
+ </li>
+ <li>
+ <div style="display: block math" class="firstletter">
+ <span>H</span>ello, World!
+ </div>
+ </li>
+</ol>
diff --git a/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-002.html b/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-002.html
new file mode 100644
index 0000000000..7e206c951e
--- /dev/null
+++ b/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-002.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>MathML and ::first-line/::first-letter pseudo-elements</title>
+<link rel="help" href="https://w3c.github.io/mathml-core/#legacy-mathml-style-attributes">
+<link rel="match" href="first-line-first-letter-pseudo-elements-002-ref.html"/>
+<meta name="assert" content="::first-line and ::first-letter do apply to non-MathML specified display math elements, because their computed values are block/inline flow.">
+<style>
+ .firstline::first-line { background: lime; }
+ .firstletter::first-letter { background: lime; }
+</style>
+<p>PASS if the first line or letter is green.</p>
+<ol>
+ <li>
+ <div style="display: inline math" class="firstline">
+ Hello,<br/>World!
+ </div>
+ </li>
+ <li>
+ <div style="display: inline math" class="firstletter">
+ Hello, World!
+ </div>
+ </li>
+ <li>
+ <div style="display: block math" class="firstline">
+ Hello,<br/>World!
+ </div>
+ </li>
+ <li>
+ <div style="display: block math" class="firstletter">
+ Hello, World!
+ </div>
+ </li>
+</ol>
diff --git a/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-003-ref.html b/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-003-ref.html
new file mode 100644
index 0000000000..825b397057
--- /dev/null
+++ b/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-003-ref.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>MathML and ::first-line/::first-letter pseudo-elements (reference)</title>
+<p>PASS if there is no red.</p>
+<ol>
+ <li>
+ <div class="firstline">
+ <math style="display: inline math"><mtext>Hello<br>World!</mtext></math>
+ </li>
+ <li>
+ <div class="firstletter">
+ <math>
+ <mtext style="display: inline math">Hello<br>World!</mtext>
+ </math>
+ </div>
+ </li>
+ <li>
+ <div class="firstline">
+ <math>
+ <mtext style="display: block math">Hello<br>World!</mtext>
+ </math>
+ </div>
+ </li>
+ <li>
+ <div class="firstletter">
+ <math>
+ <mtext style="display: block math">Hello<br>World!</mtext>
+ </math>
+ </div>
+ </li>
+</ol>
diff --git a/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-003.html b/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-003.html
new file mode 100644
index 0000000000..042a9555e6
--- /dev/null
+++ b/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-003.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>MathML and ::first-line/::first-letter pseudo-elements</title>
+<link rel="help" href="https://w3c.github.io/mathml-core/#legacy-mathml-style-attributes">
+<link rel="match" href="first-line-first-letter-pseudo-elements-003-ref.html"/>
+<meta name="assert" content="display math elements do not contribute a first formatted line/letter to ancestors.">
+<style>
+ .firstline::first-line { background: red; }
+ .firstletter::first-letter { background: red; }
+</style>
+<p>PASS if there is no red.</p>
+<ol>
+ <li>
+ <div class="firstline">
+ <math style="display: inline math"><mtext>Hello<br>World!</mtext></math>
+ </li>
+ <li>
+ <div class="firstletter">
+ <math>
+ <mtext style="display: inline math">Hello<br>World!</mtext>
+ </math>
+ </div>
+ </li>
+ <li>
+ <div class="firstline">
+ <math>
+ <mtext style="display: block math">Hello<br>World!</mtext>
+ </math>
+ </div>
+ </li>
+ <li>
+ <div class="firstletter">
+ <math>
+ <mtext style="display: block math">Hello<br>World!</mtext>
+ </math>
+ </div>
+ </li>
+</ol>
diff --git a/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-004-ref.html b/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-004-ref.html
new file mode 100644
index 0000000000..60d3427489
--- /dev/null
+++ b/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-004-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>MathML and ::first-line/::first-letter pseudo-elements (reference)</title>
+<style>
+ .firstline > span { background: lime; }
+ .firstletter > span { background: lime; }
+</style>
+<ol>
+ <li>PASS if first line is green:
+ <math>
+ <mtext><span class="firstline"><span>Hello,</span><br/>World!</span></mtext>
+ </math>
+ </li>
+ <li>PASS if first letter is green:
+ <math>
+ <mtext><span class="firstletter"><span>H</span>ello, World!</span></mtext>
+ </math>
+ </li>
+</ol>
diff --git a/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-004.html b/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-004.html
new file mode 100644
index 0000000000..b463266bdb
--- /dev/null
+++ b/testing/web-platform/tests/mathml/relations/css-styling/first-line-first-letter-pseudo-elements-004.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>MathML and ::first-line/::first-letter pseudo-elements</title>
+<link rel="help" href="https://w3c.github.io/mathml-core/#legacy-mathml-style-attributes">
+<link rel="match" href="first-line-first-letter-pseudo-elements-004-ref.html"/>
+<meta name="assert" content="::first-letter/::first-line works for HTML elements inside MathML.">
+<style>
+ .firstline::first-line { background: lightgreen; }
+ .firstletter::first-letter { background: lightgreen; }
+</style>
+<ol>
+ <li>PASS if first line is green:
+ <math>
+ <mtext><span class="firstline">Hello,<br/>World!</span></mtext>
+ </math>
+ </li>
+ <li>PASS if first letter is green:
+ <math>
+ <mtext><span class="firstletter">Hello, World!</span></mtext>
+ </math>
+ </li>
+</ol>
diff --git a/testing/web-platform/tests/mathml/tools/stretchy.py b/testing/web-platform/tests/mathml/tools/stretchy.py
index 34530f5792..33d4decd4f 100755
--- a/testing/web-platform/tests/mathml/tools/stretchy.py
+++ b/testing/web-platform/tests/mathml/tools/stretchy.py
@@ -4,7 +4,9 @@ from utils import mathfont
import fontforge
# Create a WOFF font with glyphs for all the operator strings.
-font = mathfont.create("stretchy", "Copyright (c) 2021 Igalia S.L.")
+font = mathfont.create("stretchy", "Copyright (c) 2021-2024 Igalia S.L.")
+
+font.math.AxisHeight = 0
# Set parameters for stretchy tests.
font.math.MinConnectorOverlap = mathfont.em // 2
@@ -27,6 +29,7 @@ font.math.OverbarExtraAscender = 0
# These two characters will be stretchable in both directions.
horizontalArrow = 0x295A # LEFTWARDS HARPOON WITH BARB UP FROM BAR
verticalArrow = 0x295C # UPWARDS HARPOON WITH BARB RIGHT FROM BAR
+upDownArrowWithBase = 0x21A8 # UP DOWN ARROW WITH BASE
mathfont.createSizeVariants(font, aUsePUA=True, aCenterOnBaseline=False)
@@ -40,4 +43,10 @@ mathfont.createSquareGlyph(font, verticalArrow)
mathfont.createStretchy(font, verticalArrow, True)
mathfont.createStretchy(font, verticalArrow, False)
+# U+21A8 stretches vertically using two size variants: a base glyph (of height
+# half an em) and taller glyphs (of heights 1, 2, 3 and 4 em).
+g = font.createChar(upDownArrowWithBase)
+mathfont.drawRectangleGlyph(g, mathfont.em, mathfont.em/2, 0)
+font[upDownArrowWithBase].verticalVariants = "uni21A8 v0 v1 v2 v3"
+
mathfont.save(font)