From 1545d762fc48469fcb67fc34c27a70b8f0917803 Mon Sep 17 00:00:00 2001 From: John Mark Bell Date: Tue, 18 Sep 2007 22:50:55 +0000 Subject: Implement sanity checking in dom_node_insert_before() There's still a couple of outstanding issues here, marked as todos. svn path=/trunk/dom/; revision=3548 --- src/core/node.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 116 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/core/node.c b/src/core/node.c index c33e24d..7dd084f 100644 --- a/src/core/node.c +++ b/src/core/node.c @@ -524,12 +524,13 @@ dom_exception dom_node_get_owner_document(struct dom_node *node, * DOM_NOT_FOUND_ERR if ::ref_child is not a child of * ::node, * DOM_NOT_SUPPORTED_ERR if ::node is of type Document and - * ::new_child is of type - * DocumentType or Element. + * ::new_child is of type DocumentType. * * If ::new_child is a DocumentFragment, all of its children are inserted. * If ::new_child is already in the tree, it is first removed. * + * Attempting to insert a node before itself is a NOP + * * ::new_child's reference count will be increased. The caller should unref * it (as they should already have held a reference on the node) */ @@ -537,32 +538,129 @@ dom_exception dom_node_insert_before(struct dom_node *node, struct dom_node *new_child, struct dom_node *ref_child, struct dom_node **result) { - /** \todo sanity checking etc. */ + /* Ensure that new_child and node are owned by the same document */ + if (new_child->owner != node->owner) + return DOM_WRONG_DOCUMENT_ERR; + + /** \todo ensure ::node may be written to */ + + /* Ensure that ref_child (if any) is a child of node */ + if (ref_child != NULL && ref_child->parent != node) + return DOM_NOT_FOUND_ERR; + + /* We don't support addition of DocumentType nodes using this API */ + /** \todo if we did, then we could purge dom_document_set_doctype() */ + if (node->type == DOM_DOCUMENT_NODE && + new_child->type == DOM_DOCUMENT_TYPE_NODE) + return DOM_NOT_SUPPORTED_ERR; + + /* Ensure that new_child is not an ancestor of node, nor node itself */ + for (struct dom_node *n = node; n != NULL; n = n->parent) { + if (n == new_child) + return DOM_HIERARCHY_REQUEST_ERR; + } - new_child->parent = node; + /* Ensure that the document doesn't already have a root element */ + if (node->type == DOM_DOCUMENT_NODE && node->type == DOM_ELEMENT_NODE) { + for (struct dom_node *n = node->first_child; + n != NULL; n = n->next) { + if (n->type == DOM_ELEMENT_NODE) + return DOM_HIERARCHY_REQUEST_ERR; + } + } - if (ref_child == NULL) { - new_child->previous = node->last_child; - new_child->next = NULL; + /** \todo ensure ::new_child is permitted as a child of ::node */ + + /* Attempting to insert a node before itself is a NOP */ + if (new_child == ref_child) { + dom_node_ref(new_child); + *result = new_child; + + return DOM_NO_ERR; + } - if (node->last_child != NULL) - node->last_child->next = new_child; + /* If new_child is already in the tree, remove it */ + if (new_child->parent != NULL) { + if (new_child->previous != NULL) + new_child->previous->next = new_child->next; else - node->first_child = new_child; + new_child->parent->first_child = new_child->next; - node->last_child = new_child; + if (new_child->next != NULL) + new_child->next->previous = new_child->previous; + else + new_child->parent->last_child = new_child->previous; + } + + /* If new_child is a DocumentFragment, insert its children + * Otherwise, insert new_child */ + if (new_child->type == DOM_DOCUMENT_FRAGMENT_NODE) { + if (new_child->first_child != NULL) { + /* Reparent children */ + for (struct dom_node *c = new_child->first_child; + c != NULL; c = c->next) + c->parent = node; + + if (ref_child == NULL) { + new_child->first_child->previous = + node->last_child; + + if (node->last_child != NULL) { + node->last_child->next = + new_child->first_child; + } else { + node->first_child = + new_child->first_child; + } + + node->last_child = new_child->last_child; + } else { + new_child->first_child->previous = + ref_child->previous; + + if (ref_child->previous != NULL) { + ref_child->previous->next = + new_child->first_child; + } else { + node->first_child = + new_child->first_child; + } + + new_child->last_child->next = ref_child; + ref_child->previous = new_child->last_child; + } + + new_child->first_child = NULL; + new_child->last_child = NULL; + } } else { - new_child->previous = ref_child->previous; - new_child->next = ref_child; + new_child->parent = node; - if (ref_child->previous != NULL) - ref_child->previous->next = new_child; - else - node->first_child = new_child; + if (ref_child == NULL) { + new_child->previous = node->last_child; + new_child->next = NULL; - ref_child->previous = new_child; + if (node->last_child != NULL) + node->last_child->next = new_child; + else + node->first_child = new_child; + + node->last_child = new_child; + } else { + new_child->previous = ref_child->previous; + new_child->next = ref_child; + + if (ref_child->previous != NULL) + ref_child->previous->next = new_child; + else + node->first_child = new_child; + + ref_child->previous = new_child; + } } + /** \todo Is it correct to return DocumentFragments? */ + dom_node_ref(new_child); *result = new_child; -- cgit v1.2.3