diff options
author | Michael Drake <michael.drake@codethink.co.uk> | 2021-06-29 17:45:49 +0100 |
---|---|---|
committer | Michael Drake <michael.drake@codethink.co.uk> | 2021-06-29 17:46:52 +0100 |
commit | fc079b52cb5b91347983c0523c59a4ba268ef561 (patch) | |
tree | 47ffbc296065ada4a8af740b057519896c599cff | |
parent | 7595126d25277f09f9a0fb3769407428d3862402 (diff) | |
download | libdom-fc079b52cb5b91347983c0523c59a4ba268ef561.tar.gz libdom-fc079b52cb5b91347983c0523c59a4ba268ef561.tar.bz2 |
Add DOM tree walker functionality.
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | include/dom/walk.h | 65 | ||||
-rw-r--r-- | src/utils/Makefile | 2 | ||||
-rw-r--r-- | src/utils/walk.c | 130 |
4 files changed, 197 insertions, 2 deletions
@@ -56,7 +56,7 @@ include $(NSBUILD)/Makefile.top # Extra installation rules Is := include/dom I := /$(INCLUDEDIR)/dom -INSTALL_ITEMS := $(INSTALL_ITEMS) $(I):$(Is)/dom.h;$(Is)/functypes.h;$(Is)/inttypes.h +INSTALL_ITEMS := $(INSTALL_ITEMS) $(I):$(Is)/dom.h;$(Is)/functypes.h;$(Is)/inttypes.h;$(Is)/walk.h Is := include/dom/core I := /$(INCLUDEDIR)/dom/core diff --git a/include/dom/walk.h b/include/dom/walk.h new file mode 100644 index 0000000..0cd3fd0 --- /dev/null +++ b/include/dom/walk.h @@ -0,0 +1,65 @@ +/* + * This file is part of libdom. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2021 Michael Drake <tlsa@netsurf-browser.org> + */ + +/** \file + * This is an API for walking a loaded DOM. + */ + +#ifndef dom_walk_h_ +#define dom_walk_h_ + +enum dom_walk_stage { + DOM_WALK_STAGE_ENTER, + DOM_WALK_STAGE_LEAVE, +}; + +enum dom_walk_enable { + DOM_WALK_ENABLE_ENTER = (1 << DOM_WALK_STAGE_ENTER), + DOM_WALK_ENABLE_LEAVE = (1 << DOM_WALK_STAGE_LEAVE), + DOM_WALK_ENABLE_ALL = DOM_WALK_ENABLE_ENTER | DOM_WALK_ENABLE_LEAVE, +}; + +enum dom_walk_cmd { + DOM_WALK_CMD_CONTINUE, /**< Continue the tree walk. */ + DOM_WALK_CMD_ABORT, /**< Early termination of the tree walk. */ + DOM_WALK_CMD_SKIP, /**< Skip children (only for \ref DOM_WALK_ENABLE_ENTER). */ +}; + +/** + * DOM walking callback. + * + * Client callback for DOM walk. + * + * \param[in] stage Whether the \ref node is being entered or left. + * \param[in] node The node being walked. Client must take ref itself. + * \param[in] type The node type. + * \param[in] ctx Client private data. + * \return Tree walking client command. + */ +typedef enum dom_walk_cmd (*dom_walk_cb)( + enum dom_walk_stage stage, + dom_node_type type, + dom_node *node, + void *ctx); + + +/** + * Walk a DOM subtree. + * + * \param[in] mask Mask of stages to enable callback for. + * \param[in] cb The client callback function. + * \param[in] root Node to start walk from. + * \param[in] ctx The client's private data. + * \return false for early termination of walk, true otherwise. + */ +dom_exception libdom_treewalk( + enum dom_walk_enable mask, + dom_walk_cb cb, + dom_node *root, + void *ctx); + +#endif diff --git a/src/utils/Makefile b/src/utils/Makefile index 4bb586f..f891b6e 100644 --- a/src/utils/Makefile +++ b/src/utils/Makefile @@ -1,4 +1,4 @@ # Sources -DIR_SOURCES := namespace.c hashtable.c character_valid.c validate.c +DIR_SOURCES := namespace.c hashtable.c character_valid.c validate.c walk.c include $(NSBUILD)/Makefile.subdir diff --git a/src/utils/walk.c b/src/utils/walk.c new file mode 100644 index 0000000..20314f3 --- /dev/null +++ b/src/utils/walk.c @@ -0,0 +1,130 @@ +/* + * This file is part of libdom. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2021 Michael Drake <tlsa@netsurf-browser.org> + */ + +/** \file + * This is an API for walking a loaded DOM. + */ + +#include <dom/dom.h> +#include <dom/walk.h> + +/** + * Wrapper for calling client callback. + * + * \param[in] mask Mask of stages to enable callback for. + * \param[in] stage Whether the \ref node is being entered or left. + * \param[in] node The node being walked. + * \param[in] cb The client callback function. + * \param[in] ctx The client's private data. + * \param[out] cmd_out Walk instruction from client. + * \return false for early termination of walk, true otherwise. + */ +static inline dom_exception dom_walk__cb( + enum dom_walk_enable mask, + enum dom_walk_stage stage, + dom_node *node, + dom_walk_cb cb, + void *ctx, + enum dom_walk_cmd *cmd_out) +{ + if ((1 << stage) & mask) { + dom_node_type type; + dom_exception exc; + + exc = dom_node_get_node_type(node, &type); + if (exc != DOM_NO_ERR) { + return exc; + } + + *cmd_out = cb(stage, type, node, ctx); + } + + return DOM_NO_ERR; +} + +/* exported interface documented in include/dom/walk.h */ +dom_exception libdom_treewalk( + enum dom_walk_enable mask, + dom_walk_cb cb, + dom_node *root, + void *ctx) +{ + dom_node *node; + dom_exception exc; + enum dom_walk_cmd cmd = DOM_WALK_CMD_CONTINUE; + + node = dom_node_ref(root); + + while (cmd != DOM_WALK_CMD_ABORT) { + dom_node *next = NULL; + + if (cmd != DOM_WALK_CMD_SKIP) { + exc = dom_node_get_first_child(node, &next); + if (exc != DOM_NO_ERR) { + dom_node_unref(node); + break; + } + } + + if (next != NULL) { + dom_node_unref(node); + node = next; + } else { + /* No children; siblings & ancestor's siblings */ + while (node != root) { + exc = dom_walk__cb(mask, DOM_WALK_STAGE_LEAVE, + node, cb, ctx, &cmd); + if (exc != DOM_NO_ERR || + cmd == DOM_WALK_CMD_ABORT) { + dom_node_unref(node); + return exc; + } + + exc = dom_node_get_next_sibling(node, &next); + if (exc != DOM_NO_ERR) { + dom_node_unref(node); + node = NULL; + break; + } + + if (next != NULL) { + /* Found next sibling. */ + break; + } + + exc = dom_node_get_parent_node(node, &next); + if (exc != DOM_NO_ERR) { + dom_node_unref(node); + return exc; + } + + dom_node_unref(node); + node = next; + } + + if (node == root) { + break; + } + + dom_node_unref(node); + node = next; + } + + assert(node != NULL); + assert(node != root); + + exc = dom_walk__cb(mask, DOM_WALK_STAGE_ENTER, node, + cb, ctx, &cmd); + if (exc != DOM_NO_ERR) { + return exc; + } + } + + dom_node_unref(node); + + return DOM_NO_ERR; +} |