summaryrefslogtreecommitdiff
path: root/test/dom/watcher.c
diff options
context:
space:
mode:
Diffstat (limited to 'test/dom/watcher.c')
-rw-r--r--test/dom/watcher.c231
1 files changed, 231 insertions, 0 deletions
diff --git a/test/dom/watcher.c b/test/dom/watcher.c
new file mode 100644
index 0000000..312dbf2
--- /dev/null
+++ b/test/dom/watcher.c
@@ -0,0 +1,231 @@
+/*
+ * This file is part of LibNSLayout
+ * Licensed under the ISC License, http://opensource.org/licenses/ISC
+ * Copyright 2015 Michael Drake <tlsa@netsurf-browser.org>
+ */
+
+/** \file src/dom/watcher.c
+ * Implementation of DOM mutation watching.
+ *
+ * TODO: LibDOM mutation event listeners are really slow.
+ * Need to find a better way to get DOM change notifications.
+ * LibDOM probably needs to gain Mutation Observer support, or
+ * gain a LibDOM-specific extension API.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "libnslayout/nslayout.h"
+
+#include "debug.h"
+#include "watcher.h"
+#include "util/dom-str.h"
+#include "util/util.h"
+
+
+/**
+ * A dom watcher object
+ */
+struct nsl_dom_watcher {
+ dom_document *document; /**< DOM document */
+ dom_event_listener *listener; /**< DOM event listener object */
+ nsl_dom_watcher_cb watcher_cb; /**< Client callback */
+ void *pw; /**< Client data */
+};
+
+/**
+ * LibDOM event handler
+ *
+ * \param[in] evt The LibDOM event object
+ * \param[in] pw Pointer to our dom watcher object
+ */
+static void nsl__dom_event_handler(struct dom_event *evt, void *pw)
+{
+ const struct nsl_dom_watcher *watcher = pw;
+ enum nsl_dom_watcher_type type;
+ dom_event_target *node = NULL;
+ dom_string *evt_type = NULL;
+ dom_node_type node_type;
+ dom_exception exc;
+
+ nsl_dom_debug_dump_event(evt);
+
+ exc = dom_event_get_target(evt, &node);
+ if ((exc != DOM_NO_ERR) || (node == NULL)) {
+ printf("FAILED to get target node!\n");
+ goto fail;
+ }
+
+ exc = dom_node_get_node_type(node, &node_type);
+ if (exc != DOM_NO_ERR) {
+ printf("FAILED to get target node type!\n");
+ goto fail;
+ }
+
+ exc = dom_event_get_type(evt, &evt_type);
+ if ((exc != DOM_NO_ERR) || (evt_type == NULL)) {
+ printf("FAILED to get event type!\n");
+ goto fail;
+ }
+
+ if (dom_string_isequal(evt_type,
+ nsl_dom_str_node_inserted)) {
+ type = NSL_DOM_WATCHER_NODE_INSERTED;
+
+ } else if (dom_string_isequal(evt_type,
+ nsl_dom_str_node_removed)) {
+ type = NSL_DOM_WATCHER_NODE_REMOVED;
+
+ } else if (dom_string_isequal(evt_type,
+ nsl_dom_str_subtree_modified)) {
+ type = NSL_DOM_WATCHER_SUBTREE_MODIFIED;
+
+ } else if (dom_string_isequal(evt_type,
+ nsl_dom_str_attr_modified)) {
+ type = NSL_DOM_WATCHER_ATTR_MODIFIED;
+
+ } else if (dom_string_isequal(evt_type,
+ nsl_dom_str_characterdata_modified)) {
+ type = NSL_DOM_WATCHER_CHAR_DATA_MODIFIED;
+ } else {
+ printf("FAILED: unrecognised event type: '%s'",
+ dom_string_data(evt_type));
+ goto fail;
+ }
+
+ dom_string_unref(evt_type);
+
+ watcher->watcher_cb(type, node, node_type, watcher->pw);
+ return;
+
+fail:
+ if (evt_type != NULL) dom_string_unref(evt_type);
+ if (node != NULL) dom_node_unref(node);
+}
+
+
+/**
+ * Destroy a DOM document's event listener
+ *
+ * \param[in] listener The listener to destroy
+ * \param[in] document The document that the listener was registerd for.
+ * \return NSL_OK on success, appropriate error otherwise.
+ */
+static nsl_error nsl__dom_listener_destroy(
+ dom_event_listener *listener,
+ dom_document *document)
+{
+ dom_exception exc;
+
+ /* Passing NULL as type, removes listener for all event types. */
+ exc = dom_event_target_remove_event_listener(
+ document, NULL, listener, false);
+ if (exc != DOM_NO_ERR) {
+ return NSL_DOM_ERR(exc);
+ }
+
+ dom_event_listener_unref(listener);
+
+ return NSL_OK;
+}
+
+
+/**
+ * Create a dom event listener.
+ *
+ * \param[out] listener_out Returns a dom listener for watcher's document.
+ * \param[in] watcher DOM watcher object that listener is used for.
+ * \return NSL_OK on success, appropriate error otherwise.
+ */
+static nsl_error nsl__dom_listener_create(
+ dom_event_listener **listener_out,
+ const struct nsl_dom_watcher *watcher)
+{
+ dom_event_listener *listener;
+ dom_exception exc;
+
+ /* Create listener */
+ exc = dom_event_listener_create(nsl__dom_event_handler,
+ (void *) watcher, &listener);
+ if (exc != DOM_NO_ERR) {
+ goto error;
+ }
+
+ /* Set the event types it should listen to */
+ exc = dom_event_target_add_event_listener(watcher->document,
+ nsl_dom_str_node_inserted, listener, false);
+ if (exc != DOM_NO_ERR) {
+ goto error;
+ }
+ exc = dom_event_target_add_event_listener(watcher->document,
+ nsl_dom_str_subtree_modified, listener, false);
+ if (exc != DOM_NO_ERR) {
+ goto error;
+ }
+ exc = dom_event_target_add_event_listener(watcher->document,
+ nsl_dom_str_node_removed, listener, false);
+ if (exc != DOM_NO_ERR) {
+ goto error;
+ }
+ exc = dom_event_target_add_event_listener(watcher->document,
+ nsl_dom_str_attr_modified, listener, false);
+ if (exc != DOM_NO_ERR) {
+ goto error;
+ }
+ exc = dom_event_target_add_event_listener(watcher->document,
+ nsl_dom_str_characterdata_modified, listener, false);
+ if (exc != DOM_NO_ERR) {
+ goto error;
+ }
+
+ *listener_out = listener;
+ return NSL_OK;
+
+error:
+ nsl__dom_listener_destroy(listener, watcher->document);
+
+ return NSL_DOM_ERR(exc);
+}
+
+/* Exported function, documented in src/dom/watcher.h */
+bool nsl_dom_watcher_create(
+ struct nsl_dom_watcher **watcher_out,
+ dom_document *document,
+ nsl_dom_watcher_cb watcher_cb,
+ void *pw)
+{
+ struct nsl_dom_watcher *watcher;
+ nsl_error err;
+
+ watcher = malloc(sizeof(*watcher));
+ if (watcher == NULL) {
+ return false;
+ }
+
+ watcher->document = document;
+ watcher->watcher_cb = watcher_cb;
+ watcher->pw = pw;
+
+ err = nsl__dom_listener_create(&watcher->listener, watcher);
+ if (err != NSL_OK) {
+ free(watcher);
+ return false;
+ }
+
+ *watcher_out = watcher;
+ return true;
+}
+
+/* Exported function, documented in src/dom/watcher.h */
+bool nsl_dom_watcher_destroy(
+ struct nsl_dom_watcher *watcher)
+{
+ nsl__dom_listener_destroy(watcher->listener, watcher->document);
+ free(watcher);
+
+ return true;
+}