From 3d16d9585346c6def6b4747a768c058379c739ab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Wang?=
Date: Mon, 30 Sep 2024 12:18:39 +0000
Subject: [PATCH] Bug 1916988 - Support CSS width/height properties on MathML
elements. r=emilio
This patch implements support for the width/height properties on
MathML elements [1]. The general algorithm from the spec is as
follows:
(1) The outcome of the math layout is a "math content box".
(2) The content box sets its size from computed width/height values. If
auto, it's the one of the "math content box". This patch ignores
percentage values for now [2] [3].
(3) math content box is shifted so that its inline-start and top edges
aligns with the ones of the content box. There are exceptions
elements like mfrac and munder/mover/munderover which instead
horizontally center the math content box within the content box.
For baseline adjustment, we follow what Chromium does, see [4].
(4) Padding+border are added around the content box. Note that we
ignore the box-sizing property for now [5].
The patch essentially tweaks the various MathML layout algorithms to
perform steps (3) and (4) before the calls to
GetBorderPaddingForPlace and InflateReflowAndBoundingMetrics.
[1] https://w3c.github.io/mathml-core/#layout-algorithms
[2] https://github.com/w3c/mathml-core/issues/76
[3] https://github.com/w3c/mathml-core/issues/77
[4] https://github.com/w3c/mathml-core/issues/259
[5] https://github.com/w3c/mathml-core/issues/257
Below is more information about test coverage:
- width-height-001: Verify that width, height, inline-size and block-size
properties sets the size of the content box. This test used to verify
they are ignored, this patch fixes the `` tag.
It also adds a test for the case the specified size is smaller than the
content (we force non empty descendants to make sure this content is
large enough) and to verify the width is used for the preferred width.
- width-height-002, width-height-003: These are reftests visually checking
offsets of the math content box within a larger content box (specified
by width/height) for the mtext, mrow, mpadded, mfrac, msqrt, mroot,
in LTR/RTL modes. In particular they allow to verify some painted
elements like fraction bar and radical symbols.
- width-height-004: This test more directly checks that the math content
box is horizontally centered within a larger content box for munder,
mover, munderover and mfrac. This patch extends the test to cover the
case when the math content box is wider (i.e. overflowing outside the
content box) and removes unnecessary specified height.
- width-height-005: New test for other layout algorithm that don't
center the math content box, checking inline-start edges of children
when a width is specified. We check both LTR/RTL modes and
wider/narrower content boxes.
- width-height-006: Same but checking the top edges for larger/smaller
height and verifying that baseline is perserved.
Differential Revision: https://phabricator.services.mozilla.com/D221436
---
layout/mathml/nsMathMLContainerFrame.cpp | 61 +-
layout/mathml/nsMathMLContainerFrame.h | 17 +
layout/mathml/nsMathMLTokenFrame.cpp | 6 +
layout/mathml/nsMathMLmencloseFrame.cpp | 10 +-
layout/mathml/nsMathMLmfracFrame.cpp | 12 +
layout/mathml/nsMathMLmfracFrame.h | 2 +
layout/mathml/nsMathMLmmultiscriptsFrame.cpp | 5 +
layout/mathml/nsMathMLmoFrame.cpp | 11 +-
layout/mathml/nsMathMLmpaddedFrame.cpp | 10 +-
layout/mathml/nsMathMLmrootFrame.cpp | 13 +-
layout/mathml/nsMathMLmspaceFrame.cpp | 6 +
layout/mathml/nsMathMLmunderoverFrame.cpp | 15 +
layout/mathml/nsMathMLmunderoverFrame.h | 2 +
.../fractions/frac-bar-003.html.ini | 2 -
.../spaces/mspace-width-height-001.html.ini | 12 -
.../relations/css-styling/display-2.html.ini | 4 +-
.../css-styling/width-height-001.html.ini | 144 -----
.../css-styling/width-height-002.html.ini | 2 -
.../css-styling/width-height-003.html.ini | 2 -
.../css-styling/width-height-005.html.ini | 6 +
.../css-styling/width-height-001.html | 32 +-
.../css-styling/width-height-004.html | 63 +-
.../css-styling/width-height-005.html | 541 ++++++++++++++++++
.../css-styling/width-height-006.html | 406 +++++++++++++
24 files changed, 1208 insertions(+), 176 deletions(-)
delete mode 100644 testing/web-platform/meta/mathml/presentation-markup/fractions/frac-bar-003.html.ini
delete mode 100644 testing/web-platform/meta/mathml/presentation-markup/spaces/mspace-width-height-001.html.ini
delete mode 100644 testing/web-platform/meta/mathml/relations/css-styling/width-height-001.html.ini
delete mode 100644 testing/web-platform/meta/mathml/relations/css-styling/width-height-002.html.ini
delete mode 100644 testing/web-platform/meta/mathml/relations/css-styling/width-height-003.html.ini
create mode 100644 testing/web-platform/meta/mathml/relations/css-styling/width-height-005.html.ini
create mode 100644 testing/web-platform/tests/mathml/relations/css-styling/width-height-005.html
create mode 100644 testing/web-platform/tests/mathml/relations/css-styling/width-height-006.html
diff --git a/layout/mathml/nsMathMLContainerFrame.cpp b/layout/mathml/nsMathMLContainerFrame.cpp
index 1d1af5ce24413..342bee7fd8697 100644
--- a/layout/mathml/nsMathMLContainerFrame.cpp
+++ b/layout/mathml/nsMathMLContainerFrame.cpp
@@ -133,6 +133,59 @@ void nsMathMLContainerFrame::InflateReflowAndBoundingMetrics(
aReflowOutput.Height() += aBorderPadding.TopBottom();
}
+nsMathMLContainerFrame::WidthAndHeightForPlaceAdjustment
+nsMathMLContainerFrame::GetWidthAndHeightForPlaceAdjustment(
+ const PlaceFlags& aFlags) {
+ WidthAndHeightForPlaceAdjustment sizes;
+ if (aFlags.contains(PlaceFlag::DoNotAdjustForWidthAndHeight)) {
+ return sizes;
+ }
+ const nsStylePosition* stylePos = StylePosition();
+ const auto& width = stylePos->mWidth;
+ // TODO: Resolve percentages.
+ // https://github.com/w3c/mathml-core/issues/76
+ if (width.ConvertsToLength()) {
+ sizes.width = Some(width.ToLength());
+ }
+ if (!aFlags.contains(PlaceFlag::IntrinsicSize)) {
+ // TODO: Resolve percentages.
+ // https://github.com/w3c/mathml-core/issues/77
+ const auto& height = stylePos->mHeight;
+ if (height.ConvertsToLength()) {
+ sizes.height = Some(height.ToLength());
+ }
+ }
+ return sizes;
+}
+
+nscoord nsMathMLContainerFrame::ApplyAdjustmentForWidthAndHeight(
+ const PlaceFlags& aFlags, const WidthAndHeightForPlaceAdjustment& aSizes,
+ ReflowOutput& aReflowOutput, nsBoundingMetrics& aBoundingMetrics) {
+ nscoord shiftX = 0;
+ if (aSizes.width) {
+ MOZ_ASSERT(!aFlags.contains(PlaceFlag::DoNotAdjustForWidthAndHeight));
+ auto width = *aSizes.width;
+ auto oldWidth = aReflowOutput.Width();
+ if (IsMathContentBoxHorizontallyCentered()) {
+ shiftX = (width - oldWidth) / 2;
+ } else if (StyleVisibility()->mDirection == StyleDirection::Rtl) {
+ shiftX = width - oldWidth;
+ }
+ aBoundingMetrics.leftBearing = 0;
+ aBoundingMetrics.rightBearing = width;
+ aBoundingMetrics.width = width;
+ aReflowOutput.mBoundingMetrics = aBoundingMetrics;
+ aReflowOutput.Width() = width;
+ }
+ if (aSizes.height) {
+ MOZ_ASSERT(!aFlags.contains(PlaceFlag::DoNotAdjustForWidthAndHeight));
+ MOZ_ASSERT(!aFlags.contains(PlaceFlag::IntrinsicSize));
+ auto height = *aSizes.height;
+ aReflowOutput.Height() = height;
+ }
+ return shiftX;
+}
+
// helper to get the preferred size that a container frame should use to fire
// the stretch on its stretchy child frames.
void nsMathMLContainerFrame::GetPreferredStretchSize(
@@ -1169,10 +1222,16 @@ nsresult nsMathMLContainerFrame::Place(DrawTarget* aDrawTarget,
aDesiredSize.SetBlockStartAscent(ascent);
aDesiredSize.mBoundingMetrics = mBoundingMetrics;
+ // Apply inline/block sizes to math content box.
+ auto sizes = GetWidthAndHeightForPlaceAdjustment(aFlags);
+ nscoord shiftX = ApplyAdjustmentForWidthAndHeight(aFlags, sizes, aDesiredSize,
+ mBoundingMetrics);
+
// Add padding+border.
auto borderPadding = GetBorderPaddingForPlace(aFlags);
InflateReflowAndBoundingMetrics(borderPadding, aDesiredSize,
mBoundingMetrics);
+ shiftX += borderPadding.left;
mReference.x = 0;
mReference.y = aDesiredSize.BlockStartAscent();
@@ -1180,7 +1239,7 @@ nsresult nsMathMLContainerFrame::Place(DrawTarget* aDrawTarget,
//////////////////
// Place Children
if (!aFlags.contains(PlaceFlag::MeasureOnly)) {
- PositionRowChildFrames(borderPadding.left, aDesiredSize.BlockStartAscent());
+ PositionRowChildFrames(shiftX, aDesiredSize.BlockStartAscent());
}
return NS_OK;
diff --git a/layout/mathml/nsMathMLContainerFrame.h b/layout/mathml/nsMathMLContainerFrame.h
index cb43559908a8a..ec3d28e816c1a 100644
--- a/layout/mathml/nsMathMLContainerFrame.h
+++ b/layout/mathml/nsMathMLContainerFrame.h
@@ -157,6 +157,11 @@ class nsMathMLContainerFrame : public nsContainerFrame, public nsMathMLFrame {
// place some radical symbol on top of them and finally add its
// padding/border around that radical symbol.
IgnoreBorderPadding,
+
+ // If DoNotAdjustForWidthAndHeight is set, the function will complete
+ // without setting the computed width and height after the math layout. This
+ // can be used similarly to IgnoreBorderPadding above.
+ DoNotAdjustForWidthAndHeight,
};
using PlaceFlags = mozilla::EnumSet;
@@ -247,6 +252,18 @@ class nsMathMLContainerFrame : public nsContainerFrame, public nsMathMLFrame {
nsMargin GetBorderPaddingForPlace(const PlaceFlags& aFlags);
+ struct WidthAndHeightForPlaceAdjustment {
+ mozilla::Maybe width;
+ mozilla::Maybe height;
+ };
+ WidthAndHeightForPlaceAdjustment GetWidthAndHeightForPlaceAdjustment(
+ const PlaceFlags& aFlags);
+
+ virtual bool IsMathContentBoxHorizontallyCentered() const { return false; }
+ nscoord ApplyAdjustmentForWidthAndHeight(
+ const PlaceFlags& aFlags, const WidthAndHeightForPlaceAdjustment& aSizes,
+ ReflowOutput& aReflowOutput, nsBoundingMetrics& aBoundingMetrics);
+
protected:
// helper to add the inter-spacing when
+
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+
+
+
+ X
+ X
+
+
+
+
+ X
+ X
+
+
+
+
+ X
+ X
+
+
+
+
+
+
+
+ X
+ X
+
+
+
+
+ X
+ X
+
+
+
+
+ X
+ X
+
+
+
+
+
+
+
+ X
+ X
+
+
+
+
+ X
+ X
+
+
+
+
+ X
+ X
+
+
+
+
+
+
+
+ X
+ X
+
+
+
+
+ X
+ X
+
+
+
+
+ X
+ X
+
+
+
+
+
+
+
+ X
+ X
+
+
+
+
+ X
+ X
+
+
+
+
+ X
+ X
+
+
+
+
+
+
+
+ X
+ X
+
+
+
+
+ X
+ X
+
+
+
+
+ X
+ X
+
+
+
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+
+
+
+ X
+ X
+ X
+
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+ X
+ X
+
+
+
+
+
+
+
+ X
+ X
+ X
+
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+ X
+ X
+
+
+
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+
+
+ X
+ X
+ X
+
+
+