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