summaryrefslogtreecommitdiff
path: root/src/events/event_target.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/events/event_target.c')
-rw-r--r--src/events/event_target.c812
1 files changed, 812 insertions, 0 deletions
diff --git a/src/events/event_target.c b/src/events/event_target.c
new file mode 100644
index 0000000..e17aa9c
--- /dev/null
+++ b/src/events/event_target.c
@@ -0,0 +1,812 @@
+/*
+ * This file is part of libdom.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2009 Bo Yang <struggleyb.nku@gmail.com>
+ */
+
+#include <assert.h>
+
+#include <libwapcaplet/libwapcaplet.h>
+
+#include "events/event.h"
+#include "events/event_listener.h"
+#include "events/event_target.h"
+
+#include "core/document.h"
+#include "core/node.h"
+#include "core/string.h"
+
+#include "utils/utils.h"
+#include "utils/validate.h"
+
+/* The number of chains in the hash table used for hash event types */
+#define CHAINS 11
+
+/* Entry for a EventTarget, used to record the bubbling list */
+typedef struct dom_event_target_entry {
+ struct list_entry entry; /**< The list entry */
+ dom_event_target *et; /**< The node */
+} dom_event_target_entry;
+
+/* Hash key/value functions */
+static void *_key(void *key, void *key_pw, dom_alloc alloc, void *pw,
+ bool clone);
+static void *_value(void *value, void *value_pw, dom_alloc alloc,
+ void *pw, bool clone);
+
+
+/* Initialise this EventTarget */
+dom_exception _dom_event_target_internal_initialise(struct dom_document *doc,
+ dom_event_target_internal *eti)
+{
+ UNUSED(doc);
+ eti->listeners = NULL;
+ eti->ns_listeners = NULL;
+
+ return DOM_NO_ERR;
+}
+
+/* Finalise this EventTarget */
+void _dom_event_target_internal_finalise(struct dom_document *doc,
+ dom_event_target_internal *eti)
+{
+ lwc_context *ctx = _dom_document_get_intern_context(doc);
+
+ if (eti->listeners != NULL)
+ _dom_hash_destroy(eti->listeners, _key, ctx, _value, doc);
+ /* TODO: Now, we did not support the EventListener with namespace,
+ * when we support it, we should deal with the ns_listeners hash
+ * table, too.
+ */
+}
+
+/*-------------------------------------------------------------------------*/
+/* The public API */
+
+/**
+ * Add an EventListener to the EventTarget
+ *
+ * \param et The EventTarget object
+ * \param type The event type which this event listener listens for
+ * \param listener The event listener object
+ * \param capture Whether add this listener in the capturing phase
+ * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
+ */
+dom_exception _dom_event_target_add_event_listener(dom_event_target *et,
+ struct dom_string *type, struct dom_event_listener *listener,
+ bool capture)
+{
+ struct listener_entry *le = NULL;
+ struct dom_document *doc = dom_node_get_owner(et);
+ assert(doc != NULL);
+
+ struct dom_event_target_internal *eti = &et->eti;
+ lwc_string *t = NULL;
+ lwc_context *ctx = NULL;
+ dom_exception err;
+
+ /* If there is no hash table, we should create one firstly */
+ if (eti->listeners == NULL) {
+ err = _dom_document_create_hashtable(doc, CHAINS,
+ _dom_hash_hash_lwcstring, &eti->listeners);
+ if (err != DOM_NO_ERR)
+ return err;
+ }
+
+ err = dom_string_get_intern(type, &ctx, &t);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ ctx = _dom_document_get_intern_context(doc);
+ if (t == NULL) {
+ err = _dom_string_intern(type, ctx, &t);
+ if (err != DOM_NO_ERR)
+ return err;
+ } else {
+ lwc_context_string_ref(ctx, t);
+ }
+
+ assert(t != NULL);
+
+ le = (struct listener_entry *) _dom_document_alloc(doc, NULL,
+ sizeof(struct listener_entry));
+ if (le == NULL)
+ return DOM_NO_MEM_ERR;
+
+ /* Initialise the listener_entry */
+ list_init(&le->list);
+ le->listener = listener;
+ dom_event_listener_ref(listener);
+ le->capture = capture;
+
+ /* Find the type of this event */
+ struct list_entry *item = (struct list_entry *) _dom_hash_get(
+ eti->listeners, t);
+ if (item == NULL) {
+ /* If there is no item in the hash table, we should add the
+ * first */
+ _dom_hash_add(eti->listeners, t, &le->list, false);
+ } else {
+ /* Append this listener to the end of the list */
+ list_append(item, &le->list);
+ }
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Remove an EventListener from the EventTarget
+ *
+ * \param et The EventTarget object
+ * \param type The event type this listener is registered for
+ * \param listener The listener object
+ * \param capture Whether the listener is registered at the capturing phase
+ * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
+ */
+dom_exception _dom_event_target_remove_event_listener(dom_event_target *et,
+ struct dom_string *type, struct dom_event_listener *listener,
+ bool capture)
+{
+ struct listener_entry *le = NULL;
+ struct dom_document *doc = dom_node_get_owner(et);
+ if (doc == NULL) {
+ /* TODO: In the progress of parsing, many Nodes in the DTD
+ * has no document at all, do nothing for this kind of node */
+ return DOM_NO_ERR;
+ }
+
+ struct dom_event_target_internal *eti = &et->eti;
+ lwc_string *t = NULL;
+ lwc_context *ctx = NULL;
+ dom_exception err;
+
+ err = dom_string_get_intern(type, &ctx, &t);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ ctx = _dom_document_get_intern_context(doc);
+ if (t == NULL) {
+ err = _dom_string_intern(type, ctx, &t);
+ if (err != DOM_NO_ERR)
+ return err;
+ } else {
+ lwc_context_string_ref(ctx, t);
+ }
+
+ assert(t != NULL);
+
+ /* Find the type of this event */
+ struct list_entry *item = (struct list_entry *) _dom_hash_get(
+ eti->listeners, t);
+ if (item == NULL) {
+ /* There is no such event listener */
+ lwc_context_string_unref(ctx, t);
+ return DOM_NO_ERR;
+ } else {
+ struct list_entry *i = item;
+ do {
+ le = (struct listener_entry *) i;
+ if (le->listener == listener &&
+ le->capture == capture) {
+ /* We found the listener */
+ list_del(i);
+ dom_event_listener_unref(le->listener);
+ _dom_document_alloc(doc, le,
+ sizeof(struct listener_entry));
+ break;
+ }
+ i = i->next;
+ } while(i != item);
+ }
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Dispatch an event into the implementation's event model
+ *
+ * \param et The EventTarget object
+ * \param evt The event object
+ * \param success Indicates whether any of the listeners which handled the
+ * event called Event.preventDefault(). If
+ * Event.preventDefault() was called the returned value is
+ * false, else it is true.
+ * \return DOM_NO_ERR on success
+ * DOM_DISPATCH_REQUEST_ERR If the event is already in dispatch
+ * DOM_UNSPECIFIED_EVENT_TYPE_ERR If the type of the event is Null or
+ * empty string.
+ * DOM_NOT_SUPPORTED_ERR If the event is not created by
+ * Document.createEvent
+ * DOM_INVALID_CHARACTER_ERR If the type of this event is not a
+ * valid NCName.
+ */
+dom_exception _dom_event_target_dispatch_event(dom_event_target *et,
+ struct dom_event *evt, bool *success)
+{
+ assert(et != NULL);
+
+ dom_exception err, ret = DOM_NO_ERR;
+
+ /* To test whether this event is in dispatch */
+ if (evt->in_dispatch == true) {
+ return DOM_DISPATCH_REQUEST_ERR;
+ } else {
+ evt->in_dispatch = true;
+ }
+
+ if (evt->type == NULL || lwc_string_length(evt->type) == 0) {
+ return DOM_UNSPECIFIED_EVENT_TYPE_ERR;
+ }
+
+ if (evt->doc == NULL)
+ return DOM_NOT_SUPPORTED_ERR;
+
+ struct dom_document *doc = dom_node_get_owner(et);
+ if (doc == NULL) {
+ /* TODO: In the progress of parsing, many Nodes in the DTD has
+ * no document at all, do nothing for this kind of node */
+ return DOM_NO_ERR;
+ }
+
+ struct dom_string *type = NULL;
+ err = _dom_document_create_string_from_lwcstring(doc, evt->type, &type);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ if (_dom_validate_ncname(type) == false) {
+ dom_string_unref(type);
+ return DOM_INVALID_CHARACTER_ERR;
+ }
+ dom_string_unref(type);
+
+ lwc_string *t = evt->type;
+ lwc_context *ctx = NULL;
+ dom_event_target_entry list;
+ dom_event_target *target = et;
+
+ ctx = _dom_document_get_intern_context(doc);
+ assert(t != NULL);
+
+ *success = true;
+
+ /* Compose the event target list */
+ list_init(&list.entry);
+ list.et = et;
+ dom_node_ref(et);
+ target = target->parent;
+
+ while (target != NULL) {
+ dom_event_target_entry *l = (dom_event_target_entry *)
+ _dom_document_alloc(doc, NULL,
+ sizeof(dom_event_target_entry));
+ if (l == NULL) {
+ ret = DOM_NO_MEM_ERR;
+ goto cleanup;
+ }
+ list_append(&list.entry, &l->entry);
+ l->et = target;
+ dom_node_ref(target);
+ target = target->parent;
+ }
+
+ /* Fill the target of the event */
+ evt->target = et;
+ evt->phase = DOM_CAPTURING_PHASE;
+
+ /* The capture phase */
+ struct list_entry *e = list.entry.prev;
+ for (; e != &list.entry; e = e->prev) {
+ dom_event_target_entry *l = (dom_event_target_entry *) e;
+ err = _dom_event_target_dispatch(l->et, evt,
+ DOM_CAPTURING_PHASE, success);
+ if (err != DOM_NO_ERR) {
+ ret = err;
+ goto cleanup;
+ }
+ /* If the stopImmediatePropagation or stopPropagation is
+ * called, we should break */
+ if (evt->stop_now == true || evt->stop == true)
+ goto cleanup;
+ }
+
+ /* Target phase */
+ evt->phase = DOM_AT_TARGET;
+ evt->current = et;
+ err = _dom_event_target_dispatch(et, evt, DOM_AT_TARGET,
+ success);
+ if (evt->stop_now == true || evt->stop == true)
+ goto cleanup;
+
+ /* Bubbling phase */
+ evt->phase = DOM_BUBBLING_PHASE;
+
+ e = list.entry.next;
+ for (; e != &list.entry; e = e->next) {
+ dom_event_target_entry *l = (dom_event_target_entry *) e;
+ err = _dom_event_target_dispatch(l->et, evt,
+ DOM_BUBBLING_PHASE, success);
+ if (err != DOM_NO_ERR) {
+ ret = err;
+ goto cleanup;
+ }
+ /* If the stopImmediatePropagation or stopPropagation is
+ * called, we should break */
+ if (evt->stop_now == true || evt->stop == true)
+ goto cleanup;
+ }
+
+ struct dom_document_event_internal *dei = &doc->dei;
+ if (dei->actions == NULL || evt->prevent_default == true)
+ goto cleanup;
+
+ /* The default action */
+ struct dom_string *nodename;
+ err = dom_node_get_node_name(et, &nodename);
+ if (err != DOM_NO_ERR) {
+ ret = err;
+ goto cleanup;
+ }
+ lwc_string *lnodename = NULL;
+ lwc_context *lctx = NULL;
+ err = dom_string_get_intern(nodename, &lctx, &lnodename);
+ if (err != DOM_NO_ERR) {
+ dom_string_unref(nodename);
+ ret = err;
+ goto cleanup;
+ }
+
+ dom_event_listener *da = dei->actions(lnodename, t);
+ if (da != NULL) {
+ da->handler(evt, da->pw);
+ }
+
+ dom_string_unref(nodename);
+ lwc_context_string_unref(lctx, lnodename);
+ lwc_context_unref(lctx);
+
+cleanup:
+ if (evt->prevent_default == true) {
+ *success = false;
+ }
+
+ while (list.entry.next != &list.entry) {
+ dom_event_target_entry *e = (dom_event_target_entry *)
+ list.entry.next;
+ dom_node_unref(e->et);
+ list_del(list.entry.next);
+ _dom_document_alloc(doc, e, 0);
+ }
+
+ dom_node_unref(et);
+
+ return ret;
+}
+
+/**
+ * Add an EventListener
+ *
+ * \param et The EventTarget object
+ * \param namespace The namespace of this listener
+ * \param type The event type which this event listener listens for
+ * \param listener The event listener object
+ * \param capture Whether add this listener in the capturing phase
+ * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
+ *
+ * We don't support this API now, so it always return DOM_NOT_SUPPORTED_ERR.
+ */
+dom_exception _dom_event_target_add_event_listener_ns(dom_event_target *et,
+ struct dom_string *namespace, struct dom_string *type,
+ struct dom_event_listener *listener, bool capture)
+{
+ UNUSED(et);
+ UNUSED(namespace);
+ UNUSED(type);
+ UNUSED(listener);
+ UNUSED(capture);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/**
+ * Remove an EventListener
+ *
+ * \param et The EventTarget object
+ * \param namespace The namespace of this listener
+ * \param type The event type which this event listener listens for
+ * \param listener The event listener object
+ * \param capture Whether add this listener in the capturing phase
+ * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
+ *
+ * We don't support this API now, so it always return DOM_NOT_SUPPORTED_ERR.
+ */
+dom_exception _dom_event_target_remove_event_listener_ns(dom_event_target *et,
+ struct dom_string *namespace, struct dom_string *type,
+ struct dom_event_listener *listener, bool capture)
+{
+ UNUSED(et);
+ UNUSED(namespace);
+ UNUSED(type);
+ UNUSED(listener);
+ UNUSED(capture);
+
+ return DOM_NOT_SUPPORTED_ERR;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* The key process function of the hash table, see utils/hash_table.h for
+ * detail */
+static void *_key(void *key, void *key_pw, dom_alloc alloc, void *pw,
+ bool clone)
+{
+ UNUSED(alloc);
+ UNUSED(pw);
+ /* There should never be the requirement of clone the event listener
+ * list */
+ assert(clone == false);
+ UNUSED(clone);
+
+ lwc_context *ctx = (lwc_context *) key_pw;
+ lwc_context_string_unref(ctx, (lwc_string *) key);
+
+ return NULL;
+}
+
+/* The value process function of the hash table, see utils/hash_table.h for
+ * detail */
+static void *_value(void *value, void *value_pw, dom_alloc alloc,
+ void *pw, bool clone)
+{
+ UNUSED(alloc);
+ UNUSED(pw);
+ /* There should never be the requirement of clone the event listener
+ * list */
+ assert(clone == false);
+ UNUSED(clone);
+
+ struct listener_entry *le = NULL;
+ struct dom_document *doc = (struct dom_document *) value_pw;
+ struct list_entry *i = (struct list_entry *) value;
+
+ while(i != i->next) {
+ le = (struct listener_entry *) i->next;
+ list_del(i->next);
+ dom_event_listener_unref(le->listener);
+ _dom_document_alloc(doc, le, sizeof(struct listener_entry));
+ }
+
+ le = (struct listener_entry *) i;
+ list_del(i);
+ dom_event_listener_unref(le->listener);
+ _dom_document_alloc(doc, le, sizeof(struct listener_entry));
+
+ return NULL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * Dispatch an event on certain EventTarget
+ *
+ * \param et The EventTarget object
+ * \param evt The event object
+ * \param success Indicates whether any of the listeners which handled the
+ * event called Event.preventDefault(). If
+ * Event.preventDefault() was called the returned value is
+ * false, else it is true.
+ * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
+ */
+dom_exception _dom_event_target_dispatch(dom_event_target *et,
+ struct dom_event *evt, dom_event_flow_phase phase,
+ bool *success)
+{
+ struct dom_event_target_internal *eti = &et->eti;
+ lwc_string *t = evt->type;
+
+ struct list_entry *item = (struct list_entry *) _dom_hash_get(
+ eti->listeners, t);
+ if (item == NULL) {
+ /* There is no such event listener */
+ return DOM_NO_ERR;
+ } else {
+ /* Call the handler for each listener */
+ struct list_entry *i = item;
+ /* Fill the Event fields */
+ evt->current = et;
+ do {
+ struct listener_entry *le =
+ (struct listener_entry *) i;
+ assert(le->listener->handler != NULL);
+ if ((le->capture == true &&
+ phase == DOM_CAPTURING_PHASE) ||
+ (le->capture == false &&
+ phase == DOM_BUBBLING_PHASE) ||
+ (evt->target == evt->current &&
+ phase == DOM_AT_TARGET)) {
+ /* We found the listener */
+ le->listener->handler(evt, le->listener->pw);
+ /* If the handler call
+ * stopImmediatedPropagation, we should
+ * break */
+ if (evt->stop_now == true)
+ break;
+ }
+ i = i->next;
+ } while(i != item);
+ }
+
+ if (evt->prevent_default == true)
+ *success = false;
+
+ return DOM_NO_ERR;
+}
+
+/**
+ * Dispatch a DOMNodeInserted/DOMNodeRemoved event
+ *
+ * \param doc The document object
+ * \param et The EventTarget object
+ * \param type "DOMNodeInserted" or "DOMNodeRemoved"
+ * \param related The parent of the removed/inserted node
+ * \param success Whether this event's default action get called
+ * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
+ */
+dom_exception _dom_dispatch_node_change_event(struct dom_document *doc,
+ dom_event_target *et, dom_event_target *related,
+ dom_mutation_type change, bool *success)
+{
+ struct dom_mutation_event *evt;
+ dom_exception err;
+
+ err = _dom_mutation_event_create(doc, &evt);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ lwc_string *type = NULL;
+ if (change == DOM_MUTATION_ADDITION) {
+ err = _dom_document_create_lwcstring(doc,
+ (const uint8_t *) "DOMNodeInserted",
+ SLEN("DOMNodeInserted"), &type);
+ if (err != DOM_NO_ERR)
+ goto cleanup;
+ } else if (change == DOM_MUTATION_REMOVAL) {
+ err = _dom_document_create_lwcstring(doc,
+ (const uint8_t *) "DOMNodeRemoval",
+ SLEN("DOMNodeRemoved"), &type);
+ if (err != DOM_NO_ERR)
+ goto cleanup;
+ } else {
+ assert("Should never be here" == NULL);
+ }
+
+ dom_string *t = NULL;
+ err = _dom_document_create_string_from_lwcstring(doc, type, &t);
+ _dom_document_unref_lwcstring(doc, type);
+ if (err != DOM_NO_ERR)
+ goto cleanup;
+
+ /* Initiliase the event with corresponding parameters */
+ err = dom_mutation_event_init(evt, t, true, false, related, NULL, NULL,
+ NULL, change);
+ dom_string_unref(t);
+ if (err != DOM_NO_ERR) {
+ goto cleanup;
+ }
+
+ err = dom_event_target_dispatch_event(et, evt, success);
+ if (err != DOM_NO_ERR)
+ goto cleanup;
+
+ /* Finalise the evt, and reuse it */
+ _dom_mutation_event_finalise(doc, evt);
+ /* Dispatch the DOMNodeInsertedIntoDocument/DOMNodeRemovedFromDocument
+ * event */
+ if (change == DOM_MUTATION_ADDITION) {
+ err = _dom_document_create_lwcstring(doc,
+ (const uint8_t *)
+ "DOMNodeInsertedIntoDocument",
+ SLEN("DOMNodeInsertedIntoDocument"), &type);
+ if (err != DOM_NO_ERR)
+ goto cleanup;
+ } else if (change == DOM_MUTATION_REMOVAL) {
+ err = _dom_document_create_lwcstring(doc,
+ (const uint8_t *) "DOMNodeRemovedFromDocument",
+ SLEN("DOMNodeRemovedFromDocument"), &type);
+ if (err != DOM_NO_ERR)
+ goto cleanup;
+ } else {
+ assert("Should never be here" == NULL);
+ }
+
+ err = _dom_document_create_string_from_lwcstring(doc, type, &t);
+ _dom_document_unref_lwcstring(doc, type);
+ if (err != DOM_NO_ERR)
+ goto cleanup;
+
+ dom_event_target *target = et;
+ while (target != NULL) {
+ err = dom_mutation_event_init(evt, t, true, false, NULL,
+ NULL, NULL, NULL, change);
+ if (err != DOM_NO_ERR)
+ goto cleanup;
+
+ err = dom_event_target_dispatch_event(target, evt, success);
+ if (err != DOM_NO_ERR)
+ goto cleanup;
+
+ dom_event_target *p = dom_node_get_parent(target);
+ if (target->first_child != NULL) {
+ target = target->first_child;
+ } else if (target->next != NULL) {
+ target = target->next;
+ } else {
+ while (p != et && target == p->first_child) {
+ target = p;
+ p = dom_node_get_parent(p);
+ }
+
+ target = target->next;
+ }
+ /* Finalise the event for reuse in next iteration */
+ _dom_mutation_event_finalise(doc, evt);
+ }
+
+cleanup:
+ _dom_mutation_event_destroy(doc, evt);
+
+ return err;
+}
+
+/**
+ * Dispatch a DOMAttrModified event
+ *
+ * \param doc The Document object
+ * \param et The EventTarget
+ * \param prev The previous value before change
+ * \param new The new value after change
+ * \param related The related EventTarget
+ * \param attr_name The Attribute name
+ * \param change How this attribute change
+ * \param success Whether this event's default handler get called
+ * \return DOM_NO_ERR on success, appropirate dom_exception on failure.
+ */
+dom_exception _dom_dispatch_attr_modified_event(struct dom_document *doc,
+ dom_event_target *et, dom_string *prev, dom_string *new,
+ dom_event_target *related, dom_string *attr_name,
+ dom_mutation_type change, bool *success)
+{
+ struct dom_mutation_event *evt;
+ dom_exception err;
+
+ err = _dom_mutation_event_create(doc, &evt);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ lwc_string *type = NULL;
+ err = _dom_document_create_lwcstring(doc,
+ (const uint8_t *) "DOMAttrModified",
+ SLEN("DOMAttrModified"), &type);
+ if (err != DOM_NO_ERR)
+ goto cleanup;
+
+ dom_string *t = NULL;
+ err = _dom_document_create_string_from_lwcstring(doc, type, &t);
+ _dom_document_unref_lwcstring(doc, type);
+ if (err != DOM_NO_ERR)
+ goto cleanup;
+
+ /* Initiliase the event with corresponding parameters */
+ err = dom_mutation_event_init(evt, t, true, false, related, prev, new,
+ attr_name, change);
+ dom_string_unref(t);
+ if (err != DOM_NO_ERR) {
+ goto cleanup;
+ }
+
+ err = dom_event_target_dispatch_event(et, evt, success);
+
+cleanup:
+ _dom_mutation_event_destroy(doc, evt);
+
+ return err;
+}
+
+/**
+ * Dispatch a DOMCharacterDataModified event
+ *
+ * \param et The EventTarget object
+ * \param prev The preValue of the DOMCharacterData
+ * \param new The newValue of the DOMCharacterData
+ * \return DOM_NO_ERR on success, appropirate dom_exception on failure.
+ *
+ * TODO:
+ * The character_data object may be a part of a Attr node, if so, another
+ * DOMAttrModified event should be dispatched, too. But for now, we did not
+ * support any XML feature, so just leave it as this.
+ */
+dom_exception _dom_dispatch_characterdata_modified_event(
+ struct dom_document *doc, dom_event_target *et,
+ dom_string *prev, dom_string *new, bool *success)
+{
+ struct dom_mutation_event *evt;
+ dom_exception err;
+
+ err = _dom_mutation_event_create(doc, &evt);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ lwc_string *type = NULL;
+ err = _dom_document_create_lwcstring(doc,
+ (const uint8_t *) "DOMCharacterDataModified",
+ SLEN("DOMCharacterDataModified"), &type);
+ if (err != DOM_NO_ERR)
+ goto cleanup;
+
+ dom_string *t = NULL;
+ err = _dom_document_create_string_from_lwcstring(doc, type, &t);
+ _dom_document_unref_lwcstring(doc, type);
+ if (err != DOM_NO_ERR)
+ goto cleanup;
+
+ err = dom_mutation_event_init(evt, t, true, false, et, prev, new, NULL,
+ DOM_MUTATION_MODIFICATION);
+ dom_string_unref(t);
+ if (err != DOM_NO_ERR) {
+ goto cleanup;
+ }
+
+ err = dom_event_target_dispatch_event(et, evt, success);
+
+cleanup:
+ _dom_mutation_event_destroy(doc, evt);
+
+ return err;
+}
+
+/**
+ * Dispatch a DOMSubtreeModified event
+ *
+ * \param doc The Document
+ * \param et The EventTarget object
+ * \param success The newValue of the DOMCharacterData
+ * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
+ */
+dom_exception _dom_dispatch_subtree_modified_event(struct dom_document *doc,
+ dom_event_target *et, bool *success)
+{
+ struct dom_mutation_event *evt;
+ dom_exception err;
+
+ err = _dom_mutation_event_create(doc, &evt);
+ if (err != DOM_NO_ERR)
+ return err;
+
+ lwc_string *type = NULL;
+ err = _dom_document_create_lwcstring(doc,
+ (const uint8_t *) "DOMSubtreeModified",
+ SLEN("DOMSubtreeModified"), &type);
+ if (err != DOM_NO_ERR)
+ goto cleanup;
+
+ dom_string *t = NULL;
+ err = _dom_document_create_string_from_lwcstring(doc, type, &t);
+ _dom_document_unref_lwcstring(doc, type);
+ if (err != DOM_NO_ERR)
+ goto cleanup;
+
+ err = dom_mutation_event_init(evt, t, true, false, et, NULL, NULL, NULL,
+ DOM_MUTATION_MODIFICATION);
+ dom_string_unref(t);
+ if (err != DOM_NO_ERR) {
+ goto cleanup;
+ }
+
+ err = dom_event_target_dispatch_event(et, evt, success);
+
+cleanup:
+ _dom_mutation_event_destroy(doc, evt);
+
+ return err;
+}
+