Add tests for Selection.toString() and text-transform (#41012)

* Add tests for Selection.toString() and text-transform

This is a variation of the test added in [1] verifying how selection
works on the single char 'ß' with text-transform: uppercase. This is
special because the transformed text is 'SS' which has length 2. Instead
of using a reftest to check the rendered selection really spans the 2
characters, we use a JS test to directly check the returned value of
Selection.toString().

We generalize this approach to characters that are converted to their
italic equivalent via text-transform: math-auto. Note that for most of
them, the transformed text is a character in the Mathematical
Alphanumeric Symbols block, which is represented as a surrogate pair in
UTF-16. So it has length 2 as a UTF-16 string, although it's really
rendered as one character. The new approach works better than the reftest
approach in order to exhibit Chromium's bug 750990 [2].

A similar test is added for single-char <mi> elements, which perform
the same transformations as math-auto. That way it could work with
browsers implementing this MathML feature without relying on math-auto.

[1] https://chromium-review.googlesource.com/c/chromium/src/+/2987819
[2] https://bugs.chromium.org/p/chromium/issues/detail?id=750990

* Update mathvariant-transforms.py
diff --git a/css/css-text/text-transform/math/italic-mapping.js b/css/css-text/text-transform/math/italic-mapping.js
new file mode 100644
index 0000000..ba17d3f
--- /dev/null
+++ b/css/css-text/text-transform/math/italic-mapping.js
@@ -0,0 +1,115 @@
+// Generated by mathml/tools/mathvariant.py; DO NOT EDIT.
+let italic_mapping = {
+  0x41: 0x1D434,
+  0x42: 0x1D435,
+  0x43: 0x1D436,
+  0x44: 0x1D437,
+  0x45: 0x1D438,
+  0x46: 0x1D439,
+  0x47: 0x1D43A,
+  0x48: 0x1D43B,
+  0x49: 0x1D43C,
+  0x4A: 0x1D43D,
+  0x4B: 0x1D43E,
+  0x4C: 0x1D43F,
+  0x4D: 0x1D440,
+  0x4E: 0x1D441,
+  0x4F: 0x1D442,
+  0x50: 0x1D443,
+  0x51: 0x1D444,
+  0x52: 0x1D445,
+  0x53: 0x1D446,
+  0x54: 0x1D447,
+  0x55: 0x1D448,
+  0x56: 0x1D449,
+  0x57: 0x1D44A,
+  0x58: 0x1D44B,
+  0x59: 0x1D44C,
+  0x5A: 0x1D44D,
+  0x61: 0x1D44E,
+  0x62: 0x1D44F,
+  0x63: 0x1D450,
+  0x64: 0x1D451,
+  0x65: 0x1D452,
+  0x66: 0x1D453,
+  0x67: 0x1D454,
+  0x68: 0x210E,
+  0x69: 0x1D456,
+  0x6A: 0x1D457,
+  0x6B: 0x1D458,
+  0x6C: 0x1D459,
+  0x6D: 0x1D45A,
+  0x6E: 0x1D45B,
+  0x6F: 0x1D45C,
+  0x70: 0x1D45D,
+  0x71: 0x1D45E,
+  0x72: 0x1D45F,
+  0x73: 0x1D460,
+  0x74: 0x1D461,
+  0x75: 0x1D462,
+  0x76: 0x1D463,
+  0x77: 0x1D464,
+  0x78: 0x1D465,
+  0x79: 0x1D466,
+  0x7A: 0x1D467,
+  0x131: 0x1D6A4,
+  0x237: 0x1D6A5,
+  0x391: 0x1D6E2,
+  0x392: 0x1D6E3,
+  0x393: 0x1D6E4,
+  0x394: 0x1D6E5,
+  0x395: 0x1D6E6,
+  0x396: 0x1D6E7,
+  0x397: 0x1D6E8,
+  0x398: 0x1D6E9,
+  0x399: 0x1D6EA,
+  0x39A: 0x1D6EB,
+  0x39B: 0x1D6EC,
+  0x39C: 0x1D6ED,
+  0x39D: 0x1D6EE,
+  0x39E: 0x1D6EF,
+  0x39F: 0x1D6F0,
+  0x3A0: 0x1D6F1,
+  0x3A1: 0x1D6F2,
+  0x3F4: 0x1D6F3,
+  0x3A3: 0x1D6F4,
+  0x3A4: 0x1D6F5,
+  0x3A5: 0x1D6F6,
+  0x3A6: 0x1D6F7,
+  0x3A7: 0x1D6F8,
+  0x3A8: 0x1D6F9,
+  0x3A9: 0x1D6FA,
+  0x2207: 0x1D6FB,
+  0x3B1: 0x1D6FC,
+  0x3B2: 0x1D6FD,
+  0x3B3: 0x1D6FE,
+  0x3B4: 0x1D6FF,
+  0x3B5: 0x1D700,
+  0x3B6: 0x1D701,
+  0x3B7: 0x1D702,
+  0x3B8: 0x1D703,
+  0x3B9: 0x1D704,
+  0x3BA: 0x1D705,
+  0x3BB: 0x1D706,
+  0x3BC: 0x1D707,
+  0x3BD: 0x1D708,
+  0x3BE: 0x1D709,
+  0x3BF: 0x1D70A,
+  0x3C0: 0x1D70B,
+  0x3C1: 0x1D70C,
+  0x3C2: 0x1D70D,
+  0x3C3: 0x1D70E,
+  0x3C4: 0x1D70F,
+  0x3C5: 0x1D710,
+  0x3C6: 0x1D711,
+  0x3C7: 0x1D712,
+  0x3C8: 0x1D713,
+  0x3C9: 0x1D714,
+  0x2202: 0x1D715,
+  0x3F5: 0x1D716,
+  0x3D1: 0x1D717,
+  0x3F0: 0x1D718,
+  0x3D5: 0x1D719,
+  0x3F1: 0x1D71A,
+  0x3D6: 0x1D71B,
+}
diff --git a/css/css-text/text-transform/math/text-transform-math-auto-003.html b/css/css-text/text-transform/math/text-transform-math-auto-003.html
new file mode 100644
index 0000000..eff01b0
--- /dev/null
+++ b/css/css-text/text-transform/math/text-transform-math-auto-003.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>text-transform math-auto</title>
+    <link rel="help" href="https://w3c.github.io/mathml-core/#new-text-transform-values">
+    <link rel="help" href="https://w3c.github.io/mathml-core/#italic-mappings">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="italic-mapping.js"></script>
+    <meta name="assert" content="Verify Selection.toString() on a character with 'text-transform: math-auto' returns the transformed unicode character.">
+    <style>
+      #container {
+        text-transform: math-auto;
+      }
+    </style>
+  </head>
+  <body>
+    <span id="container"></span>
+    <script>
+      add_completion_callback(() => {
+        container.remove();
+      });
+      for (let code_point in italic_mapping) {
+        test(() => {
+          container.textContent = String.fromCodePoint(code_point);
+          window
+            .getSelection()
+            .setBaseAndExtent(container.firstChild, 0, container.firstChild, 1);
+          assert_equals(
+            window.getSelection().toString(),
+            String.fromCodePoint(italic_mapping[code_point])
+          );
+        }, `Selection.toString() for math-auto '${String.fromCodePoint(code_point)}' returns the transformed character.`);
+      }
+    </script>
+  </body>
+</html>
diff --git a/css/css-text/text-transform/text-transform-upperlower-107.html b/css/css-text/text-transform/text-transform-upperlower-107.html
new file mode 100644
index 0000000..791edd1
--- /dev/null
+++ b/css/css-text/text-transform/text-transform-upperlower-107.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text Test: text-transform uppercase German sharp S and selection</title>
+<link rel="author" title="Frédéric Wang" href="mailto:fwang@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-text/#propdef-text-transform">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="text-transform: uppercase will uppercase the German sharp S as SS, the whole 2 characters is selectable and should be returned when retriving the selected string.">
+<style>
+  #target {
+    text-transform: uppercase;
+  }
+</style>
+
+<span id="target" lang="de">&#x00DF;</span>
+
+<script>
+  test(() => {
+    window.getSelection().setBaseAndExtent(target, 0, target, 1);
+    assert_equals(window.getSelection().toString(), "SS");
+  }, "Selection.toString() for 'ß' with text-transform: uppercase");
+</script>
diff --git a/mathml/relations/css-styling/mathvariant-auto-selection.html b/mathml/relations/css-styling/mathvariant-auto-selection.html
new file mode 100644
index 0000000..f915c44
--- /dev/null
+++ b/mathml/relations/css-styling/mathvariant-auto-selection.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>text-transform math-auto</title>
+    <link rel="help" href="https://w3c.github.io/mathml-core/#new-text-transform-values">
+    <link rel="help" href="https://w3c.github.io/mathml-core/#italic-mappings">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/css/css-text/text-transform/math/italic-mapping.js"></script>
+    <meta name="assert" content="Verify Selection.toString() on a single character <mi> within returns the transformed unicode character.">
+  </head>
+  <body>
+    <math><mi id="container"></mi></math>
+    <script>
+      add_completion_callback(() => {
+        container.remove();
+      });
+      for (let code_point in italic_mapping) {
+        test(() => {
+          container.textContent = String.fromCodePoint(code_point);
+          window
+            .getSelection()
+            .setBaseAndExtent(container.firstChild, 0, container.firstChild, 1);
+          assert_equals(
+            window.getSelection().toString(),
+            String.fromCodePoint(italic_mapping[code_point])
+          );
+        }, `Selection.toString() for <mi>${String.fromCodePoint(code_point)}</mi> returns the transformed character.`);
+      }
+    </script>
+  </body>
+</html>
diff --git a/mathml/tools/mathvariant-transforms.py b/mathml/tools/mathvariant-transforms.py
index e4857d2..dca85b5 100755
--- a/mathml/tools/mathvariant-transforms.py
+++ b/mathml/tools/mathvariant-transforms.py
@@ -195,6 +195,18 @@
 generateTestFor(mathvariant="auto", mathml=False)
 generateTestFor(mathvariant="auto", mathml=True)
 
+# Generate italic_mapping.js file used by selection tests.
+print("Generating italic_mapping.js...", end="")
+italic_mapping = open("../../css/css-text/text-transform/math/italic-mapping.js", "w")
+italic_mapping.write("// Generated by mathml/tools/mathvariant.py; DO NOT EDIT.\n");
+italic_mapping.write("let italic_mapping = {\n");
+for baseChar in mathvariantTransforms["italic"]:
+    transformedChar = mathvariantTransforms[mathvariant][baseChar]
+    italic_mapping.write("  0x%0X: 0x%0X,\n" % (baseChar, transformedChar));
+italic_mapping.write("}\n");
+italic_mapping.close()
+print(" done.")
+
 # Other mathvariant tests can be generated by the following command. They are
 # still use internally by browsers implementing full mathvariant support.
 # See https://github.com/w3c/mathml-core/issues/182