Gitweb links:
...log
http://git.netsurf-browser.org/libdom.git/shortlog/6f9b1a501fa8b95ba0befc...
...commit
http://git.netsurf-browser.org/libdom.git/commit/6f9b1a501fa8b95ba0befc9f...
...tree
http://git.netsurf-browser.org/libdom.git/tree/6f9b1a501fa8b95ba0befc9f3e...
The branch, master has been updated
via 6f9b1a501fa8b95ba0befc9f3eea815f2ba4035d (commit)
via fc079b52cb5b91347983c0523c59a4ba268ef561 (commit)
from 7595126d25277f09f9a0fb3769407428d3862402 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff
http://git.netsurf-browser.org/libdom.git/commit/?id=6f9b1a501fa8b95ba0be...
commit 6f9b1a501fa8b95ba0befc9f3eea815f2ba4035d
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
Example: Convert to use DOM walk API.
diff --git a/examples/dom-structure-dump.c b/examples/dom-structure-dump.c
index c719903..2cdc7c5 100644
--- a/examples/dom-structure-dump.c
+++ b/examples/dom-structure-dump.c
@@ -44,6 +44,7 @@
#include <string.h>
#include <dom/dom.h>
+#include <dom/walk.h>
#include <dom/bindings/hubbub/parser.h>
@@ -181,6 +182,12 @@ bool dump_dom_element_attribute(dom_node *node, char *attribute)
return true;
}
+static inline void dump_indent(int depth)
+{
+ for (int i = 0; i < depth; i++) {
+ printf(" ");
+ }
+}
/**
* Print a line in a DOM structure dump for an element
@@ -189,25 +196,13 @@ bool dump_dom_element_attribute(dom_node *node, char *attribute)
* \param depth The node's depth
* \return true on success, or false on error
*/
-bool dump_dom_element(dom_node *node, int depth)
+bool dump_dom_element(dom_node *node, int depth, bool close)
{
dom_exception exc;
dom_string *node_name = NULL;
- dom_node_type type;
- int i;
const char *string;
size_t length;
- /* Only interested in element nodes */
- exc = dom_node_get_node_type(node, &type);
- if (exc != DOM_NO_ERR) {
- printf("Exception raised for node_get_node_type\n");
- return false;
- } else if (type != DOM_ELEMENT_NODE) {
- /* Nothing to print */
- return true;
- }
-
/* Get element name */
exc = dom_node_get_node_name(node, &node_name);
if (exc != DOM_NO_ERR) {
@@ -215,48 +210,84 @@ bool dump_dom_element(dom_node *node, int depth)
return false;
} else if (node_name == NULL) {
printf("Broken: root_name == NULL\n");
- return false;
+ return false;
}
/* Print ASCII tree structure for current node */
- if (depth > 0) {
- for (i = 0; i < depth; i++) {
- printf("| ");
- }
- printf("+-");
- }
+ dump_indent(depth);
/* Get string data and print element name */
string = dom_string_data(node_name);
length = dom_string_byte_length(node_name);
- printf("[%.*s]", (int)length, string);
-
- if (length == 5 && strncmp(string, "title", 5) == 0) {
- /* Title tag, gather the title */
- dom_string *str;
- exc = dom_node_get_text_content(node, &str);
- if (exc == DOM_NO_ERR && str != NULL) {
- printf(" $%.*s$", (int)dom_string_byte_length(str),
- dom_string_data(str));
- dom_string_unref(str);
- }
- }
- /* Finished with the node_name dom_string */
+ /* TODO: Some elements don't have close tags; only print close tags for
+ * those that do. */
+ printf("<%s%.*s", close ? "/" : "", (int)length,
string);
+
dom_string_unref(node_name);
- /* Print the element's id & class, if it has them */
- if (dump_dom_element_attribute(node, "id") == false ||
- dump_dom_element_attribute(node, "class") == false) {
- /* Error occured */
- printf("\n");
- return false;
+ if (!close) {
+ if (length == 5 && strncmp(string, "title", 5) == 0) {
+ /* Title tag, gather the title */
+ dom_string *s;
+ exc = dom_node_get_text_content(node, &s);
+ if (exc == DOM_NO_ERR && s != NULL) {
+ printf(" $%.*s$",
+ (int)dom_string_byte_length(s),
+ dom_string_data(s));
+ dom_string_unref(s);
+ }
+ }
+
+ /* Print the element's id & class, if it has them */
+ if (dump_dom_element_attribute(node, "id") == false ||
+ dump_dom_element_attribute(node, "class") == false) {
+ /* Error occured */
+ printf(">\n");
+ return false;
+ }
}
- printf("\n");
+ printf(">\n");
return true;
}
+/**
+ * Structure dump callback for DOM walker.
+ */
+enum dom_walk_cmd dump_dom_structure__cb(
+ enum dom_walk_stage stage,
+ dom_node_type type,
+ dom_node *node,
+ void *ctx)
+{
+ int *depth = ctx;
+
+ switch (type) {
+ case DOM_ELEMENT_NODE:
+ switch (stage) {
+ case DOM_WALK_STAGE_ENTER:
+ (*depth)++;
+ if (!dump_dom_element(node, *depth, false)) {
+ return DOM_WALK_CMD_ABORT;
+ }
+ break;
+
+ case DOM_WALK_STAGE_LEAVE:
+ if (!dump_dom_element(node, *depth, true)) {
+ return DOM_WALK_CMD_ABORT;
+ }
+ (*depth)--;
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return DOM_WALK_CMD_CONTINUE;
+}
/**
* Walk though a DOM (sub)tree, in depth first order, printing DOM structure.
@@ -267,46 +298,20 @@ bool dump_dom_element(dom_node *node, int depth)
bool dump_dom_structure(dom_node *node, int depth)
{
dom_exception exc;
- dom_node *child;
- /* Print this node's entry */
- if (dump_dom_element(node, depth) == false) {
- /* There was an error; return */
+ if (!dump_dom_element(node, depth, false)) {
return false;
}
- /* Get the node's first child */
- exc = dom_node_get_first_child(node, &child);
+ exc = libdom_treewalk(DOM_WALK_ENABLE_ALL,
+ dump_dom_structure__cb,
+ node, &depth);
if (exc != DOM_NO_ERR) {
- printf("Exception raised for node_get_first_child\n");
return false;
- } else if (child != NULL) {
- /* node has children; decend to children's depth */
- depth++;
-
- /* Loop though all node's children */
- do {
- dom_node *next_child;
-
- /* Visit node's descendents */
- if (dump_dom_structure(child, depth) == false) {
- /* There was an error; return */
- dom_node_unref(child);
- return false;
- }
-
- /* Go to next sibling */
- exc = dom_node_get_next_sibling(child, &next_child);
- if (exc != DOM_NO_ERR) {
- printf("Exception raised for "
- "node_get_next_sibling\n");
- dom_node_unref(child);
- return false;
- }
+ }
- dom_node_unref(child);
- child = next_child;
- } while (child != NULL); /* No more children */
+ if (!dump_dom_element(node, depth, true)) {
+ return false;
}
return true;
commitdiff
http://git.netsurf-browser.org/libdom.git/commit/?id=fc079b52cb5b91347983...
commit fc079b52cb5b91347983c0523c59a4ba268ef561
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
Add DOM tree walker functionality.
diff --git a/Makefile b/Makefile
index 4b82106..8d9df61 100644
--- a/Makefile
+++ b/Makefile
@@ -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(a)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(a)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;
+}
-----------------------------------------------------------------------
Summary of changes:
Makefile | 2 +-
examples/dom-structure-dump.c | 151 +++++++++++++++++++++--------------------
include/dom/walk.h | 65 ++++++++++++++++++
src/utils/Makefile | 2 +-
src/utils/walk.c | 130 +++++++++++++++++++++++++++++++++++
5 files changed, 275 insertions(+), 75 deletions(-)
create mode 100644 include/dom/walk.h
create mode 100644 src/utils/walk.c
diff --git a/Makefile b/Makefile
index 4b82106..8d9df61 100644
--- a/Makefile
+++ b/Makefile
@@ -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/examples/dom-structure-dump.c b/examples/dom-structure-dump.c
index c719903..2cdc7c5 100644
--- a/examples/dom-structure-dump.c
+++ b/examples/dom-structure-dump.c
@@ -44,6 +44,7 @@
#include <string.h>
#include <dom/dom.h>
+#include <dom/walk.h>
#include <dom/bindings/hubbub/parser.h>
@@ -181,6 +182,12 @@ bool dump_dom_element_attribute(dom_node *node, char *attribute)
return true;
}
+static inline void dump_indent(int depth)
+{
+ for (int i = 0; i < depth; i++) {
+ printf(" ");
+ }
+}
/**
* Print a line in a DOM structure dump for an element
@@ -189,25 +196,13 @@ bool dump_dom_element_attribute(dom_node *node, char *attribute)
* \param depth The node's depth
* \return true on success, or false on error
*/
-bool dump_dom_element(dom_node *node, int depth)
+bool dump_dom_element(dom_node *node, int depth, bool close)
{
dom_exception exc;
dom_string *node_name = NULL;
- dom_node_type type;
- int i;
const char *string;
size_t length;
- /* Only interested in element nodes */
- exc = dom_node_get_node_type(node, &type);
- if (exc != DOM_NO_ERR) {
- printf("Exception raised for node_get_node_type\n");
- return false;
- } else if (type != DOM_ELEMENT_NODE) {
- /* Nothing to print */
- return true;
- }
-
/* Get element name */
exc = dom_node_get_node_name(node, &node_name);
if (exc != DOM_NO_ERR) {
@@ -215,48 +210,84 @@ bool dump_dom_element(dom_node *node, int depth)
return false;
} else if (node_name == NULL) {
printf("Broken: root_name == NULL\n");
- return false;
+ return false;
}
/* Print ASCII tree structure for current node */
- if (depth > 0) {
- for (i = 0; i < depth; i++) {
- printf("| ");
- }
- printf("+-");
- }
+ dump_indent(depth);
/* Get string data and print element name */
string = dom_string_data(node_name);
length = dom_string_byte_length(node_name);
- printf("[%.*s]", (int)length, string);
-
- if (length == 5 && strncmp(string, "title", 5) == 0) {
- /* Title tag, gather the title */
- dom_string *str;
- exc = dom_node_get_text_content(node, &str);
- if (exc == DOM_NO_ERR && str != NULL) {
- printf(" $%.*s$", (int)dom_string_byte_length(str),
- dom_string_data(str));
- dom_string_unref(str);
- }
- }
- /* Finished with the node_name dom_string */
+ /* TODO: Some elements don't have close tags; only print close tags for
+ * those that do. */
+ printf("<%s%.*s", close ? "/" : "", (int)length,
string);
+
dom_string_unref(node_name);
- /* Print the element's id & class, if it has them */
- if (dump_dom_element_attribute(node, "id") == false ||
- dump_dom_element_attribute(node, "class") == false) {
- /* Error occured */
- printf("\n");
- return false;
+ if (!close) {
+ if (length == 5 && strncmp(string, "title", 5) == 0) {
+ /* Title tag, gather the title */
+ dom_string *s;
+ exc = dom_node_get_text_content(node, &s);
+ if (exc == DOM_NO_ERR && s != NULL) {
+ printf(" $%.*s$",
+ (int)dom_string_byte_length(s),
+ dom_string_data(s));
+ dom_string_unref(s);
+ }
+ }
+
+ /* Print the element's id & class, if it has them */
+ if (dump_dom_element_attribute(node, "id") == false ||
+ dump_dom_element_attribute(node, "class") == false) {
+ /* Error occured */
+ printf(">\n");
+ return false;
+ }
}
- printf("\n");
+ printf(">\n");
return true;
}
+/**
+ * Structure dump callback for DOM walker.
+ */
+enum dom_walk_cmd dump_dom_structure__cb(
+ enum dom_walk_stage stage,
+ dom_node_type type,
+ dom_node *node,
+ void *ctx)
+{
+ int *depth = ctx;
+
+ switch (type) {
+ case DOM_ELEMENT_NODE:
+ switch (stage) {
+ case DOM_WALK_STAGE_ENTER:
+ (*depth)++;
+ if (!dump_dom_element(node, *depth, false)) {
+ return DOM_WALK_CMD_ABORT;
+ }
+ break;
+
+ case DOM_WALK_STAGE_LEAVE:
+ if (!dump_dom_element(node, *depth, true)) {
+ return DOM_WALK_CMD_ABORT;
+ }
+ (*depth)--;
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return DOM_WALK_CMD_CONTINUE;
+}
/**
* Walk though a DOM (sub)tree, in depth first order, printing DOM structure.
@@ -267,46 +298,20 @@ bool dump_dom_element(dom_node *node, int depth)
bool dump_dom_structure(dom_node *node, int depth)
{
dom_exception exc;
- dom_node *child;
- /* Print this node's entry */
- if (dump_dom_element(node, depth) == false) {
- /* There was an error; return */
+ if (!dump_dom_element(node, depth, false)) {
return false;
}
- /* Get the node's first child */
- exc = dom_node_get_first_child(node, &child);
+ exc = libdom_treewalk(DOM_WALK_ENABLE_ALL,
+ dump_dom_structure__cb,
+ node, &depth);
if (exc != DOM_NO_ERR) {
- printf("Exception raised for node_get_first_child\n");
return false;
- } else if (child != NULL) {
- /* node has children; decend to children's depth */
- depth++;
-
- /* Loop though all node's children */
- do {
- dom_node *next_child;
-
- /* Visit node's descendents */
- if (dump_dom_structure(child, depth) == false) {
- /* There was an error; return */
- dom_node_unref(child);
- return false;
- }
-
- /* Go to next sibling */
- exc = dom_node_get_next_sibling(child, &next_child);
- if (exc != DOM_NO_ERR) {
- printf("Exception raised for "
- "node_get_next_sibling\n");
- dom_node_unref(child);
- return false;
- }
+ }
- dom_node_unref(child);
- child = next_child;
- } while (child != NULL); /* No more children */
+ if (!dump_dom_element(node, depth, true)) {
+ return false;
}
return true;
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(a)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(a)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;
+}
--
Document Object Model library