StackOfOpenElements.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. /*
  2. * Copyright (c) 2020-2022, Andreas Kling <andreas@ladybird.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibWeb/DOM/Element.h>
  7. #include <LibWeb/HTML/Parser/HTMLParser.h>
  8. #include <LibWeb/HTML/Parser/StackOfOpenElements.h>
  9. #include <LibWeb/Namespace.h>
  10. namespace Web::HTML {
  11. static Vector<FlyString> s_base_list { "applet"_fly_string, "caption"_fly_string, "html"_fly_string, "table"_fly_string, "td"_fly_string, "th"_fly_string, "marquee"_fly_string, "object"_fly_string, "template"_fly_string, "select"_fly_string };
  12. StackOfOpenElements::~StackOfOpenElements() = default;
  13. void StackOfOpenElements::visit_edges(JS::Cell::Visitor& visitor)
  14. {
  15. visitor.visit(m_elements);
  16. }
  17. bool StackOfOpenElements::has_in_scope_impl(FlyString const& tag_name, Vector<FlyString> const& list) const
  18. {
  19. for (auto const& element : m_elements.in_reverse()) {
  20. if (element->local_name() == tag_name)
  21. return true;
  22. if (list.contains_slow(element->local_name()))
  23. return false;
  24. }
  25. VERIFY_NOT_REACHED();
  26. }
  27. bool StackOfOpenElements::has_in_scope(FlyString const& tag_name) const
  28. {
  29. return has_in_scope_impl(tag_name, s_base_list);
  30. }
  31. bool StackOfOpenElements::has_in_scope_impl(const DOM::Element& target_node, Vector<FlyString> const& list) const
  32. {
  33. for (auto& element : m_elements.in_reverse()) {
  34. if (element.ptr() == &target_node)
  35. return true;
  36. if (list.contains_slow(element->local_name()))
  37. return false;
  38. }
  39. VERIFY_NOT_REACHED();
  40. }
  41. bool StackOfOpenElements::has_in_scope(const DOM::Element& target_node) const
  42. {
  43. return has_in_scope_impl(target_node, s_base_list);
  44. }
  45. bool StackOfOpenElements::has_in_button_scope(FlyString const& tag_name) const
  46. {
  47. auto list = s_base_list;
  48. list.append("button"_fly_string);
  49. return has_in_scope_impl(tag_name, list);
  50. }
  51. bool StackOfOpenElements::has_in_table_scope(FlyString const& tag_name) const
  52. {
  53. return has_in_scope_impl(tag_name, { "html"_fly_string, "table"_fly_string, "template"_fly_string });
  54. }
  55. bool StackOfOpenElements::has_in_list_item_scope(FlyString const& tag_name) const
  56. {
  57. auto list = s_base_list;
  58. list.append("ol"_fly_string);
  59. list.append("ul"_fly_string);
  60. return has_in_scope_impl(tag_name, list);
  61. }
  62. bool StackOfOpenElements::contains(const DOM::Element& element) const
  63. {
  64. for (auto& element_on_stack : m_elements) {
  65. if (&element == element_on_stack.ptr())
  66. return true;
  67. }
  68. return false;
  69. }
  70. bool StackOfOpenElements::contains_template_element() const
  71. {
  72. for (auto const& element : m_elements) {
  73. if (element->namespace_uri() != Namespace::HTML)
  74. continue;
  75. if (element->local_name() == HTML::TagNames::template_)
  76. return true;
  77. }
  78. return false;
  79. }
  80. void StackOfOpenElements::pop_until_an_element_with_tag_name_has_been_popped(FlyString const& tag_name)
  81. {
  82. while (m_elements.last()->namespace_uri() != Namespace::HTML || m_elements.last()->local_name() != tag_name)
  83. (void)pop();
  84. (void)pop();
  85. }
  86. GC::Ptr<DOM::Element> StackOfOpenElements::topmost_special_node_below(DOM::Element const& formatting_element)
  87. {
  88. GC::Ptr<DOM::Element> found_element = nullptr;
  89. for (auto& element : m_elements.in_reverse()) {
  90. if (element.ptr() == &formatting_element)
  91. break;
  92. if (HTMLParser::is_special_tag(element->local_name(), element->namespace_uri()))
  93. found_element = element.ptr();
  94. }
  95. return found_element.ptr();
  96. }
  97. StackOfOpenElements::LastElementResult StackOfOpenElements::last_element_with_tag_name(FlyString const& tag_name)
  98. {
  99. for (ssize_t i = m_elements.size() - 1; i >= 0; --i) {
  100. auto& element = m_elements[i];
  101. if (element->local_name() == tag_name)
  102. return { element.ptr(), i };
  103. }
  104. return { nullptr, -1 };
  105. }
  106. GC::Ptr<DOM::Element> StackOfOpenElements::element_immediately_above(DOM::Element const& target)
  107. {
  108. bool found_target = false;
  109. for (auto& element : m_elements.in_reverse()) {
  110. if (element.ptr() == &target) {
  111. found_target = true;
  112. } else if (found_target)
  113. return element.ptr();
  114. }
  115. return nullptr;
  116. }
  117. void StackOfOpenElements::remove(DOM::Element const& element)
  118. {
  119. m_elements.remove_first_matching([&element](auto& other) {
  120. return other.ptr() == &element;
  121. });
  122. }
  123. void StackOfOpenElements::replace(DOM::Element const& to_remove, GC::Ref<DOM::Element> to_add)
  124. {
  125. for (size_t i = 0; i < m_elements.size(); i++) {
  126. if (m_elements[i].ptr() == &to_remove) {
  127. m_elements.remove(i);
  128. m_elements.insert(i, to_add);
  129. break;
  130. }
  131. }
  132. }
  133. void StackOfOpenElements::insert_immediately_below(GC::Ref<DOM::Element> element_to_add, DOM::Element const& target)
  134. {
  135. for (size_t i = 0; i < m_elements.size(); i++) {
  136. if (m_elements[i].ptr() == &target) {
  137. m_elements.insert(i + 1, element_to_add);
  138. break;
  139. }
  140. }
  141. }
  142. }