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