summaryrefslogtreecommitdiff
path: root/src/dom.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dom.c')
-rw-r--r--src/dom.c3145
1 files changed, 3145 insertions, 0 deletions
diff --git a/src/dom.c b/src/dom.c
new file mode 100644
index 0000000..714d305
--- /dev/null
+++ b/src/dom.c
@@ -0,0 +1,3145 @@
+/* domc document object model library in c
+ * Copyright (c) 2001 Michael B. Allen <mba2000 ioplex.com>
+ *
+ * The MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* dom.c - document object model interface
+ */
+
+#include <string.h>
+#include <wchar.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "dom.h"
+
+#if defined(__sparc__)
+
+#define NL "\n"
+#define HAVE_ENCDEC 0
+#define HAVE_STRDUP 1
+#define HAVE_STRNLEN 0
+#define HAVE_EXPAT 195
+#define HAVE_MBSTATE 0
+#define HAVE_WCWIDTH 1
+#define HAVE_SNPRINTF 1
+#define HAVE_VARMACRO 1
+#define HAVE_LANGINFO 1
+#define HAVE_X11_KEYSYMS 0
+
+#elif defined(_WIN32)
+
+#define NL "\r\n"
+#define HAVE_ENCDEC 0
+#define HAVE_STRDUP 1
+#define HAVE_STRNLEN 0
+#define HAVE_EXPAT 195
+#define HAVE_MBSTATE 0
+#define HAVE_WCWIDTH 0
+#define HAVE_SNPRINTF 0
+#define HAVE_VARMACRO 0
+#define HAVE_LANGINFO 0
+#define HAVE_X11_KEYSYMS 0
+
+#else
+
+#define NL "\n"
+#define HAVE_ENCDEC 0
+#define HAVE_STRDUP 1
+#define HAVE_STRNLEN 1
+#define HAVE_EXPAT 195
+#define HAVE_MBSTATE 1
+#define HAVE_WCWIDTH 1
+#define HAVE_SNPRINTF 1
+#define HAVE_VARMACRO 1
+#define HAVE_LANGINFO 1
+#define HAVE_X11_KEYSYMS 0
+
+#endif
+
+#if defined(__GNUC__)
+#if defined(MSGNO)
+
+#if defined(__GNUC__) && (__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 95))
+
+#define MSG(fmt, args...) _msgno_printf("%s:%u:%s: " fmt, \
+ __FILE__, __LINE__, __FUNCTION__, ## args)
+#define MNO(msgno) _msgno_printf("%s:%u:%s: %s", \
+ __FILE__, __LINE__, __FUNCTION__, msgno_msg(msgno))
+#define MNF(msgno, fmt, args...) _msgno_printf("%s:%u:%s: %s" fmt, \
+ __FILE__, __LINE__, __FUNCTION__, msgno_msg(msgno), ## args)
+
+#define PMSG(fmt, args...) (_msgno_buf_idx = sprintf(_msgno_buf, \
+ "%s:%u:%s: " fmt "\n", \
+ __FILE__, __LINE__, __FUNCTION__, ## args))
+#define PMNO(msgno) (_msgno_buf_idx = sprintf(_msgno_buf, \
+ "%s:%u:%s: %s\n", \
+ __FILE__, __LINE__, __FUNCTION__, msgno_msg(msgno)))
+#define PMNF(msgno, fmt, args...) (_msgno_buf_idx = sprintf(_msgno_buf, \
+ "%s:%u:%s: %s" fmt "\n", \
+ __FILE__, __LINE__, __FUNCTION__, msgno_msg(msgno), ## args))
+
+#define AMSG(fmt, args...) (_msgno_buf_idx += sprintf(_msgno_buf + _msgno_buf_idx, \
+ " %s:%u:%s: " fmt "\n", \
+ __FILE__, __LINE__, __FUNCTION__, ## args))
+#define AMNO(msgno) (_msgno_buf_idx += sprintf(_msgno_buf + _msgno_buf_idx, \
+ " %s:%u:%s: %s\n", \
+ __FILE__, __LINE__, __FUNCTION__, msgno_msg(msgno)))
+#define AMNF(msgno, fmt, args...) (_msgno_buf_idx += sprintf(_msgno_buf + _msgno_buf_idx, \
+ " %s:%u:%s: %s" fmt "\n", \
+ __FILE__, __LINE__, __FUNCTION__, msgno_msg(msgno), ## args))
+
+#else
+
+#define MSG(fmt, args...) _msgno_printf("%s:%u: " fmt "\n", \
+ __FILE__, __LINE__ , ## args)
+#define MNO(msgno) _msgno_printf("%s:%u: %s\n", \
+ __FILE__, __LINE__, msgno_msg(msgno))
+#define MNF(msgno, fmt, args...) _msgno_printf("%s:%u: %s" fmt "\n", \
+ __FILE__, __LINE__, msgno_msg(msgno) , ## args)
+
+#define PMSG(fmt, args...) (_msgno_buf_idx = sprintf(_msgno_buf, \
+ "%s:%u: " fmt "\n", __FILE__, __LINE__ , ## args))
+#define PMNO(msgno) (_msgno_buf_idx = sprintf(_msgno_buf, \
+ "%s:%u: %s\n", __FILE__, __LINE__, msgno_msg(msgno)))
+#define PMNF(msgno, fmt, args...) (_msgno_buf_idx = sprintf(_msgno_buf, \
+ "%s:%u: %s" fmt "\n", __FILE__, __LINE__, msgno_msg(msgno) , ## args))
+
+#define AMSG(fmt, args...) (_msgno_buf_idx += sprintf(_msgno_buf + _msgno_buf_idx, \
+ " %s:%u: "fmt"\n", __FILE__, __LINE__ , ## args))
+#define AMNO(msgno) (_msgno_buf_idx += sprintf(_msgno_buf + _msgno_buf_idx, \
+ " %s:%u: %s\n", __FILE__, __LINE__, msgno_msg(msgno)))
+#define AMNF(msgno, fmt, args...) (_msgno_buf_idx += sprintf(_msgno_buf + _msgno_buf_idx, \
+ " %s:%u: %s" fmt "\n", __FILE__, __LINE__, msgno_msg(msgno) , ## args))
+
+#endif
+#else
+
+#define MSG(fmt, args...)
+#define MNO(msgno)
+#define MNF(msgno, fmt, args...)
+#define PMSG(fmt, args...)
+#define PMNO(msgno)
+#define PMNF(msgno, fmt, args...)
+#define AMSG(fmt, args...)
+#define AMNO(msgno)
+#define AMNF(msgno, fmt, args...)
+
+#endif
+#else
+#undef MSG
+#if defined(MSGNO)
+
+#define MSG msgno_hdlr
+#define MNO msgno_hdlr_mno
+#define MNF msgno_hdlr_mnf
+#define PMSG msgno_hdlr
+#define PMNO msgno_hdlr_mno
+#define PMNF msgno_hdlr_mnf
+#define AMSG msgno_hdlr
+#define AMNO msgno_hdlr_mno
+#define AMNF msgno_hdlr_mnf
+
+#else
+
+#define MSG msgno_noop_msg
+#define MNO msgno_noop_mno
+#define MNF msgno_noop_mnf
+#define PMSG msgno_noop_msg
+#define PMNO msgno_noop_mno
+#define PMNF msgno_noop_mnf
+#define AMSG msgno_noop_msg
+#define AMNO msgno_noop_mno
+#define AMNF msgno_noop_mnf
+
+#endif
+#endif
+
+#ifndef MSGNO_NUM_LISTS
+#define MSGNO_NUM_LISTS 16
+#endif
+#ifndef MSGNO_BUFSIZ
+#define MSGNO_BUFSIZ 1024
+#endif
+
+#define NULL_POINTER_ERR _builtin_codes[0].msgno
+
+struct msgno_entry _builtin_codes[2] = {
+ { (1 << 16), "A parameter was NULL" },
+ { 0, NULL }
+};
+
+static struct tbl_entry {
+ struct msgno_entry *list;
+ unsigned int num_msgs;
+} list_tbl[MSGNO_NUM_LISTS] = {
+ { _builtin_codes, 1 }
+};
+
+static unsigned int next_tbl_idx = 1;
+
+const char *
+msgno_msg(int msgno)
+{
+ struct tbl_entry *te;
+ unsigned int i;
+
+ i = msgno >> 16;
+ if (i == 0) {
+ return strerror(msgno);
+ } else if (i >= MSGNO_NUM_LISTS ||
+ (te = list_tbl + (i - 1)) == NULL) {
+ return "No such msgno list";
+ }
+
+ for (i = 0; i < te->num_msgs; i++) {
+ if (te->list[i].msgno == msgno) {
+ return te->list[i].msg;
+ }
+ }
+
+ return "No such message in msgno list";
+}
+
+int
+msgno_add_codes(struct msgno_entry *list)
+{
+ struct tbl_entry *te;
+ int next_msgno = 0, hi_bits;
+
+ if (list == NULL || list->msg == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (next_tbl_idx == MSGNO_NUM_LISTS) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ for (te = list_tbl + 1; te->list; te++) {
+ if (te->list == list) {
+ return 0; /* already in list_tbl */
+ }
+ }
+
+ hi_bits = (next_tbl_idx + 1) << 16;
+ te->list = list;
+ while (list->msg) {
+ if ((list->msgno & 0xFFFF0000)) {
+ te->list = NULL;
+ errno = ERANGE;
+ return -1;
+ }
+ if (list->msgno == 0) {
+ list->msgno = hi_bits | next_msgno++;
+ } else if (list->msgno >= next_msgno) {
+ next_msgno = list->msgno + 1;
+ list->msgno = hi_bits | list->msgno;
+ } else {
+ te->list = NULL;
+ errno = ERANGE;
+ return -1;
+ }
+ te->num_msgs++;
+ list++;
+ }
+ next_tbl_idx++;
+
+ return 0;
+}
+
+
+int
+mbslen(const char *src)
+{
+ return mbsnlen(src, -1, -1);
+}
+int
+mbsnlen(const char *src, size_t sn, int cn)
+{
+ wchar_t ucs;
+ int count, w;
+ size_t n;
+#if HAVE_MBSTATE > 0
+ mbstate_t ps;
+#endif
+
+ ucs = 1;
+ count = 0;
+
+ if (sn > INT_MAX) {
+ sn = INT_MAX;
+ }
+ if (cn < 0) {
+ cn = INT_MAX;
+ }
+
+#if HAVE_MBSTATE
+ memset(&ps, 0, sizeof(ps));
+ while (ucs && (n = mbrtowc(&ucs, src, sn, &ps)) != (size_t)-2) {
+#else
+ while (ucs && sn > 0) {
+ n = mbtowc(&ucs, src, sn);
+#endif
+ if (n == (size_t)-1) {
+ PMNO(errno);
+ return -1;
+ }
+ if ((w = wcwidth(ucs)) == -1) {
+ w = 1;
+ }
+ if (w > cn) {
+ break;
+ }
+ cn -= w;
+ sn -= n;
+ src += n;
+ count += w;
+ }
+
+ return count;
+}
+
+size_t
+mbsnsize(const char *src, size_t sn, int cn)
+{
+ wchar_t ucs;
+ int w;
+ size_t tot, n;
+#if HAVE_MBSTATE > 0
+ mbstate_t ps;
+#endif
+
+ tot = n = 0;
+ ucs = 1;
+
+ if (sn > INT_MAX) {
+ sn = INT_MAX;
+ }
+ if (cn < 0) {
+ cn = INT_MAX;
+ }
+
+#if HAVE_MBSTATE
+ memset(&ps, 0, sizeof(ps));
+ while (ucs && sn > 0 && (n = mbrtowc(&ucs, src, sn, &ps)) != (size_t)-2 && n) {
+#else
+ while (ucs && sn > 0 && (n = mbtowc(&ucs, src, sn))) {
+#endif
+ if (n == (size_t)-1) {
+ PMNO(errno);
+ return -1;
+ }
+ if ((w = wcwidth(ucs)) == -1) {
+ w = 1;
+ }
+ if (w > cn) {
+ break;
+ }
+ cn -= w;
+ sn -= n;
+ src += n;
+ tot += n;
+ }
+
+ return tot;
+}
+
+size_t
+mbssize(const char *src)
+{
+ return mbsnsize(src, -1, -1);
+}
+
+static char *
+mbsnoff(char *src, int off, size_t sn)
+{
+ wchar_t ucs;
+ size_t n;
+ int w;
+#if HAVE_MBSTATE > 0
+ mbstate_t ps;
+#endif
+
+ if (off == 0) {
+ return src;
+ }
+
+ if (sn > INT_MAX) {
+ sn = 0xFFFF;
+ }
+ if (off < 0) {
+ off = 0xFFFF;
+ }
+
+#if HAVE_MBSTATE > 0
+ memset(&ps, 0, sizeof(ps));
+ while (sn > 0 && (n = mbrtowc(&ucs, src, sn, &ps)) != (size_t)-2) {
+#else
+ while (sn > 0) {
+ n = mbtowc(&ucs, src, sn);
+#endif
+ if (n == (size_t)-1) {
+ PMNF(errno, "src=[%s]", mbstoax(src, sn, 1));
+ return NULL;
+ }
+ if (n == 0 || (w = wcwidth(ucs)) != 0) {
+ w = 1;
+ }
+ if (w > off) {
+ break;
+ }
+ if (w) off--;
+ sn -= n;
+ src += n ? n : 1;
+ }
+
+ return src;
+}
+
+
+static char *
+mbsoff(char *src, int off)
+{
+ return mbsnoff(src, off, -1);
+}
+
+static char *
+mbsndup(const char *src, size_t sn, int cn)
+{
+ size_t n;
+ char *dst;
+
+ if (src == NULL) {
+ errno = EINVAL;
+ PMNO(errno);
+ return NULL;
+ }
+ if ((n = mbsnsize(src, sn, cn)) == (size_t)-1) {
+ AMSG("");
+ return NULL;
+ }
+ if ((dst = malloc(n + 1)) == NULL) {
+ PMNO(errno);
+ return NULL;
+ }
+ memcpy(dst, src, n);
+ dst[n] = '\0';
+
+ return dst;
+}
+
+static char *
+mbsdup(const char *src)
+{
+ return mbsndup(src, -1, -1);
+}
+
+#if defined(_WIN32)
+#include <Windows.h>
+
+typedef unsigned __int64 uint64_t;
+
+uint64_t
+timestamp(void)
+{
+ FILETIME ftime;
+ uint64_t ret;
+
+ GetSystemTimeAsFileTime(&ftime);
+
+ ret = ftime.dwHighDateTime;
+ ret <<= 32Ui64;
+ ret |= ftime.dwLowDateTime;
+
+ return ret;
+}
+
+#else
+#include <sys/time.h>
+
+uint64_t
+timestamp(void)
+{
+ struct timeval tval;
+
+ if (gettimeofday(&tval, NULL) < 0) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ return 1;
+ }
+ return tval.tv_sec * 1000LL + tval.tv_usec / 1000;
+}
+
+#endif
+
+/* Forward references for node.c
+ */
+
+DOM_Node *Document_createNode(DOM_Document *doc, unsigned short nodeType);
+NodeEntry *NodeList_remove(DOM_NodeList *nl, DOM_Node *oldChild);
+NodeEntry *NodeList_append(DOM_NodeList *nl, DOM_Node *newChild);
+DOM_NodeList *Document_createNodeList(DOM_Document *doc);
+DOM_NamedNodeMap *Document_createNamedNodeMap(DOM_Document *doc);
+
+/* Forward references for events.c
+ */
+
+void updateCommonParent(DOM_Node *node);
+
+/* DOM_Exception
+ */
+
+struct msgno_entry dom_codes[] = {
+ { 1, "The index specified was out of range" },
+ { 0, "The text size is out of range" },
+ { 0, "The request violated tree hierarchy constraints" },
+ { 0, "The document context is invalid" },
+ { 0, "An inappropriate character was encountered" },
+ { 0, "The node does not support the addition of data" },
+ { 0, "No modification allowed" },
+ { 0, "The specified node was not found" },
+ { 0, "The requested operation is not supported" },
+ { 0, "The attribute is being used elsewhere" },
+ { 0, "An XML parser error occured" },
+ { 0, "Failed to create DOM object" },
+ { 0, "Character encoding error" },
+ { 0, "The event type was not specified by initialization" },
+ { 0, "A filtered list cannot be modified" },
+ { 0, NULL }
+};
+
+int _exception = 0;
+
+int *
+_DOM_Exception(void)
+{
+ return &_exception;
+}
+
+unsigned short child_matrix[] = {
+ 0x00dd, /* DOM_ELEMENT_NODE 1 */
+ 0x0014, /* DOM_ATTRIBUTE_NODE 2 */
+ 0x0000, /* DOM_TEXT_NODE 3 */
+ 0x0000, /* DOM_CDATA_SECTION_NODE 4 */
+ 0x00dd, /* DOM_ENTITY_REFERENCE_NODE 5 */
+ 0x00dd, /* DOM_ENTITY_NODE 6 */
+ 0x0000, /* DOM_PROCESSING_INSTRUCTION_NODE 7 */
+ 0x0000, /* DOM_COMMENT_NODE 8 */
+ 0x02c1, /* DOM_DOCUMENT_NODE 9 */
+ 0x0820, /* DOM_DOCUMENT_TYPE_NODE 10 */
+ 0x00dd, /* DOM_DOCUMENT_FRAGMENT_NODE 11 */
+ 0x0000 /* DOM_NOTATION_NODE 12 */
+};
+
+const char *node_names[] = {
+ "No such node type",
+ "DOM_ELEMENT_NODE",
+ "DOM_ATTRIBUTE_NODE",
+ "DOM_TEXT_NODE",
+ "DOM_CDATA_SECTION_NODE",
+ "DOM_ENTITY_REFERENCE_NODE",
+ "DOM_ENTITY_NODE",
+ "DOM_PROCESSING_INSTRUCTION_NODE",
+ "DOM_COMMENT_NODE",
+ "DOM_DOCUMENT_NODE",
+ "DOM_DOCUMENT_TYPE_NODE",
+ "DOM_DOCUMENT_FRAGMENT_NODE",
+ "DOM_NOTATION_NODE"
+};
+
+/* DOM_Implementation and DOM_ImplementationLS
+ */
+
+int
+DOM_Implementation_hasFeature(DOM_String *feature, DOM_String *version)
+{
+ feature = NULL; version = NULL;
+ return 0;
+}
+DOM_DocumentType *
+DOM_Implementation_createDocumentType(DOM_String *qualifiedName,
+ DOM_String *publicId, DOM_String *systemId)
+{
+ DOM_DocumentType *doctype;
+ DOM_NamedNodeMap *entities;
+ DOM_NamedNodeMap *notations;
+
+ if ((doctype = Document_createNode(NULL, DOM_DOCUMENT_TYPE_NODE)) == NULL) {
+ AMSG("");
+ return NULL;
+ }
+ if ((doctype->nodeName = doctype->u.DocumentType.name = strdup(qualifiedName)) == NULL ||
+ (publicId && ((doctype->u.DocumentType.publicId = strdup(publicId))) == NULL) ||
+ (systemId && ((doctype->u.DocumentType.systemId = strdup(systemId))) == NULL)) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ DOM_Document_destroyNode(NULL, doctype);
+ return NULL;
+ }
+ if ((entities = Document_createNamedNodeMap(NULL)) == NULL ||
+ (notations = Document_createNamedNodeMap(NULL)) == NULL) {
+ AMNO(DOM_CREATE_FAILED);
+ DOM_Document_destroyNode(NULL, doctype);
+ return NULL;
+ }
+ entities->filter = DOM_ENTITY_NODE;
+ notations->filter = DOM_NOTATION_NODE;
+ entities->list = notations->list = doctype->childNodes;
+ doctype->u.DocumentType.entities = entities;
+ doctype->u.DocumentType.notations = notations;
+
+ return doctype;
+}
+DOM_Document *
+DOM_Implementation_createDocument(DOM_String *namespaceURI,
+ DOM_String *qualifiedName, DOM_DocumentType *doctype)
+{
+ DOM_Document *doc;
+ DOM_Element *el;
+
+ namespaceURI = NULL;
+
+ msgno_add_codes(dom_codes);
+
+ if ((doc = Document_createNode(NULL, DOM_DOCUMENT_NODE)) == NULL) {
+ AMSG("");
+ return NULL;
+ }
+ doc->nodeName = "#document";
+ if (doctype) {
+ DOM_Node_appendChild(doc, doctype);
+ }
+
+ if (qualifiedName && *qualifiedName) {
+ if ((el = DOM_Document_createElement(doc, qualifiedName)) == NULL) {
+ AMSG("");
+ DOM_Document_destroyNode(doc, doc);
+ return NULL;
+ }
+ DOM_Node_appendChild(doc, el);
+ }
+
+ return doc;
+}
+
+DOM_String *
+DOM_Element_getAttribute(const DOM_Element *element, const DOM_String *name)
+{
+ DOM_Node *node;
+ DOM_String *r = NULL;
+
+ if (element && name && element->attributes) {
+ if ((node = DOM_NamedNodeMap_getNamedItem(element->attributes, name))) {
+ if ((r = strdup(node->nodeValue)) == NULL) {
+ AMSG("");
+ return NULL;
+ }
+ } else if ((r = strdup("")) == NULL) {
+ PMNO(errno);
+ return NULL;
+ }
+ }
+
+ return r;
+}
+void
+DOM_Element_setAttribute(DOM_Element *element,
+ const DOM_String *name, const DOM_String *value)
+{
+ DOM_Attr *attr;
+ DOM_String *prevValue;
+ unsigned short attrChange;
+ DOM_MutationEvent evt;
+
+ if (element == NULL || name == NULL ||
+ value == NULL || element->attributes == NULL) {
+ return;
+ }
+
+ attr = DOM_NamedNodeMap_getNamedItem(element->attributes, name);
+ if (attr) {
+ prevValue = attr->nodeValue;
+ attrChange = DOM_MUTATION_EVENT_MODIFICATION;
+ if ((attr->nodeValue = attr->u.Attr.value = strdup(value)) == NULL) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ DOM_Document_destroyNode(attr->ownerDocument, attr);
+ return;
+ }
+ } else {
+ prevValue = NULL;
+ attrChange = DOM_MUTATION_EVENT_ADDITION;
+ if ((attr = DOM_Document_createAttribute(element->ownerDocument, name)) == NULL) {
+ AMNO(DOM_CREATE_FAILED);
+ return;
+ }
+ free(attr->nodeValue);
+ if ((attr->nodeValue = attr->u.Attr.value = strdup(value)) == NULL) {
+ DOM_Exception = errno;
+ DOM_Document_destroyNode(attr->ownerDocument, attr);
+ return;
+ }
+ DOM_NamedNodeMap_setNamedItem(element->attributes, attr);
+ }
+
+ DOM_MutationEvent_initMutationEvent(&evt, "DOMAttrModified", 1, 0,
+ attr, prevValue, attr->nodeValue, attr->nodeName, attrChange);
+/*MSG("attr=%p,prevValue=[%s],newValue=[%s],attrName=%s", attr, prevValue, attr->nodeValue, attr->nodeName);
+ */
+ DOM_EventTarget_dispatchEvent(element, &evt);
+
+ updateCommonParent(element->parentNode);
+
+ free(prevValue);
+}
+void
+DOM_Element_removeAttribute(DOM_Element *element, const DOM_String *name)
+{
+ DOM_Attr *attr;
+ DOM_MutationEvent evt;
+
+ if (element == NULL || name == NULL) {
+ return;
+ }
+
+ attr = DOM_NamedNodeMap_removeNamedItem(element->attributes, name);
+
+ /* removeAttribute doesn't throw exceptions on NOT_FOUND_ERR */
+ if (DOM_Exception == DOM_NOT_FOUND_ERR)
+ DOM_Exception = 0;
+
+ if (attr) {
+ DOM_MutationEvent_initMutationEvent(&evt, "DOMAttrModified", 1, 0,
+ attr, attr->nodeValue, NULL, attr->nodeName, DOM_MUTATION_EVENT_REMOVAL);
+ DOM_EventTarget_dispatchEvent(element, &evt);
+
+ updateCommonParent(element->parentNode);
+
+ DOM_Document_destroyNode(attr->ownerDocument, attr);
+ }
+}
+DOM_Attr *
+DOM_Element_getAttributeNode(const DOM_Element *element, const DOM_String *name)
+{
+ if (element && name) {
+ return DOM_NamedNodeMap_getNamedItem(element->attributes, name);
+ }
+ return NULL;
+}
+DOM_Attr *
+DOM_Element_setAttributeNode(DOM_Element *element, DOM_Attr *newAttr)
+{
+ DOM_Node *attr;
+ DOM_MutationEvent evt;
+
+ if (element == NULL || newAttr == NULL) {
+ return NULL;
+ }
+ if (element->ownerDocument != newAttr->ownerDocument) {
+ DOM_Exception = DOM_WRONG_DOCUMENT_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+
+ attr = DOM_NamedNodeMap_setNamedItem(element->attributes, newAttr);
+
+ if (attr) {
+ DOM_MutationEvent_initMutationEvent(&evt, "DOMAttrModified", 1, 0,
+ attr, attr->nodeValue, NULL, attr->nodeName, DOM_MUTATION_EVENT_REMOVAL);
+ DOM_EventTarget_dispatchEvent(element, &evt);
+ }
+
+ DOM_MutationEvent_initMutationEvent(&evt, "DOMAttrModified", 1, 0,
+ newAttr, NULL, newAttr->nodeValue, newAttr->nodeName, DOM_MUTATION_EVENT_ADDITION);
+ DOM_EventTarget_dispatchEvent(element, &evt);
+
+ updateCommonParent(element->parentNode);
+
+ return attr;
+}
+DOM_Attr *
+DOM_Element_removeAttributeNode(DOM_Element *element, DOM_Attr *oldAttr)
+{
+ DOM_MutationEvent evt;
+
+ if (element == NULL || oldAttr == NULL ||
+ NodeList_remove(element->attributes, oldAttr) == NULL) {
+ DOM_Exception = DOM_NOT_FOUND_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ DOM_MutationEvent_initMutationEvent(&evt, "DOMAttrModified", 1, 0,
+ oldAttr, oldAttr->nodeValue, NULL, oldAttr->nodeName, DOM_MUTATION_EVENT_REMOVAL);
+ DOM_EventTarget_dispatchEvent(element, &evt);
+
+ updateCommonParent(element->parentNode);
+
+ return oldAttr;
+}
+
+static void
+getElementsPreorder(DOM_NodeList *list, DOM_Node *node, const DOM_String *tagname)
+{
+ DOM_Node *n;
+
+ if (list && node && node->nodeType == DOM_ELEMENT_NODE && tagname) {
+ if ((tagname[0] == '*' && tagname[1] == '\0') ||
+ strcoll(tagname, node->nodeName) == 0) {
+ NodeList_append(list, node);
+ }
+ for (n = node->firstChild; n != NULL; n = n->nextSibling) {
+ getElementsPreorder(list, n, tagname);
+ }
+ }
+}
+
+DOM_NodeList *
+DOM_Element_getElementsByTagName(DOM_Element *element, const DOM_String *name)
+{
+ DOM_NodeList *list;
+ DOM_Node *n;
+
+ if (element && element->nodeType == DOM_ELEMENT_NODE && name &&
+ (list = Document_createNodeList(element->ownerDocument))) {
+ for (n = element->firstChild; n != NULL; n = n->nextSibling) {
+ getElementsPreorder(list, n, name);
+ }
+ return list;
+ }
+
+ return NULL;
+}
+void
+DOM_Element_normalize(DOM_Element *element)
+{
+ DOM_Node *node;
+ DOM_Text *last = NULL;
+
+ if (element) {
+ for (node = element->firstChild; node != NULL; node = node->nextSibling) {
+ if (node->nodeType == DOM_TEXT_NODE) {
+ if (last) {
+ DOM_CharacterData_insertData(node, 0, last->nodeValue);
+ DOM_Node_removeChild(element, last);
+ DOM_Document_destroyNode(last->ownerDocument, last);
+ }
+ last = node;
+ } else {
+ last = NULL;
+ DOM_Element_normalize(node);
+ }
+ if (DOM_Exception) {
+ AMSG("");
+ return;
+ }
+ }
+ }
+}
+
+DOM_String *
+DOM_CharacterData_substringData(DOM_CharacterData *data,
+ int offset, int count)
+{
+ DOM_String *sub;
+ int dlen;
+
+ if (data == NULL) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ if (offset < 0 || offset > (dlen = data->u.CharacterData.length) || count < 0) {
+ DOM_Exception = DOM_INDEX_SIZE_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ if (count > (dlen - offset)) {
+ count = dlen - offset;
+ }
+ if ((sub = mbsoff(data->nodeValue, offset)) == NULL ||
+ (sub = mbsndup(sub, -1, count)) == NULL) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ return sub;
+}
+void
+DOM_CharacterData_appendData(DOM_CharacterData *data, const DOM_String *arg)
+{
+ DOM_String *str, *prevValue;
+ size_t dsize, asize;
+ DOM_MutationEvent evt;
+
+ if (data == NULL) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNO(DOM_Exception);
+ return;
+ }
+ if (arg) {
+ dsize = mbssize(data->nodeValue);
+ asize = mbssize(arg);
+ if ((str = malloc(dsize + asize + 1)) == NULL) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ return;
+ }
+
+ memcpy(str, data->nodeValue, dsize);
+ memcpy(str + dsize, arg, asize);
+ str[dsize + asize] = '\0';
+
+ prevValue = data->nodeValue;
+ data->nodeValue = data->u.CharacterData.data = str;
+ data->u.CharacterData.length += mbslen(arg);
+
+ DOM_MutationEvent_initMutationEvent(&evt, "DOMCharacterDataModified", 1, 0,
+ NULL, prevValue, data->nodeValue, NULL, 0);
+ DOM_EventTarget_dispatchEvent(data, &evt);
+
+ updateCommonParent(data->parentNode);
+
+ free(prevValue);
+ }
+}
+void
+DOM_CharacterData_insertData(DOM_CharacterData *data,
+ int offset, const DOM_String *arg)
+{
+ DOM_String *str, *prevValue;
+ size_t dsize, asize, o;
+ DOM_MutationEvent evt;
+
+ if (data == NULL) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNO(DOM_Exception);
+ return;
+ }
+ if (offset < 0 || offset > data->u.CharacterData.length) {
+ DOM_Exception = DOM_INDEX_SIZE_ERR;
+ PMNO(DOM_Exception);
+ return;
+ }
+ if (arg) {
+ dsize = mbssize(data->nodeValue);
+ asize = mbssize(arg);
+ if ((str = malloc(dsize + asize + 1)) == NULL) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ return;
+ }
+
+ o = mbsoff(data->nodeValue, offset) - data->nodeValue;
+ memcpy(str, data->nodeValue, o);
+ memcpy(str + o, arg, asize);
+ memcpy(str + o + asize, data->nodeValue + o, dsize - o);
+ str[dsize + asize] = '\0';
+
+ prevValue = data->nodeValue;
+ data->nodeValue = data->u.CharacterData.data = str;
+ data->u.CharacterData.length += mbslen(arg);
+
+ DOM_MutationEvent_initMutationEvent(&evt, "DOMCharacterDataModified", 1, 0,
+ NULL, prevValue, data->nodeValue, NULL, 0);
+ DOM_EventTarget_dispatchEvent(data, &evt);
+
+ updateCommonParent(data->parentNode);
+
+ free(prevValue);
+ }
+}
+void
+DOM_CharacterData_deleteData(DOM_CharacterData *data,
+ int offset, int count)
+{
+ DOM_String *p1, *p2, *str, *prevValue;
+ int p1size, p2size, dlen;
+ DOM_MutationEvent evt;
+
+ if (data == NULL) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNO(DOM_Exception);
+ return;
+ }
+ if (offset < 0 || offset > (dlen = data->u.CharacterData.length)) {
+ DOM_Exception = DOM_INDEX_SIZE_ERR;
+ PMNO(DOM_Exception);
+ return;
+ }
+ if (count < 0 || (offset + count) > dlen) {
+ count = dlen - offset;
+ }
+
+ p1 = mbsoff(data->nodeValue, offset);
+ p1size = p1 - data->nodeValue;
+ p2 = mbsoff(p1, count);
+ p2size = strlen(p2);
+
+ if ((str = malloc(p1size + p2size + 1)) == NULL) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ return;
+ }
+ memcpy(str, data->nodeValue, p1size);
+ memcpy(str + p1size, p2, p2size);
+ str[p1size + p2size] = '\0';
+
+ prevValue = data->nodeValue;
+ data->nodeValue = data->u.CharacterData.data = str;
+ data->u.CharacterData.length = dlen - count;
+
+ DOM_MutationEvent_initMutationEvent(&evt, "DOMCharacterDataModified", 1, 0,
+ NULL, prevValue, data->nodeValue, NULL, 0);
+ DOM_EventTarget_dispatchEvent(data, &evt);
+
+ updateCommonParent(data->parentNode);
+
+ free(prevValue);
+}
+void
+DOM_CharacterData_replaceData(DOM_CharacterData *data, int offset, int count,
+ const DOM_String *arg)
+{
+ DOM_CharacterData_deleteData(data, offset, count);
+ DOM_CharacterData_insertData(data, offset, arg);
+}
+int
+DOM_CharacterData_getLength(DOM_CharacterData *data)
+{
+ return data ? data->u.CharacterData.length : 0;
+}
+
+DOM_Text *
+DOM_Text_splitText(DOM_Text *text, int offset)
+{
+ DOM_Text *node;
+
+ if (text == NULL || text->parentNode == NULL) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+
+ if (offset < 0 || offset > text->u.CharacterData.length) {
+ DOM_Exception = DOM_INDEX_SIZE_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ node = DOM_Document_createTextNode(text->ownerDocument,
+ mbsoff(text->nodeValue, offset));
+ if (node == NULL) {
+ AMNO(DOM_CREATE_FAILED);
+ return NULL;
+ }
+ DOM_CharacterData_deleteData(text, offset, -1);
+ DOM_Node_insertBefore(text->parentNode, node, text->nextSibling);
+
+ return node;
+}
+
+
+DOM_Element *
+DOM_Document_createElement(DOM_Document *doc, const DOM_String *tagName)
+{
+ DOM_Element *element;
+
+ element = Document_createNode(doc, DOM_ELEMENT_NODE);
+ if (element) {
+ if ((element->nodeName = element->u.Element.tagName = strdup(tagName)) == NULL) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ DOM_Document_destroyNode(doc, element);
+ return NULL;
+ }
+ if ((element->attributes = Document_createNamedNodeMap(doc)) == NULL) {
+ AMNO(DOM_CREATE_FAILED);
+ DOM_Document_destroyNode(doc, element);
+ return NULL;
+ }
+ element->attributes->_ownerElement = element; /* for Attr.ownerElement */
+ }
+
+ return element;
+}
+DOM_DocumentFragment *
+DOM_Document_createDocumentFragment(DOM_Document *doc)
+{
+ DOM_DocumentFragment *frag;
+
+ frag = Document_createNode(doc, DOM_DOCUMENT_FRAGMENT_NODE);
+ if (frag) {
+ frag->nodeName = "#document-fragment";
+ }
+
+ return frag;
+}
+DOM_Text *
+DOM_Document_createTextNode(DOM_Document *doc, const DOM_String *data)
+{
+ DOM_Text *text;
+
+ text = Document_createNode(doc, DOM_TEXT_NODE);
+ if (text) {
+ text->nodeName = "#text";
+ text->nodeValue = text->u.CharacterData.data = strdup(data);
+ if (text->nodeValue == NULL) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ DOM_Document_destroyNode(doc, text);
+ return NULL;
+ }
+ text->u.CharacterData.length = mbslen(data);
+ }
+
+ return text;
+}
+DOM_Comment *
+DOM_Document_createComment(DOM_Document *doc, const DOM_String *data)
+{
+ DOM_Comment *comment;
+
+ comment = Document_createNode(doc, DOM_COMMENT_NODE);
+ if (comment) {
+ comment->nodeName = "#comment";
+ comment->nodeValue = comment->u.CharacterData.data = strdup(data);
+ if (comment->nodeValue == NULL) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ DOM_Document_destroyNode(doc, comment);
+ return NULL;
+ }
+ comment->u.CharacterData.length = mbslen(data);
+ }
+
+ return comment;
+}
+DOM_CDATASection *
+DOM_Document_createCDATASection(DOM_Document *doc, const DOM_String *data)
+{
+ DOM_CDATASection *cdata;
+
+ cdata = Document_createNode(doc, DOM_CDATA_SECTION_NODE);
+ if (cdata) {
+ cdata->nodeName = "#cdata-section";
+ cdata->nodeValue = cdata->u.CharacterData.data = strdup(data);
+ if (cdata->u.CharacterData.data == NULL) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ DOM_Document_destroyNode(doc, cdata);
+ return NULL;
+ }
+ cdata->u.CharacterData.length = mbslen(data);
+ }
+
+ return cdata;
+}
+DOM_ProcessingInstruction *
+DOM_Document_createProcessingInstruction(DOM_Document *doc,
+ const DOM_String *target, const DOM_String *data)
+{
+ DOM_ProcessingInstruction *pi;
+
+ pi = Document_createNode(doc, DOM_PROCESSING_INSTRUCTION_NODE);
+ if (pi) {
+ pi->nodeName = pi->u.ProcessingInstruction.target = strdup(target);
+ pi->nodeValue = pi->u.ProcessingInstruction.data = strdup(data);
+ if (pi->nodeName == NULL || pi->nodeValue == NULL) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ DOM_Document_destroyNode(doc, pi);
+ return NULL;
+ }
+ }
+
+ return pi;
+}
+DOM_Attr *
+DOM_Document_createAttribute(DOM_Document *doc, const DOM_String *name)
+{
+ DOM_Attr *attr;
+
+ attr = Document_createNode(doc, DOM_ATTRIBUTE_NODE);
+ if (attr) {
+ attr->nodeName = attr->u.Attr.name = strdup(name);
+ attr->nodeValue = attr->u.Attr.value = strdup("");
+ attr->u.Attr.specified = 1;
+ if (attr->nodeName == NULL || attr->nodeValue == NULL) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ DOM_Document_destroyNode(doc, attr);
+ return NULL;
+ }
+ }
+
+ return attr;
+}
+DOM_EntityReference *
+DOM_Document_createEntityReference(DOM_Document *doc, const DOM_String *name)
+{
+ DOM_EntityReference *eref;
+
+ eref = Document_createNode(doc, DOM_ENTITY_REFERENCE_NODE);
+ if (eref && (eref->nodeName = strdup(name)) == NULL) {
+ DOM_Document_destroyNode(doc, eref);
+ return NULL;
+ }
+
+ return eref;
+}
+
+DOM_NodeList *
+DOM_Document_getElementsByTagName(DOM_Document *doc, const DOM_String *tagname)
+{
+ DOM_NodeList *list;
+
+ if (doc && doc->nodeType == DOM_DOCUMENT_NODE &&
+ tagname && (list = Document_createNodeList(doc))) {
+ getElementsPreorder(list, doc->u.Document.documentElement, tagname);
+ return list;
+ }
+
+ return NULL;
+}
+DOM_DocumentType *
+DOM_Document_getDoctype(DOM_Document *doc)
+{
+ return doc ? doc->u.Document.doctype : NULL;
+}
+DOM_Element *
+DOM_Document_getDocumentElement(DOM_Document *doc)
+{
+ return doc ? doc->u.Document.documentElement : NULL;
+}
+
+/* Temporary functions */
+
+void
+printNode(DOM_Node *node, int indent)
+{
+ DOM_Node *n;
+ int i;
+
+ if (node == NULL) {
+ printf("node was null\n");
+ return;
+ }
+
+ for (i = 0; i < indent; i++) {
+ printf(" ");
+ }
+
+ printf("%s: %s=%s\n", node_names[node->nodeType], node->nodeName, node->nodeValue);
+
+ if (node->nodeType == DOM_ELEMENT_NODE && node->attributes->length) {
+ printf(" ");
+ n = DOM_NamedNodeMap_item(node->attributes, 0);
+ printf("%s=%s", n->nodeName, n->nodeValue);
+ for (i = 1; i < node->attributes->length; i++) {
+ n = DOM_NamedNodeMap_item(node->attributes, i);
+ printf(",%s=%s", n->nodeName, n->nodeValue);
+ }
+ printf("\n");
+ for (i = 0; i < indent; i++) {
+ printf(" ");
+ }
+ }
+ for (n = node->firstChild; n != NULL; n = n->nextSibling) {
+ printNode(n, indent + 1);
+ }
+}
+void
+DOM_Node_printNode2(DOM_Node *node)
+{
+ printf("\n");
+ printNode(node, 0);
+}
+void
+DOM_Node_printNode(DOM_Node *node)
+{
+ if (node == NULL) {
+ printf("node was null\n");
+ return;
+ }
+
+ printf("\nnodeName=%s,nodeValue=%s,", node->nodeName, node->nodeValue);
+ printf("\n\ttype=%u", node->nodeType);
+ printf(",parentNode->nodeName=%s,firstChild->nodeName=%s", (node->parentNode == NULL ? "(null)" : node->parentNode->nodeName), (node->firstChild == NULL ? "(null)" : node->firstChild->nodeName));
+ printf(",lastChild->nodeName=%s,\n\tchildNodes->length=%u", (node->lastChild == NULL ? "(null)" : node->lastChild->nodeName), (node->childNodes == NULL ? 0 : node->childNodes->length));
+ printf(",previousSibling->nodeName=%s,nextSibling->nodeName=%s,attributes->length=%u\n", (node->previousSibling == NULL ? "(null)" : node->previousSibling->nodeName), (node->nextSibling == NULL ? "(null)": node->nextSibling->nodeName), (node->attributes == NULL ? 0 : node->attributes->length));
+ fflush(stdout);
+}
+
+
+/* NamedNodeMap
+ */
+
+void
+DOM_Document_destroyNamedNodeMap(DOM_Document *doc, DOM_NamedNodeMap *map, int free_nodes)
+{
+ DOM_Document_destroyNodeList(doc, map, free_nodes);
+}
+DOM_NamedNodeMap *
+Document_createNamedNodeMap(DOM_Document *doc)
+{
+ return Document_createNodeList(doc);
+}
+
+DOM_Node *
+DOM_NamedNodeMap_getNamedItem(const DOM_NamedNodeMap *map, const DOM_String *name)
+{
+ NodeEntry *e;
+ unsigned short nodeType;
+
+ if (map && name) {
+ if (map->filter) {
+ nodeType = map->filter;
+ map = map->list;
+ for (e = map->first; e != NULL; e = e->next) {
+ if (e->node->nodeType == nodeType && strcoll(name, e->node->nodeName) == 0) {
+ return e->node;
+ }
+ }
+ } else {
+ for (e = map->first; e != NULL; e = e->next) {
+ if (strcoll(name, e->node->nodeName) == 0) {
+ return e->node;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+DOM_Node *
+DOM_NamedNodeMap_setNamedItem(DOM_NamedNodeMap *map, DOM_Node *arg)
+{
+ NodeEntry *e;
+
+ if (map && arg) {
+ if (map->filter) {
+ DOM_Exception = DOM_NO_MODIFICATION_ALLOWED_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ if (map->_ownerDocument != arg->ownerDocument) {
+ DOM_Exception = DOM_WRONG_DOCUMENT_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ if (arg->nodeType == DOM_ATTRIBUTE_NODE && arg->u.Attr.ownerElement &&
+ arg->u.Attr.ownerElement != map->_ownerElement) {
+ DOM_Exception = DOM_INUSE_ATTRIBUTE_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ for (e = map->first; e != NULL && strcoll(arg->nodeName, e->node->nodeName); e = e->next) {
+ ;
+ }
+ if (e) {
+ DOM_Node *tmp = e->node;
+ e->node = arg;
+ if (arg->nodeType == DOM_ATTRIBUTE_NODE) {
+ arg->u.Attr.ownerElement = map->_ownerElement;
+ tmp->u.Attr.ownerElement = NULL;
+ }
+ return tmp;
+ }
+ NodeList_append(map, arg);
+ }
+
+ return NULL;
+}
+DOM_Node *
+DOM_NamedNodeMap_removeNamedItem(DOM_NamedNodeMap *map, const DOM_String *name)
+{
+ NodeEntry *e;
+ DOM_Node *r = NULL;
+
+ if (map && name) {
+ if (map->filter) {
+ DOM_Exception = DOM_NO_MODIFICATION_ALLOWED_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ for (e = map->first; e != NULL; e = e->next) {
+ if (strcoll(name, e->node->nodeName) == 0 &&
+ NodeList_remove(map, e->node)) {
+ r = e->node;
+ free(e);
+ if (r->nodeType == DOM_ATTRIBUTE_NODE) {
+ r->u.Attr.ownerElement = NULL;
+ }
+ return r;
+ }
+ }
+ }
+
+ DOM_Exception = DOM_NOT_FOUND_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+}
+DOM_Node *
+DOM_NamedNodeMap_item(const DOM_NamedNodeMap *map, int index)
+{
+ return DOM_NodeList_item(map, index);
+}
+
+
+/* Forward references
+ */
+
+void DOM_Document_destroyNamedNodeMap(DOM_Document *doc, DOM_NamedNodeMap *nnm, int free_nodes);
+void updateCommonParent(DOM_Node *node);
+
+/* Node
+ */
+
+void
+DOM_Document_destroyNode(DOM_Document *doc, DOM_Node *node)
+{
+ if (node == NULL) {
+ return;
+ }
+
+ if (node->childNodes) {
+ DOM_Document_destroyNodeList(doc, node->childNodes, 1);
+ }
+ if (node->listeners) {
+ unsigned int i;
+
+ for (i = 0; i < node->listeners_len; i++) {
+ if (node->listeners[i]) {
+ free(node->listeners[i]->type);
+ free(node->listeners[i]);
+ }
+ }
+ free(node->listeners);
+ }
+
+ switch(node->nodeType) {
+ case DOM_ELEMENT_NODE:
+ DOM_Document_destroyNamedNodeMap(doc, node->attributes, 1);
+ free(node->nodeName);
+ break;
+ case DOM_TEXT_NODE:
+ case DOM_COMMENT_NODE:
+ case DOM_CDATA_SECTION_NODE:
+ free(node->nodeValue);
+ break;
+ case DOM_ATTRIBUTE_NODE:
+ free(node->nodeName);
+ free(node->nodeValue);
+ break;
+ case DOM_ENTITY_REFERENCE_NODE:
+ case DOM_ENTITY_NODE:
+ free(node->nodeName);
+ free(node->nodeValue);
+ free(node->u.Entity.publicId);
+ free(node->u.Entity.systemId);
+ free(node->u.Entity.notationName);
+ break;
+ case DOM_PROCESSING_INSTRUCTION_NODE:
+ free(node->nodeName);
+ free(node->nodeValue);
+ break;
+ case DOM_DOCUMENT_NODE:
+ free(node->u.Document.version);
+ free(node->u.Document.encoding);
+ break;
+ case DOM_DOCUMENT_TYPE_NODE:
+ DOM_Document_destroyNamedNodeMap(doc, node->u.DocumentType.entities, 0);
+ DOM_Document_destroyNamedNodeMap(doc, node->u.DocumentType.notations, 0);
+ free(node->u.DocumentType.publicId);
+ free(node->u.DocumentType.systemId);
+ free(node->nodeName);
+ break;
+ case DOM_NOTATION_NODE:
+ free(node->nodeName);
+ free(node->u.Notation.publicId);
+ free(node->u.Notation.systemId);
+ break;
+ }
+ free(node);
+}
+DOM_Node *
+Document_createNode(DOM_Document *doc, unsigned short nodeType)
+{
+ DOM_Node *node;
+
+ msgno_add_codes(dom_codes);
+
+ if (doc == NULL && nodeType != DOM_DOCUMENT_NODE && nodeType != DOM_DOCUMENT_TYPE_NODE) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNF(DOM_Exception, ": doc=NULL,nodeType=%u", nodeType);
+ return NULL;
+ }
+
+ node = calloc(sizeof *node, 1);
+ if (node == NULL) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+
+ node->nodeType = nodeType;
+ node->ownerDocument = doc;
+ switch (nodeType) {
+ case DOM_DOCUMENT_NODE:
+ case DOM_DOCUMENT_TYPE_NODE:
+ /* DocumentType doesn't really have children but we need to store DTD
+ * entries other than entities and notations */
+ case DOM_ELEMENT_NODE:
+ case DOM_ATTRIBUTE_NODE:
+ case DOM_ENTITY_REFERENCE_NODE:
+ case DOM_ENTITY_NODE:
+ case DOM_DOCUMENT_FRAGMENT_NODE:
+ node->childNodes = Document_createNodeList(doc);
+ if (node->childNodes == NULL) {
+ AMNO(DOM_CREATE_FAILED);
+ DOM_Document_destroyNode(doc, node);
+ return NULL;
+ }
+ }
+
+ return node;
+}
+
+static int
+_isAncestor(DOM_Node *node, DOM_Node *parent)
+{
+ DOM_Node *p;
+
+ for (p = parent; p; p = p->parentNode) {
+ if (p == node) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void
+dispatchEventPreorder(DOM_Node *node, DOM_MutationEvent *evt)
+{
+ DOM_Node *n;
+
+ DOM_EventTarget_dispatchEvent(node, evt);
+
+ for (n = node->firstChild; n != NULL; n = n->nextSibling) {
+ dispatchEventPreorder(n, evt);
+ }
+}
+static void
+dispatchEventPostorder(DOM_Node *node, DOM_MutationEvent *evt)
+{
+ DOM_Node *n;
+
+ for (n = node->firstChild; n != NULL; n = n->nextSibling) {
+ dispatchEventPostorder(n, evt);
+ }
+
+ DOM_EventTarget_dispatchEvent(node, evt);
+}
+static DOM_Node *
+_removeChild(DOM_Node *node, DOM_Node *oldChild)
+{
+ NodeEntry *e;
+ DOM_MutationEvent evt;
+
+ if (NodeList_exists(node->childNodes, oldChild) == 0) {
+ return NULL;
+ }
+
+ DOM_MutationEvent_initMutationEvent(&evt,
+ "DOMNodeRemoved", 1, 0, node, NULL, NULL, NULL, 0);
+ DOM_EventTarget_dispatchEvent(oldChild, &evt);
+
+ DOM_MutationEvent_initMutationEvent(&evt,
+ "DOMNodeRemovedFromDocument", 0, 0, NULL, NULL, NULL, NULL, 0);
+ dispatchEventPostorder(oldChild, &evt);
+
+ e = NodeList_remove(node->childNodes, oldChild);
+ free(e);
+
+ if (node->firstChild == node->lastChild) {
+ node->firstChild = node->lastChild = NULL;
+ } else if (oldChild == node->firstChild) {
+ node->firstChild = oldChild->nextSibling;
+ node->firstChild->previousSibling = NULL;
+ } else if (oldChild == node->lastChild) {
+ node->lastChild = oldChild->previousSibling;
+ node->lastChild->nextSibling = NULL;
+ } else {
+ oldChild->previousSibling->nextSibling = oldChild->nextSibling;
+ oldChild->nextSibling->previousSibling = oldChild->previousSibling;
+ }
+
+ oldChild->previousSibling = NULL;
+ oldChild->nextSibling = NULL;
+ oldChild->parentNode = NULL;
+
+ if (MODIFYING_DOC_ELEM(node, oldChild)) {
+ node->u.Document.documentElement = NULL;
+ } else if (MODIFYING_DOCTYPE_ELEM(node, oldChild)) {
+ node->u.Document.doctype = NULL;
+ oldChild->ownerDocument = NULL;
+ } else {
+ updateCommonParent(node);
+ }
+
+ return oldChild;
+}
+DOM_Node *
+DOM_Node_insertBefore(DOM_Node *node, DOM_Node *newChild, DOM_Node *refChild)
+{
+ NodeEntry *e;
+ DOM_MutationEvent evt;
+
+ if (node == NULL || newChild == NULL) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ if (newChild->ownerDocument != node->ownerDocument &&
+ newChild->ownerDocument != node) {
+ DOM_Exception = DOM_WRONG_DOCUMENT_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ if (refChild != NULL && refChild->parentNode != node) {
+ DOM_Exception = DOM_HIERARCHY_REQUEST_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+
+ if (newChild->nodeType == DOM_DOCUMENT_FRAGMENT_NODE) {
+ DOM_Node *n, *nxt;
+
+ for (n = newChild->firstChild; n != NULL; n = n->nextSibling) {
+ if (INVALID_HIER_REQ(node, n) || _isAncestor(n, node)) {
+ DOM_Exception = DOM_HIERARCHY_REQUEST_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ }
+ for (n = newChild->firstChild; n != NULL; n = nxt) {
+ nxt = n->nextSibling;
+ if (_removeChild(newChild, n) == NULL) {
+ return NULL;
+ }
+ if (DOM_Node_insertBefore(node, n, refChild) == NULL) {
+ DOM_Document_destroyNode(n->ownerDocument, n);
+ return NULL;
+ }
+ }
+ return newChild;
+ }
+ if (INVALID_HIER_REQ(node, newChild) || _isAncestor(newChild, node)) {
+ DOM_Exception = DOM_HIERARCHY_REQUEST_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+
+ _removeChild(node, newChild);
+
+ if ((e = NodeList_insert(node->childNodes, newChild, refChild)) == NULL) {
+ return NULL;
+ }
+
+ if (node->firstChild == NULL) {
+ node->firstChild = node->lastChild = newChild;
+ newChild->previousSibling = NULL;
+ newChild->nextSibling = NULL;
+ } else if (refChild == NULL) {
+ newChild->previousSibling = node->lastChild;
+ node->lastChild->nextSibling = newChild;
+ node->lastChild = newChild;
+ newChild->nextSibling = NULL;
+ } else {
+ newChild->previousSibling = refChild->previousSibling;
+ newChild->nextSibling = refChild;
+ if (refChild == node->firstChild) {
+ node->firstChild = newChild;
+ newChild->previousSibling = NULL;
+ } else {
+ refChild->previousSibling->nextSibling = newChild;
+ }
+ refChild->previousSibling = newChild;
+ }
+ newChild->parentNode = node;
+
+ if (MODIFYING_DOC_ELEM(node, newChild)) {
+ node->u.Document.documentElement = newChild;
+ } else if (MODIFYING_DOCTYPE_ELEM(node, newChild)) {
+ node->u.Document.doctype = newChild;
+ newChild->ownerDocument = node;
+ }
+
+ DOM_MutationEvent_initMutationEvent(&evt,
+ "DOMNodeInserted", 1, 0, node, NULL, NULL, NULL, 0);
+ DOM_EventTarget_dispatchEvent(newChild, &evt);
+
+ DOM_MutationEvent_initMutationEvent(&evt,
+ "DOMNodeInsertedIntoDocument", 0, 0, NULL, NULL, NULL, NULL, 0);
+ dispatchEventPreorder(newChild, &evt);
+
+ updateCommonParent(node);
+
+ return newChild;
+}
+DOM_Node *
+DOM_Node_replaceChild(DOM_Node *node, DOM_Node *newChild, DOM_Node *oldChild)
+{
+ DOM_MutationEvent evt;
+
+ if (node == NULL || newChild == NULL || oldChild == NULL) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ if (newChild->ownerDocument != node->ownerDocument &&
+ newChild->ownerDocument != node) {
+ DOM_Exception = DOM_WRONG_DOCUMENT_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+
+ if (!NodeList_exists(node->childNodes, oldChild)) {
+ DOM_Exception = DOM_NOT_FOUND_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+
+ if (newChild->nodeType == DOM_DOCUMENT_FRAGMENT_NODE) {
+ DOM_Node *n, *nxt;
+
+ for (n = newChild->firstChild; n != NULL; n = n->nextSibling) {
+ if (INVALID_HIER_REQ(node, n) || _isAncestor(n, node)) {
+ DOM_Exception = DOM_HIERARCHY_REQUEST_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ }
+ for (n = newChild->firstChild; n != NULL; n = nxt) {
+ nxt = n->nextSibling;
+ if (_removeChild(newChild, n) == NULL) {
+ return NULL;
+ }
+ if (DOM_Node_insertBefore(node, n, oldChild) == NULL) {
+ DOM_Document_destroyNode(n->ownerDocument, n);
+ return NULL;
+ }
+ }
+
+ if (_removeChild(node, oldChild) == NULL) {
+ return NULL;
+ }
+
+ return oldChild;
+ }
+ if (INVALID_HIER_REQ(node, newChild) || _isAncestor(newChild, node)) {
+ DOM_Exception = DOM_HIERARCHY_REQUEST_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+
+ _removeChild(node, newChild);
+
+ if (NodeList_exists(node->childNodes, oldChild) == 0) {
+ return NULL;
+ }
+
+ DOM_MutationEvent_initMutationEvent(&evt,
+ "DOMNodeRemoved", 1, 0, node, NULL, NULL, NULL, 0);
+ DOM_EventTarget_dispatchEvent(oldChild, &evt);
+
+ DOM_MutationEvent_initMutationEvent(&evt,
+ "DOMNodeRemovedFromDocument", 0, 0, NULL, NULL, NULL, NULL, 0);
+ dispatchEventPostorder(oldChild, &evt);
+
+ NodeList_replace(node->childNodes, newChild, oldChild);
+
+ node->firstChild = node->childNodes->first->node;
+ node->lastChild = node->childNodes->last->node;
+
+ if ((newChild->previousSibling = oldChild->previousSibling)) {
+ newChild->previousSibling->nextSibling = newChild;
+ }
+ if ((newChild->nextSibling = oldChild->nextSibling)) {
+ newChild->nextSibling->previousSibling = newChild;
+ }
+
+ newChild->parentNode = node;
+ oldChild->parentNode = NULL;
+ oldChild->previousSibling = NULL;
+ oldChild->nextSibling = NULL;
+
+ if (MODIFYING_DOC_ELEM(node, newChild)) {
+ node->u.Document.documentElement = newChild;
+ } else if (MODIFYING_DOCTYPE_ELEM(node, newChild)) {
+ node->u.Document.doctype = newChild;
+ newChild->ownerDocument = node;
+ }
+
+ DOM_MutationEvent_initMutationEvent(&evt,
+ "DOMNodeInserted", 1, 0, node, NULL, NULL, NULL, 0);
+ DOM_EventTarget_dispatchEvent(newChild, &evt);
+
+ DOM_MutationEvent_initMutationEvent(&evt,
+ "DOMNodeInsertedIntoDocument", 0, 0, NULL, NULL, NULL, NULL, 0);
+ dispatchEventPreorder(newChild, &evt);
+
+ updateCommonParent(node);
+
+ return oldChild;
+}
+DOM_Node *
+DOM_Node_removeChild(DOM_Node *node, DOM_Node *oldChild)
+{
+ if (node == NULL || oldChild == NULL) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ if (oldChild->ownerDocument != node->ownerDocument &&
+ oldChild->ownerDocument != node) {
+ DOM_Exception = DOM_WRONG_DOCUMENT_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+
+ if ((oldChild = _removeChild(node, oldChild)) == NULL) {
+ DOM_Exception = DOM_NOT_FOUND_ERR;
+ PMNO(DOM_Exception);
+ }
+
+ return oldChild;
+}
+DOM_Node *
+DOM_Node_appendChild(DOM_Node *node, DOM_Node *newChild)
+{
+ DOM_MutationEvent evt;
+
+ if (node == NULL || newChild == NULL) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ if (newChild->ownerDocument != node->ownerDocument &&
+ node->nodeType != DOM_DOCUMENT_NODE &&
+ newChild->nodeType != DOM_DOCUMENT_TYPE_NODE) {
+ DOM_Exception = DOM_WRONG_DOCUMENT_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+
+ if (newChild->nodeType == DOM_DOCUMENT_FRAGMENT_NODE) {
+ DOM_Node *n, *nxt;
+
+ for (n = newChild->firstChild; n != NULL; n = n->nextSibling) {
+ if (INVALID_HIER_REQ(node, n) || _isAncestor(n, node)) {
+ DOM_Exception = DOM_HIERARCHY_REQUEST_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ }
+ for (n = newChild->firstChild; n != NULL; n = nxt) {
+ nxt = n->nextSibling;
+ if (_removeChild(newChild, n) == NULL) {
+ return NULL;
+ }
+ if (DOM_Node_appendChild(node, n) == NULL) {
+ DOM_Document_destroyNode(n->ownerDocument, n);
+ return NULL;
+ }
+ }
+ return newChild;
+ }
+ if (INVALID_HIER_REQ(node, newChild) || _isAncestor(newChild, node)) {
+ DOM_Exception = DOM_HIERARCHY_REQUEST_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+
+ _removeChild(node, newChild);
+
+ if (NodeList_append(node->childNodes, newChild) == NULL) {
+ return NULL;
+ }
+
+ if (node->firstChild == NULL) {
+ node->firstChild = node->lastChild = newChild;
+ newChild->previousSibling = NULL;
+ newChild->nextSibling = NULL;
+ } else {
+ node->lastChild->nextSibling = newChild;
+ newChild->previousSibling = node->lastChild;
+ node->lastChild = newChild;
+ }
+ newChild->nextSibling = NULL;
+ newChild->parentNode = node;
+
+ if (MODIFYING_DOC_ELEM(node, newChild)) {
+ node->u.Document.documentElement = newChild;
+ } else if (MODIFYING_DOCTYPE_ELEM(node, newChild)) {
+ node->u.Document.doctype = newChild;
+ newChild->ownerDocument = node;
+ }
+
+ DOM_MutationEvent_initMutationEvent(&evt,
+ "DOMNodeInserted", 1, 0, node, NULL, NULL, NULL, 0);
+ DOM_EventTarget_dispatchEvent(newChild, &evt);
+
+ DOM_MutationEvent_initMutationEvent(&evt,
+ "DOMNodeInsertedIntoDocument", 0, 0, NULL, NULL, NULL, NULL, 0);
+ dispatchEventPreorder(newChild, &evt);
+
+ updateCommonParent(node);
+
+ return newChild;
+}
+int
+DOM_Node_hasChildNodes(const DOM_Node *node)
+{
+ return node != NULL && node->firstChild;
+}
+extern const char *node_names[];
+static DOM_Node *
+Node_cloneNode(DOM_Document *ownerDocument, DOM_Node *node, int deep)
+{
+ DOM_Node *clone = NULL;
+ DOM_Node *ntmp, *ctmp;
+ NodeEntry *e;
+ DOM_String *tmp;
+
+ switch(node->nodeType) {
+ case DOM_ELEMENT_NODE:
+ clone = DOM_Document_createElement(ownerDocument, node->nodeName);
+ if (clone) {
+ for (e = node->attributes->first; e != NULL; e = e->next) {
+ if ((ctmp = Node_cloneNode(ownerDocument, e->node, deep)) == NULL ||
+ NodeList_append(clone->attributes, ctmp) == NULL) {
+ DOM_Document_destroyNode(clone->ownerDocument, ctmp);
+ DOM_Document_destroyNode(clone->ownerDocument, clone);
+ return NULL;
+ }
+ }
+ }
+ break;
+ case DOM_ATTRIBUTE_NODE:
+ if ((clone = DOM_Document_createAttribute(ownerDocument, node->nodeName))) {
+ clone->u.Attr.specified = node->u.Attr.specified;
+ free(clone->nodeValue);
+ clone->u.Attr.value = clone->nodeValue = mbsdup(node->nodeValue);
+ if (clone->u.Attr.value == NULL) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ DOM_Document_destroyNode(clone->ownerDocument, clone);
+ return NULL;
+ }
+ }
+ break;
+ case DOM_COMMENT_NODE:
+ clone = DOM_Document_createComment(ownerDocument, node->nodeValue);
+ break;
+ case DOM_TEXT_NODE:
+ clone = DOM_Document_createTextNode(ownerDocument, node->nodeValue);
+ break;
+ case DOM_CDATA_SECTION_NODE:
+ clone = DOM_Document_createCDATASection(ownerDocument, node->nodeValue);
+ break;
+ case DOM_DOCUMENT_FRAGMENT_NODE:
+ clone = DOM_Document_createDocumentFragment(ownerDocument);
+ break;
+ case DOM_DOCUMENT_NODE:
+ clone = ownerDocument;
+ break;
+ case DOM_PROCESSING_INSTRUCTION_NODE:
+ clone = DOM_Document_createProcessingInstruction(ownerDocument,
+ node->u.ProcessingInstruction.target,
+ node->u.ProcessingInstruction.data);
+ break;
+ case DOM_ENTITY_NODE:
+ if ((clone = Document_createNode(ownerDocument, DOM_ENTITY_NODE))) {
+ tmp = node->nodeValue;
+ if ((clone->nodeName = mbsdup(node->nodeName)) == NULL ||
+ /* This will eventually go away as Entities should not have nodeValues
+ */
+ (clone->nodeValue = mbsdup(node->nodeValue)) == NULL ||
+ (node->u.Entity.publicId &&
+ (clone->u.Entity.publicId = mbsdup(node->u.Entity.publicId)) == NULL) ||
+ (node->u.Entity.systemId &&
+ (clone->u.Entity.systemId = mbsdup(node->u.Entity.systemId)) == NULL) ||
+ (node->u.Entity.notationName &&
+ (clone->u.Entity.notationName = mbsdup(node->u.Entity.notationName)) == NULL)) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ DOM_Document_destroyNode(clone->ownerDocument, clone);
+ return NULL;
+ }
+ free(tmp);
+ }
+ break;
+ case DOM_NOTATION_NODE:
+ if ((clone = Document_createNode(ownerDocument, DOM_NOTATION_NODE))) {
+ if ((clone->nodeName = mbsdup(node->nodeName)) == NULL ||
+ (node->u.Notation.publicId &&
+ (clone->u.Notation.publicId = mbsdup(node->u.Notation.publicId)) == NULL) ||
+ (node->u.Notation.systemId &&
+ (clone->u.Notation.systemId = mbsdup(node->u.Notation.systemId)) == NULL)) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ DOM_Document_destroyNode(clone->ownerDocument, clone);
+ return NULL;
+ }
+ }
+ break;
+ case DOM_DOCUMENT_TYPE_NODE:
+ if ((clone = DOM_Implementation_createDocumentType(node->nodeName, NULL, NULL))) {
+ if ((node->u.DocumentType.publicId &&
+ (clone->u.DocumentType.publicId = mbsdup(node->u.DocumentType.publicId)) == NULL) ||
+ (node->u.DocumentType.systemId &&
+ (clone->u.DocumentType.systemId = mbsdup(node->u.DocumentType.systemId)) == NULL)) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ DOM_Document_destroyNode(clone->ownerDocument, clone);
+ return NULL;
+ }
+ }
+ ownerDocument->u.Document.doctype = clone;
+ clone->ownerDocument = ownerDocument;
+ break;
+ case DOM_ENTITY_REFERENCE_NODE:
+ DOM_Exception = DOM_NOT_SUPPORTED_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+
+ if (deep && clone && node->childNodes) {
+ for (ntmp = node->firstChild; ntmp != NULL; ntmp = ntmp->nextSibling) {
+ ctmp = Node_cloneNode(ownerDocument, ntmp, 1);
+ if (ctmp == NULL || DOM_Node_appendChild(clone, ctmp) == NULL) {
+ DOM_Document_destroyNode(clone->ownerDocument, ctmp);
+ DOM_Document_destroyNode(clone->ownerDocument, clone);
+ return NULL;
+ }
+ }
+ }
+
+ return clone;
+}
+DOM_Node *
+DOM_Node_cloneNode(DOM_Node *node, int deep)
+{
+ if (node == NULL) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ if (node->nodeType == DOM_DOCUMENT_NODE) {
+ DOM_Document *doc;
+ if ((doc = DOM_Implementation_createDocument(NULL, NULL, NULL)) == NULL) {
+ AMSG("");
+ return NULL;
+ }
+ return Node_cloneNode(doc, node, deep);
+ }
+ return Node_cloneNode(node->ownerDocument, node, deep);
+}
+DOM_String *
+DOM_Node_getNodeValue(DOM_Node *node)
+{
+ if (node == NULL) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ return node->nodeValue;
+}
+void
+DOM_Node_setNodeValue(DOM_Node *node, DOM_String *value)
+{
+ DOM_String *str = NULL;
+
+ if (node == NULL) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNO(DOM_Exception);
+ return;
+ }
+ switch(node->nodeType) {
+ case DOM_ATTRIBUTE_NODE:
+ case DOM_COMMENT_NODE:
+ case DOM_TEXT_NODE:
+ case DOM_CDATA_SECTION_NODE:
+ case DOM_PROCESSING_INSTRUCTION_NODE:
+ if ((str = mbsdup(value)) == NULL) {
+ DOM_Exception = errno;
+ AMSG("");
+ return;
+ }
+ break;
+ }
+ switch(node->nodeType) {
+ case DOM_ATTRIBUTE_NODE:
+ free(node->nodeValue);
+ node->nodeValue = node->u.Attr.value = str;
+ break;
+ case DOM_COMMENT_NODE:
+ case DOM_TEXT_NODE:
+ case DOM_CDATA_SECTION_NODE:
+ free(node->nodeValue);
+ node->nodeValue = node->u.CharacterData.data = str;
+ break;
+ case DOM_PROCESSING_INSTRUCTION_NODE:
+ free(node->nodeValue);
+ node->nodeValue = node->u.ProcessingInstruction.data = str;
+ break;
+ default:
+ return; /* No effect */
+ }
+}
+
+
+#if FAST_NODELIST
+
+/* The number of nodes required in a list before hashing starts */
+#define FAST_FILLFACTOR 16
+
+static void _removeFromMap(DOM_NodeList* nl, DOM_Node* key)
+{
+ if (nl->_map) {
+ if (hashmap_get(nl->_map, key) != NULL) {
+ void* d = NULL;
+ void* k = key;
+ hashmap_remove(nl->_map, &k, &d);
+ }
+ }
+}
+
+static int _addToMap(DOM_NodeList* nl, DOM_Node* key, NodeEntry* val)
+{
+ if (!nl->_map && nl->length > FAST_FILLFACTOR) {
+
+ nl->_map = hashmap_new(0, NULL, NULL, NULL);
+
+ /* Hash what we currently have */
+ if (nl->_map) {
+ NodeEntry *e = nl->first;
+ while (e) {
+ _addToMap(nl, e->node, e);
+ e = e->next;
+ }
+ }
+ }
+
+ if (nl->_map) {
+ _removeFromMap(nl, key);
+
+ if (hashmap_put(nl->_map, key, val) == -1) {
+ DOM_Exception = errno;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+#endif
+
+static NodeEntry* _lookupNode(DOM_NodeList* nl, DOM_Node* node)
+{
+ NodeEntry* s;
+
+#if FAST_NODELIST
+ if (nl->_map)
+ s = (NodeEntry*)hashmap_get(nl->_map, node);
+ else
+#endif
+ for (s = nl->first; s != NULL && s->node != node; s = s->next) {
+ ;
+ }
+
+ return s;
+}
+
+/* NodeList
+ */
+
+DOM_NodeList *
+Document_createNodeList(DOM_Document *doc)
+{
+ DOM_NodeList *r;
+
+ if ((r = calloc(sizeof *r, 1)) == NULL) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ }
+ r->_ownerDocument = doc;
+
+ return r;
+}
+void
+DOM_Document_destroyNodeList(DOM_Document *doc, DOM_NodeList *nl, int free_nodes)
+{
+ if (nl) {
+ if (nl->filter == 0) {
+ NodeEntry *e, *tmp;
+
+ e = nl->first;
+ while (e != NULL) {
+ if (free_nodes) {
+ DOM_Document_destroyNode(doc, e->node);
+ }
+ tmp = e;
+ e = e->next;
+ free(tmp);
+ }
+ }
+
+#if FAST_NODELIST
+ if(nl->_map)
+ hashmap_del(nl->_map, NULL, NULL, NULL);
+#endif
+
+ free(nl);
+ }
+}
+
+DOM_Node *
+NodeList_itemFiltered(const DOM_NodeList *list, int index, unsigned short nodeType)
+{
+ if (list && index >= 0 && index < list->length) {
+ NodeEntry *e;
+
+ for (e = list->first; e != NULL; e = e->next) {
+ if (e->node->nodeType == nodeType) {
+ if (index == 0) {
+ return e->node;
+ }
+ index--;
+ }
+ }
+ }
+
+ return NULL;
+}
+DOM_Node *
+DOM_NodeList_item(const DOM_NodeList *list, int index)
+{
+ if (list) {
+ if (list->filter) {
+ return NodeList_itemFiltered(list->list, index, list->filter);
+ }
+
+ if (index >= 0 && index < list->length) {
+ NodeEntry *e;
+
+ for (e = list->first; e != NULL; e = e->next, index--) {
+ if (index == 0) {
+ return e->node;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+NodeEntry *
+NodeList_insert(DOM_NodeList *nl, DOM_Node *newChild, DOM_Node *refChild)
+{
+ NodeEntry *e;
+ NodeEntry *s = NULL;
+
+ if (nl == NULL) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ if (nl->filter) {
+ DOM_Exception = DOM_FILTERED_LIST_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+
+ if(refChild != NULL)
+ {
+ s = _lookupNode(nl, refChild);
+ if(s == NULL || s->node != refChild) {
+ DOM_Exception = DOM_NOT_FOUND_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ }
+
+ if ((e = calloc(sizeof *e, 1)) == NULL) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+
+#if FAST_NODELIST
+ if (_addToMap(nl, newChild, e) == -1) {
+ PMNO(DOM_Exception);
+ free(e);
+ return NULL;
+ }
+#endif
+
+ e->node = newChild;
+ if (nl->length == 0) {
+ nl->first = nl->last = e;
+ } else if (refChild == NULL) {
+ e->prev = nl->last;
+ nl->last->next = e;
+ nl->last = e;
+ } else {
+ e->prev = s->prev;
+ e->next = s;
+ if (s == nl->first) {
+ nl->first = e;
+ } else {
+ s->prev->next = e;
+ }
+ s->prev = e;
+ }
+ nl->length++;
+
+ /* If an attribute is being added this is probably a NamedNodeMap
+ * in which case we must set the ownerElement.
+ */
+ if (newChild->nodeType == DOM_ATTRIBUTE_NODE) {
+ newChild->u.Attr.ownerElement = nl->_ownerElement;
+ }
+
+ return e;
+}
+NodeEntry *
+NodeList_replace(DOM_NodeList *nl, DOM_Node *newChild, DOM_Node *oldChild)
+{
+ NodeEntry *e;
+
+ if (nl == NULL) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ if (nl->filter) {
+ DOM_Exception = DOM_FILTERED_LIST_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+
+ e = _lookupNode(nl, oldChild);
+ if (e == NULL) {
+ DOM_Exception = DOM_NOT_FOUND_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+
+#if FAST_NODELIST
+ _removeFromMap(nl, oldChild);
+ if(_addToMap(nl, newChild, e) == -1) {
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+#endif
+
+ e->node = newChild;
+
+ if (oldChild->nodeType == DOM_ATTRIBUTE_NODE) {
+ oldChild->u.Attr.ownerElement = NULL;
+ }
+
+ return e;
+}
+NodeEntry *
+NodeList_remove(DOM_NodeList *nl, DOM_Node *oldChild)
+{
+ NodeEntry *e;
+
+ if (nl == NULL) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ if (nl->filter) {
+ DOM_Exception = DOM_FILTERED_LIST_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+
+ e = _lookupNode(nl, oldChild);
+ if (e == NULL) {
+ return NULL;
+ }
+
+#if FAST_NODELIST
+ _removeFromMap(nl, oldChild);
+#endif
+
+ if (nl->first == nl->last) {
+ nl->first = nl->last = NULL;
+ } else if (e == nl->first) {
+ nl->first = e->next;
+ nl->first->prev = NULL;
+ } else if (e == nl->last) {
+ nl->last = e->prev;
+ nl->last->next = NULL;
+ } else {
+ e->prev->next = e->next;
+ e->next->prev = e->prev;
+ }
+ nl->length--;
+
+ /* Decrement a filtered node list too? */
+
+ if (oldChild->nodeType == DOM_ATTRIBUTE_NODE) {
+ oldChild->u.Attr.ownerElement = NULL;
+ }
+
+ return e;
+}
+extern const char *node_names[];
+NodeEntry *
+NodeList_append(DOM_NodeList *nl, DOM_Node *newChild)
+{
+ NodeEntry *e;
+ DOM_DocumentType *doctype;
+
+ if (nl == NULL) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNF(DOM_Exception, ": %p", newChild);
+ return NULL;
+ }
+ if (nl->filter) {
+ DOM_Exception = DOM_FILTERED_LIST_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+
+ if ((e = calloc(sizeof *e, 1)) == NULL) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+
+#if FAST_NODELIST
+ if(_addToMap(nl, newChild, e) == -1) {
+ PMNO(DOM_Exception);
+ free(e);
+ return NULL;
+ }
+#endif
+
+ e->node = newChild;
+ if (nl->first == NULL) {
+ nl->first = nl->last = e;
+ } else {
+ nl->last->next = e;
+ e->prev = nl->last;
+ nl->last = e;
+ }
+
+ nl->length++;
+
+ /* If the node list is the DocumentType children and a Notation
+ * or Entity is being added we must artificially update the length
+ * member of those filtered lists
+ */
+ if (newChild->ownerDocument &&
+ (doctype = newChild->ownerDocument->u.Document.doctype) &&
+ nl == doctype->childNodes) {
+ if (newChild->nodeType == DOM_NOTATION_NODE) {
+ doctype->u.DocumentType.notations->length++;
+ } else if (newChild->nodeType == DOM_ENTITY_NODE) {
+ doctype->u.DocumentType.entities->length++;
+ }
+ }
+ /* If an attribute is being added this is probably a NamedNodeMap
+ * in which case we must set the ownerElement.
+ */
+ if (newChild->nodeType == DOM_ATTRIBUTE_NODE) {
+ newChild->u.Attr.ownerElement = nl->_ownerElement;
+ }
+
+ return e;
+}
+int
+NodeList_exists(DOM_NodeList *nl, DOM_Node *child)
+{
+ NodeEntry *e;
+
+ if (nl == NULL || nl->filter) {
+ return 0;
+ }
+
+ e = _lookupNode(nl, child);
+ return e != NULL;
+}
+
+#if HAVE_ENCDEC > 0
+#include <encdec.h>
+#elif HAVE_STRNLEN < 1
+
+static size_t
+strnlen(const char *s, size_t maxlen)
+{
+ size_t len;
+ for (len = 0; *s && len < maxlen; s++, len++);
+ return len;
+}
+
+#else
+
+size_t strnlen(const char *s, size_t maxlen);
+
+#endif
+
+/* Forward references for node.c
+ */
+
+DOM_Node *Document_createNode(DOM_Document *doc, unsigned short nodeType);
+
+static int
+fputds(const DOM_String *s, FILE *stream)
+{
+ return fputs(s, stream);
+}
+
+static void
+fputds_encoded(const DOM_String *s, FILE *stream)
+{
+ size_t l;
+
+ while (*s) {
+ l = strcspn(s, "<>&\"");
+ if (l > 0) {
+ fwrite((void*)s, 1, sizeof(DOM_String) * l, stream);
+ s += l;
+ }
+ switch (*s) {
+ case '\0':
+ break;
+ case '<':
+ fputs("&lt;", stream);
+ break;
+ case '>':
+ fputs("&gt;", stream);
+ break;
+ case '&':
+ fputs("&apos;", stream);
+ break;
+ case '"':
+ fputs("&quot;", stream);
+ break;
+ default:
+ AMSG("");
+ break;
+ };
+ if(*s)
+ ++s;
+ }
+}
+
+int
+DOM_DocumentLS_fwrite(const DOM_DocumentLS *node, FILE *stream)
+{
+ NodeEntry *e;
+ DOM_Node *c;
+
+ if (node == NULL || stream == NULL) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNF(DOM_Exception, ": node=%p,stream=%p", node, stream);
+ return -1;
+ }
+
+ if (DOM_Exception) {
+ return -1;
+ }
+
+ switch (node->nodeType) {
+ case DOM_ELEMENT_NODE:
+ fputc('<', stream);
+ fputds(node->nodeName, stream);
+ for (e = node->attributes->first; e != NULL; e = e->next) {
+ fputc(' ', stream);
+ fputds(e->node->nodeName, stream);
+ fputs("=\"", stream);
+ fputds_encoded(e->node->nodeValue, stream);
+ fputc('"', stream);
+ }
+ if (DOM_Node_hasChildNodes(node)) {
+ fputc('>', stream);
+ for (c = node->firstChild; c != NULL; c = c->nextSibling) {
+ if (DOM_DocumentLS_fwrite(c, stream) == -1) {
+ /* Don't put msgno macro here or might overrun buf */
+ return -1;
+ }
+ }
+ fputs("</", stream);
+ fputds(node->nodeName, stream);
+ fputc('>', stream);
+ } else {
+ fputs("/>", stream);
+ }
+ break;
+ case DOM_ATTRIBUTE_NODE:
+ break;
+ case DOM_TEXT_NODE:
+ fputds_encoded(node->nodeValue, stream);
+ break;
+ case DOM_CDATA_SECTION_NODE:
+ break;
+ case DOM_ENTITY_REFERENCE_NODE:
+ break;
+ case DOM_NOTATION_NODE:
+ fputs(" <!NOTATION ", stream);
+ fputds(node->nodeName, stream);
+ if (node->u.Entity.publicId) {
+ fputs(" PUBLIC \"", stream);
+ fputds(node->u.Entity.publicId, stream);
+ fputs("\" \"", stream);
+ fputds(node->u.Entity.systemId, stream);
+ fputc('"', stream);
+ } else if (node->u.Entity.systemId) {
+ fputs(" SYSTEM \"", stream);
+ fputds(node->u.Entity.systemId, stream);
+ fputc('"', stream);
+ }
+ fputs(">", stream);
+ break;
+ case DOM_ENTITY_NODE:
+ fputs(" <!ENTITY ", stream);
+ fputds(node->nodeName, stream);
+ if (node->nodeValue) {
+ fputc('"', stream);
+ fputds(node->nodeValue, stream);
+ fputc('"', stream);
+ } else {
+ if (node->u.Entity.publicId) {
+ fputs(" PUBLIC \"", stream);
+ fputds(node->u.Entity.publicId, stream);
+ fputs("\" \"", stream);
+ fputds(node->u.Entity.systemId, stream);
+ fputc('"', stream);
+ } else if (node->u.Entity.systemId) {
+ fputs(" SYSTEM \"", stream);
+ fputds(node->u.Entity.systemId, stream);
+ fputc('"', stream);
+ }
+ if (node->u.Entity.notationName) {
+ fputs(" NDATA ", stream);
+ fputds(node->u.Entity.notationName, stream);
+ }
+ }
+ fputs(">", stream);
+ break;
+ case DOM_PROCESSING_INSTRUCTION_NODE:
+ fputs("<?", stream);
+ fputds(node->u.ProcessingInstruction.target, stream);
+ fputc(' ', stream);
+ fputds_encoded(node->u.ProcessingInstruction.data, stream);
+ fputs("?>", stream);
+ break;
+ case DOM_COMMENT_NODE:
+ fputs("<!--", stream);
+ fputds_encoded(node->nodeValue, stream);
+ fputs("-->", stream);
+ break;
+ case DOM_DOCUMENT_NODE:
+ fputs("<?xml", stream);
+ fputs(" version=\"", stream);
+ fputds(node->u.Document.version ? node->u.Document.version : "1.0", stream);
+ fputc('\"', stream);
+#ifdef CODESET
+ fputs(" encoding=\"", stream);
+ fputs(nl_langinfo(CODESET), stream);
+ fputc('\"', stream);
+#endif
+ if (node->u.Document.standalone != 0) {
+ fputs(" standalone=\"yes\"", stream);
+ }
+ fputs("?>"NL, stream);
+ for (c = node->firstChild; c != NULL; c = c->nextSibling) {
+ if (DOM_DocumentLS_fwrite(c, stream) == -1) {
+ AMSG("");
+ return -1;
+ }
+ }
+ fputs(NL, stream);
+ break;
+ case DOM_DOCUMENT_TYPE_NODE:
+ fputs(NL"<!DOCTYPE ", stream);
+ fputs(node->u.DocumentType.name, stream);
+ if (node->u.DocumentType.systemId) {
+ fputs(" SYSTEM \"", stream);
+ fputds(node->u.DocumentType.systemId, stream);
+ fputc('"', stream);
+ } else if (node->u.DocumentType.publicId) {
+ fputs(" PUBLIC \"", stream);
+ fputds(node->u.DocumentType.publicId, stream);
+ fputc('"', stream);
+ }
+ if (node->u.DocumentType.internalSubset) {
+ fputs(" ["NL, stream);
+ fputds(node->u.DocumentType.internalSubset, stream);
+ fputs("]>"NL, stream);
+ } else {
+ fputs(">"NL, stream);
+ }
+ break;
+ case DOM_DOCUMENT_FRAGMENT_NODE:
+ break;
+ }
+
+ return DOM_Exception ? -1 : 0;
+}
+int
+DOM_DocumentLS_save(DOM_DocumentLS *doc, const char *uri, const DOM_Node *node)
+{
+ FILE *fd;
+
+ if ((doc == NULL && node == NULL) || uri == NULL) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNF(DOM_Exception, ": doc=%p,uri=%s,node=%p", doc, uri, node);
+ return -1;
+ }
+
+ fd = fopen(uri, "w");
+ if (fd && DOM_DocumentLS_fwrite(doc ? doc : node, fd) == 0) {
+ fclose(fd);
+ return 0;
+ }
+ DOM_Exception = errno;
+ PMNF(DOM_Exception, ": uri=%s", uri);
+
+ return -1;
+}
+
+
+/* DOM_DocumentEvent - Introduced in DOM Level 2
+ */
+
+void
+DOM_DocumentEvent_destroyEvent(DOM_DocumentEvent *doc, DOM_Event *evt)
+{
+ if (doc && evt) {
+ if (evt->type) {
+ /*free(evt->type); */
+ }
+ free(evt);
+ }
+}
+
+DOM_Event *
+DOM_DocumentEvent_createEvent(DOM_DocumentEvent *doc, const DOM_String *eventType)
+{
+ DOM_Event *evt;
+
+ if (doc == NULL || eventType == NULL) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ if (strcmp(eventType, "Events") == 0 ||
+ strcmp(eventType, "UIEvents") == 0 ||
+ strcmp(eventType, "TextEvents") == 0) {
+ evt = calloc(sizeof *evt, 1);
+ if (evt == NULL) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ } else {
+ DOM_Exception = DOM_NOT_SUPPORTED_ERR;
+ PMNO(DOM_Exception);
+ return NULL;
+ }
+ return evt;
+}
+
+/* DOM_Event - Introduced in DOM Level 2
+ */
+
+void
+DOM_Event_stopPropagation(DOM_Event *evt)
+{
+ if (evt) {
+ evt->_sp = 1;
+ }
+}
+void
+DOM_Event_preventDefault(DOM_Event *evt)
+{
+ if (evt && evt->cancelable) {
+ evt->_pd = 1;
+ }
+}
+void
+DOM_Event_initEvent(DOM_Event *evt, const DOM_String *eventTypeArg,
+ int canBubbleArg, int cancelableArg)
+{
+ if (evt == NULL || eventTypeArg == NULL || *eventTypeArg == '\0') {
+ return;
+ }
+ evt->type = eventTypeArg; /* no dup? */
+ evt->bubbles = canBubbleArg;
+ evt->cancelable = cancelableArg;
+}
+
+/* DOM_UIEvent
+ */
+
+void
+DOM_UIEvent_initUIEvent(DOM_UIEvent *evt, const DOM_String *typeArg,
+ int canBubbleArg, int cancelableArg,
+ DOM_AbstractView *viewArg, long detailArg)
+{
+ if (evt == NULL || typeArg == NULL || *typeArg == '\0') {
+ return;
+ }
+ DOM_Event_initEvent(evt, typeArg, canBubbleArg, cancelableArg);
+ evt->view = viewArg;
+ evt->detail = detailArg;
+}
+
+/* DOM_EventTarget - Introduced in DOM Level 2
+ */
+
+void
+DOM_EventTarget_addEventListener(DOM_EventTarget *target, const DOM_String *type,
+ DOM_EventListener *listener,
+ DOM_EventListener_handleEvent listener_fn,
+ int useCapture)
+{
+ ListenerEntry *e;
+ unsigned int i;
+ int opos = -1;
+
+ if (target == NULL || type == NULL || listener_fn == NULL) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNO(DOM_Exception);
+ return;
+ }
+
+ for (i = 0; i < target->listeners_len; i++) {
+ e = target->listeners[i]; /* skip duplicates */
+
+ if (e == NULL) {
+ if (opos == -1) {
+ opos = i; /* find open position for new entry */
+ } /* really need a hash code for this */
+ } else if (e->listener == listener && e->listener_fn == listener_fn &&
+ e->useCapture == useCapture && strcmp(e->type, type) == 0) {
+ return;
+ }
+ }
+
+ if ((e = malloc(sizeof *e)) == NULL ||
+ (e->type = mbsdup(type)) == NULL) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ free(e);
+ return;
+ }
+ e->listener = listener;
+ e->listener_fn = listener_fn;
+ e->useCapture = useCapture;
+
+ if (opos == -1) {
+ target->listeners = realloc(target->listeners,
+ sizeof *e * (target->listeners_len + 1));
+ if (target->listeners == NULL) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ free(e);
+ return;
+ }
+ target->listeners[target->listeners_len++] = e;
+ } else {
+ target->listeners[opos] = e;
+ }
+/*MSG("added listener: type=%s,target=%s,useCapture=%d\n", type, target->nodeName, useCapture);
+ */
+}
+void
+DOM_EventTarget_removeEventListener(DOM_EventTarget *target, const DOM_String *type,
+ DOM_EventListener *listener,
+ DOM_EventListener_handleEvent listener_fn,
+ int useCapture)
+{
+ ListenerEntry *e;
+ unsigned int i;
+
+ if (target == NULL || type == NULL || listener_fn == NULL) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNO(DOM_Exception);
+ return;
+ }
+
+ for (i = 0; i < target->listeners_len; i++) {
+ e = target->listeners[i];
+
+ if (e && e->listener == listener && e->listener_fn == listener_fn &&
+ e->useCapture == useCapture && strcmp(e->type, type) == 0) {
+ target->listeners[i] = NULL;
+ free(e->type);
+ free(e);
+ return;
+ }
+ }
+}
+static void
+trigger(DOM_EventTarget *target, DOM_Event *evt, int useCapture)
+{
+ ListenerEntry *e;
+ unsigned int j, lcount;
+
+ if (target && target->listeners_len && evt->_sp == 0) {
+ DOM_EventListener_handleEvent *cpy_of_listener_fns;
+
+ if ((cpy_of_listener_fns = malloc(target->listeners_len *
+ sizeof(*cpy_of_listener_fns))) == NULL) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ return;
+ }
+
+ lcount = target->listeners_len; /* copy listeners */
+ for (j = 0; j < lcount; j++) {
+ e = target->listeners[j];
+ cpy_of_listener_fns[j] = e ? e->listener_fn : NULL;
+ }
+
+ evt->currentTarget = target;
+ for (j = 0; j < lcount; j++) {
+ e = target->listeners[j]; /* If the entry is NULL, the listener has
+ * since been removed and is therefore skipped. If it
+ * is not NULL but the listeners do not match, then
+ * it was removed but another listener was added in
+ * its place and therefore should be skipped. However,
+ * if a listener is removed but then added again
+ * while the current set is still being processed
+ * there is a chance that it will be tiggered if it
+ * meets the criteria tested below. Regardless of
+ * the slim chances of this occuring (have to be the
+ * same listener added to the same array) it may
+ * need to be addressed.
+ */
+/*MSG("e=%p,e->listener_fn=%p,cpy_of_listener_fns[%d]=%p,e->useCapture=%d,e->type=%s,evt->type=%s", e, e->listener_fn, j, cpy_of_listener_fns[j], e->useCapture, e->type, evt->type);
+ */
+
+ if (e && e->listener_fn == cpy_of_listener_fns[j] &&
+ e->useCapture == useCapture && strcmp(e->type, evt->type) == 0) {
+ e->listener_fn(e->listener, evt); /* invoke the listener function */
+ }
+ }
+ free(cpy_of_listener_fns);
+ }
+}
+int
+DOM_EventTarget_dispatchEvent(DOM_EventTarget *target, DOM_Event *evt)
+{
+ DOM_EventTarget **targets, *t;
+ unsigned int tcount, i;
+
+ if (target == NULL || evt == NULL) {
+ DOM_Exception = NULL_POINTER_ERR;
+ PMNO(DOM_Exception);
+ return 1;
+ }
+/*MSG("type=%s,target=%s,listeners_len=%d", evt->type, target->nodeName, target->listeners_len);
+ */
+
+ if (evt->type == NULL || *evt->type == '\0') {
+ DOM_Exception = DOM_UNSPECIFIED_EVENT_TYPE_ERR;
+ PMNO(DOM_Exception);
+ return 1;
+ }
+
+ targets = NULL;
+ evt->target = target; /* post-initialization */
+ evt->timeStamp = timestamp();
+ evt->_sp = 0;
+ evt->_pd = 0;
+
+ tcount = 0; /* count targets/ancestors */
+ for (t = target->parentNode; t; t = t->parentNode) {
+ tcount++;
+ }
+ if (tcount) {
+ targets = malloc(sizeof *targets * tcount);
+ if (targets == NULL) {
+ DOM_Exception = errno;
+ PMNO(DOM_Exception);
+ return 1;
+ }
+ }
+ i = tcount; /* save state of tree in targets array */
+ for (t = target->parentNode; t; t = t->parentNode) {
+ targets[--i] = t;
+ }
+ /* Trigger capturers
+ */
+ evt->eventPhase = DOM_EVENT_CAPTURING_PHASE;
+ for (i = 0; i < tcount && evt->_sp == 0; i++) {
+ trigger(targets[i], evt, 1);
+ }
+ /* Trigger regular listeners
+ */
+ evt->eventPhase = DOM_EVENT_AT_TARGET;
+ trigger(target, evt, 0);
+
+ /* Trigger bubblers
+ */
+ evt->eventPhase = DOM_EVENT_BUBBLING_PHASE;
+ i = tcount;
+ while (i-- && evt->bubbles && evt->_sp == 0) {
+ trigger(targets[i], evt, 0);
+ }
+
+ if (targets) {
+ free(targets);
+ }
+ return !evt->_pd;
+}
+
+/* DOM_TextEvent - Introduced in DOM Level 3
+ * http://www.w3.org/TR/2002/WD-DOM-Level-3-Events-20020208/events.html
+ */
+
+
+int
+DOM_TextEvent_checkModifier(DOM_TextEvent *evt, unsigned int modifier)
+{
+ return evt->_modifiers & (1 << (modifier - 1));
+}
+void
+DOM_TextEvent_initTextEvent(DOM_TextEvent *evt, const DOM_String *typeArg,
+ int canBubbleArg, int cancelableArg,
+ DOM_AbstractView *viewArg, long detailArg,
+ DOM_String *outputStringArg,
+ unsigned int keyValArg,
+ unsigned int virtKeyValArg,
+ int visibleOutputGeneratedArg,
+ int numPadArg)
+{
+ if (evt == NULL || typeArg == NULL || *typeArg == '\0') {
+ return;
+ }
+ DOM_UIEvent_initUIEvent(evt, typeArg, canBubbleArg, cancelableArg, viewArg, detailArg);
+ evt->outputString = outputStringArg;
+ evt->keyVal = keyValArg;
+ evt->virtKeyVal = virtKeyValArg;
+ evt->visibleOutputGenerated = visibleOutputGeneratedArg;
+ evt->numPad = numPadArg;
+}
+void
+DOM_TextEvent_initModifier(DOM_TextEvent *evt, unsigned int modifier, int value)
+{
+ if (evt && modifier > 0 && modifier <= DOM_VK_RIGHT_META) {
+ if (value) {
+ evt->_modifiers |= 1 << (modifier - 1);
+ } else {
+ evt->_modifiers &= ~(1 << (modifier - 1));
+ }
+ }
+}
+
+/* MutationEvent
+ */
+
+void
+DOM_MutationEvent_initMutationEvent(DOM_MutationEvent *evt, DOM_String *typeArg,
+ int canBubbleArg, int cancelableArg,
+ DOM_Node *relatedNodeArg,
+ DOM_String *prevValueArg,
+ DOM_String *newValueArg,
+ DOM_String *attrNameArg,
+ unsigned short attrChangeArg)
+{
+ if (evt == NULL || typeArg == NULL || *typeArg == '\0') {
+ return;
+ }
+ DOM_Event_initEvent(evt, typeArg, canBubbleArg, cancelableArg);
+ evt->relatedNode = relatedNodeArg;
+ evt->prevValue = prevValueArg;
+ evt->newValue = newValueArg;
+ evt->attrName = attrNameArg;
+ evt->attrChange = attrChangeArg;
+}
+void
+updateCommonParent(DOM_Node *node)
+{
+ DOM_Node *n, *cp;
+
+ if (node == NULL || node->ownerDocument == NULL) {
+ return;
+ }
+ if (node->ownerDocument->u.Document.commonParent == NULL) {
+ node->ownerDocument->u.Document.commonParent = node;
+ return;
+ }
+
+ cp = NULL;
+ for (n = node; n; n = n->parentNode) {
+ if (n == node->ownerDocument->u.Document.commonParent) {
+ return;
+ } else if (cp == NULL && n->subtreeModified == 1) {
+ cp = n;
+ } else {
+ n->subtreeModified = 1;
+ }
+ }
+
+ node->ownerDocument->u.Document.commonParent = cp;
+}
+static void
+_clearSubtreeModified(DOM_Document *doc)
+{
+ DOM_Node *n;
+
+ for (n = doc->firstChild; n != NULL; n = n->nextSibling) {
+ if (n->subtreeModified) {
+ n->subtreeModified = 0;
+ _clearSubtreeModified(n);
+ }
+ }
+ doc->u.Document.commonParent = NULL;
+}
+void
+DOM_MutationEvent_processSubtreeModified(DOM_Document *doc)
+{
+ DOM_Node *target;
+ DOM_MutationEvent evt;
+
+ if (doc->u.Document.commonParent == NULL) {
+ return;
+ }
+
+ target = doc->u.Document.commonParent;
+ _clearSubtreeModified(doc);
+
+ DOM_MutationEvent_initMutationEvent(&evt, "DOMSubtreeModified",
+ 1, 0, NULL, NULL, NULL, NULL, 0);
+ DOM_EventTarget_dispatchEvent(target, &evt);
+}