summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Mark Bell <jmb@netsurf-browser.org>2007-09-22 01:28:54 +0000
committerJohn Mark Bell <jmb@netsurf-browser.org>2007-09-22 01:28:54 +0000
commit37e763350d7fb9bb185f3a9db36159b1c8bc1302 (patch)
treee9d4a97d2e7a4142abc68c10025df68ff2404aef
parent74edf9308e38c0c7e61d10cc7e08555513281102 (diff)
downloadlibdom-37e763350d7fb9bb185f3a9db36159b1c8bc1302.tar.gz
libdom-37e763350d7fb9bb185f3a9db36159b1c8bc1302.tar.bz2
Implement dom_node_replace_child()
svn path=/trunk/dom/; revision=3557
-rw-r--r--src/core/node.c88
1 files changed, 83 insertions, 5 deletions
diff --git a/src/core/node.c b/src/core/node.c
index d570a72..c6bed0d 100644
--- a/src/core/node.c
+++ b/src/core/node.c
@@ -39,6 +39,8 @@ static inline void _dom_node_attach_range(struct dom_node *first,
struct dom_node *next);
static inline void _dom_node_detach_range(struct dom_node *first,
struct dom_node *last);
+static inline void _dom_node_replace(struct dom_node *old,
+ struct dom_node *replacement);
/**
* Destroy a DOM node
@@ -716,12 +718,59 @@ dom_exception dom_node_replace_child(struct dom_node *node,
struct dom_node *new_child, struct dom_node *old_child,
struct dom_node **result)
{
- UNUSED(node);
- UNUSED(new_child);
- UNUSED(old_child);
- UNUSED(result);
+ /* We don't support replacement of DocumentType or root Elements */
+ if (node->type == DOM_DOCUMENT_NODE &&
+ (new_child->type == DOM_DOCUMENT_TYPE_NODE ||
+ new_child->type == DOM_ELEMENT_NODE))
+ return DOM_NOT_SUPPORTED_ERR;
- return DOM_NOT_SUPPORTED_ERR;
+ /* Ensure that new_child and node are owned by the same document */
+ if (new_child->owner != node->owner)
+ return DOM_WRONG_DOCUMENT_ERR;
+
+ /* Ensure node isn't read only */
+ if (_dom_node_readonly(node))
+ return DOM_NO_MODIFICATION_ALLOWED_ERR;
+
+ /* Ensure that old_child is a child of node */
+ if (old_child->parent != node)
+ return DOM_NOT_FOUND_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;
+ }
+
+ /* Ensure that new_child is permitted as a child of node */
+ if (!_dom_node_permitted_child(node, new_child))
+ return DOM_HIERARCHY_REQUEST_ERR;
+
+ /* Attempting to replace a node with itself is a NOP */
+ if (new_child == old_child) {
+ dom_node_ref(old_child);
+ *result = old_child;
+
+ return DOM_NO_ERR;
+ }
+
+ /* If new_child is already in the tree and
+ * its parent isn't read only, remove it */
+ if (new_child->parent != NULL) {
+ if (_dom_node_readonly(new_child->parent))
+ return DOM_NO_MODIFICATION_ALLOWED_ERR;
+
+ _dom_node_detach(new_child);
+ }
+
+ /* Perform the replacement */
+ _dom_node_replace(old_child, new_child);
+
+ /* Sort out the return value */
+ dom_node_ref(old_child);
+ *result = old_child;
+
+ return DOM_NO_ERR;
}
/**
@@ -1507,3 +1556,32 @@ inline void _dom_node_detach_range(struct dom_node *first,
n->parent = NULL;
}
+/**
+ * Replace a node in the tree
+ *
+ * \param old Node to replace
+ * \param replacement Replacement node
+ *
+ * This is not implemented in terms of attach/detach in case
+ * we want to perform any special replacement-related behaviour
+ * at a later date.
+ */
+inline void _dom_node_replace(struct dom_node *old,
+ struct dom_node *replacement)
+{
+ replacement->previous = old->previous;
+ replacement->next = old->next;
+
+ if (old->previous != NULL)
+ old->previous->next = replacement;
+ else
+ old->parent->first_child = replacement;
+
+ if (old->next != NULL)
+ old->next->previous = replacement;
+ else
+ old->parent->last_child = replacement;
+
+ replacement->parent = old->parent;
+}
+