summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorMichael Drake <michael.drake@codethink.co.uk>2018-08-15 15:05:54 +0100
committerMichael Drake <michael.drake@codethink.co.uk>2018-08-15 15:05:54 +0100
commitb34c357bbd4883e409a8d617c528db92bf0963c0 (patch)
treeb272a51d36733739b05053437eca8aeba94724d7 /test
parentecb247eb155ff5495a91e32b6e416fd0b6c6a53e (diff)
downloadlibnslayout-b34c357bbd4883e409a8d617c528db92bf0963c0.tar.gz
libnslayout-b34c357bbd4883e409a8d617c528db92bf0963c0.tar.bz2
Library: DOM change watching is now the job of the client.
Diffstat (limited to 'test')
-rw-r--r--test/dom/Makefile13
-rw-r--r--test/dom/debug.c103
-rw-r--r--test/dom/debug.h41
-rw-r--r--test/dom/watcher.c231
-rw-r--r--test/dom/watcher.h71
-rw-r--r--test/test-loader.c75
6 files changed, 512 insertions, 22 deletions
diff --git a/test/dom/Makefile b/test/dom/Makefile
new file mode 100644
index 0000000..0b345cb
--- /dev/null
+++ b/test/dom/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for libnslayout
+#
+# Copyright 2015 Michael Drake <tlsa@netsurf-browser.org>
+#
+# Released under the ISC License (see COPYING file)
+
+# Sources
+DIR_SOURCES := \
+ debug.c \
+ watcher.c
+
+include $(NSBUILD)/Makefile.subdir
diff --git a/test/dom/debug.c b/test/dom/debug.c
new file mode 100644
index 0000000..9e3e0b0
--- /dev/null
+++ b/test/dom/debug.c
@@ -0,0 +1,103 @@
+/*
+ * This file is part of LibNSLayout
+ * Licensed under the ISC License, http://opensource.org/licenses/ISC
+ * Copyright 2015-2017 Michael Drake <tlsa@netsurf-browser.org>
+ */
+
+/** \file src/dom/debug.c
+ * DOM debug
+ */
+
+#include <stdio.h>
+
+#include <dom/dom.h>
+
+#include "debug.h"
+
+
+#ifdef NSL_DOM_TRACE
+
+/**
+ * Convert a dom node type to a string
+ *
+ * \param[in] type DOM node type
+ * \return appropriate string.
+ */
+static inline const char *nsl__dom_node_type_to_string(dom_node_type type)
+{
+ static const char *str[DOM_NODE_TYPE_COUNT] = {
+ [DOM_ELEMENT_NODE] = "ELEMENT_NODE",
+ [DOM_ATTRIBUTE_NODE] = "ATTRIBUTE_NODE",
+ [DOM_TEXT_NODE] = "TEXT_NODE",
+ [DOM_CDATA_SECTION_NODE] = "CDATA_SECTION_NODE",
+ [DOM_ENTITY_REFERENCE_NODE] = "ENTITY_REFERENCE_NODE",
+ [DOM_ENTITY_NODE] = "ENTITY_NODE",
+ [DOM_PROCESSING_INSTRUCTION_NODE] = "PROCESSING_INSTRUCTION_NODE",
+ [DOM_COMMENT_NODE] = "COMMENT_NODE",
+ [DOM_DOCUMENT_NODE] = "DOCUMENT_NODE",
+ [DOM_DOCUMENT_TYPE_NODE] = "DOCUMENT_TYPE_NODE",
+ [DOM_DOCUMENT_FRAGMENT_NODE] = "DOCUMENT_FRAGMENT_NODE",
+ [DOM_NOTATION_NODE] = "NOTATION_NODE"
+ };
+
+ return str[type];
+}
+
+
+/* Exported function, documented in src/dom/debug.h */
+void nsl__dom_debug_dump_event(const struct dom_event *evt)
+{
+ dom_event_target *node = NULL;
+ dom_node_type node_type;
+ dom_string *name = NULL;
+ dom_string *type = NULL;
+ dom_exception exc;
+
+ printf(" DOM Event: ");
+
+ /* Ugly test to see what events come out */
+ 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;
+ }
+
+ if (node_type == DOM_ELEMENT_NODE) {
+ exc = dom_node_get_node_name(node, &name);
+ if ((exc != DOM_NO_ERR) || (name == NULL)) {
+ printf("FAILED to get target node name!\n");
+ goto fail;
+ }
+ }
+
+ exc = dom_event_get_type(evt, &type);
+ if ((exc != DOM_NO_ERR) || (type == NULL)) {
+ printf("FAILED to get event type!\n");
+ goto fail;
+ }
+
+ if (node_type == DOM_ELEMENT_NODE) {
+ printf("<%s> %s",
+ dom_string_data(name),
+ dom_string_data(type));
+ } else {
+ printf("%s %s",
+ nsl__dom_node_type_to_string(node_type),
+ dom_string_data(type));
+ }
+
+fail:
+ if (type != NULL) dom_string_unref(type);
+ if (name != NULL) dom_string_unref(name);
+ if (node != NULL) dom_node_unref(node);
+
+ printf("\n");
+}
+
+#endif
diff --git a/test/dom/debug.h b/test/dom/debug.h
new file mode 100644
index 0000000..cbe8559
--- /dev/null
+++ b/test/dom/debug.h
@@ -0,0 +1,41 @@
+/*
+ * This file is part of LibNSLayout
+ * Licensed under the ISC License, http://opensource.org/licenses/ISC
+ * Copyright 2015-2017 Michael Drake <tlsa@netsurf-browser.org>
+ */
+
+/** \file src/dom/debug.h
+ * DOM debug
+ */
+
+#ifndef nsl_dom_debug_h_
+#define nsl_dom_debug_h_
+
+/** Define to enable DOM trace debug output */
+#undef NSL_DOM_TRACE
+#define NSL_DOM_TRACE
+
+#include "util/util.h"
+
+struct dom_event;
+
+/** Don't use directly */
+void nsl__dom_debug_dump_event(const struct dom_event *evt);
+
+
+/**
+ * Dump debug for dom event
+ *
+ * \param[in] evt Dump debug concerning a DOM event.
+ */
+static inline void nsl_dom_debug_dump_event(const struct dom_event *evt)
+{
+#ifdef NSL_DOM_TRACE
+ nsl__dom_debug_dump_event(evt);
+#else
+ UNUSED(evt);
+#endif
+}
+
+
+#endif
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;
+}
diff --git a/test/dom/watcher.h b/test/dom/watcher.h
new file mode 100644
index 0000000..cf26a56
--- /dev/null
+++ b/test/dom/watcher.h
@@ -0,0 +1,71 @@
+/*
+ * 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.h
+ * Interface to DOM mutation watching.
+ */
+
+#ifndef nsl_dom_watcher_h_
+#define nsl_dom_watcher_h_
+
+struct dom_document;
+struct nsl_dom_watcher;
+
+/**
+ * DOM watcher's mutation types
+ */
+enum nsl_dom_watcher_type {
+ NSL_DOM_WATCHER_NODE_INSERTED,
+ NSL_DOM_WATCHER_NODE_REMOVED,
+ NSL_DOM_WATCHER_SUBTREE_MODIFIED,
+ NSL_DOM_WATCHER_ATTR_MODIFIED,
+ NSL_DOM_WATCHER_CHAR_DATA_MODIFIED,
+ NSL_DOM_WATCHER__COUNT,
+};
+
+
+/**
+ * Callback function for dom modifications.
+ *
+ * \param[in] type The mutation type.
+ * \param[in] node The target node. (Caller yields ownership.)
+ * \param[in] node_type The type of node.
+ * \param[in] pw The dom watcher owner's private data.
+ * \return NSL_OK on success, appropriate error otherwise.
+ */
+typedef bool (*nsl_dom_watcher_cb)(
+ enum nsl_dom_watcher_type type,
+ dom_event_target *node,
+ dom_node_type node_type,
+ void *pw);
+
+
+/**
+ * Create DOM change watcher for a DOM document.
+ *
+ * \param[out] watcher_out Returns a dom watcher object for layout.
+ * \param[in] document DOM document to create watcher for.
+ * \param[in] watcher_cb Function to call when dom modification happens.
+ * \param[in] pw Private data passed back to `watcher_cb`.
+ * \return NSL_OK on success, appropriate error otherwise.
+ */
+bool nsl_dom_watcher_create(
+ struct nsl_dom_watcher **watcher_out,
+ dom_document *document,
+ nsl_dom_watcher_cb watcher_cb,
+ void *pw);
+
+
+/**
+ * Destroy a document change DOM change watcher.
+ *
+ * \param[in] watcher DOM change watcher to destroy.
+ * \return NSL_OK on success, appropriate error otherwise.
+ */
+bool nsl_dom_watcher_destroy(
+ struct nsl_dom_watcher *watcher);
+
+#endif
diff --git a/test/test-loader.c b/test/test-loader.c
index cab08d6..5cbd788 100644
--- a/test/test-loader.c
+++ b/test/test-loader.c
@@ -15,6 +15,8 @@
#include <libnslayout/nslayout.h>
+#include "dom/watcher.h"
+
#ifndef UNUSED
#define UNUSED(x) (void)(x)
#endif
@@ -42,11 +44,46 @@ struct test_loader_ctx {
dom_document *doc;
css_select_ctx *css_ctx;
css_stylesheet *css_sheet;
+
+ struct nsl_dom_watcher *watcher;
};
+/**
+ * Callback function for dom modifications.
+ *
+ * \param[in] type The mutation type.
+ * \param[in] node The target node. (Caller yields ownership.)
+ * \param[in] node_type The type of node.
+ * \param[in] pw The layout object.
+ * \return NSL_OK on success, appropriate error otherwise.
+ */
+static bool nsl_layout_dom_watcher_cb(
+ enum nsl_dom_watcher_type type,
+ dom_event_target *node,
+ dom_node_type node_type,
+ void *pw)
+{
+ nsl_layout *layout = pw;
+
+ UNUSED(type);
+ UNUSED(layout);
+ UNUSED(node_type);
+
+ /* TODO: Based on event type:
+ * 1. call to do (re)selection:
+ * a. all nodes?
+ * b. just this node?
+ * 2. call to update layout, if needed.
+ */
+
+ dom_node_unref(node);
+
+ return true;
+}
+
+
static bool test_loader_doc_load_start(
- size_t chunk_length,
struct test_loader_ctx *load_ctx)
{
dom_hubbub_parser_params params;
@@ -67,21 +104,6 @@ static bool test_loader_doc_load_start(
return false;
}
- /* Find length of first chunk */
- if (chunk_length > (load_ctx->html->len - load_ctx->html->pos))
- chunk_length = load_ctx->html->len - load_ctx->html->pos;
-
- /* Load first chunk */
- error = dom_hubbub_parser_parse_chunk(load_ctx->parser,
- load_ctx->html->buf + load_ctx->html->pos,
- chunk_length);
- load_ctx->html->pos += chunk_length;
- if (error != DOM_HUBBUB_OK) {
- dom_hubbub_parser_destroy(load_ctx->parser);
- printf("Parsing errors occur\n");
- return false;
- }
-
return true;
}
@@ -106,7 +128,6 @@ bool test_loader_doc_load_next(
chunk_length);
load_ctx->html->pos += chunk_length;
if (error != DOM_HUBBUB_OK) {
- dom_hubbub_parser_destroy(load_ctx->parser);
printf("Parsing errors occur\n");
return false;
}
@@ -122,14 +143,10 @@ bool test_loader_doc_load_next(
/* Done parsing file */
error = dom_hubbub_parser_completed(load_ctx->parser);
if (error != DOM_HUBBUB_OK) {
- dom_hubbub_parser_destroy(load_ctx->parser);
printf("Parsing error when construct DOM\n");
return false;
}
- /* Finished with parser */
- dom_hubbub_parser_destroy(load_ctx->parser);
-
return true;
}
@@ -308,11 +325,18 @@ static bool test_loader(
load_ctx.css_ctx = NULL;
printf("Starting load\n");
- if (!test_loader_doc_load_start(chunk_size, &load_ctx)) {
+ if (!test_loader_doc_load_start(&load_ctx)) {
printf("ERROR: doc_load_start\n");
goto fail;
}
+ printf("Adding dom watcher\n");
+ if (!nsl_dom_watcher_create(&load_ctx.watcher, load_ctx.doc,
+ nsl_layout_dom_watcher_cb, &load_ctx)) {
+ printf("ERROR: nsl_dom_watcher_create\n");
+ goto fail;
+ }
+
printf("Creating style context\n");
if (!test_loader_css_init(&load_ctx)) {
printf("ERROR: create_style_context\n");
@@ -349,8 +373,15 @@ fail:
nsl_layout_destroy(layout);
}
test_loader_css_fini(&load_ctx);
+
+ nsl_dom_watcher_destroy(load_ctx.watcher);
+
dom_node_unref(load_ctx.doc);
+ if (load_ctx.parser != NULL) {
+ dom_hubbub_parser_destroy(load_ctx.parser);
+ }
+
return ret;
}