/* * This file is part of libdom. * Licensed under the MIT License, * http://www.opensource.org/licenses/mit-license.php * Copyright 2007 John-Mark Bell * Copyright 2009 Bo Yang */ #include #include #include #include #include #include #include #include "core/string.h" #include "core/attr.h" #include "core/cdatasection.h" #include "core/comment.h" #include "core/document.h" #include "core/doc_fragment.h" #include "core/element.h" #include "core/entity_ref.h" #include "core/namednodemap.h" #include "core/nodelist.h" #include "core/pi.h" #include "core/text.h" #include "utils/validate.h" #include "utils/namespace.h" #include "utils/utils.h" /** * Item in list of active nodelists */ struct dom_doc_nl { dom_nodelist *list; /**< Nodelist */ struct dom_doc_nl *next; /**< Next item */ struct dom_doc_nl *prev; /**< Previous item */ }; /* The virtual functions of this dom_document */ static struct dom_document_vtable document_vtable = { { { DOM_NODE_EVENT_TARGET_VTABLE }, DOM_NODE_VTABLE_DOCUMENT }, DOM_DOCUMENT_VTABLE }; static struct dom_node_protect_vtable document_protect_vtable = { DOM_DOCUMENT_PROTECT_VTABLE }; /*----------------------------------------------------------------------*/ /* Internally used helper functions */ static dom_exception dom_document_dup_node(dom_document *doc, dom_node *node, bool deep, dom_node **result, dom_node_operation opt); /*----------------------------------------------------------------------*/ /* The constructors and destructors */ /** * Create a Document * * \param doc Pointer to location to receive created document * \param daf The default action fetcher * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion. * * The returned document will already be referenced. */ dom_exception _dom_document_create(dom_events_default_action_fetcher daf, dom_document **doc) { dom_document *d; dom_exception err; /* Create document */ d = malloc(sizeof(dom_document)); if (d == NULL) return DOM_NO_MEM_ERR; /* Initialise the virtual table */ d->base.base.vtable = &document_vtable; d->base.vtable = &document_protect_vtable; /* Initialise base class -- the Document has no parent, so * destruction will be attempted as soon as its reference count * reaches zero. Documents own themselves (this simplifies the * rest of the code, as it doesn't need to special case Documents) */ err = _dom_document_initialise(d, daf); if (err != DOM_NO_ERR) { /* Clean up document */ free(d); return err; } *doc = d; return DOM_NO_ERR; } /* Initialise the document */ dom_exception _dom_document_initialise(dom_document *doc, dom_events_default_action_fetcher daf) { dom_exception err; dom_string *name; err = dom_string_create((const uint8_t *) "#document", SLEN("#document"), &name); if (err != DOM_NO_ERR) return err; doc->nodelists = NULL; err = _dom_node_initialise(&doc->base, doc, DOM_DOCUMENT_NODE, name, NULL, NULL, NULL); dom_string_unref(name); if (err != DOM_NO_ERR) return err; list_init(&doc->pending_nodes); doc->id_name = NULL; doc->quirks = DOM_DOCUMENT_QUIRKS_MODE_NONE; err = dom_string_create_interned((const uint8_t *) "class", SLEN("class"), &doc->class_string); if (err != DOM_NO_ERR) return err; /* We should not pass a NULL when all things hook up */ return _dom_document_event_internal_initialise(doc, &doc->dei, daf); } /* Finalise the document */ bool _dom_document_finalise(dom_document *doc) { /* Finalise base class, delete the tree in force */ _dom_node_finalise(&doc->base); /* Now, the first_child and last_child should be null */ doc->base.first_child = NULL; doc->base.last_child = NULL; /* Ensure list of nodes pending deletion is empty. If not, * then we can't yet destroy the document (its destruction will * have to wait until the pending nodes are destroyed) */ if (doc->pending_nodes.next != &doc->pending_nodes) return false; /* Ok, the document tree is empty, as is the list of nodes pending * deletion. Therefore, it is safe to destroy the document. */ /* This is paranoia -- if there are any remaining nodelists, * then the document's reference count will be * non-zero as these data structures reference the document because * they are held by the client. */ doc->nodelists = NULL; if (doc->id_name != NULL) dom_string_unref(doc->id_name); dom_string_unref(doc->class_string); _dom_document_event_internal_finalise(doc, &doc->dei); return true; } /*----------------------------------------------------------------------*/ /* Public virtual functions */ /** * Retrieve the doctype of a document * * \param doc The document to retrieve the doctype from * \param result Pointer to location to receive result * \return DOM_NO_ERR. * * The returned node will have its reference count increased. It is * the responsibility of the caller to unref the node once it has * finished with it. */ dom_exception _dom_document_get_doctype(dom_document *doc, dom_document_type **result) { dom_node_internal *c; for (c = doc->base.first_child; c != NULL; c = c->next) { if (c->type == DOM_DOCUMENT_TYPE_NODE) break; } if (c != NULL) dom_node_ref(c); *result = (dom_document_type *) c; return DOM_NO_ERR; } /** * Retrieve the DOM implementation that handles this document * * \param doc The document to retrieve the implementation from * \param result Pointer to location to receive result * \return DOM_NO_ERR. * * The returned implementation will have its reference count increased. * It is the responsibility of the caller to unref the implementation once * it has finished with it. */ dom_exception _dom_document_get_implementation(dom_document *doc, dom_implementation **result) { UNUSED(doc); *result = (dom_implementation *) "libdom"; return DOM_NO_ERR; } /** * Retrieve the document element of a document * * \param doc The document to retrieve the document element from * \param result Pointer to location to receive result * \return DOM_NO_ERR. * * The returned node will have its reference count increased. It is * the responsibility of the caller to unref the node once it has * finished with it. */ dom_exception _dom_document_get_document_element(dom_document *doc, dom_element **result) { dom_node_internal *root; /* Find the first element node in child list */ for (root = doc->base.first_child; root != NULL; root = root->next) { if (root->type == DOM_ELEMENT_NODE) break; } if (root != NULL) dom_node_ref(root); *result = (dom_element *) root; return DOM_NO_ERR; } /** * Create an element * * \param doc The document owning the element * \param tag_name The name of the element * \param result Pointer to location to receive result * \return DOM_NO_ERR on success, * DOM_INVALID_CHARACTER_ERR if ::tag_name is invalid. * * ::doc and ::tag_name will have their reference counts increased. * * The returned node will have its reference count increased. It is * the responsibility of the caller to unref the node once it has * finished with it. */ dom_exception _dom_document_create_element(dom_document *doc, dom_string *tag_name, dom_element **result) { if (_dom_validate_name(tag_name) == false) return DOM_INVALID_CHARACTER_ERR; return _dom_element_create(doc, tag_name, NULL, NULL, result); } /** * Create a document fragment * * \param doc The document owning the fragment * \param result Pointer to location to receive result * \return DOM_NO_ERR. * * The returned node will have its reference count increased. It is * the responsibility of the caller to unref the node once it has * finished with it. */ dom_exception _dom_document_create_document_fragment(dom_document *doc, dom_document_fragment **result) { dom_string *name; dom_exception err; err = dom_string_create((const uint8_t *) "#document-fragment", SLEN("#document-fragment"), &name); if (err != DOM_NO_ERR) return err; err = _dom_document_fragment_create(doc, name, NULL, result); dom_string_unref(name); return err; } /** * Create a text node * * \param doc The document owning the node * \param data The data for the node * \param result Pointer to location to receive result * \return DOM_NO_ERR. * * The returned node will have its reference count increased. It is * the responsibility of the caller to unref the node once it has * finished with it. */ dom_exception _dom_document_create_text_node(dom_document *doc, dom_string *data, dom_text **result) { dom_string *name; dom_exception err; err = dom_string_create((const uint8_t *) "#text", SLEN("#text"), &name); if (err != DOM_NO_ERR) return err; err = _dom_text_create(doc, name, data, result); dom_string_unref(name); return err; } /** * Create a comment node * * \param doc The document owning the node * \param data The data for the node * \param result Pointer to location to receive result * \return DOM_NO_ERR. * * The returned node will have its reference count increased. It is * the responsibility of the caller to unref the node once it has * finished with it. */ dom_exception _dom_document_create_comment(dom_document *doc, dom_string *data, dom_comment **result) { dom_string *name; dom_exception err; err = dom_string_create((const uint8_t *) "#comment", SLEN("#comment"), &name); if (err != DOM_NO_ERR) return err; err = _dom_comment_create(doc, name, data, result); dom_string_unref(name); return err; } /** * Create a CDATA section * * \param doc The document owning the section * \param data The data for the section contents * \param result Pointer to location to receive result * \return DOM_NO_ERR on success, * DOM_NOT_SUPPORTED_ERR if this is an HTML document. * * The returned node will have its reference count increased. It is * the responsibility of the caller to unref the node once it has * finished with it. */ dom_exception _dom_document_create_cdata_section(dom_document *doc, dom_string *data, dom_cdata_section **result) { dom_string *name; dom_exception err; err = dom_string_create((const uint8_t *) "#cdata-section", SLEN("#cdata-section"), &name); if (err != DOM_NO_ERR) return err; err = _dom_cdata_section_create(doc, name, data, result); dom_string_unref(name); return err; } /** * Create a processing instruction * * \param doc The document owning the instruction * \param target The instruction target * \param data The data for the node * \param result Pointer to location to receive result * \return DOM_NO_ERR on success, * DOM_INVALID_CHARACTER_ERR if ::target is invalid, * DOM_NOT_SUPPORTED_ERR if this is an HTML document. * * The returned node will have its reference count increased. It is * the responsibility of the caller to unref the node once it has * finished with it. */ dom_exception _dom_document_create_processing_instruction( dom_document *doc, dom_string *target, dom_string *data, dom_processing_instruction **result) { if (_dom_validate_name(target) == false) return DOM_INVALID_CHARACTER_ERR; return _dom_processing_instruction_create(doc, target, data, result); } /** * Create an attribute * * \param doc The document owning the attribute * \param name The name of the attribute * \param result Pointer to location to receive result * \return DOM_NO_ERR on success, * DOM_INVALID_CHARACTER_ERR if ::name is invalid. * * The constructed attribute will always be classified as 'specified'. * * The returned node will have its reference count increased. It is * the responsibility of the caller to unref the node once it has * finished with it. */ dom_exception _dom_document_create_attribute(dom_document *doc, dom_string *name, dom_attr **result) { if (_dom_validate_name(name) == false) return DOM_INVALID_CHARACTER_ERR; return _dom_attr_create(doc, name, NULL, NULL, true, result); } /** * Create an entity reference * * \param doc The document owning the reference * \param name The name of the entity to reference * \param result Pointer to location to receive result * \return DOM_NO_ERR on success, * DOM_INVALID_CHARACTER_ERR if ::name is invalid, * DOM_NOT_SUPPORTED_ERR if this is an HTML document. * * The returned node will have its reference count increased. It is * the responsibility of the caller to unref the node once it has * finished with it. */ dom_exception _dom_document_create_entity_reference(dom_document *doc, dom_string *name, dom_entity_reference **result) { if (_dom_validate_name(name) == false) return DOM_INVALID_CHARACTER_ERR; return _dom_entity_reference_create(doc, name, NULL, result); } /** * Retrieve a list of all elements with a given tag name * * \param doc The document to search in * \param tagname The tag name to search for ("*" for all) * \param result Pointer to location to receive result * \return DOM_NO_ERR. * * The returned list will have its reference count increased. It is * the responsibility of the caller to unref the list once it has * finished with it. */ dom_exception _dom_document_get_elements_by_tag_name(dom_document *doc, dom_string *tagname, dom_nodelist **result) { return _dom_document_get_nodelist(doc, DOM_NODELIST_BY_NAME, (dom_node_internal *) doc, tagname, NULL, NULL, result); } /** * Import a node from another document into this one * * \param doc The document to import into * \param node The node to import * \param deep Whether to copy the node's subtree * \param result Pointer to location to receive imported node in this document. * \return DOM_NO_ERR on success, * DOM_INVALID_CHARACTER_ERR if any of the names are invalid, * DOM_NOT_SUPPORTED_ERR if the type of ::node is unsupported * * The returned node will have its reference count increased. It is * the responsibility of the caller to unref the node once it has * finished with it. */ dom_exception _dom_document_import_node(dom_document *doc, dom_node *node, bool deep, dom_node **result) { /* TODO: The DOM_INVALID_CHARACTER_ERR exception */ return dom_document_dup_node(doc, node, deep, result, DOM_NODE_IMPORTED); } /** * Create an element from the qualified name and namespace URI * * \param doc The document owning the element * \param namespace The namespace URI to use, or NULL for none * \param qname The qualified name of the element * \param result Pointer to location to receive result * \return DOM_NO_ERR on success, * DOM_INVALID_CHARACTER_ERR if ::qname is invalid, * DOM_NAMESPACE_ERR if ::qname is malformed, or it has a * prefix and ::namespace is NULL, or * ::qname has a prefix "xml" and * ::namespace is not * "http://www.w3.org/XML/1998/namespace", * or ::qname has a prefix "xmlns" and * ::namespace is not * "http://www.w3.org/2000/xmlns", or * ::namespace is * "http://www.w3.org/2000/xmlns" and * ::qname is not (or is not prefixed by) * "xmlns", * DOM_NOT_SUPPORTED_ERR if ::doc does not support the "XML" * feature. * * The returned node will have its reference count increased. It is * the responsibility of the caller to unref the node once it has * finished with it. */ dom_exception _dom_document_create_element_ns(dom_document *doc, dom_string *namespace, dom_string *qname, dom_element **result) { dom_string *prefix, *localname; dom_exception err; if (_dom_validate_name(qname) == false) return DOM_INVALID_CHARACTER_ERR; /* Validate qname */ err = _dom_namespace_validate_qname(qname, namespace); if (err != DOM_NO_ERR) { return err; } /* Divide QName into prefix/localname pair */ err = _dom_namespace_split_qname(qname, &prefix, &localname); if (err != DOM_NO_ERR) { return err; } /* Attempt to create element */ err = _dom_element_create(doc, localname, namespace, prefix, result); /* Tidy up */ if (localname != NULL) { dom_string_unref(localname); } if (prefix != NULL) { dom_string_unref(prefix); } return err; } /** * Create an attribute from the qualified name and namespace URI * * \param doc The document owning the attribute * \param namespace The namespace URI to use * \param qname The qualified name of the attribute * \param result Pointer to location to receive result * \return DOM_NO_ERR on success, * DOM_INVALID_CHARACTER_ERR if ::qname is invalid, * DOM_NAMESPACE_ERR if ::qname is malformed, or it has a * prefix and ::namespace is NULL, or * ::qname has a prefix "xml" and * ::namespace is not * "http://www.w3.org/XML/1998/namespace", * or ::qname has a prefix "xmlns" and * ::namespace is not * "http://www.w3.org/2000/xmlns", or * ::namespace is * "http://www.w3.org/2000/xmlns" and * ::qname is not (or is not prefixed by) * "xmlns", * DOM_NOT_SUPPORTED_ERR if ::doc does not support the "XML" * feature. * * The returned node will have its reference count increased. It is * the responsibility of the caller to unref the node once it has * finished with it. */ dom_exception _dom_document_create_attribute_ns(dom_document *doc, dom_string *namespace, dom_string *qname, dom_attr **result) { dom_string *prefix, *localname; dom_exception err; if (_dom_validate_name(qname) == false) return DOM_INVALID_CHARACTER_ERR; /* Validate qname */ err = _dom_namespace_validate_qname(qname, namespace); if (err != DOM_NO_ERR) { return err; } /* Divide QName into prefix/localname pair */ err = _dom_namespace_split_qname(qname, &prefix, &localname); if (err != DOM_NO_ERR) { return err; } /* Attempt to create attribute */ err = _dom_attr_create(doc, localname, namespace, prefix, true, result); /* Tidy up */ if (localname != NULL) { dom_string_unref(localname); } if (prefix != NULL) { dom_string_unref(prefix); } return err; } /** * Retrieve a list of all elements with a given local name and namespace URI * * \param doc The document to search in * \param namespace The namespace URI * \param localname The local name * \param result Pointer to location to receive result * \return DOM_NO_ERR on success, appropriate dom_exception on failure. * * The returned list will have its reference count increased. It is * the responsibility of the caller to unref the list once it has * finished with it. */ dom_exception _dom_document_get_elements_by_tag_name_ns( dom_document *doc, dom_string *namespace, dom_string *localname, dom_nodelist **result) { return _dom_document_get_nodelist(doc, DOM_NODELIST_BY_NAMESPACE, (dom_node_internal *) doc, NULL, namespace, localname, result); } /** * Retrieve the element that matches the specified ID * * \param doc The document to search in * \param id The ID to search for * \param result Pointer to location to receive result * \return DOM_NO_ERR on success, appropriate dom_exception on failure. * * The returned node will have its reference count increased. It is * the responsibility of the caller to unref the node once it has * finished with it. */ dom_exception _dom_document_get_element_by_id(dom_document *doc, dom_string *id, dom_element **result) { dom_node_internal *root; dom_exception err; *result = NULL; err = dom_document_get_document_element(doc, (void *) &root); if (err != DOM_NO_ERR) return err; err = _dom_find_element_by_id(root, id, result); dom_node_unref(root); return err; } /** * Retrieve the input encoding of the document * * \param doc The document to query * \param result Pointer to location to receive result * \return DOM_NOT_SUPPORTED_ERR, we don't support this API now. * * 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. */ dom_exception _dom_document_get_input_encoding(dom_document *doc, dom_string **result) { UNUSED(doc); UNUSED(result); return DOM_NOT_SUPPORTED_ERR; } /** * Retrieve the XML encoding of the document * * \param doc The document to query * \param result Pointer to location to receive result * \return DOM_NOT_SUPPORTED_ERR, we don't support this API now. * * 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. */ dom_exception _dom_document_get_xml_encoding(dom_document *doc, dom_string **result) { UNUSED(doc); UNUSED(result); return DOM_NOT_SUPPORTED_ERR; } /** * Retrieve the standalone status of the document * * \param doc The document to query * \param result Pointer to location to receive result * \return DOM_NOT_SUPPORTED_ERR, we don't support this API now. */ dom_exception _dom_document_get_xml_standalone(dom_document *doc, bool *result) { UNUSED(doc); UNUSED(result); return DOM_NOT_SUPPORTED_ERR; } /** * Set the standalone status of the document * * \param doc The document to query * \param standalone Standalone status to use * \return DOM_NO_ERR on success, * DOM_NOT_SUPPORTED_ERR if the document does not support the "XML" * feature. * * We don't support this API now, so the return value is always * DOM_NOT_SUPPORTED_ERR. */ dom_exception _dom_document_set_xml_standalone(dom_document *doc, bool standalone) { UNUSED(doc); UNUSED(standalone); return DOM_NOT_SUPPORTED_ERR; } /** * Retrieve the XML version of the document * * \param doc The document to query * \param result Pointer to location to receive result * \return DOM_NO_ERR * * 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 the return value is always * DOM_NOT_SUPPORTED_ERR. */ dom_exception _dom_document_get_xml_version(dom_document *doc, dom_string **result) { UNUSED(doc); UNUSED(result); return DOM_NOT_SUPPORTED_ERR; } /** * Set the XML version of the document * * \param doc The document to query * \param version XML version to use * \return DOM_NO_ERR on success, * DOM_NOT_SUPPORTED_ERR if the document does not support the "XML" * feature. * * We don't support this API now, so the return value is always * DOM_NOT_SUPPORTED_ERR. */ dom_exception _dom_document_set_xml_version(dom_document *doc, dom_string *version) { UNUSED(doc); UNUSED(version); return DOM_NOT_SUPPORTED_ERR; } /** * Retrieve the error checking mode of the document * * \param doc The document to query * \param result Pointer to location to receive result * \return DOM_NOT_SUPPORTED_ERR, we don't support this API now. */ dom_exception _dom_document_get_strict_error_checking( dom_document *doc, bool *result) { UNUSED(doc); UNUSED(result); return DOM_NOT_SUPPORTED_ERR; } /** * Set the error checking mode of the document * * \param doc The document to query * \param strict Whether to use strict error checking * \return DOM_NOT_SUPPORTED_ERR, we don't support this API now. */ dom_exception _dom_document_set_strict_error_checking( dom_document *doc, bool strict) { UNUSED(doc); UNUSED(strict); return DOM_NOT_SUPPORTED_ERR; } /** * Retrieve the URI of the document * * \param doc The document to query * \param result Pointer to location to receive result * \return DOM_NO_ERR. * * 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. */ dom_exception _dom_document_get_uri(dom_document *doc, dom_string **result) { *result = dom_string_ref(doc->uri); return DOM_NO_ERR; } /** * Set the URI of the document * * \param doc The document to query * \param uri The URI to use * \return DOM_NO_ERR. * * 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. */ dom_exception _dom_document_set_uri(dom_document *doc, dom_string *uri) { dom_string_unref(doc->uri); doc->uri = dom_string_ref(uri); return DOM_NO_ERR; } /** * Attempt to adopt a node from another document into this document * * \param doc The document to adopt into * \param node The node to adopt * \param result Pointer to location to receive adopted node * \return DOM_NO_ERR on success, * DOM_NO_MODIFICATION_ALLOWED_ERR if ::node is readonly, * DOM_NOT_SUPPORTED_ERR if ::node is of type Document or * DocumentType * * The returned node will have its reference count increased. It is * the responsibility of the caller to unref the node once it has * finished with it. * * @note: The spec said adoptNode may be light weight than the importNode * because the former need no Node creation. But in our implementation * this can't be ensured. Both adoptNode and importNode create new * nodes using the importing/adopting document's resource manager. So, * generally, the adoptNode and importNode call the same function * dom_document_dup_node. */ dom_exception _dom_document_adopt_node(dom_document *doc, dom_node *node, dom_node **result) { dom_node_internal *n = (dom_node_internal *) node; dom_exception err; dom_node_internal *parent; dom_node_internal *tmp; *result = NULL; if (n->type == DOM_DOCUMENT_NODE || n->type == DOM_DOCUMENT_TYPE_NODE) { return DOM_NOT_SUPPORTED_ERR; } if (n->type == DOM_ENTITY_NODE || n->type == DOM_NOTATION_NODE || n->type == DOM_PROCESSING_INSTRUCTION_NODE || n->type == DOM_TEXT_NODE || n->type == DOM_CDATA_SECTION_NODE || n->type == DOM_COMMENT_NODE) { *result = NULL; return DOM_NO_ERR; } /* Support XML when necessary */ if (n->type == DOM_ENTITY_REFERENCE_NODE) { return DOM_NOT_SUPPORTED_ERR; } err = dom_document_dup_node(doc, node, true, result, DOM_NODE_ADOPTED); if (err != DOM_NO_ERR) { *result = NULL; return err; } parent = n->parent; if (parent != NULL) { err = dom_node_remove_child(parent, node, (void *) &tmp); if (err != DOM_NO_ERR) { dom_node_unref(*result); *result = NULL; return err; } dom_node_unref(tmp); } return DOM_NO_ERR; } /** * Retrieve the DOM configuration associated with a document * * \param doc The document to query * \param result Pointer to location to receive result * \return DOM_NOT_SUPPORTED_ERR, we don't support this API now. * * The returned object will have its reference count increased. It is * the responsibility of the caller to unref the object once it has * finished with it. */ dom_exception _dom_document_get_dom_config(dom_document *doc, struct dom_configuration **result) { UNUSED(doc); UNUSED(result); return DOM_NOT_SUPPORTED_ERR; } /** * Normalize a document * * \param doc The document to normalize * \return DOM_NOT_SUPPORTED_ERR, we don't support this API now. */ dom_exception _dom_document_normalize(dom_document *doc) { UNUSED(doc); return DOM_NOT_SUPPORTED_ERR; } /** * Rename a node in a document * * \param doc The document containing the node * \param node The node to rename * \param namespace The new namespace for the node * \param qname The new qualified name for the node * \param result Pointer to location to receive renamed node * \return DOM_NO_ERR on success, * DOM_INVALID_CHARACTER_ERR if ::tag_name is invalid, * DOM_WRONG_DOCUMENT_ERR if ::node was created in a different * document * DOM_NAMESPACE_ERR if ::qname is malformed, or it has a * prefix and ::namespace is NULL, or * ::qname has a prefix "xml" and * ::namespace is not * "http://www.w3.org/XML/1998/namespace", * or ::qname has a prefix "xmlns" and * ::namespace is not * "http://www.w3.org/2000/xmlns", or * ::namespace is * "http://www.w3.org/2000/xmlns" and * ::qname is not (or is not prefixed by) * "xmlns", * DOM_NOT_SUPPORTED_ERR if ::doc does not support the "XML" * feature. * * The returned node will have its reference count increased. It is * the responsibility of the caller to unref the node once it has * finished with it. * * We don't support this API now, so the return value is always * DOM_NOT_SUPPORTED_ERR. */ dom_exception _dom_document_rename_node(dom_document *doc, dom_node *node, dom_string *namespace, dom_string *qname, dom_node **result) { UNUSED(doc); UNUSED(node); UNUSED(namespace); UNUSED(qname); UNUSED(result); return DOM_NOT_SUPPORTED_ERR; } dom_exception _dom_document_get_text_content(dom_node_internal *node, dom_string **result) { UNUSED(node); *result = NULL; return DOM_NO_ERR; } dom_exception _dom_document_set_text_content(dom_node_internal *node, dom_string *content) { UNUSED(node); UNUSED(content); return DOM_NO_ERR; } /*-----------------------------------------------------------------------*/ /* Overload protected virtual functions */ /* The virtual destroy function of this class */ void _dom_document_destroy(dom_node_internal *node) { dom_document *doc = (dom_document *) node; if (_dom_document_finalise(doc) == true) { free(doc); } } /* The copy constructor function of this class */ dom_exception _dom_document_copy(dom_node_internal *old, dom_node_internal **copy) { UNUSED(old); UNUSED(copy); return DOM_NOT_SUPPORTED_ERR; } /* ----------------------------------------------------------------------- */ /* Helper functions */ /** * Get a nodelist, creating one if necessary * * \param doc The document to get a nodelist for * \param type The type of the NodeList * \param root Root node of subtree that list applies to * \param tagname Name of nodes in list (or NULL) * \param namespace Namespace part of nodes in list (or NULL) * \param localname Local part of nodes in list (or NULL) * \param list Pointer to location to receive list * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion * * The returned list will have its reference count increased. It is * the responsibility of the caller to unref the list once it has * finished with it. */ dom_exception _dom_document_get_nodelist(dom_document *doc, nodelist_type type, dom_node_internal *root, dom_string *tagname, dom_string *namespace, dom_string *localname, dom_nodelist **list) { struct dom_doc_nl *l; dom_exception err; for (l = doc->nodelists; l; l = l->next) { if (_dom_nodelist_match(l->list, type, root, tagname, namespace, localname)) break; } if (l != NULL) { /* Found an existing list, so use it */ dom_nodelist_ref(l->list); } else { /* No existing list */ /* Create active list entry */ l = malloc(sizeof(struct dom_doc_nl)); if (l == NULL) return DOM_NO_MEM_ERR; /* Create nodelist */ err = _dom_nodelist_create(doc, type, root, tagname, namespace, localname, &l->list); if (err != DOM_NO_ERR) { free(l); return err; } /* Add to document's list of active nodelists */ l->prev = NULL; l->next = doc->nodelists; if (doc->nodelists) doc->nodelists->prev = l; doc->nodelists = l; } /* Note: the document does not claim a reference on the nodelist * If it did, the nodelist's reference count would never reach zero, * and the list would remain indefinitely. This is not a problem as * the list notifies the document of its destruction via * _dom_document_remove_nodelist. */ *list = l->list; return DOM_NO_ERR; } /** * Remove a nodelist from a document * * \param doc The document to remove the list from * \param list The list to remove */ void _dom_document_remove_nodelist(dom_document *doc, dom_nodelist *list) { struct dom_doc_nl *l; for (l = doc->nodelists; l; l = l->next) { if (l->list == list) break; } if (l == NULL) { /* This should never happen; we should probably abort here */ return; } /* Remove from list */ if (l->prev != NULL) l->prev->next = l->next; else doc->nodelists = l->next; if (l->next != NULL) l->next->prev = l->prev; /* And free item */ free(l); } /** * Find element with certain ID in the subtree rooted at root * * \param root The root element from where we start * \param id The ID of the target element * \param result The result element * \return DOM_NO_ERR on success, appropriate dom_exception on failure. */ dom_exception _dom_find_element_by_id(dom_node_internal *root, dom_string *id, dom_element **result) { dom_node_internal *node = root; *result = NULL; while (node != NULL) { if (node->type == DOM_ELEMENT_NODE) { dom_string *real_id; _dom_element_get_id((dom_element *) node, &real_id); if (dom_string_isequal(real_id, id)) { dom_string_unref(real_id); *result = (dom_element *) node; return DOM_NO_ERR; } dom_string_unref(real_id); } if (node->first_child != NULL) { /* Has children */ node = node->first_child; } else if (node->next != NULL) { /* No children, but has siblings */ node = node->next; } else { /* No children or siblings. * Find first unvisited relation. */ dom_node_internal *parent = node->parent; while (parent != root && node == parent->last_child) { node = parent; parent = parent->parent; } node = node->next; } } return DOM_NO_ERR; } /** * Duplicate a Node * * \param doc The documen * \param node The node to duplicate * \param deep Whether to make a deep copy * \param result The returned node * \param opt Whether this is adopt or import operation * \return DOM_NO_ERR on success, appropriate dom_exception on failure. */ dom_exception dom_document_dup_node(dom_document *doc, dom_node *node, bool deep, dom_node **result, dom_node_operation opt) { dom_node_internal *n = (dom_node_internal *) node; dom_node_internal *ret; dom_exception err; dom_node_internal *child, *r; dom_user_data *ud; if (opt == DOM_NODE_ADOPTED && _dom_node_readonly(n)) return DOM_NO_MODIFICATION_ALLOWED_ERR; if (n->type == DOM_DOCUMENT_NODE || n->type == DOM_DOCUMENT_TYPE_NODE) return DOM_NOT_SUPPORTED_ERR; err = dom_node_copy(node, &ret); if (err != DOM_NO_ERR) return err; if (n->type == DOM_ATTRIBUTE_NODE) { _dom_attr_set_specified((dom_attr *) node, true); deep = true; } if (n->type == DOM_ENTITY_REFERENCE_NODE) { deep = false; } if (n->type == DOM_ELEMENT_NODE) { /* Specified attributes are copyied but not default attributes, * if the document object hold all the default attributes, we * have nothing to do here */ } if (opt == DOM_NODE_ADOPTED && (n->type == DOM_ENTITY_NODE || n->type == DOM_NOTATION_NODE)) { /* We did not support XML now */ return DOM_NOT_SUPPORTED_ERR; } if (deep == true) { child = ((dom_node_internal *) node)->first_child; while (child != NULL) { err = dom_document_import_node(doc, child, deep, (void *) &r); if (err != DOM_NO_ERR) { dom_node_unref(ret); return err; } err = dom_node_append_child(ret, r, (void *) &r); if (err != DOM_NO_ERR) { dom_node_unref(ret); dom_node_unref(r); return err; } dom_node_unref(r); child = child->next; } } /* Call the dom_user_data_handlers */ ud = n->user_data; while (ud != NULL) { if (ud->handler != NULL) { ud->handler(opt, ud->key, ud->data, node, (dom_node *) ret); } ud = ud->next; } *result = (dom_node *) ret; return DOM_NO_ERR; } /** * Try to destroy the document. * * \param doc The instance of Document * * Delete the document if: * 1. The refcnt reach zero * 2. The pending list is empty * * else, do nothing. */ void _dom_document_try_destroy(dom_document *doc) { if (doc->base.base.refcnt != 0 || doc->base.parent != NULL) return; _dom_document_destroy((dom_node_internal *) doc); } /** * Set the ID attribute name of this document * * \param doc The document object * \param name The ID name of the elements in this document */ void _dom_document_set_id_name(dom_document *doc, dom_string *name) { if (doc->id_name != NULL) dom_string_unref(doc->id_name); doc->id_name = dom_string_ref(name); } /*-----------------------------------------------------------------------*/ /* Semi-internal API extensions for NetSurf */ dom_exception _dom_document_get_quirks_mode(dom_document *doc, dom_document_quirks_mode *result) { *result = doc->quirks; return DOM_NO_ERR; } dom_exception _dom_document_set_quirks_mode(dom_document *doc, dom_document_quirks_mode quirks) { doc->quirks = quirks; return DOM_NO_ERR; }