CalculatedStyleValue.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
  4. * Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
  5. * Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
  6. *
  7. * SPDX-License-Identifier: BSD-2-Clause
  8. */
  9. #include "CalculatedStyleValue.h"
  10. #include <LibWeb/CSS/Percentage.h>
  11. namespace Web::CSS {
  12. static bool is_number(CalculatedStyleValue::ResolvedType type)
  13. {
  14. return type == CalculatedStyleValue::ResolvedType::Number || type == CalculatedStyleValue::ResolvedType::Integer;
  15. }
  16. static bool is_dimension(CalculatedStyleValue::ResolvedType type)
  17. {
  18. return type != CalculatedStyleValue::ResolvedType::Number
  19. && type != CalculatedStyleValue::ResolvedType::Integer
  20. && type != CalculatedStyleValue::ResolvedType::Percentage;
  21. }
  22. CalculationNode::CalculationNode(Type type)
  23. : m_type(type)
  24. {
  25. }
  26. CalculationNode::~CalculationNode() = default;
  27. ErrorOr<NonnullOwnPtr<NumericCalculationNode>> NumericCalculationNode::create(NumericValue value)
  28. {
  29. return adopt_nonnull_own_or_enomem(new (nothrow) NumericCalculationNode(move(value)));
  30. }
  31. NumericCalculationNode::NumericCalculationNode(NumericValue value)
  32. : CalculationNode(Type::Numeric)
  33. , m_value(move(value))
  34. {
  35. }
  36. NumericCalculationNode::~NumericCalculationNode() = default;
  37. ErrorOr<String> NumericCalculationNode::to_string() const
  38. {
  39. return m_value.visit([](auto& value) { return value.to_string(); });
  40. }
  41. Optional<CalculatedStyleValue::ResolvedType> NumericCalculationNode::resolved_type() const
  42. {
  43. return m_value.visit(
  44. [](Number const&) { return CalculatedStyleValue::ResolvedType::Number; },
  45. [](Angle const&) { return CalculatedStyleValue::ResolvedType::Angle; },
  46. [](Frequency const&) { return CalculatedStyleValue::ResolvedType::Frequency; },
  47. [](Length const&) { return CalculatedStyleValue::ResolvedType::Length; },
  48. [](Percentage const&) { return CalculatedStyleValue::ResolvedType::Percentage; },
  49. [](Time const&) { return CalculatedStyleValue::ResolvedType::Time; });
  50. }
  51. bool NumericCalculationNode::contains_percentage() const
  52. {
  53. return m_value.has<Percentage>();
  54. }
  55. CalculatedStyleValue::CalculationResult NumericCalculationNode::resolve(Layout::Node const*, CalculatedStyleValue::PercentageBasis const&) const
  56. {
  57. return m_value;
  58. }
  59. ErrorOr<void> NumericCalculationNode::dump(StringBuilder& builder, int indent) const
  60. {
  61. return builder.try_appendff("{: >{}}NUMERIC({})\n", "", indent, TRY(m_value.visit([](auto& it) { return it.to_string(); })));
  62. }
  63. ErrorOr<NonnullOwnPtr<SumCalculationNode>> SumCalculationNode::create(Vector<NonnullOwnPtr<CalculationNode>> values)
  64. {
  65. return adopt_nonnull_own_or_enomem(new (nothrow) SumCalculationNode(move(values)));
  66. }
  67. SumCalculationNode::SumCalculationNode(Vector<NonnullOwnPtr<CalculationNode>> values)
  68. : CalculationNode(Type::Sum)
  69. , m_values(move(values))
  70. {
  71. VERIFY(!m_values.is_empty());
  72. }
  73. SumCalculationNode::~SumCalculationNode() = default;
  74. ErrorOr<String> SumCalculationNode::to_string() const
  75. {
  76. bool first = true;
  77. StringBuilder builder;
  78. for (auto& value : m_values) {
  79. if (!first)
  80. TRY(builder.try_append(" + "sv));
  81. TRY(builder.try_append(TRY(value->to_string())));
  82. first = false;
  83. }
  84. return builder.to_string();
  85. }
  86. Optional<CalculatedStyleValue::ResolvedType> SumCalculationNode::resolved_type() const
  87. {
  88. // FIXME: Implement https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
  89. // For now, this is just ad-hoc, based on the old implementation.
  90. Optional<CalculatedStyleValue::ResolvedType> type;
  91. for (auto const& value : m_values) {
  92. auto maybe_value_type = value->resolved_type();
  93. if (!maybe_value_type.has_value())
  94. return {};
  95. auto value_type = maybe_value_type.value();
  96. if (!type.has_value()) {
  97. type = value_type;
  98. continue;
  99. }
  100. // At + or -, check that both sides have the same type, or that one side is a <number> and the other is an <integer>.
  101. // If both sides are the same type, resolve to that type.
  102. if (value_type == type)
  103. continue;
  104. // If one side is a <number> and the other is an <integer>, resolve to <number>.
  105. if (is_number(*type) && is_number(value_type)) {
  106. type = CalculatedStyleValue::ResolvedType::Number;
  107. continue;
  108. }
  109. // FIXME: calc() handles <percentage> by allowing them to pretend to be whatever <dimension> type is allowed at this location.
  110. // Since we can't easily check what that type is, we just allow <percentage> to combine with any other <dimension> type.
  111. if (type == CalculatedStyleValue::ResolvedType::Percentage && is_dimension(value_type)) {
  112. type = value_type;
  113. continue;
  114. }
  115. if (is_dimension(*type) && value_type == CalculatedStyleValue::ResolvedType::Percentage)
  116. continue;
  117. return {};
  118. }
  119. return type;
  120. }
  121. bool SumCalculationNode::contains_percentage() const
  122. {
  123. for (auto const& value : m_values) {
  124. if (value->contains_percentage())
  125. return true;
  126. }
  127. return false;
  128. }
  129. CalculatedStyleValue::CalculationResult SumCalculationNode::resolve(Layout::Node const* layout_node, CalculatedStyleValue::PercentageBasis const& percentage_basis) const
  130. {
  131. Optional<CalculatedStyleValue::CalculationResult> total;
  132. for (auto& additional_product : m_values) {
  133. auto additional_value = additional_product->resolve(layout_node, percentage_basis);
  134. if (!total.has_value()) {
  135. total = additional_value;
  136. continue;
  137. }
  138. total->add(additional_value, layout_node, percentage_basis);
  139. }
  140. return total.value();
  141. }
  142. ErrorOr<void> SumCalculationNode::for_each_child_node(Function<ErrorOr<void>(NonnullOwnPtr<CalculationNode>&)> const& callback)
  143. {
  144. for (auto& item : m_values) {
  145. TRY(item->for_each_child_node(callback));
  146. TRY(callback(item));
  147. }
  148. return {};
  149. }
  150. ErrorOr<void> SumCalculationNode::dump(StringBuilder& builder, int indent) const
  151. {
  152. TRY(builder.try_appendff("{: >{}}SUM:\n", "", indent));
  153. for (auto const& item : m_values)
  154. TRY(item->dump(builder, indent + 2));
  155. return {};
  156. }
  157. ErrorOr<NonnullOwnPtr<ProductCalculationNode>> ProductCalculationNode::create(Vector<NonnullOwnPtr<CalculationNode>> values)
  158. {
  159. return adopt_nonnull_own_or_enomem(new (nothrow) ProductCalculationNode(move(values)));
  160. }
  161. ProductCalculationNode::ProductCalculationNode(Vector<NonnullOwnPtr<CalculationNode>> values)
  162. : CalculationNode(Type::Product)
  163. , m_values(move(values))
  164. {
  165. VERIFY(!m_values.is_empty());
  166. }
  167. ProductCalculationNode::~ProductCalculationNode() = default;
  168. ErrorOr<String> ProductCalculationNode::to_string() const
  169. {
  170. bool first = true;
  171. StringBuilder builder;
  172. for (auto& value : m_values) {
  173. if (!first)
  174. TRY(builder.try_append(" * "sv));
  175. TRY(builder.try_append(TRY(value->to_string())));
  176. first = false;
  177. }
  178. return builder.to_string();
  179. }
  180. Optional<CalculatedStyleValue::ResolvedType> ProductCalculationNode::resolved_type() const
  181. {
  182. // FIXME: Implement https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
  183. // For now, this is just ad-hoc, based on the old implementation.
  184. Optional<CalculatedStyleValue::ResolvedType> type;
  185. for (auto const& value : m_values) {
  186. auto maybe_value_type = value->resolved_type();
  187. if (!maybe_value_type.has_value())
  188. return {};
  189. auto value_type = maybe_value_type.value();
  190. if (!type.has_value()) {
  191. type = value_type;
  192. continue;
  193. }
  194. // At *, check that at least one side is <number>.
  195. if (!(is_number(*type) || is_number(value_type)))
  196. return {};
  197. // If both sides are <integer>, resolve to <integer>.
  198. if (type == CalculatedStyleValue::ResolvedType::Integer && value_type == CalculatedStyleValue::ResolvedType::Integer) {
  199. type = CalculatedStyleValue::ResolvedType::Integer;
  200. } else {
  201. // Otherwise, resolve to the type of the other side.
  202. if (is_number(*type))
  203. type = value_type;
  204. }
  205. }
  206. return type;
  207. }
  208. bool ProductCalculationNode::contains_percentage() const
  209. {
  210. for (auto const& value : m_values) {
  211. if (value->contains_percentage())
  212. return true;
  213. }
  214. return false;
  215. }
  216. CalculatedStyleValue::CalculationResult ProductCalculationNode::resolve(Layout::Node const* layout_node, CalculatedStyleValue::PercentageBasis const& percentage_basis) const
  217. {
  218. Optional<CalculatedStyleValue::CalculationResult> total;
  219. for (auto& additional_product : m_values) {
  220. auto additional_value = additional_product->resolve(layout_node, percentage_basis);
  221. if (!total.has_value()) {
  222. total = additional_value;
  223. continue;
  224. }
  225. total->multiply_by(additional_value, layout_node);
  226. }
  227. return total.value();
  228. }
  229. ErrorOr<void> ProductCalculationNode::for_each_child_node(Function<ErrorOr<void>(NonnullOwnPtr<CalculationNode>&)> const& callback)
  230. {
  231. for (auto& item : m_values) {
  232. TRY(item->for_each_child_node(callback));
  233. TRY(callback(item));
  234. }
  235. return {};
  236. }
  237. ErrorOr<void> ProductCalculationNode::dump(StringBuilder& builder, int indent) const
  238. {
  239. TRY(builder.try_appendff("{: >{}}PRODUCT:\n", "", indent));
  240. for (auto const& item : m_values)
  241. TRY(item->dump(builder, indent + 2));
  242. return {};
  243. }
  244. ErrorOr<NonnullOwnPtr<NegateCalculationNode>> NegateCalculationNode::create(NonnullOwnPtr<Web::CSS::CalculationNode> value)
  245. {
  246. return adopt_nonnull_own_or_enomem(new (nothrow) NegateCalculationNode(move(value)));
  247. }
  248. NegateCalculationNode::NegateCalculationNode(NonnullOwnPtr<CalculationNode> value)
  249. : CalculationNode(Type::Negate)
  250. , m_value(move(value))
  251. {
  252. }
  253. NegateCalculationNode::~NegateCalculationNode() = default;
  254. ErrorOr<String> NegateCalculationNode::to_string() const
  255. {
  256. return String::formatted("(0 - {})", TRY(m_value->to_string()));
  257. }
  258. Optional<CalculatedStyleValue::ResolvedType> NegateCalculationNode::resolved_type() const
  259. {
  260. return m_value->resolved_type();
  261. }
  262. bool NegateCalculationNode::contains_percentage() const
  263. {
  264. return m_value->contains_percentage();
  265. }
  266. CalculatedStyleValue::CalculationResult NegateCalculationNode::resolve(Layout::Node const* layout_node, CalculatedStyleValue::PercentageBasis const& percentage_basis) const
  267. {
  268. auto child_value = m_value->resolve(layout_node, percentage_basis);
  269. child_value.negate();
  270. return child_value;
  271. }
  272. ErrorOr<void> NegateCalculationNode::for_each_child_node(Function<ErrorOr<void>(NonnullOwnPtr<CalculationNode>&)> const& callback)
  273. {
  274. TRY(m_value->for_each_child_node(callback));
  275. TRY(callback(m_value));
  276. return {};
  277. }
  278. ErrorOr<void> NegateCalculationNode::dump(StringBuilder& builder, int indent) const
  279. {
  280. TRY(builder.try_appendff("{: >{}}NEGATE:\n", "", indent));
  281. TRY(m_value->dump(builder, indent + 2));
  282. return {};
  283. }
  284. ErrorOr<NonnullOwnPtr<InvertCalculationNode>> InvertCalculationNode::create(NonnullOwnPtr<Web::CSS::CalculationNode> value)
  285. {
  286. return adopt_nonnull_own_or_enomem(new (nothrow) InvertCalculationNode(move(value)));
  287. }
  288. InvertCalculationNode::InvertCalculationNode(NonnullOwnPtr<CalculationNode> value)
  289. : CalculationNode(Type::Invert)
  290. , m_value(move(value))
  291. {
  292. }
  293. InvertCalculationNode::~InvertCalculationNode() = default;
  294. ErrorOr<String> InvertCalculationNode::to_string() const
  295. {
  296. return String::formatted("(1 / {})", TRY(m_value->to_string()));
  297. }
  298. Optional<CalculatedStyleValue::ResolvedType> InvertCalculationNode::resolved_type() const
  299. {
  300. auto type = m_value->resolved_type();
  301. if (type == CalculatedStyleValue::ResolvedType::Integer)
  302. return CalculatedStyleValue::ResolvedType::Number;
  303. return type;
  304. }
  305. bool InvertCalculationNode::contains_percentage() const
  306. {
  307. return m_value->contains_percentage();
  308. }
  309. CalculatedStyleValue::CalculationResult InvertCalculationNode::resolve(Layout::Node const* layout_node, CalculatedStyleValue::PercentageBasis const& percentage_basis) const
  310. {
  311. auto child_value = m_value->resolve(layout_node, percentage_basis);
  312. child_value.invert();
  313. return child_value;
  314. }
  315. ErrorOr<void> InvertCalculationNode::for_each_child_node(Function<ErrorOr<void>(NonnullOwnPtr<CalculationNode>&)> const& callback)
  316. {
  317. TRY(m_value->for_each_child_node(callback));
  318. TRY(callback(m_value));
  319. return {};
  320. }
  321. ErrorOr<void> InvertCalculationNode::dump(StringBuilder& builder, int indent) const
  322. {
  323. TRY(builder.try_appendff("{: >{}}INVERT:\n", "", indent));
  324. TRY(m_value->dump(builder, indent + 2));
  325. return {};
  326. }
  327. void CalculatedStyleValue::CalculationResult::add(CalculationResult const& other, Layout::Node const* layout_node, PercentageBasis const& percentage_basis)
  328. {
  329. add_or_subtract_internal(SumOperation::Add, other, layout_node, percentage_basis);
  330. }
  331. void CalculatedStyleValue::CalculationResult::subtract(CalculationResult const& other, Layout::Node const* layout_node, PercentageBasis const& percentage_basis)
  332. {
  333. add_or_subtract_internal(SumOperation::Subtract, other, layout_node, percentage_basis);
  334. }
  335. void CalculatedStyleValue::CalculationResult::add_or_subtract_internal(SumOperation op, CalculationResult const& other, Layout::Node const* layout_node, PercentageBasis const& percentage_basis)
  336. {
  337. // We know from validation when resolving the type, that "both sides have the same type, or that one side is a <number> and the other is an <integer>".
  338. // Though, having the same type may mean that one side is a <dimension> and the other a <percentage>.
  339. // Note: This is almost identical to ::add()
  340. m_value.visit(
  341. [&](Number const& number) {
  342. auto other_number = other.m_value.get<Number>();
  343. if (op == SumOperation::Add) {
  344. m_value = number + other_number;
  345. } else {
  346. m_value = number - other_number;
  347. }
  348. },
  349. [&](Angle const& angle) {
  350. auto this_degrees = angle.to_degrees();
  351. if (other.m_value.has<Angle>()) {
  352. auto other_degrees = other.m_value.get<Angle>().to_degrees();
  353. if (op == SumOperation::Add)
  354. m_value = Angle::make_degrees(this_degrees + other_degrees);
  355. else
  356. m_value = Angle::make_degrees(this_degrees - other_degrees);
  357. } else {
  358. VERIFY(percentage_basis.has<Angle>());
  359. auto other_degrees = percentage_basis.get<Angle>().percentage_of(other.m_value.get<Percentage>()).to_degrees();
  360. if (op == SumOperation::Add)
  361. m_value = Angle::make_degrees(this_degrees + other_degrees);
  362. else
  363. m_value = Angle::make_degrees(this_degrees - other_degrees);
  364. }
  365. },
  366. [&](Frequency const& frequency) {
  367. auto this_hertz = frequency.to_hertz();
  368. if (other.m_value.has<Frequency>()) {
  369. auto other_hertz = other.m_value.get<Frequency>().to_hertz();
  370. if (op == SumOperation::Add)
  371. m_value = Frequency::make_hertz(this_hertz + other_hertz);
  372. else
  373. m_value = Frequency::make_hertz(this_hertz - other_hertz);
  374. } else {
  375. VERIFY(percentage_basis.has<Frequency>());
  376. auto other_hertz = percentage_basis.get<Frequency>().percentage_of(other.m_value.get<Percentage>()).to_hertz();
  377. if (op == SumOperation::Add)
  378. m_value = Frequency::make_hertz(this_hertz + other_hertz);
  379. else
  380. m_value = Frequency::make_hertz(this_hertz - other_hertz);
  381. }
  382. },
  383. [&](Length const& length) {
  384. auto this_px = length.to_px(*layout_node);
  385. if (other.m_value.has<Length>()) {
  386. auto other_px = other.m_value.get<Length>().to_px(*layout_node);
  387. if (op == SumOperation::Add)
  388. m_value = Length::make_px(this_px + other_px);
  389. else
  390. m_value = Length::make_px(this_px - other_px);
  391. } else {
  392. VERIFY(percentage_basis.has<Length>());
  393. auto other_px = percentage_basis.get<Length>().percentage_of(other.m_value.get<Percentage>()).to_px(*layout_node);
  394. if (op == SumOperation::Add)
  395. m_value = Length::make_px(this_px + other_px);
  396. else
  397. m_value = Length::make_px(this_px - other_px);
  398. }
  399. },
  400. [&](Time const& time) {
  401. auto this_seconds = time.to_seconds();
  402. if (other.m_value.has<Time>()) {
  403. auto other_seconds = other.m_value.get<Time>().to_seconds();
  404. if (op == SumOperation::Add)
  405. m_value = Time::make_seconds(this_seconds + other_seconds);
  406. else
  407. m_value = Time::make_seconds(this_seconds - other_seconds);
  408. } else {
  409. VERIFY(percentage_basis.has<Time>());
  410. auto other_seconds = percentage_basis.get<Time>().percentage_of(other.m_value.get<Percentage>()).to_seconds();
  411. if (op == SumOperation::Add)
  412. m_value = Time::make_seconds(this_seconds + other_seconds);
  413. else
  414. m_value = Time::make_seconds(this_seconds - other_seconds);
  415. }
  416. },
  417. [&](Percentage const& percentage) {
  418. if (other.m_value.has<Percentage>()) {
  419. if (op == SumOperation::Add)
  420. m_value = Percentage { percentage.value() + other.m_value.get<Percentage>().value() };
  421. else
  422. m_value = Percentage { percentage.value() - other.m_value.get<Percentage>().value() };
  423. return;
  424. }
  425. // Other side isn't a percentage, so the easiest way to handle it without duplicating all the logic, is just to swap `this` and `other`.
  426. CalculationResult new_value = other;
  427. if (op == SumOperation::Add) {
  428. new_value.add(*this, layout_node, percentage_basis);
  429. } else {
  430. // Turn 'this - other' into '-other + this', as 'A + B == B + A', but 'A - B != B - A'
  431. new_value.multiply_by({ Number { Number::Type::Integer, -1.0f } }, layout_node);
  432. new_value.add(*this, layout_node, percentage_basis);
  433. }
  434. *this = new_value;
  435. });
  436. }
  437. void CalculatedStyleValue::CalculationResult::multiply_by(CalculationResult const& other, Layout::Node const* layout_node)
  438. {
  439. // We know from validation when resolving the type, that at least one side must be a <number> or <integer>.
  440. // Both of these are represented as a float.
  441. VERIFY(m_value.has<Number>() || other.m_value.has<Number>());
  442. bool other_is_number = other.m_value.has<Number>();
  443. m_value.visit(
  444. [&](Number const& number) {
  445. if (other_is_number) {
  446. m_value = number * other.m_value.get<Number>();
  447. } else {
  448. // Avoid duplicating all the logic by swapping `this` and `other`.
  449. CalculationResult new_value = other;
  450. new_value.multiply_by(*this, layout_node);
  451. *this = new_value;
  452. }
  453. },
  454. [&](Angle const& angle) {
  455. m_value = Angle::make_degrees(angle.to_degrees() * other.m_value.get<Number>().value());
  456. },
  457. [&](Frequency const& frequency) {
  458. m_value = Frequency::make_hertz(frequency.to_hertz() * other.m_value.get<Number>().value());
  459. },
  460. [&](Length const& length) {
  461. VERIFY(layout_node);
  462. m_value = Length::make_px(length.to_px(*layout_node) * other.m_value.get<Number>().value());
  463. },
  464. [&](Time const& time) {
  465. m_value = Time::make_seconds(time.to_seconds() * other.m_value.get<Number>().value());
  466. },
  467. [&](Percentage const& percentage) {
  468. m_value = Percentage { percentage.value() * other.m_value.get<Number>().value() };
  469. });
  470. }
  471. void CalculatedStyleValue::CalculationResult::divide_by(CalculationResult const& other, Layout::Node const* layout_node)
  472. {
  473. // We know from validation when resolving the type, that `other` must be a <number> or <integer>.
  474. // Both of these are represented as a Number.
  475. auto denominator = other.m_value.get<Number>().value();
  476. // FIXME: Dividing by 0 is invalid, and should be caught during parsing.
  477. VERIFY(denominator != 0.0f);
  478. m_value.visit(
  479. [&](Number const& number) {
  480. m_value = Number {
  481. Number::Type::Number,
  482. number.value() / denominator
  483. };
  484. },
  485. [&](Angle const& angle) {
  486. m_value = Angle::make_degrees(angle.to_degrees() / denominator);
  487. },
  488. [&](Frequency const& frequency) {
  489. m_value = Frequency::make_hertz(frequency.to_hertz() / denominator);
  490. },
  491. [&](Length const& length) {
  492. VERIFY(layout_node);
  493. m_value = Length::make_px(length.to_px(*layout_node) / denominator);
  494. },
  495. [&](Time const& time) {
  496. m_value = Time::make_seconds(time.to_seconds() / denominator);
  497. },
  498. [&](Percentage const& percentage) {
  499. m_value = Percentage { percentage.value() / denominator };
  500. });
  501. }
  502. void CalculatedStyleValue::CalculationResult::negate()
  503. {
  504. m_value.visit(
  505. [&](Number const& number) {
  506. m_value = Number { number.type(), 0 - number.value() };
  507. },
  508. [&](Angle const& angle) {
  509. m_value = Angle { 0 - angle.raw_value(), angle.type() };
  510. },
  511. [&](Frequency const& frequency) {
  512. m_value = Frequency { 0 - frequency.raw_value(), frequency.type() };
  513. },
  514. [&](Length const& length) {
  515. m_value = Length { 0 - length.raw_value(), length.type() };
  516. },
  517. [&](Time const& time) {
  518. m_value = Time { 0 - time.raw_value(), time.type() };
  519. },
  520. [&](Percentage const& percentage) {
  521. m_value = Percentage { 0 - percentage.value() };
  522. });
  523. }
  524. void CalculatedStyleValue::CalculationResult::invert()
  525. {
  526. // FIXME: Correctly handle division by zero.
  527. m_value.visit(
  528. [&](Number const& number) {
  529. m_value = Number { Number::Type::Number, 1 / number.value() };
  530. },
  531. [&](Angle const& angle) {
  532. m_value = Angle { 1 / angle.raw_value(), angle.type() };
  533. },
  534. [&](Frequency const& frequency) {
  535. m_value = Frequency { 1 / frequency.raw_value(), frequency.type() };
  536. },
  537. [&](Length const& length) {
  538. m_value = Length { 1 / length.raw_value(), length.type() };
  539. },
  540. [&](Time const& time) {
  541. m_value = Time { 1 / time.raw_value(), time.type() };
  542. },
  543. [&](Percentage const& percentage) {
  544. m_value = Percentage { 1 / percentage.value() };
  545. });
  546. }
  547. ErrorOr<String> CalculatedStyleValue::to_string() const
  548. {
  549. // FIXME: Implement this according to https://www.w3.org/TR/css-values-4/#calc-serialize once that stabilizes.
  550. return String::formatted("calc({})", TRY(m_calculation->to_string()));
  551. }
  552. bool CalculatedStyleValue::equals(StyleValue const& other) const
  553. {
  554. if (type() != other.type())
  555. return false;
  556. // This is a case where comparing the strings actually makes sense.
  557. return to_string().release_value_but_fixme_should_propagate_errors() == other.to_string().release_value_but_fixme_should_propagate_errors();
  558. }
  559. Optional<Angle> CalculatedStyleValue::resolve_angle() const
  560. {
  561. auto result = m_calculation->resolve(nullptr, {});
  562. if (result.value().has<Angle>())
  563. return result.value().get<Angle>();
  564. return {};
  565. }
  566. Optional<Angle> CalculatedStyleValue::resolve_angle_percentage(Angle const& percentage_basis) const
  567. {
  568. auto result = m_calculation->resolve(nullptr, percentage_basis);
  569. return result.value().visit(
  570. [&](Angle const& angle) -> Optional<Angle> {
  571. return angle;
  572. },
  573. [&](Percentage const& percentage) -> Optional<Angle> {
  574. return percentage_basis.percentage_of(percentage);
  575. },
  576. [&](auto const&) -> Optional<Angle> {
  577. return {};
  578. });
  579. }
  580. Optional<Frequency> CalculatedStyleValue::resolve_frequency() const
  581. {
  582. auto result = m_calculation->resolve(nullptr, {});
  583. if (result.value().has<Frequency>())
  584. return result.value().get<Frequency>();
  585. return {};
  586. }
  587. Optional<Frequency> CalculatedStyleValue::resolve_frequency_percentage(Frequency const& percentage_basis) const
  588. {
  589. auto result = m_calculation->resolve(nullptr, percentage_basis);
  590. return result.value().visit(
  591. [&](Frequency const& frequency) -> Optional<Frequency> {
  592. return frequency;
  593. },
  594. [&](Percentage const& percentage) -> Optional<Frequency> {
  595. return percentage_basis.percentage_of(percentage);
  596. },
  597. [&](auto const&) -> Optional<Frequency> {
  598. return {};
  599. });
  600. }
  601. Optional<Length> CalculatedStyleValue::resolve_length(Layout::Node const& layout_node) const
  602. {
  603. auto result = m_calculation->resolve(&layout_node, {});
  604. if (result.value().has<Length>())
  605. return result.value().get<Length>();
  606. return {};
  607. }
  608. Optional<Length> CalculatedStyleValue::resolve_length_percentage(Layout::Node const& layout_node, Length const& percentage_basis) const
  609. {
  610. auto result = m_calculation->resolve(&layout_node, percentage_basis);
  611. return result.value().visit(
  612. [&](Length const& length) -> Optional<Length> {
  613. return length;
  614. },
  615. [&](Percentage const& percentage) -> Optional<Length> {
  616. return percentage_basis.percentage_of(percentage);
  617. },
  618. [&](auto const&) -> Optional<Length> {
  619. return {};
  620. });
  621. }
  622. Optional<Percentage> CalculatedStyleValue::resolve_percentage() const
  623. {
  624. auto result = m_calculation->resolve(nullptr, {});
  625. if (result.value().has<Percentage>())
  626. return result.value().get<Percentage>();
  627. return {};
  628. }
  629. Optional<Time> CalculatedStyleValue::resolve_time() const
  630. {
  631. auto result = m_calculation->resolve(nullptr, {});
  632. if (result.value().has<Time>())
  633. return result.value().get<Time>();
  634. return {};
  635. }
  636. Optional<Time> CalculatedStyleValue::resolve_time_percentage(Time const& percentage_basis) const
  637. {
  638. auto result = m_calculation->resolve(nullptr, percentage_basis);
  639. return result.value().visit(
  640. [&](Time const& time) -> Optional<Time> {
  641. return time;
  642. },
  643. [&](auto const&) -> Optional<Time> {
  644. return {};
  645. });
  646. }
  647. Optional<float> CalculatedStyleValue::resolve_number()
  648. {
  649. auto result = m_calculation->resolve(nullptr, {});
  650. if (result.value().has<Number>())
  651. return result.value().get<Number>().value();
  652. return {};
  653. }
  654. Optional<i64> CalculatedStyleValue::resolve_integer()
  655. {
  656. auto result = m_calculation->resolve(nullptr, {});
  657. if (result.value().has<Number>())
  658. return result.value().get<Number>().integer_value();
  659. return {};
  660. }
  661. bool CalculatedStyleValue::contains_percentage() const
  662. {
  663. return m_calculation->contains_percentage();
  664. }
  665. }