1 /*
2  * Copyright (C) 2021-2024 Alexander Borisov
3  *
4  * Author: Alexander Borisov <borisov@lexbor.com>
5  */
6 
7 #include "lexbor/selectors/selectors.h"
8 
9 #include <math.h>
10 
11 
12 static lxb_status_t
13 lxb_selectors_state_tree(lxb_selectors_t *selectors, lxb_dom_node_t *root,
14                          const lxb_css_selector_list_t *list);
15 
16 static lxb_status_t
17 lxb_selectors_state_run(lxb_selectors_t *selectors, lxb_dom_node_t *node,
18                         const lxb_css_selector_list_t *list);
19 
20 static lxb_selectors_entry_t *
21 lxb_selectors_state_find(lxb_selectors_t *selectors,
22                          lxb_selectors_entry_t *entry);
23 
24 static lxb_selectors_entry_t *
25 lxb_selectors_state_find_check(lxb_selectors_t *selectors, lxb_dom_node_t *node,
26                                const lxb_css_selector_t *selector,
27                                lxb_selectors_entry_t *entry);
28 
29 static lxb_selectors_entry_t *
30 lxb_selectors_state_pseudo_class_function(lxb_selectors_t *selectors,
31                                           lxb_selectors_entry_t *entry);
32 
33 static lxb_dom_node_t *
34 lxb_selectors_next_node(lxb_selectors_nested_t *main);
35 
36 static lxb_dom_node_t *
37 lxb_selectors_state_has_relative(lxb_dom_node_t *node,
38                                  const lxb_css_selector_t *selector);
39 
40 static lxb_selectors_entry_t *
41 lxb_selectors_state_after_find_has(lxb_selectors_t *selectors,
42                                    lxb_selectors_entry_t *entry);
43 
44 static lxb_selectors_entry_t *
45 lxb_selectors_state_after_find(lxb_selectors_t *selectors,
46                                lxb_selectors_entry_t *entry);
47 
48 static lxb_selectors_entry_t *
49 lxb_selectors_state_after_nth_child(lxb_selectors_t *selectors,
50                                     lxb_selectors_entry_t *entry);
51 
52 static bool
53 lxb_selectors_match(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry,
54                     const lxb_css_selector_t *selector, lxb_dom_node_t *node);
55 
56 static bool
57 lxb_selectors_match_element(const lxb_css_selector_t *selector,
58                             lxb_dom_node_t *node, lxb_selectors_entry_t *entry);
59 
60 static bool
61 lxb_selectors_match_id(const lxb_css_selector_t *selector, lxb_dom_node_t *node);
62 
63 static bool
64 lxb_selectors_match_class(const lexbor_str_t *target, const lexbor_str_t *src,
65                           bool quirks);
66 
67 static bool
68 lxb_selectors_match_attribute(const lxb_css_selector_t *selector,
69                               lxb_dom_node_t *node, lxb_selectors_entry_t *entry);
70 
71 static bool
72 lxb_selectors_pseudo_class(const lxb_css_selector_t *selector,
73                            const lxb_dom_node_t *node);
74 
75 static bool
76 lxb_selectors_pseudo_class_function(const lxb_css_selector_t *selector,
77                                     lxb_dom_node_t *node);
78 
79 static bool
80 lxb_selectors_pseudo_element(const lxb_css_selector_t *selector,
81                              const lxb_dom_node_t *node);
82 
83 static bool
84 lxb_selectors_pseudo_class_disabled(const lxb_dom_node_t *node);
85 
86 static bool
87 lxb_selectors_pseudo_class_first_child(const lxb_dom_node_t *node);
88 
89 static bool
90 lxb_selectors_pseudo_class_first_of_type(const lxb_dom_node_t *node);
91 
92 static bool
93 lxb_selectors_pseudo_class_last_child(const lxb_dom_node_t *node);
94 
95 static bool
96 lxb_selectors_pseudo_class_last_of_type(const lxb_dom_node_t *node);
97 
98 static bool
99 lxb_selectors_pseudo_class_read_write(const lxb_dom_node_t *node);
100 
101 static bool
102 lxb_selectors_anb_calc(lxb_css_selector_anb_of_t *anb, size_t index);
103 
104 static lxb_status_t
105 lxb_selectors_cb_ok(lxb_dom_node_t *node,
106                     lxb_css_selector_specificity_t spec, void *ctx);
107 
108 static lxb_status_t
109 lxb_selectors_cb_not(lxb_dom_node_t *node,
110                      lxb_css_selector_specificity_t spec, void *ctx);
111 
112 
113 lxb_selectors_t *
lxb_selectors_create(void)114 lxb_selectors_create(void)
115 {
116     return lexbor_calloc(1, sizeof(lxb_selectors_t));
117 }
118 
119 lxb_status_t
lxb_selectors_init(lxb_selectors_t * selectors)120 lxb_selectors_init(lxb_selectors_t *selectors)
121 {
122     lxb_status_t status;
123 
124     if (selectors == NULL) {
125         return LXB_STATUS_ERROR_INCOMPLETE_OBJECT;
126     }
127 
128     selectors->objs = lexbor_dobject_create();
129     status = lexbor_dobject_init(selectors->objs,
130                                  128, sizeof(lxb_selectors_entry_t));
131     if (status != LXB_STATUS_OK) {
132         return status;
133     }
134 
135     selectors->nested = lexbor_dobject_create();
136     status = lexbor_dobject_init(selectors->nested,
137                                  64, sizeof(lxb_selectors_nested_t));
138     if (status != LXB_STATUS_OK) {
139         return status;
140     }
141 
142     selectors->options = LXB_SELECTORS_OPT_DEFAULT;
143 
144     return LXB_STATUS_OK;
145 }
146 
147 void
lxb_selectors_clean(lxb_selectors_t * selectors)148 lxb_selectors_clean(lxb_selectors_t *selectors)
149 {
150     lexbor_dobject_clean(selectors->objs);
151     lexbor_dobject_clean(selectors->nested);
152 }
153 
154 lxb_selectors_t *
lxb_selectors_destroy(lxb_selectors_t * selectors,bool self_destroy)155 lxb_selectors_destroy(lxb_selectors_t *selectors, bool self_destroy)
156 {
157     if (selectors == NULL) {
158         return NULL;
159     }
160 
161     selectors->objs = lexbor_dobject_destroy(selectors->objs, true);
162     selectors->nested = lexbor_dobject_destroy(selectors->nested, true);
163 
164     if (self_destroy) {
165         return lexbor_free(selectors);
166     }
167 
168     return selectors;
169 }
170 
171 lxb_inline lxb_dom_node_t *
lxb_selectors_descendant(lxb_selectors_t * selectors,lxb_selectors_entry_t * entry,const lxb_css_selector_t * selector,lxb_dom_node_t * node)172 lxb_selectors_descendant(lxb_selectors_t *selectors,
173                          lxb_selectors_entry_t *entry,
174                          const lxb_css_selector_t *selector,
175                          lxb_dom_node_t *node)
176 {
177     node = node->parent;
178 
179     while (node != NULL) {
180         if (node->type == LXB_DOM_NODE_TYPE_ELEMENT
181             && lxb_selectors_match(selectors, entry, selector, node))
182         {
183             return node;
184         }
185 
186         node = node->parent;
187     }
188 
189     return NULL;
190 }
191 
192 lxb_inline lxb_dom_node_t *
lxb_selectors_close(lxb_selectors_t * selectors,lxb_selectors_entry_t * entry,const lxb_css_selector_t * selector,lxb_dom_node_t * node)193 lxb_selectors_close(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry,
194                     const lxb_css_selector_t *selector, lxb_dom_node_t *node)
195 {
196     if (lxb_selectors_match(selectors, entry, selector, node)) {
197         return node;
198     }
199 
200     return NULL;
201 }
202 
203 lxb_inline lxb_dom_node_t *
lxb_selectors_child(lxb_selectors_t * selectors,lxb_selectors_entry_t * entry,const lxb_css_selector_t * selector,lxb_dom_node_t * root)204 lxb_selectors_child(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry,
205                     const lxb_css_selector_t *selector, lxb_dom_node_t *root)
206 {
207     root = root->parent;
208 
209     if (root != NULL && root->type == LXB_DOM_NODE_TYPE_ELEMENT
210         && lxb_selectors_match(selectors, entry, selector, root))
211     {
212         return root;
213     }
214 
215     return NULL;
216 }
217 
218 lxb_inline lxb_dom_node_t *
lxb_selectors_sibling(lxb_selectors_t * selectors,lxb_selectors_entry_t * entry,const lxb_css_selector_t * selector,lxb_dom_node_t * node)219 lxb_selectors_sibling(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry,
220                       const lxb_css_selector_t *selector, lxb_dom_node_t *node)
221 {
222     node = node->prev;
223 
224     while (node != NULL) {
225         if (node->type == LXB_DOM_NODE_TYPE_ELEMENT) {
226             if (lxb_selectors_match(selectors, entry, selector, node)) {
227                 return node;
228             }
229 
230             return NULL;
231         }
232 
233         node = node->prev;
234     }
235 
236     return NULL;
237 }
238 
239 lxb_inline lxb_dom_node_t *
lxb_selectors_following(lxb_selectors_t * selectors,lxb_selectors_entry_t * entry,const lxb_css_selector_t * selector,lxb_dom_node_t * node)240 lxb_selectors_following(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry,
241                         const lxb_css_selector_t *selector, lxb_dom_node_t *node)
242 {
243     node = node->prev;
244 
245     while (node != NULL) {
246         if (node->type == LXB_DOM_NODE_TYPE_ELEMENT &&
247             lxb_selectors_match(selectors, entry, selector, node))
248         {
249             return node;
250         }
251 
252         node = node->prev;
253     }
254 
255     return NULL;
256 }
257 
258 lxb_status_t
lxb_selectors_find(lxb_selectors_t * selectors,lxb_dom_node_t * root,const lxb_css_selector_list_t * list,lxb_selectors_cb_f cb,void * ctx)259 lxb_selectors_find(lxb_selectors_t *selectors, lxb_dom_node_t *root,
260                    const lxb_css_selector_list_t *list,
261                    lxb_selectors_cb_f cb, void *ctx)
262 {
263     lxb_selectors_entry_t *entry;
264     lxb_selectors_nested_t nested;
265 
266     entry = lexbor_dobject_calloc(selectors->objs);
267     if (entry == NULL) {
268         return LXB_STATUS_ERROR_MEMORY_ALLOCATION;
269     }
270 
271     entry->combinator = LXB_CSS_SELECTOR_COMBINATOR_CLOSE;
272     entry->selector = list->last;
273 
274     nested.parent = NULL;
275     nested.entry = entry;
276     nested.cb = cb;
277     nested.ctx = ctx;
278 
279     selectors->current = &nested;
280     selectors->status = LXB_STATUS_OK;
281 
282     return lxb_selectors_state_tree(selectors, root, list);
283 }
284 
285 lxb_status_t
lxb_selectors_match_node(lxb_selectors_t * selectors,lxb_dom_node_t * node,const lxb_css_selector_list_t * list,lxb_selectors_cb_f cb,void * ctx)286 lxb_selectors_match_node(lxb_selectors_t *selectors, lxb_dom_node_t *node,
287                          const lxb_css_selector_list_t *list,
288                          lxb_selectors_cb_f cb, void *ctx)
289 {
290     lxb_status_t status;
291     lxb_selectors_entry_t *entry;
292     lxb_selectors_nested_t nested;
293 
294     if (node->type != LXB_DOM_NODE_TYPE_ELEMENT) {
295         return LXB_STATUS_OK;
296     }
297 
298     entry = lexbor_dobject_calloc(selectors->objs);
299     if (entry == NULL) {
300         return LXB_STATUS_ERROR_MEMORY_ALLOCATION;
301     }
302 
303     entry->combinator = LXB_CSS_SELECTOR_COMBINATOR_CLOSE;
304     entry->selector = list->last;
305 
306     nested.parent = NULL;
307     nested.entry = entry;
308     nested.cb = cb;
309     nested.ctx = ctx;
310 
311     selectors->current = &nested;
312     selectors->status = LXB_STATUS_OK;
313 
314     status = lxb_selectors_state_run(selectors, node, list);
315 
316     lxb_selectors_clean(selectors);
317 
318     return status;
319 }
320 
321 lxb_status_t
lxb_selectors_find_reverse(lxb_selectors_t * selectors,lxb_dom_node_t * root,lxb_css_selector_list_t * list,lxb_selectors_cb_f cb,void * ctx)322 lxb_selectors_find_reverse(lxb_selectors_t *selectors, lxb_dom_node_t *root,
323                            lxb_css_selector_list_t *list,
324                            lxb_selectors_cb_f cb, void *ctx)
325 {
326     return lxb_selectors_find(selectors, root, list, cb, ctx);
327 }
328 
329 static lxb_status_t
lxb_selectors_state_tree(lxb_selectors_t * selectors,lxb_dom_node_t * root,const lxb_css_selector_list_t * list)330 lxb_selectors_state_tree(lxb_selectors_t *selectors, lxb_dom_node_t *root,
331                          const lxb_css_selector_list_t *list)
332 {
333     lxb_status_t status;
334     lxb_dom_node_t *node;
335 
336     if (selectors->options & LXB_SELECTORS_OPT_MATCH_ROOT) {
337         node = root;
338 
339         if (node->type == LXB_DOM_NODE_TYPE_DOCUMENT) {
340             node = root->first_child;
341         }
342     }
343     else {
344         node = root->first_child;
345     }
346 
347     if (node == NULL) {
348         goto out;
349     }
350 
351     do {
352         if (node->type != LXB_DOM_NODE_TYPE_ELEMENT) {
353             goto next;
354         }
355 
356         status = lxb_selectors_state_run(selectors, node, list);
357         if (status != LXB_STATUS_OK) {
358             if (status == LXB_STATUS_STOP) {
359                 break;
360             }
361 
362             lxb_selectors_clean(selectors);
363 
364             return status;
365         }
366 
367         if (node->first_child != NULL) {
368             node = node->first_child;
369         }
370         else {
371 
372         next:
373 
374             while (node != root && node->next == NULL) {
375                 node = node->parent;
376             }
377 
378             if (node == root) {
379                 break;
380             }
381 
382             node = node->next;
383         }
384     }
385     while (true);
386 
387 out:
388     lxb_selectors_clean(selectors);
389 
390     return LXB_STATUS_OK;
391 }
392 
393 static lxb_status_t
lxb_selectors_state_run(lxb_selectors_t * selectors,lxb_dom_node_t * node,const lxb_css_selector_list_t * list)394 lxb_selectors_state_run(lxb_selectors_t *selectors, lxb_dom_node_t *node,
395                         const lxb_css_selector_list_t *list)
396 {
397     lxb_selectors_entry_t *entry;
398 
399     entry = selectors->current->entry;
400 
401     entry->node = node;
402     selectors->state = lxb_selectors_state_find;
403     selectors->first = entry;
404 
405 again:
406 
407     do {
408         entry = selectors->state(selectors, entry);
409     }
410     while (entry != NULL);
411 
412     if (selectors->current->parent != NULL
413         && selectors->status == LXB_STATUS_OK)
414     {
415         entry = selectors->current->entry;
416         selectors->state = selectors->current->return_state;
417 
418         goto again;
419     }
420 
421     return selectors->status;
422 }
423 
424 static lxb_selectors_entry_t *
lxb_selectors_state_find(lxb_selectors_t * selectors,lxb_selectors_entry_t * entry)425 lxb_selectors_state_find(lxb_selectors_t *selectors,
426                          lxb_selectors_entry_t *entry)
427 {
428     lxb_dom_node_t *node;
429     lxb_selectors_entry_t *next;
430     const lxb_css_selector_t *selector;
431     const lxb_css_selector_anb_of_t *anb;
432     const lxb_css_selector_pseudo_t *pseudo;
433 
434     selector = entry->selector;
435 
436     if (selector->type == LXB_CSS_SELECTOR_TYPE_PSEUDO_CLASS_FUNCTION) {
437         pseudo = &selector->u.pseudo;
438 
439         /* Optimizing. */
440 
441         switch (pseudo->type) {
442             case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_CHILD:
443             case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_CHILD:
444                 anb = pseudo->data;
445 
446                 if (anb->of != NULL) {
447                     break;
448                 }
449 
450                 goto godoit;
451 
452             case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_OF_TYPE:
453             case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_OF_TYPE:
454                 goto godoit;
455 
456             default:
457                 break;
458         }
459 
460         if (entry->nested == NULL) {
461             next = lexbor_dobject_calloc(selectors->objs);
462             if (next == NULL) {
463                 selectors->status = LXB_STATUS_ERROR_MEMORY_ALLOCATION;
464                 return NULL;
465             }
466 
467             next->combinator = LXB_CSS_SELECTOR_COMBINATOR_CLOSE;
468 
469             entry->nested = lexbor_dobject_calloc(selectors->nested);
470             if (entry->nested == NULL) {
471                 selectors->status = LXB_STATUS_ERROR_MEMORY_ALLOCATION;
472                 return NULL;
473             }
474 
475             entry->nested->entry = next;
476             entry->nested->parent = selectors->current;
477         }
478 
479         selectors->state = lxb_selectors_state_pseudo_class_function;
480         selectors->current->last = entry;
481         selectors->current = entry->nested;
482 
483         next = entry->nested->entry;
484         next->node = entry->node;
485 
486         return next;
487     }
488 
489 godoit:
490 
491     switch (entry->combinator) {
492         case LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT:
493             node = lxb_selectors_descendant(selectors, entry,
494                                             selector, entry->node);
495             break;
496 
497         case LXB_CSS_SELECTOR_COMBINATOR_CLOSE:
498             node = lxb_selectors_close(selectors, entry,
499                                        selector, entry->node);
500             break;
501 
502         case LXB_CSS_SELECTOR_COMBINATOR_CHILD:
503             node = lxb_selectors_child(selectors, entry,
504                                        selector, entry->node);
505             break;
506 
507         case LXB_CSS_SELECTOR_COMBINATOR_SIBLING:
508             node = lxb_selectors_sibling(selectors, entry,
509                                          selector, entry->node);
510             break;
511 
512         case LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING:
513             node = lxb_selectors_following(selectors, entry,
514                                            selector, entry->node);
515             break;
516 
517         case LXB_CSS_SELECTOR_COMBINATOR_CELL:
518         default:
519             selectors->status = LXB_STATUS_ERROR;
520             return NULL;
521     }
522 
523     return lxb_selectors_state_find_check(selectors, node, selector, entry);
524 }
525 
526 static lxb_selectors_entry_t *
lxb_selectors_state_find_check(lxb_selectors_t * selectors,lxb_dom_node_t * node,const lxb_css_selector_t * selector,lxb_selectors_entry_t * entry)527 lxb_selectors_state_find_check(lxb_selectors_t *selectors, lxb_dom_node_t *node,
528                                const lxb_css_selector_t *selector,
529                                lxb_selectors_entry_t *entry)
530 {
531     lxb_selectors_entry_t *next;
532     lxb_selectors_nested_t *current;
533 
534     if (node == NULL) {
535 
536     try_next:
537 
538         if (entry->next == NULL) {
539 
540         try_next_list:
541 
542             if (selector->list->next == NULL) {
543                 return NULL;
544             }
545 
546             /*
547              * Try the following selectors from the selector list.
548              */
549 
550             if (entry->following != NULL) {
551                 entry->following->node = entry->node;
552 
553                 if (selectors->current->parent == NULL) {
554                     selectors->first = entry->following;
555                 }
556 
557                 return entry->following;
558             }
559 
560             next = lexbor_dobject_calloc(selectors->objs);
561             if (next == NULL) {
562                 selectors->status = LXB_STATUS_ERROR_MEMORY_ALLOCATION;
563                 return NULL;
564             }
565 
566             next->combinator = LXB_CSS_SELECTOR_COMBINATOR_CLOSE;
567             next->selector = selector->list->next->last;
568             next->node = entry->node;
569 
570             entry->following = next;
571 
572             if (selectors->current->parent == NULL) {
573                 selectors->first = next;
574             }
575 
576             return next;
577         }
578 
579         do {
580             entry = entry->next;
581 
582             while (entry->combinator == LXB_CSS_SELECTOR_COMBINATOR_CLOSE) {
583                 if (entry->next == NULL) {
584                     selector = entry->selector;
585                     goto try_next;
586                 }
587 
588                 entry = entry->next;
589             }
590 
591             switch (entry->combinator) {
592                 case LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT:
593                     node = entry->node->parent;
594 
595                     if (node == NULL
596                         || node->type != LXB_DOM_NODE_TYPE_ELEMENT)
597                     {
598                         node = NULL;
599                     }
600 
601                     break;
602 
603                 case LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING:
604                     node = entry->node->prev;
605                     break;
606 
607                 case LXB_CSS_SELECTOR_COMBINATOR_SIBLING:
608                 case LXB_CSS_SELECTOR_COMBINATOR_CHILD:
609                 case LXB_CSS_SELECTOR_COMBINATOR_CLOSE:
610                     node = NULL;
611                     break;
612 
613                 case LXB_CSS_SELECTOR_COMBINATOR_CELL:
614                 default:
615                     selectors->status = LXB_STATUS_ERROR;
616                     return NULL;
617             }
618         }
619         while (node == NULL);
620 
621         entry->node = node;
622 
623         return entry;
624     }
625 
626     if (selector->prev == NULL) {
627         current = selectors->current;
628 
629         selectors->status = current->cb(current->entry->node,
630                                         selector->list->specificity,
631                                         current->ctx);
632 
633         if ((selectors->options & LXB_SELECTORS_OPT_MATCH_FIRST) == 0
634             && current->parent == NULL)
635         {
636             if (selectors->status == LXB_STATUS_OK) {
637                 entry = selectors->first;
638                 goto try_next_list;
639             }
640         }
641 
642         return NULL;
643     }
644 
645     if (entry->prev == NULL) {
646         next = lexbor_dobject_calloc(selectors->objs);
647         if (next == NULL) {
648             selectors->status = LXB_STATUS_ERROR_MEMORY_ALLOCATION;
649             return NULL;
650         }
651 
652         next->combinator = selector->combinator;
653         next->selector = selector->prev;
654         next->node = node;
655 
656         next->next = entry;
657         entry->prev = next;
658 
659         return next;
660     }
661 
662     entry->prev->node = node;
663 
664     return entry->prev;
665 }
666 
667 static lxb_selectors_entry_t *
lxb_selectors_state_pseudo_class_function(lxb_selectors_t * selectors,lxb_selectors_entry_t * entry)668 lxb_selectors_state_pseudo_class_function(lxb_selectors_t *selectors,
669                                           lxb_selectors_entry_t *entry)
670 {
671     lxb_dom_node_t *node, *base;
672     lxb_selectors_nested_t *current;
673     const lxb_css_selector_list_t *list;
674     lxb_css_selector_anb_of_t *anb;
675     const lxb_css_selector_pseudo_t *pseudo;
676 
677     current = selectors->current;
678 
679     base = lxb_selectors_next_node(current);
680     if (base == NULL) {
681         goto not_found;
682     }
683 
684     pseudo = &current->parent->last->selector->u.pseudo;
685 
686     switch (pseudo->type) {
687         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_HAS:
688             list = (lxb_css_selector_list_t *) pseudo->data;
689             node = lxb_selectors_state_has_relative(base, list->first);
690 
691             if (node == NULL) {
692                 selectors->current = selectors->current->parent;
693                 entry = selectors->current->last;
694 
695                 selectors->state = lxb_selectors_state_find;
696 
697                 return lxb_selectors_state_find_check(selectors, NULL,
698                                                       entry->selector, entry);
699             }
700 
701             current->root = base;
702 
703             current->entry->selector = list->last;
704             current->entry->node = node;
705             current->return_state = lxb_selectors_state_after_find_has;
706             current->cb = lxb_selectors_cb_ok;
707             current->ctx = &current->found;
708             current->found = false;
709 
710             selectors->state = lxb_selectors_state_find;
711 
712             return entry;
713 
714         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_CURRENT:
715         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_IS:
716         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_WHERE:
717             current->entry->selector = ((lxb_css_selector_list_t *) pseudo->data)->last;
718             current->entry->node = base;
719             current->return_state = lxb_selectors_state_after_find;
720             current->cb = lxb_selectors_cb_ok;
721             current->ctx = &current->found;
722             current->found = false;
723 
724             selectors->state = lxb_selectors_state_find;
725 
726             return entry;
727 
728         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NOT:
729             current->entry->selector = ((lxb_css_selector_list_t *) pseudo->data)->last;
730             current->entry->node = base;
731             current->return_state = lxb_selectors_state_after_find;
732             current->cb = lxb_selectors_cb_not;
733             current->ctx = &current->found;
734             current->found = true;
735 
736             selectors->state = lxb_selectors_state_find;
737 
738             return entry;
739 
740         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_CHILD:
741         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_CHILD:
742             anb = pseudo->data;
743 
744             current->entry->selector = anb->of->last;
745             current->entry->node = base;
746             current->return_state = lxb_selectors_state_after_nth_child;
747             current->cb = lxb_selectors_cb_ok;
748             current->ctx = &current->found;
749             current->root = base;
750             current->index = 0;
751             current->found = false;
752 
753             selectors->state = lxb_selectors_state_find;
754 
755             return entry;
756 
757         /*
758          * This one can only happen if the user has somehow messed up the
759          * selector.
760          */
761 
762         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_OF_TYPE:
763         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_OF_TYPE:
764         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_DIR:
765         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_LANG:
766         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_COL:
767         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_COL:
768         default:
769             break;
770     }
771 
772 not_found:
773 
774     selectors->current = selectors->current->parent;
775     entry = selectors->current->last;
776 
777     selectors->state = lxb_selectors_state_find;
778 
779     return lxb_selectors_state_find_check(selectors, NULL,
780                                           entry->selector, entry);
781 }
782 
783 static lxb_dom_node_t *
lxb_selectors_next_node(lxb_selectors_nested_t * main)784 lxb_selectors_next_node(lxb_selectors_nested_t *main)
785 {
786     lxb_dom_node_t *node = main->entry->node;
787 
788     switch (main->parent->last->combinator) {
789         case LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT:
790         case LXB_CSS_SELECTOR_COMBINATOR_CHILD:
791             if (node->parent == NULL
792                 || node->parent->type != LXB_DOM_NODE_TYPE_ELEMENT)
793             {
794                 return NULL;
795             }
796 
797             return node->parent;
798 
799         case LXB_CSS_SELECTOR_COMBINATOR_CLOSE:
800             return node;
801 
802         case LXB_CSS_SELECTOR_COMBINATOR_SIBLING:
803         case LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING:
804             node = node->prev;
805             break;
806 
807         default:
808             return NULL;
809     }
810 
811     while (node != NULL) {
812         if (node->type == LXB_DOM_NODE_TYPE_ELEMENT) {
813             break;
814         }
815 
816         node = node->prev;
817     }
818 
819     return node;
820 }
821 
822 static lxb_dom_node_t *
lxb_selectors_state_has_relative(lxb_dom_node_t * node,const lxb_css_selector_t * selector)823 lxb_selectors_state_has_relative(lxb_dom_node_t *node,
824                                  const lxb_css_selector_t *selector)
825 {
826     lxb_dom_node_t *root = node;
827 
828     switch (selector->combinator) {
829         case LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT:
830         case LXB_CSS_SELECTOR_COMBINATOR_CHILD:
831             node = node->first_child;
832             break;
833 
834         case LXB_CSS_SELECTOR_COMBINATOR_SIBLING:
835         case LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING:
836             node = node->next;
837             break;
838 
839         default:
840             return NULL;
841     }
842 
843     while (node != NULL) {
844         if (node->type == LXB_DOM_NODE_TYPE_ELEMENT) {
845             break;
846         }
847 
848         while (node !=root && node->next == NULL) {
849             node = node->parent;
850         }
851 
852         if (node == root) {
853             return NULL;
854         }
855 
856         node = node->next;
857     }
858 
859     return node;
860 }
861 
862 static lxb_selectors_entry_t *
lxb_selectors_state_after_find_has(lxb_selectors_t * selectors,lxb_selectors_entry_t * entry)863 lxb_selectors_state_after_find_has(lxb_selectors_t *selectors,
864                                    lxb_selectors_entry_t *entry)
865 {
866     lxb_dom_node_t *node;
867     lxb_selectors_entry_t *parent;
868     lxb_selectors_nested_t *current;
869 
870     if (selectors->current->found) {
871         node = selectors->current->root;
872 
873         selectors->current = selectors->current->parent;
874         parent = selectors->current->last;
875 
876         selectors->state = lxb_selectors_state_find;
877 
878         return lxb_selectors_state_find_check(selectors, node,
879                                               parent->selector, parent);
880     }
881 
882     current = selectors->current;
883     node = entry->node;
884 
885     switch (entry->selector->list->first->combinator) {
886         case LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT:
887             if (node->first_child != NULL) {
888                 node = node->first_child;
889             }
890             else {
891 
892             next:
893 
894                 while (node != current->root && node->next == NULL) {
895                     node = node->parent;
896                 }
897 
898                 if (node == current->root) {
899                     goto failed;
900                 }
901 
902                 node = node->next;
903             }
904 
905             if (node->type != LXB_DOM_NODE_TYPE_ELEMENT) {
906                 goto next;
907             }
908 
909             break;
910 
911         case LXB_CSS_SELECTOR_COMBINATOR_CHILD:
912         case LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING:
913             node = node->next;
914 
915             while (node != NULL && node->type != LXB_DOM_NODE_TYPE_ELEMENT) {
916                 node = node->next;
917             }
918 
919             if (node == NULL) {
920                 goto failed;
921             }
922 
923             break;
924 
925         case LXB_CSS_SELECTOR_COMBINATOR_SIBLING:
926             goto failed;
927 
928         case LXB_CSS_SELECTOR_COMBINATOR_CLOSE:
929         case LXB_CSS_SELECTOR_COMBINATOR_CELL:
930         default:
931             selectors->status = LXB_STATUS_ERROR;
932             return NULL;
933     }
934 
935     entry->node = node;
936     selectors->state = lxb_selectors_state_find;
937 
938     return entry;
939 
940 failed:
941 
942     selectors->current = selectors->current->parent;
943     parent = selectors->current->last;
944 
945     selectors->state = lxb_selectors_state_find;
946 
947     return lxb_selectors_state_find_check(selectors, NULL,
948                                           parent->selector, parent);
949 }
950 
951 
952 static lxb_selectors_entry_t *
lxb_selectors_state_after_find(lxb_selectors_t * selectors,lxb_selectors_entry_t * entry)953 lxb_selectors_state_after_find(lxb_selectors_t *selectors,
954                                lxb_selectors_entry_t *entry)
955 {
956     lxb_dom_node_t *node;
957     lxb_selectors_entry_t *parent;
958     lxb_selectors_nested_t *current;
959 
960     current = selectors->current;
961 
962     if (current->found) {
963         node = entry->node;
964 
965         selectors->current = current->parent;
966         parent = selectors->current->last;
967 
968         selectors->state = lxb_selectors_state_find;
969 
970         return lxb_selectors_state_find_check(selectors, node,
971                                               parent->selector, parent);
972     }
973 
974     node = entry->node;
975 
976     switch (current->parent->last->combinator) {
977         case LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT:
978             if (node->parent != NULL
979                 && node->parent->type == LXB_DOM_NODE_TYPE_ELEMENT)
980             {
981                 node = node->parent;
982             }
983             else {
984                 node = NULL;
985             }
986 
987             break;
988 
989         case LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING:
990             node = node->prev;
991 
992             while (node != NULL && node->type != LXB_DOM_NODE_TYPE_ELEMENT) {
993                 node = node->prev;
994             }
995 
996             break;
997 
998         case LXB_CSS_SELECTOR_COMBINATOR_CHILD:
999         case LXB_CSS_SELECTOR_COMBINATOR_SIBLING:
1000         case LXB_CSS_SELECTOR_COMBINATOR_CLOSE:
1001             node = NULL;
1002             break;
1003 
1004         case LXB_CSS_SELECTOR_COMBINATOR_CELL:
1005         default:
1006             selectors->status = LXB_STATUS_ERROR;
1007             return NULL;
1008     }
1009 
1010     if (node == NULL) {
1011         selectors->current = current->parent;
1012         parent = selectors->current->last;
1013 
1014         selectors->state = lxb_selectors_state_find;
1015 
1016         return lxb_selectors_state_find_check(selectors, node,
1017                                               parent->selector, parent);
1018     }
1019 
1020     entry->node = node;
1021     selectors->state = lxb_selectors_state_find;
1022 
1023     return entry;
1024 }
1025 
1026 static lxb_selectors_entry_t *
lxb_selectors_state_after_nth_child(lxb_selectors_t * selectors,lxb_selectors_entry_t * entry)1027 lxb_selectors_state_after_nth_child(lxb_selectors_t *selectors,
1028                                     lxb_selectors_entry_t *entry)
1029 {
1030     bool found;
1031     lxb_dom_node_t *node;
1032     lxb_selectors_entry_t *parent;
1033     lxb_selectors_nested_t *current;
1034     const lxb_css_selector_t *selector;
1035     const lxb_css_selector_pseudo_t *pseudo;
1036 
1037     current = selectors->current;
1038     selector = current->parent->last->selector;
1039     pseudo = &selector->u.pseudo;
1040 
1041     node = entry->node;
1042 
1043     if (current->found) {
1044         current->index += 1;
1045     }
1046     else if (current->root == node) {
1047         node = NULL;
1048         goto done;
1049     }
1050 
1051     if (pseudo->type == LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_CHILD) {
1052         node = node->prev;
1053 
1054         while (node != NULL) {
1055             if (node->type == LXB_DOM_NODE_TYPE_ELEMENT) {
1056                 break;
1057             }
1058 
1059             node = node->prev;
1060         }
1061     }
1062     else {
1063         node = node->next;
1064 
1065         while (node != NULL) {
1066             if (node->type == LXB_DOM_NODE_TYPE_ELEMENT) {
1067                 break;
1068             }
1069 
1070             node = node->next;
1071         }
1072     }
1073 
1074     if (node == NULL) {
1075         goto done;
1076     }
1077 
1078     entry->node = node;
1079     current->found = false;
1080     selectors->state = lxb_selectors_state_find;
1081 
1082     return entry;
1083 
1084 done:
1085 
1086     if (current->index > 0) {
1087         found = lxb_selectors_anb_calc(pseudo->data, current->index);
1088 
1089         node = (found) ? current->root : NULL;
1090     }
1091 
1092     selectors->state = lxb_selectors_state_find;
1093     selectors->current = selectors->current->parent;
1094 
1095     parent = selectors->current->last;
1096 
1097     return lxb_selectors_state_find_check(selectors, node,
1098                                           parent->selector, parent);
1099 }
1100 
1101 static bool
lxb_selectors_match(lxb_selectors_t * selectors,lxb_selectors_entry_t * entry,const lxb_css_selector_t * selector,lxb_dom_node_t * node)1102 lxb_selectors_match(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry,
1103                     const lxb_css_selector_t *selector, lxb_dom_node_t *node)
1104 {
1105     lxb_dom_element_t *element;
1106 
1107     switch (selector->type) {
1108         case LXB_CSS_SELECTOR_TYPE_ANY:
1109             return true;
1110 
1111         case LXB_CSS_SELECTOR_TYPE_ELEMENT:
1112             return lxb_selectors_match_element(selector, node, entry);
1113 
1114         case LXB_CSS_SELECTOR_TYPE_ID:
1115             return lxb_selectors_match_id(selector, node);
1116 
1117         case LXB_CSS_SELECTOR_TYPE_CLASS:
1118             element = lxb_dom_interface_element(node);
1119 
1120             if (element->attr_class == NULL
1121                 || element->attr_class->value == NULL)
1122             {
1123                 return false;
1124             }
1125 
1126             return lxb_selectors_match_class(element->attr_class->value,
1127                                              &selector->name, true);
1128 
1129         case LXB_CSS_SELECTOR_TYPE_ATTRIBUTE:
1130             return lxb_selectors_match_attribute(selector, node, entry);
1131 
1132         case LXB_CSS_SELECTOR_TYPE_PSEUDO_CLASS:
1133             return lxb_selectors_pseudo_class(selector, node);
1134 
1135         case LXB_CSS_SELECTOR_TYPE_PSEUDO_CLASS_FUNCTION:
1136             return lxb_selectors_pseudo_class_function(selector, node);
1137 
1138         case LXB_CSS_SELECTOR_TYPE_PSEUDO_ELEMENT:
1139             return lxb_selectors_pseudo_element(selector, node);
1140 
1141         case LXB_CSS_SELECTOR_TYPE_PSEUDO_ELEMENT_FUNCTION:
1142             return false;
1143 
1144         default:
1145             break;
1146     }
1147 
1148     return false;
1149 }
1150 
1151 static bool
lxb_selectors_match_element(const lxb_css_selector_t * selector,lxb_dom_node_t * node,lxb_selectors_entry_t * entry)1152 lxb_selectors_match_element(const lxb_css_selector_t *selector,
1153                             lxb_dom_node_t *node, lxb_selectors_entry_t *entry)
1154 {
1155     lxb_tag_id_t tag_id;
1156 
1157     if (entry->id == 0) {
1158         tag_id = lxb_tag_id_by_name(node->owner_document->tags,
1159                                     selector->name.data, selector->name.length);
1160         if (tag_id == LXB_TAG__UNDEF) {
1161             return false;
1162         }
1163 
1164         entry->id = tag_id;
1165     }
1166 
1167     return node->local_name == entry->id;
1168 }
1169 
1170 static bool
lxb_selectors_match_id(const lxb_css_selector_t * selector,lxb_dom_node_t * node)1171 lxb_selectors_match_id(const lxb_css_selector_t *selector, lxb_dom_node_t *node)
1172 {
1173     const lexbor_str_t *trg, *src;
1174     lxb_dom_element_t *element;
1175 
1176     element = lxb_dom_interface_element(node);
1177 
1178     if (element->attr_id == NULL || element->attr_id->value == NULL) {
1179         return false;
1180     }
1181 
1182     trg = element->attr_id->value;
1183     src = &selector->name;
1184 
1185     return trg->length == src->length
1186            && lexbor_str_data_ncasecmp(trg->data, src->data, src->length);
1187 }
1188 
1189 static bool
lxb_selectors_match_class(const lexbor_str_t * target,const lexbor_str_t * src,bool quirks)1190 lxb_selectors_match_class(const lexbor_str_t *target, const lexbor_str_t *src,
1191                           bool quirks)
1192 {
1193     lxb_char_t chr;
1194 
1195     if (target->length < src->length) {
1196         return false;
1197     }
1198 
1199     bool is_it = false;
1200 
1201     const lxb_char_t *data = target->data;
1202     const lxb_char_t *pos = data;
1203     const lxb_char_t *end = data + target->length;
1204 
1205     for (; data < end; data++) {
1206         chr = *data;
1207 
1208         if (lexbor_utils_whitespace(chr, ==, ||)) {
1209 
1210             if ((size_t) (data - pos) == src->length) {
1211                 if (quirks) {
1212                     is_it = lexbor_str_data_ncasecmp(pos, src->data, src->length);
1213                 }
1214                 else {
1215                     is_it = lexbor_str_data_ncmp(pos, src->data, src->length);
1216                 }
1217 
1218                 if (is_it) {
1219                     return true;
1220                 }
1221             }
1222 
1223             if ((size_t) (end - data) < src->length) {
1224                 return false;
1225             }
1226 
1227             pos = data + 1;
1228         }
1229     }
1230 
1231     if ((size_t) (end - pos) == src->length && src->length != 0) {
1232         if (quirks) {
1233             is_it = lexbor_str_data_ncasecmp(pos, src->data, src->length);
1234         }
1235         else {
1236             is_it = lexbor_str_data_ncmp(pos, src->data, src->length);
1237         }
1238     }
1239 
1240     return is_it;
1241 }
1242 
1243 static bool
lxb_selectors_match_attribute(const lxb_css_selector_t * selector,lxb_dom_node_t * node,lxb_selectors_entry_t * entry)1244 lxb_selectors_match_attribute(const lxb_css_selector_t *selector,
1245                               lxb_dom_node_t *node, lxb_selectors_entry_t *entry)
1246 {
1247     bool res, ins;
1248     lxb_dom_attr_t *dom_attr;
1249     lxb_dom_element_t *element;
1250     const lexbor_str_t *trg, *src;
1251     const lxb_dom_attr_data_t *attr_data;
1252     const lxb_css_selector_attribute_t *attr;
1253 
1254     static const lexbor_str_t lxb_blank_str = {
1255         .data = (lxb_char_t *) "",
1256         .length = 0
1257     };
1258 
1259     element = lxb_dom_interface_element(node);
1260     attr = &selector->u.attribute;
1261 
1262     if (entry->id == 0) {
1263         attr_data = lxb_dom_attr_data_by_local_name(node->owner_document->attrs,
1264                                     selector->name.data, selector->name.length);
1265         if (attr_data == NULL) {
1266             return false;
1267         }
1268 
1269         entry->id = attr_data->attr_id;
1270     }
1271 
1272     dom_attr = lxb_dom_element_attr_by_id(element, entry->id);
1273     if (dom_attr == NULL) {
1274         return false;
1275     }
1276 
1277     trg = dom_attr->value;
1278     src = &attr->value;
1279 
1280     if (src->data == NULL) {
1281         return true;
1282     }
1283 
1284     if (trg == NULL) {
1285         trg = &lxb_blank_str;
1286     }
1287 
1288     ins = attr->modifier == LXB_CSS_SELECTOR_MODIFIER_I;
1289 
1290     switch (attr->match) {
1291         case LXB_CSS_SELECTOR_MATCH_EQUAL:      /*  = */
1292             if (trg->length == src->length) {
1293                 if (ins) {
1294                     return lexbor_str_data_ncasecmp(trg->data, src->data,
1295                                                     src->length);
1296                 }
1297 
1298                 return lexbor_str_data_ncmp(trg->data, src->data,
1299                                             src->length);
1300             }
1301 
1302             return false;
1303 
1304         case LXB_CSS_SELECTOR_MATCH_INCLUDE:    /* ~= */
1305             return lxb_selectors_match_class(trg, src, ins);
1306 
1307         case LXB_CSS_SELECTOR_MATCH_DASH:       /* |= */
1308             if (trg->length == src->length) {
1309                 if (ins) {
1310                     return lexbor_str_data_ncasecmp(trg->data, src->data,
1311                                                     src->length);
1312                 }
1313 
1314                 return lexbor_str_data_ncmp(trg->data, src->data,
1315                                             src->length);
1316             }
1317 
1318             if (trg->length > src->length) {
1319                 if (ins) {
1320                     res = lexbor_str_data_ncasecmp(trg->data,
1321                                                    src->data, src->length);
1322                 }
1323                 else {
1324                     res = lexbor_str_data_ncmp(trg->data,
1325                                                src->data, src->length);
1326                 }
1327 
1328                 if (res && trg->data[src->length] == '-') {
1329                     return true;
1330                 }
1331             }
1332 
1333             return false;
1334 
1335         case LXB_CSS_SELECTOR_MATCH_PREFIX:     /* ^= */
1336             if (src->length != 0 && trg->length >= src->length) {
1337                 if (ins) {
1338                     return lexbor_str_data_ncasecmp(trg->data, src->data,
1339                                                     src->length);
1340                 }
1341 
1342                 return lexbor_str_data_ncmp(trg->data, src->data,
1343                                             src->length);
1344             }
1345 
1346             return false;
1347 
1348         case LXB_CSS_SELECTOR_MATCH_SUFFIX:     /* $= */
1349             if (src->length != 0 && trg->length >= src->length) {
1350                 size_t dif = trg->length - src->length;
1351 
1352                 if (ins) {
1353                     return lexbor_str_data_ncasecmp(trg->data + dif,
1354                                                     src->data, src->length);
1355                 }
1356 
1357                 return lexbor_str_data_ncmp(trg->data + dif, src->data,
1358                                             src->length);
1359             }
1360 
1361             return false;
1362 
1363         case LXB_CSS_SELECTOR_MATCH_SUBSTRING:  /* *= */
1364             if (src->length == 0) {
1365                 return false;
1366             }
1367 
1368             if (ins) {
1369                 return lexbor_str_data_ncasecmp_contain(trg->data, trg->length,
1370                                                         src->data, src->length);
1371             }
1372 
1373             return lexbor_str_data_ncmp_contain(trg->data, trg->length,
1374                                                 src->data, src->length);
1375         default:
1376             break;
1377     }
1378 
1379     return false;
1380 }
1381 
1382 static bool
lxb_selectors_pseudo_class(const lxb_css_selector_t * selector,const lxb_dom_node_t * node)1383 lxb_selectors_pseudo_class(const lxb_css_selector_t *selector,
1384                            const lxb_dom_node_t *node)
1385 {
1386     lexbor_str_t *str;
1387     lxb_dom_attr_t *attr;
1388     const lxb_dom_node_t *root;
1389     const lxb_css_selector_pseudo_t *pseudo = &selector->u.pseudo;
1390 
1391     static const lxb_char_t checkbox[] = "checkbox";
1392     static const size_t checkbox_length = sizeof(checkbox) / sizeof(lxb_char_t) - 1;
1393 
1394     static const lxb_char_t radio[] = "radio";
1395     static const size_t radio_length = sizeof(radio) / sizeof(lxb_char_t) - 1;
1396 
1397     switch (pseudo->type) {
1398         case LXB_CSS_SELECTOR_PSEUDO_CLASS_ACTIVE:
1399             attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node),
1400                                               LXB_DOM_ATTR_ACTIVE);
1401             return attr != NULL;
1402 
1403         case LXB_CSS_SELECTOR_PSEUDO_CLASS_ANY_LINK:
1404             if(node->local_name == LXB_TAG_A ||
1405                node->local_name == LXB_TAG_AREA ||
1406                node->local_name == LXB_TAG_MAP)
1407             {
1408                 attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node),
1409                                                   LXB_DOM_ATTR_HREF);
1410                 return attr != NULL;
1411             }
1412 
1413             return false;
1414 
1415         case LXB_CSS_SELECTOR_PSEUDO_CLASS_BLANK:
1416             return lxb_dom_node_is_empty(node);
1417 
1418         case LXB_CSS_SELECTOR_PSEUDO_CLASS_CHECKED:
1419             if (node->local_name == LXB_TAG_INPUT) {
1420                 attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node),
1421                                                   LXB_DOM_ATTR_TYPE);
1422                 if (attr == NULL) {
1423                     return false;
1424                 }
1425 
1426                 if (attr->value == NULL) {
1427                     return false;
1428                 }
1429 
1430                 str = attr->value;
1431 
1432                 if(str->length == 8) {
1433                     if (lexbor_str_data_ncasecmp(checkbox, str->data, checkbox_length)) {
1434                         goto check;
1435                     }
1436                 }
1437                 else if(str->length == 5) {
1438                     if (lexbor_str_data_ncasecmp(radio, str->data, radio_length)) {
1439                         goto check;
1440                     }
1441                 }
1442             }
1443             else if(node->local_name == LXB_TAG_OPTION) {
1444                 attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node),
1445                                                   LXB_DOM_ATTR_SELECTED);
1446                 if (attr != NULL) {
1447                     return true;
1448                 }
1449             }
1450             else if(node->local_name >= LXB_TAG__LAST_ENTRY) {
1451                 goto check;
1452             }
1453 
1454             return false;
1455 
1456         check:
1457 
1458             attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node),
1459                                               LXB_DOM_ATTR_CHECKED);
1460             if (attr != NULL) {
1461                 return true;
1462             }
1463 
1464             return false;
1465 
1466         case LXB_CSS_SELECTOR_PSEUDO_CLASS_CURRENT:
1467         case LXB_CSS_SELECTOR_PSEUDO_CLASS_DEFAULT:
1468             return false;
1469 
1470         case LXB_CSS_SELECTOR_PSEUDO_CLASS_DISABLED:
1471             return lxb_selectors_pseudo_class_disabled(node);
1472 
1473         case LXB_CSS_SELECTOR_PSEUDO_CLASS_EMPTY:
1474             root = node;
1475             node = node->first_child;
1476 
1477             while (node != NULL) {
1478                 if (node->local_name != LXB_TAG__EM_COMMENT) {
1479                     return false;
1480                 }
1481 
1482                 if (node->first_child != NULL) {
1483                     node = node->first_child;
1484                 }
1485                 else {
1486                     while (node != root && node->next == NULL) {
1487                         node = node->parent;
1488                     }
1489 
1490                     if (node == root) {
1491                         break;
1492                     }
1493 
1494                     node = node->next;
1495                 }
1496             }
1497 
1498             return true;
1499 
1500         case LXB_CSS_SELECTOR_PSEUDO_CLASS_ENABLED:
1501             return !lxb_selectors_pseudo_class_disabled(node);
1502 
1503         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FIRST_CHILD:
1504             return lxb_selectors_pseudo_class_first_child(node);
1505 
1506         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FIRST_OF_TYPE:
1507             return lxb_selectors_pseudo_class_first_of_type(node);
1508 
1509         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FOCUS:
1510             attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node),
1511                                               LXB_DOM_ATTR_FOCUS);
1512             return attr != NULL;
1513 
1514         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FOCUS_VISIBLE:
1515             break;
1516 
1517         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FOCUS_WITHIN:
1518             break;
1519 
1520         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FULLSCREEN:
1521             break;
1522 
1523         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUTURE:
1524             break;
1525 
1526         case LXB_CSS_SELECTOR_PSEUDO_CLASS_HOVER:
1527             attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node),
1528                                               LXB_DOM_ATTR_HOVER);
1529             return attr != NULL;
1530 
1531         case LXB_CSS_SELECTOR_PSEUDO_CLASS_IN_RANGE:
1532             break;
1533 
1534         case LXB_CSS_SELECTOR_PSEUDO_CLASS_INDETERMINATE:
1535             break;
1536 
1537         case LXB_CSS_SELECTOR_PSEUDO_CLASS_INVALID:
1538             break;
1539 
1540         case LXB_CSS_SELECTOR_PSEUDO_CLASS_LAST_CHILD:
1541             return lxb_selectors_pseudo_class_last_child(node);
1542 
1543         case LXB_CSS_SELECTOR_PSEUDO_CLASS_LAST_OF_TYPE:
1544             return lxb_selectors_pseudo_class_last_of_type(node);
1545 
1546         case LXB_CSS_SELECTOR_PSEUDO_CLASS_LINK:
1547             if (node->local_name == LXB_TAG_A
1548                 || node->local_name == LXB_TAG_AREA
1549                 || node->local_name == LXB_TAG_LINK)
1550             {
1551                 attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node),
1552                                                   LXB_DOM_ATTR_HREF);
1553                 return attr != NULL;
1554             }
1555 
1556             return false;
1557 
1558         case LXB_CSS_SELECTOR_PSEUDO_CLASS_LOCAL_LINK:
1559             break;
1560 
1561         case LXB_CSS_SELECTOR_PSEUDO_CLASS_ONLY_CHILD:
1562             return lxb_selectors_pseudo_class_first_child(node)
1563             && lxb_selectors_pseudo_class_last_child(node);
1564 
1565         case LXB_CSS_SELECTOR_PSEUDO_CLASS_ONLY_OF_TYPE:
1566             return lxb_selectors_pseudo_class_first_of_type(node)
1567             && lxb_selectors_pseudo_class_last_of_type(node);
1568 
1569         case LXB_CSS_SELECTOR_PSEUDO_CLASS_OPTIONAL:
1570             if (node->local_name == LXB_TAG_INPUT
1571                 || node->local_name == LXB_TAG_SELECT
1572                 || node->local_name == LXB_TAG_TEXTAREA)
1573             {
1574                 attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node),
1575                                                   LXB_DOM_ATTR_REQUIRED);
1576                 return attr == NULL;
1577             }
1578 
1579             return false;
1580 
1581         case LXB_CSS_SELECTOR_PSEUDO_CLASS_OUT_OF_RANGE:
1582             break;
1583 
1584         case LXB_CSS_SELECTOR_PSEUDO_CLASS_PAST:
1585             break;
1586 
1587         case LXB_CSS_SELECTOR_PSEUDO_CLASS_PLACEHOLDER_SHOWN:
1588             if (node->local_name == LXB_TAG_INPUT
1589                 || node->local_name == LXB_TAG_TEXTAREA)
1590             {
1591                 attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node),
1592                                                   LXB_DOM_ATTR_PLACEHOLDER);
1593                 return attr != NULL;
1594             }
1595 
1596             return false;
1597 
1598         case LXB_CSS_SELECTOR_PSEUDO_CLASS_READ_ONLY:
1599             return !lxb_selectors_pseudo_class_read_write(node);
1600 
1601         case LXB_CSS_SELECTOR_PSEUDO_CLASS_READ_WRITE:
1602             return lxb_selectors_pseudo_class_read_write(node);
1603 
1604         case LXB_CSS_SELECTOR_PSEUDO_CLASS_REQUIRED:
1605             if (node->local_name == LXB_TAG_INPUT
1606                 || node->local_name == LXB_TAG_SELECT
1607                 || node->local_name == LXB_TAG_TEXTAREA)
1608             {
1609                 attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node),
1610                                                   LXB_DOM_ATTR_REQUIRED);
1611                 return attr != NULL;
1612             }
1613 
1614             return false;
1615 
1616         case LXB_CSS_SELECTOR_PSEUDO_CLASS_ROOT:
1617             return lxb_dom_document_root(node->owner_document) == node;
1618 
1619         case LXB_CSS_SELECTOR_PSEUDO_CLASS_SCOPE:
1620             break;
1621 
1622         case LXB_CSS_SELECTOR_PSEUDO_CLASS_TARGET:
1623             break;
1624 
1625         case LXB_CSS_SELECTOR_PSEUDO_CLASS_TARGET_WITHIN:
1626             break;
1627 
1628         case LXB_CSS_SELECTOR_PSEUDO_CLASS_USER_INVALID:
1629             break;
1630 
1631         case LXB_CSS_SELECTOR_PSEUDO_CLASS_VALID:
1632             break;
1633 
1634         case LXB_CSS_SELECTOR_PSEUDO_CLASS_VISITED:
1635             break;
1636 
1637         case LXB_CSS_SELECTOR_PSEUDO_CLASS_WARNING:
1638             break;
1639     }
1640 
1641     return false;
1642 }
1643 
1644 static bool
lxb_selectors_pseudo_class_function(const lxb_css_selector_t * selector,lxb_dom_node_t * node)1645 lxb_selectors_pseudo_class_function(const lxb_css_selector_t *selector,
1646                                     lxb_dom_node_t *node)
1647 {
1648     size_t index;
1649     lxb_dom_node_t *base;
1650     const lxb_css_selector_pseudo_t *pseudo;
1651 
1652     pseudo = &selector->u.pseudo;
1653 
1654     switch (pseudo->type) {
1655         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_CHILD:
1656         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_CHILD:
1657             index = 0;
1658 
1659             if (pseudo->type == LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_CHILD) {
1660                 while (node != NULL) {
1661                     if (node->local_name != LXB_TAG__TEXT
1662                         && node->local_name != LXB_TAG__EM_COMMENT)
1663                     {
1664                         index++;
1665                     }
1666 
1667                     node = node->prev;
1668                 }
1669             }
1670             else {
1671                 while (node != NULL) {
1672                     if (node->local_name != LXB_TAG__TEXT
1673                         && node->local_name != LXB_TAG__EM_COMMENT)
1674                     {
1675                         index++;
1676                     }
1677 
1678                     node = node->next;
1679                 }
1680             }
1681 
1682             return lxb_selectors_anb_calc(pseudo->data, index);
1683 
1684         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_OF_TYPE:
1685         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_OF_TYPE:
1686             index = 0;
1687             base = node;
1688 
1689             if (pseudo->type == LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_OF_TYPE) {
1690                 while (node != NULL) {
1691                     if(node->local_name == base->local_name
1692                        && node->ns == base->ns)
1693                     {
1694                         index++;
1695                     }
1696 
1697                     node = node->prev;
1698                 }
1699             }
1700             else {
1701                 while (node != NULL) {
1702                     if(node->local_name == base->local_name
1703                        && node->ns == base->ns)
1704                     {
1705                         index++;
1706                     }
1707 
1708                     node = node->next;
1709                 }
1710             }
1711 
1712             return lxb_selectors_anb_calc(pseudo->data, index);
1713 
1714         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_DIR:
1715         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_LANG:
1716         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_COL:
1717         case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_COL:
1718         default:
1719             break;
1720     }
1721 
1722     return false;
1723 }
1724 
1725 static bool
lxb_selectors_pseudo_element(const lxb_css_selector_t * selector,const lxb_dom_node_t * node)1726 lxb_selectors_pseudo_element(const lxb_css_selector_t *selector,
1727                              const lxb_dom_node_t *node)
1728 {
1729     const lxb_css_selector_pseudo_t *pseudo = &selector->u.pseudo;
1730 
1731     switch (pseudo->type) {
1732         case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_AFTER:
1733         case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_BACKDROP:
1734         case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_BEFORE:
1735         case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_FIRST_LETTER:
1736         case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_FIRST_LINE:
1737         case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_GRAMMAR_ERROR:
1738         case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_INACTIVE_SELECTION:
1739         case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_MARKER:
1740         case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_PLACEHOLDER:
1741         case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_SELECTION:
1742         case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_SPELLING_ERROR:
1743         case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_TARGET_TEXT:
1744             break;
1745     }
1746 
1747     return false;
1748 }
1749 
1750 static bool
lxb_selectors_pseudo_class_disabled(const lxb_dom_node_t * node)1751 lxb_selectors_pseudo_class_disabled(const lxb_dom_node_t *node)
1752 {
1753     lxb_dom_attr_t *attr;
1754     uintptr_t tag_id = node->local_name;
1755 
1756     attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node),
1757                                       LXB_DOM_ATTR_DISABLED);
1758     if (attr == NULL) {
1759         return false;
1760     }
1761 
1762     if (tag_id == LXB_TAG_BUTTON || tag_id == LXB_TAG_INPUT ||
1763         tag_id == LXB_TAG_SELECT || tag_id == LXB_TAG_TEXTAREA ||
1764         tag_id >= LXB_TAG__LAST_ENTRY)
1765     {
1766         return true;
1767     }
1768 
1769     node = node->parent;
1770 
1771     while (node != NULL) {
1772         if (node->local_name == LXB_TAG_FIELDSET
1773             && node->first_child->local_name != LXB_TAG_LEGEND)
1774         {
1775             return true;
1776         }
1777 
1778         node = node->parent;
1779     }
1780 
1781     return false;
1782 }
1783 
1784 static bool
lxb_selectors_pseudo_class_first_child(const lxb_dom_node_t * node)1785 lxb_selectors_pseudo_class_first_child(const lxb_dom_node_t *node)
1786 {
1787     node = node->prev;
1788 
1789     while (node != NULL) {
1790         if (node->local_name != LXB_TAG__TEXT
1791             && node->local_name != LXB_TAG__EM_COMMENT)
1792         {
1793             return false;
1794         }
1795 
1796         node = node->prev;
1797     }
1798 
1799     return true;
1800 }
1801 
1802 static bool
lxb_selectors_pseudo_class_first_of_type(const lxb_dom_node_t * node)1803 lxb_selectors_pseudo_class_first_of_type(const lxb_dom_node_t *node)
1804 {
1805     const lxb_dom_node_t *root = node;
1806     node = node->prev;
1807 
1808     while (node) {
1809         if (node->local_name == root->local_name
1810             && node->ns == root->ns)
1811         {
1812             return false;
1813         }
1814 
1815         node = node->prev;
1816     }
1817 
1818     return true;
1819 }
1820 
1821 static bool
lxb_selectors_pseudo_class_last_child(const lxb_dom_node_t * node)1822 lxb_selectors_pseudo_class_last_child(const lxb_dom_node_t *node)
1823 {
1824     node = node->next;
1825 
1826     while (node != NULL) {
1827         if (node->local_name != LXB_TAG__TEXT
1828             && node->local_name != LXB_TAG__EM_COMMENT)
1829         {
1830             return false;
1831         }
1832 
1833         node = node->next;
1834     }
1835 
1836     return true;
1837 }
1838 
1839 static bool
lxb_selectors_pseudo_class_last_of_type(const lxb_dom_node_t * node)1840 lxb_selectors_pseudo_class_last_of_type(const lxb_dom_node_t *node)
1841 {
1842     const lxb_dom_node_t *root = node;
1843     node = node->next;
1844 
1845     while (node) {
1846         if (node->local_name == root->local_name
1847             && node->ns == root->ns)
1848         {
1849             return false;
1850         }
1851 
1852         node = node->next;
1853     }
1854 
1855     return true;
1856 }
1857 
1858 static bool
lxb_selectors_pseudo_class_read_write(const lxb_dom_node_t * node)1859 lxb_selectors_pseudo_class_read_write(const lxb_dom_node_t *node)
1860 {
1861     lxb_dom_attr_t *attr;
1862 
1863     if (node->local_name == LXB_TAG_INPUT
1864         || node->local_name == LXB_TAG_TEXTAREA)
1865     {
1866         attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node),
1867                                           LXB_DOM_ATTR_READONLY);
1868         if (attr != NULL) {
1869             return false;
1870         }
1871 
1872         return !lxb_selectors_pseudo_class_disabled(node);
1873     }
1874 
1875     return false;
1876 }
1877 
1878 static bool
lxb_selectors_anb_calc(lxb_css_selector_anb_of_t * anb,size_t index)1879 lxb_selectors_anb_calc(lxb_css_selector_anb_of_t *anb, size_t index)
1880 {
1881     double num;
1882 
1883     if (anb->anb.a == 0) {
1884         if (anb->anb.b >= 0 && (size_t) anb->anb.b == index) {
1885             return true;
1886         }
1887     }
1888     else {
1889         num = ((double) index - (double) anb->anb.b) / (double) anb->anb.a;
1890 
1891         if (num >= 0.0f && (num - trunc(num)) == 0.0f) {
1892             return true;
1893         }
1894     }
1895 
1896     return false;
1897 }
1898 
1899 static lxb_status_t
lxb_selectors_cb_ok(lxb_dom_node_t * node,lxb_css_selector_specificity_t spec,void * ctx)1900 lxb_selectors_cb_ok(lxb_dom_node_t *node,
1901                     lxb_css_selector_specificity_t spec, void *ctx)
1902 {
1903     *((bool *) ctx) = true;
1904     return LXB_STATUS_OK;
1905 }
1906 
1907 static lxb_status_t
lxb_selectors_cb_not(lxb_dom_node_t * node,lxb_css_selector_specificity_t spec,void * ctx)1908 lxb_selectors_cb_not(lxb_dom_node_t *node,
1909                      lxb_css_selector_specificity_t spec, void *ctx)
1910 {
1911     *((bool *) ctx) = false;
1912     return LXB_STATUS_OK;
1913 }
1914 
1915 void
lxb_selectors_opt_set_noi(lxb_selectors_t * selectors,lxb_selectors_opt_t opt)1916 lxb_selectors_opt_set_noi(lxb_selectors_t *selectors, lxb_selectors_opt_t opt)
1917 {
1918     lxb_selectors_opt_set(selectors, opt);
1919 }
1920 
1921 const lxb_css_selector_list_t *
lxb_selectors_selector_noi(const lxb_selectors_t * selectors)1922 lxb_selectors_selector_noi(const lxb_selectors_t *selectors)
1923 {
1924     return lxb_selectors_selector(selectors);
1925 }
1926