1 /* DOM node selection */
9 #include "document/css/scanner.h"
10 #include "document/dom/dom.h"
11 #include "document/dom/node.h"
12 #include "document/dom/select.h"
13 #include "document/dom/stack.h"
14 #include "util/memory.h"
15 #include "util/scanner.h"
16 #include "util/string.h"
19 static enum dom_select_pseudo
20 get_dom_select_pseudo(struct scanner_token
*token
)
23 struct dom_string string
;
24 enum dom_select_pseudo pseudo
;
27 #define INIT_DOM_SELECT_PSEUDO_STRING(str, type) \
28 { INIT_DOM_STRING(str, -1), DOM_SELECT_PSEUDO_##type }
30 INIT_DOM_SELECT_PSEUDO_STRING("first-line", FIRST_LINE
),
31 INIT_DOM_SELECT_PSEUDO_STRING("first-letter", FIRST_LETTER
),
32 INIT_DOM_SELECT_PSEUDO_STRING("selection", SELECTION
),
33 INIT_DOM_SELECT_PSEUDO_STRING("after", AFTER
),
34 INIT_DOM_SELECT_PSEUDO_STRING("before", BEFORE
),
35 INIT_DOM_SELECT_PSEUDO_STRING("link", LINK
),
36 INIT_DOM_SELECT_PSEUDO_STRING("visited", VISITED
),
37 INIT_DOM_SELECT_PSEUDO_STRING("active", ACTIVE
),
38 INIT_DOM_SELECT_PSEUDO_STRING("hover", HOVER
),
39 INIT_DOM_SELECT_PSEUDO_STRING("focus", FOCUS
),
40 INIT_DOM_SELECT_PSEUDO_STRING("target", TARGET
),
41 INIT_DOM_SELECT_PSEUDO_STRING("enabled", ENABLED
),
42 INIT_DOM_SELECT_PSEUDO_STRING("disabled", DISABLED
),
43 INIT_DOM_SELECT_PSEUDO_STRING("checked", CHECKED
),
44 INIT_DOM_SELECT_PSEUDO_STRING("indeterminate", INDETERMINATE
),
46 /* Content pseudo-classes: */
48 INIT_DOM_SELECT_PSEUDO_STRING("contains", CONTAINS
),
50 /* Structural pseudo-classes: */
52 INIT_DOM_SELECT_PSEUDO_STRING("nth-child", NTH_CHILD
),
53 INIT_DOM_SELECT_PSEUDO_STRING("nth-last-child", NTH_LAST_CHILD
),
54 INIT_DOM_SELECT_PSEUDO_STRING("first-child", FIRST_CHILD
),
55 INIT_DOM_SELECT_PSEUDO_STRING("last-child", LAST_CHILD
),
56 INIT_DOM_SELECT_PSEUDO_STRING("only-child", ONLY_CHILD
),
58 INIT_DOM_SELECT_PSEUDO_STRING("nth-of-type", NTH_TYPE
),
59 INIT_DOM_SELECT_PSEUDO_STRING("nth-last-of-type",NTH_LAST_TYPE
),
60 INIT_DOM_SELECT_PSEUDO_STRING("first-of-type", FIRST_TYPE
),
61 INIT_DOM_SELECT_PSEUDO_STRING("last-of-type", LAST_TYPE
),
62 INIT_DOM_SELECT_PSEUDO_STRING("only-of-type", ONLY_TYPE
),
64 INIT_DOM_SELECT_PSEUDO_STRING("root", ROOT
),
65 INIT_DOM_SELECT_PSEUDO_STRING("empty", EMPTY
),
67 #undef INIT_DOM_SELECT_PSEUDO_STRING
70 struct dom_string string
;
73 set_dom_string(&string
, token
->string
, token
->length
);
75 for (i
= 0; i
< sizeof_array(pseudo_info
); i
++)
76 if (!dom_string_casecmp(&pseudo_info
[i
].string
, &string
))
77 return pseudo_info
[i
].pseudo
;
79 return DOM_SELECT_PSEUDO_UNKNOWN
;
83 static enum dom_exception_code
84 parse_dom_select_attribute(struct dom_select_node
*sel
, struct scanner
*scanner
)
86 struct scanner_token
*token
= get_scanner_token(scanner
);
88 if (token
->type
!= '[')
89 return DOM_ERR_INVALID_STATE
;
91 token
= get_next_scanner_token(scanner
);
92 if (!token
|| token
->type
!= CSS_TOKEN_IDENT
)
93 return DOM_ERR_SYNTAX
;
95 set_dom_string(&sel
->node
.string
, token
->string
, token
->length
);
97 token
= get_next_scanner_token(scanner
);
98 if (!token
) return DOM_ERR_SYNTAX
;
100 switch (token
->type
) {
102 sel
->match
.attribute
|= DOM_SELECT_ATTRIBUTE_ANY
;
105 case CSS_TOKEN_SELECT_SPACE_LIST
:
106 sel
->match
.attribute
|= DOM_SELECT_ATTRIBUTE_SPACE_LIST
;
109 case CSS_TOKEN_SELECT_HYPHEN_LIST
:
110 sel
->match
.attribute
|= DOM_SELECT_ATTRIBUTE_HYPHEN_LIST
;
113 case CSS_TOKEN_SELECT_BEGIN
:
114 sel
->match
.attribute
|= DOM_SELECT_ATTRIBUTE_BEGIN
;
117 case CSS_TOKEN_SELECT_END
:
118 sel
->match
.attribute
|= DOM_SELECT_ATTRIBUTE_END
;
121 case CSS_TOKEN_SELECT_CONTAINS
:
122 sel
->match
.attribute
|= DOM_SELECT_ATTRIBUTE_CONTAINS
;
126 return DOM_ERR_SYNTAX
;
129 token
= get_next_scanner_token(scanner
);
130 if (!token
) return DOM_ERR_SYNTAX
;
132 switch (token
->type
) {
133 case CSS_TOKEN_IDENT
:
134 case CSS_TOKEN_STRING
:
135 set_dom_string(&sel
->node
.data
.attribute
.value
, token
->string
, token
->length
);
139 return DOM_ERR_SYNTAX
;
142 token
= get_next_scanner_token(scanner
);
143 if (token
&& token
->type
== ']')
146 return DOM_ERR_SYNTAX
;
161 get_scanner_token_number(struct scanner_token
*token
)
165 while (token
->length
> 0 && isdigit(token
->string
[0])) {
166 size_t old_number
= number
;
171 if (old_number
> number
)
174 number
+= token
->string
[0] - '0';
175 token
->string
++, token
->length
--;
181 static enum dom_exception_code
182 parse_dom_select_nth_arg(struct dom_select_nth_match
*nth
, struct scanner
*scanner
)
184 struct scanner_token
*token
= get_next_scanner_token(scanner
);
188 if (!token
|| token
->type
!= '(')
189 return DOM_ERR_SYNTAX
;
191 token
= get_next_scanner_token(scanner
);
193 return DOM_ERR_SYNTAX
;
195 switch (token
->type
) {
196 case CSS_TOKEN_IDENT
:
197 if (scanner_token_contains(token
, "even")) {
201 } else if (scanner_token_contains(token
, "odd")) {
206 /* Check for 'n' ident below. */
210 if (skip_css_tokens(scanner
, ')'))
213 return DOM_ERR_SYNTAX
;
218 token
= get_next_scanner_token(scanner
);
219 if (!token
) return DOM_ERR_SYNTAX
;
221 if (token
->type
!= CSS_TOKEN_IDENT
)
224 if (token
->type
!= CSS_TOKEN_NUMBER
)
225 return DOM_ERR_SYNTAX
;
228 case CSS_TOKEN_NUMBER
:
229 number
= get_scanner_token_number(token
);
231 return DOM_ERR_INVALID_STATE
;
233 token
= get_next_scanner_token(scanner
);
234 if (!token
) return DOM_ERR_SYNTAX
;
238 return DOM_ERR_SYNTAX
;
241 /* The rest can contain n+ part */
242 switch (token
->type
) {
243 case CSS_TOKEN_IDENT
:
244 if (!scanner_token_contains(token
, "n"))
245 return DOM_ERR_SYNTAX
;
247 nth
->step
= sign
* number
;
249 token
= get_next_scanner_token(scanner
);
250 if (!token
) return DOM_ERR_SYNTAX
;
252 if (token
->type
!= '+')
255 token
= get_next_scanner_token(scanner
);
256 if (!token
) return DOM_ERR_SYNTAX
;
258 if (token
->type
!= CSS_TOKEN_NUMBER
)
261 number
= get_scanner_token_number(token
);
263 return DOM_ERR_INVALID_STATE
;
265 nth
->index
= sign
* number
;
270 nth
->index
= sign
* number
;
273 if (skip_css_tokens(scanner
, ')'))
276 return DOM_ERR_SYNTAX
;
279 static enum dom_exception_code
280 parse_dom_select_pseudo(struct dom_select
*select
, struct dom_select_node
*sel
,
281 struct scanner
*scanner
)
283 struct scanner_token
*token
= get_scanner_token(scanner
);
284 enum dom_select_pseudo pseudo
;
285 enum dom_exception_code code
;
287 /* Skip double :'s in front of some pseudo's */
289 token
= get_next_scanner_token(scanner
);
290 } while (token
&& token
->type
== ':');
292 if (!token
|| token
->type
!= CSS_TOKEN_IDENT
)
293 return DOM_ERR_SYNTAX
;
295 pseudo
= get_dom_select_pseudo(token
);
297 case DOM_SELECT_PSEUDO_UNKNOWN
:
298 return DOM_ERR_NOT_FOUND
;
300 case DOM_SELECT_PSEUDO_CONTAINS
:
301 /* FIXME: E:contains("text") */
304 case DOM_SELECT_PSEUDO_NTH_CHILD
:
305 case DOM_SELECT_PSEUDO_NTH_LAST_CHILD
:
306 code
= parse_dom_select_nth_arg(&sel
->nth_child
, scanner
);
307 if (code
!= DOM_ERR_NONE
)
310 sel
->match
.element
|= DOM_SELECT_ELEMENT_NTH_CHILD
;
313 case DOM_SELECT_PSEUDO_FIRST_CHILD
:
314 sel
->match
.element
|= DOM_SELECT_ELEMENT_NTH_CHILD
;
315 set_dom_select_nth_match(&sel
->nth_child
, 0, 1);
318 case DOM_SELECT_PSEUDO_LAST_CHILD
:
319 sel
->match
.element
|= DOM_SELECT_ELEMENT_NTH_CHILD
;
320 set_dom_select_nth_match(&sel
->nth_child
, 0, -1);
323 case DOM_SELECT_PSEUDO_ONLY_CHILD
:
324 sel
->match
.element
|= DOM_SELECT_ELEMENT_NTH_CHILD
;
325 set_dom_select_nth_match(&sel
->nth_child
, 0, 0);
328 case DOM_SELECT_PSEUDO_NTH_TYPE
:
329 case DOM_SELECT_PSEUDO_NTH_LAST_TYPE
:
330 code
= parse_dom_select_nth_arg(&sel
->nth_type
, scanner
);
331 if (code
!= DOM_ERR_NONE
)
334 sel
->match
.element
|= DOM_SELECT_ELEMENT_NTH_TYPE
;
337 case DOM_SELECT_PSEUDO_FIRST_TYPE
:
338 sel
->match
.element
|= DOM_SELECT_ELEMENT_NTH_TYPE
;
339 set_dom_select_nth_match(&sel
->nth_type
, 0, 1);
342 case DOM_SELECT_PSEUDO_LAST_TYPE
:
343 sel
->match
.element
|= DOM_SELECT_ELEMENT_NTH_TYPE
;
344 set_dom_select_nth_match(&sel
->nth_type
, 0, -1);
347 case DOM_SELECT_PSEUDO_ONLY_TYPE
:
348 sel
->match
.element
|= DOM_SELECT_ELEMENT_NTH_TYPE
;
349 set_dom_select_nth_match(&sel
->nth_type
, 0, 0);
352 case DOM_SELECT_PSEUDO_ROOT
:
353 sel
->match
.element
|= DOM_SELECT_ELEMENT_ROOT
;
356 case DOM_SELECT_PSEUDO_EMPTY
:
357 sel
->match
.element
|= DOM_SELECT_ELEMENT_EMPTY
;
361 /* It's a bitflag! */
362 select
->pseudo
|= pseudo
;
368 #define get_element_relation(sel) \
369 ((sel)->match.element & DOM_SELECT_RELATION_FLAGS)
371 static enum dom_exception_code
372 parse_dom_select(struct dom_select
*select
, unsigned char *string
, int length
)
374 struct dom_stack stack
;
375 struct scanner scanner
;
376 struct dom_select_node sel
;
378 init_scanner(&scanner
, &css_scanner_info
, string
, string
+ length
);
379 init_dom_stack(&stack
, select
, 0, 1);
381 memset(&sel
, 0, sizeof(sel
));
383 while (scanner_has_tokens(&scanner
)) {
384 struct scanner_token
*token
= get_scanner_token(&scanner
);
385 enum dom_exception_code code
;
386 struct dom_select_node
*select_node
;
390 if (token
->type
== '{'
391 || token
->type
== '}'
392 || token
->type
== ';'
393 || token
->type
== ',')
396 /* Examine the selector fragment */
398 switch (token
->type
) {
399 case CSS_TOKEN_IDENT
:
400 sel
.node
.type
= DOM_NODE_ELEMENT
;
401 set_dom_string(&sel
.node
.string
, token
->string
, token
->length
);
402 if (token
->length
== 1 && token
->string
[0] == '*')
403 sel
.match
.element
|= DOM_SELECT_ELEMENT_UNIVERSAL
;
407 case CSS_TOKEN_HEX_COLOR
:
409 sel
.node
.type
= DOM_NODE_ATTRIBUTE
;
410 sel
.match
.attribute
|= DOM_SELECT_ATTRIBUTE_ID
;
411 /* Skip the leading '#'. */
412 token
->string
++, token
->length
--;
416 sel
.node
.type
= DOM_NODE_ATTRIBUTE
;
417 code
= parse_dom_select_attribute(&sel
, &scanner
);
418 if (code
!= DOM_ERR_NONE
)
423 token
= get_next_scanner_token(&scanner
);
424 if (!token
|| token
->type
!= CSS_TOKEN_IDENT
)
425 return DOM_ERR_SYNTAX
;
427 sel
.node
.type
= DOM_NODE_ATTRIBUTE
;
428 sel
.match
.attribute
|= DOM_SELECT_ATTRIBUTE_SPACE_LIST
;
429 set_dom_string(&sel
.node
.string
, "class", -1);
430 set_dom_string(&sel
.node
.data
.attribute
.value
, token
->string
, token
->length
);
434 code
= parse_dom_select_pseudo(select
, &sel
, &scanner
);
435 if (code
!= DOM_ERR_NONE
)
440 if (get_element_relation(&sel
))
441 return DOM_ERR_SYNTAX
;
442 sel
.match
.element
|= DOM_SELECT_RELATION_DIRECT_CHILD
;
446 if (get_element_relation(&sel
))
447 return DOM_ERR_SYNTAX
;
448 sel
.match
.element
|= DOM_SELECT_RELATION_DIRECT_ADJACENT
;
452 if (get_element_relation(&sel
))
453 return DOM_ERR_SYNTAX
;
454 sel
.match
.element
|= DOM_SELECT_RELATION_INDIRECT_ADJACENT
;
458 return DOM_ERR_SYNTAX
;
461 skip_scanner_token(&scanner
);
463 if (sel
.node
.type
== DOM_NODE_UNKNOWN
)
466 WDBG("Adding %s: %.*s", (sel
.node
.type
== DOM_NODE_ELEMENT
) ? "element" : "attr", sel
.node
.string
.length
, sel
.node
.string
.string
);
468 select_node
= mem_calloc(1, sizeof(*select_node
));
469 copy_struct(select_node
, &sel
);
471 if (select_node
->node
.parent
) {
472 struct dom_node
*node
= &select_node
->node
;
473 struct dom_node
*parent
= node
->parent
;
474 struct dom_node_list
**list
= get_dom_node_list(parent
, node
);
475 int sort
= (node
->type
== DOM_NODE_ATTRIBUTE
);
478 assertm(list
, "Adding node to bad parent",
479 get_dom_node_type_name(node
->type
),
480 get_dom_node_type_name(parent
->type
));
482 index
= *list
&& (*list
)->size
> 0 && sort
483 ? get_dom_node_map_index(*list
, node
) : -1;
485 if (!add_to_dom_node_list(list
, node
, index
)) {
487 return DOM_ERR_INVALID_STATE
;
490 assert(!select
->selector
);
491 select
->selector
= select_node
;
494 memset(&sel
, 0, sizeof(sel
));
495 sel
.node
.parent
= &select_node
->node
;
498 if (select
->selector
)
501 return DOM_ERR_INVALID_STATE
;
505 init_dom_select(enum dom_select_syntax syntax
,
506 unsigned char *string
, int length
)
508 struct dom_select
*select
= mem_calloc(1, sizeof(select
));
509 enum dom_exception_code code
;
511 code
= parse_dom_select(select
, string
, length
);
512 if (code
== DOM_ERR_NONE
)
515 done_dom_select(select
);
521 done_dom_select(struct dom_select
*select
)
523 if (select
->selector
) {
524 struct dom_node
*node
= (struct dom_node
*) select
->selector
;
533 struct dom_select_data
{
534 struct dom_stack stack
;
535 struct dom_select
*select
;
536 struct dom_node_list
*list
;
539 struct dom_select_state
{
540 struct dom_node
*node
;
545 compare_element_type(struct dom_node
*node1
, struct dom_node
*node2
)
547 /* Assuming the same document type */
548 if (node1
->data
.element
.type
549 && node2
->data
.element
.type
550 && node1
->data
.element
.type
== node2
->data
.element
.type
)
553 return dom_string_casecmp(&node1
->string
, &node2
->string
);
556 static struct dom_select_node
*
557 get_child_dom_select_node(struct dom_select_node
*selector
,
558 enum dom_node_type type
)
560 struct dom_node_list
*children
= selector
->node
.data
.element
.children
;
561 struct dom_node
*node
;
567 foreach_dom_node (children
, node
, index
) {
568 if (node
->type
== type
)
569 return (struct dom_select_node
*) node
;
576 #define has_attribute_match(selector, name) \
577 ((selector)->match.attribute & (name))
580 match_attribute_selectors(struct dom_select_node
*base
, struct dom_node
*node
)
582 struct dom_node_list
*attrs
= node
->data
.element
.map
;
583 struct dom_node_list
*selnodes
= base
->node
.data
.element
.map
;
584 struct dom_node
*selnode
;
587 assert(base
->node
.type
== DOM_NODE_ELEMENT
588 && node
->type
== DOM_NODE_ELEMENT
);
596 foreach_dom_node (selnodes
, selnode
, index
) {
597 struct dom_select_node
*selector
= (void *) selnode
;
598 struct dom_node
*attr
;
599 struct dom_string
*value
;
600 struct dom_string
*selvalue
;
602 if (has_attribute_match(selector
, DOM_SELECT_ATTRIBUTE_ID
)) {
606 foreach_dom_node (attrs
, attr
, idindex
) {
607 if (attr
->data
.attribute
.id
)
611 if (!is_dom_node_list_member(attrs
, idindex
))
615 attr
= get_dom_node_map_entry(attrs
, DOM_NODE_ATTRIBUTE
,
616 selnode
->data
.attribute
.type
,
623 if (has_attribute_match(selector
, DOM_SELECT_ATTRIBUTE_ANY
))
626 value
= &attr
->data
.attribute
.value
;
627 selvalue
= &selnode
->data
.attribute
.value
;
629 if (value
->length
< selvalue
->length
)
632 if (has_attribute_match(selector
, DOM_SELECT_ATTRIBUTE_EXACT
)
633 && dom_string_casecmp(value
, selvalue
))
636 if (has_attribute_match(selector
, DOM_SELECT_ATTRIBUTE_BEGIN
))
639 if (has_attribute_match(selector
, DOM_SELECT_ATTRIBUTE_END
))
642 if (has_attribute_match(selector
, DOM_SELECT_ATTRIBUTE_SPACE_LIST
))
645 if (has_attribute_match(selector
, DOM_SELECT_ATTRIBUTE_HYPHEN_LIST
))
648 if (has_attribute_match(selector
, DOM_SELECT_ATTRIBUTE_CONTAINS
))
655 #define has_element_match(selector, name) \
656 ((selector)->match.element & (name))
659 dom_select_push_element(struct dom_stack
*stack
, struct dom_node
*node
, void *data
)
661 struct dom_select_data
*select_data
= stack
->data
;
662 struct dom_stack_state
*state
;
665 WDBG("Push element %.*s.", node
->string
.length
, node
->string
.string
);
667 foreach_dom_stack_state(&select_data
->stack
, state
, pos
) {
668 struct dom_select_node
*selector
= (void *) state
->node
;
670 /* Match the node. */
671 if (!has_element_match(selector
, DOM_SELECT_ELEMENT_UNIVERSAL
)
672 && compare_element_type(&selector
->node
, node
))
675 switch (get_element_relation(selector
)) {
676 case DOM_SELECT_RELATION_DIRECT_CHILD
: /* E > F */
678 /* Check all states to see if node->parent is there
679 * and for the right reasons. */
682 case DOM_SELECT_RELATION_DIRECT_ADJACENT
: /* E + F */
683 /* Get preceding node to see if it is on the stack. */
686 case DOM_SELECT_RELATION_INDIRECT_ADJACENT
: /* E ~ F */
687 /* Check all states with same depth? */
690 case DOM_SELECT_RELATION_DESCENDANT
: /* E F */
695 /* Roots don't have parent nodes. */
696 if (has_element_match(selector
, DOM_SELECT_ELEMENT_ROOT
)
700 if (has_element_match(selector
, DOM_SELECT_ELEMENT_EMPTY
)
701 && node
->data
.element
.map
->size
> 0)
704 if (has_element_match(selector
, DOM_SELECT_ELEMENT_NTH_CHILD
)) {
709 if (has_element_match(selector
, DOM_SELECT_ELEMENT_NTH_TYPE
)) {
714 /* Check attribute selectors. */
715 if (selector
->node
.data
.element
.map
716 && !match_attribute_selectors(selector
, node
))
719 WDBG("Matched element: %.*s.", node
->string
.length
, node
->string
.string
);
720 /* This node is matched, so push the next selector node to
721 * match on the stack. */
722 selector
= get_child_dom_select_node(selector
, DOM_NODE_ELEMENT
);
724 push_dom_node(&select_data
->stack
, &selector
->node
);
729 dom_select_pop_element(struct dom_stack
*stack
, struct dom_node
*node
, void *data
)
731 struct dom_select_data
*select_data
= stack
->data
;
732 struct dom_stack_state
*state
;
735 WDBG("Pop element: %.*s", node
->string
.length
, node
->string
.string
);
736 stack
= &select_data
->stack
;
738 foreachback_dom_stack_state (stack
, state
, index
) {
739 struct dom_select_node
*selector
= (void *) state
->node
;
740 struct dom_select_state
*select_state
;
742 select_state
= get_dom_stack_state_data(stack
, state
);
743 if (select_state
->node
== node
) {
744 pop_dom_state(stack
, state
);
745 WDBG("Remove element.");
749 /* Pop states that no longer lives up to a relation. */
750 switch (get_element_relation(selector
)) {
751 case DOM_SELECT_RELATION_DIRECT_CHILD
: /* E > F */
752 case DOM_SELECT_RELATION_DIRECT_ADJACENT
: /* E + F */
753 case DOM_SELECT_RELATION_INDIRECT_ADJACENT
: /* E ~ F */
754 case DOM_SELECT_RELATION_DESCENDANT
: /* E F */
762 dom_select_push_text(struct dom_stack
*stack
, struct dom_node
*node
, void *data
)
764 struct dom_select_data
*select_data
= stack
->data
;
765 struct dom_stack_state
*state
= get_dom_stack_top(&select_data
->stack
);
766 struct dom_select_node
*selector
= (void *) state
->node
;
767 struct dom_select_node
*text_sel
= get_child_dom_select_node(selector
, DOM_NODE_TEXT
);
768 struct dom_string
*text
;
770 WDBG("Text node: %d chars", node
->string
.length
);
775 text
= &text_sel
->node
.string
;
777 switch (node
->type
) {
779 case DOM_NODE_CDATA_SECTION
:
780 case DOM_NODE_ENTITY_REFERENCE
:
783 ERROR("Unhandled type");
787 static struct dom_stack_context_info dom_select_context_info
= {
791 /* DOM_NODE_ELEMENT */ dom_select_push_element
,
792 /* DOM_NODE_ATTRIBUTE */ NULL
,
793 /* DOM_NODE_TEXT */ dom_select_push_text
,
794 /* DOM_NODE_CDATA_SECTION */ dom_select_push_text
,
795 /* DOM_NODE_ENTITY_REFERENCE */ dom_select_push_text
,
796 /* DOM_NODE_ENTITY */ NULL
,
797 /* DOM_NODE_PROC_INSTRUCTION */ NULL
,
798 /* DOM_NODE_COMMENT */ NULL
,
799 /* DOM_NODE_DOCUMENT */ NULL
,
800 /* DOM_NODE_DOCUMENT_TYPE */ NULL
,
801 /* DOM_NODE_DOCUMENT_FRAGMENT */ NULL
,
802 /* DOM_NODE_NOTATION */ NULL
,
807 /* DOM_NODE_ELEMENT */ dom_select_pop_element
,
808 /* DOM_NODE_ATTRIBUTE */ NULL
,
809 /* DOM_NODE_TEXT */ NULL
,
810 /* DOM_NODE_CDATA_SECTION */ NULL
,
811 /* DOM_NODE_ENTITY_REFERENCE */ NULL
,
812 /* DOM_NODE_ENTITY */ NULL
,
813 /* DOM_NODE_PROC_INSTRUCTION */ NULL
,
814 /* DOM_NODE_COMMENT */ NULL
,
815 /* DOM_NODE_DOCUMENT */ NULL
,
816 /* DOM_NODE_DOCUMENT_TYPE */ NULL
,
817 /* DOM_NODE_DOCUMENT_FRAGMENT */ NULL
,
818 /* DOM_NODE_NOTATION */ NULL
,
822 struct dom_node_list
*
823 select_dom_nodes(struct dom_select
*select
, struct dom_node
*root
)
825 struct dom_select_data select_data
;
826 struct dom_stack stack
;
827 size_t obj_size
= sizeof(struct dom_select_state
);
829 memset(&select_data
, 0, sizeof(select_data
));
831 select_data
.select
= select
;;
833 init_dom_stack(&stack
, &select_data
, 0, 1);
834 add_dom_stack_callbacks(&stack
, &dom_select_context_info
);
836 init_dom_stack(&select_data
.stack
, &select_data
, obj_size
, 1);
838 if (push_dom_node(&select_data
.stack
, &select
->selector
->node
)) {
839 get_dom_stack_top(&select_data
.stack
)->immutable
= 1;
840 walk_dom_nodes(&stack
, root
);
843 done_dom_stack(&select_data
.stack
);
844 done_dom_stack(&stack
);
846 return select_data
.list
;