/* * Copyright (c) 2018-2024, Andreas Kling * Copyright (c) 2021-2025, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include namespace Web::CSS { class WEB_API Length { public: struct FontMetrics { FontMetrics(CSSPixels font_size, Gfx::FontPixelMetrics const&); CSSPixels font_size; CSSPixels x_height; CSSPixels cap_height; CSSPixels zero_advance; CSSPixels line_height; bool operator==(FontMetrics const&) const = default; }; Length(double value, LengthUnit unit); ~Length(); static Length make_px(double value); static Length make_px(CSSPixels value); Length percentage_of(Percentage const&) const; bool is_px() const { return m_unit == LengthUnit::Px; } bool is_absolute() const { return CSS::is_absolute(m_unit); } bool is_font_relative() const { return CSS::is_font_relative(m_unit); } bool is_viewport_relative() const { return CSS::is_viewport_relative(m_unit); } bool is_relative() const { return CSS::is_relative(m_unit); } double raw_value() const { return m_value; } LengthUnit unit() const { return m_unit; } FlyString unit_name() const { return CSS::to_string(m_unit); } struct ResolutionContext { [[nodiscard]] static ResolutionContext for_element(DOM::AbstractElement const&); [[nodiscard]] static ResolutionContext for_window(HTML::Window const&); [[nodiscard]] static ResolutionContext for_layout_node(Layout::Node const&); CSSPixelRect viewport_rect; FontMetrics font_metrics; FontMetrics root_font_metrics; bool operator==(ResolutionContext const&) const = default; }; [[nodiscard]] CSSPixels to_px(ResolutionContext const&) const; [[nodiscard]] ALWAYS_INLINE CSSPixels to_px(Layout::Node const& node) const { if (is_absolute()) return absolute_length_to_px(); return to_px_slow_case(node); } ALWAYS_INLINE CSSPixels to_px(CSSPixelRect const& viewport_rect, FontMetrics const& font_metrics, FontMetrics const& root_font_metrics) const { if (is_absolute()) return absolute_length_to_px(); if (is_font_relative()) return font_relative_length_to_px(font_metrics, root_font_metrics); if (is_viewport_relative()) return viewport_relative_length_to_px(viewport_rect); VERIFY_NOT_REACHED(); } ALWAYS_INLINE CSSPixels absolute_length_to_px() const { return CSSPixels::nearest_value_for(absolute_length_to_px_without_rounding()); } ALWAYS_INLINE double absolute_length_to_px_without_rounding() const { return ratio_between_units(m_unit, LengthUnit::Px) * m_value; } String to_string(SerializationMode = SerializationMode::Normal) const; bool operator==(Length const& other) const { return m_unit == other.m_unit && m_value == other.m_value; } CSSPixels font_relative_length_to_px(FontMetrics const& font_metrics, FontMetrics const& root_font_metrics) const; CSSPixels viewport_relative_length_to_px(CSSPixelRect const& viewport_rect) const; // Returns empty optional if it's already absolute. Optional absolutize(CSSPixelRect const& viewport_rect, FontMetrics const& font_metrics, FontMetrics const& root_font_metrics) const; Length absolutized(CSSPixelRect const& viewport_rect, FontMetrics const& font_metrics, FontMetrics const& root_font_metrics) const; static Length resolve_calculated(NonnullRefPtr const&, Layout::Node const&, Length const& reference_value); static Length resolve_calculated(NonnullRefPtr const&, Layout::Node const&, CSSPixels reference_value); private: [[nodiscard]] CSSPixels to_px_slow_case(Layout::Node const&) const; LengthUnit m_unit; double m_value { 0 }; }; class LengthOrAuto { public: LengthOrAuto(Length length) : m_length(move(length)) { } static LengthOrAuto make_auto() { return LengthOrAuto { OptionalNone {} }; } bool is_length() const { return m_length.has_value(); } bool is_auto() const { return !m_length.has_value(); } Length const& length() const { return m_length.value(); } String to_string(SerializationMode mode = SerializationMode::Normal) const { if (is_auto()) return "auto"_string; return m_length->to_string(mode); } CSSPixels to_px_or_zero(Layout::Node const& node) const { if (is_auto()) return 0; return m_length->to_px(node); } bool operator==(LengthOrAuto const&) const = default; private: explicit LengthOrAuto(Optional maybe_length) : m_length(move(maybe_length)) { } Optional m_length; }; } template<> struct AK::Formatter : Formatter { ErrorOr format(FormatBuilder& builder, Web::CSS::Length const& length) { return Formatter::format(builder, length.to_string()); } }; template<> struct AK::Formatter : Formatter { ErrorOr format(FormatBuilder& builder, Web::CSS::LengthOrAuto const& length_or_auto) { return Formatter::format(builder, length_or_auto.to_string()); } };