Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 3583 → Rev 3584

/programs/network/netsurf/libdom/src/core/document.c
0,0 → 1,1523
/*
* This file is part of libdom.
* 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>
#include <stdlib.h>
 
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
 
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
 
#include <dom/functypes.h>
#include <dom/core/attr.h>
#include <dom/core/element.h>
#include <dom/core/document.h>
#include <dom/core/implementation.h>
 
 
 
 
#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,
void *daf_ctx,
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, daf_ctx);
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,
void *daf_ctx)
{
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);
 
err = dom_string_create_interned((const uint8_t *) "id",
SLEN("id"), &doc->id_name);
if (err != DOM_NO_ERR)
return err;
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) {
dom_string_unref(doc->id_name);
return err;
}
 
/* Intern the empty string. The use of a space in the constant
* is to prevent the compiler warning about an empty string.
*/
err = dom_string_create_interned((const uint8_t *) " ", 0,
&doc->_memo_empty);
if (err != DOM_NO_ERR) {
dom_string_unref(doc->id_name);
dom_string_unref(doc->class_string);
return err;
}
 
err = dom_string_create_interned((const uint8_t *) "DOMNodeInserted",
SLEN("DOMNodeInserted"),
&doc->_memo_domnodeinserted);
if (err != DOM_NO_ERR) {
dom_string_unref(doc->_memo_empty);
dom_string_unref(doc->id_name);
dom_string_unref(doc->class_string);
return err;
}
 
err = dom_string_create_interned((const uint8_t *) "DOMNodeRemoved",
SLEN("DOMNodeRemoved"),
&doc->_memo_domnoderemoved);
if (err != DOM_NO_ERR) {
dom_string_unref(doc->_memo_domnodeinserted);
dom_string_unref(doc->_memo_empty);
dom_string_unref(doc->id_name);
dom_string_unref(doc->class_string);
return err;
}
 
err = dom_string_create_interned((const uint8_t *) "DOMNodeInsertedIntoDocument",
SLEN("DOMNodeInsertedIntoDocument"),
&doc->_memo_domnodeinsertedintodocument);
if (err != DOM_NO_ERR) {
dom_string_unref(doc->_memo_domnoderemoved);
dom_string_unref(doc->_memo_domnodeinserted);
dom_string_unref(doc->_memo_empty);
dom_string_unref(doc->id_name);
dom_string_unref(doc->class_string);
return err;
}
 
err = dom_string_create_interned((const uint8_t *) "DOMNodeRemovedFromDocument",
SLEN("DOMNodeRemovedFromDocument"),
&doc->_memo_domnoderemovedfromdocument);
if (err != DOM_NO_ERR) {
dom_string_unref(doc->_memo_domnodeinsertedintodocument);
dom_string_unref(doc->_memo_domnoderemoved);
dom_string_unref(doc->_memo_domnodeinserted);
dom_string_unref(doc->_memo_empty);
dom_string_unref(doc->id_name);
dom_string_unref(doc->class_string);
return err;
}
 
err = dom_string_create_interned((const uint8_t *) "DOMAttrModified",
SLEN("DOMAttrModified"),
&doc->_memo_domattrmodified);
if (err != DOM_NO_ERR) {
dom_string_unref(doc->_memo_domnoderemovedfromdocument);
dom_string_unref(doc->_memo_domnodeinsertedintodocument);
dom_string_unref(doc->_memo_domnoderemoved);
dom_string_unref(doc->_memo_domnodeinserted);
dom_string_unref(doc->_memo_empty);
dom_string_unref(doc->id_name);
dom_string_unref(doc->class_string);
return err;
}
 
err = dom_string_create_interned((const uint8_t *) "DOMCharacterDataModified",
SLEN("DOMCharacterDataModified"),
&doc->_memo_domcharacterdatamodified);
if (err != DOM_NO_ERR) {
dom_string_unref(doc->_memo_domattrmodified);
dom_string_unref(doc->_memo_domnoderemovedfromdocument);
dom_string_unref(doc->_memo_domnodeinsertedintodocument);
dom_string_unref(doc->_memo_domnoderemoved);
dom_string_unref(doc->_memo_domnodeinserted);
dom_string_unref(doc->_memo_empty);
dom_string_unref(doc->id_name);
dom_string_unref(doc->class_string);
return err;
}
 
err = dom_string_create_interned((const uint8_t *) "DOMSubtreeModified",
SLEN("DOMSubtreeModified"),
&doc->_memo_domsubtreemodified);
if (err != DOM_NO_ERR) {
dom_string_unref(doc->_memo_domcharacterdatamodified);
dom_string_unref(doc->_memo_domattrmodified);
dom_string_unref(doc->_memo_domnoderemovedfromdocument);
dom_string_unref(doc->_memo_domnodeinsertedintodocument);
dom_string_unref(doc->_memo_domnoderemoved);
dom_string_unref(doc->_memo_domnodeinserted);
dom_string_unref(doc->_memo_empty);
dom_string_unref(doc->id_name);
dom_string_unref(doc->class_string);
return err;
}
 
/* We should not pass a NULL when all things hook up */
return _dom_document_event_internal_initialise(doc, &doc->dei, daf, daf_ctx);
}
 
 
/* 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_string_unref(doc->_memo_empty);
dom_string_unref(doc->_memo_domnodeinserted);
dom_string_unref(doc->_memo_domnoderemoved);
dom_string_unref(doc->_memo_domnodeinsertedintodocument);
dom_string_unref(doc->_memo_domnoderemovedfromdocument);
dom_string_unref(doc->_memo_domattrmodified);
dom_string_unref(doc->_memo_domcharacterdatamodified);
dom_string_unref(doc->_memo_domsubtreemodified);
_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 (real_id != NULL) {
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;
}