1 /* DOM node selection */
9 #include "dom/css/scanner.h"
12 #include "dom/scanner.h"
13 #include "dom/select.h"
14 #include "dom/stack.h"
15 #include "dom/string.h"
16 #include "util/memory.h"
19 /* Selector parsing: */
21 /* Maps the content of a scanner token to a pseudo-class or -element ID. */
22 static enum dom_select_pseudo
23 get_dom_select_pseudo(struct dom_scanner_token
*token
)
26 struct dom_string string
;
27 enum dom_select_pseudo pseudo
;
30 #define INIT_DOM_SELECT_PSEUDO_STRING(str, type) \
31 { STATIC_DOM_STRING(str), DOM_SELECT_PSEUDO_##type }
33 INIT_DOM_SELECT_PSEUDO_STRING("first-line", FIRST_LINE
),
34 INIT_DOM_SELECT_PSEUDO_STRING("first-letter", FIRST_LETTER
),
35 INIT_DOM_SELECT_PSEUDO_STRING("selection", SELECTION
),
36 INIT_DOM_SELECT_PSEUDO_STRING("after", AFTER
),
37 INIT_DOM_SELECT_PSEUDO_STRING("before", BEFORE
),
38 INIT_DOM_SELECT_PSEUDO_STRING("link", LINK
),
39 INIT_DOM_SELECT_PSEUDO_STRING("visited", VISITED
),
40 INIT_DOM_SELECT_PSEUDO_STRING("active", ACTIVE
),
41 INIT_DOM_SELECT_PSEUDO_STRING("hover", HOVER
),
42 INIT_DOM_SELECT_PSEUDO_STRING("focus", FOCUS
),
43 INIT_DOM_SELECT_PSEUDO_STRING("target", TARGET
),
44 INIT_DOM_SELECT_PSEUDO_STRING("enabled", ENABLED
),
45 INIT_DOM_SELECT_PSEUDO_STRING("disabled", DISABLED
),
46 INIT_DOM_SELECT_PSEUDO_STRING("checked", CHECKED
),
47 INIT_DOM_SELECT_PSEUDO_STRING("indeterminate", INDETERMINATE
),
49 /* Content pseudo-classes: */
51 INIT_DOM_SELECT_PSEUDO_STRING("contains", CONTAINS
),
53 /* Structural pseudo-classes: */
55 INIT_DOM_SELECT_PSEUDO_STRING("nth-child", NTH_CHILD
),
56 INIT_DOM_SELECT_PSEUDO_STRING("nth-last-child", NTH_LAST_CHILD
),
57 INIT_DOM_SELECT_PSEUDO_STRING("first-child", FIRST_CHILD
),
58 INIT_DOM_SELECT_PSEUDO_STRING("last-child", LAST_CHILD
),
59 INIT_DOM_SELECT_PSEUDO_STRING("only-child", ONLY_CHILD
),
61 INIT_DOM_SELECT_PSEUDO_STRING("nth-of-type", NTH_TYPE
),
62 INIT_DOM_SELECT_PSEUDO_STRING("nth-last-of-type",NTH_LAST_TYPE
),
63 INIT_DOM_SELECT_PSEUDO_STRING("first-of-type", FIRST_TYPE
),
64 INIT_DOM_SELECT_PSEUDO_STRING("last-of-type", LAST_TYPE
),
65 INIT_DOM_SELECT_PSEUDO_STRING("only-of-type", ONLY_TYPE
),
67 INIT_DOM_SELECT_PSEUDO_STRING("root", ROOT
),
68 INIT_DOM_SELECT_PSEUDO_STRING("empty", EMPTY
),
70 #undef INIT_DOM_SELECT_PSEUDO_STRING
75 for (i
= 0; i
< sizeof_array(pseudo_info
); i
++)
76 if (!dom_string_casecmp(&pseudo_info
[i
].string
, &token
->string
))
77 return pseudo_info
[i
].pseudo
;
79 return DOM_SELECT_PSEUDO_UNKNOWN
;
82 /* Parses attribute selector. For example '[foo="bar"]' or '[foo|="boo"]'. */
84 parse_dom_select_attribute(struct dom_select_node
*sel
, struct dom_scanner
*scanner
)
86 struct dom_scanner_token
*token
= get_dom_scanner_token(scanner
);
90 if (token
->type
!= '[')
91 return DOM_CODE_SYNTAX_ERR
;
93 /* Get the attribute name. */
95 token
= get_next_dom_scanner_token(scanner
);
96 if (!token
|| token
->type
!= CSS_TOKEN_IDENT
)
97 return DOM_CODE_SYNTAX_ERR
;
99 copy_dom_string(&sel
->node
.string
, &token
->string
);
101 /* Get the optional '=' combo or ending ']'. */
103 token
= get_next_dom_scanner_token(scanner
);
104 if (!token
) return DOM_CODE_SYNTAX_ERR
;
106 switch (token
->type
) {
108 sel
->match
.attribute
|= DOM_SELECT_ATTRIBUTE_ANY
;
111 case CSS_TOKEN_SELECT_SPACE_LIST
:
112 sel
->match
.attribute
|= DOM_SELECT_ATTRIBUTE_SPACE_LIST
;
115 case CSS_TOKEN_SELECT_HYPHEN_LIST
:
116 sel
->match
.attribute
|= DOM_SELECT_ATTRIBUTE_HYPHEN_LIST
;
119 case CSS_TOKEN_SELECT_BEGIN
:
120 sel
->match
.attribute
|= DOM_SELECT_ATTRIBUTE_BEGIN
;
123 case CSS_TOKEN_SELECT_END
:
124 sel
->match
.attribute
|= DOM_SELECT_ATTRIBUTE_END
;
127 case CSS_TOKEN_SELECT_CONTAINS
:
128 sel
->match
.attribute
|= DOM_SELECT_ATTRIBUTE_CONTAINS
;
132 return DOM_CODE_SYNTAX_ERR
;
135 /* Get the required value. */
137 token
= get_next_dom_scanner_token(scanner
);
138 if (!token
) return DOM_CODE_SYNTAX_ERR
;
140 switch (token
->type
) {
141 case CSS_TOKEN_IDENT
:
142 case CSS_TOKEN_STRING
:
143 copy_dom_string(&sel
->node
.data
.attribute
.value
, &token
->string
);
147 return DOM_CODE_SYNTAX_ERR
;
150 /* Get the ending ']'. */
152 token
= get_next_dom_scanner_token(scanner
);
153 if (token
&& token
->type
== ']')
156 return DOM_CODE_SYNTAX_ERR
;
170 /* FIXME: Move somewhere else? dom/scanner.h? */
172 get_scanner_token_number(struct dom_scanner_token
*token
)
176 while (token
->string
.length
> 0 && isdigit(token
->string
.string
[0])) {
177 size_t old_number
= number
;
182 if (old_number
> number
)
185 number
+= token
->string
.string
[0] - '0';
186 skip_dom_scanner_token_char(token
);
192 /* Parses the '(...)' part of ':nth-of-type(...)' and ':nth-child(...)'. */
194 parse_dom_select_nth_arg(struct dom_select_nth_match
*nth
, struct dom_scanner
*scanner
)
196 struct dom_scanner_token
*token
= get_next_dom_scanner_token(scanner
);
200 if (!token
|| token
->type
!= '(')
201 return DOM_CODE_SYNTAX_ERR
;
203 token
= get_next_dom_scanner_token(scanner
);
205 return DOM_CODE_SYNTAX_ERR
;
207 switch (token
->type
) {
208 case CSS_TOKEN_IDENT
:
209 if (dom_scanner_token_contains(token
, "even")) {
213 } else if (dom_scanner_token_contains(token
, "odd")) {
218 /* Check for 'n' ident below. */
222 if (skip_css_tokens(scanner
, ')'))
225 return DOM_CODE_SYNTAX_ERR
;
230 token
= get_next_dom_scanner_token(scanner
);
231 if (!token
) return DOM_CODE_SYNTAX_ERR
;
233 if (token
->type
!= CSS_TOKEN_IDENT
)
236 if (token
->type
!= CSS_TOKEN_NUMBER
)
237 return DOM_CODE_SYNTAX_ERR
;
240 case CSS_TOKEN_NUMBER
:
241 number
= get_scanner_token_number(token
);
243 return DOM_CODE_VALUE_ERR
;
245 token
= get_next_dom_scanner_token(scanner
);
246 if (!token
) return DOM_CODE_SYNTAX_ERR
;
250 return DOM_CODE_SYNTAX_ERR
;
253 /* The rest can contain n+ part */
254 switch (token
->type
) {
255 case CSS_TOKEN_IDENT
:
256 if (!dom_scanner_token_contains(token
, "n"))
257 return DOM_CODE_SYNTAX_ERR
;
259 nth
->step
= sign
* number
;
261 token
= get_next_dom_scanner_token(scanner
);
262 if (!token
) return DOM_CODE_SYNTAX_ERR
;
264 if (token
->type
!= '+')
267 token
= get_next_dom_scanner_token(scanner
);
268 if (!token
) return DOM_CODE_SYNTAX_ERR
;
270 if (token
->type
!= CSS_TOKEN_NUMBER
)
273 number
= get_scanner_token_number(token
);
275 return DOM_CODE_VALUE_ERR
;
277 nth
->index
= sign
* number
;
282 nth
->index
= sign
* number
;
285 if (skip_css_tokens(scanner
, ')'))
288 return DOM_CODE_SYNTAX_ERR
;
291 /* Parse a pseudo-class or -element with the syntax: ':<ident>'. */
293 parse_dom_select_pseudo(struct dom_select
*select
, struct dom_select_node
*sel
,
294 struct dom_scanner
*scanner
)
296 struct dom_scanner_token
*token
= get_dom_scanner_token(scanner
);
297 enum dom_select_pseudo pseudo
;
300 /* Skip double :'s in front of some pseudo's (::first-line, etc.) */
302 token
= get_next_dom_scanner_token(scanner
);
303 } while (token
&& token
->type
== ':');
305 if (!token
|| token
->type
!= CSS_TOKEN_IDENT
)
306 return DOM_CODE_SYNTAX_ERR
;
308 pseudo
= get_dom_select_pseudo(token
);
310 case DOM_SELECT_PSEUDO_UNKNOWN
:
313 case DOM_SELECT_PSEUDO_CONTAINS
:
314 /* FIXME: E:contains("text") */
317 case DOM_SELECT_PSEUDO_NTH_CHILD
:
318 case DOM_SELECT_PSEUDO_NTH_LAST_CHILD
:
319 code
= parse_dom_select_nth_arg(&sel
->nth_child
, scanner
);
320 if (code
!= DOM_CODE_OK
)
323 sel
->match
.element
|= DOM_SELECT_ELEMENT_NTH_CHILD
;
326 case DOM_SELECT_PSEUDO_FIRST_CHILD
:
327 sel
->match
.element
|= DOM_SELECT_ELEMENT_NTH_CHILD
;
328 set_dom_select_nth_match(&sel
->nth_child
, 0, 1);
331 case DOM_SELECT_PSEUDO_LAST_CHILD
:
332 sel
->match
.element
|= DOM_SELECT_ELEMENT_NTH_CHILD
;
333 set_dom_select_nth_match(&sel
->nth_child
, 0, -1);
336 case DOM_SELECT_PSEUDO_ONLY_CHILD
:
337 sel
->match
.element
|= DOM_SELECT_ELEMENT_NTH_CHILD
;
338 set_dom_select_nth_match(&sel
->nth_child
, 0, 0);
341 case DOM_SELECT_PSEUDO_NTH_TYPE
:
342 case DOM_SELECT_PSEUDO_NTH_LAST_TYPE
:
343 code
= parse_dom_select_nth_arg(&sel
->nth_type
, scanner
);
344 if (code
!= DOM_CODE_OK
)
347 sel
->match
.element
|= DOM_SELECT_ELEMENT_NTH_TYPE
;
350 case DOM_SELECT_PSEUDO_FIRST_TYPE
:
351 sel
->match
.element
|= DOM_SELECT_ELEMENT_NTH_TYPE
;
352 set_dom_select_nth_match(&sel
->nth_type
, 0, 1);
355 case DOM_SELECT_PSEUDO_LAST_TYPE
:
356 sel
->match
.element
|= DOM_SELECT_ELEMENT_NTH_TYPE
;
357 set_dom_select_nth_match(&sel
->nth_type
, 0, -1);
360 case DOM_SELECT_PSEUDO_ONLY_TYPE
:
361 sel
->match
.element
|= DOM_SELECT_ELEMENT_NTH_TYPE
;
362 set_dom_select_nth_match(&sel
->nth_type
, 0, 0);
365 case DOM_SELECT_PSEUDO_ROOT
:
366 sel
->match
.element
|= DOM_SELECT_ELEMENT_ROOT
;
369 case DOM_SELECT_PSEUDO_EMPTY
:
370 sel
->match
.element
|= DOM_SELECT_ELEMENT_EMPTY
;
374 /* It's a bitflag! */
375 select
->pseudo
|= pseudo
;
381 /* The element relation flags are mutual exclusive. This macro can be used
382 * for checking if anyflag is set. */
383 #define get_element_relation(sel) \
384 ((sel)->match.element & DOM_SELECT_RELATION_FLAGS)
386 /* Parse a CSS3 selector and add selector nodes to the @select struct. */
388 parse_dom_select(struct dom_select
*select
, struct dom_stack
*stack
,
389 struct dom_string
*string
)
391 struct dom_scanner scanner
;
392 struct dom_select_node sel
;
394 init_dom_scanner(&scanner
, &dom_css_scanner_info
, string
, 0, 0, 1, 0, 0);
396 memset(&sel
, 0, sizeof(sel
));
398 while (dom_scanner_has_tokens(&scanner
)) {
399 struct dom_scanner_token
*token
= get_dom_scanner_token(&scanner
);
401 struct dom_select_node
*select_node
;
405 if (token
->type
== '{'
406 || token
->type
== '}'
407 || token
->type
== ';'
408 || token
->type
== ',')
411 /* Examine the selector fragment */
413 switch (token
->type
) {
414 case CSS_TOKEN_IDENT
:
415 sel
.node
.type
= DOM_NODE_ELEMENT
;
416 copy_dom_string(&sel
.node
.string
, &token
->string
);
417 if (dom_scanner_token_contains(token
, "*"))
418 sel
.match
.element
|= DOM_SELECT_ELEMENT_UNIVERSAL
;
422 case CSS_TOKEN_HEX_COLOR
:
424 sel
.node
.type
= DOM_NODE_ATTRIBUTE
;
425 sel
.match
.attribute
|= DOM_SELECT_ATTRIBUTE_ID
;
426 /* Skip the leading '#'. */
427 skip_dom_scanner_token_char(token
);
431 sel
.node
.type
= DOM_NODE_ATTRIBUTE
;
432 code
= parse_dom_select_attribute(&sel
, &scanner
);
433 if (code
!= DOM_CODE_OK
)
438 token
= get_next_dom_scanner_token(&scanner
);
439 if (!token
|| token
->type
!= CSS_TOKEN_IDENT
)
440 return DOM_CODE_SYNTAX_ERR
;
442 sel
.node
.type
= DOM_NODE_ATTRIBUTE
;
443 sel
.match
.attribute
|= DOM_SELECT_ATTRIBUTE_SPACE_LIST
;
444 set_dom_string(&sel
.node
.string
, "class", -1);
445 copy_dom_string(&sel
.node
.data
.attribute
.value
, &token
->string
);
449 code
= parse_dom_select_pseudo(select
, &sel
, &scanner
);
450 if (code
!= DOM_CODE_OK
)
455 if (get_element_relation(&sel
) != DOM_SELECT_RELATION_DESCENDANT
)
456 return DOM_CODE_SYNTAX_ERR
;
457 sel
.match
.element
|= DOM_SELECT_RELATION_DIRECT_CHILD
;
461 if (get_element_relation(&sel
) != DOM_SELECT_RELATION_DESCENDANT
)
462 return DOM_CODE_SYNTAX_ERR
;
463 sel
.match
.element
|= DOM_SELECT_RELATION_DIRECT_ADJACENT
;
467 if (get_element_relation(&sel
) != DOM_SELECT_RELATION_DESCENDANT
)
468 return DOM_CODE_SYNTAX_ERR
;
469 sel
.match
.element
|= DOM_SELECT_RELATION_INDIRECT_ADJACENT
;
473 return DOM_CODE_SYNTAX_ERR
;
476 skip_dom_scanner_token(&scanner
);
478 if (sel
.node
.type
== DOM_NODE_UNKNOWN
)
481 select_node
= mem_calloc(1, sizeof(*select_node
));
482 copy_struct(select_node
, &sel
);
484 if (!dom_stack_is_empty(stack
)) {
485 struct dom_node
*node
= &select_node
->node
;
486 struct dom_node
*parent
= get_dom_stack_top(stack
)->node
;
487 struct dom_node_list
**list
= get_dom_node_list(parent
, node
);
488 int sort
= (node
->type
== DOM_NODE_ATTRIBUTE
);
491 assertm(list
!= NULL
, "Adding node to bad parent [%d -> %d]",
492 node
->type
, parent
->type
);
494 index
= *list
&& (*list
)->size
> 0 && sort
495 ? get_dom_node_map_index(*list
, node
) : -1;
497 if (!add_to_dom_node_list(list
, node
, index
)) {
499 return DOM_CODE_ALLOC_ERR
;
502 node
->parent
= parent
;
505 assert(!select
->selector
);
506 select
->selector
= select_node
;
509 code
= push_dom_node(stack
, &select_node
->node
);
510 if (code
!= DOM_CODE_OK
)
513 if (select_node
->node
.type
!= DOM_NODE_ELEMENT
)
516 memset(&sel
, 0, sizeof(sel
));
519 if (select
->selector
)
525 /* Basically this is just a wrapper for parse_dom_select() to ease error
528 init_dom_select(enum dom_select_syntax syntax
, struct dom_string
*string
)
530 struct dom_select
*select
= mem_calloc(1, sizeof(select
));
531 struct dom_stack stack
;
534 init_dom_stack(&stack
, DOM_STACK_FLAG_NONE
);
535 add_dom_stack_tracer(&stack
, "init-select: ");
537 code
= parse_dom_select(select
, &stack
, string
);
538 done_dom_stack(&stack
);
540 if (code
== DOM_CODE_OK
)
543 done_dom_select(select
);
549 done_dom_select(struct dom_select
*select
)
551 if (select
->selector
) {
552 struct dom_node
*node
= (struct dom_node
*) select
->selector
;
554 /* This will recursively free all children select nodes. */
562 /* DOM node selection: */
564 /* This struct stores data related to the 'application' of a DOM selector
565 * on a DOM tree or stream. */
566 struct dom_select_data
{
567 /* The selector matching stack. The various selector nodes are pushed
568 * onto this stack as they are matched (and later popped when they are
569 * no longer 'reachable', that is, has been popped from the DOM tree or
570 * stream. This way the selector can match each selector node multiple
571 * times and the selection is a simple matter of matching the current
572 * node against each state on this stack. */
573 struct dom_stack stack
;
575 /* Reference to the selector. */
576 struct dom_select
*select
;
578 /* The list of nodes who have been matched / selected. */
579 struct dom_node_list
*list
;
582 /* This state struct is used for the select data stack and holds info about the
583 * node that was matched. */
584 struct dom_select_state
{
585 /* The matched node. This is always an element node. */
586 struct dom_node
*node
;
589 /* Get a child node of a given type. By design, a selector node can
590 * only have one child per type of node. */
591 static struct dom_select_node
*
592 get_child_dom_select_node(struct dom_select_node
*selector
,
593 enum dom_node_type type
)
595 struct dom_node_list
*children
= selector
->node
.data
.element
.children
;
596 struct dom_node
*node
;
602 foreach_dom_node (children
, node
, index
) {
603 if (node
->type
== type
)
604 return (struct dom_select_node
*) node
;
611 #define has_attribute_match(selector, name) \
612 ((selector)->match.attribute & (name))
615 match_attribute_value(struct dom_select_node
*selector
, struct dom_node
*node
)
617 struct dom_string str
;
618 struct dom_string
*selvalue
= &selector
->node
.data
.attribute
.value
;
619 struct dom_string
*value
= &node
->data
.attribute
.value
;
620 unsigned char separator
;
623 assert(selvalue
->length
);
625 /* The attribute selector value should atleast be contained in the
626 * attribute value. */
627 if (value
->length
< selvalue
->length
)
630 /* The following three matching methods requires the selector value to
631 * match a substring at a well-defined offset. */
633 if (has_attribute_match(selector
, DOM_SELECT_ATTRIBUTE_EXACT
)) {
634 return !dom_string_casecmp(value
, selvalue
);
637 if (has_attribute_match(selector
, DOM_SELECT_ATTRIBUTE_BEGIN
)) {
638 set_dom_string(&str
, value
->string
, selvalue
->length
);
640 return !dom_string_casecmp(&str
, selvalue
);
643 if (has_attribute_match(selector
, DOM_SELECT_ATTRIBUTE_END
)) {
644 size_t offset
= value
->length
- selvalue
->length
;
646 set_dom_string(&str
, value
->string
+ offset
, selvalue
->length
);
648 return !dom_string_casecmp(&str
, selvalue
);
651 /* The 3 following matching methods requires the selector value to be a
652 * substring of the value enclosed in a specific separator (with the
653 * begining and ending of the attribute value both being valid
656 set_dom_string(&str
, value
->string
, value
->length
);
658 if (has_attribute_match(selector
, DOM_SELECT_ATTRIBUTE_HYPHEN_LIST
)) {
661 } else if (has_attribute_match(selector
, DOM_SELECT_ATTRIBUTE_CONTAINS
)) {
664 } if (has_attribute_match(selector
, DOM_SELECT_ATTRIBUTE_SPACE_LIST
)) {
668 INTERNAL("No attribute selector matching method defined");
676 && !dom_string_ncasecmp(&str
, selvalue
, selvalue
->length
)) {
677 /* "Contains" matches no matter what comes after. */
678 if (str
.length
== selvalue
->length
)
683 /* "Contains" matches no matter what comes after. */
687 if (str
.string
[str
.length
] == separator
)
692 if (isspace(str
.string
[str
.length
]))
703 do_compare
= (str
.string
[0] == '-');
707 do_compare
= isspace(str
.string
[0]);
710 str
.length
--, str
.string
++;
712 } while (str
.length
>= selvalue
->length
);
717 /* Match the attribute of an element @node against attribute selector nodes
718 * of a given @base. */
720 match_attribute_selectors(struct dom_select_node
*base
, struct dom_node
*node
)
722 struct dom_node_list
*attrs
= node
->data
.element
.map
;
723 struct dom_node_list
*selnodes
= base
->node
.data
.element
.map
;
724 struct dom_node
*selnode
;
727 assert(base
->node
.type
== DOM_NODE_ELEMENT
728 && node
->type
== DOM_NODE_ELEMENT
);
730 /* If there are no attribute selectors that is a clean match ... */
734 /* ... the opposite goes if there are no attributes to match. */
738 foreach_dom_node (selnodes
, selnode
, index
) {
739 struct dom_select_node
*selector
= (void *) selnode
;
740 struct dom_node
*attr
;
742 if (has_attribute_match(selector
, DOM_SELECT_ATTRIBUTE_ID
)) {
746 foreach_dom_node (attrs
, attr
, idindex
) {
747 if (attr
->data
.attribute
.id
)
751 if (!is_dom_node_list_member(attrs
, idindex
))
755 attr
= get_dom_node_map_entry(attrs
, DOM_NODE_ATTRIBUTE
,
756 selnode
->data
.attribute
.type
,
763 if (has_attribute_match(selector
, DOM_SELECT_ATTRIBUTE_ANY
))
766 if (!match_attribute_value(selector
, attr
))
773 /* XXX: Assume the first context is the one! */
774 #define get_dom_select_state(stack, state) \
775 ((struct dom_select_state *) get_dom_stack_state_data((stack)->contexts[0], state))
778 match_element_relation(struct dom_select_node
*selector
, struct dom_node
*node
,
779 struct dom_stack
*stack
)
781 struct dom_stack_state
*state
;
782 enum dom_select_element_match relation
= get_element_relation(selector
);
787 /* When matching any relation there must be a parent, either so that
788 * the node is a descendant or it is possible to check for siblings. */
792 if (relation
!= DOM_SELECT_RELATION_DIRECT_CHILD
) {
793 /* When looking for preceeding siblings of the current node,
794 * the current node cannot be first or not in the list (-1). */
795 index
= get_dom_node_list_index(node
->parent
, node
);
802 /* Find states which hold the parent of the current selector
803 * and check if the parent selector's node is the parent of the
805 foreachback_dom_stack_state(stack
, state
, i
) {
806 struct dom_node
*selnode
;
808 /* We are only interested in states which hold the parent of
809 * the current selector. */
810 if (state
->node
!= selector
->node
.parent
)
813 selnode
= get_dom_select_state(stack
, state
)->node
;
815 if (relation
== DOM_SELECT_RELATION_DIRECT_CHILD
) {
816 /* Check if the parent selector's node is the parent of
817 * the current node. */
818 if (selnode
== node
->parent
)
824 /* Check if they are siblings. */
825 if (selnode
->parent
!= node
->parent
)
828 sibindex
= get_dom_node_list_index(node
->parent
, selnode
);
830 if (relation
== DOM_SELECT_RELATION_DIRECT_ADJACENT
) {
831 /* Check if the sibling node immediately
832 * preceeds the current node. */
833 if (sibindex
+ 1 == index
)
836 } else { /* DOM_SELECT_RELATION_INDIRECT_ADJACENT */
837 /* Check if the sibling node preceeds the
839 if (sibindex
< index
)
848 #define has_element_match(selector, name) \
849 ((selector)->match.element & (name))
852 match_element_selector(struct dom_select_node
*selector
, struct dom_node
*node
,
853 struct dom_stack
*stack
)
855 assert(node
&& node
->type
== DOM_NODE_ELEMENT
);
857 if (!has_element_match(selector
, DOM_SELECT_ELEMENT_UNIVERSAL
)
858 && dom_node_casecmp(&selector
->node
, node
))
861 if (get_element_relation(selector
) != DOM_SELECT_RELATION_DESCENDANT
862 && !match_element_relation(selector
, node
, stack
))
865 /* Root nodes either have no parents or are the single child of the
867 if (has_element_match(selector
, DOM_SELECT_ELEMENT_ROOT
)
869 if (node
->parent
->type
!= DOM_NODE_DOCUMENT
870 || node
->parent
->data
.document
.children
->size
> 1)
874 if (has_element_match(selector
, DOM_SELECT_ELEMENT_EMPTY
)
875 && node
->data
.element
.children
876 && node
->data
.element
.children
->size
> 0)
879 if (has_element_match(selector
, DOM_SELECT_ELEMENT_NTH_CHILD
)) {
884 if (has_element_match(selector
, DOM_SELECT_ELEMENT_NTH_TYPE
)) {
889 /* Check attribute selectors. */
890 if (selector
->node
.data
.element
.map
891 && !match_attribute_selectors(selector
, node
))
898 #define get_dom_select_data(stack) ((stack)->current->data)
900 /* Matches an element node being visited against the current selector stack. */
902 dom_select_push_element(struct dom_stack
*stack
, struct dom_node
*node
, void *data
)
904 struct dom_select_data
*select_data
= get_dom_select_data(stack
);
905 struct dom_stack_state
*state
;
908 foreach_dom_stack_state(&select_data
->stack
, state
, pos
) {
909 struct dom_select_node
*selector
= (void *) state
->node
;
911 /* FIXME: Since the same dom_select_node can be multiple times
912 * on the select_data->stack, cache what select nodes was
913 * matches so that it is only checked once. */
915 if (!match_element_selector(selector
, node
, &select_data
->stack
))
918 WDBG("Matched element: %.*s.", node
->string
.length
, node
->string
.string
);
919 /* This node is matched, so push the next selector node to
920 * match on the stack. */
921 selector
= get_child_dom_select_node(selector
, DOM_NODE_ELEMENT
);
923 push_dom_node(&select_data
->stack
, &selector
->node
);
929 /* Ensures that nodes, no longer 'reachable' on the stack do not have any
930 * states associated with them on the select data stack. */
932 dom_select_pop_element(struct dom_stack
*stack
, struct dom_node
*node
, void *data
)
934 struct dom_select_data
*select_data
= get_dom_select_data(stack
);
935 struct dom_stack_state
*state
;
938 stack
= &select_data
->stack
;
940 foreachback_dom_stack_state (stack
, state
, index
) {
941 struct dom_select_state
*select_state
;
943 select_state
= get_dom_select_state(stack
, state
);
944 if (select_state
->node
== node
) {
945 pop_dom_state(stack
, state
);
946 WDBG("Remove element.");
954 /* For now this is only for matching the ':contains(<string>)' pseudo-class.
955 * Any node which can contain text and thus characters from the given <string>
956 * are handled in this common callback. */
958 dom_select_push_text(struct dom_stack
*stack
, struct dom_node
*node
, void *data
)
960 struct dom_select_data
*select_data
= get_dom_select_data(stack
);
961 struct dom_stack_state
*state
= get_dom_stack_top(&select_data
->stack
);
962 struct dom_select_node
*selector
= (void *) state
->node
;
963 struct dom_select_node
*text_sel
= get_child_dom_select_node(selector
, DOM_NODE_TEXT
);
965 WDBG("Text node: %d chars", node
->string
.length
);
970 switch (node
->type
) {
972 case DOM_NODE_CDATA_SECTION
:
973 case DOM_NODE_ENTITY_REFERENCE
:
976 ERROR("Unhandled type");
982 /* Context info for interacting with the DOM tree or stream stack. */
983 static struct dom_stack_context_info dom_select_context_info
= {
984 /* Object size: */ 0,
988 /* DOM_NODE_ELEMENT */ dom_select_push_element
,
989 /* DOM_NODE_ATTRIBUTE */ NULL
,
990 /* DOM_NODE_TEXT */ dom_select_push_text
,
991 /* DOM_NODE_CDATA_SECTION */ dom_select_push_text
,
992 /* DOM_NODE_ENTITY_REFERENCE */ dom_select_push_text
,
993 /* DOM_NODE_ENTITY */ NULL
,
994 /* DOM_NODE_PROC_INSTRUCTION */ NULL
,
995 /* DOM_NODE_COMMENT */ NULL
,
996 /* DOM_NODE_DOCUMENT */ NULL
,
997 /* DOM_NODE_DOCUMENT_TYPE */ NULL
,
998 /* DOM_NODE_DOCUMENT_FRAGMENT */ NULL
,
999 /* DOM_NODE_NOTATION */ NULL
,
1004 /* DOM_NODE_ELEMENT */ dom_select_pop_element
,
1005 /* DOM_NODE_ATTRIBUTE */ NULL
,
1006 /* DOM_NODE_TEXT */ NULL
,
1007 /* DOM_NODE_CDATA_SECTION */ NULL
,
1008 /* DOM_NODE_ENTITY_REFERENCE */ NULL
,
1009 /* DOM_NODE_ENTITY */ NULL
,
1010 /* DOM_NODE_PROC_INSTRUCTION */ NULL
,
1011 /* DOM_NODE_COMMENT */ NULL
,
1012 /* DOM_NODE_DOCUMENT */ NULL
,
1013 /* DOM_NODE_DOCUMENT_TYPE */ NULL
,
1014 /* DOM_NODE_DOCUMENT_FRAGMENT */ NULL
,
1015 /* DOM_NODE_NOTATION */ NULL
,
1019 /* Context info related to the private select data stack of matched nodes. */
1020 static struct dom_stack_context_info dom_select_data_context_info
= {
1021 /* Object size: */ sizeof(struct dom_select_state
),
1025 /* DOM_NODE_ELEMENT */ NULL
,
1026 /* DOM_NODE_ATTRIBUTE */ NULL
,
1027 /* DOM_NODE_TEXT */ NULL
,
1028 /* DOM_NODE_CDATA_SECTION */ NULL
,
1029 /* DOM_NODE_ENTITY_REFERENCE */ NULL
,
1030 /* DOM_NODE_ENTITY */ NULL
,
1031 /* DOM_NODE_PROC_INSTRUCTION */ NULL
,
1032 /* DOM_NODE_COMMENT */ NULL
,
1033 /* DOM_NODE_DOCUMENT */ NULL
,
1034 /* DOM_NODE_DOCUMENT_TYPE */ NULL
,
1035 /* DOM_NODE_DOCUMENT_FRAGMENT */ NULL
,
1036 /* DOM_NODE_NOTATION */ NULL
,
1041 /* DOM_NODE_ELEMENT */ NULL
,
1042 /* DOM_NODE_ATTRIBUTE */ NULL
,
1043 /* DOM_NODE_TEXT */ NULL
,
1044 /* DOM_NODE_CDATA_SECTION */ NULL
,
1045 /* DOM_NODE_ENTITY_REFERENCE */ NULL
,
1046 /* DOM_NODE_ENTITY */ NULL
,
1047 /* DOM_NODE_PROC_INSTRUCTION */ NULL
,
1048 /* DOM_NODE_COMMENT */ NULL
,
1049 /* DOM_NODE_DOCUMENT */ NULL
,
1050 /* DOM_NODE_DOCUMENT_TYPE */ NULL
,
1051 /* DOM_NODE_DOCUMENT_FRAGMENT */ NULL
,
1052 /* DOM_NODE_NOTATION */ NULL
,
1057 struct dom_node_list
*
1058 select_dom_nodes(struct dom_select
*select
, struct dom_node
*root
)
1060 struct dom_select_data select_data
;
1061 struct dom_stack stack
;
1063 memset(&select_data
, 0, sizeof(select_data
));
1065 select_data
.select
= select
;;
1067 init_dom_stack(&stack
, DOM_STACK_FLAG_NONE
);
1068 add_dom_stack_context(&stack
, &select_data
,
1069 &dom_select_context_info
);
1070 add_dom_stack_tracer(&stack
, "select-tree: ");
1072 init_dom_stack(&select_data
.stack
, DOM_STACK_FLAG_NONE
);
1073 add_dom_stack_context(&select_data
.stack
, &select_data
,
1074 &dom_select_data_context_info
);
1075 add_dom_stack_tracer(&select_data
.stack
, "select-match: ");
1077 if (push_dom_node(&select_data
.stack
, &select
->selector
->node
) == DOM_CODE_OK
) {
1078 get_dom_stack_top(&select_data
.stack
)->immutable
= 1;
1079 walk_dom_nodes(&stack
, root
);
1082 done_dom_stack(&select_data
.stack
);
1083 done_dom_stack(&stack
);
1085 return select_data
.list
;