ChildNode.h 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. /*
  2. * Copyright (c) 2021-2022, Luke Wilde <lukew@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #pragma once
  7. #include <LibWeb/DOM/ExceptionOr.h>
  8. #include <LibWeb/DOM/NodeOperations.h>
  9. namespace Web::DOM {
  10. // https://dom.spec.whatwg.org/#childnode
  11. template<typename NodeType>
  12. class ChildNode {
  13. public:
  14. // https://dom.spec.whatwg.org/#dom-childnode-before
  15. ExceptionOr<void> before(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes)
  16. {
  17. auto* node = static_cast<NodeType*>(this);
  18. // 1. Let parent be this’s parent.
  19. auto* parent = node->parent();
  20. // 2. If parent is null, then return.
  21. if (!parent)
  22. return {};
  23. // 3. Let viablePreviousSibling be this’s first preceding sibling not in nodes; otherwise null.
  24. auto viable_previous_sibling = viable_previous_sibling_for_insertion(nodes);
  25. // 4. Let node be the result of converting nodes into a node, given nodes and this’s node document.
  26. auto node_to_insert = TRY(convert_nodes_to_single_node(nodes, node->document()));
  27. // 5. If viablePreviousSibling is null, then set it to parent’s first child; otherwise to viablePreviousSibling’s next sibling.
  28. if (!viable_previous_sibling)
  29. viable_previous_sibling = parent->first_child();
  30. else
  31. viable_previous_sibling = viable_previous_sibling->next_sibling();
  32. // 6. Pre-insert node into parent before viablePreviousSibling.
  33. (void)TRY(parent->pre_insert(node_to_insert, viable_previous_sibling));
  34. return {};
  35. }
  36. // https://dom.spec.whatwg.org/#dom-childnode-after
  37. ExceptionOr<void> after(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes)
  38. {
  39. auto* node = static_cast<NodeType*>(this);
  40. // 1. Let parent be this’s parent.
  41. auto* parent = node->parent();
  42. // 2. If parent is null, then return.
  43. if (!parent)
  44. return {};
  45. // 3. Let viableNextSibling be this’s first following sibling not in nodes; otherwise null.
  46. auto viable_next_sibling = viable_nest_sibling_for_insertion(nodes);
  47. // 4. Let node be the result of converting nodes into a node, given nodes and this’s node document.
  48. auto node_to_insert = TRY(convert_nodes_to_single_node(nodes, node->document()));
  49. // 5. Pre-insert node into parent before viableNextSibling.
  50. (void)TRY(parent->pre_insert(node_to_insert, viable_next_sibling));
  51. return {};
  52. }
  53. // https://dom.spec.whatwg.org/#dom-childnode-replacewith
  54. ExceptionOr<void> replace_with(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes)
  55. {
  56. auto* node = static_cast<NodeType*>(this);
  57. // 1. Let parent be this’s parent.
  58. auto* parent = node->parent();
  59. // 2. If parent is null, then return.
  60. if (!parent)
  61. return {};
  62. // 3. Let viableNextSibling be this’s first following sibling not in nodes; otherwise null.
  63. auto viable_next_sibling = viable_nest_sibling_for_insertion(nodes);
  64. // 4. Let node be the result of converting nodes into a node, given nodes and this’s node document.
  65. auto node_to_insert = TRY(convert_nodes_to_single_node(nodes, node->document()));
  66. // 5. If this’s parent is parent, replace this with node within parent.
  67. // Note: This could have been inserted into node.
  68. if (node->parent() == parent) {
  69. (void)TRY(parent->replace_child(node_to_insert, *node));
  70. return {};
  71. }
  72. // 6. Otherwise, pre-insert node into parent before viableNextSibling.
  73. (void)TRY(parent->pre_insert(node_to_insert, viable_next_sibling));
  74. return {};
  75. }
  76. // https://dom.spec.whatwg.org/#dom-childnode-remove
  77. void remove_binding()
  78. {
  79. auto* node = static_cast<NodeType*>(this);
  80. // 1. If this’s parent is null, then return.
  81. if (!node->parent())
  82. return;
  83. // 2. Remove this.
  84. node->remove();
  85. }
  86. protected:
  87. ChildNode() = default;
  88. private:
  89. RefPtr<Node> viable_previous_sibling_for_insertion(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes) const
  90. {
  91. auto* node = static_cast<NodeType const*>(this);
  92. while (auto* previous_sibling = node->previous_sibling()) {
  93. bool contained_in_nodes = false;
  94. for (auto const& node_or_string : nodes) {
  95. if (!node_or_string.template has<NonnullRefPtr<Node>>())
  96. continue;
  97. auto node_in_vector = node_or_string.template get<NonnullRefPtr<Node>>();
  98. if (node_in_vector.ptr() == previous_sibling) {
  99. contained_in_nodes = true;
  100. break;
  101. }
  102. }
  103. if (!contained_in_nodes)
  104. return previous_sibling;
  105. }
  106. return nullptr;
  107. }
  108. RefPtr<Node> viable_nest_sibling_for_insertion(Vector<Variant<NonnullRefPtr<Node>, String>> const& nodes) const
  109. {
  110. auto* node = static_cast<NodeType const*>(this);
  111. while (auto* next_sibling = node->next_sibling()) {
  112. bool contained_in_nodes = false;
  113. for (auto const& node_or_string : nodes) {
  114. if (!node_or_string.template has<NonnullRefPtr<Node>>())
  115. continue;
  116. auto node_in_vector = node_or_string.template get<NonnullRefPtr<Node>>();
  117. if (node_in_vector.ptr() == next_sibling) {
  118. contained_in_nodes = true;
  119. break;
  120. }
  121. }
  122. if (!contained_in_nodes)
  123. return next_sibling;
  124. }
  125. return nullptr;
  126. }
  127. };
  128. }