[mathml] Wrap <mtd> children in an anonymous <mrow>

This CL wraps the children of an <mtd> cell within an anonymous <mrow>.
That way, such children are laid out as if they were in an explicit
<mrow>, in particular avoiding line breaks, performing operator
stretching and adding proper operator spacing [1]. In particular, this
fixes the second use case demonstrated at BlinkOn 16 [2].

[1] https://w3c.github.io/mathml-core/#layout-of-mrow
[2] https://people.igalia.com/fwang/2022-blinkon-16-remainder-estimate-for-mathml-integration/?showNotes=true#/19

Bug: 6606, 1125111, 1237043
Change-Id: Icd330b523df4548bf7065eed986bd3698514f50b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3657309
Reviewed-by: Ian Kilpatrick <ikilpatrick@chromium.org>
Commit-Queue: Frédéric Wang <fwang@igalia.com>
Cr-Commit-Position: refs/heads/main@{#1010954}
diff --git a/mathml/presentation-markup/mrow/inferred-mrow-baseline.html b/mathml/presentation-markup/mrow/inferred-mrow-baseline.html
index 2b208aa..cc007cf 100644
--- a/mathml/presentation-markup/mrow/inferred-mrow-baseline.html
+++ b/mathml/presentation-markup/mrow/inferred-mrow-baseline.html
@@ -20,7 +20,7 @@
   window.addEventListener("load", runTests);
   function runTests()
   {
-      ["Mrow", "Sqrt", "Style", "Error", "Phantom", "Math", "Menclose", "Mpadded", "Unknown"].forEach((tag) => {
+      ["Mrow", "Sqrt", "Style", "Error", "Phantom", "Math", "Menclose", "Mpadded", "Unknown", "Mtd"].forEach((tag) => {
           var x = document.getElementById("above" + tag).getBoundingClientRect();
           var y = document.getElementById("below" + tag).getBoundingClientRect();
           test(function() {
@@ -48,6 +48,8 @@
 ></mspace><mspace id="belowMpadded" width="10px" depth="30px" style="background: blue"></mspace></mpadded></math>
     <math><unknown><mspace id="aboveUnknown" width="10px" height="30px" style="background: purple"
 ></mspace><mspace id="belowUnknown" width="10px" depth="30px" style="background: blue"></mspace></unknown></math>
+    <math><mtable><mtr><mtd><mspace id="aboveMtd" width="10px" height="30px" style="background: purple"
+></mspace><mspace id="belowMtd" width="10px" depth="30px" style="background: blue"></mspace></mtd></mtr></mtable></math>
   </p>
 </body>
 </html>
diff --git a/mathml/presentation-markup/mrow/inferred-mrow-stretchy.html b/mathml/presentation-markup/mrow/inferred-mrow-stretchy.html
index 35c6d69..a1e409a 100644
--- a/mathml/presentation-markup/mrow/inferred-mrow-stretchy.html
+++ b/mathml/presentation-markup/mrow/inferred-mrow-stretchy.html
@@ -32,7 +32,7 @@
   window.addEventListener("load", () => { loadAllFonts().then(runTests); });
   function runTests()
   {
-      ["Mrow", "Sqrt", "Style", "Error", "Phantom", "Math", "Menclose", "Mpadded", "Unknown"].forEach((tag) => {
+      ["Mrow", "Sqrt", "Style", "Error", "Phantom", "Math", "Menclose", "Mpadded", "Unknown", "Mtd"].forEach((tag) => {
           var mo = document.getElementById("mo" + tag);
           test(function() {
               assert_true(MathMLFeatureDetection.has_mspace());
@@ -56,6 +56,7 @@
     <math><menclose notation="box"><mo id="moMenclose">&#x21A8;</mo><mspace width="1px" height="100px" style="background: magenta"></mspace></menclose></math>
     <math><mpadded lspace="10px"><mo id="moMpadded">&#x21A8;</mo><mspace width="1px" height="100px" style="background: magenta"></mspace></mpadded></math>
     <math><unknown><mo id="moUnknown">&#x21A8;</mo><mspace width="1px" height="100px" style="background: magenta"></mspace></unknown></math>
+    <math><mtable><mtr><mtd><mo id="moMtd">&#x21A8;</mo><mspace width="1px" height="100px" style="background: magenta"></mspace></mtd></mtr></mtable></math>
   </p>
 </body>
 </html>
diff --git a/mathml/presentation-markup/mrow/spacing.html b/mathml/presentation-markup/mrow/spacing.html
index bad3370..c066f72 100644
--- a/mathml/presentation-markup/mrow/spacing.html
+++ b/mathml/presentation-markup/mrow/spacing.html
@@ -22,7 +22,7 @@
   window.addEventListener("load", () => { loadAllFonts().then(runTests); });
   function runTests()
   {
-      ["Mrow", "Sqrt", "Style", "Error", "Phantom", "Math", "Menclose", "Mpadded", "Unknown"].forEach((tag) => {
+      ["Mrow", "Sqrt", "Style", "Error", "Phantom", "Math", "Menclose", "Mpadded", "Unknown", "Mtd"].forEach((tag) => {
           test(function() {
               assert_true(MathMLFeatureDetection.has_operator_spacing());
               var mrow = document.getElementById(tag);
@@ -48,6 +48,7 @@
     <math><menclose id="Menclose" notation="box"><mn>1</mn><mo lspace="50px">|</mo><mn>2</mn></menclose></math>
     <math><mpadded id="Mpadded" lspace="10px"><mn>1</mn><mo lspace="50px">|</mo><mn>2</mn></mpadded></math>
     <math><unknown id="Unknown"><mn>1</mn><mo lspace="50px">|</mo><mn>2</mn></unknown></math>
+    <math><mtable><mtr><mtd id="Mtd"><mn>1</mn><mo lspace="50px">|</mo><mn>2</mn></mtd></mtr></mtable></math>
   </p>
 </body>
 </html>
diff --git a/mathml/presentation-markup/tables/table-cell-mrow-layout.html b/mathml/presentation-markup/tables/table-cell-mrow-layout.html
new file mode 100644
index 0000000..1e76e39
--- /dev/null
+++ b/mathml/presentation-markup/tables/table-cell-mrow-layout.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Table cell mrow layout</title>
+<link rel="help" href="https://w3c.github.io/mathml-core/#table-or-matrix-mtable">
+<meta name="assert" content="Table cell relies on the mrow layout for their children.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
+<script src="/mathml/support/layout-comparison.js"></script>
+<style>
+  /* Remove default padding, since there is none on reference mrow element. */
+  mtd { padding: 0; }
+</style>
+</head>
+<body>
+  <div id="log"></div>
+  <p>
+    <math>
+      <mtable id="mtable">
+        <mtr>
+          <mtd id="mtd">
+            <mspace width="10px" depth="20px" height="20px" style="background: blue"/>
+            <mspace width="10px" depth="10px" height="30px" style="background: lightblue"/>
+            <mspace width="10px" depth="30px" height="10px" style="background: black"/>
+          </mtd>
+        </mtr>
+      </mtable>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mrow id="mtd-reference">
+        <mspace width="10px" depth="20px" height="20px" style="background: blue"/>
+        <mspace width="10px" depth="10px" height="30px" style="background: lightblue"/>
+        <mspace width="10px" depth="30px" height="10px" style="background: black"/>
+      </mrow>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mtable id="mtable-reference">
+        <mtr>
+          <mtd>
+            <mrow>
+              <mspace width="10px" depth="20px" height="20px" style="background: blue"/>
+              <mspace width="10px" depth="10px" height="30px" style="background: lightblue"/>
+              <mspace width="10px" depth="30px" height="10px" style="background: black"/>
+            </mrow>
+          </mtd>
+        </mtr>
+      </mtable>
+    </math>
+  </p>
+  <script>
+    const epsilon = 1;
+
+    test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+      let mtd = document.getElementById("mtd");
+      let reference = document.getElementById("mtd-reference");
+      compareLayout(mtd, reference, epsilon);
+    }, "<mtd> relies on mrow algorithm to layout its children");
+
+    test(function() {
+      assert_true(MathMLFeatureDetection.has_mspace());
+      let mtable = document.getElementById("mtable");
+      let reference = document.getElementById("mtable-reference");
+      compareLayout(mtable, reference, epsilon);
+    }, "<mtable> layout does not change if children of <mtd> elements are wrapped in an explicit <mrow>");
+  </script>
+</body>
+</html>
diff --git a/mathml/relations/html5-tree/dynamic-childlist-002.html b/mathml/relations/html5-tree/dynamic-childlist-002.html
index e4b4313..099401e 100644
--- a/mathml/relations/html5-tree/dynamic-childlist-002.html
+++ b/mathml/relations/html5-tree/dynamic-childlist-002.html
@@ -30,7 +30,17 @@
               element.appendChild(mrow);
           }
       }
-      math.appendChild(element);
+      if (FragmentHelper.isValidChildOfMrow(tag)) {
+          math.appendChild(element);
+      } else if (tag === "mtd") {
+          let mtr = FragmentHelper.createElement("mtr");
+          mtr.appendChild(element);
+          let mtable = FragmentHelper.createElement("mtable");
+          mtable.appendChild(mtr);
+          math.appendChild(mtable);
+      } else {
+          throw `Invalid argument: ${tag}`;
+      }
       return math;
   }
 
@@ -38,7 +48,7 @@
   window.addEventListener("load", function() {
 
       for (tag in MathMLFragments) {
-          if (!FragmentHelper.isValidChildOfMrow(tag))
+          if (!FragmentHelper.isValidChildOfMrow(tag) || tag === "mtd")
               continue;
 
           document.body.insertAdjacentHTML("beforeend", `<div style='display: none; background: pink;'>${tag}: <div></div><div></div><div></div></div>`);