From f3124c492b05af07f6c435de29ef4592924915a3 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 7 Sep 2023 16:03:20 +0100 Subject: [PATCH] LibWeb: Add `display: math` This is a `` keyword added by the MathML spec, and has the rough meaning of "display in the default way". It enables the standard layout rules for each MathML element (and is ignored for anything that isn't a MathML element). I believe we'll need an actual MathML formatting context to do the layout correctly, but we can at least support a couple of elements that behave the same as HTML ones. --- Userland/Libraries/LibWeb/CSS/Display.h | 7 +++++++ Userland/Libraries/LibWeb/CSS/Enums.json | 3 ++- Userland/Libraries/LibWeb/CSS/Identifiers.json | 1 + .../Libraries/LibWeb/CSS/Parser/Parser.cpp | 2 ++ .../Libraries/LibWeb/CSS/StyleComputer.cpp | 18 ++++++++++++++++++ Userland/Libraries/LibWeb/DOM/Element.cpp | 8 ++++++++ 6 files changed, 38 insertions(+), 1 deletion(-) diff --git a/Userland/Libraries/LibWeb/CSS/Display.h b/Userland/Libraries/LibWeb/CSS/Display.h index 4d922024b2..cc7c7df702 100644 --- a/Userland/Libraries/LibWeb/CSS/Display.h +++ b/Userland/Libraries/LibWeb/CSS/Display.h @@ -100,6 +100,7 @@ public: bool is_flex_inside() const { return is_outside_and_inside() && inside() == DisplayInside::Flex; } bool is_grid_inside() const { return is_outside_and_inside() && inside() == DisplayInside::Grid; } bool is_ruby_inside() const { return is_outside_and_inside() && inside() == DisplayInside::Ruby; } + bool is_math_inside() const { return is_outside_and_inside() && inside() == DisplayInside::Math; } enum class Short { None, @@ -119,6 +120,7 @@ public: Ruby, Table, InlineTable, + Math, }; static Display from_short(Short short_) @@ -158,6 +160,11 @@ public: return Display { DisplayOutside::Block, DisplayInside::Table }; case Short::InlineTable: return Display { DisplayOutside::Inline, DisplayInside::Table }; + case Short::Math: + // NOTE: The spec ( https://w3c.github.io/mathml-core/#new-display-math-value ) does not + // mention what the outside value for `display: math` should be. + // The UA stylesheet does `* { display: block math; }` so let's go with that. + return Display { DisplayOutside::Block, DisplayInside::Math }; } VERIFY_NOT_REACHED(); } diff --git a/Userland/Libraries/LibWeb/CSS/Enums.json b/Userland/Libraries/LibWeb/CSS/Enums.json index 003143999e..3b48c16f05 100644 --- a/Userland/Libraries/LibWeb/CSS/Enums.json +++ b/Userland/Libraries/LibWeb/CSS/Enums.json @@ -143,7 +143,8 @@ "table", "flex", "grid", - "ruby" + "ruby", + "math" ], "display-internal": [ "table-row-group", diff --git a/Userland/Libraries/LibWeb/CSS/Identifiers.json b/Userland/Libraries/LibWeb/CSS/Identifiers.json index 797043150b..7fddd4e6df 100644 --- a/Userland/Libraries/LibWeb/CSS/Identifiers.json +++ b/Userland/Libraries/LibWeb/CSS/Identifiers.json @@ -219,6 +219,7 @@ "listbox", "mark", "marktext", + "math", "max-content", "medium", "menu", diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 1a0bdb3aeb..e408fda748 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -3574,6 +3574,8 @@ RefPtr Parser::parse_display_value(Vector const& com return Display::from_short(Display::Short::Grid); case DisplayInside::Ruby: return Display::from_short(Display::Short::Ruby); + case DisplayInside::Math: + return Display::from_short(Display::Short::Math); } } diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp index 5e69ff9a1e..66101ead36 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -72,6 +72,7 @@ #include #include #include +#include #include #include #include @@ -2243,6 +2244,23 @@ void StyleComputer::transform_box_type_if_needed(StyleProperties& style, DOM::El auto new_display = display; + if (display.is_math_inside()) { + // https://w3c.github.io/mathml-core/#new-display-math-value + // For elements that are not MathML elements, if the specified value of display is inline math or block math + // then the computed value is block flow and inline flow respectively. + if (element.namespace_() != Namespace::MathML) + new_display = CSS::Display { display.outside(), CSS::DisplayInside::Flow }; + // For the mtable element the computed value is block table and inline table respectively. + else if (element.tag_name().equals_ignoring_ascii_case("mtable"sv)) + new_display = CSS::Display { display.outside(), CSS::DisplayInside::Table }; + // For the mtr element, the computed value is table-row. + else if (element.tag_name().equals_ignoring_ascii_case("mtr"sv)) + new_display = CSS::Display { CSS::DisplayInternal::TableRow }; + // For the mtd element, the computed value is table-cell. + else if (element.tag_name().equals_ignoring_ascii_case("mtd"sv)) + new_display = CSS::Display { CSS::DisplayInternal::TableCell }; + } + switch (required_box_type_transformation(style, element, pseudo_element)) { case BoxTypeTransformation::None: break; diff --git a/Userland/Libraries/LibWeb/DOM/Element.cpp b/Userland/Libraries/LibWeb/DOM/Element.cpp index 2ea33c771b..79b1de2931 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.cpp +++ b/Userland/Libraries/LibWeb/DOM/Element.cpp @@ -373,6 +373,14 @@ JS::GCPtr Element::create_layout_node_for_display_type(DOM::Docume return document.heap().allocate_without_realm(document, element, move(style)); } + if (display.is_math_inside()) { + // https://w3c.github.io/mathml-core/#new-display-math-value + // MathML elements with a computed display value equal to block math or inline math control box generation + // and layout according to their tag name, as described in the relevant sections. + // FIXME: Figure out what kind of node we should make for them. For now, we'll stick with a generic Box. + return document.heap().allocate_without_realm(document, element, move(style)); + } + if (display.is_inline_outside()) { if (display.is_flow_root_inside()) return document.heap().allocate_without_realm(document, element, move(style));