From 9c65c102452adddfa1bf1d2f8c79ed760eb1c647 Mon Sep 17 00:00:00 2001 From: Tobias Christiansen Date: Sat, 12 Jun 2021 00:50:16 +0200 Subject: [PATCH] LibWeb: Add calc() resolution to CSS::Length This patch finally adds the actual calculation that goes into calc() expressions. When the resolution of a Length that is a calculated value the parsed CalculatedStyleValue gets traversed and appropriate values get calculated. --- Userland/Libraries/LibWeb/CSS/Length.cpp | 121 +++++++++++++++++++++++ Userland/Libraries/LibWeb/CSS/Length.h | 5 +- 2 files changed, 124 insertions(+), 2 deletions(-) diff --git a/Userland/Libraries/LibWeb/CSS/Length.cpp b/Userland/Libraries/LibWeb/CSS/Length.cpp index a8c73d134a..2b2c91d6d7 100644 --- a/Userland/Libraries/LibWeb/CSS/Length.cpp +++ b/Userland/Libraries/LibWeb/CSS/Length.cpp @@ -1,9 +1,13 @@ /* * Copyright (c) 2020, Andreas Kling + * Copyright (c) 2021, Tobias Christiansen * * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include +#include #include #include #include @@ -39,6 +43,123 @@ float Length::relative_length_to_px(const Layout::Node& layout_node) const } } +static float resolve_calc_value(CalculatedStyleValue::CalcValue const&, const Layout::Node& layout_node, float reference_for_percent); +static float resolve_calc_number_value(CalculatedStyleValue::CalcNumberValue const&); +static float resolve_calc_product(NonnullOwnPtr const&, const Layout::Node& layout_node, float reference_for_percent); +static float resolve_calc_sum(NonnullOwnPtr const&, const Layout::Node& layout_node, float reference_for_percent); +static float resolve_calc_number_sum(NonnullOwnPtr const&); +static float resolve_calc_number_product(NonnullOwnPtr const&); + +float Length::resolve_calculated_value(const Layout::Node& layout_node, float reference_for_percent) const +{ + if (!m_calculated_style) + return 0.0f; + + auto& expression = m_calculated_style->expression(); + + auto length = resolve_calc_sum(expression, layout_node, reference_for_percent); + return length; +}; + +static float resolve_calc_value(CalculatedStyleValue::CalcValue const& calc_value, const Layout::Node& layout_node, float reference_for_percent) +{ + return calc_value.visit( + [](float value) { return value; }, + [&](Length length) { + return length.resolved_or_zero(layout_node, reference_for_percent).to_px(layout_node); + }, + [&](NonnullOwnPtr& calc_sum) { + return resolve_calc_sum(calc_sum, layout_node, reference_for_percent); + }, + [](auto&) { + VERIFY_NOT_REACHED(); + return 0.0f; + }); +} + +static float resolve_calc_number_product(NonnullOwnPtr const& calc_number_product) +{ + auto value = resolve_calc_number_value(calc_number_product->first_calc_number_value); + + for (auto& additional_number_value : calc_number_product->zero_or_more_additional_calc_number_values) { + auto additional_value = resolve_calc_number_value(additional_number_value.value); + if (additional_number_value.op == CalculatedStyleValue::CalcNumberProductPartWithOperator::Multiply) + value *= additional_value; + else if (additional_number_value.op == CalculatedStyleValue::CalcNumberProductPartWithOperator::Divide) + value /= additional_value; + else + VERIFY_NOT_REACHED(); + } + + return value; +} + +static float resolve_calc_number_sum(NonnullOwnPtr const& calc_number_sum) +{ + auto value = resolve_calc_number_product(calc_number_sum->first_calc_number_product); + + for (auto& additional_product : calc_number_sum->zero_or_more_additional_calc_number_products) { + auto additional_value = resolve_calc_number_product(additional_product.calc_number_product); + if (additional_product.op == CSS::CalculatedStyleValue::CalcNumberSumPartWithOperator::Add) + value += additional_value; + else if (additional_product.op == CSS::CalculatedStyleValue::CalcNumberSumPartWithOperator::Subtract) + value -= additional_value; + else + VERIFY_NOT_REACHED(); + } + + return value; +} + +static float resolve_calc_number_value(CalculatedStyleValue::CalcNumberValue const& number_value) +{ + return number_value.visit( + [](float number) { return number; }, + [](NonnullOwnPtr& calc_number_sum) { + return resolve_calc_number_sum(calc_number_sum); + }); +} + +static float resolve_calc_product(NonnullOwnPtr const& calc_product, const Layout::Node& layout_node, float reference_for_percent) +{ + auto value = resolve_calc_value(calc_product->first_calc_value, layout_node, reference_for_percent); + + for (auto& additional_value : calc_product->zero_or_more_additional_calc_values) { + additional_value.value.visit( + [&](CalculatedStyleValue::CalcValue const& calc_value) { + if (additional_value.op != CalculatedStyleValue::CalcProductPartWithOperator::Multiply) + VERIFY_NOT_REACHED(); + auto resolved_value = resolve_calc_value(calc_value, layout_node, reference_for_percent); + value *= resolved_value; + }, + [&](CalculatedStyleValue::CalcNumberValue const& calc_number_value) { + if (additional_value.op != CalculatedStyleValue::CalcProductPartWithOperator::Divide) + VERIFY_NOT_REACHED(); + auto resolved_calc_number_value = resolve_calc_number_value(calc_number_value); + value /= resolved_calc_number_value; + }); + } + + return value; +} + +static float resolve_calc_sum(NonnullOwnPtr const& calc_sum, const Layout::Node& layout_node, float reference_for_percent) +{ + auto value = resolve_calc_product(calc_sum->first_calc_product, layout_node, reference_for_percent); + + for (auto& additional_product : calc_sum->zero_or_more_additional_calc_products) { + auto additional_value = resolve_calc_product(additional_product.calc_product, layout_node, reference_for_percent); + if (additional_product.op == CalculatedStyleValue::CalcSumPartWithOperator::Operation::Add) + value += additional_value; + else if (additional_product.op == CalculatedStyleValue::CalcSumPartWithOperator::Operation::Subtract) + value -= additional_value; + else + VERIFY_NOT_REACHED(); + } + + return value; +} + const char* Length::unit_name() const { switch (m_type) { diff --git a/Userland/Libraries/LibWeb/CSS/Length.h b/Userland/Libraries/LibWeb/CSS/Length.h index c9c8c465d1..5e14e11818 100644 --- a/Userland/Libraries/LibWeb/CSS/Length.h +++ b/Userland/Libraries/LibWeb/CSS/Length.h @@ -53,10 +53,10 @@ public: { if (is_undefined()) return fallback_for_undefined; + if (is_calculated()) + return Length(resolve_calculated_value(layout_node, reference_for_percent), Type::Px); if (is_percentage()) return make_px(raw_value() / 100.0f * reference_for_percent); - if (is_calculated()) - return {}; if (is_relative()) return make_px(to_px(layout_node)); return *this; @@ -153,6 +153,7 @@ public: private: float relative_length_to_px(const Layout::Node&) const; + float resolve_calculated_value(const Layout::Node& layout_node, float reference_for_percent) const; const char* unit_name() const;