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 = ¤t->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 = ¤t->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 = ¤t->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 = ¤t->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 = ¤t->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