Sfoglia il codice sorgente

LibWeb: Add preceding and following Node cases in tree constraints

This also does some east-const changes in TreeNode.
Luke Wilde 3 anni fa
parent
commit
1a28fc3cb5

+ 6 - 6
Userland/Libraries/LibWeb/DOM/Node.cpp

@@ -242,14 +242,14 @@ ExceptionOr<void> Node::ensure_pre_insertion_validity(NonnullRefPtr<Node> node,
         if (is<DocumentFragment>(*node)) {
             auto node_element_child_count = verify_cast<DocumentFragment>(*node).child_element_count();
             if ((node_element_child_count > 1 || node->has_child_of_type<Text>())
-                || (node_element_child_count == 1 && (has_child_of_type<Element>() || is<DocumentType>(child.ptr()) /* FIXME: or child is non-null and a doctype is following child. */))) {
+                || (node_element_child_count == 1 && (has_child_of_type<Element>() || is<DocumentType>(child.ptr()) || (child && child->has_following_node_of_type_in_tree_order<DocumentType>())))) {
                 return DOM::HierarchyRequestError::create("Invalid node type for insertion");
             }
         } else if (is<Element>(*node)) {
-            if (has_child_of_type<Element>() || is<DocumentType>(child.ptr()) /* FIXME: or child is non-null and a doctype is following child. */)
+            if (has_child_of_type<Element>() || is<DocumentType>(child.ptr()) || (child && child->has_following_node_of_type_in_tree_order<DocumentType>()))
                 return DOM::HierarchyRequestError::create("Invalid node type for insertion");
         } else if (is<DocumentType>(*node)) {
-            if (has_child_of_type<DocumentType>() /* FIXME: or child is non-null and an element is preceding child */ || (!child && has_child_of_type<Element>()))
+            if (has_child_of_type<DocumentType>() || (child && child->has_preceding_node_of_type_in_tree_order<Element>()) || (!child && has_child_of_type<Element>()))
                 return DOM::HierarchyRequestError::create("Invalid node type for insertion");
         }
     }
@@ -424,14 +424,14 @@ ExceptionOr<NonnullRefPtr<Node>> Node::replace_child(NonnullRefPtr<Node> node, N
         if (is<DocumentFragment>(*node)) {
             auto node_element_child_count = verify_cast<DocumentFragment>(*node).child_element_count();
             if ((node_element_child_count > 1 || node->has_child_of_type<Text>())
-                || (node_element_child_count == 1 && (first_child_of_type<Element>() != child /* FIXME: or a doctype is following child. */))) {
+                || (node_element_child_count == 1 && (first_child_of_type<Element>() != child || child->has_following_node_of_type_in_tree_order<DocumentType>()))) {
                 return DOM::HierarchyRequestError::create("Invalid node type for insertion");
             }
         } else if (is<Element>(*node)) {
-            if (first_child_of_type<Element>() != child /* FIXME: or a doctype is following child. */)
+            if (first_child_of_type<Element>() != child || child->has_following_node_of_type_in_tree_order<DocumentType>())
                 return DOM::HierarchyRequestError::create("Invalid node type for insertion");
         } else if (is<DocumentType>(*node)) {
-            if (first_child_of_type<DocumentType>() != node /* FIXME: or an element is preceding child */)
+            if (first_child_of_type<DocumentType>() != node || child->has_preceding_node_of_type_in_tree_order<Element>())
                 return DOM::HierarchyRequestError::create("Invalid node type for insertion");
         }
     }

+ 41 - 2
Userland/Libraries/LibWeb/TreeNode.h

@@ -137,12 +137,29 @@ public:
         return node;
     }
 
-    const T* next_in_pre_order() const
+    T const* next_in_pre_order() const
     {
         return const_cast<TreeNode*>(this)->next_in_pre_order();
     }
 
-    bool is_before(const T& other) const
+    T* previous_in_pre_order()
+    {
+        if (auto* node = previous_sibling()) {
+            while (node->last_child())
+                node = node->last_child();
+
+            return node;
+        }
+
+        return parent();
+    }
+
+    T const* previous_in_pre_order() const
+    {
+        return const_cast<TreeNode*>(this)->previous_in_pre_order();
+    }
+
+    bool is_before(T const& other) const
     {
         if (this == &other)
             return false;
@@ -153,6 +170,28 @@ public:
         return false;
     }
 
+    // https://dom.spec.whatwg.org/#concept-tree-preceding (Object A is 'typename U' and Object B is 'this')
+    template<typename U>
+    bool has_preceding_node_of_type_in_tree_order() const
+    {
+        for (auto* node = previous_in_pre_order(); node; node = node->previous_in_pre_order()) {
+            if (is<U>(node))
+                return true;
+        }
+        return false;
+    }
+
+    // https://dom.spec.whatwg.org/#concept-tree-following (Object A is 'typename U' and Object B is 'this')
+    template<typename U>
+    bool has_following_node_of_type_in_tree_order() const
+    {
+        for (auto* node = next_in_pre_order(); node; node = node->next_in_pre_order()) {
+            if (is<U>(node))
+                return true;
+        }
+        return false;
+    }
+
     template<typename Callback>
     IterationDecision for_each_in_inclusive_subtree(Callback callback) const
     {