LibWeb: Implement the math-depth CSS property

This one is a bit fun because it can be `add(<integer>)` or `auto-add`,
but children have to inherit the computed value not the specified one.
We also have to compute it before computing the font-size, because of
`font-size: math` which will be implemented later.
This commit is contained in:
Sam Atkins 2023-09-07 15:29:54 +01:00 committed by Sam Atkins
parent 53f3ed026a
commit 6476dea898
Notes: sideshowbarker 2024-07-17 07:08:37 +09:00
19 changed files with 285 additions and 11 deletions

View file

@ -1,6 +1,6 @@
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
BlockContainer <html> at (0,0) content-size 800x600 [BFC] children: not-inline
BlockContainer <body> at (8,8) content-size 784x201.125 children: not-inline
BlockContainer <body> at (8,8) content-size 784x201.15625 children: not-inline
BlockContainer <(anonymous)> at (8,8) content-size 784x155.46875 children: inline
line 0 width: 312, height: 155.46875, bottom: 155.46875, baseline: 152
frag 0 from SVGSVGBox start: 0, length: 0, rect: [9,9 300x150]
@ -16,24 +16,24 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline
Box <a> at (8,8) content-size 100x100 children: inline
TextNode <#text>
TextNode <#text>
BlockContainer <div> at (9,164.46875) content-size 782x43.65625 children: inline
line 0 width: 101.453125, height: 43.65625, bottom: 43.65625, baseline: 33.828125
BlockContainer <div> at (9,164.46875) content-size 782x43.6875 children: inline
line 0 width: 101.453125, height: 43.6875, bottom: 43.6875, baseline: 33.84375
frag 0 from TextNode start: 0, length: 5, rect: [10,164.46875 99.453125x43.671875]
"Hello"
InlineNode <a>
TextNode <#text>
BlockContainer <(anonymous)> at (8,209.125) content-size 784x0 children: inline
BlockContainer <(anonymous)> at (8,209.15625) content-size 784x0 children: inline
TextNode <#text>
ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x600]
PaintableWithLines (BlockContainer<BODY>) [8,8 784x201.125]
PaintableWithLines (BlockContainer<BODY>) [8,8 784x201.15625]
PaintableWithLines (BlockContainer(anonymous)) [8,8 784x155.46875]
SVGSVGPaintable (SVGSVGBox<svg>) [8,8 302x152]
TextPaintable (TextNode<#text>)
PaintableBox (Box<math>) [318,50 2x110] overflow: [8,8 100x100]
PaintableBox (Box<a>) [8,8 100x100]
PaintableWithLines (BlockContainer<DIV>) [8,163.46875 784x45.65625] overflow: [9,164.46875 782x43.671875]
PaintableWithLines (BlockContainer<DIV>) [8,163.46875 784x45.6875]
InlinePaintable (InlineNode<A>)
TextPaintable (TextNode<#text>)
PaintableWithLines (BlockContainer(anonymous)) [8,209.125 784x0]
PaintableWithLines (BlockContainer(anonymous)) [8,209.15625 784x0]

View file

@ -0,0 +1,6 @@
0
1
1
1
2
2

View file

@ -0,0 +1,26 @@
<script src="../include.js"></script>
<script>
test(() => {
println(getComputedStyle(document.getElementById("a")).mathDepth);
println(getComputedStyle(document.getElementById("b")).mathDepth);
println(getComputedStyle(document.getElementById("c")).mathDepth);
println(getComputedStyle(document.getElementById("d")).mathDepth);
println(getComputedStyle(document.getElementById("e")).mathDepth);
println(getComputedStyle(document.getElementById("f")).mathDepth);
});
</script>
<style>
body { math-depth: 0; }
ul > li { math-depth: add(1); }
</style>
<body>
<ul id="a">
<li id="b"></li>
<li id="c">
<ul id="d">
<li id="e"></li>
<li id="f"></li>
</ul>
</li>
</ul>
</body>

View file

@ -111,6 +111,7 @@ set(SOURCES
CSS/StyleValues/LengthStyleValue.cpp
CSS/StyleValues/LinearGradientStyleValue.cpp
CSS/StyleValues/ListStyleStyleValue.cpp
CSS/StyleValues/MathDepthStyleValue.cpp
CSS/StyleValues/NumberStyleValue.cpp
CSS/StyleValues/OverflowStyleValue.cpp
CSS/StyleValues/PlaceContentStyleValue.cpp

View file

@ -123,6 +123,7 @@ public:
static CSS::MathShift math_shift() { return CSS::MathShift::Normal; }
static CSS::MathStyle math_style() { return CSS::MathStyle::Normal; }
static int math_depth() { return 0; }
};
enum class BackgroundSize {
@ -351,6 +352,7 @@ public:
CSS::MathShift math_shift() const { return m_inherited.math_shift; }
CSS::MathStyle math_style() const { return m_inherited.math_style; }
int math_depth() const { return m_inherited.math_depth; }
ComputedValues clone_inherited_values() const
{
@ -394,6 +396,7 @@ protected:
CSS::MathShift math_shift { InitialValues::math_shift() };
CSS::MathStyle math_style { InitialValues::math_style() };
int math_depth { InitialValues::math_depth() };
} m_inherited;
struct {
@ -591,6 +594,7 @@ public:
void set_math_shift(CSS::MathShift value) { m_inherited.math_shift = value; }
void set_math_style(CSS::MathStyle value) { m_inherited.math_style = value; }
void set_math_depth(int value) { m_inherited.math_depth = value; }
};
}

View file

@ -73,6 +73,7 @@
"anywhere",
"appworkspace",
"auto",
"auto-add",
"back",
"background",
"backwards",

View file

@ -67,6 +67,7 @@
#include <LibWeb/CSS/StyleValues/IntegerStyleValue.h>
#include <LibWeb/CSS/StyleValues/LengthStyleValue.h>
#include <LibWeb/CSS/StyleValues/ListStyleStyleValue.h>
#include <LibWeb/CSS/StyleValues/MathDepthStyleValue.h>
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
#include <LibWeb/CSS/StyleValues/OverflowStyleValue.h>
#include <LibWeb/CSS/StyleValues/PercentageStyleValue.h>
@ -4455,6 +4456,51 @@ RefPtr<StyleValue> Parser::parse_list_style_value(Vector<ComponentValue> const&
return ListStyleStyleValue::create(list_position.release_nonnull(), list_image.release_nonnull(), list_type.release_nonnull());
}
RefPtr<StyleValue> Parser::parse_math_depth_value(Vector<ComponentValue> const& component_values)
{
// https://w3c.github.io/mathml-core/#propdef-math-depth
// auto-add | add(<integer>) | <integer>
auto tokens = TokenStream { component_values };
tokens.skip_whitespace();
auto token = tokens.next_token();
tokens.skip_whitespace();
if (tokens.has_next_token())
return nullptr;
// auto-add
if (token.is_ident("auto-add"sv))
return MathDepthStyleValue::create_auto_add();
// FIXME: Make it easier to parse "thing that might be <bar> or literally anything that resolves to it" and get rid of this
auto parse_something_that_resolves_to_integer = [this](ComponentValue& token) -> RefPtr<StyleValue> {
if (token.is(Token::Type::Number) && token.token().number().is_integer())
return IntegerStyleValue::create(token.token().to_integer());
if (auto value = parse_calculated_value(token); value && value->resolves_to_number())
return value;
return nullptr;
};
// add(<integer>)
if (token.is_function("add"sv)) {
auto add_tokens = TokenStream { token.function().values() };
add_tokens.skip_whitespace();
auto integer_token = add_tokens.next_token();
add_tokens.skip_whitespace();
if (add_tokens.has_next_token())
return nullptr;
if (auto integer_value = parse_something_that_resolves_to_integer(integer_token))
return MathDepthStyleValue::create_add(integer_value.release_nonnull());
return nullptr;
}
// <integer>
if (auto integer_value = parse_something_that_resolves_to_integer(token))
return MathDepthStyleValue::create_integer(integer_value.release_nonnull());
return nullptr;
}
RefPtr<StyleValue> Parser::parse_overflow_value(Vector<ComponentValue> const& component_values)
{
auto tokens = TokenStream { component_values };
@ -5799,6 +5845,10 @@ Parser::ParseErrorOr<NonnullRefPtr<StyleValue>> Parser::parse_css_value(Property
if (auto parsed_value = parse_list_style_value(component_values))
return parsed_value.release_nonnull();
return ParseError::SyntaxError;
case PropertyID::MathDepth:
if (auto parsed_value = parse_math_depth_value(component_values))
return parsed_value.release_nonnull();
return ParseError::SyntaxError;
case PropertyID::Overflow:
if (auto parsed_value = parse_overflow_value(component_values))
return parsed_value.release_nonnull();

View file

@ -238,6 +238,7 @@ private:
RefPtr<StyleValue> parse_font_value(Vector<ComponentValue> const&);
RefPtr<StyleValue> parse_font_family_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue> parse_list_style_value(Vector<ComponentValue> const&);
RefPtr<StyleValue> parse_math_depth_value(Vector<ComponentValue> const&);
RefPtr<StyleValue> parse_overflow_value(Vector<ComponentValue> const&);
RefPtr<StyleValue> parse_place_content_value(Vector<ComponentValue> const&);
RefPtr<StyleValue> parse_place_items_value(Vector<ComponentValue> const&);

View file

@ -1479,6 +1479,17 @@
"unitless-length"
]
},
"math-depth": {
"inherited": true,
"initial": "0",
"__comment": "FIXME: `add(<integer>)` is also valid but we can't represent that here yet.",
"valid-types": [
"integer"
],
"valid-identifiers": [
"auto-add"
]
},
"math-shift": {
"inherited": true,
"initial": "normal",

View file

@ -51,6 +51,7 @@
#include <LibWeb/CSS/StyleValues/IntegerStyleValue.h>
#include <LibWeb/CSS/StyleValues/LengthStyleValue.h>
#include <LibWeb/CSS/StyleValues/ListStyleStyleValue.h>
#include <LibWeb/CSS/StyleValues/MathDepthStyleValue.h>
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
#include <LibWeb/CSS/StyleValues/OverflowStyleValue.h>
#include <LibWeb/CSS/StyleValues/PercentageStyleValue.h>
@ -2314,6 +2315,7 @@ void StyleComputer::transform_box_type_if_needed(StyleProperties& style, DOM::El
NonnullRefPtr<StyleProperties> StyleComputer::create_document_style() const
{
auto style = StyleProperties::create();
compute_math_depth(style, nullptr, {});
compute_font(style, nullptr, {});
compute_defaulted_values(style, nullptr, {});
absolutize_values(style, nullptr, {});
@ -2346,16 +2348,19 @@ ErrorOr<RefPtr<StyleProperties>> StyleComputer::compute_style_impl(DOM::Element&
if (mode == ComputeStyleMode::CreatePseudoElementStyleIfNeeded && !did_match_any_pseudo_element_rules)
return nullptr;
// 2. Compute the font, since that may be needed for font-relative CSS units
// 2. Compute the math-depth property, since that might affect the font-size
compute_math_depth(style, &element, pseudo_element);
// 3. Compute the font, since that may be needed for font-relative CSS units
compute_font(style, &element, pseudo_element);
// 3. Absolutize values, turning font/viewport relative lengths into absolute lengths
// 4. Absolutize values, turning font/viewport relative lengths into absolute lengths
absolutize_values(style, &element, pseudo_element);
// 4. Default the values, applying inheritance and 'initial' as needed
// 5. Default the values, applying inheritance and 'initial' as needed
compute_defaulted_values(style, &element, pseudo_element);
// 5. Run automatic box type transformations
// 6. Run automatic box type transformations
transform_box_type_if_needed(style, element, pseudo_element);
return style;
@ -2605,4 +2610,57 @@ void StyleComputer::load_fonts_from_sheet(CSSStyleSheet const& sheet)
}
}
void StyleComputer::compute_math_depth(StyleProperties& style, DOM::Element const* element, Optional<CSS::Selector::PseudoElement> pseudo_element) const
{
// https://w3c.github.io/mathml-core/#propdef-math-depth
// First, ensure that the relevant CSS properties have been defaulted.
// FIXME: This should be more sophisticated.
compute_defaulted_property_value(style, element, CSS::PropertyID::MathDepth, pseudo_element);
compute_defaulted_property_value(style, element, CSS::PropertyID::MathStyle, pseudo_element);
auto inherited_math_depth = [&]() {
if (!element || !element->parent_element())
return InitialValues::math_depth();
return element->parent_element()->computed_css_values()->math_depth();
};
auto value = style.property(CSS::PropertyID::MathDepth);
if (!value->is_math_depth()) {
style.set_math_depth(inherited_math_depth());
return;
}
auto& math_depth = value->as_math_depth();
auto resolve_integer = [&](StyleValue const& integer_value) {
if (integer_value.is_integer())
return integer_value.as_integer().integer();
if (integer_value.is_calculated())
return integer_value.as_calculated().resolve_integer().value();
VERIFY_NOT_REACHED();
};
// The computed value of the math-depth value is determined as follows:
// - If the specified value of math-depth is auto-add and the inherited value of math-style is compact
// then the computed value of math-depth of the element is its inherited value plus one.
if (math_depth.is_auto_add() && style.property(CSS::PropertyID::MathStyle)->to_identifier() == CSS::ValueID::Compact) {
style.set_math_depth(inherited_math_depth() + 1);
return;
}
// - If the specified value of math-depth is of the form add(<integer>) then the computed value of
// math-depth of the element is its inherited value plus the specified integer.
if (math_depth.is_add()) {
style.set_math_depth(inherited_math_depth() + resolve_integer(*math_depth.integer_value()));
return;
}
// - If the specified value of math-depth is of the form <integer> then the computed value of math-depth
// of the element is the specified integer.
if (math_depth.is_integer()) {
style.set_math_depth(resolve_integer(*math_depth.integer_value()));
return;
}
// - Otherwise, the computed value of math-depth of the element is the inherited one.
style.set_math_depth(inherited_math_depth());
}
}

View file

@ -127,6 +127,7 @@ private:
static RefPtr<Gfx::Font const> find_matching_font_weight_descending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, bool inclusive);
RefPtr<Gfx::Font const> font_matching_algorithm(FontFaceKey const& key, float font_size_in_pt) const;
void compute_font(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement>) const;
void compute_math_depth(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement>) const;
void compute_defaulted_values(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement>) const;
void absolutize_values(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement>) const;
void transform_box_type_if_needed(StyleProperties&, DOM::Element const&, Optional<CSS::Selector::PseudoElement>) const;

View file

@ -19,6 +19,7 @@
#include <LibWeb/CSS/StyleValues/IdentifierStyleValue.h>
#include <LibWeb/CSS/StyleValues/IntegerStyleValue.h>
#include <LibWeb/CSS/StyleValues/LengthStyleValue.h>
#include <LibWeb/CSS/StyleValues/MathDepthStyleValue.h>
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
#include <LibWeb/CSS/StyleValues/PercentageStyleValue.h>
#include <LibWeb/CSS/StyleValues/RectStyleValue.h>
@ -982,4 +983,11 @@ Color StyleProperties::stop_color() const
return Color::Black;
}
void StyleProperties::set_math_depth(int math_depth)
{
m_math_depth = math_depth;
// Make our children inherit our computed value, not our specified value.
set_property(PropertyID::MathDepth, MathDepthStyleValue::create_integer(IntegerStyleValue::create(math_depth)));
}
}

View file

@ -143,6 +143,9 @@ public:
Optional<CSS::Position> position() const;
Optional<int> z_index() const;
void set_math_depth(int math_depth);
int math_depth() const { return m_math_depth; }
static NonnullRefPtr<Gfx::Font const> font_fallback(bool monospace, bool bold);
private:
@ -152,6 +155,7 @@ private:
Optional<CSS::Overflow> overflow(CSS::PropertyID) const;
Vector<CSS::ShadowData> shadow(CSS::PropertyID, Layout::Node const&) const;
int m_math_depth { InitialValues::math_depth() };
mutable RefPtr<Gfx::Font const> m_font;
};

View file

@ -47,6 +47,7 @@
#include <LibWeb/CSS/StyleValues/LengthStyleValue.h>
#include <LibWeb/CSS/StyleValues/LinearGradientStyleValue.h>
#include <LibWeb/CSS/StyleValues/ListStyleStyleValue.h>
#include <LibWeb/CSS/StyleValues/MathDepthStyleValue.h>
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
#include <LibWeb/CSS/StyleValues/OverflowStyleValue.h>
#include <LibWeb/CSS/StyleValues/PercentageStyleValue.h>

View file

@ -119,6 +119,7 @@ using StyleValueVector = Vector<ValueComparingNonnullRefPtr<StyleValue const>>;
__ENUMERATE_STYLE_VALUE_TYPE(Length, length) \
__ENUMERATE_STYLE_VALUE_TYPE(LinearGradient, linear_gradient) \
__ENUMERATE_STYLE_VALUE_TYPE(ListStyle, list_style) \
__ENUMERATE_STYLE_VALUE_TYPE(MathDepth, math_depth) \
__ENUMERATE_STYLE_VALUE_TYPE(Number, number) \
__ENUMERATE_STYLE_VALUE_TYPE(Overflow, overflow) \
__ENUMERATE_STYLE_VALUE_TYPE(Percentage, percentage) \

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "MathDepthStyleValue.h"
namespace Web::CSS {
ValueComparingNonnullRefPtr<MathDepthStyleValue> MathDepthStyleValue::create_auto_add()
{
return adopt_ref(*new (nothrow) MathDepthStyleValue(MathDepthType::AutoAdd));
}
ValueComparingNonnullRefPtr<MathDepthStyleValue> MathDepthStyleValue::create_add(ValueComparingNonnullRefPtr<StyleValue const> integer_value)
{
return adopt_ref(*new (nothrow) MathDepthStyleValue(MathDepthType::Add, move(integer_value)));
}
ValueComparingNonnullRefPtr<MathDepthStyleValue> MathDepthStyleValue::create_integer(ValueComparingNonnullRefPtr<StyleValue const> integer_value)
{
return adopt_ref(*new (nothrow) MathDepthStyleValue(MathDepthType::Integer, move(integer_value)));
}
MathDepthStyleValue::MathDepthStyleValue(MathDepthType type, ValueComparingRefPtr<StyleValue const> integer_value)
: StyleValueWithDefaultOperators(Type::MathDepth)
, m_type(type)
, m_integer_value(move(integer_value))
{
}
bool MathDepthStyleValue::properties_equal(MathDepthStyleValue const& other) const
{
return m_type == other.m_type
&& m_integer_value == other.m_integer_value;
}
String MathDepthStyleValue::to_string() const
{
switch (m_type) {
case MathDepthType::AutoAdd:
return "auto-add"_string;
case MathDepthType::Add:
return MUST(String::formatted("add({})", m_integer_value->to_string()));
case MathDepthType::Integer:
return m_integer_value->to_string();
}
VERIFY_NOT_REACHED();
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/CSS/StyleValue.h>
namespace Web::CSS {
class MathDepthStyleValue : public StyleValueWithDefaultOperators<MathDepthStyleValue> {
public:
static ValueComparingNonnullRefPtr<MathDepthStyleValue> create_auto_add();
static ValueComparingNonnullRefPtr<MathDepthStyleValue> create_add(ValueComparingNonnullRefPtr<StyleValue const> integer_value);
static ValueComparingNonnullRefPtr<MathDepthStyleValue> create_integer(ValueComparingNonnullRefPtr<StyleValue const> integer_value);
virtual ~MathDepthStyleValue() override = default;
bool is_auto_add() const { return m_type == MathDepthType::AutoAdd; }
bool is_add() const { return m_type == MathDepthType::Add; }
bool is_integer() const { return m_type == MathDepthType::Integer; }
auto integer_value() const
{
VERIFY(!m_integer_value.is_null());
return m_integer_value;
}
virtual String to_string() const override;
bool properties_equal(MathDepthStyleValue const& other) const;
private:
enum class MathDepthType {
AutoAdd,
Add,
Integer,
};
MathDepthStyleValue(MathDepthType type, ValueComparingRefPtr<StyleValue const> integer_value = nullptr);
MathDepthType m_type;
ValueComparingRefPtr<StyleValue const> m_integer_value;
};
}

View file

@ -137,6 +137,7 @@ class LengthPercentage;
class LengthStyleValue;
class LinearGradientStyleValue;
class ListStyleStyleValue;
class MathDepthStyleValue;
class MediaFeatureValue;
class MediaList;
class MediaQuery;

View file

@ -14,6 +14,7 @@
#include <LibWeb/CSS/StyleValues/IdentifierStyleValue.h>
#include <LibWeb/CSS/StyleValues/IntegerStyleValue.h>
#include <LibWeb/CSS/StyleValues/LengthStyleValue.h>
#include <LibWeb/CSS/StyleValues/MathDepthStyleValue.h>
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
#include <LibWeb/CSS/StyleValues/PercentageStyleValue.h>
#include <LibWeb/CSS/StyleValues/RatioStyleValue.h>
@ -797,6 +798,8 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style)
if (auto math_style = value_id_to_math_style(math_style_value->to_identifier()); math_style.has_value())
computed_values.set_math_style(math_style.value());
computed_values.set_math_depth(computed_style.math_depth());
// Update any anonymous children that inherit from this node.
// FIXME: This is pretty hackish. It would be nicer if they shared the inherited style
// data structure somehow, so this wasn't necessary.