1/* GTK - The GIMP Toolkit
2 * Copyright (C) 2011 Benjamin Otte <otte@gnome.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "config.h"
19
20#include "gtkcssselectorprivate.h"
21
22#include <stdlib.h>
23#include <string.h>
24
25#include "gtkcssprovider.h"
26#include "gtkstylecontextprivate.h"
27
28#include <errno.h>
29#if defined(_MSC_VER) && _MSC_VER >= 1500
30# include <intrin.h>
31#endif
32
33/*
34 * @GTK_CSS_SELECTOR_CATEGORY_SIMPLE: A simple selector
35 * @GTK_CSS_SELECTOR_CATEGORY_SIMPLE_RADICAL: A simple selector that matches
36 * what change tracking considers a "radical change"
37 * @GTK_CSS_SELECTOR_SIBLING: A selector matching siblings
38 * @GTK_CSS_SELECTOR_CATEGORY_PARENT: A selector matching a parent or other
39 * ancestor
40 *
41 * Categorize the selectors. This helps in various loops when matching.
42 */
43typedef enum {
44 GTK_CSS_SELECTOR_CATEGORY_SIMPLE,
45 GTK_CSS_SELECTOR_CATEGORY_SIMPLE_RADICAL,
46 GTK_CSS_SELECTOR_CATEGORY_PARENT,
47 GTK_CSS_SELECTOR_CATEGORY_SIBLING,
48} GtkCssSelectorCategory;
49
50typedef struct _GtkCssSelectorClass GtkCssSelectorClass;
51
52struct _GtkCssSelectorClass {
53 const char *name;
54 GtkCssSelectorCategory category;
55
56 void (* print) (const GtkCssSelector *selector,
57 GString *string);
58 /* NULL or an iterator that returns the next node or %NULL if there are no
59 * more nodes.
60 * Call it like:
61 * for (iter = gtk_css_selector_iterator (node, NULL);
62 * iter;
63 * iter = gtk_css_selector_iterator (node, iter))
64 * {
65 * do_stuff();
66 * }
67 */
68 GtkCssNode * (* iterator) (const GtkCssSelector *selector,
69 GtkCssNode *node,
70 GtkCssNode *current);
71 gboolean (* match_one) (const GtkCssSelector *selector,
72 GtkCssNode *node);
73 GtkCssChange (* get_change) (const GtkCssSelector *selector,
74 GtkCssChange previous_change);
75 void (* add_specificity) (const GtkCssSelector *selector,
76 guint *ids,
77 guint *classes,
78 guint *elements);
79 guint (* hash_one) (const GtkCssSelector *selector);
80 int (* compare_one) (const GtkCssSelector *a,
81 const GtkCssSelector *b);
82};
83
84typedef enum {
85 POSITION_FORWARD,
86 POSITION_BACKWARD,
87 POSITION_ONLY,
88} PositionType;
89#define POSITION_TYPE_BITS 4
90#define POSITION_NUMBER_BITS ((sizeof (gpointer) * 8 - POSITION_TYPE_BITS) / 2)
91
92union _GtkCssSelector
93{
94 const GtkCssSelectorClass *class; /* type of check this selector does */
95 struct {
96 const GtkCssSelectorClass *class;
97 GQuark name;
98 } id;
99 struct {
100 const GtkCssSelectorClass *class;
101 GQuark style_class;
102 } style_class;
103 struct {
104 const GtkCssSelectorClass *class;
105 GQuark name;
106 } name;
107 struct {
108 const GtkCssSelectorClass *class;
109 GtkStateFlags state;
110 } state;
111 struct {
112 const GtkCssSelectorClass *class;
113 PositionType type :POSITION_TYPE_BITS;
114 gssize a :POSITION_NUMBER_BITS;
115 gssize b :POSITION_NUMBER_BITS;
116 } position;
117};
118
119#define GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET G_MAXINT32
120struct _GtkCssSelectorTree
121{
122 GtkCssSelector selector;
123 gint32 parent_offset;
124 gint32 previous_offset;
125 gint32 sibling_offset;
126 gint32 matches_offset; /* pointers that we return as matches if selector matches */
127};
128
129static gboolean
130gtk_css_selector_equal (const GtkCssSelector *a,
131 const GtkCssSelector *b)
132{
133 return
134 a->class == b->class &&
135 a->class->compare_one (a, b) == 0;
136}
137
138static guint
139gtk_css_selector_hash_one (const GtkCssSelector *selector)
140{
141 return selector->class->hash_one (selector);
142}
143
144static inline gpointer *
145gtk_css_selector_tree_get_matches (const GtkCssSelectorTree *tree)
146{
147 if (tree->matches_offset == GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
148 return NULL;
149
150 return (gpointer *) ((guint8 *)tree + tree->matches_offset);
151}
152
153static void
154gtk_css_selector_matches_insert_sorted (GtkCssSelectorMatches *matches,
155 gpointer data)
156{
157 guint i;
158
159 for (i = 0; i < gtk_css_selector_matches_get_size (self: matches); i++)
160 {
161 gpointer elem = gtk_css_selector_matches_get (self: matches, pos: i);
162
163 if (data == elem)
164 return;
165
166 if (data < elem)
167 break;
168 }
169
170 gtk_css_selector_matches_splice (self: matches, pos: i, removed: 0, FALSE, additions: (gpointer[1]) { data }, added: 1);
171}
172
173static inline gboolean
174gtk_css_selector_match_one (const GtkCssSelector *selector,
175 GtkCssNode *node)
176{
177 return selector->class->match_one (selector, node);
178}
179
180static inline GtkCssNode *
181gtk_css_selector_iterator (const GtkCssSelector *selector,
182 GtkCssNode *node,
183 GtkCssNode *current)
184{
185 return selector->class->iterator (selector, node, current);
186}
187
188static int
189gtk_css_selector_compare_one (const GtkCssSelector *a, const GtkCssSelector *b)
190{
191 if (a->class != b->class)
192 return strcmp (s1: a->class->name, s2: b->class->name);
193 else
194 return a->class->compare_one (a, b);
195}
196
197static const GtkCssSelector *
198gtk_css_selector_previous (const GtkCssSelector *selector)
199{
200 selector = selector + 1;
201
202 return selector->class ? selector : NULL;
203}
204
205static inline const GtkCssSelectorTree *
206gtk_css_selector_tree_at_offset (const GtkCssSelectorTree *tree,
207 gint32 offset)
208{
209 if (offset == GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
210 return NULL;
211
212 return (GtkCssSelectorTree *) ((guint8 *)tree + offset);
213}
214
215static inline const GtkCssSelectorTree *
216gtk_css_selector_tree_get_parent (const GtkCssSelectorTree *tree)
217{
218 return gtk_css_selector_tree_at_offset (tree, offset: tree->parent_offset);
219}
220
221static inline const GtkCssSelectorTree *
222gtk_css_selector_tree_get_previous (const GtkCssSelectorTree *tree)
223{
224 return gtk_css_selector_tree_at_offset (tree, offset: tree->previous_offset);
225}
226
227static inline const GtkCssSelectorTree *
228gtk_css_selector_tree_get_sibling (const GtkCssSelectorTree *tree)
229{
230 return gtk_css_selector_tree_at_offset (tree, offset: tree->sibling_offset);
231}
232
233/* DEFAULTS */
234
235static void
236gtk_css_selector_default_add_specificity (const GtkCssSelector *selector,
237 guint *ids,
238 guint *classes,
239 guint *elements)
240{
241 /* no specificity changes */
242}
243
244static GtkCssNode *
245gtk_css_selector_default_iterator (const GtkCssSelector *selector,
246 GtkCssNode *node,
247 GtkCssNode *current)
248{
249 if (current)
250 return NULL;
251 else
252 return node;
253}
254
255static gboolean
256gtk_css_selector_default_match_one (const GtkCssSelector *selector,
257 GtkCssNode *node)
258{
259 return TRUE;
260}
261
262static guint
263gtk_css_selector_default_hash_one (const GtkCssSelector *selector)
264{
265 return GPOINTER_TO_UINT (selector->class);
266}
267
268static int
269gtk_css_selector_default_compare_one (const GtkCssSelector *a,
270 const GtkCssSelector *b)
271{
272 return 0;
273}
274
275/* DESCENDANT */
276
277static void
278gtk_css_selector_descendant_print (const GtkCssSelector *selector,
279 GString *string)
280{
281 g_string_append_c (string, ' ');
282}
283
284static GtkCssNode *
285gtk_css_selector_descendant_iterator (const GtkCssSelector *selector,
286 GtkCssNode *node,
287 GtkCssNode *current)
288{
289 return gtk_css_node_get_parent (cssnode: current ? current : node);
290}
291
292static GtkCssChange
293gtk_css_selector_descendant_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
294{
295 return _gtk_css_change_for_child (match: previous_change);
296}
297
298static const GtkCssSelectorClass GTK_CSS_SELECTOR_DESCENDANT = {
299 "descendant",
300 GTK_CSS_SELECTOR_CATEGORY_PARENT,
301 gtk_css_selector_descendant_print,
302 gtk_css_selector_descendant_iterator,
303 gtk_css_selector_default_match_one,
304 gtk_css_selector_descendant_get_change,
305 gtk_css_selector_default_add_specificity,
306 gtk_css_selector_default_hash_one,
307 gtk_css_selector_default_compare_one,
308};
309
310/* CHILD */
311
312static void
313gtk_css_selector_child_print (const GtkCssSelector *selector,
314 GString *string)
315{
316 g_string_append (string, val: " > ");
317}
318
319static GtkCssNode *
320gtk_css_selector_child_iterator (const GtkCssSelector *selector,
321 GtkCssNode *node,
322 GtkCssNode *current)
323{
324 if (current == NULL)
325 return gtk_css_node_get_parent (cssnode: node);
326
327 return NULL;
328}
329
330static GtkCssChange
331gtk_css_selector_child_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
332{
333 return _gtk_css_change_for_child (match: previous_change);
334}
335
336static const GtkCssSelectorClass GTK_CSS_SELECTOR_CHILD = {
337 "child",
338 GTK_CSS_SELECTOR_CATEGORY_PARENT,
339 gtk_css_selector_child_print,
340 gtk_css_selector_child_iterator,
341 gtk_css_selector_default_match_one,
342 gtk_css_selector_child_get_change,
343 gtk_css_selector_default_add_specificity,
344 gtk_css_selector_default_hash_one,
345 gtk_css_selector_default_compare_one,
346};
347
348/* SIBLING */
349
350static void
351gtk_css_selector_sibling_print (const GtkCssSelector *selector,
352 GString *string)
353{
354 g_string_append (string, val: " ~ ");
355}
356
357static GtkCssNode *
358get_previous_visible_sibling (GtkCssNode *node)
359{
360 do {
361 node = gtk_css_node_get_previous_sibling (cssnode: node);
362 } while (node && !gtk_css_node_get_visible (cssnode: node));
363
364 return node;
365}
366
367static GtkCssNode *
368get_next_visible_sibling (GtkCssNode *node)
369{
370 do {
371 node = gtk_css_node_get_next_sibling (cssnode: node);
372 } while (node && !gtk_css_node_get_visible (cssnode: node));
373
374 return node;
375}
376
377static GtkCssNode *
378gtk_css_selector_sibling_iterator (const GtkCssSelector *selector,
379 GtkCssNode *node,
380 GtkCssNode *current)
381{
382 return get_previous_visible_sibling (node: current ? current : node);
383}
384
385static GtkCssChange
386gtk_css_selector_sibling_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
387{
388 return _gtk_css_change_for_sibling (match: previous_change);
389}
390
391static const GtkCssSelectorClass GTK_CSS_SELECTOR_SIBLING = {
392 "sibling",
393 GTK_CSS_SELECTOR_CATEGORY_SIBLING,
394 gtk_css_selector_sibling_print,
395 gtk_css_selector_sibling_iterator,
396 gtk_css_selector_default_match_one,
397 gtk_css_selector_sibling_get_change,
398 gtk_css_selector_default_add_specificity,
399 gtk_css_selector_default_hash_one,
400 gtk_css_selector_default_compare_one,
401};
402
403/* ADJACENT */
404
405static void
406gtk_css_selector_adjacent_print (const GtkCssSelector *selector,
407 GString *string)
408{
409 g_string_append (string, val: " + ");
410}
411
412static GtkCssNode *
413gtk_css_selector_adjacent_iterator (const GtkCssSelector *selector,
414 GtkCssNode *node,
415 GtkCssNode *current)
416{
417 if (current == NULL)
418 return get_previous_visible_sibling (node);
419
420 return NULL;
421}
422
423static GtkCssChange
424gtk_css_selector_adjacent_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
425{
426 return _gtk_css_change_for_sibling (match: previous_change);
427}
428
429static const GtkCssSelectorClass GTK_CSS_SELECTOR_ADJACENT = {
430 "adjacent",
431 GTK_CSS_SELECTOR_CATEGORY_SIBLING,
432 gtk_css_selector_adjacent_print,
433 gtk_css_selector_adjacent_iterator,
434 gtk_css_selector_default_match_one,
435 gtk_css_selector_adjacent_get_change,
436 gtk_css_selector_default_add_specificity,
437 gtk_css_selector_default_hash_one,
438 gtk_css_selector_default_compare_one,
439};
440
441/* SIMPLE SELECTOR DEFINE */
442
443#define DEFINE_SIMPLE_SELECTOR(n, \
444 c, \
445 print_func, \
446 match_func, \
447 hash_func, \
448 comp_func, \
449 increase_id_specificity, \
450 increase_class_specificity, \
451 increase_element_specificity, \
452 ignore_for_change) \
453static void \
454gtk_css_selector_ ## n ## _print (const GtkCssSelector *selector, \
455 GString *string) \
456{ \
457 print_func (selector, string); \
458} \
459\
460static void \
461gtk_css_selector_not_ ## n ## _print (const GtkCssSelector *selector, \
462 GString *string) \
463{ \
464 g_string_append (string, ":not("); \
465 print_func (selector, string); \
466 g_string_append (string, ")"); \
467} \
468\
469static gboolean \
470gtk_css_selector_not_ ## n ## _match_one (const GtkCssSelector *selector, \
471 GtkCssNode *node) \
472{ \
473 return !match_func (selector, node); \
474} \
475\
476static GtkCssChange \
477gtk_css_selector_ ## n ## _get_change (const GtkCssSelector *selector, GtkCssChange previous_change) \
478{ \
479 return previous_change | GTK_CSS_CHANGE_ ## c; \
480} \
481\
482static void \
483gtk_css_selector_ ## n ## _add_specificity (const GtkCssSelector *selector, \
484 guint *ids, \
485 guint *classes, \
486 guint *elements) \
487{ \
488 if (increase_id_specificity) \
489 { \
490 (*ids)++; \
491 } \
492 if (increase_class_specificity) \
493 { \
494 (*classes)++; \
495 } \
496 if (increase_element_specificity) \
497 { \
498 (*elements)++; \
499 } \
500} \
501\
502static const GtkCssSelectorClass GTK_CSS_SELECTOR_ ## c = { \
503 G_STRINGIFY(n), \
504 ignore_for_change ? GTK_CSS_SELECTOR_CATEGORY_SIMPLE : GTK_CSS_SELECTOR_CATEGORY_SIMPLE_RADICAL, \
505 gtk_css_selector_ ## n ## _print, \
506 gtk_css_selector_default_iterator, \
507 match_func, \
508 gtk_css_selector_ ## n ## _get_change, \
509 gtk_css_selector_ ## n ## _add_specificity, \
510 hash_func, \
511 comp_func, \
512};\
513\
514static const GtkCssSelectorClass GTK_CSS_SELECTOR_NOT_ ## c = { \
515 "not_" G_STRINGIFY(n), \
516 GTK_CSS_SELECTOR_CATEGORY_SIMPLE, \
517 gtk_css_selector_not_ ## n ## _print, \
518 gtk_css_selector_default_iterator, \
519 gtk_css_selector_not_ ## n ## _match_one, \
520 gtk_css_selector_ ## n ## _get_change, \
521 gtk_css_selector_ ## n ## _add_specificity, \
522 hash_func, \
523 comp_func, \
524};
525
526/* ANY */
527
528static void
529print_any (const GtkCssSelector *selector,
530 GString *string)
531{
532 g_string_append_c (string, '*');
533}
534
535static gboolean
536match_any (const GtkCssSelector *selector,
537 GtkCssNode *node)
538{
539 return TRUE;
540}
541
542#undef GTK_CSS_CHANGE_ANY
543#define GTK_CSS_CHANGE_ANY 0
544DEFINE_SIMPLE_SELECTOR(any, ANY, print_any, match_any,
545 gtk_css_selector_default_hash_one, gtk_css_selector_default_compare_one,
546 FALSE, FALSE, FALSE, TRUE)
547#undef GTK_CSS_CHANGE_ANY
548
549/* NAME */
550
551static void
552print_name (const GtkCssSelector *selector,
553 GString *string)
554{
555 g_string_append (string, val: g_quark_to_string (quark: selector->name.name));
556}
557
558static gboolean
559match_name (const GtkCssSelector *selector,
560 GtkCssNode *node)
561{
562 return gtk_css_node_get_name (cssnode: node) == selector->name.name;
563}
564
565static guint
566hash_name (const GtkCssSelector *a)
567{
568 return gtk_css_hash_name (name: a->name.name);
569}
570
571static int
572comp_name (const GtkCssSelector *a,
573 const GtkCssSelector *b)
574{
575 return a->name.name - b->name.name;
576}
577
578DEFINE_SIMPLE_SELECTOR(name, NAME, print_name, match_name, hash_name, comp_name, FALSE, FALSE, TRUE, FALSE)
579
580/* CLASS */
581
582static void
583print_class (const GtkCssSelector *selector,
584 GString *string)
585{
586 g_string_append_c (string, '.');
587 g_string_append (string, val: g_quark_to_string (quark: selector->style_class.style_class));
588}
589
590static gboolean
591match_class (const GtkCssSelector *selector,
592 GtkCssNode *node)
593{
594 return gtk_css_node_has_class (cssnode: node, style_class: selector->style_class.style_class);
595}
596
597static guint
598hash_class (const GtkCssSelector *a)
599{
600 return gtk_css_hash_class (klass: a->style_class.style_class);
601}
602
603static int
604comp_class (const GtkCssSelector *a,
605 const GtkCssSelector *b)
606{
607 if (a->style_class.style_class < b->style_class.style_class)
608 return -1;
609 if (a->style_class.style_class > b->style_class.style_class)
610 return 1;
611 else
612 return 0;
613}
614
615DEFINE_SIMPLE_SELECTOR(class, CLASS, print_class, match_class, hash_class, comp_class, FALSE, TRUE, FALSE, FALSE)
616
617/* ID */
618
619static void
620print_id (const GtkCssSelector *selector,
621 GString *string)
622{
623 g_string_append_c (string, '#');
624 g_string_append (string, val: g_quark_to_string (quark: selector->id.name));
625}
626
627static gboolean
628match_id (const GtkCssSelector *selector,
629 GtkCssNode *node)
630{
631 return gtk_css_node_get_id (cssnode: node) == selector->id.name;
632}
633
634static guint
635hash_id (const GtkCssSelector *a)
636{
637 return gtk_css_hash_id (id: a->id.name);
638}
639
640static int
641comp_id (const GtkCssSelector *a,
642 const GtkCssSelector *b)
643{
644 return a->id.name - b->id.name;
645}
646
647DEFINE_SIMPLE_SELECTOR(id, ID, print_id, match_id, hash_id, comp_id, TRUE, FALSE, FALSE, FALSE)
648
649/* PSEUDOCLASS FOR STATE */
650static void
651print_pseudoclass_state (const GtkCssSelector *selector,
652 GString *string)
653{
654 g_string_append_c (string, ':');
655 g_string_append (string, val: gtk_css_pseudoclass_name (flags: selector->state.state));
656}
657
658static gboolean
659match_pseudoclass_state (const GtkCssSelector *selector,
660 GtkCssNode *node)
661{
662 return (gtk_css_node_get_state (cssnode: node) & selector->state.state) == selector->state.state;
663}
664
665static guint
666hash_pseudoclass_state (const GtkCssSelector *selector)
667{
668 return selector->state.state;
669}
670
671
672static int
673comp_pseudoclass_state (const GtkCssSelector *a,
674 const GtkCssSelector *b)
675{
676 return a->state.state - b->state.state;
677}
678
679static GtkCssChange
680change_pseudoclass_state (const GtkCssSelector *selector)
681{
682 GtkStateFlags states = selector->state.state;
683 GtkCssChange change = 0;
684
685 if (states & GTK_STATE_FLAG_PRELIGHT)
686 change |= GTK_CSS_CHANGE_HOVER;
687 if (states & GTK_STATE_FLAG_INSENSITIVE)
688 change |= GTK_CSS_CHANGE_DISABLED;
689 if (states & GTK_STATE_FLAG_BACKDROP)
690 change |= GTK_CSS_CHANGE_BACKDROP;
691 if (states & GTK_STATE_FLAG_SELECTED)
692 change |= GTK_CSS_CHANGE_SELECTED;
693 if (states & ~(GTK_STATE_FLAG_PRELIGHT |
694 GTK_STATE_FLAG_INSENSITIVE |
695 GTK_STATE_FLAG_BACKDROP |
696 GTK_STATE_FLAG_SELECTED))
697 change |= GTK_CSS_CHANGE_STATE;
698
699 return change;
700}
701
702#define GTK_CSS_CHANGE_PSEUDOCLASS_STATE change_pseudoclass_state (selector)
703DEFINE_SIMPLE_SELECTOR(pseudoclass_state, PSEUDOCLASS_STATE, print_pseudoclass_state,
704 match_pseudoclass_state, hash_pseudoclass_state, comp_pseudoclass_state,
705 FALSE, TRUE, FALSE, TRUE)
706#undef GTK_CSS_CHANGE_PSEUDOCLASS_STATE
707
708/* PSEUDOCLASS FOR POSITION */
709
710static void
711print_pseudoclass_position (const GtkCssSelector *selector,
712 GString *string)
713{
714 switch (selector->position.type)
715 {
716 case POSITION_FORWARD:
717 if (selector->position.a == 0)
718 {
719 if (selector->position.b == 1)
720 g_string_append (string, val: ":first-child");
721 else
722 g_string_append_printf (string, format: ":nth-child(%d)", selector->position.b);
723 }
724 else if (selector->position.a == 2 && selector->position.b == 0)
725 g_string_append (string, val: ":nth-child(even)");
726 else if (selector->position.a == 2 && selector->position.b == 1)
727 g_string_append (string, val: ":nth-child(odd)");
728 else
729 {
730 g_string_append (string, val: ":nth-child(");
731 if (selector->position.a == 1)
732 g_string_append (string, val: "n");
733 else if (selector->position.a == -1)
734 g_string_append (string, val: "-n");
735 else
736 g_string_append_printf (string, format: "%dn", selector->position.a);
737 if (selector->position.b > 0)
738 g_string_append_printf (string, format: "+%d)", selector->position.b);
739 else if (selector->position.b < 0)
740 g_string_append_printf (string, format: "%d)", selector->position.b);
741 else
742 g_string_append (string, val: ")");
743 }
744 break;
745 case POSITION_BACKWARD:
746 if (selector->position.a == 0)
747 {
748 if (selector->position.b == 1)
749 g_string_append (string, val: ":last-child");
750 else
751 g_string_append_printf (string, format: ":nth-last-child(%d)", selector->position.b);
752 }
753 else if (selector->position.a == 2 && selector->position.b == 0)
754 g_string_append (string, val: ":nth-last-child(even)");
755 else if (selector->position.a == 2 && selector->position.b == 1)
756 g_string_append (string, val: ":nth-last-child(odd)");
757 else
758 {
759 g_string_append (string, val: ":nth-last-child(");
760 if (selector->position.a == 1)
761 g_string_append (string, val: "n");
762 else if (selector->position.a == -1)
763 g_string_append (string, val: "-n");
764 else
765 g_string_append_printf (string, format: "%dn", selector->position.a);
766 if (selector->position.b > 0)
767 g_string_append_printf (string, format: "+%d)", selector->position.b);
768 else if (selector->position.b < 0)
769 g_string_append_printf (string, format: "%d)", selector->position.b);
770 else
771 g_string_append (string, val: ")");
772 }
773 break;
774 case POSITION_ONLY:
775 g_string_append (string, val: ":only-child");
776 break;
777 default:
778 g_assert_not_reached ();
779 break;
780 }
781}
782
783static gboolean
784match_position (GtkCssNode *node,
785 GtkCssNode *(* prev_node_func) (GtkCssNode *),
786 int a,
787 int b)
788{
789 int pos, x;
790
791 /* special-case the common "first-child" and "last-child" */
792 if (a == 0)
793 {
794 while (b > 0 && node != NULL)
795 {
796 b--;
797 node = prev_node_func (node);
798 }
799
800 return b == 0 && node == NULL;
801 }
802
803 /* count nodes */
804 for (pos = 0; node != NULL; pos++)
805 node = prev_node_func (node);
806
807 /* solve pos = a * X + b
808 * and return TRUE if X is integer >= 0 */
809 x = pos - b;
810
811 if (x % a)
812 return FALSE;
813
814 return x / a >= 0;
815}
816
817static gboolean
818match_pseudoclass_position (const GtkCssSelector *selector,
819 GtkCssNode *node)
820{
821 switch (selector->position.type)
822 {
823 case POSITION_FORWARD:
824 if (!match_position (node, prev_node_func: get_previous_visible_sibling, a: selector->position.a, b: selector->position.b))
825 return FALSE;
826 break;
827 case POSITION_BACKWARD:
828 if (!match_position (node, prev_node_func: get_next_visible_sibling, a: selector->position.a, b: selector->position.b))
829 return FALSE;
830 break;
831 case POSITION_ONLY:
832 if (get_previous_visible_sibling (node) ||
833 get_next_visible_sibling (node))
834 return FALSE;
835 break;
836 default:
837 g_assert_not_reached ();
838 return FALSE;
839 }
840
841 return TRUE;
842}
843
844static guint
845hash_pseudoclass_position (const GtkCssSelector *a)
846{
847 return (guint)(((((gulong)a->position.type) << POSITION_NUMBER_BITS) | a->position.a) << POSITION_NUMBER_BITS) | a->position.b;
848}
849
850static int
851comp_pseudoclass_position (const GtkCssSelector *a,
852 const GtkCssSelector *b)
853{
854 int diff;
855
856 diff = a->position.type - b->position.type;
857 if (diff)
858 return diff;
859
860 diff = a->position.a - b->position.a;
861 if (diff)
862 return diff;
863
864 return a->position.b - b->position.b;
865}
866
867static GtkCssChange
868change_pseudoclass_position (const GtkCssSelector *selector)
869{
870 switch (selector->position.type)
871 {
872 case POSITION_FORWARD:
873 if (selector->position.a == 0 && selector->position.b == 1)
874 return GTK_CSS_CHANGE_FIRST_CHILD;
875 else
876 return GTK_CSS_CHANGE_NTH_CHILD;
877 case POSITION_BACKWARD:
878 if (selector->position.a == 0 && selector->position.b == 1)
879 return GTK_CSS_CHANGE_LAST_CHILD;
880 else
881 return GTK_CSS_CHANGE_NTH_LAST_CHILD;
882 case POSITION_ONLY:
883 return GTK_CSS_CHANGE_FIRST_CHILD | GTK_CSS_CHANGE_LAST_CHILD;
884 default:
885 g_assert_not_reached ();
886 return 0;
887 }
888}
889
890#define GTK_CSS_CHANGE_PSEUDOCLASS_POSITION change_pseudoclass_position(selector)
891DEFINE_SIMPLE_SELECTOR(pseudoclass_position, PSEUDOCLASS_POSITION, print_pseudoclass_position,
892 match_pseudoclass_position, hash_pseudoclass_position, comp_pseudoclass_position,
893 FALSE, TRUE, FALSE, TRUE)
894#undef GTK_CSS_CHANGE_PSEUDOCLASS_POSITION
895/* API */
896
897static guint
898gtk_css_selector_size (const GtkCssSelector *selector)
899{
900 guint size = 0;
901
902 while (selector)
903 {
904 selector = gtk_css_selector_previous (selector);
905 size++;
906 }
907
908 return size;
909}
910
911static GtkCssSelector *
912gtk_css_selector_new (const GtkCssSelectorClass *class,
913 GtkCssSelector *selector)
914{
915 guint size;
916
917 size = gtk_css_selector_size (selector);
918 selector = g_realloc (mem: selector, n_bytes: sizeof (GtkCssSelector) * (size + 1) + sizeof (gpointer));
919 if (size == 0)
920 selector[1].class = NULL;
921 else
922 memmove (dest: selector + 1, src: selector, n: sizeof (GtkCssSelector) * size + sizeof (gpointer));
923
924 memset (s: selector, c: 0, n: sizeof (GtkCssSelector));
925 selector->class = class;
926
927 return selector;
928}
929
930static GtkCssSelector *
931gtk_css_selector_parse_selector_class (GtkCssParser *parser,
932 GtkCssSelector *selector,
933 gboolean negate)
934{
935 const GtkCssToken *token;
936
937 gtk_css_parser_consume_token (self: parser);
938 for (token = gtk_css_parser_peek_token (self: parser);
939 gtk_css_token_is (token, GTK_CSS_TOKEN_COMMENT);
940 token = gtk_css_parser_peek_token (self: parser))
941 {
942 gtk_css_parser_consume_token (self: parser);
943 }
944
945 if (gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT))
946 {
947 selector = gtk_css_selector_new (class: negate ? &GTK_CSS_SELECTOR_NOT_CLASS
948 : &GTK_CSS_SELECTOR_CLASS,
949 selector);
950 selector->style_class.style_class = g_quark_from_string (string: token->string.string);
951 gtk_css_parser_consume_token (self: parser);
952 return selector;
953 }
954 else
955 {
956 gtk_css_parser_error_syntax (self: parser, format: "No class name after '.' in selector");
957 if (selector)
958 _gtk_css_selector_free (selector);
959 return NULL;
960 }
961}
962
963static gboolean
964string_has_number (const char *string,
965 const char *prefix,
966 int *number)
967{
968 gsize len = strlen (s: prefix);
969 char *end;
970
971 if (g_ascii_strncasecmp (s1: string, s2: prefix, n: len) != 0)
972 return FALSE;
973
974 errno = 0;
975 *number = strtoul (nptr: string + len, endptr: &end, base: 10);
976 if (*end != '\0' || errno != 0)
977 return FALSE;
978
979 return TRUE;
980}
981
982static gboolean
983parse_plus_b (GtkCssParser *parser,
984 gboolean negate,
985 int *b)
986{
987 const GtkCssToken *token;
988 gboolean has_seen_sign;
989
990 token = gtk_css_parser_get_token (self: parser);
991
992 if (negate)
993 {
994 has_seen_sign = TRUE;
995 }
996 else
997 {
998 if (gtk_css_token_is_delim (token, delim: '+'))
999 {
1000 gtk_css_parser_consume_token (self: parser);
1001 has_seen_sign = TRUE;
1002 }
1003 else if (gtk_css_token_is_delim (token, delim: '-'))
1004 {
1005 gtk_css_parser_consume_token (self: parser);
1006 negate = TRUE;
1007 has_seen_sign = TRUE;
1008 }
1009 else
1010 {
1011 has_seen_sign = FALSE;
1012 }
1013 }
1014
1015 token = gtk_css_parser_get_token (self: parser);
1016 if (!has_seen_sign && gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_INTEGER))
1017 {
1018 *b = token->number.number;
1019 gtk_css_parser_consume_token (self: parser);
1020 return TRUE;
1021 }
1022 else if (has_seen_sign && gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_INTEGER))
1023 {
1024 *b = token->number.number;
1025 if (negate)
1026 *b = - *b;
1027 gtk_css_parser_consume_token (self: parser);
1028 return TRUE;
1029 }
1030 else if (!has_seen_sign)
1031 {
1032 *b = 0;
1033 return TRUE;
1034 }
1035
1036 gtk_css_parser_error_syntax (self: parser, format: "Not a valid an+b type");
1037 return FALSE;
1038}
1039
1040static gboolean
1041parse_n_plus_b (GtkCssParser *parser,
1042 int before,
1043 int *a,
1044 int *b)
1045{
1046 const GtkCssToken *token;
1047
1048 token = gtk_css_parser_get_token (self: parser);
1049
1050 if (gtk_css_token_is_ident (token, ident: "n"))
1051 {
1052 *a = before;
1053 gtk_css_parser_consume_token (self: parser);
1054 return parse_plus_b (parser, FALSE, b);
1055 }
1056 else if (gtk_css_token_is_ident (token, ident: "n-"))
1057 {
1058 *a = before;
1059 gtk_css_parser_consume_token (self: parser);
1060 return parse_plus_b (parser, TRUE, b);
1061 }
1062 else if (gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT) &&
1063 string_has_number (string: token->string.string, prefix: "n-", number: b))
1064 {
1065 *a = before;
1066 *b = -*b;
1067 gtk_css_parser_consume_token (self: parser);
1068 return TRUE;
1069 }
1070 else
1071 {
1072 *b = before;
1073 *a = 0;
1074 return TRUE;
1075 }
1076}
1077
1078static gboolean
1079parse_a_n_plus_b (GtkCssParser *parser,
1080 int seen_sign,
1081 int *a,
1082 int *b)
1083{
1084 const GtkCssToken *token;
1085
1086 token = gtk_css_parser_get_token (self: parser);
1087
1088 if (!seen_sign && gtk_css_token_is_ident (token, ident: "even"))
1089 {
1090 *a = 2;
1091 *b = 0;
1092 gtk_css_parser_consume_token (self: parser);
1093 return TRUE;
1094 }
1095 else if (!seen_sign && gtk_css_token_is_ident (token, ident: "odd"))
1096 {
1097 *a = 2;
1098 *b = 1;
1099 gtk_css_parser_consume_token (self: parser);
1100 return TRUE;
1101 }
1102 else if (!seen_sign && gtk_css_token_is_delim (token, delim: '+'))
1103 {
1104 gtk_css_parser_consume_token (self: parser);
1105 return parse_a_n_plus_b (parser, seen_sign: 1, a, b);
1106 }
1107 else if (!seen_sign && gtk_css_token_is_delim (token, delim: '-'))
1108 {
1109 gtk_css_parser_consume_token (self: parser);
1110 return parse_a_n_plus_b (parser, seen_sign: -1, a, b);
1111 }
1112 else if ((!seen_sign && gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_INTEGER)) ||
1113 gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_INTEGER))
1114 {
1115 int x = token->number.number * (seen_sign ? seen_sign : 1);
1116 gtk_css_parser_consume_token (self: parser);
1117
1118 return parse_n_plus_b (parser, before: x , a, b);
1119 }
1120 else if (((!seen_sign && gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_INTEGER_DIMENSION)) ||
1121 gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_INTEGER_DIMENSION)) &&
1122 g_ascii_strcasecmp (s1: token->dimension.dimension, s2: "n") == 0)
1123 {
1124 *a = token->dimension.value * (seen_sign ? seen_sign : 1);
1125 gtk_css_parser_consume_token (self: parser);
1126 return parse_plus_b (parser, FALSE, b);
1127 }
1128 else if (((!seen_sign && gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_INTEGER_DIMENSION)) ||
1129 gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_INTEGER_DIMENSION)) &&
1130 g_ascii_strcasecmp (s1: token->dimension.dimension, s2: "n-") == 0)
1131 {
1132 *a = token->dimension.value * (seen_sign ? seen_sign : 1);
1133 gtk_css_parser_consume_token (self: parser);
1134 return parse_plus_b (parser, TRUE, b);
1135 }
1136 else if (((!seen_sign && gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_INTEGER_DIMENSION)) ||
1137 gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_INTEGER_DIMENSION)) &&
1138 string_has_number (string: token->dimension.dimension, prefix: "n-", number: b))
1139 {
1140 *a = token->dimension.value * (seen_sign ? seen_sign : 1);
1141 *b = -*b;
1142 gtk_css_parser_consume_token (self: parser);
1143 return TRUE;
1144 }
1145 else if (!seen_sign && gtk_css_token_is_ident (token, ident: "-n"))
1146 {
1147 *a = -1;
1148 gtk_css_parser_consume_token (self: parser);
1149 return parse_plus_b (parser, FALSE, b);
1150 }
1151 else if (!seen_sign && gtk_css_token_is_ident (token, ident: "-n-"))
1152 {
1153 *a = -1;
1154 gtk_css_parser_consume_token (self: parser);
1155 return parse_plus_b (parser, TRUE, b);
1156 }
1157 else if (!seen_sign &&
1158 gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT) &&
1159 string_has_number (string: token->string.string, prefix: "-n-", number: b))
1160 {
1161 *a = -1;
1162 *b = -*b;
1163 gtk_css_parser_consume_token (self: parser);
1164 return TRUE;
1165 }
1166 else if (gtk_css_token_is_ident (token, ident: "n") ||
1167 gtk_css_token_is_ident (token, ident: "n-"))
1168 {
1169 return parse_n_plus_b (parser, before: seen_sign ? seen_sign : 1, a, b);
1170 }
1171 else if (gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT) &&
1172 string_has_number (string: token->string.string, prefix: "n-", number: b))
1173 {
1174 *a = seen_sign ? seen_sign : 1;
1175 *b = -*b;
1176 gtk_css_parser_consume_token (self: parser);
1177 return TRUE;
1178 }
1179 else if (!seen_sign && gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT) &&
1180 string_has_number (string: token->string.string, prefix: "-n-", number: b))
1181 {
1182 *a = -1;
1183 *b = -*b;
1184 gtk_css_parser_consume_token (self: parser);
1185 return TRUE;
1186 }
1187
1188 gtk_css_parser_error_syntax (self: parser, format: "Not a valid an+b type");
1189 return FALSE;
1190}
1191
1192static guint
1193parse_a_n_plus_b_arg (GtkCssParser *parser,
1194 guint arg,
1195 gpointer data)
1196{
1197 int *ab = data;
1198
1199 if (!parse_a_n_plus_b (parser, FALSE, a: &ab[0], b: &ab[1]))
1200 return 0;
1201
1202 return 1;
1203}
1204
1205static guint
1206parse_dir_arg (GtkCssParser *parser,
1207 guint arg,
1208 gpointer data)
1209{
1210 GtkStateFlags *flag = data;
1211
1212 if (gtk_css_parser_try_ident (self: parser, ident: "ltr"))
1213 {
1214 *flag = GTK_STATE_FLAG_DIR_LTR;
1215 return 1;
1216 }
1217 else if (gtk_css_parser_try_ident (self: parser, ident: "rtl"))
1218 {
1219 *flag = GTK_STATE_FLAG_DIR_RTL;
1220 return 1;
1221 }
1222 else
1223 {
1224 gtk_css_parser_error_value (self: parser, format: "Expected \"ltr\" or \"rtl\"");
1225 return 0;
1226 }
1227}
1228
1229static guint
1230parse_identifier_arg (GtkCssParser *parser,
1231 guint arg,
1232 gpointer data)
1233{
1234 const char *ident = data;
1235
1236 if (!gtk_css_parser_try_ident (self: parser, ident))
1237 {
1238 gtk_css_parser_error_value (self: parser, format: "Expected \"%s\"", ident);
1239 return 0;
1240 }
1241
1242 return 1;
1243}
1244
1245static GtkCssSelector *
1246gtk_css_selector_parse_selector_pseudo_class (GtkCssParser *parser,
1247 GtkCssSelector *selector,
1248 gboolean negate)
1249{
1250 GtkCssLocation start_location;
1251 const GtkCssToken *token;
1252
1253 start_location = *gtk_css_parser_get_start_location (self: parser);
1254 gtk_css_parser_consume_token (self: parser);
1255 for (token = gtk_css_parser_peek_token (self: parser);
1256 gtk_css_token_is (token, GTK_CSS_TOKEN_COMMENT);
1257 token = gtk_css_parser_peek_token (self: parser))
1258 {
1259 gtk_css_parser_consume_token (self: parser);
1260 }
1261
1262 if (gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT))
1263 {
1264 static const struct {
1265 const char *name;
1266 GtkStateFlags state_flag;
1267 PositionType position_type;
1268 int position_a;
1269 int position_b;
1270 } pseudo_classes[] = {
1271 { "first-child", 0, POSITION_FORWARD, 0, 1 },
1272 { "last-child", 0, POSITION_BACKWARD, 0, 1 },
1273 { "only-child", 0, POSITION_ONLY, 0, 0 },
1274 { "active", GTK_STATE_FLAG_ACTIVE, },
1275 { "hover", GTK_STATE_FLAG_PRELIGHT, },
1276 { "selected", GTK_STATE_FLAG_SELECTED, },
1277 { "disabled", GTK_STATE_FLAG_INSENSITIVE, },
1278 { "indeterminate", GTK_STATE_FLAG_INCONSISTENT, },
1279 { "focus", GTK_STATE_FLAG_FOCUSED, },
1280 { "backdrop", GTK_STATE_FLAG_BACKDROP, },
1281 { "link", GTK_STATE_FLAG_LINK, },
1282 { "visited", GTK_STATE_FLAG_VISITED, },
1283 { "checked", GTK_STATE_FLAG_CHECKED, },
1284 { "focus-visible", GTK_STATE_FLAG_FOCUS_VISIBLE, },
1285 { "focus-within", GTK_STATE_FLAG_FOCUS_WITHIN, },
1286 };
1287 guint i;
1288
1289 for (i = 0; i < G_N_ELEMENTS (pseudo_classes); i++)
1290 {
1291 if (g_ascii_strcasecmp (s1: pseudo_classes[i].name, s2: token->string.string) == 0)
1292 {
1293 if (pseudo_classes[i].state_flag)
1294 {
1295 selector = gtk_css_selector_new (class: negate ? &GTK_CSS_SELECTOR_NOT_PSEUDOCLASS_STATE
1296 : &GTK_CSS_SELECTOR_PSEUDOCLASS_STATE,
1297 selector);
1298 selector->state.state = pseudo_classes[i].state_flag;
1299 }
1300 else
1301 {
1302 selector = gtk_css_selector_new (class: negate ? &GTK_CSS_SELECTOR_NOT_PSEUDOCLASS_POSITION
1303 : &GTK_CSS_SELECTOR_PSEUDOCLASS_POSITION,
1304 selector);
1305 selector->position.type = pseudo_classes[i].position_type;
1306 selector->position.a = pseudo_classes[i].position_a;
1307 selector->position.b = pseudo_classes[i].position_b;
1308 }
1309 gtk_css_parser_consume_token (self: parser);
1310 return selector;
1311 }
1312 }
1313
1314 gtk_css_parser_error (self: parser,
1315 code: GTK_CSS_PARSER_ERROR_UNKNOWN_VALUE,
1316 start: &start_location,
1317 end: gtk_css_parser_get_end_location (self: parser),
1318 format: "Unknown name of pseudo-class");
1319 if (selector)
1320 _gtk_css_selector_free (selector);
1321 return NULL;
1322 }
1323 else if (gtk_css_token_is (token, GTK_CSS_TOKEN_FUNCTION))
1324 {
1325 if (gtk_css_token_is_function (token, ident: "nth-child"))
1326 {
1327 int ab[2];
1328
1329 if (!gtk_css_parser_consume_function (self: parser, min_args: 1, max_args: 1, parse_func: parse_a_n_plus_b_arg, data: ab))
1330 {
1331 if (selector)
1332 _gtk_css_selector_free (selector);
1333 return NULL;
1334 }
1335
1336 selector = gtk_css_selector_new (class: negate ? &GTK_CSS_SELECTOR_NOT_PSEUDOCLASS_POSITION
1337 : &GTK_CSS_SELECTOR_PSEUDOCLASS_POSITION,
1338 selector);
1339 selector->position.type = POSITION_FORWARD;
1340 selector->position.a = ab[0];
1341 selector->position.b = ab[1];
1342 }
1343 else if (gtk_css_token_is_function (token, ident: "nth-last-child"))
1344 {
1345 int ab[2];
1346
1347 if (!gtk_css_parser_consume_function (self: parser, min_args: 1, max_args: 1, parse_func: parse_a_n_plus_b_arg, data: ab))
1348 {
1349 if (selector)
1350 _gtk_css_selector_free (selector);
1351 return NULL;
1352 }
1353
1354 selector = gtk_css_selector_new (class: negate ? &GTK_CSS_SELECTOR_NOT_PSEUDOCLASS_POSITION
1355 : &GTK_CSS_SELECTOR_PSEUDOCLASS_POSITION,
1356 selector);
1357 selector->position.type = POSITION_BACKWARD;
1358 selector->position.a = ab[0];
1359 selector->position.b = ab[1];
1360 }
1361 else if (gtk_css_token_is_function (token, ident: "not"))
1362 {
1363 if (negate)
1364 {
1365 gtk_css_parser_error_syntax (self: parser, format: "Nesting of :not() not allowed");
1366 if (selector)
1367 _gtk_css_selector_free (selector);
1368 return NULL;
1369 }
1370 else
1371 {
1372 gtk_css_parser_start_block (self: parser);
1373 token = gtk_css_parser_get_token (self: parser);
1374
1375 if (gtk_css_token_is_delim (token, delim: '*'))
1376 {
1377 selector = gtk_css_selector_new (class: &GTK_CSS_SELECTOR_NOT_ANY, selector);
1378 gtk_css_parser_consume_token (self: parser);
1379 }
1380 else if (gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT))
1381 {
1382 selector = gtk_css_selector_new (class: &GTK_CSS_SELECTOR_NOT_NAME, selector);
1383 selector->name.name = g_quark_from_string (string: token->string.string);
1384 gtk_css_parser_consume_token (self: parser);
1385 }
1386 else if (gtk_css_token_is (token, GTK_CSS_TOKEN_HASH_ID))
1387 {
1388 selector = gtk_css_selector_new (class: &GTK_CSS_SELECTOR_NOT_ID, selector);
1389 selector->id.name = g_quark_from_string (string: token->string.string);
1390 gtk_css_parser_consume_token (self: parser);
1391 }
1392 else if (gtk_css_token_is_delim (token, delim: '.'))
1393 {
1394 selector = gtk_css_selector_parse_selector_class (parser, selector, TRUE);
1395 }
1396 else if (gtk_css_token_is (token, GTK_CSS_TOKEN_COLON))
1397 {
1398 selector = gtk_css_selector_parse_selector_pseudo_class (parser, selector, TRUE);
1399 }
1400 else
1401 {
1402 gtk_css_parser_error_syntax (self: parser, format: "Invalid contents of :not() selector");
1403 gtk_css_parser_end_block (self: parser);
1404 if (selector)
1405 _gtk_css_selector_free (selector);
1406 selector = NULL;
1407 return NULL;
1408 }
1409
1410 token = gtk_css_parser_get_token (self: parser);
1411 if (!gtk_css_token_is (token, GTK_CSS_TOKEN_EOF))
1412 {
1413 gtk_css_parser_error_syntax (self: parser, format: "Invalid contents of :not() selector");
1414 gtk_css_parser_end_block (self: parser);
1415 if (selector)
1416 _gtk_css_selector_free (selector);
1417 selector = NULL;
1418 return NULL;
1419 }
1420 gtk_css_parser_end_block (self: parser);
1421 }
1422 }
1423 else if (gtk_css_token_is_function (token, ident: "dir"))
1424 {
1425 GtkStateFlags flag;
1426
1427 if (!gtk_css_parser_consume_function (self: parser, min_args: 1, max_args: 1, parse_func: parse_dir_arg, data: &flag))
1428 {
1429 if (selector)
1430 _gtk_css_selector_free (selector);
1431 return NULL;
1432 }
1433
1434 selector = gtk_css_selector_new (class: negate ? &GTK_CSS_SELECTOR_NOT_PSEUDOCLASS_STATE
1435 : &GTK_CSS_SELECTOR_PSEUDOCLASS_STATE,
1436 selector);
1437 selector->state.state = flag;
1438 }
1439 else if (gtk_css_token_is_function (token, ident: "drop"))
1440 {
1441 if (!gtk_css_parser_consume_function (self: parser, min_args: 1, max_args: 1, parse_func: parse_identifier_arg, data: (gpointer) "active"))
1442 {
1443 if (selector)
1444 _gtk_css_selector_free (selector);
1445 return NULL;
1446 }
1447 selector = gtk_css_selector_new (class: negate ? &GTK_CSS_SELECTOR_NOT_PSEUDOCLASS_STATE
1448 : &GTK_CSS_SELECTOR_PSEUDOCLASS_STATE,
1449 selector);
1450 selector->state.state = GTK_STATE_FLAG_DROP_ACTIVE;
1451 }
1452 else
1453 {
1454 gtk_css_parser_error (self: parser,
1455 code: GTK_CSS_PARSER_ERROR_UNKNOWN_VALUE,
1456 start: &start_location,
1457 end: gtk_css_parser_get_end_location (self: parser),
1458 format: "Unknown pseudoclass");
1459 if (selector)
1460 _gtk_css_selector_free (selector);
1461 return NULL;
1462 }
1463 }
1464 else
1465 {
1466 gtk_css_parser_error (self: parser,
1467 code: GTK_CSS_PARSER_ERROR_UNKNOWN_VALUE,
1468 start: &start_location,
1469 end: gtk_css_parser_get_end_location (self: parser),
1470 format: "Unknown pseudoclass");
1471 if (selector)
1472 _gtk_css_selector_free (selector);
1473 return NULL;
1474 }
1475
1476 return selector;
1477}
1478
1479static GtkCssSelector *
1480gtk_css_selector_parse_simple_selector (GtkCssParser *parser,
1481 GtkCssSelector *selector)
1482{
1483 gboolean parsed_something = FALSE;
1484 const GtkCssToken *token;
1485
1486 do {
1487 for (token = gtk_css_parser_peek_token (self: parser);
1488 gtk_css_token_is (token, GTK_CSS_TOKEN_COMMENT);
1489 token = gtk_css_parser_peek_token (self: parser))
1490 {
1491 gtk_css_parser_consume_token (self: parser);
1492 }
1493
1494 if (!parsed_something && gtk_css_token_is_delim (token, delim: '*'))
1495 {
1496 selector = gtk_css_selector_new (class: &GTK_CSS_SELECTOR_ANY, selector);
1497 gtk_css_parser_consume_token (self: parser);
1498 }
1499 else if (!parsed_something && gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT))
1500 {
1501 selector = gtk_css_selector_new (class: &GTK_CSS_SELECTOR_NAME, selector);
1502 selector->name.name = g_quark_from_string (string: token->string.string);
1503 gtk_css_parser_consume_token (self: parser);
1504 }
1505 else if (gtk_css_token_is (token, GTK_CSS_TOKEN_HASH_ID))
1506 {
1507 selector = gtk_css_selector_new (class: &GTK_CSS_SELECTOR_ID, selector);
1508 selector->id.name = g_quark_from_string (string: token->string.string);
1509 gtk_css_parser_consume_token (self: parser);
1510 }
1511 else if (gtk_css_token_is_delim (token, delim: '.'))
1512 {
1513 selector = gtk_css_selector_parse_selector_class (parser, selector, FALSE);
1514 }
1515 else if (gtk_css_token_is (token, GTK_CSS_TOKEN_COLON))
1516 {
1517 selector = gtk_css_selector_parse_selector_pseudo_class (parser, selector, FALSE);
1518 }
1519 else
1520 {
1521 if (!parsed_something)
1522 {
1523 gtk_css_parser_error_syntax (self: parser, format: "Expected a valid selector");
1524 if (selector)
1525 _gtk_css_selector_free (selector);
1526 selector = NULL;
1527 }
1528 break;
1529 }
1530
1531 parsed_something = TRUE;
1532 }
1533 while (TRUE);
1534
1535 return selector;
1536}
1537
1538GtkCssSelector *
1539_gtk_css_selector_parse (GtkCssParser *parser)
1540{
1541 GtkCssSelector *selector = NULL;
1542 const GtkCssToken *token;
1543
1544 while (TRUE)
1545 {
1546 gboolean seen_whitespace = FALSE;
1547
1548 /* skip all whitespace and comments */
1549 gtk_css_parser_get_token (self: parser);
1550
1551 selector = gtk_css_selector_parse_simple_selector (parser, selector);
1552 if (selector == NULL)
1553 return NULL;
1554
1555 for (token = gtk_css_parser_peek_token (self: parser);
1556 gtk_css_token_is (token, GTK_CSS_TOKEN_COMMENT) ||
1557 gtk_css_token_is (token, GTK_CSS_TOKEN_WHITESPACE);
1558 token = gtk_css_parser_peek_token (self: parser))
1559 {
1560 seen_whitespace |= gtk_css_token_is (token, GTK_CSS_TOKEN_WHITESPACE);
1561 gtk_css_parser_consume_token (self: parser);
1562 }
1563
1564 if (gtk_css_token_is_delim (token, delim: '+'))
1565 {
1566 selector = gtk_css_selector_new (class: &GTK_CSS_SELECTOR_ADJACENT, selector);
1567 gtk_css_parser_consume_token (self: parser);
1568 }
1569 else if (gtk_css_token_is_delim (token, delim: '~'))
1570 {
1571 selector = gtk_css_selector_new (class: &GTK_CSS_SELECTOR_SIBLING, selector);
1572 gtk_css_parser_consume_token (self: parser);
1573 }
1574 else if (gtk_css_token_is_delim (token, delim: '>'))
1575 {
1576 selector = gtk_css_selector_new (class: &GTK_CSS_SELECTOR_CHILD, selector);
1577 gtk_css_parser_consume_token (self: parser);
1578 }
1579 else if (gtk_css_token_is (token, GTK_CSS_TOKEN_EOF) ||
1580 gtk_css_token_is (token, GTK_CSS_TOKEN_COMMA) ||
1581 gtk_css_token_is (token, GTK_CSS_TOKEN_OPEN_CURLY))
1582 {
1583 break;
1584 }
1585 else if (seen_whitespace)
1586 {
1587 selector = gtk_css_selector_new (class: &GTK_CSS_SELECTOR_DESCENDANT, selector);
1588 }
1589 else
1590 {
1591 gtk_css_parser_error_syntax (self: parser, format: "Expected a valid selector");
1592 _gtk_css_selector_free (selector);
1593 return NULL;
1594 }
1595 }
1596
1597 return selector;
1598}
1599
1600void
1601_gtk_css_selector_free (GtkCssSelector *selector)
1602{
1603 g_return_if_fail (selector != NULL);
1604
1605 g_free (mem: selector);
1606}
1607
1608void
1609_gtk_css_selector_print (const GtkCssSelector *selector,
1610 GString * str)
1611{
1612 const GtkCssSelector *previous;
1613
1614 g_return_if_fail (selector != NULL);
1615
1616 previous = gtk_css_selector_previous (selector);
1617 if (previous)
1618 _gtk_css_selector_print (selector: previous, str);
1619
1620 selector->class->print (selector, str);
1621}
1622
1623char *
1624_gtk_css_selector_to_string (const GtkCssSelector *selector)
1625{
1626 GString *string;
1627
1628 g_return_val_if_fail (selector != NULL, NULL);
1629
1630 string = g_string_new (NULL);
1631
1632 _gtk_css_selector_print (selector, str: string);
1633
1634 return g_string_free (string, FALSE);
1635}
1636
1637/**
1638 * gtk_css_selector_matches:
1639 * @selector: the selector
1640 * @node: The node to match
1641 *
1642 * Checks if the @selector matches the given @node.
1643 *
1644 * Returns: %TRUE if the selector matches @node
1645 **/
1646gboolean
1647gtk_css_selector_matches (const GtkCssSelector *selector,
1648 GtkCssNode *node)
1649{
1650 GtkCssNode *child;
1651 const GtkCssSelector *prev;
1652
1653 g_return_val_if_fail (selector != NULL, FALSE);
1654 g_return_val_if_fail (node != NULL, FALSE);
1655
1656 if (!gtk_css_selector_match_one (selector, node))
1657 return FALSE;
1658
1659 prev = gtk_css_selector_previous (selector);
1660 if (prev == NULL)
1661 return TRUE;
1662
1663 for (child = gtk_css_selector_iterator (selector, node, NULL);
1664 child;
1665 child = gtk_css_selector_iterator (selector, node, current: child))
1666 {
1667 if (gtk_css_selector_matches (selector: prev, node: child))
1668 return TRUE;
1669 }
1670
1671 return FALSE;
1672}
1673
1674/* Computes specificity according to CSS 2.1.
1675 * The arguments must be initialized to 0 */
1676static void
1677_gtk_css_selector_get_specificity (const GtkCssSelector *selector,
1678 guint *ids,
1679 guint *classes,
1680 guint *elements)
1681{
1682 for (; selector; selector = gtk_css_selector_previous (selector))
1683 {
1684 selector->class->add_specificity (selector, ids, classes, elements);
1685 }
1686}
1687
1688int
1689_gtk_css_selector_compare (const GtkCssSelector *a,
1690 const GtkCssSelector *b)
1691{
1692 guint a_ids = 0, a_classes = 0, a_elements = 0;
1693 guint b_ids = 0, b_classes = 0, b_elements = 0;
1694 int compare;
1695
1696 _gtk_css_selector_get_specificity (selector: a, ids: &a_ids, classes: &a_classes, elements: &a_elements);
1697 _gtk_css_selector_get_specificity (selector: b, ids: &b_ids, classes: &b_classes, elements: &b_elements);
1698
1699 compare = a_ids - b_ids;
1700 if (compare)
1701 return compare;
1702
1703 compare = a_classes - b_classes;
1704 if (compare)
1705 return compare;
1706
1707 return a_elements - b_elements;
1708}
1709
1710GtkCssChange
1711_gtk_css_selector_get_change (const GtkCssSelector *selector)
1712{
1713 if (selector == NULL)
1714 return 0;
1715
1716 return selector->class->get_change (selector, _gtk_css_selector_get_change (selector: gtk_css_selector_previous (selector)));
1717}
1718
1719/******************** SelectorTree handling *****************/
1720
1721static gboolean
1722gtk_css_selector_is_simple (const GtkCssSelector *selector)
1723{
1724 switch (selector->class->category)
1725 {
1726 case GTK_CSS_SELECTOR_CATEGORY_SIMPLE:
1727 case GTK_CSS_SELECTOR_CATEGORY_SIMPLE_RADICAL:
1728 return TRUE;
1729 case GTK_CSS_SELECTOR_CATEGORY_PARENT:
1730 case GTK_CSS_SELECTOR_CATEGORY_SIBLING:
1731 return FALSE;
1732 default:
1733 g_assert_not_reached ();
1734 return FALSE;
1735 }
1736}
1737
1738static GHashTable *
1739gtk_css_selectors_count_initial_init (void)
1740{
1741 return g_hash_table_new (hash_func: (GHashFunc)gtk_css_selector_hash_one, key_equal_func: (GEqualFunc)gtk_css_selector_equal);
1742}
1743
1744static void
1745gtk_css_selectors_count_initial (const GtkCssSelector *selector, GHashTable *hash_one)
1746{
1747 if (!gtk_css_selector_is_simple (selector))
1748 {
1749 guint count = GPOINTER_TO_INT (g_hash_table_lookup (hash_one, selector));
1750 g_hash_table_replace (hash_table: hash_one, key: (gpointer)selector, GUINT_TO_POINTER (count + 1));
1751 return;
1752 }
1753
1754 for (;
1755 selector && gtk_css_selector_is_simple (selector);
1756 selector = gtk_css_selector_previous (selector))
1757 {
1758 guint count = GPOINTER_TO_INT (g_hash_table_lookup (hash_one, selector));
1759 g_hash_table_replace (hash_table: hash_one, key: (gpointer)selector, GUINT_TO_POINTER (count + 1));
1760 }
1761}
1762
1763static gboolean
1764gtk_css_selectors_has_initial_selector (const GtkCssSelector *selector, const GtkCssSelector *initial)
1765{
1766 if (!gtk_css_selector_is_simple (selector))
1767 return gtk_css_selector_equal (a: selector, b: initial);
1768
1769 for (;
1770 selector && gtk_css_selector_is_simple (selector);
1771 selector = gtk_css_selector_previous (selector))
1772 {
1773 if (gtk_css_selector_equal (a: selector, b: initial))
1774 return TRUE;
1775 }
1776
1777 return FALSE;
1778}
1779
1780static GtkCssSelector *
1781gtk_css_selectors_skip_initial_selector (GtkCssSelector *selector, const GtkCssSelector *initial)
1782{
1783 GtkCssSelector *found;
1784 GtkCssSelector tmp;
1785
1786 /* If the initial simple selector is not first, move it there so we can skip it
1787 without losing any other selectors */
1788 if (!gtk_css_selector_equal (a: selector, b: initial))
1789 {
1790 for (found = selector; found && gtk_css_selector_is_simple (selector: found); found = (GtkCssSelector *)gtk_css_selector_previous (selector: found))
1791 {
1792 if (gtk_css_selector_equal (a: found, b: initial))
1793 break;
1794 }
1795
1796 g_assert (found != NULL && gtk_css_selector_is_simple (found));
1797
1798 tmp = *found;
1799 *found = *selector;
1800 *selector = tmp;
1801 }
1802
1803 return (GtkCssSelector *)gtk_css_selector_previous (selector);
1804}
1805
1806/* When checking for changes via the tree we need to know if a rule further
1807 down the tree matched, because if so we need to add "our bit" to the
1808 Change. For instance in a match like *.class:active we'll
1809 get a tree that first checks :active, if that matches we continue down
1810 to the tree, and if we get a match we add CHANGE_CLASS. However, the
1811 end of the tree where we have a match is an ANY which doesn't actually
1812 modify the change, so we don't know if we have a match or not. We fix
1813 this by setting GTK_CSS_CHANGE_GOT_MATCH which lets us guarantee
1814 that change != 0 on any match. */
1815#define GTK_CSS_CHANGE_GOT_MATCH GTK_CSS_CHANGE_RESERVED_BIT
1816
1817/* The code for collecting matches assumes that the name, id and classes
1818 * of a node remain unchanged, and anything else can change. This needs to
1819 * be kept in sync with the definition of 'radical change' in gtkcssnode.c.
1820 */
1821
1822static GtkCssChange
1823gtk_css_selector_tree_get_change (const GtkCssSelectorTree *tree,
1824 const GtkCountingBloomFilter *filter,
1825 GtkCssNode *node,
1826 gboolean skipping)
1827{
1828 GtkCssChange change = 0;
1829 const GtkCssSelectorTree *prev;
1830
1831 switch (tree->selector.class->category)
1832 {
1833 case GTK_CSS_SELECTOR_CATEGORY_SIMPLE:
1834 break;
1835 case GTK_CSS_SELECTOR_CATEGORY_SIMPLE_RADICAL:
1836 if (skipping)
1837 break;
1838 if (node)
1839 {
1840 if (!tree->selector.class->match_one (&tree->selector, node))
1841 return 0;
1842 }
1843 else if (filter)
1844 {
1845 if (!gtk_counting_bloom_filter_may_contain (self: filter,
1846 hash: gtk_css_selector_hash_one (selector: &tree->selector)))
1847 return 0;
1848 }
1849 break;
1850 case GTK_CSS_SELECTOR_CATEGORY_PARENT:
1851 skipping = FALSE;
1852 node = NULL;
1853 break;
1854 case GTK_CSS_SELECTOR_CATEGORY_SIBLING:
1855 skipping = TRUE;
1856 node = NULL;
1857 break;
1858 default:
1859 g_assert_not_reached ();
1860 return 0;
1861 }
1862
1863 for (prev = gtk_css_selector_tree_get_previous (tree);
1864 prev != NULL;
1865 prev = gtk_css_selector_tree_get_sibling (tree: prev))
1866 change |= gtk_css_selector_tree_get_change (tree: prev, filter, node, skipping);
1867
1868 if (change || gtk_css_selector_tree_get_matches (tree))
1869 change = tree->selector.class->get_change (&tree->selector, change & ~GTK_CSS_CHANGE_GOT_MATCH) | GTK_CSS_CHANGE_GOT_MATCH;
1870
1871 return change;
1872}
1873
1874static void
1875gtk_css_selector_tree_found_match (const GtkCssSelectorTree *tree,
1876 GtkCssSelectorMatches *results)
1877{
1878 int i;
1879 gpointer *matches;
1880
1881 matches = gtk_css_selector_tree_get_matches (tree);
1882 if (!matches)
1883 return;
1884
1885 for (i = 0; matches[i] != NULL; i++)
1886 gtk_css_selector_matches_insert_sorted (matches: results, data: matches[i]);
1887}
1888
1889static gboolean
1890gtk_css_selector_tree_match (const GtkCssSelectorTree *tree,
1891 const GtkCountingBloomFilter *filter,
1892 gboolean match_filter,
1893 GtkCssNode *node,
1894 GtkCssSelectorMatches *results)
1895{
1896 const GtkCssSelectorTree *prev;
1897 GtkCssNode *child;
1898
1899 if (match_filter && tree->selector.class->category == GTK_CSS_SELECTOR_CATEGORY_SIMPLE_RADICAL &&
1900 !gtk_counting_bloom_filter_may_contain (self: filter, hash: gtk_css_selector_hash_one (selector: &tree->selector)))
1901 return FALSE;
1902
1903 if (!gtk_css_selector_match_one (selector: &tree->selector, node))
1904 return TRUE;
1905
1906 gtk_css_selector_tree_found_match (tree, results);
1907
1908 if (filter && !gtk_css_selector_is_simple (selector: &tree->selector))
1909 match_filter = tree->selector.class->category == GTK_CSS_SELECTOR_CATEGORY_PARENT;
1910
1911 for (prev = gtk_css_selector_tree_get_previous (tree);
1912 prev != NULL;
1913 prev = gtk_css_selector_tree_get_sibling (tree: prev))
1914 {
1915 for (child = gtk_css_selector_iterator (selector: &tree->selector, node, NULL);
1916 child;
1917 child = gtk_css_selector_iterator (selector: &tree->selector, node, current: child))
1918 {
1919 if (!gtk_css_selector_tree_match (tree: prev, filter, match_filter, node: child, results))
1920 break;
1921 }
1922 }
1923
1924 return TRUE;
1925}
1926
1927void
1928_gtk_css_selector_tree_match_all (const GtkCssSelectorTree *tree,
1929 const GtkCountingBloomFilter *filter,
1930 GtkCssNode *node,
1931 GtkCssSelectorMatches *out_tree_rules)
1932{
1933 const GtkCssSelectorTree *iter;
1934
1935 for (iter = tree;
1936 iter != NULL;
1937 iter = gtk_css_selector_tree_get_sibling (tree: iter))
1938 {
1939 gtk_css_selector_tree_match (tree: iter, filter, FALSE, node, results: out_tree_rules);
1940 }
1941}
1942
1943gboolean
1944_gtk_css_selector_tree_is_empty (const GtkCssSelectorTree *tree)
1945{
1946 return tree == NULL;
1947}
1948
1949GtkCssChange
1950gtk_css_selector_tree_get_change_all (const GtkCssSelectorTree *tree,
1951 const GtkCountingBloomFilter *filter,
1952 GtkCssNode *node)
1953{
1954 GtkCssChange change = 0;
1955
1956 for (; tree != NULL;
1957 tree = gtk_css_selector_tree_get_sibling (tree))
1958 change |= gtk_css_selector_tree_get_change (tree, filter, node, FALSE);
1959
1960 /* Never return reserved bit set */
1961 return change & ~GTK_CSS_CHANGE_RESERVED_BIT;
1962}
1963
1964#ifdef PRINT_TREE
1965static void
1966_gtk_css_selector_tree_print (const GtkCssSelectorTree *tree, GString *str, const char *prefix)
1967{
1968 gboolean first = TRUE;
1969 int len, i;
1970 gpointer *matches;
1971
1972 for (; tree != NULL; tree = gtk_css_selector_tree_get_sibling (tree), first = FALSE)
1973 {
1974 if (!first)
1975 g_string_append (str, prefix);
1976
1977 if (first)
1978 {
1979 if (gtk_css_selector_tree_get_sibling (tree))
1980 g_string_append (str, "─┬─");
1981 else
1982 g_string_append (str, "───");
1983 }
1984 else
1985 {
1986 if (gtk_css_selector_tree_get_sibling (tree))
1987 g_string_append (str, " ├─");
1988 else
1989 g_string_append (str, " └─");
1990 }
1991
1992 len = str->len;
1993 tree->selector.class->print (&tree->selector, str);
1994 matches = gtk_css_selector_tree_get_matches (tree);
1995 if (matches)
1996 {
1997 int n;
1998 for (n = 0; matches[n] != NULL; n++) ;
1999 if (n == 1)
2000 g_string_append (str, " (1 match)");
2001 else
2002 g_string_append_printf (str, " (%d matches)", n);
2003 }
2004 len = str->len - len;
2005
2006 if (gtk_css_selector_tree_get_previous (tree))
2007 {
2008 GString *prefix2 = g_string_new (prefix);
2009
2010 if (gtk_css_selector_tree_get_sibling (tree))
2011 g_string_append (prefix2, " │ ");
2012 else
2013 g_string_append (prefix2, " ");
2014 for (i = 0; i < len; i++)
2015 g_string_append_c (prefix2, ' ');
2016
2017 _gtk_css_selector_tree_print (gtk_css_selector_tree_get_previous (tree), str, prefix2->str);
2018 g_string_free (prefix2, TRUE);
2019 }
2020 else
2021 g_string_append (str, "\n");
2022 }
2023}
2024#endif
2025
2026void
2027_gtk_css_selector_tree_match_print (const GtkCssSelectorTree *tree,
2028 GString *str)
2029{
2030 const GtkCssSelectorTree *iter;
2031
2032 g_return_if_fail (tree != NULL);
2033
2034 /* print name and * selector before others */
2035 for (iter = tree;
2036 iter && gtk_css_selector_is_simple (selector: &iter->selector);
2037 iter = gtk_css_selector_tree_get_parent (tree: iter))
2038 {
2039 if (iter->selector.class == &GTK_CSS_SELECTOR_NAME ||
2040 iter->selector.class == &GTK_CSS_SELECTOR_ANY)
2041 {
2042 iter->selector.class->print (&iter->selector, str);
2043 }
2044 }
2045 /* now print other simple selectors */
2046 for (iter = tree;
2047 iter && gtk_css_selector_is_simple (selector: &iter->selector);
2048 iter = gtk_css_selector_tree_get_parent (tree: iter))
2049 {
2050 if (iter->selector.class != &GTK_CSS_SELECTOR_NAME &&
2051 iter->selector.class != &GTK_CSS_SELECTOR_ANY)
2052 {
2053 iter->selector.class->print (&iter->selector, str);
2054 }
2055 }
2056
2057 /* now if there's a combinator, print that one */
2058 if (iter != NULL)
2059 {
2060 iter->selector.class->print (&iter->selector, str);
2061 tree = gtk_css_selector_tree_get_parent (tree: iter);
2062 if (tree)
2063 _gtk_css_selector_tree_match_print (tree, str);
2064 }
2065}
2066
2067void
2068_gtk_css_selector_tree_free (GtkCssSelectorTree *tree)
2069{
2070 if (tree == NULL)
2071 return;
2072
2073 g_free (mem: tree);
2074}
2075
2076
2077typedef struct {
2078 gpointer match;
2079 GtkCssSelector *current_selector;
2080 GtkCssSelectorTree **selector_match;
2081} GtkCssSelectorRuleSetInfo;
2082
2083static GtkCssSelectorTree *
2084get_tree (GByteArray *array, gint32 offset)
2085{
2086 return (GtkCssSelectorTree *) (array->data + offset);
2087}
2088
2089static GtkCssSelectorTree *
2090alloc_tree (GByteArray *array, gint32 *offset)
2091{
2092 GtkCssSelectorTree tree = { { NULL} };
2093
2094 *offset = array->len;
2095 g_byte_array_append (array, data: (guint8 *)&tree, len: sizeof (GtkCssSelectorTree));
2096 return get_tree (array, offset: *offset);
2097}
2098
2099static gint32
2100subdivide_infos (GByteArray *array,
2101 GtkCssSelectorRuleSetInfo **infos,
2102 guint n_infos,
2103 gint32 parent_offset)
2104{
2105 GtkCssSelectorRuleSetInfo **matched_infos;
2106 guint n_matched = 0;
2107 GtkCssSelectorRuleSetInfo **remaining_infos;
2108 guint n_remaining = 0;
2109 GHashTable *ht;
2110 gint32 tree_offset;
2111 GtkCssSelectorTree *tree;
2112 GtkCssSelector max_selector;
2113 GHashTableIter iter;
2114 guint max_count;
2115 gpointer key, value;
2116 GtkCssSelectorMatches exact_matches;
2117 gint32 res;
2118 guint i;
2119
2120 if (n_infos == 0)
2121 return GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
2122
2123 ht = gtk_css_selectors_count_initial_init ();
2124
2125 for (i = 0; i < n_infos; i++)
2126 {
2127 const GtkCssSelectorRuleSetInfo *info = infos[i];
2128 gtk_css_selectors_count_initial (selector: info->current_selector, hash_one: ht);
2129 }
2130
2131 /* Pick the selector with highest count, and use as decision on this level
2132 as that makes it possible to skip the largest amount of checks later */
2133
2134 max_count = 0;
2135
2136 g_hash_table_iter_init (iter: &iter, hash_table: ht);
2137 while (g_hash_table_iter_next (iter: &iter, key: &key, value: &value))
2138 {
2139 GtkCssSelector *selector = key;
2140 if (GPOINTER_TO_UINT (value) > max_count ||
2141 (GPOINTER_TO_UINT (value) == max_count &&
2142 gtk_css_selector_compare_one (a: selector, b: &max_selector) < 0))
2143 {
2144 max_count = GPOINTER_TO_UINT (value);
2145 max_selector = *selector;
2146 }
2147 }
2148
2149 tree = alloc_tree (array, offset: &tree_offset);
2150 tree->parent_offset = parent_offset;
2151 tree->selector = max_selector;
2152
2153 /* Allocate maximum for both of them */
2154 /* TODO: Potentially dangerous? */
2155 matched_infos = g_alloca (sizeof (GtkCssSelectorRuleSetInfo *) * n_infos);
2156 remaining_infos = g_alloca (sizeof (GtkCssSelectorRuleSetInfo *) * n_infos);
2157
2158 gtk_css_selector_matches_init (self: &exact_matches);
2159 for (i = 0; i < n_infos; i++)
2160 {
2161 GtkCssSelectorRuleSetInfo *info = infos[i];
2162
2163 if (gtk_css_selectors_has_initial_selector (selector: info->current_selector, initial: &max_selector))
2164 {
2165 info->current_selector = gtk_css_selectors_skip_initial_selector (selector: info->current_selector, initial: &max_selector);
2166 if (info->current_selector == NULL)
2167 {
2168 /* Matches current node */
2169 gtk_css_selector_matches_append (self: &exact_matches, value: info->match);
2170 if (info->selector_match != NULL)
2171 *info->selector_match = GUINT_TO_POINTER (tree_offset);
2172 }
2173 else
2174 {
2175 matched_infos[n_matched] = info;
2176 n_matched++;
2177 }
2178 }
2179 else
2180 {
2181 remaining_infos[n_remaining] = info;
2182 n_remaining++;
2183 }
2184 }
2185
2186 if (!gtk_css_selector_matches_is_empty (self: &exact_matches))
2187 {
2188 gtk_css_selector_matches_append (self: &exact_matches, NULL); /* Null terminate */
2189 res = array->len;
2190 g_byte_array_append (array, data: (guint8 *) gtk_css_selector_matches_get_data (self: &exact_matches),
2191 len: gtk_css_selector_matches_get_size (self: &exact_matches) * sizeof (gpointer));
2192 }
2193 else
2194 res = GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
2195 gtk_css_selector_matches_clear (self: &exact_matches);
2196 get_tree (array, offset: tree_offset)->matches_offset = res;
2197
2198 res = subdivide_infos (array, infos: matched_infos, n_infos: n_matched, parent_offset: tree_offset);
2199 get_tree (array, offset: tree_offset)->previous_offset = res;
2200
2201 res = subdivide_infos (array, infos: remaining_infos, n_infos: n_remaining, parent_offset);
2202 get_tree (array, offset: tree_offset)->sibling_offset = res;
2203
2204 g_hash_table_unref (hash_table: ht);
2205
2206 return tree_offset;
2207}
2208
2209struct _GtkCssSelectorTreeBuilder {
2210 GArray *infos;
2211};
2212
2213GtkCssSelectorTreeBuilder *
2214_gtk_css_selector_tree_builder_new (void)
2215{
2216 GtkCssSelectorTreeBuilder *builder = g_new0 (GtkCssSelectorTreeBuilder, 1);
2217
2218 builder->infos = g_array_new (FALSE, TRUE, element_size: sizeof (GtkCssSelectorRuleSetInfo));
2219
2220 return builder;
2221}
2222
2223void
2224_gtk_css_selector_tree_builder_free (GtkCssSelectorTreeBuilder *builder)
2225{
2226 g_array_free (array: builder->infos, TRUE);
2227 g_free (mem: builder);
2228}
2229
2230void
2231_gtk_css_selector_tree_builder_add (GtkCssSelectorTreeBuilder *builder,
2232 GtkCssSelector *selectors,
2233 GtkCssSelectorTree **selector_match,
2234 gpointer match)
2235{
2236 GtkCssSelectorRuleSetInfo *info;
2237
2238 g_array_set_size (array: builder->infos, length: builder->infos->len + 1);
2239 info = &g_array_index (builder->infos, GtkCssSelectorRuleSetInfo, builder->infos->len - 1);
2240 info->match = match;
2241 info->current_selector = selectors;
2242 info->selector_match = selector_match;
2243}
2244
2245/* Convert all offsets to node-relative */
2246static void
2247fixup_offsets (GtkCssSelectorTree *tree, guint8 *data)
2248{
2249 while (tree != NULL)
2250 {
2251 if (tree->parent_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
2252 tree->parent_offset -= ((guint8 *)tree - data);
2253
2254 if (tree->previous_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
2255 tree->previous_offset -= ((guint8 *)tree - data);
2256
2257 if (tree->sibling_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
2258 tree->sibling_offset -= ((guint8 *)tree - data);
2259
2260 if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
2261 tree->matches_offset -= ((guint8 *)tree - data);
2262
2263 fixup_offsets (tree: (GtkCssSelectorTree *)gtk_css_selector_tree_get_previous (tree), data);
2264
2265 tree = (GtkCssSelectorTree *)gtk_css_selector_tree_get_sibling (tree);
2266 }
2267}
2268
2269GtkCssSelectorTree *
2270_gtk_css_selector_tree_builder_build (GtkCssSelectorTreeBuilder *builder)
2271{
2272 GtkCssSelectorTree *tree;
2273 GByteArray *array;
2274 guint8 *data;
2275 guint len;
2276 guint i;
2277 GtkCssSelectorRuleSetInfo **infos_array;
2278
2279 array = g_byte_array_new ();
2280
2281 infos_array = g_alloca (sizeof (GtkCssSelectorRuleSetInfo *) * builder->infos->len);
2282 for (i = 0; i < builder->infos->len; i++)
2283 infos_array[i] = &g_array_index (builder->infos, GtkCssSelectorRuleSetInfo, i);
2284
2285 subdivide_infos (array, infos: infos_array, n_infos: builder->infos->len, GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET);
2286
2287 len = array->len;
2288 data = g_byte_array_free (array, FALSE);
2289
2290 /* shrink to final size */
2291 data = g_realloc (mem: data, n_bytes: len);
2292
2293 tree = (GtkCssSelectorTree *)data;
2294
2295 fixup_offsets (tree, data);
2296
2297 /* Convert offsets to final pointers */
2298 for (i = 0; i < builder->infos->len; i++)
2299 {
2300 GtkCssSelectorRuleSetInfo *info = &g_array_index (builder->infos, GtkCssSelectorRuleSetInfo, i);
2301
2302 if (info->selector_match)
2303 *info->selector_match = (GtkCssSelectorTree *)(data + GPOINTER_TO_UINT (*info->selector_match));
2304 }
2305
2306
2307#ifdef PRINT_TREE
2308 {
2309 GString *s = g_string_new ("");
2310 _gtk_css_selector_tree_print (tree, s, "");
2311 g_print ("%s", s->str);
2312 g_string_free (s, TRUE);
2313 }
2314#endif
2315
2316 return tree;
2317}
2318

source code of gtk/gtk/gtkcssselector.c