diff options
author | Bo Yang <struggleyb.nku@gmail.com> | 2009-08-11 11:17:23 +0000 |
---|---|---|
committer | Bo Yang <struggleyb.nku@gmail.com> | 2009-08-11 11:17:23 +0000 |
commit | 399da01ae4eb5c5e3e9349bacc2063c946c3d4a1 (patch) | |
tree | 433c8bcde94fc7a6e6f2e5cbf23842a84db98146 /src/core/node.c | |
parent | eec057c7437e19b59ca1e698ce548cb56ce37240 (diff) | |
download | libdom-399da01ae4eb5c5e3e9349bacc2063c946c3d4a1.tar.gz libdom-399da01ae4eb5c5e3e9349bacc2063c946c3d4a1.tar.bz2 |
Merge the branches/struggleyb/libdom-remain back to trunk.
svn path=/trunk/dom/; revision=9191
Diffstat (limited to 'src/core/node.c')
-rw-r--r-- | src/core/node.c | 1147 |
1 files changed, 934 insertions, 213 deletions
diff --git a/src/core/node.c b/src/core/node.c index 518d88c..0645769 100644 --- a/src/core/node.c +++ b/src/core/node.c @@ -3,6 +3,7 @@ * Licensed under the MIT License, * http://www.opensource.org/licenses/mit-license.php * Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org> + * Copyright 2009 Bo Yang <struggleyb.nku@gmail.com> */ #include <assert.h> @@ -10,9 +11,15 @@ #include <stdio.h> #include <dom/core/attr.h> +#include <dom/core/text.h> #include <dom/core/document.h> -#include <dom/core/string.h> +#include <dom/core/namednodemap.h> +#include <dom/core/nodelist.h> +#include <dom/core/implementation.h> +#include <dom/core/document_type.h> +#include "core/string.h" +#include "core/namednodemap.h" #include "core/attr.h" #include "core/cdatasection.h" #include "core/comment.h" @@ -25,6 +32,7 @@ #include "core/pi.h" #include "core/text.h" #include "utils/utils.h" +#include "utils/resource_mgr.h" static bool _dom_node_permitted_child(const dom_node_internal *parent, const dom_node_internal *child); @@ -48,21 +56,27 @@ static struct dom_node_vtable node_vtable = { DOM_NODE_VTABLE }; -/** - * Create a DOM node and compose the vtable - * - * Return The new constructed DOM node or NULL if fail. - */ -dom_node_internal * dom_node_create(struct dom_document *doc) +static struct dom_node_protect_vtable node_protect_vtable = { + DOM_NODE_PROTECT_VTABLE +}; + + + +/*----------------------------------------------------------------------*/ + +/* The constructor and destructor of this object */ + +/* Create a DOM node and compose the vtable */ +dom_node_internal * _dom_node_create(struct dom_document *doc) { - dom_node_internal *node = dom_document_alloc(doc, NULL, + dom_node_internal *node = _dom_document_alloc(doc, NULL, sizeof(struct dom_node_internal)); if (node == NULL) return NULL; node->base.vtable = &node_vtable; - node->destroy = _dom_node_destroy; + node->vtable = &node_protect_vtable; return node; } @@ -86,9 +100,6 @@ void _dom_node_destroy(struct dom_node_internal *node) bool null_owner_permitted = (node->type == DOM_DOCUMENT_NODE || node->type == DOM_DOCUMENT_TYPE_NODE); - /* This function simply acts as a central despatcher - * for type-specific destructors. */ - assert(null_owner_permitted || owner != NULL); if (!null_owner_permitted) { @@ -98,56 +109,19 @@ void _dom_node_destroy(struct dom_node_internal *node) dom_node_ref(owner); } -/* This type dependent switch is not necessary from now. - But I still keep them for a while untill all the functions works well. - switch (node->type) { - case DOM_ELEMENT_NODE: - dom_element_destroy(owner, (struct dom_element *) node); - break; - case DOM_ATTRIBUTE_NODE: - dom_attr_destroy(owner, (struct dom_attr *) node); - break; - case DOM_TEXT_NODE: - dom_text_destroy(owner, (struct dom_text *) node); - break; - case DOM_CDATA_SECTION_NODE: - dom_cdata_section_destroy(owner, - (struct dom_cdata_section *) node); - break; - case DOM_ENTITY_REFERENCE_NODE: - dom_entity_reference_destroy(owner, - (struct dom_entity_reference *) node); - break; - case DOM_ENTITY_NODE: - break; - case DOM_PROCESSING_INSTRUCTION_NODE: - dom_processing_instruction_destroy(owner, - (struct dom_processing_instruction *) node); - break; - case DOM_COMMENT_NODE: - dom_comment_destroy(owner, (struct dom_comment *) node); - break; - case DOM_DOCUMENT_NODE: - dom_document_destroy((struct dom_document *) node); - break; - case DOM_DOCUMENT_TYPE_NODE: - dom_document_type_destroy((struct dom_document_type *) node); - break; - case DOM_DOCUMENT_FRAGMENT_NODE: - dom_document_fragment_destroy(owner, - (struct dom_document_fragment *) node); - break; - case DOM_NOTATION_NODE: - break; - } -*/ + /* Finalise this node, this should also destroy all the child nodes. */ + _dom_node_finalise(owner, node); + if (!null_owner_permitted) { - /* Release the reference we claimed on the document. If this - * is the last reference held on the document and the list - * of nodes pending deletion is empty, then the document will + /* Release the reference we claimed on the document. If this + * is the last reference held on the document and the list + * of nodes pending deletion is empty, then the document will * be destroyed. */ dom_node_unref(owner); } + + /* Release our memory */ + _dom_document_alloc(owner, node, 0); } /** @@ -165,13 +139,63 @@ void _dom_node_destroy(struct dom_node_internal *node) * ::name, ::value, ::namespace, and ::prefix will have their reference * counts increased. */ -dom_exception dom_node_initialise(dom_node_internal *node, +dom_exception _dom_node_initialise(dom_node_internal *node, struct dom_document *doc, dom_node_type type, - struct dom_string *name, struct dom_string *value, - struct dom_string *namespace, struct dom_string *prefix) + struct lwc_string_s *name, struct dom_string *value, + struct lwc_string_s *namespace, struct lwc_string_s *prefix) +{ + lwc_context *ctx; + dom_alloc alloc; + void *pw; + dom_exception err; + + ctx = _dom_document_get_intern_context(doc); + /* The lwc_context for a document never can be NULL */ + assert(ctx != NULL); + + _dom_document_get_allocator(doc, &alloc, &pw); + + err = _dom_node_initialise_generic(node, doc, alloc, pw, ctx, type, + name, value, namespace, prefix); + if (err != DOM_NO_ERR) + return err; + + return DOM_NO_ERR; +} + +/** + * Initialise a DOM node + * + * \param node The node to initialise + * \param doc The document object + * \param alloc The memory allocator + * \param pw The allocator private pointer data + * \param ctx The intern context + * \param type The node type required + * \param name The node (local) name, or NULL + * \param value The node value, or NULL + * \param namespace Namespace URI to use for node, or NULL + * \param prefix Namespace prefix to use for node, or NULL + * \return DOM_NO_ERR on success. + * + * ::name, ::value, ::namespace, and ::prefix will have their reference + * counts increased. + */ +dom_exception _dom_node_initialise_generic( + struct dom_node_internal *node, struct dom_document *doc, + dom_alloc alloc, void *pw, struct lwc_context_s *ctx, + dom_node_type type, struct lwc_string_s *name, + struct dom_string *value, struct lwc_string_s *namespace, + struct lwc_string_s *prefix) { + UNUSED(alloc); + UNUSED(pw); + + assert(ctx != NULL); + node->owner = doc; + if (name != NULL) - dom_string_ref(name); + lwc_context_string_ref(ctx, name); node->name = name; if (value != NULL) @@ -204,15 +228,14 @@ dom_exception dom_node_initialise(dom_node_internal *node, * deletion. This list will not be forcibly emptied, as it contains * those nodes (and their sub-trees) in use by client code. */ - node->owner = doc; if (namespace != NULL) { - dom_string_ref(namespace); + lwc_context_string_ref(ctx, namespace); } node->namespace = namespace; if (prefix != NULL) { - dom_string_ref(prefix); + lwc_context_string_ref(ctx, prefix); } node->prefix = prefix; @@ -220,6 +243,12 @@ dom_exception dom_node_initialise(dom_node_internal *node, node->refcnt = 1; + list_init(&node->pending_list); + if (node->type != DOM_DOCUMENT_NODE) { + /* A Node should be in the pending list when it is created */ + dom_node_mark_pending(node); + } + return DOM_NO_ERR; } @@ -232,33 +261,62 @@ dom_exception dom_node_initialise(dom_node_internal *node, * The contents of ::node will be cleaned up. ::node will not be freed. * All children of ::node should have been removed prior to finalisation. */ -void dom_node_finalise(struct dom_document *doc, dom_node_internal *node) +void _dom_node_finalise(struct dom_document *doc, dom_node_internal *node) +{ + lwc_context *ctx; + dom_alloc alloc; + void *pw; + + ctx = _dom_document_get_intern_context(doc); + /* The lwc_context for a document never can be NULL */ + assert(ctx != NULL); + + _dom_document_get_allocator(doc, &alloc, &pw); + + _dom_node_finalise_generic(node, alloc, pw, ctx); +} + +/** + * Finalise a DOM node + * + * \param node The node to finalise + * \param alloc The allocator + * \param pw The allocator private data + * \param ctx The intern string context + */ +void _dom_node_finalise_generic(dom_node_internal *node, dom_alloc alloc, + void *pw, struct lwc_context_s *ctx) { struct dom_user_data *u, *v; - /* Standalone DocumentType nodes may not have user data attached */ - assert(node->type != DOM_DOCUMENT_TYPE_NODE || - node->user_data == NULL); + UNUSED(alloc); + UNUSED(pw); + + assert(ctx != NULL); /* Destroy user data */ for (u = node->user_data; u != NULL; u = v) { v = u->next; - dom_string_unref(u->key); - - dom_document_alloc(doc, u, 0); + alloc(u, 0, pw); } + node->user_data = NULL; if (node->prefix != NULL) - dom_string_unref(node->prefix); + lwc_context_string_unref(ctx, node->prefix); if (node->namespace != NULL) - dom_string_unref(node->namespace); - - /** \todo check if this node is in list of nodes pending deletion. - * If so, it must be removed from the list, so the document gets - * destroyed once the list is empty (and no longer referenced) */ - node->owner = NULL; + lwc_context_string_unref(ctx, node->namespace); + + /* Destroy all the child nodes of this node */ + struct dom_node_internal *p = node->first_child; + struct dom_node_internal *n = NULL; + while (p != NULL) { + n = p->next; + p->parent = NULL; + dom_node_try_destroy(p); + p = n; + } /* Paranoia */ node->next = NULL; @@ -271,7 +329,19 @@ void dom_node_finalise(struct dom_document *doc, dom_node_internal *node) dom_string_unref(node->value); if (node->name != NULL) - dom_string_unref(node->name); + lwc_context_string_unref(ctx, node->name); + + /* Detach from the pending list, if we are in it */ + if (node->pending_list.prev != &node->pending_list) { + assert (node->pending_list.next != &node->pending_list); + list_del(&node->pending_list); + if (node->owner != NULL && node->type != DOM_DOCUMENT_NODE) { + /* Deleting this node from the pending list may cause + * the list to be null and we should try to destroy + * the document. */ + _dom_document_try_destroy(node->owner); + } + } } /** @@ -284,6 +354,11 @@ void _dom_node_ref(dom_node_internal *node) node->refcnt++; } + +/* ---------------------------------------------------------------------*/ + +/* The public virtual function of this interface Node */ + /** * Release a reference on a DOM node * @@ -291,15 +366,20 @@ void _dom_node_ref(dom_node_internal *node) * * If the reference count reaches zero and the node is not part of any * document, any memory claimed by the node will be released. + * + * If the parent of the node is NULL but the reference count does not reach + * zero, this means we should put this node to the document's deletion pending + * list. When the refcnt reach zero, we delete it. */ void _dom_node_unref(dom_node_internal *node) { + if (node == NULL) + return; + if (node->refcnt > 0) node->refcnt--; - if (node->refcnt == 0 && node->parent == NULL) { - dom_node_destroy(node); - } + dom_node_try_destroy(node); } /** @@ -316,50 +396,85 @@ void _dom_node_unref(dom_node_internal *node) dom_exception _dom_node_get_node_name(dom_node_internal *node, struct dom_string **result) { - struct dom_string *node_name; + struct dom_string *node_name, *temp; + dom_document *doc; + dom_exception err; + struct dom_resource_mgr rm; + + doc = node->owner; + /* Document Node and DocumentType Node can have no owner */ + assert(node->type == DOM_DOCUMENT_TYPE_NODE || + node->type == DOM_DOCUMENT_NODE || + doc != NULL); assert(node->name != NULL); + if (doc != NULL) { + _dom_document_get_resource_mgr(doc, &rm); + } else if (node->type == DOM_DOCUMENT_TYPE_NODE) { + _dom_document_type_get_resource_mgr( + (dom_document_type *) node, &rm); + } + /* If this node was created using a namespace-aware method and * has a defined prefix, then nodeName is a QName comprised * of prefix:name. */ - if ((node->type == DOM_ELEMENT_NODE || - node->type == DOM_ATTRIBUTE_NODE) && - node->prefix != NULL) { + if(node->prefix != NULL) { struct dom_string *colon; - dom_exception err; - err = dom_document_create_string(node->owner, + err = _dom_resource_mgr_create_string(&rm, (const uint8_t *) ":", SLEN(":"), &colon); if (err != DOM_NO_ERR) { return err; } + /* Make a temp prefix dom_string */ + err = _dom_resource_mgr_create_string_from_lwcstring(&rm, + node->prefix, &temp); + if (err != DOM_NO_ERR) { + dom_string_unref(colon); + return err; + } + /* Prefix + : */ - err = dom_string_concat(node->prefix, colon, &node_name); + err = dom_string_concat(temp, colon, &node_name); if (err != DOM_NO_ERR) { + dom_string_unref(temp); dom_string_unref(colon); return err; } + /*Finished with temp*/ + dom_string_unref(temp); /* Finished with colon */ dom_string_unref(colon); + /* Make a temp name dom_string */ + err = _dom_resource_mgr_create_string_from_lwcstring(&rm, + node->name, &temp); + if (err != DOM_NO_ERR) { + return err; + } /* Prefix + : + Localname */ - err = dom_string_concat(node_name, node->name, &colon); + err = dom_string_concat(node_name, temp, &colon); if (err != DOM_NO_ERR) { + dom_string_unref(temp); dom_string_unref(node_name); return err; } + /* Finished with temp */ + dom_string_unref(temp); /* Finished with intermediate node name */ dom_string_unref(node_name); node_name = colon; } else { - dom_string_ref(node->name); - - node_name = node->name; + err = _dom_resource_mgr_create_string_from_lwcstring(&rm, + node->name, &node_name); + if (err != DOM_NO_ERR) { + return err; + } } *result = node_name; @@ -384,10 +499,6 @@ dom_exception _dom_node_get_node_name(dom_node_internal *node, dom_exception _dom_node_get_node_value(dom_node_internal *node, struct dom_string **result) { - if (node->type == DOM_ATTRIBUTE_NODE) { - return dom_attr_get_value((struct dom_attr *) node, result); - } - if (node->value != NULL) dom_string_ref(node->value); @@ -412,6 +523,9 @@ dom_exception _dom_node_get_node_value(dom_node_internal *node, dom_exception _dom_node_set_node_value(dom_node_internal *node, struct dom_string *value) { + /* TODO + * Whether we should change this to a virtual function? + */ /* This is a NOP if the value is defined to be null. */ if (node->type == DOM_DOCUMENT_NODE || node->type == DOM_DOCUMENT_FRAGMENT_NODE || @@ -507,8 +621,8 @@ dom_exception _dom_node_get_child_nodes(dom_node_internal *node, if (node->owner == NULL) return DOM_NOT_SUPPORTED_ERR; - return dom_document_get_nodelist(node->owner, node, - NULL, NULL, NULL, result); + return _dom_document_get_nodelist(node->owner, DOM_NODELIST_CHILDREN, + node, NULL, NULL, NULL, result); } /** @@ -630,13 +744,10 @@ dom_exception _dom_node_get_next_sibling(dom_node_internal *node, dom_exception _dom_node_get_attributes(dom_node_internal *node, struct dom_namednodemap **result) { - if (node->type != DOM_ELEMENT_NODE) { - *result = NULL; - - return DOM_NO_ERR; - } + UNUSED(node); + *result = NULL; - return dom_element_get_attributes((struct dom_element *) node, result); + return DOM_NO_ERR; } /** @@ -654,8 +765,7 @@ dom_exception _dom_node_get_owner_document(dom_node_internal *node, struct dom_document **result) { /* Document nodes have no owner, as far as clients are concerned - * In reality, they own themselves as this simplifies code elsewhere - */ + * In reality, they own themselves as this simplifies code elsewhere */ if (node->type == DOM_DOCUMENT_NODE) { *result = NULL; @@ -664,7 +774,7 @@ dom_exception _dom_node_get_owner_document(dom_node_internal *node, /* If there is an owner, increase its reference count */ if (node->owner != NULL) - dom_node_ref((dom_node_internal *) node->owner); + dom_node_ref(node->owner); *result = node->owner; @@ -729,19 +839,10 @@ dom_exception _dom_node_insert_before(dom_node_internal *node, } /* Ensure that new_child is permitted as a child of node */ - if (!_dom_node_permitted_child(node, new_child)) + if (new_child->type != DOM_DOCUMENT_FRAGMENT_NODE && + !_dom_node_permitted_child(node, new_child)) return DOM_HIERARCHY_REQUEST_ERR; - /* DocumentType nodes are created outside the Document so, - * if we're trying to attach a DocumentType node, then we - * also need to set its owner. */ - if (node->type == DOM_DOCUMENT_NODE && - new_child->type == DOM_DOCUMENT_TYPE_NODE) { - /* See long comment in dom_node_initialise as to why - * we don't ref the document here */ - new_child->owner = (struct dom_document *) node; - } - /* Attempting to insert a node before itself is a NOP */ if (new_child == ref_child) { dom_node_ref(new_child); @@ -759,9 +860,19 @@ dom_exception _dom_node_insert_before(dom_node_internal *node, _dom_node_detach(new_child); } - /* If new_child is a DocumentFragment, insert its children + /* When a Node is attached, it should be removed from the pending + * list */ + dom_node_remove_pending(new_child); + + /* If new_child is a DocumentFragment, insert its children. * Otherwise, insert new_child */ if (new_child->type == DOM_DOCUMENT_FRAGMENT_NODE) { + /* Test the children of the docment fragment can be appended */ + dom_node_internal *c = new_child->first_child; + for (; c != NULL; c = c->next) + if (!_dom_node_permitted_child(node, c)) + return DOM_HIERARCHY_REQUEST_ERR; + if (new_child->first_child != NULL) { _dom_node_attach_range(new_child->first_child, new_child->last_child, @@ -783,6 +894,16 @@ dom_exception _dom_node_insert_before(dom_node_internal *node, : ref_child); } + /* DocumentType nodes are created outside the Document so, + * if we're trying to attach a DocumentType node, then we + * also need to set its owner. */ + if (node->type == DOM_DOCUMENT_NODE && + new_child->type == DOM_DOCUMENT_TYPE_NODE) { + /* See long comment in _dom_node_initialise as to why + * we don't ref the document here */ + new_child->owner = (struct dom_document *) node; + } + /** \todo Is it correct to return DocumentFragments? */ dom_node_ref(new_child); @@ -853,8 +974,21 @@ dom_exception _dom_node_replace_child(dom_node_internal *node, } /* Ensure that new_child is permitted as a child of node */ - if (!_dom_node_permitted_child(node, new_child)) - return DOM_HIERARCHY_REQUEST_ERR; + if (new_child->type == DOM_DOCUMENT_FRAGMENT_NODE) { + /* If this node is a doc fragment, we should test all its + * children nodes */ + dom_node_internal *c; + c = new_child->first_child; + while (c != NULL) { + if (!_dom_node_permitted_child(node, c)) + return DOM_HIERARCHY_REQUEST_ERR; + + c = c->next; + } + } else { + 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) { @@ -878,6 +1012,8 @@ dom_exception _dom_node_replace_child(dom_node_internal *node, /* Sort out the return value */ dom_node_ref(old_child); + /* The replaced node should be marded pending */ + dom_node_mark_pending(old_child); *result = old_child; return DOM_NO_ERR; @@ -922,8 +1058,12 @@ dom_exception _dom_node_remove_child(dom_node_internal *node, /* Detach the node */ _dom_node_detach(old_child); - /* Sort out the return value */ + /* When a Node is removed, it should be destroy. When its refcnt is not + * zero, it will be added to the document's deletion pending list. + * When a Node is removed, its parent should be NULL, but its owner + * should remain to be the document. */ dom_node_ref(old_child); + dom_node_try_destroy(old_child); *result = old_child; return DOM_NO_ERR; @@ -1015,15 +1155,67 @@ dom_exception _dom_node_has_child_nodes(dom_node_internal *node, bool *result) * * \todo work out what happens when cloning Document, DocumentType, Entity * and Notation nodes. + * + * Note: we adopt a OO paradigm, this clone_node just provide a basic operation + * of clone. Special clones like Attr/EntitiReference stated above should + * provide their overload of this interface in their implementation file. */ dom_exception _dom_node_clone_node(dom_node_internal *node, bool deep, dom_node_internal **result) { - UNUSED(node); - UNUSED(deep); - UNUSED(result); + dom_node_internal *n, *child, *r; + dom_exception err; + dom_document *doc; + dom_user_data *ud; + + doc = node->owner; + assert(doc != NULL); + + err = dom_node_alloc(doc, node, &n); + if (err != DOM_NO_ERR) + return err; + + err = dom_node_copy(n, node); + if (err != DOM_NO_ERR) { + _dom_document_alloc(doc, n, 0); + return err; + } - return DOM_NOT_SUPPORTED_ERR; + if (deep) { + child = node->first_child; + while (child != NULL) { + err = dom_node_clone_node(child, deep, (void *) &r); + if (err != DOM_NO_ERR) { + dom_node_unref(n); + return err; + } + + err = dom_node_append_child(n, r, (void *) &r); + if (err != DOM_NO_ERR) { + dom_node_unref(n); + return err; + } + + /* Clean up the new node, we have reference it two + * times */ + dom_node_unref(r); + dom_node_unref(r); + child = child->next; + } + } + + *result = n; + + /* Call the dom_user_data_handlers */ + ud = node->user_data; + while (ud != NULL) { + if (ud->handler != NULL) + ud->handler(DOM_NODE_CLONED, ud->key, ud->data, + (dom_node *) node, (dom_node *) n); + ud = ud->next; + } + + return DOM_NO_ERR; } /** @@ -1038,9 +1230,36 @@ dom_exception _dom_node_clone_node(dom_node_internal *node, bool deep, */ dom_exception _dom_node_normalize(dom_node_internal *node) { - UNUSED(node); + dom_node_internal *n, *p; + dom_exception err; - return DOM_NOT_SUPPORTED_ERR; + p = node->first_child; + if (p == NULL) + return DOM_NO_ERR; + + n = p->next; + + while (n != NULL) { + if (n->type == DOM_TEXT_NODE && p->type == DOM_TEXT_NODE) { + err = _dom_merge_adjacent_text(p, n); + if (err != DOM_NO_ERR) + return err; + + _dom_node_detach(n); + dom_node_unref(n); + n = p->next; + continue; + } + if (n->type != DOM_TEXT_NODE) { + err = dom_node_normalize(n); + if (err != DOM_NO_ERR) + return err; + } + p = n; + n = n->next; + } + + return DOM_NO_ERR; } /** @@ -1054,15 +1273,22 @@ dom_exception _dom_node_normalize(dom_node_internal *node) * \return DOM_NO_ERR. */ dom_exception _dom_node_is_supported(dom_node_internal *node, - struct dom_string *feature, dom_node_internal *version, + struct dom_string *feature, struct dom_string *version, bool *result) { - UNUSED(node); - UNUSED(feature); - UNUSED(version); - UNUSED(result); + dom_document *doc; + dom_implementation *impl; + bool has; - return DOM_NOT_SUPPORTED_ERR; + doc = node->owner; + assert(doc != NULL); + dom_document_get_implementation(doc, &impl); + assert(impl != NULL); + dom_implementation_has_feature(impl, feature, version, &has); + + *result = has; + + return DOM_NO_ERR; } /** @@ -1079,13 +1305,18 @@ dom_exception _dom_node_is_supported(dom_node_internal *node, dom_exception _dom_node_get_namespace(dom_node_internal *node, struct dom_string **result) { + lwc_context *ctx; + + assert(node->owner != NULL); + ctx = _dom_document_get_intern_context(node->owner); + assert(ctx != NULL); + /* If there is a namespace, increase its reference count */ if (node->namespace != NULL) - dom_string_ref(node->namespace); + lwc_context_string_ref(ctx, node->namespace); - *result = node->namespace; - - return DOM_NO_ERR; + return _dom_document_create_string_from_lwcstring(node->owner, + node->namespace, result); } /** @@ -1102,13 +1333,19 @@ dom_exception _dom_node_get_namespace(dom_node_internal *node, dom_exception _dom_node_get_prefix(dom_node_internal *node, struct dom_string **result) { + lwc_context *ctx; + + assert(node->owner != NULL); + ctx = _dom_document_get_intern_context(node->owner); + assert(ctx != NULL); + /* If there is a prefix, increase its reference count */ if (node->prefix != NULL) - dom_string_ref(node->prefix); + lwc_context_string_ref(ctx, node->prefix); - *result = node->prefix; - - return DOM_NO_ERR; + return _dom_document_create_string_from_lwcstring(node->owner, + node->prefix, + result); } /** @@ -1137,6 +1374,13 @@ dom_exception _dom_node_get_prefix(dom_node_internal *node, dom_exception _dom_node_set_prefix(dom_node_internal *node, struct dom_string *prefix) { + dom_exception err; + lwc_string *str; + lwc_context *docctx; + + docctx = _dom_document_get_intern_context(node->owner); + assert(docctx != NULL); + /* Only Element and Attribute nodes created using * namespace-aware methods may have a prefix */ if ((node->type != DOM_ELEMENT_NODE && @@ -1154,7 +1398,7 @@ dom_exception _dom_node_set_prefix(dom_node_internal *node, /* No longer want existing prefix */ if (node->prefix != NULL) { - dom_string_unref(node->prefix); + lwc_context_string_unref(docctx, node->prefix); } /* Set the prefix */ @@ -1163,8 +1407,11 @@ dom_exception _dom_node_set_prefix(dom_node_internal *node, if (dom_string_length(prefix) == 0) { node->prefix = NULL; } else { - dom_string_ref(prefix); - node->prefix = prefix; + err = _dom_node_get_intern_string(node, prefix, &str); + if (err != DOM_NO_ERR) + return err; + + node->prefix = str; } } else { node->prefix = NULL; @@ -1186,7 +1433,13 @@ dom_exception _dom_node_set_prefix(dom_node_internal *node, */ dom_exception _dom_node_get_local_name(dom_node_internal *node, struct dom_string **result) -{ +{ + lwc_context *ctx; + + assert(node->owner != NULL); + ctx = _dom_document_get_intern_context(node->owner); + assert(ctx != NULL); + /* Only Element and Attribute nodes may have a local name */ if (node->type != DOM_ELEMENT_NODE && node->type != DOM_ATTRIBUTE_NODE) { @@ -1194,19 +1447,13 @@ dom_exception _dom_node_get_local_name(dom_node_internal *node, return DOM_NO_ERR; } - /* Node must have been created using a namespace-aware method */ - if (node->namespace == NULL) { - *result = NULL; - return DOM_NO_ERR; - } - /* The node may have a local name, reference it if so */ if (node->name != NULL) { - dom_string_ref(node->name); + lwc_context_string_ref(ctx, node->name); } - *result = node->name; - return DOM_NO_ERR; + return _dom_document_create_string_from_lwcstring(node->owner, + node->name, result); } /** @@ -1218,13 +1465,10 @@ dom_exception _dom_node_get_local_name(dom_node_internal *node, */ dom_exception _dom_node_has_attributes(dom_node_internal *node, bool *result) { - if (node->type != DOM_ELEMENT_NODE) { - *result = false; - - return DOM_NO_ERR; - } + UNUSED(node); + *result = false; - return dom_element_has_attributes((struct dom_element *) node, result); + return DOM_NO_ERR; } /** @@ -1237,6 +1481,9 @@ dom_exception _dom_node_has_attributes(dom_node_internal *node, bool *result) * The returned string will have its reference count increased. It is * the responsibility of the caller to unref the string once it has * finished with it. + * + * We don't support this API now, so this function call should always + * return DOM_NOT_SUPPORTED_ERR. */ dom_exception _dom_node_get_base(dom_node_internal *node, struct dom_string **result) @@ -1258,6 +1505,9 @@ dom_exception _dom_node_get_base(dom_node_internal *node, * implementations. * * The result is a bitfield of dom_document_position values. + * + * We don't support this API now, so this function call should always + * return DOM_NOT_SUPPORTED_ERR. */ dom_exception _dom_node_compare_document_position(dom_node_internal *node, dom_node_internal *other, uint16_t *result) @@ -1286,10 +1536,21 @@ dom_exception _dom_node_compare_document_position(dom_node_internal *node, dom_exception _dom_node_get_text_content(dom_node_internal *node, struct dom_string **result) { - UNUSED(node); - UNUSED(result); + dom_node_internal *n; + dom_string *str; + dom_string *ret; - return DOM_NOT_SUPPORTED_ERR; + assert(node->owner != NULL); + + for (n = node->first_child; n != NULL; n = n->next) { + dom_node_get_text_content(n, &ret); + dom_string_concat(str, ret, &str); + } + + dom_string_ref(str); + *result = str; + + return DOM_NO_ERR; } /** @@ -1306,10 +1567,36 @@ dom_exception _dom_node_get_text_content(dom_node_internal *node, dom_exception _dom_node_set_text_content(dom_node_internal *node, struct dom_string *content) { - UNUSED(node); - UNUSED(content); + dom_node_internal *n, *p, *r; + dom_document *doc; + dom_text *text; + dom_exception err; + + n = node->first_child; + + while (n != NULL) { + p = n; + n = n->next; + /* Add the (void *) casting to avoid gcc warning: + * dereferencing type-punned pointer will break + * strict-aliasing rules */ + err = dom_node_remove_child(node, n, (void *) &r); + if (err != DOM_NO_ERR) + return err; + } - return DOM_NOT_SUPPORTED_ERR; + doc = node->owner; + assert(doc != NULL); + + err = dom_document_create_text_node(doc, content, &text); + if (err != DOM_NO_ERR) + return err; + + err = dom_node_append_child(node, text, (void *) &r); + if (err != DOM_NO_ERR) + return err; + + return DOM_NO_ERR; } /** @@ -1322,8 +1609,8 @@ dom_exception _dom_node_set_text_content(dom_node_internal *node, * * This tests if the two nodes reference the same object. */ -dom_exception _dom_node_is_same(dom_node_internal *node, dom_node_internal *other, - bool *result) +dom_exception _dom_node_is_same(dom_node_internal *node, + dom_node_internal *other, bool *result) { *result = (node == other); @@ -1345,11 +1632,12 @@ dom_exception _dom_node_is_same(dom_node_internal *node, dom_node_internal *othe dom_exception _dom_node_lookup_prefix(dom_node_internal *node, struct dom_string *namespace, struct dom_string **result) { - UNUSED(node); - UNUSED(namespace); - UNUSED(result); + if (node->parent != NULL) + return dom_node_lookup_prefix(node, namespace, result); + else + *result = NULL; - return DOM_NOT_SUPPORTED_ERR; + return DOM_NO_ERR; } /** @@ -1363,11 +1651,11 @@ dom_exception _dom_node_lookup_prefix(dom_node_internal *node, dom_exception _dom_node_is_default_namespace(dom_node_internal *node, struct dom_string *namespace, bool *result) { - UNUSED(node); - UNUSED(namespace); - UNUSED(result); - - return DOM_NOT_SUPPORTED_ERR; + if (node->parent != NULL) + return dom_node_is_default_namespace(node, namespace, result); + else + *result = false; + return DOM_NO_ERR; } /** @@ -1385,11 +1673,12 @@ dom_exception _dom_node_is_default_namespace(dom_node_internal *node, dom_exception _dom_node_lookup_namespace(dom_node_internal *node, struct dom_string *prefix, struct dom_string **result) { - UNUSED(node); - UNUSED(prefix); - UNUSED(result); + if (node->parent != NULL) + return dom_node_lookup_namespace(node->parent, prefix, result); + else + *result = NULL; - return DOM_NOT_SUPPORTED_ERR; + return DOM_NO_ERR; } /** @@ -1410,15 +1699,106 @@ dom_exception _dom_node_lookup_namespace(dom_node_internal *node, * + publicId, systemId, internalSubset are equal * + The node entities are equal * + The node notations are equal + * TODO: in document_type, we should override this virtual function */ dom_exception _dom_node_is_equal(dom_node_internal *node, dom_node_internal *other, bool *result) { - UNUSED(node); - UNUSED(other); - UNUSED(result); + dom_exception err; + dom_string *s1, *s2; + lwc_context *c1, *c2; + dom_namednodemap *m1, *m2; + dom_nodelist *l1, *l2; - return DOM_NOT_SUPPORTED_ERR; + if (node->type != other->type){ + *result = false; + return DOM_NO_ERR; + } + + assert(node->owner != NULL); + assert(other->owner != NULL); + + err = dom_node_get_node_name(node, &s1); + if (err != DOM_NO_ERR) + return err; + + err = dom_node_get_node_name(other, &s2); + if (err != DOM_NO_ERR) + return err; + + if (dom_string_cmp(s1, s2) != 0) { + *result = false; + return DOM_NO_ERR; + } + + c1 = _dom_document_get_intern_context(node->owner); + assert(c1 != NULL); + + c2 = _dom_document_get_intern_context(other->owner); + assert(c2 != NULL); + + if (c1 == c2) { + if (node->name != other->name || + node->namespace != other->namespace || + node->prefix != other->prefix) { + *result = false; + return DOM_NO_ERR; + } + } else { + if (_dom_lwc_string_compare_raw(node->name, other->name) != 0){ + *result = false; + return DOM_NO_ERR; + } + + if (_dom_lwc_string_compare_raw(node->namespace, + other->namespace) != 0){ + *result = false; + return DOM_NO_ERR; + } + + if (_dom_lwc_string_compare_raw(node->prefix, + other->prefix) != 0){ + *result = false; + return DOM_NO_ERR; + } + } + + if (dom_string_cmp(node->value, other->value) != 0) { + *result = false; + return DOM_NO_ERR; + } + + // Following comes the attributes + err = dom_node_get_attributes(node, &m1); + if (err != DOM_NO_ERR) + return err; + + err = dom_node_get_attributes(other, &m2); + if (err != DOM_NO_ERR) + return err; + + if (dom_namednodemap_equal(m1, m2) != true) { + *result = false; + return DOM_NO_ERR; + } + + // Finally the childNodes + err = dom_node_get_child_nodes(node, &l1); + if (err != DOM_NO_ERR) + return err; + + err = dom_node_get_child_nodes(other, &l2); + if (err != DOM_NO_ERR) + return err; + + if (dom_nodelist_equal(l1, l2) != true) { + *result = false; + return DOM_NO_ERR; + } + + *result = true; + + return DOM_NO_ERR; } /** @@ -1435,12 +1815,23 @@ dom_exception _dom_node_get_feature(dom_node_internal *node, struct dom_string *feature, struct dom_string *version, void **result) { - UNUSED(node); - UNUSED(feature); - UNUSED(version); - UNUSED(result); + dom_document *doc; + dom_implementation *impl; + bool has; + + doc = node->owner; + assert(doc != NULL); + dom_document_get_implementation(doc, &impl); + assert(impl != NULL); + dom_implementation_has_feature(impl, feature, version, &has); + + if (has) { + *result = node; + } else { + *result = NULL; + } - return DOM_NOT_SUPPORTED_ERR; + return DOM_NO_ERR; } /** @@ -1479,14 +1870,14 @@ dom_exception _dom_node_set_user_data(dom_node_internal *node, *result = ud->data; - dom_document_alloc(node->owner, ud, 0); + _dom_document_alloc(node->owner, ud, 0); return DOM_NO_ERR; } /* Otherwise, create a new user data object if one wasn't found */ if (ud == NULL) { - ud = dom_document_alloc(node->owner, NULL, + ud = _dom_document_alloc(node->owner, NULL, sizeof(struct dom_user_data)); if (ud == NULL) return DOM_NO_MEM_ERR; @@ -1542,9 +1933,124 @@ dom_exception _dom_node_get_user_data(dom_node_internal *node, return DOM_NO_ERR; } -/* */ -/*----------------------------------------------------------------------------*/ -/* */ + +/*--------------------------------------------------------------------------*/ + +/* The protected virtual functions */ + +/* We should never call this pure-virtual function directly */ +dom_exception _dom_node_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret) +{ + UNUSED(doc); + UNUSED(n); + UNUSED(ret); + + return DOM_NOT_SUPPORTED_ERR; +} + + +/* Copy the internal attributes of a Node from old to new */ +dom_exception _dom_node_copy(dom_node_internal *new, dom_node_internal *old) +{ + lwc_context *nctx, *octx; + dom_exception err; + + new->vtable = old->vtable; + new->base.vtable = old->base.vtable; + + assert(old->owner != NULL); + octx = _dom_document_get_intern_context(old->owner); + assert(octx != NULL); + + assert(new->owner != NULL); + nctx = _dom_document_get_intern_context(old->owner); + assert(nctx != NULL); + + new->type = old->type; + new->parent = NULL; + new->first_child = NULL; + new->last_child = NULL; + new->previous = NULL; + new->next = NULL; + new->owner = old->owner; + + if (octx == nctx) { + lwc_context_string_ref(octx, old->name); + new->name = old->name; + + if (old->namespace != NULL) + lwc_context_string_ref(octx, old->namespace); + new->namespace = old->namespace; + + if (old->prefix != NULL) + lwc_context_string_ref(octx, old->prefix); + new->prefix = old->prefix; + } else { + lwc_string *str; + lwc_error lerr; + + lerr = lwc_context_intern(nctx, lwc_string_data(old->name), + lwc_string_length(old->name), &str); + if (lerr != lwc_error_ok) + return _dom_exception_from_lwc_error(lerr); + + new->name = str; + + if (old->namespace != NULL) { + lerr = lwc_context_intern(nctx, + lwc_string_data(old->namespace), + lwc_string_length(old->namespace), + &str); + if (lerr != lwc_error_ok) + return _dom_exception_from_lwc_error(lerr); + + new->namespace = str; + } else + new->namespace = NULL; + + if (old->prefix != NULL) { + lerr = lwc_context_intern(nctx, + lwc_string_data(old->prefix), + lwc_string_length(old->prefix), &str); + if (lerr != lwc_error_ok) + return _dom_exception_from_lwc_error(lerr); + + new->prefix = str; + } else + new->prefix = NULL; + } + + dom_alloc al; + void *pw; + + if (old->value != NULL) { + _dom_document_get_allocator(new->owner, &al, &pw); + dom_string *value; + err = dom_string_clone(al, pw, old->value, &value); + if (err != DOM_NO_ERR) + return err; + + new->value = value; + } else { + new->value = NULL; + } + + new->user_data = NULL; + new->refcnt = 1; + + list_init(&new->pending_list); + /* The new copyed node has no parent, + * so it should be put in the pending list. */ + dom_node_mark_pending(new); + + return DOM_NO_ERR; +} + + +/*--------------------------------------------------------------------------*/ + +/* The helper functions */ /** * Determine if a node is permitted as a child of another node @@ -1626,20 +2132,18 @@ bool _dom_node_permitted_child(const dom_node_internal *parent, */ bool _dom_node_readonly(const dom_node_internal *node) { - /* DocumentType and Notation nodes are read only */ - if (node->type == DOM_DOCUMENT_TYPE_NODE || - node->type == DOM_NOTATION_NODE) - return true; + const dom_node_internal *n = node; - /* Entity nodes and their descendants are read only */ - for (; node != NULL; node = node->parent) { - if (node->type == DOM_ENTITY_NODE) - return true; - } + /* DocumentType and Notation ns are read only */ + if (n->type == DOM_DOCUMENT_TYPE_NODE || + n->type == DOM_NOTATION_NODE) + return true; - /* EntityReference nodes and their descendants are read only */ - for (; node != NULL; node = node->parent) { - if (node->type == DOM_ENTITY_REFERENCE_NODE) + /* Entity ns and their descendants are read only + * EntityReference ns and their descendants are read only */ + for (n = node; n != NULL; n = n->parent) { + if (n->type == DOM_ENTITY_NODE + || n->type == DOM_ENTITY_REFERENCE_NODE) return true; } @@ -1668,6 +2172,10 @@ void _dom_node_attach(dom_node_internal *node, dom_node_internal *parent, */ void _dom_node_detach(dom_node_internal *node) { + /* When a Node is not in the document tree, it must be in the + pending list */ + dom_node_mark_pending(node); + _dom_node_detach_range(node, node); } @@ -1701,8 +2209,9 @@ void _dom_node_attach_range(dom_node_internal *first, else parent->last_child = last; - for (dom_node_internal *n = first; n != last->next; n = n->next) + for (dom_node_internal *n = first; n != last->next; n = n->next) { n->parent = parent; + } } /** @@ -1726,8 +2235,9 @@ void _dom_node_detach_range(dom_node_internal *first, else last->parent->last_child = first->previous; - for (dom_node_internal *n = first; n != last->next; n = n->next) + for (dom_node_internal *n = first; n != last->next; n = n->next) { n->parent = NULL; + } first->previous = NULL; last->next = NULL; @@ -1771,9 +2281,220 @@ void _dom_node_replace(dom_node_internal *old, else old->parent->last_child = last; - for (dom_node_internal *n = first; n != last->next; n = n->next) + for (dom_node_internal *n = first; n != last->next; n = n->next) { n->parent = old->parent; + } old->previous = old->next = old->parent = NULL; } +/** + * Migrate one lwc_string from one context to another, this function + * may be used when we import/adopt a Node between documents. + * + * \param old The source context + * \param new The new context + * \param string The lwc_string to migrate + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _redocument_lwcstring(lwc_context *old, lwc_context *new, + lwc_string **string) +{ + lwc_string *str; + lwc_error lerr; + + lerr = lwc_context_intern(new, lwc_string_data(*string), + lwc_string_length(*string), &str); + if (lerr != lwc_error_ok) + return _dom_exception_from_lwc_error(lerr); + + lwc_context_string_unref(old, *string); + *string = str; + + return DOM_NO_ERR; +} + +/** + * Migrate one dom_string from one document to another, this function + * may be used when we import/adopt a Node between documents. + * + * \param old The source document + * \param new The new document + * \param string The dom_string to migrate + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _redocument_domstring(dom_document *old, dom_document* new, + dom_string **string) +{ + dom_exception err; + dom_string *str; + + UNUSED(old); + err = _dom_document_create_string(new, NULL, 0, &str); + if (err != DOM_NO_ERR) + return err; + + err = dom_string_dup(*string, &str); + if (err != DOM_NO_ERR) + return err; + + dom_string_unref(*string); + *string = str; + + return DOM_NO_ERR; +} + +/** + * Merge two adjacent text nodes into one text node. + * + * \param p The first text node + * \param n The second text node + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _dom_merge_adjacent_text(dom_node_internal *p, + dom_node_internal *n) +{ + assert(p->type = DOM_TEXT_NODE); + assert(n->type = DOM_TEXT_NODE); + + dom_string *str; + dom_exception err; + + err = dom_text_get_whole_text(n, &str); + if (err != DOM_NO_ERR) + return err; + + err = dom_characterdata_append_data(p, str); + if (err != DOM_NO_ERR) + return err; + + dom_string_unref(str); + + return DOM_NO_ERR; +} + +/** + * Intern a dom_string using the node's owner document's lwc_context + * + * \param node The node + * \param str The dom_string to be interned + * \param intern The returned interned string + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _dom_node_get_intern_string(dom_node_internal *node, + dom_string *str, lwc_string **intern) +{ + dom_exception err; + lwc_context *ctx, *docctx; + lwc_string *ret; + + assert(str != NULL); + assert(node->owner != NULL); + + docctx = _dom_document_get_intern_context(node->owner); + assert(docctx != NULL); + + err = dom_string_get_intern(str, &ctx, &ret); + if (err != DOM_NO_ERR) + return err; + + if (ctx != docctx) { + err = _dom_string_intern(str, docctx, &ret); + if (err != DOM_NO_ERR) + return err; + } + + *intern = ret; + + return DOM_NO_ERR; +} + +/** + * Unref a lwc_string used in this node + * + * \param node The node + * \param intern The lwc_string to unref + */ +void _dom_node_unref_intern_string(dom_node_internal *node, + struct lwc_string_s *intern) +{ + struct dom_resource_mgr rm; + struct dom_document *doc = node->owner; + + if (doc != NULL) { + _dom_document_get_resource_mgr(doc, &rm); + } else if (node->type == DOM_DOCUMENT_TYPE_NODE) { + _dom_document_type_get_resource_mgr( + (dom_document_type *) node, &rm); + } + + lwc_context_string_unref(rm.ctx, intern); +} + +/** + * Try to destroy this node. + * + * \param node The node to destroy + * + * When some node owns this node, (such as an elment owns its attribute nodes) + * when this node being not owned, the owner should call this function to try + * to destroy this node. + * + * @note: Owning a node does not means this node's refcnt is above zero. + */ +void _dom_node_try_destroy(dom_node_internal *node) +{ + if (node == NULL) + return; + + if (node->parent == NULL) { + if (node->refcnt == 0) { + dom_node_destroy(node); + } else if (node->pending_list.prev == &node->pending_list){ + assert (node->pending_list.next == &node->pending_list); + list_append(&node->owner->pending_nodes, + &node->pending_list); + } + } +} + +/** + * To add some node to the pending list, when a node is removed from its parent + * or an attribute is removed from its element + * + * \param node The Node instance + */ +void _dom_node_mark_pending(dom_node_internal *node) +{ + struct dom_document *doc = node->owner; + + /* TODO: the pending_list is located at in dom_document, but some + * nodes can be created without a document created, such as a + * dom_document_type node. For this reason, we should test whether + * the doc is NULL. */ + if (doc != NULL) { + /* The node must not be in the pending list */ + assert(node->pending_list.prev == &node->pending_list); + + list_append(&doc->pending_nodes, &node->pending_list); + } +} + +/** + * To remove the node from the pending list, this may happen when + * a node is removed and then appended to another parent + * + * \param node The Node instance + */ +void _dom_node_remove_pending(dom_node_internal *node) +{ + struct dom_document *doc = node->owner; + + if (doc != NULL) { + /* The node must be in the pending list */ + assert(node->pending_list.prev != &node->pending_list); + + list_del(&node->pending_list); + } +} + |