Length.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /*
  2. * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/NonnullOwnPtr.h>
  8. #include <AK/Variant.h>
  9. #include <LibGfx/Font.h>
  10. #include <LibGfx/Rect.h>
  11. #include <LibWeb/CSS/Length.h>
  12. #include <LibWeb/DOM/Document.h>
  13. #include <LibWeb/HTML/BrowsingContext.h>
  14. #include <LibWeb/HTML/HTMLHtmlElement.h>
  15. namespace Web::CSS {
  16. Length::Length() = default;
  17. Length::Length(int value, Type type)
  18. : m_type(type)
  19. , m_value(value)
  20. {
  21. }
  22. Length::Length(float value, Type type)
  23. : m_type(type)
  24. , m_value(value)
  25. {
  26. }
  27. Length Length::make_auto()
  28. {
  29. return Length(0, Type::Auto);
  30. }
  31. Length Length::make_px(float value)
  32. {
  33. return Length(value, Type::Px);
  34. }
  35. Length Length::resolved(const Length& fallback_for_undefined, const Layout::Node& layout_node, float reference_for_percent) const
  36. {
  37. if (is_undefined())
  38. return fallback_for_undefined;
  39. if (is_calculated())
  40. return Length(resolve_calculated_value(layout_node, reference_for_percent), Type::Px);
  41. if (is_percentage())
  42. return make_px(raw_value() / 100.0f * reference_for_percent);
  43. if (is_relative())
  44. return make_px(to_px(layout_node));
  45. return *this;
  46. }
  47. Length Length::resolved_or_auto(const Layout::Node& layout_node, float reference_for_percent) const
  48. {
  49. return resolved(make_auto(), layout_node, reference_for_percent);
  50. }
  51. Length Length::resolved_or_zero(const Layout::Node& layout_node, float reference_for_percent) const
  52. {
  53. return resolved(make_px(0), layout_node, reference_for_percent);
  54. }
  55. void Length::set_calculated_style(CalculatedStyleValue* value)
  56. {
  57. m_calculated_style = value;
  58. }
  59. float Length::relative_length_to_px(Gfx::IntRect const& viewport_rect, Gfx::FontMetrics const& font_metrics, float root_font_size) const
  60. {
  61. switch (m_type) {
  62. case Type::Ex:
  63. return m_value * font_metrics.x_height;
  64. case Type::Em:
  65. return m_value * font_metrics.size;
  66. case Type::Ch:
  67. // FIXME: Use layout_node.font().glyph_height() when writing-mode is not horizontal-tb (it has to be implemented first)
  68. return m_value * (font_metrics.glyph_width + font_metrics.glyph_spacing);
  69. case Type::Rem:
  70. return m_value * root_font_size;
  71. case Type::Vw:
  72. return viewport_rect.width() * (m_value / 100);
  73. case Type::Vh:
  74. return viewport_rect.height() * (m_value / 100);
  75. case Type::Vmin:
  76. return min(viewport_rect.width(), viewport_rect.height()) * (m_value / 100);
  77. case Type::Vmax:
  78. return max(viewport_rect.width(), viewport_rect.height()) * (m_value / 100);
  79. default:
  80. VERIFY_NOT_REACHED();
  81. }
  82. }
  83. float Length::to_px(Layout::Node const& layout_node) const
  84. {
  85. if (!layout_node.document().browsing_context())
  86. return 0;
  87. auto viewport_rect = layout_node.document().browsing_context()->viewport_rect();
  88. auto* root_element = layout_node.document().document_element();
  89. if (!root_element || !root_element->layout_node())
  90. return 0;
  91. return to_px(viewport_rect, layout_node.font().metrics('M'), root_element->layout_node()->font().presentation_size());
  92. }
  93. static float resolve_calc_value(CalculatedStyleValue::CalcValue const&, const Layout::Node& layout_node, float reference_for_percent);
  94. static float resolve_calc_number_value(CalculatedStyleValue::CalcNumberValue const&);
  95. static float resolve_calc_product(NonnullOwnPtr<CalculatedStyleValue::CalcProduct> const&, const Layout::Node& layout_node, float reference_for_percent);
  96. static float resolve_calc_sum(NonnullOwnPtr<CalculatedStyleValue::CalcSum> const&, const Layout::Node& layout_node, float reference_for_percent);
  97. static float resolve_calc_number_sum(NonnullOwnPtr<CalculatedStyleValue::CalcNumberSum> const&);
  98. static float resolve_calc_number_product(NonnullOwnPtr<CalculatedStyleValue::CalcNumberProduct> const&);
  99. float Length::resolve_calculated_value(const Layout::Node& layout_node, float reference_for_percent) const
  100. {
  101. if (!m_calculated_style)
  102. return 0.0f;
  103. auto& expression = m_calculated_style->expression();
  104. auto length = resolve_calc_sum(expression, layout_node, reference_for_percent);
  105. return length;
  106. };
  107. static float resolve_calc_value(CalculatedStyleValue::CalcValue const& calc_value, const Layout::Node& layout_node, float reference_for_percent)
  108. {
  109. return calc_value.visit(
  110. [](float value) { return value; },
  111. [&](Length length) {
  112. return length.resolved_or_zero(layout_node, reference_for_percent).to_px(layout_node);
  113. },
  114. [&](NonnullOwnPtr<CalculatedStyleValue::CalcSum>& calc_sum) {
  115. return resolve_calc_sum(calc_sum, layout_node, reference_for_percent);
  116. },
  117. [](auto&) {
  118. VERIFY_NOT_REACHED();
  119. return 0.0f;
  120. });
  121. }
  122. static float resolve_calc_number_product(NonnullOwnPtr<CalculatedStyleValue::CalcNumberProduct> const& calc_number_product)
  123. {
  124. auto value = resolve_calc_number_value(calc_number_product->first_calc_number_value);
  125. for (auto& additional_number_value : calc_number_product->zero_or_more_additional_calc_number_values) {
  126. auto additional_value = resolve_calc_number_value(additional_number_value.value);
  127. if (additional_number_value.op == CalculatedStyleValue::CalcNumberProductPartWithOperator::Multiply)
  128. value *= additional_value;
  129. else if (additional_number_value.op == CalculatedStyleValue::CalcNumberProductPartWithOperator::Divide)
  130. value /= additional_value;
  131. else
  132. VERIFY_NOT_REACHED();
  133. }
  134. return value;
  135. }
  136. static float resolve_calc_number_sum(NonnullOwnPtr<CalculatedStyleValue::CalcNumberSum> const& calc_number_sum)
  137. {
  138. auto value = resolve_calc_number_product(calc_number_sum->first_calc_number_product);
  139. for (auto& additional_product : calc_number_sum->zero_or_more_additional_calc_number_products) {
  140. auto additional_value = resolve_calc_number_product(additional_product.calc_number_product);
  141. if (additional_product.op == CSS::CalculatedStyleValue::CalcNumberSumPartWithOperator::Add)
  142. value += additional_value;
  143. else if (additional_product.op == CSS::CalculatedStyleValue::CalcNumberSumPartWithOperator::Subtract)
  144. value -= additional_value;
  145. else
  146. VERIFY_NOT_REACHED();
  147. }
  148. return value;
  149. }
  150. static float resolve_calc_number_value(CalculatedStyleValue::CalcNumberValue const& number_value)
  151. {
  152. return number_value.visit(
  153. [](float number) { return number; },
  154. [](NonnullOwnPtr<CalculatedStyleValue::CalcNumberSum>& calc_number_sum) {
  155. return resolve_calc_number_sum(calc_number_sum);
  156. });
  157. }
  158. static float resolve_calc_product(NonnullOwnPtr<CalculatedStyleValue::CalcProduct> const& calc_product, const Layout::Node& layout_node, float reference_for_percent)
  159. {
  160. auto value = resolve_calc_value(calc_product->first_calc_value, layout_node, reference_for_percent);
  161. for (auto& additional_value : calc_product->zero_or_more_additional_calc_values) {
  162. additional_value.value.visit(
  163. [&](CalculatedStyleValue::CalcValue const& calc_value) {
  164. if (additional_value.op != CalculatedStyleValue::CalcProductPartWithOperator::Multiply)
  165. VERIFY_NOT_REACHED();
  166. auto resolved_value = resolve_calc_value(calc_value, layout_node, reference_for_percent);
  167. value *= resolved_value;
  168. },
  169. [&](CalculatedStyleValue::CalcNumberValue const& calc_number_value) {
  170. if (additional_value.op != CalculatedStyleValue::CalcProductPartWithOperator::Divide)
  171. VERIFY_NOT_REACHED();
  172. auto resolved_calc_number_value = resolve_calc_number_value(calc_number_value);
  173. value /= resolved_calc_number_value;
  174. });
  175. }
  176. return value;
  177. }
  178. static float resolve_calc_sum(NonnullOwnPtr<CalculatedStyleValue::CalcSum> const& calc_sum, const Layout::Node& layout_node, float reference_for_percent)
  179. {
  180. auto value = resolve_calc_product(calc_sum->first_calc_product, layout_node, reference_for_percent);
  181. for (auto& additional_product : calc_sum->zero_or_more_additional_calc_products) {
  182. auto additional_value = resolve_calc_product(additional_product.calc_product, layout_node, reference_for_percent);
  183. if (additional_product.op == CalculatedStyleValue::CalcSumPartWithOperator::Operation::Add)
  184. value += additional_value;
  185. else if (additional_product.op == CalculatedStyleValue::CalcSumPartWithOperator::Operation::Subtract)
  186. value -= additional_value;
  187. else
  188. VERIFY_NOT_REACHED();
  189. }
  190. return value;
  191. }
  192. const char* Length::unit_name() const
  193. {
  194. switch (m_type) {
  195. case Type::Cm:
  196. return "cm";
  197. case Type::In:
  198. return "in";
  199. case Type::Px:
  200. return "px";
  201. case Type::Pt:
  202. return "pt";
  203. case Type::Mm:
  204. return "mm";
  205. case Type::Q:
  206. return "Q";
  207. case Type::Pc:
  208. return "pc";
  209. case Type::Ex:
  210. return "ex";
  211. case Type::Em:
  212. return "em";
  213. case Type::Ch:
  214. return "ch";
  215. case Type::Rem:
  216. return "rem";
  217. case Type::Auto:
  218. return "auto";
  219. case Type::Percentage:
  220. return "%";
  221. case Type::Undefined:
  222. return "undefined";
  223. case Type::Vh:
  224. return "vh";
  225. case Type::Vw:
  226. return "vw";
  227. case Type::Vmax:
  228. return "vmax";
  229. case Type::Vmin:
  230. return "vmin";
  231. case Type::Calculated:
  232. return "calculated";
  233. }
  234. VERIFY_NOT_REACHED();
  235. }
  236. }