mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 08:57:47 +00:00
LibWeb: Bring CSS line-height
closer to other engines
This patch makes a few changes to the way we calculate line-height: - `line-height: normal` is now resolved using metrics from the used font (specifically, round(A + D + lineGap)). - `line-height: calc(...)` is now resolved at style compute time. - `line-height` values are now absolutized at style compute time. As a consequence of the above, we no longer need to walk the DOM ancestor chain looking for line-heights during style computation. Instead, values are inherited, resolved and absolutized locally. This is not only much faster, but also makes our line-height metrics match those of other engines like Gecko and Blink.
This commit is contained in:
parent
f0722671c3
commit
e7de5cb4d2
385 changed files with 6889 additions and 6893 deletions
|
@ -19,14 +19,14 @@
|
|||
|
||||
namespace Web::CSS {
|
||||
|
||||
Length::FontMetrics::FontMetrics(CSSPixels font_size, Gfx::FontPixelMetrics const& pixel_metrics, CSSPixels line_height)
|
||||
Length::FontMetrics::FontMetrics(CSSPixels font_size, Gfx::FontPixelMetrics const& pixel_metrics)
|
||||
: font_size(font_size)
|
||||
, x_height(pixel_metrics.x_height)
|
||||
// FIXME: This is only approximately the cap height. The spec suggests measuring the "O" glyph:
|
||||
// https://www.w3.org/TR/css-values-4/#cap
|
||||
, cap_height(pixel_metrics.ascent)
|
||||
, zero_advance(pixel_metrics.advance_of_ascii_zero + pixel_metrics.glyph_spacing)
|
||||
, line_height(line_height)
|
||||
, line_height(round(pixel_metrics.line_spacing()))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -138,8 +138,8 @@ Length::ResolutionContext Length::ResolutionContext::for_layout_node(Layout::Nod
|
|||
VERIFY(root_element->layout_node());
|
||||
return Length::ResolutionContext {
|
||||
.viewport_rect = node.navigable()->viewport_rect(),
|
||||
.font_metrics = { node.computed_values().font_size(), node.first_available_font().pixel_metrics(), node.line_height() },
|
||||
.root_font_metrics = { root_element->layout_node()->computed_values().font_size(), root_element->layout_node()->first_available_font().pixel_metrics(), root_element->layout_node()->line_height() },
|
||||
.font_metrics = { node.computed_values().font_size(), node.first_available_font().pixel_metrics() },
|
||||
.root_font_metrics = { root_element->layout_node()->computed_values().font_size(), root_element->layout_node()->first_available_font().pixel_metrics() },
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -170,12 +170,10 @@ CSSPixels Length::to_px(Layout::Node const& layout_node) const
|
|||
FontMetrics font_metrics {
|
||||
layout_node.computed_values().font_size(),
|
||||
layout_node.first_available_font().pixel_metrics(),
|
||||
layout_node.line_height()
|
||||
};
|
||||
FontMetrics root_font_metrics {
|
||||
root_element->layout_node()->computed_values().font_size(),
|
||||
root_element->layout_node()->first_available_font().pixel_metrics(),
|
||||
root_element->layout_node()->line_height()
|
||||
};
|
||||
|
||||
return font_relative_length_to_px(font_metrics, root_font_metrics);
|
||||
|
|
|
@ -72,7 +72,7 @@ public:
|
|||
};
|
||||
|
||||
struct FontMetrics {
|
||||
FontMetrics(CSSPixels font_size, Gfx::FontPixelMetrics const&, CSSPixels line_height);
|
||||
FontMetrics(CSSPixels font_size, Gfx::FontPixelMetrics const&);
|
||||
|
||||
CSSPixels font_size;
|
||||
CSSPixels x_height;
|
||||
|
|
|
@ -169,7 +169,7 @@ bool MediaFeature::compare(HTML::Window const& window, MediaFeatureValue left, C
|
|||
|
||||
auto const& initial_font = window.associated_document().style_computer().initial_font();
|
||||
Gfx::FontPixelMetrics const& initial_font_metrics = initial_font.pixel_metrics();
|
||||
Length::FontMetrics font_metrics { initial_font.presentation_size(), initial_font_metrics, CSSPixels::nearest_value_for(initial_font_metrics.line_spacing()) };
|
||||
Length::FontMetrics font_metrics { initial_font.presentation_size(), initial_font_metrics };
|
||||
|
||||
left_px = left.length().to_px(viewport_rect, font_metrics, font_metrics);
|
||||
right_px = right.length().to_px(viewport_rect, font_metrics, font_metrics);
|
||||
|
|
|
@ -81,7 +81,7 @@ static NonnullRefPtr<StyleValue const> get_inherit_value(JS::Realm& initial_valu
|
|||
|
||||
StyleComputer::StyleComputer(DOM::Document& document)
|
||||
: m_document(document)
|
||||
, m_default_font_metrics(16, Gfx::FontDatabase::default_font().pixel_metrics(), 16)
|
||||
, m_default_font_metrics(16, Gfx::FontDatabase::default_font().pixel_metrics())
|
||||
, m_root_element_font_metrics(m_default_font_metrics)
|
||||
{
|
||||
}
|
||||
|
@ -1529,9 +1529,9 @@ Length::FontMetrics StyleComputer::calculate_root_element_font_metrics(StyleProp
|
|||
auto root_value = style.property(CSS::PropertyID::FontSize);
|
||||
|
||||
auto font_pixel_metrics = style.first_available_computed_font().pixel_metrics();
|
||||
Length::FontMetrics font_metrics { m_default_font_metrics.font_size, font_pixel_metrics, CSSPixels::nearest_value_for(font_pixel_metrics.line_spacing()) };
|
||||
Length::FontMetrics font_metrics { m_default_font_metrics.font_size, font_pixel_metrics };
|
||||
font_metrics.font_size = root_value->as_length().length().to_px(viewport_rect(), font_metrics, font_metrics);
|
||||
font_metrics.line_height = style.line_height(viewport_rect(), font_metrics, font_metrics);
|
||||
font_metrics.line_height = style.compute_line_height(viewport_rect(), font_metrics, font_metrics);
|
||||
|
||||
return font_metrics;
|
||||
}
|
||||
|
@ -1645,7 +1645,6 @@ RefPtr<Gfx::FontCascadeList const> StyleComputer::compute_font_for_style_values(
|
|||
// FIXME: Should be based on "user's default font size"
|
||||
CSSPixels font_size_in_px = 16;
|
||||
|
||||
auto parent_line_height = parent_or_root_element_line_height(element, pseudo_element);
|
||||
Gfx::FontPixelMetrics font_pixel_metrics;
|
||||
if (parent_element && parent_element->computed_css_values())
|
||||
font_pixel_metrics = parent_element->computed_css_values()->first_available_computed_font().pixel_metrics();
|
||||
|
@ -1657,15 +1656,14 @@ RefPtr<Gfx::FontCascadeList const> StyleComputer::compute_font_for_style_values(
|
|||
auto value = parent_element->computed_css_values()->property(CSS::PropertyID::FontSize);
|
||||
if (value->is_length()) {
|
||||
auto length = value->as_length().length();
|
||||
auto parent_line_height = parent_or_root_element_line_height(parent_element, {});
|
||||
if (length.is_absolute() || length.is_relative()) {
|
||||
Length::FontMetrics font_metrics { font_size_in_px, font_pixel_metrics, parent_line_height };
|
||||
Length::FontMetrics font_metrics { font_size_in_px, font_pixel_metrics };
|
||||
return length.to_px(viewport_rect(), font_metrics, m_root_element_font_metrics);
|
||||
}
|
||||
}
|
||||
return font_size_in_px;
|
||||
};
|
||||
Length::FontMetrics font_metrics { parent_font_size(), font_pixel_metrics, parent_line_height };
|
||||
Length::FontMetrics font_metrics { parent_font_size(), font_pixel_metrics };
|
||||
|
||||
if (font_size.is_identifier()) {
|
||||
// https://w3c.github.io/csswg-drafts/css-fonts/#absolute-size-mapping
|
||||
|
@ -1924,30 +1922,12 @@ Gfx::Font const& StyleComputer::initial_font() const
|
|||
return StyleProperties::font_fallback(false, false);
|
||||
}
|
||||
|
||||
CSSPixels StyleComputer::parent_or_root_element_line_height(DOM::Element const* element, Optional<CSS::Selector::PseudoElement::Type> pseudo_element) const
|
||||
void StyleComputer::absolutize_values(StyleProperties& style) const
|
||||
{
|
||||
auto* parent_element = element_to_inherit_style_from(element, pseudo_element);
|
||||
if (!parent_element)
|
||||
return m_root_element_font_metrics.line_height;
|
||||
auto const* computed_values = parent_element->computed_css_values();
|
||||
if (!computed_values)
|
||||
return m_root_element_font_metrics.line_height;
|
||||
auto parent_font_pixel_metrics = computed_values->first_available_computed_font().pixel_metrics();
|
||||
auto parent_font_size = computed_values->property(CSS::PropertyID::FontSize)->as_length().length();
|
||||
// FIXME: Can the parent font size be non-absolute here?
|
||||
auto parent_font_size_value = parent_font_size.is_absolute() ? parent_font_size.absolute_length_to_px() : m_root_element_font_metrics.font_size;
|
||||
auto parent_parent_line_height = parent_or_root_element_line_height(parent_element, {});
|
||||
Length::FontMetrics parent_font_metrics { parent_font_size_value, parent_font_pixel_metrics, parent_parent_line_height };
|
||||
return computed_values->line_height(viewport_rect(), parent_font_metrics, m_root_element_font_metrics);
|
||||
}
|
||||
|
||||
void StyleComputer::absolutize_values(StyleProperties& style, DOM::Element const* element, Optional<CSS::Selector::PseudoElement::Type> pseudo_element) const
|
||||
{
|
||||
auto parent_or_root_line_height = parent_or_root_element_line_height(element, pseudo_element);
|
||||
|
||||
auto font_pixel_metrics = style.first_available_computed_font().pixel_metrics();
|
||||
|
||||
Length::FontMetrics font_metrics { m_root_element_font_metrics.font_size, font_pixel_metrics, parent_or_root_line_height };
|
||||
Length::FontMetrics font_metrics {
|
||||
m_root_element_font_metrics.font_size,
|
||||
style.first_available_computed_font().pixel_metrics()
|
||||
};
|
||||
|
||||
auto font_size = style.property(CSS::PropertyID::FontSize)->as_length().length().to_px(viewport_rect(), font_metrics, m_root_element_font_metrics);
|
||||
font_metrics.font_size = font_size;
|
||||
|
@ -1962,7 +1942,7 @@ void StyleComputer::absolutize_values(StyleProperties& style, DOM::Element const
|
|||
Length::make_px(CSSPixels::nearest_value_for(font_size * static_cast<double>((*line_height_value_slot)->as_percentage().percentage().as_fraction()))));
|
||||
}
|
||||
|
||||
auto line_height = style.line_height(viewport_rect(), font_metrics, m_root_element_font_metrics);
|
||||
auto line_height = style.compute_line_height(viewport_rect(), font_metrics, m_root_element_font_metrics);
|
||||
font_metrics.line_height = line_height;
|
||||
|
||||
// NOTE: line-height might be using lh which should be resolved against the parent line height (like we did here already)
|
||||
|
@ -1975,6 +1955,8 @@ void StyleComputer::absolutize_values(StyleProperties& style, DOM::Element const
|
|||
continue;
|
||||
value_slot->style = value_slot->style->absolutized(viewport_rect(), font_metrics, m_root_element_font_metrics);
|
||||
}
|
||||
|
||||
style.set_line_height({}, line_height);
|
||||
}
|
||||
|
||||
enum class BoxTypeTransformation {
|
||||
|
@ -2098,7 +2080,7 @@ NonnullRefPtr<StyleProperties> StyleComputer::create_document_style() const
|
|||
compute_math_depth(style, nullptr, {});
|
||||
compute_font(style, nullptr, {});
|
||||
compute_defaulted_values(style, nullptr, {});
|
||||
absolutize_values(style, nullptr, {});
|
||||
absolutize_values(style);
|
||||
style->set_property(CSS::PropertyID::Width, CSS::LengthStyleValue::create(CSS::Length::make_px(viewport_rect().width())), nullptr);
|
||||
style->set_property(CSS::PropertyID::Height, CSS::LengthStyleValue::create(CSS::Length::make_px(viewport_rect().height())), nullptr);
|
||||
style->set_property(CSS::PropertyID::Display, CSS::DisplayStyleValue::create(CSS::Display::from_short(CSS::Display::Short::Block)), nullptr);
|
||||
|
@ -2149,7 +2131,7 @@ ErrorOr<RefPtr<StyleProperties>> StyleComputer::compute_style_impl(DOM::Element&
|
|||
compute_font(style, &element, pseudo_element);
|
||||
|
||||
// 4. Absolutize values, turning font/viewport relative lengths into absolute lengths
|
||||
absolutize_values(style, &element, pseudo_element);
|
||||
absolutize_values(style);
|
||||
|
||||
// 5. Default the values, applying inheritance and 'initial' as needed
|
||||
compute_defaulted_values(style, &element, pseudo_element);
|
||||
|
|
|
@ -128,7 +128,7 @@ private:
|
|||
void compute_font(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement::Type>) const;
|
||||
void compute_math_depth(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement::Type>) const;
|
||||
void compute_defaulted_values(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement::Type>) const;
|
||||
void absolutize_values(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement::Type>) const;
|
||||
void absolutize_values(StyleProperties&) const;
|
||||
void transform_box_type_if_needed(StyleProperties&, DOM::Element const&, Optional<CSS::Selector::PseudoElement::Type>) const;
|
||||
|
||||
void compute_defaulted_property_value(StyleProperties&, DOM::Element const*, CSS::PropertyID, Optional<CSS::Selector::PseudoElement::Type>) const;
|
||||
|
@ -141,7 +141,6 @@ private:
|
|||
[[nodiscard]] CSSPixelRect viewport_rect() const { return m_viewport_rect; }
|
||||
|
||||
[[nodiscard]] Length::FontMetrics calculate_root_element_font_metrics(StyleProperties const&) const;
|
||||
CSSPixels parent_or_root_element_line_height(DOM::Element const*, Optional<CSS::Selector::PseudoElement::Type>) const;
|
||||
|
||||
struct MatchingRuleSet {
|
||||
Vector<MatchingRule> user_agent_rules;
|
||||
|
|
|
@ -154,8 +154,8 @@ NonnullRefPtr<Gfx::Font const> StyleProperties::font_fallback(bool monospace, bo
|
|||
return Platform::FontPlugin::the().default_font();
|
||||
}
|
||||
|
||||
// FIXME: This implementation is almost identical to line_height(Layout::Node) below. Maybe they can be combined somehow.
|
||||
CSSPixels StyleProperties::line_height(CSSPixelRect const& viewport_rect, Length::FontMetrics const& font_metrics, Length::FontMetrics const& root_font_metrics) const
|
||||
// FIXME: This implementation is almost identical to compute_line_height(Layout::Node) below. Maybe they can be combined somehow.
|
||||
CSSPixels StyleProperties::compute_line_height(CSSPixelRect const& viewport_rect, Length::FontMetrics const& font_metrics, Length::FontMetrics const& root_font_metrics) const
|
||||
{
|
||||
auto line_height = property(CSS::PropertyID::LineHeight);
|
||||
|
||||
|
@ -178,14 +178,27 @@ CSSPixels StyleProperties::line_height(CSSPixelRect const& viewport_rect, Length
|
|||
}
|
||||
|
||||
if (line_height->is_calculated()) {
|
||||
// FIXME: Handle `line-height: calc(...)` despite not having a LayoutNode here.
|
||||
return font_metrics.line_height;
|
||||
if (line_height->as_calculated().resolves_to_number()) {
|
||||
auto resolved = line_height->as_calculated().resolve_number();
|
||||
if (!resolved.has_value()) {
|
||||
dbgln("FIXME: Failed to resolve calc() line-height (number): {}", line_height->as_calculated().to_string());
|
||||
return CSSPixels::nearest_value_for(m_font_list->first().pixel_metrics().line_spacing());
|
||||
}
|
||||
return Length(resolved.value(), Length::Type::Em).to_px(viewport_rect, font_metrics, root_font_metrics);
|
||||
}
|
||||
|
||||
auto resolved = line_height->as_calculated().resolve_length(Length::ResolutionContext { viewport_rect, font_metrics, root_font_metrics });
|
||||
if (!resolved.has_value()) {
|
||||
dbgln("FIXME: Failed to resolve calc() line-height: {}", line_height->as_calculated().to_string());
|
||||
return CSSPixels::nearest_value_for(m_font_list->first().pixel_metrics().line_spacing());
|
||||
}
|
||||
return resolved->to_px(viewport_rect, font_metrics, root_font_metrics);
|
||||
}
|
||||
|
||||
return font_metrics.line_height;
|
||||
}
|
||||
|
||||
CSSPixels StyleProperties::line_height(Layout::Node const& layout_node) const
|
||||
CSSPixels StyleProperties::compute_line_height(Layout::Node const& layout_node) const
|
||||
{
|
||||
auto line_height = property(CSS::PropertyID::LineHeight);
|
||||
|
||||
|
|
|
@ -141,8 +141,11 @@ public:
|
|||
m_font_list = move(font_list);
|
||||
}
|
||||
|
||||
CSSPixels line_height(CSSPixelRect const& viewport_rect, Length::FontMetrics const& font_metrics, Length::FontMetrics const& root_font_metrics) const;
|
||||
CSSPixels line_height(Layout::Node const&) const;
|
||||
[[nodiscard]] CSSPixels compute_line_height(CSSPixelRect const& viewport_rect, Length::FontMetrics const& font_metrics, Length::FontMetrics const& root_font_metrics) const;
|
||||
[[nodiscard]] CSSPixels compute_line_height(Layout::Node const&) const;
|
||||
|
||||
[[nodiscard]] CSSPixels line_height() const { return *m_line_height; }
|
||||
void set_line_height(Badge<StyleComputer> const&, CSSPixels line_height) { m_line_height = line_height; }
|
||||
|
||||
bool operator==(StyleProperties const&) const;
|
||||
|
||||
|
@ -165,6 +168,8 @@ private:
|
|||
|
||||
int m_math_depth { InitialValues::math_depth() };
|
||||
mutable RefPtr<Gfx::FontCascadeList> m_font_list;
|
||||
|
||||
Optional<CSSPixels> m_line_height;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -352,7 +352,7 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style)
|
|||
m_font_list = computed_style.computed_font_list();
|
||||
computed_values.set_font_size(computed_style.property(CSS::PropertyID::FontSize)->as_length().length().to_px(*this));
|
||||
computed_values.set_font_weight(round_to<int>(computed_style.property(CSS::PropertyID::FontWeight)->as_number().number()));
|
||||
m_line_height = computed_style.line_height(*this);
|
||||
m_line_height = computed_style.line_height();
|
||||
|
||||
computed_values.set_vertical_align(computed_style.vertical_align());
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue