1 /* The DOM tree navigation interface */
12 #include "document/dom/node.h"
13 #include "document/dom/stack.h"
14 #include "util/memory.h"
15 #include "util/string.h"
18 /* Navigator states */
20 #define DOM_STACK_STATE_GRANULARITY 0x7
21 #define DOM_STACK_CALLBACKS_SIZE (sizeof(dom_stack_callback_T) * DOM_NODES)
23 static inline struct dom_stack_state
*
24 realloc_dom_stack_states(struct dom_stack_state
**states
, size_t size
)
26 return mem_align_alloc(states
, size
, size
+ 1,
27 struct dom_stack_state
,
28 DOM_STACK_STATE_GRANULARITY
);
31 static inline unsigned char *
32 realloc_dom_stack_state_objects(struct dom_stack
*stack
)
35 return mem_align_alloc__(__FILE__
, __LINE__
, (void **) &stack
->state_objects
,
36 stack
->depth
, stack
->depth
+ 1,
38 DOM_STACK_STATE_GRANULARITY
);
40 return mem_align_alloc__((void **) &stack
->state_objects
,
41 stack
->depth
, stack
->depth
+ 1,
43 DOM_STACK_STATE_GRANULARITY
);
48 init_dom_stack(struct dom_stack
*stack
, void *parser
, void *renderer
,
49 dom_stack_callback_T callbacks
[DOM_NODES
],
54 memset(stack
, 0, sizeof(*stack
));
56 stack
->parser
= parser
;
57 stack
->renderer
= renderer
;
58 stack
->object_size
= object_size
;
61 memcpy(stack
->callbacks
, callbacks
, DOM_STACK_CALLBACKS_SIZE
);
65 done_dom_stack(struct dom_stack
*stack
)
69 mem_free_if(stack
->states
);
70 mem_free_if(stack
->state_objects
);
72 memset(stack
, 0, sizeof(*stack
));
76 push_dom_node(struct dom_stack
*stack
, struct dom_node
*node
)
78 dom_stack_callback_T callback
;
79 struct dom_stack_state
*state
;
81 assert(stack
&& node
);
82 assert(0 < node
->type
&& node
->type
< DOM_NODES
);
84 if (stack
->depth
> DOM_STACK_MAX_DEPTH
) {
88 state
= realloc_dom_stack_states(&stack
->states
, stack
->depth
);
94 state
+= stack
->depth
;
96 if (stack
->object_size
) {
97 unsigned char *state_objects
;
99 state_objects
= realloc_dom_stack_state_objects(stack
);
100 if (!state_objects
) {
105 state
->depth
= stack
->depth
;
110 /* Grow the state array to the new depth so the state accessors work
111 * in the callbacks */
114 callback
= stack
->callbacks
[node
->type
];
116 void *state_data
= get_dom_stack_state_data(stack
, state
);
118 node
= callback(stack
, node
, state_data
);
120 /* If the callback returned NULL pop the state immediately */
122 memset(state
, 0, sizeof(*state
));
124 assert(stack
->depth
>= 0);
132 do_pop_dom_node(struct dom_stack
*stack
, struct dom_stack_state
*parent
)
134 struct dom_stack_state
*state
;
137 if (!dom_stack_has_parents(stack
)) return 0;
139 state
= get_dom_stack_top(stack
);
140 if (state
->callback
) {
141 void *state_data
= get_dom_stack_state_data(stack
, state
);
143 /* Pass the node we are popping to and _not_ the state->node */
144 state
->callback(stack
, parent
->node
, state_data
);
148 assert(stack
->depth
>= 0);
150 if (stack
->object_size
) {
151 void *state_data
= get_dom_stack_state_data(stack
, state
);
153 memset(state_data
, 0, stack
->object_size
);
156 memset(state
, 0, sizeof(*state
));
158 return state
== parent
;
162 pop_dom_node(struct dom_stack
*stack
)
165 if (!dom_stack_has_parents(stack
)) return;
167 do_pop_dom_node(stack
, get_dom_stack_parent(stack
));
171 pop_dom_nodes(struct dom_stack
*stack
, enum dom_node_type type
,
172 unsigned char *string
, uint16_t length
)
174 struct dom_stack_state
*state
, *parent
;
177 if (!dom_stack_has_parents(stack
)) return;
179 parent
= search_dom_stack(stack
, type
, string
, length
);
182 foreachback_dom_state (stack
, state
, pos
) {
183 if (do_pop_dom_node(stack
, parent
))
189 walk_dom_nodes(struct dom_stack
*stack
, struct dom_node
*root
)
191 assert(root
&& stack
);
193 push_dom_node(stack
, root
);
195 while (dom_stack_has_parents(stack
)) {
196 struct dom_stack_state
*state
= get_dom_stack_top(stack
);
197 struct dom_node_list
*list
= state
->list
;
198 struct dom_node
*node
= state
->node
;
200 switch (node
->type
) {
201 case DOM_NODE_DOCUMENT
:
202 if (!list
) list
= node
->data
.document
.children
;
205 case DOM_NODE_ELEMENT
:
206 if (!list
) list
= node
->data
.element
.map
;
208 if (list
== node
->data
.element
.children
) break;
209 if (is_dom_node_list_member(list
, state
->index
)
210 && list
== node
->data
.element
.map
)
213 list
= node
->data
.element
.children
;
216 case DOM_NODE_PROCESSING_INSTRUCTION
:
217 if (!list
) list
= node
->data
.proc_instruction
.map
;
220 case DOM_NODE_DOCUMENT_TYPE
:
221 if (!list
) list
= node
->data
.document_type
.entities
;
223 if (list
== node
->data
.document_type
.notations
) break;
224 if (is_dom_node_list_member(list
, state
->index
)
225 && list
== node
->data
.document_type
.entities
)
228 list
= node
->data
.document_type
.notations
;
231 case DOM_NODE_ATTRIBUTE
:
233 case DOM_NODE_CDATA_SECTION
:
234 case DOM_NODE_COMMENT
:
235 case DOM_NODE_NOTATION
:
236 case DOM_NODE_DOCUMENT_FRAGMENT
:
237 case DOM_NODE_ENTITY_REFERENCE
:
238 case DOM_NODE_ENTITY
:
243 /* Reset list state if it is a new list */
244 if (list
!= state
->list
) {
249 /* If we have next child node */
250 if (is_dom_node_list_member(list
, state
->index
)) {
251 struct dom_node
*child
= list
->entries
[state
->index
++];
253 if (push_dom_node(stack
, child
))