CountersSet.cpp 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. /*
  2. * Copyright (c) 2024, Sam Atkins <sam@ladybird.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "CountersSet.h"
  7. #include <LibWeb/DOM/Element.h>
  8. #include <LibWeb/DOM/Node.h>
  9. namespace Web::CSS {
  10. // https://drafts.csswg.org/css-lists-3/#instantiate-counter
  11. Counter& CountersSet::instantiate_a_counter(FlyString name, i32 originating_element_id, bool reversed, Optional<CounterValue> value)
  12. {
  13. // 1. Let counters be element’s CSS counters set.
  14. auto* element = DOM::Node::from_unique_id(originating_element_id);
  15. // 2. Let innermost counter be the last counter in counters with the name name.
  16. // If innermost counter’s originating element is element or a previous sibling of element,
  17. // remove innermost counter from counters.
  18. auto innermost_counter = last_counter_with_name(name);
  19. if (innermost_counter.has_value()) {
  20. auto* originating_node = DOM::Node::from_unique_id(innermost_counter->originating_element_id);
  21. VERIFY(originating_node);
  22. auto& innermost_element = verify_cast<DOM::Element>(*originating_node);
  23. if (&innermost_element == element
  24. || (innermost_element.parent() == element->parent() && innermost_element.is_before(*element))) {
  25. m_counters.remove_first_matching([&innermost_counter](auto& it) {
  26. return it.name == innermost_counter->name
  27. && it.originating_element_id == innermost_counter->originating_element_id;
  28. });
  29. }
  30. }
  31. // 3. Append a new counter to counters with name name, originating element element,
  32. // reversed being reversed, and initial value value (if given)
  33. m_counters.append({
  34. .name = move(name),
  35. .originating_element_id = originating_element_id,
  36. .reversed = reversed,
  37. .value = value,
  38. });
  39. return m_counters.last();
  40. }
  41. // https://drafts.csswg.org/css-lists-3/#propdef-counter-set
  42. void CountersSet::set_a_counter(FlyString name, i32 originating_element_id, CounterValue value)
  43. {
  44. if (auto existing_counter = last_counter_with_name(name); existing_counter.has_value()) {
  45. existing_counter->value = value;
  46. return;
  47. }
  48. // If there is not currently a counter of the given name on the element, the element instantiates
  49. // a new counter of the given name with a starting value of 0 before setting or incrementing its value.
  50. // https://drafts.csswg.org/css-lists-3/#valdef-counter-set-counter-name-integer
  51. auto& counter = instantiate_a_counter(name, originating_element_id, false, 0);
  52. counter.value = value;
  53. }
  54. // https://drafts.csswg.org/css-lists-3/#propdef-counter-increment
  55. void CountersSet::increment_a_counter(FlyString name, i32 originating_element_id, CounterValue amount)
  56. {
  57. if (auto existing_counter = last_counter_with_name(name); existing_counter.has_value()) {
  58. // FIXME: How should we handle existing counters with no value? Can that happen?
  59. VERIFY(existing_counter->value.has_value());
  60. existing_counter->value->saturating_add(amount.value());
  61. return;
  62. }
  63. // If there is not currently a counter of the given name on the element, the element instantiates
  64. // a new counter of the given name with a starting value of 0 before setting or incrementing its value.
  65. // https://drafts.csswg.org/css-lists-3/#valdef-counter-set-counter-name-integer
  66. auto& counter = instantiate_a_counter(name, originating_element_id, false, 0);
  67. counter.value->saturating_add(amount.value());
  68. }
  69. Optional<Counter&> CountersSet::last_counter_with_name(FlyString const& name)
  70. {
  71. for (auto& counter : m_counters.in_reverse()) {
  72. if (counter.name == name)
  73. return counter;
  74. }
  75. return {};
  76. }
  77. Optional<Counter&> CountersSet::counter_with_same_name_and_creator(FlyString const& name, i32 originating_element_id)
  78. {
  79. return m_counters.first_matching([&](auto& it) {
  80. return it.name == name && it.originating_element_id == originating_element_id;
  81. });
  82. }
  83. void CountersSet::append_copy(Counter const& counter)
  84. {
  85. m_counters.append(counter);
  86. }
  87. }