summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/Makefile2
-rw-r--r--src/core/attr.c2
-rw-r--r--src/core/document.c114
-rw-r--r--src/core/document.h16
-rw-r--r--src/core/node.c2
-rw-r--r--src/core/node.h2
-rw-r--r--src/core/nodelist.c228
-rw-r--r--src/core/nodelist.h31
-rw-r--r--src/core/string.c54
9 files changed, 449 insertions, 2 deletions
diff --git a/src/core/Makefile b/src/core/Makefile
index 3924ebb..3245df3 100644
--- a/src/core/Makefile
+++ b/src/core/Makefile
@@ -22,7 +22,7 @@
CFLAGS += -I$(CURDIR)
# Objects
-OBJS = attr document node string
+OBJS = attr document node nodelist string
.PHONY: clean debug distclean export release setup test
diff --git a/src/core/attr.c b/src/core/attr.c
index 5414ce0..1a5926b 100644
--- a/src/core/attr.c
+++ b/src/core/attr.c
@@ -5,6 +5,8 @@
* Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org>
*/
+#include <stddef.h>
+
#include <dom/core/attr.h>
#include "core/node.h"
diff --git a/src/core/document.c b/src/core/document.c
index 6edce6e..71b3a6a 100644
--- a/src/core/document.c
+++ b/src/core/document.c
@@ -10,14 +10,27 @@
#include "core/document.h"
#include "core/node.h"
+#include "core/nodelist.h"
#include "utils/utils.h"
/**
+ * Item in list of active nodelists
+ */
+struct dom_doc_nl {
+ struct dom_nodelist *list; /**< Nodelist */
+
+ struct dom_doc_nl *next; /**< Next item */
+ struct dom_doc_nl *prev; /**< Previous item */
+};
+
+/**
* DOM document
*/
struct dom_document {
struct dom_node base; /**< Base node */
+ struct dom_doc_nl *nodelists; /**< List of active nodelists */
+
dom_alloc alloc; /**< Memory (de)allocation function */
void *pw; /**< Pointer to client data */
};
@@ -735,3 +748,104 @@ void *dom_document_alloc(struct dom_document *doc, void *ptr, size_t size)
{
return doc->alloc(ptr, size, doc->pw);
}
+
+/**
+ * Get a nodelist, creating one if necessary
+ *
+ * \param doc The document to get a nodelist for
+ * \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(struct dom_document *doc,
+ struct dom_node *root, struct dom_string *tagname,
+ struct dom_string *namespace, struct dom_string *localname,
+ struct 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, 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 = doc->alloc(NULL, sizeof(struct dom_doc_nl), doc->pw);
+ if (l == NULL)
+ return DOM_NO_MEM_ERR;
+
+ /* Create nodelist */
+ err = dom_nodelist_create(doc, root, tagname, namespace,
+ localname, &l->list);
+ if (err != DOM_NO_ERR) {
+ doc->alloc(l, 0, doc->pw);
+ 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(struct dom_document *doc,
+ struct 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 */
+ doc->alloc(l, 0, doc->pw);
+}
diff --git a/src/core/document.h b/src/core/document.h
index 7c18fe9..a51d1e1 100644
--- a/src/core/document.h
+++ b/src/core/document.h
@@ -12,8 +12,24 @@
#include <stddef.h>
struct dom_document;
+struct dom_node;
+struct dom_nodelist;
+struct dom_string;
+/* Get base of document buffer */
const uint8_t *dom_document_get_base(struct dom_document *doc);
+
+/* (De)allocate memory */
void *dom_document_alloc(struct dom_document *doc, void *ptr, size_t size);
+/* Get a nodelist, creating one if necessary */
+dom_exception dom_document_get_nodelist(struct dom_document *doc,
+ struct dom_node *root, struct dom_string *tagname,
+ struct dom_string *namespace, struct dom_string *localname,
+ struct dom_nodelist **list);
+
+/* Remove a nodelist */
+void dom_document_remove_nodelist(struct dom_document *doc,
+ struct dom_nodelist *list);
+
#endif
diff --git a/src/core/node.c b/src/core/node.c
index 468965b..40848df 100644
--- a/src/core/node.c
+++ b/src/core/node.c
@@ -5,6 +5,8 @@
* Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org>
*/
+#include <dom/core/string.h>
+
#include "core/document.h"
#include "core/node.h"
#include "utils/utils.h"
diff --git a/src/core/node.h b/src/core/node.h
index bfd7fa0..d8982f6 100644
--- a/src/core/node.h
+++ b/src/core/node.h
@@ -9,9 +9,9 @@
#define dom_internal_core_node_h_
#include <dom/core/node.h>
-#include <dom/core/string.h>
struct dom_attr;
+struct dom_string;
/**
* User data context attached to a DOM node
diff --git a/src/core/nodelist.c b/src/core/nodelist.c
new file mode 100644
index 0000000..1fff561
--- /dev/null
+++ b/src/core/nodelist.c
@@ -0,0 +1,228 @@
+/*
+ * 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>
+ */
+
+#include <dom/core/node.h>
+#include <dom/core/nodelist.h>
+#include <dom/core/string.h>
+
+#include "core/document.h"
+#include "core/nodelist.h"
+
+#include "utils/utils.h"
+
+struct dom_node;
+
+/**
+ * DOM node list
+ */
+struct dom_nodelist {
+ struct dom_document *owner; /**< Owning document */
+
+ struct dom_node *root; /**< Root of applicable subtree */
+
+ enum { DOM_NODELIST_CHILDREN,
+ DOM_NODELIST_BY_NAME,
+ DOM_NODELIST_BY_NAMESPACE
+ } type; /**< List type */
+
+ union {
+ struct dom_string *name; /**< Tag name to match */
+ struct {
+ struct dom_string *namespace; /**< Namespace */
+ struct dom_string *localname; /**< Localname */
+ } ns; /**< Data for namespace matching */
+ } data;
+
+ uint32_t refcnt; /**< Reference count */
+};
+
+/**
+ * Create a nodelist
+ *
+ * \param doc Owning document
+ * \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
+ *
+ * ::root must be a node owned by ::doc
+ *
+ * If ::tagname is non-NULL, ::namespace and ::localname must be NULL and
+ * the created list will match nodes by name
+ * If ::namespace is non-NULL, ::localname must be non-NULL and
+ * ::tagname must be NULL and the created list will match nodes by namespace
+ * and localname
+ * If ::tagname, ::namespace and ::localname are NULL, the created list
+ * will match the children of ::root.
+ *
+ * The returned list will already be referenced, so the client need not
+ * do so explicitly. The client should unref the list once finished with it.
+ */
+dom_exception dom_nodelist_create(struct dom_document *doc,
+ struct dom_node *root, struct dom_string *tagname,
+ struct dom_string *namespace, struct dom_string *localname,
+ struct dom_nodelist **list)
+{
+ struct dom_nodelist *l;
+
+ l = dom_document_alloc(doc, NULL, sizeof(struct dom_nodelist));
+ if (l == NULL)
+ return DOM_NO_MEM_ERR;
+
+ dom_node_ref((struct dom_node *) doc);
+ l->owner = doc;
+
+ dom_node_ref(root);
+ l->root = root;
+
+ if (tagname != NULL && namespace == NULL && localname == NULL) {
+ dom_string_ref(tagname);
+ l->type = DOM_NODELIST_BY_NAME;
+ l->data.name = tagname;
+ } else if (namespace != NULL && localname != NULL &&
+ tagname == NULL) {
+ dom_string_ref(namespace);
+ dom_string_ref(localname);
+ l->type = DOM_NODELIST_BY_NAMESPACE;
+ l->data.ns.namespace = namespace;
+ l->data.ns.localname = localname;
+ } else {
+ l->type = DOM_NODELIST_CHILDREN;
+ }
+
+ l->refcnt = 1;
+
+ *list = l;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Claim a reference on a DOM node list
+ *
+ * \param list The list to claim a reference on
+ */
+void dom_nodelist_ref(struct dom_nodelist *list)
+{
+ list->refcnt++;
+}
+
+/**
+ * Release a reference on a DOM node list
+ *
+ * \param list The list to release the reference from
+ *
+ * If the reference count reaches zero, any memory claimed by the
+ * list will be released
+ */
+void dom_nodelist_unref(struct dom_nodelist *list)
+{
+ if (--list->refcnt == 0) {
+ switch (list->type) {
+ case DOM_NODELIST_CHILDREN:
+ /* Nothing to do */
+ break;
+ case DOM_NODELIST_BY_NAMESPACE:
+ dom_string_unref(list->data.ns.namespace);
+ dom_string_unref(list->data.ns.localname);
+ break;
+ case DOM_NODELIST_BY_NAME:
+ dom_string_unref(list->data.name);
+ break;
+ }
+
+ dom_node_unref(list->root);
+
+ dom_node_unref((struct dom_node *) list->owner);
+
+ /* Remove list from document */
+ dom_document_remove_nodelist(list->owner, list);
+
+ /* And destroy the list object */
+ dom_document_alloc(list->owner, list, 0);
+ }
+}
+
+/**
+ * Retrieve the length of a node list
+ *
+ * \param list List to retrieve length of
+ * \param length Pointer to location to receive length
+ * \return DOM_NO_ERR.
+ */
+dom_exception dom_nodelist_get_length(struct dom_nodelist *list,
+ unsigned long *length)
+{
+ UNUSED(list);
+ UNUSED(length);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Retrieve an item from a node list
+ *
+ * \param list The list to retrieve the item from
+ * \param index The list index to retrieve
+ * \param node Pointer to location to receive item
+ * \return DOM_NO_ERR.
+ *
+ * ::index is a zero-based index into ::list.
+ * ::index lies in the range [0, length-1]
+ *
+ * The returned node will have had its reference count increased. The client
+ * should unref the node once it has finished with it.
+ */
+dom_exception dom_nodelist_item(struct dom_nodelist *list,
+ unsigned long index, struct dom_node **node)
+{
+ UNUSED(list);
+ UNUSED(index);
+ UNUSED(node);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Match a nodelist instance against a set of nodelist creation parameters
+ *
+ * \param list List to match
+ * \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)
+ * \return true if list matches, false otherwise
+ */
+bool dom_nodelist_match(struct dom_nodelist *list, struct dom_node *root,
+ struct dom_string *tagname, struct dom_string *namespace,
+ struct dom_string *localname)
+{
+ if (list->root != root)
+ return false;
+
+ if (list->type == DOM_NODELIST_CHILDREN && tagname == NULL &&
+ namespace == NULL && localname == NULL) {
+ return true;
+ }
+
+ if (list->type == DOM_NODELIST_BY_NAME && tagname != NULL &&
+ namespace == NULL && localname == NULL) {
+ return (dom_string_cmp(list->data.name, tagname) == 0);
+ }
+
+ if (list->type == DOM_NODELIST_BY_NAMESPACE && tagname == NULL &&
+ namespace != NULL && localname != NULL) {
+ return (dom_string_cmp(list->data.ns.namespace,
+ namespace) == 0) &&
+ (dom_string_cmp(list->data.ns.localname,
+ localname) == 0);
+ }
+
+ return false;
+}
diff --git a/src/core/nodelist.h b/src/core/nodelist.h
new file mode 100644
index 0000000..1e96f8d
--- /dev/null
+++ b/src/core/nodelist.h
@@ -0,0 +1,31 @@
+/*
+ * 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>
+ */
+
+#ifndef dom_internal_nodelist_h_
+#define dom_internal_nodelist_h_
+
+#include <stdbool.h>
+
+#include <dom/core/nodelist.h>
+
+struct dom_document;
+struct dom_node;
+struct dom_nodelist;
+struct dom_string;
+
+/* Create a nodelist */
+dom_exception dom_nodelist_create(struct dom_document *doc,
+ struct dom_node *root, struct dom_string *tagname,
+ struct dom_string *namespace, struct dom_string *localname,
+ struct dom_nodelist **list);
+
+/* Match a nodelist instance against a set of nodelist creation parameters */
+bool dom_nodelist_match(struct dom_nodelist *list, struct dom_node *root,
+ struct dom_string *tagname, struct dom_string *namespace,
+ struct dom_string *localname);
+
+#endif
diff --git a/src/core/string.c b/src/core/string.c
index a60fa94..d73ce8d 100644
--- a/src/core/string.c
+++ b/src/core/string.c
@@ -217,3 +217,57 @@ dom_exception dom_string_get_data(struct dom_string *str,
return DOM_NO_ERR;
}
+
+/**
+ * Case sensitively compare two DOM strings
+ *
+ * \param s1 The first string to compare
+ * \param s2 The second string to compare
+ * \return 0 if strings match, non-0 otherwise
+ */
+int dom_string_cmp(struct dom_string *s1, struct dom_string *s2)
+{
+ const uint8_t *d1, *d2;
+ size_t l1, l2;
+ dom_exception err;
+
+ err = dom_string_get_data(s1, &d1, &l1);
+ if (err != DOM_NO_ERR)
+ return 1; /* arbitrary */
+
+ err = dom_string_get_data(s2, &d2, &l2);
+ if (err != DOM_NO_ERR)
+ return 1; /* arbitrary */
+
+ if (l1 != l2)
+ return 1; /* arbitrary */
+
+ return strncmp((const char *) d1, (const char *) d2, l1);
+}
+
+/**
+ * Case insensitively compare two DOM strings
+ *
+ * \param s1 The first string to compare
+ * \param s2 The second string to compare
+ * \return 0 if strings match, non-0 otherwise
+ */
+int dom_string_icmp(struct dom_string *s1, struct dom_string *s2)
+{
+ const uint8_t *d1, *d2;
+ size_t l1, l2;
+ dom_exception err;
+
+ err = dom_string_get_data(s1, &d1, &l1);
+ if (err != DOM_NO_ERR)
+ return 1; /* arbitrary */
+
+ err = dom_string_get_data(s2, &d2, &l2);
+ if (err != DOM_NO_ERR)
+ return 1; /* arbitrary */
+
+ if (l1 != l2)
+ return 1; /* arbitrary */
+
+ return strncasecmp((const char *) d1, (const char *) d2, l1);
+}