1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 15:17:36 +00:00

LibWeb: Remove implicit conversion from float and double to CSSPixels

In general it is not safe to convert any arbitrary floating-point value
to CSSPixels. CSSPixels has a resolution of 0.015625, which for small
values (e.g. scale factors between 0 and 1), can produce bad results
if converted to CSSPixels then scaled back up. In the worst case values
can underflow to zero and produce incorrect results.
This commit is contained in:
MacDue 2023-08-26 15:03:04 +01:00 committed by Alexander Kalenik
parent 0f9c088302
commit 360c0eb509
43 changed files with 248 additions and 221 deletions

View file

@ -24,10 +24,10 @@ Gfx::FloatRect EdgeRect::resolved(Layout::Node const& layout_node, Gfx::Rect<dou
// widths for <bottom>, and the same as the used value of the width plus the sum of the
// horizontal padding and border widths for <right>, such that four 'auto' values result in the
// clipping region being the same as the element's border box).
auto left = border_box.left() + (left_edge.is_auto() ? 0 : left_edge.to_px(layout_node)).to_double();
auto top = border_box.top() + (top_edge.is_auto() ? 0 : top_edge.to_px(layout_node)).to_double();
auto right = border_box.left() + (right_edge.is_auto() ? border_box.width() : right_edge.to_px(layout_node)).to_double();
auto bottom = border_box.top() + (bottom_edge.is_auto() ? border_box.height() : bottom_edge.to_px(layout_node)).to_double();
auto left = border_box.left() + (left_edge.is_auto() ? 0 : left_edge.to_px(layout_node).to_double());
auto top = border_box.top() + (top_edge.is_auto() ? 0 : top_edge.to_px(layout_node).to_double());
auto right = border_box.left() + (right_edge.is_auto() ? border_box.width() : right_edge.to_px(layout_node).to_double());
auto bottom = border_box.top() + (bottom_edge.is_auto() ? border_box.height() : bottom_edge.to_px(layout_node).to_double());
return Gfx::FloatRect {
left,
top,

View file

@ -61,31 +61,31 @@ CSSPixels Length::font_relative_length_to_px(Length::FontMetrics const& font_met
{
switch (m_type) {
case Type::Em:
return m_value * font_metrics.font_size.to_double();
return CSSPixels(m_value * font_metrics.font_size.to_double());
case Type::Rem:
return m_value * root_font_metrics.font_size.to_double();
return CSSPixels(m_value * root_font_metrics.font_size.to_double());
case Type::Ex:
return m_value * font_metrics.x_height.to_double();
return CSSPixels(m_value * font_metrics.x_height.to_double());
case Type::Rex:
return m_value * root_font_metrics.x_height.to_double();
return CSSPixels(m_value * root_font_metrics.x_height.to_double());
case Type::Cap:
return m_value * font_metrics.cap_height.to_double();
return CSSPixels(m_value * font_metrics.cap_height.to_double());
case Type::Rcap:
return m_value * root_font_metrics.cap_height.to_double();
return CSSPixels(m_value * root_font_metrics.cap_height.to_double());
case Type::Ch:
return m_value * font_metrics.zero_advance.to_double();
return CSSPixels(m_value * font_metrics.zero_advance.to_double());
case Type::Rch:
return m_value * root_font_metrics.zero_advance.to_double();
return CSSPixels(m_value * root_font_metrics.zero_advance.to_double());
case Type::Ic:
// FIXME: Use the "advance measure of the “水” (CJK water ideograph, U+6C34) glyph"
return m_value * font_metrics.font_size.to_double();
return CSSPixels(m_value * font_metrics.font_size.to_double());
case Type::Ric:
// FIXME: Use the "advance measure of the “水” (CJK water ideograph, U+6C34) glyph"
return m_value * root_font_metrics.font_size.to_double();
return CSSPixels(m_value * root_font_metrics.font_size.to_double());
case Type::Lh:
return m_value * font_metrics.line_height.to_double();
return CSSPixels(m_value * font_metrics.line_height.to_double());
case Type::Rlh:
return m_value * root_font_metrics.line_height.to_double();
return CSSPixels(m_value * root_font_metrics.line_height.to_double());
default:
VERIFY_NOT_REACHED();
}
@ -98,34 +98,34 @@ CSSPixels Length::viewport_relative_length_to_px(CSSPixelRect const& viewport_re
case Type::Svw:
case Type::Lvw:
case Type::Dvw:
return viewport_rect.width() * (m_value / 100);
return CSSPixels(viewport_rect.width() * (m_value / 100));
case Type::Vh:
case Type::Svh:
case Type::Lvh:
case Type::Dvh:
return viewport_rect.height() * (m_value / 100);
return CSSPixels(viewport_rect.height() * (m_value / 100));
case Type::Vi:
case Type::Svi:
case Type::Lvi:
case Type::Dvi:
// FIXME: Select the width or height based on which is the inline axis.
return viewport_rect.width() * (m_value / 100);
return CSSPixels(viewport_rect.width() * (m_value / 100));
case Type::Vb:
case Type::Svb:
case Type::Lvb:
case Type::Dvb:
// FIXME: Select the width or height based on which is the block axis.
return viewport_rect.height() * (m_value / 100);
return CSSPixels(viewport_rect.height() * (m_value / 100));
case Type::Vmin:
case Type::Svmin:
case Type::Lvmin:
case Type::Dvmin:
return min(viewport_rect.width(), viewport_rect.height()) * (m_value / 100);
return CSSPixels(min(viewport_rect.width(), viewport_rect.height()) * (m_value / 100));
case Type::Vmax:
case Type::Svmax:
case Type::Lvmax:
case Type::Dvmax:
return max(viewport_rect.width(), viewport_rect.height()) * (m_value / 100);
return CSSPixels(max(viewport_rect.width(), viewport_rect.height()) * (m_value / 100));
default:
VERIFY_NOT_REACHED();
}
@ -138,8 +138,8 @@ Length::ResolutionContext Length::ResolutionContext::for_layout_node(Layout::Nod
VERIFY(root_element->layout_node());
return Length::ResolutionContext {
.viewport_rect = node.browsing_context().viewport_rect(),
.font_metrics = { node.computed_values().font_size(), node.font().pixel_metrics(), node.line_height() },
.root_font_metrics = { root_element->layout_node()->computed_values().font_size(), root_element->layout_node()->font().pixel_metrics(), root_element->layout_node()->line_height() },
.font_metrics = { CSSPixels(node.computed_values().font_size()), node.font().pixel_metrics(), node.line_height() },
.root_font_metrics = { CSSPixels(root_element->layout_node()->computed_values().font_size()), root_element->layout_node()->font().pixel_metrics(), root_element->layout_node()->line_height() },
};
}
@ -168,12 +168,12 @@ CSSPixels Length::to_px(Layout::Node const& layout_node) const
return 0;
FontMetrics font_metrics {
layout_node.computed_values().font_size(),
CSSPixels(layout_node.computed_values().font_size()),
layout_node.font().pixel_metrics(),
layout_node.line_height()
};
FontMetrics root_font_metrics {
root_element->layout_node()->computed_values().font_size(),
CSSPixels(root_element->layout_node()->computed_values().font_size()),
root_element->layout_node()->font().pixel_metrics(),
root_element->layout_node()->line_height()
};

View file

@ -185,19 +185,19 @@ public:
constexpr double centimeter_pixels = (inch_pixels / 2.54);
switch (m_type) {
case Type::Cm:
return m_value * centimeter_pixels; // 1cm = 96px/2.54
return CSSPixels(m_value * centimeter_pixels); // 1cm = 96px/2.54
case Type::In:
return m_value * inch_pixels; // 1in = 2.54 cm = 96px
return CSSPixels(m_value * inch_pixels); // 1in = 2.54 cm = 96px
case Type::Px:
return m_value; // 1px = 1/96th of 1in
return CSSPixels(m_value); // 1px = 1/96th of 1in
case Type::Pt:
return m_value * ((1.0 / 72.0) * inch_pixels); // 1pt = 1/72th of 1in
return CSSPixels(m_value * ((1.0 / 72.0) * inch_pixels)); // 1pt = 1/72th of 1in
case Type::Pc:
return m_value * ((1.0 / 6.0) * inch_pixels); // 1pc = 1/6th of 1in
return CSSPixels(m_value * ((1.0 / 6.0) * inch_pixels)); // 1pc = 1/6th of 1in
case Type::Mm:
return m_value * ((1.0 / 10.0) * centimeter_pixels); // 1mm = 1/10th of 1cm
return CSSPixels(m_value * ((1.0 / 10.0) * centimeter_pixels)); // 1mm = 1/10th of 1cm
case Type::Q:
return m_value * ((1.0 / 40.0) * centimeter_pixels); // 1Q = 1/40th of 1cm
return CSSPixels(m_value * ((1.0 / 40.0) * centimeter_pixels)); // 1Q = 1/40th of 1cm
default:
VERIFY_NOT_REACHED();
}

View file

@ -168,7 +168,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 { static_cast<CSSPixels>(initial_font.presentation_size()), initial_font_metrics, initial_font_metrics.line_spacing() };
Length::FontMetrics font_metrics { CSSPixels(initial_font.presentation_size()), initial_font_metrics, CSSPixels(initial_font_metrics.line_spacing()) };
left_px = left.length().to_px(viewport_rect, font_metrics, font_metrics);
right_px = right.length().to_px(viewport_rect, font_metrics, font_metrics);

View file

@ -1787,7 +1787,7 @@ Optional<Dimension> Parser::parse_dimension(ComponentValue const& component_valu
// FIXME: Disallow quirk when inside a CSS sub-expression (like `calc()`)
// "The <quirky-length> value must not be supported in arguments to CSS expressions other than the rect()
// expression, and must not be supported in the supports() static method of the CSS interface."
return Length::make_px(numeric_value);
return Length::make_px(CSSPixels(numeric_value));
}
}

View file

@ -18,11 +18,11 @@ CSSPixelPoint PositionValue::resolved(Layout::Node const& node, CSSPixelRect con
return rect.width() * [&] {
switch (preset) {
case HorizontalPreset::Left:
return 0.;
return CSSPixels(0.0);
case HorizontalPreset::Center:
return 0.5;
return CSSPixels(0.5);
case HorizontalPreset::Right:
return 1.;
return CSSPixels(1.0);
default:
VERIFY_NOT_REACHED();
}
@ -36,11 +36,11 @@ CSSPixelPoint PositionValue::resolved(Layout::Node const& node, CSSPixelRect con
return rect.height() * [&] {
switch (preset) {
case VerticalPreset::Top:
return 0.;
return CSSPixels(0.0);
case VerticalPreset::Center:
return 0.5;
return CSSPixels(0.5);
case VerticalPreset::Bottom:
return 1.;
return CSSPixels(1.0);
default:
VERIFY_NOT_REACHED();
}

View file

@ -596,7 +596,7 @@ RefPtr<StyleValue const> ResolvedCSSStyleDeclaration::style_value_for_property(L
case PropertyID::Float:
return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().float_()));
case PropertyID::FontSize:
return LengthStyleValue::create(Length::make_px(layout_node.computed_values().font_size()));
return LengthStyleValue::create(Length::make_px(CSSPixels(layout_node.computed_values().font_size())));
case PropertyID::FontVariant: {
auto font_variant = layout_node.computed_values().font_variant();
switch (font_variant) {

View file

@ -2036,7 +2036,7 @@ Length::FontMetrics StyleComputer::calculate_root_element_font_metrics(StyleProp
auto root_value = style.property(CSS::PropertyID::FontSize);
auto font_pixel_metrics = style.computed_font().pixel_metrics();
Length::FontMetrics font_metrics { m_default_font_metrics.font_size, font_pixel_metrics, font_pixel_metrics.line_spacing() };
Length::FontMetrics font_metrics { m_default_font_metrics.font_size, font_pixel_metrics, CSSPixels(font_pixel_metrics.line_spacing()) };
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);
@ -2150,7 +2150,7 @@ RefPtr<Gfx::Font const> StyleComputer::compute_font_for_style_values(DOM::Elemen
bool bold = weight > Gfx::FontWeight::Regular;
// FIXME: Should be based on "user's default font size"
float font_size_in_px = 16;
CSSPixels font_size_in_px = 16;
auto parent_line_height = parent_or_root_element_line_height(element, pseudo_element);
Gfx::FontPixelMetrics font_pixel_metrics;
@ -2197,11 +2197,11 @@ RefPtr<Gfx::Font const> StyleComputer::compute_font_for_style_values(DOM::Elemen
// and smaller may compute the font size to the previous entry in the table.
if (identifier == CSS::ValueID::Smaller || identifier == CSS::ValueID::Larger) {
if (parent_element && parent_element->computed_css_values()) {
font_size_in_px = parent_element->computed_css_values()->computed_font().pixel_metrics().size;
font_size_in_px = CSSPixels(parent_element->computed_css_values()->computed_font().pixel_metrics().size);
}
}
auto const multiplier = absolute_size_mapping.get(identifier).value_or(1.0);
font_size_in_px *= multiplier;
font_size_in_px.scale_by(multiplier);
} else {
Length::ResolutionContext const length_resolution_context {
@ -2213,7 +2213,7 @@ RefPtr<Gfx::Font const> StyleComputer::compute_font_for_style_values(DOM::Elemen
Optional<Length> maybe_length;
if (font_size.is_percentage()) {
// Percentages refer to parent element's font size
maybe_length = Length::make_px(font_size.as_percentage().percentage().as_fraction() * parent_font_size().to_double());
maybe_length = Length::make_px(CSSPixels(font_size.as_percentage().percentage().as_fraction() * parent_font_size().to_double()));
} else if (font_size.is_length()) {
maybe_length = font_size.as_length().length();
@ -2350,7 +2350,7 @@ void StyleComputer::compute_font(StyleProperties& style, DOM::Element const* ele
auto found_font = compute_font_for_style_values(element, pseudo_element, font_family, font_size, font_style, font_weight, font_stretch);
style.set_property(CSS::PropertyID::FontSize, LengthStyleValue::create(CSS::Length::make_px(found_font->pixel_size())), nullptr);
style.set_property(CSS::PropertyID::FontSize, LengthStyleValue::create(CSS::Length::make_px(CSSPixels(found_font->pixel_size()))), nullptr);
style.set_property(CSS::PropertyID::FontWeight, NumberStyleValue::create(font_weight->to_font_weight()));
style.set_computed_font(found_font.release_nonnull());
@ -2401,7 +2401,7 @@ void StyleComputer::absolutize_values(StyleProperties& style, DOM::Element const
auto line_height_value_slot = style.m_property_values[to_underlying(CSS::PropertyID::LineHeight)].map([](auto& x) -> auto& { return x.style; });
if (line_height_value_slot.has_value() && (*line_height_value_slot)->is_percentage()) {
*line_height_value_slot = LengthStyleValue::create(
Length::make_px(font_size * static_cast<double>((*line_height_value_slot)->as_percentage().percentage().as_fraction())));
Length::make_px(CSSPixels(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);

View file

@ -204,7 +204,7 @@ CSSPixels StyleProperties::line_height(Layout::Node const& layout_node) const
auto line_height = property(CSS::PropertyID::LineHeight);
if (line_height->is_identifier() && line_height->to_identifier() == ValueID::Normal)
return layout_node.font().pixel_metrics().line_spacing();
return CSSPixels(layout_node.font().pixel_metrics().line_spacing());
if (line_height->is_length()) {
auto line_height_length = line_height->as_length().length();
@ -226,7 +226,7 @@ CSSPixels StyleProperties::line_height(Layout::Node const& layout_node) const
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 layout_node.font().pixel_metrics().line_spacing();
return CSSPixels(layout_node.font().pixel_metrics().line_spacing());
}
return Length(resolved.value(), Length::Type::Em).to_px(layout_node);
}
@ -234,12 +234,12 @@ CSSPixels StyleProperties::line_height(Layout::Node const& layout_node) const
auto resolved = line_height->as_calculated().resolve_length(layout_node);
if (!resolved.has_value()) {
dbgln("FIXME: Failed to resolve calc() line-height: {}", line_height->as_calculated().to_string());
return layout_node.font().pixel_metrics().line_spacing();
return CSSPixels(layout_node.font().pixel_metrics().line_spacing());
}
return resolved->to_px(layout_node);
}
return layout_node.font().pixel_metrics().line_spacing();
return CSSPixels(layout_node.font().pixel_metrics().line_spacing());
}
Optional<int> StyleProperties::z_index() const

View file

@ -77,7 +77,7 @@ static CalculatedStyleValue::CalculationResult to_resolved_type(CalculatedStyleV
case CalculatedStyleValue::ResolvedType::Frequency:
return { Frequency::make_hertz(value) };
case CalculatedStyleValue::ResolvedType::Length:
return { Length::make_px(value) };
return { Length::make_px(CSSPixels(value)) };
case CalculatedStyleValue::ResolvedType::Percentage:
return { Percentage(value) };
case CalculatedStyleValue::ResolvedType::Time:
@ -2155,7 +2155,7 @@ void CalculatedStyleValue::CalculationResult::multiply_by(CalculationResult cons
m_value = Frequency::make_hertz(frequency.to_hertz() * other.m_value.get<Number>().value());
},
[&](Length const& length) {
m_value = Length::make_px(length.to_px(*context) * static_cast<double>(other.m_value.get<Number>().value()));
m_value = Length::make_px(CSSPixels(length.to_px(*context) * static_cast<double>(other.m_value.get<Number>().value())));
},
[&](Time const& time) {
m_value = Time::make_seconds(time.to_seconds() * other.m_value.get<Number>().value());
@ -2187,7 +2187,7 @@ void CalculatedStyleValue::CalculationResult::divide_by(CalculationResult const&
m_value = Frequency::make_hertz(frequency.to_hertz() / denominator);
},
[&](Length const& length) {
m_value = Length::make_px(length.to_px(*context) / static_cast<double>(denominator));
m_value = Length::make_px(CSSPixels(length.to_px(*context) / static_cast<double>(denominator)));
},
[&](Time const& time) {
m_value = Time::make_seconds(time.to_seconds() / denominator);

View file

@ -150,8 +150,8 @@ Gfx::FloatSize RadialGradientStyleValue::resolve_size(Layout::Node const& node,
return Gfx::FloatSize { radius.to_float(), radius.to_float() };
},
[&](EllipseSize const& ellipse_size) {
auto radius_a = ellipse_size.radius_a.resolved(node, CSS::Length::make_px(size.width())).to_px(node);
auto radius_b = ellipse_size.radius_b.resolved(node, CSS::Length::make_px(size.height())).to_px(node);
auto radius_a = ellipse_size.radius_a.resolved(node, CSS::Length::make_px(CSSPixels(size.width()))).to_px(node);
auto radius_b = ellipse_size.radius_b.resolved(node, CSS::Length::make_px(CSSPixels(size.height()))).to_px(node);
return Gfx::FloatSize { radius_a.to_float(), radius_b.to_float() };
});