+/* When checking for changes via the tree we need to know if a rule further
+ down the tree matched, because if so we need to add "our bit" to the
+ Change. For instance in a a match like *.class:active we'll
+ get a tree that first checks :active, if that matches we continue down
+ to the tree, and if we get a match we add CHANGE_CLASS. However, the
+ end of the tree where we have a match is an ANY which doesn't actually
+ modify the change, so we don't know if we have a match or not. We fix
+ this by setting GTK_CSS_CHANGE_GOT_MATCH which lets us guarantee
+ that change != 0 on any match. */
+#define GTK_CSS_CHANGE_GOT_MATCH GTK_CSS_CHANGE_RESERVED_BIT
+
+typedef struct _GtkCssSelectorClass GtkCssSelectorClass;
+
+struct _GtkCssSelectorClass {
+ const char *name;
+
+ void (* print) (const GtkCssSelector *selector,
+ GString *string);
+ gboolean (* match) (const GtkCssSelector *selector,
+ const GtkCssMatcher *matcher);
+ void (* tree_match) (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher,
+ GHashTable *res);
+ GtkCssChange (* get_change) (const GtkCssSelector *selector,
+ GtkCssChange previous_change);
+ GtkCssChange (* tree_get_change) (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher);
+ int (* compare_one) (const GtkCssSelector *a,
+ const GtkCssSelector *b);
+
+ guint increase_id_specificity :1;
+ guint increase_class_specificity :1;
+ guint increase_element_specificity :1;
+ guint is_simple :1;
+ guint must_keep_order :1; /* Due to region weirdness these must be kept before a DESCENDANT, so don't reorder */
+};
+
+struct _GtkCssSelector
+{
+ const GtkCssSelectorClass *class; /* type of check this selector does */
+ gconstpointer data; /* data for matching:
+ - interned string for CLASS, NAME and ID
+ - GUINT_TO_POINTER() for PSEUDOCLASS_REGION/STATE */
+};
+
+#define GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET G_MAXINT32
+struct _GtkCssSelectorTree
+{
+ GtkCssSelector selector;
+ gint32 parent_offset;
+ gint32 previous_offset;
+ gint32 sibling_offset;
+ gint32 matches_offset; /* pointers that we return as matches if selector matches */
+};
+
+static gboolean
+gtk_css_selector_equal (const GtkCssSelector *a,
+ const GtkCssSelector *b)
+{
+ return
+ a->class == b->class &&
+ a->data == b->data;
+}
+
+static guint
+gtk_css_selector_hash (const GtkCssSelector *selector)
+{
+ return GPOINTER_TO_UINT (selector->class) ^ GPOINTER_TO_UINT (selector->data);
+}
+
+static gpointer *
+gtk_css_selector_tree_get_matches (const GtkCssSelectorTree *tree)
+{
+ if (tree->matches_offset == GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+ return NULL;
+
+ return (gpointer *) ((guint8 *)tree + tree->matches_offset);
+}
+
+static void
+gtk_css_selector_tree_found_match (const GtkCssSelectorTree *tree,
+ GHashTable *res)
+{
+ int i;
+ gpointer *matches;
+
+ matches = gtk_css_selector_tree_get_matches (tree);
+ if (matches)
+ {
+ for (i = 0; matches[i] != NULL; i++)
+ g_hash_table_insert (res, matches[i], matches[i]);
+ }
+}
+
+static void
+gtk_css_selector_tree_match (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher,
+ GHashTable *res)
+{
+ if (tree == NULL)
+ return;
+
+ tree->selector.class->tree_match (tree, matcher, res);
+}
+
+static GtkCssChange
+gtk_css_selector_tree_get_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ if (tree == NULL)
+ return 0;
+
+ return tree->selector.class->tree_get_change (tree, matcher);
+}
+
+static gboolean
+gtk_css_selector_match (const GtkCssSelector *selector,
+ const GtkCssMatcher *matcher)
+{
+ if (selector == NULL)
+ return TRUE;
+
+ return selector->class->match (selector, matcher);
+}
+
+static int
+gtk_css_selector_compare_one (const GtkCssSelector *a, const GtkCssSelector *b)
+{
+ if (a->class != b->class)
+ return strcmp (a->class->name, b->class->name);
+ else
+ return a->class->compare_one (a, b);
+}
+
+static const GtkCssSelector *
+gtk_css_selector_previous (const GtkCssSelector *selector)
+{
+ selector = selector + 1;
+
+ return selector->class ? selector : NULL;
+}
+
+static const GtkCssSelectorTree *
+gtk_css_selector_tree_at_offset (const GtkCssSelectorTree *tree,
+ gint32 offset)
+{
+ if (offset == GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+ return NULL;
+
+ return (GtkCssSelectorTree *) ((guint8 *)tree + offset);
+}
+
+static const GtkCssSelectorTree *
+gtk_css_selector_tree_get_parent (const GtkCssSelectorTree *tree)
+{
+ return gtk_css_selector_tree_at_offset (tree, tree->parent_offset);
+}
+
+static const GtkCssSelectorTree *
+gtk_css_selector_tree_get_previous (const GtkCssSelectorTree *tree)
+{
+ return gtk_css_selector_tree_at_offset (tree, tree->previous_offset);
+}
+
+static const GtkCssSelectorTree *
+gtk_css_selector_tree_get_sibling (const GtkCssSelectorTree *tree)
+{
+ return gtk_css_selector_tree_at_offset (tree, tree->sibling_offset);
+}
+
+static void
+gtk_css_selector_tree_match_previous (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher,
+ GHashTable *res)
+{
+ const GtkCssSelectorTree *prev;
+
+ for (prev = gtk_css_selector_tree_get_previous (tree);
+ prev != NULL;
+ prev = gtk_css_selector_tree_get_sibling (prev))
+ gtk_css_selector_tree_match (prev, matcher, res);
+}
+
+static GtkCssChange
+gtk_css_selector_tree_get_previous_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ GtkCssChange previous_change = 0;
+ const GtkCssSelectorTree *prev;
+
+ for (prev = gtk_css_selector_tree_get_previous (tree);
+ prev != NULL;
+ prev = gtk_css_selector_tree_get_sibling (prev))
+ previous_change |= gtk_css_selector_tree_get_change (prev, matcher);
+
+ return previous_change;
+}
+
+/* DESCENDANT */
+
+static void
+gtk_css_selector_descendant_print (const GtkCssSelector *selector,
+ GString *string)
+{
+ g_string_append_c (string, ' ');
+}
+
+static gboolean
+gtk_css_selector_descendant_match (const GtkCssSelector *selector,
+ const GtkCssMatcher *matcher)
+{
+ GtkCssMatcher ancestor;
+
+ while (_gtk_css_matcher_get_parent (&ancestor, matcher))
+ {
+ matcher = &ancestor;
+
+ if (gtk_css_selector_match (gtk_css_selector_previous (selector), matcher))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+gtk_css_selector_descendant_tree_match (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher,
+ GHashTable *res)
+{
+ GtkCssMatcher ancestor;
+
+ while (_gtk_css_matcher_get_parent (&ancestor, matcher))
+ {
+ matcher = &ancestor;
+
+ gtk_css_selector_tree_match_previous (tree, matcher, res);
+
+ /* any matchers are dangerous here, as we may loop forever, but
+ we can terminate now as all possible matches have already been added */
+ if (_gtk_css_matcher_matches_any (matcher))
+ break;
+ }
+}
+
+static GtkCssChange
+gtk_css_selector_descendant_tree_get_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ GtkCssMatcher ancestor;
+ GtkCssChange change, previous_change;
+
+ change = 0;
+ previous_change = 0;
+ while (_gtk_css_matcher_get_parent (&ancestor, matcher))
+ {
+ matcher = &ancestor;
+
+ previous_change |= gtk_css_selector_tree_get_previous_change (tree, matcher);
+
+ /* any matchers are dangerous here, as we may loop forever, but
+ we can terminate now as all possible matches have already been added */
+ if (_gtk_css_matcher_matches_any (matcher))
+ break;
+ }
+
+ if (previous_change != 0)
+ change |= _gtk_css_change_for_child (previous_change) | GTK_CSS_CHANGE_GOT_MATCH;
+
+ return change;
+}
+
+static int
+gtk_css_selector_descendant_compare_one (const GtkCssSelector *a,
+ const GtkCssSelector *b)
+{
+ return 0;
+}
+
+static GtkCssChange
+gtk_css_selector_descendant_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
+{
+ return _gtk_css_change_for_child (previous_change);
+}
+
+static const GtkCssSelectorClass GTK_CSS_SELECTOR_DESCENDANT = {
+ "descendant",
+ gtk_css_selector_descendant_print,
+ gtk_css_selector_descendant_match,
+ gtk_css_selector_descendant_tree_match,
+ gtk_css_selector_descendant_get_change,
+ gtk_css_selector_descendant_tree_get_change,
+ gtk_css_selector_descendant_compare_one,
+ FALSE, FALSE, FALSE, FALSE, FALSE
+};
+
+/* CHILD */
+
+static void
+gtk_css_selector_child_print (const GtkCssSelector *selector,
+ GString *string)
+{
+ g_string_append (string, " > ");
+}
+
+static gboolean
+gtk_css_selector_child_match (const GtkCssSelector *selector,
+ const GtkCssMatcher *matcher)
+{
+ GtkCssMatcher parent;
+
+ if (!_gtk_css_matcher_get_parent (&parent, matcher))
+ return FALSE;
+
+ return gtk_css_selector_match (gtk_css_selector_previous (selector), &parent);
+}
+
+static void
+gtk_css_selector_child_tree_match (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher,
+ GHashTable *res)
+{
+ GtkCssMatcher parent;
+
+ if (!_gtk_css_matcher_get_parent (&parent, matcher))
+ return;
+
+ gtk_css_selector_tree_match_previous (tree, &parent, res);
+}
+
+
+static GtkCssChange
+gtk_css_selector_child_tree_get_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ GtkCssMatcher parent;
+ GtkCssChange change, previous_change;
+
+ if (!_gtk_css_matcher_get_parent (&parent, matcher))
+ return 0;
+
+ change = 0;
+
+ previous_change = gtk_css_selector_tree_get_previous_change (tree, &parent);
+
+ if (previous_change != 0)
+ change |= _gtk_css_change_for_child (previous_change) | GTK_CSS_CHANGE_GOT_MATCH;
+
+ return change;
+}
+
+static GtkCssChange
+gtk_css_selector_child_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
+{
+ return _gtk_css_change_for_child (previous_change);
+}
+
+static int
+gtk_css_selector_child_compare_one (const GtkCssSelector *a,
+ const GtkCssSelector *b)
+{
+ return 0;
+}
+
+static const GtkCssSelectorClass GTK_CSS_SELECTOR_CHILD = {
+ "child",
+ gtk_css_selector_child_print,
+ gtk_css_selector_child_match,
+ gtk_css_selector_child_tree_match,
+ gtk_css_selector_child_get_change,
+ gtk_css_selector_child_tree_get_change,
+ gtk_css_selector_child_compare_one,
+ FALSE, FALSE, FALSE, FALSE, FALSE
+};
+
+/* SIBLING */
+
+static void
+gtk_css_selector_sibling_print (const GtkCssSelector *selector,
+ GString *string)
+{
+ g_string_append (string, " ~ ");
+}
+
+static gboolean
+gtk_css_selector_sibling_match (const GtkCssSelector *selector,
+ const GtkCssMatcher *matcher)
+{
+ GtkCssMatcher previous;
+
+ while (_gtk_css_matcher_get_previous (&previous, matcher))
+ {
+ matcher = &previous;
+
+ if (gtk_css_selector_match (gtk_css_selector_previous (selector), matcher))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+gtk_css_selector_sibling_tree_match (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher,
+ GHashTable *res)
+{
+ GtkCssMatcher previous;
+
+ while (_gtk_css_matcher_get_previous (&previous, matcher))
+ {
+ matcher = &previous;
+
+ gtk_css_selector_tree_match_previous (tree, matcher, res);
+
+ /* any matchers are dangerous here, as we may loop forever, but
+ we can terminate now as all possible matches have already been added */
+ if (_gtk_css_matcher_matches_any (matcher))
+ break;
+ }
+}
+
+static GtkCssChange
+gtk_css_selector_sibling_tree_get_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ GtkCssMatcher previous;
+ GtkCssChange change, previous_change;
+
+ change = 0;
+
+ previous_change = 0;
+ while (_gtk_css_matcher_get_previous (&previous, matcher))
+ {
+ matcher = &previous;
+
+ previous_change |= gtk_css_selector_tree_get_previous_change (tree, matcher);
+
+ /* any matchers are dangerous here, as we may loop forever, but
+ we can terminate now as all possible matches have already been added */
+ if (_gtk_css_matcher_matches_any (matcher))
+ break;
+ }
+
+ if (previous_change != 0)
+ change |= _gtk_css_change_for_sibling (previous_change) | GTK_CSS_CHANGE_GOT_MATCH;
+
+ return change;
+}
+
+static GtkCssChange
+gtk_css_selector_sibling_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
+{
+ return _gtk_css_change_for_sibling (previous_change);
+}
+
+static int
+gtk_css_selector_sibling_compare_one (const GtkCssSelector *a,
+ const GtkCssSelector *b)
+{
+ return 0;
+}
+
+
+static const GtkCssSelectorClass GTK_CSS_SELECTOR_SIBLING = {
+ "sibling",
+ gtk_css_selector_sibling_print,
+ gtk_css_selector_sibling_match,
+ gtk_css_selector_sibling_tree_match,
+ gtk_css_selector_sibling_get_change,
+ gtk_css_selector_sibling_tree_get_change,
+ gtk_css_selector_sibling_compare_one,
+ FALSE, FALSE, FALSE, FALSE, FALSE
+};
+
+/* ADJACENT */
+
+static void
+gtk_css_selector_adjacent_print (const GtkCssSelector *selector,
+ GString *string)
+{
+ g_string_append (string, " + ");
+}
+
+static gboolean
+gtk_css_selector_adjacent_match (const GtkCssSelector *selector,
+ const GtkCssMatcher *matcher)
+{
+ GtkCssMatcher previous;
+
+ if (!_gtk_css_matcher_get_previous (&previous, matcher))
+ return FALSE;
+
+ return gtk_css_selector_match (gtk_css_selector_previous (selector), &previous);
+}
+
+static void
+gtk_css_selector_adjacent_tree_match (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher,
+ GHashTable *res)
+{
+ GtkCssMatcher previous;
+
+ if (!_gtk_css_matcher_get_previous (&previous, matcher))
+ return;
+
+ matcher = &previous;
+
+ gtk_css_selector_tree_match_previous (tree, matcher, res);
+}
+
+static GtkCssChange
+gtk_css_selector_adjacent_tree_get_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ GtkCssMatcher previous;
+ GtkCssChange change, previous_change;
+
+ if (!_gtk_css_matcher_get_previous (&previous, matcher))
+ return 0;
+
+ change = 0;
+
+ previous_change = gtk_css_selector_tree_get_previous_change (tree, &previous);
+
+ if (previous_change != 0)
+ change |= _gtk_css_change_for_sibling (previous_change) | GTK_CSS_CHANGE_GOT_MATCH;
+
+ return change;
+}
+
+static GtkCssChange
+gtk_css_selector_adjacent_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
+{
+ return _gtk_css_change_for_sibling (previous_change);
+}
+
+static int
+gtk_css_selector_adjacent_compare_one (const GtkCssSelector *a,
+ const GtkCssSelector *b)
+{
+ return 0;
+}
+
+static const GtkCssSelectorClass GTK_CSS_SELECTOR_ADJACENT = {
+ "adjacent",
+ gtk_css_selector_adjacent_print,
+ gtk_css_selector_adjacent_match,
+ gtk_css_selector_adjacent_tree_match,
+ gtk_css_selector_adjacent_get_change,
+ gtk_css_selector_adjacent_tree_get_change,
+ gtk_css_selector_adjacent_compare_one,
+ FALSE, FALSE, FALSE, FALSE, FALSE
+};
+
+/* ANY */
+
+static void
+gtk_css_selector_any_print (const GtkCssSelector *selector,
+ GString *string)
+{
+ g_string_append_c (string, '*');
+}
+
+static gboolean
+gtk_css_selector_any_match (const GtkCssSelector *selector,
+ const GtkCssMatcher *matcher)
+{
+ const GtkCssSelector *previous = gtk_css_selector_previous (selector);
+
+ if (previous &&
+ previous->class == >K_CSS_SELECTOR_DESCENDANT &&
+ _gtk_css_matcher_has_regions (matcher))
+ {
+ if (gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
+ return TRUE;
+ }
+
+ return gtk_css_selector_match (previous, matcher);
+}
+
+static void
+gtk_css_selector_any_tree_match (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher,
+ GHashTable *res)
+{
+ const GtkCssSelectorTree *prev;
+
+ gtk_css_selector_tree_found_match (tree, res);
+
+ for (prev = gtk_css_selector_tree_get_previous (tree);
+ prev != NULL;
+ prev = gtk_css_selector_tree_get_sibling (prev))
+ {
+ if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT &&
+ _gtk_css_matcher_has_regions (matcher))
+ gtk_css_selector_tree_match_previous (prev, matcher, res);
+
+ gtk_css_selector_tree_match (prev, matcher, res);
+ }
+}
+
+static GtkCssChange
+gtk_css_selector_any_tree_get_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ const GtkCssSelectorTree *prev;
+ GtkCssChange change, previous_change;
+
+ change = 0;
+
+ if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+ change |= GTK_CSS_CHANGE_GOT_MATCH;
+
+ previous_change = 0;
+ for (prev = gtk_css_selector_tree_get_previous (tree);
+ prev != NULL;
+ prev = gtk_css_selector_tree_get_sibling (prev))
+ {
+ if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT &&
+ _gtk_css_matcher_has_regions (matcher))
+ previous_change |= gtk_css_selector_tree_get_previous_change (prev, matcher);
+
+ previous_change |= gtk_css_selector_tree_get_change (prev, matcher);
+ }
+
+ if (previous_change != 0)
+ change |= previous_change | GTK_CSS_CHANGE_GOT_MATCH;
+
+ return change;
+}
+
+
+static GtkCssChange
+gtk_css_selector_any_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
+{
+ return previous_change;
+}
+
+static int
+gtk_css_selector_any_compare_one (const GtkCssSelector *a,
+ const GtkCssSelector *b)
+{
+ return 0;
+}
+
+static const GtkCssSelectorClass GTK_CSS_SELECTOR_ANY = {
+ "any",
+ gtk_css_selector_any_print,
+ gtk_css_selector_any_match,
+ gtk_css_selector_any_tree_match,
+ gtk_css_selector_any_get_change,
+ gtk_css_selector_any_tree_get_change,
+ gtk_css_selector_any_compare_one,
+ FALSE, FALSE, FALSE, TRUE, TRUE
+};
+
+/* NAME */
+
+typedef struct {
+ GType type;
+ const char *name;
+} TypeReference;
+
+static GHashTable *type_refs_ht = NULL;
+static guint type_refs_last_serial = 0;
+
+static TypeReference *
+get_type_reference (const char *name)
+{
+ TypeReference *ref;
+
+
+ if (type_refs_ht == NULL)
+ type_refs_ht = g_hash_table_new (g_str_hash, g_str_equal);
+
+ ref = g_hash_table_lookup (type_refs_ht, name);
+
+ if (ref != NULL)
+ return ref;
+
+ ref = g_slice_new (TypeReference);
+ ref->name = g_intern_string (name);
+ ref->type = g_type_from_name (ref->name);
+
+ g_hash_table_insert (type_refs_ht,
+ (gpointer)ref->name, ref);
+
+ return ref;
+}
+
+static void
+update_type_references (void)
+{
+ GHashTableIter iter;
+ guint serial;
+ gpointer value;
+
+ serial = g_type_get_type_registration_serial ();
+
+ if (serial == type_refs_last_serial)
+ return;
+
+ type_refs_last_serial = serial;
+
+ if (type_refs_ht == NULL)
+ return;
+
+ g_hash_table_iter_init (&iter, type_refs_ht);
+ while (g_hash_table_iter_next (&iter,
+ NULL, &value))
+ {
+ TypeReference *ref = value;
+ if (ref->type == G_TYPE_INVALID)
+ ref->type = g_type_from_name (ref->name);
+ }
+}
+
+static void
+gtk_css_selector_name_print (const GtkCssSelector *selector,
+ GString *string)
+{
+ g_string_append (string, ((TypeReference *)selector->data)->name);
+}
+
+static gboolean
+gtk_css_selector_name_match (const GtkCssSelector *selector,
+ const GtkCssMatcher *matcher)
+{
+ if (!_gtk_css_matcher_has_type (matcher, ((TypeReference *)selector->data)->type))
+ return FALSE;
+
+ return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
+}
+
+static void
+gtk_css_selector_name_tree_match (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher,
+ GHashTable *res)
+{
+ if (!_gtk_css_matcher_has_type (matcher, ((TypeReference *)tree->selector.data)->type))
+ return;
+
+ gtk_css_selector_tree_found_match (tree, res);
+
+ gtk_css_selector_tree_match_previous (tree, matcher, res);
+}
+
+static GtkCssChange
+gtk_css_selector_name_tree_get_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ GtkCssChange change, previous_change;
+
+ if (!_gtk_css_matcher_has_type (matcher, ((TypeReference *)tree->selector.data)->type))
+ return 0;
+
+ change = 0;
+
+ if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+ change |= GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_GOT_MATCH;
+
+ previous_change = gtk_css_selector_tree_get_previous_change (tree, matcher);
+
+ if (previous_change)
+ change |= previous_change | GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_GOT_MATCH;
+
+ return change;
+}
+
+
+static GtkCssChange
+gtk_css_selector_name_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
+{
+ return previous_change | GTK_CSS_CHANGE_NAME;
+}
+
+static int
+gtk_css_selector_name_compare_one (const GtkCssSelector *a,
+ const GtkCssSelector *b)
+{
+ return strcmp (((TypeReference *)a->data)->name,
+ ((TypeReference *)b->data)->name);
+}
+
+static const GtkCssSelectorClass GTK_CSS_SELECTOR_NAME = {
+ "name",
+ gtk_css_selector_name_print,
+ gtk_css_selector_name_match,
+ gtk_css_selector_name_tree_match,
+ gtk_css_selector_name_get_change,
+ gtk_css_selector_name_tree_get_change,
+ gtk_css_selector_name_compare_one,
+ FALSE, FALSE, TRUE, TRUE, FALSE
+};
+
+/* REGION */
+
+static void
+gtk_css_selector_region_print (const GtkCssSelector *selector,
+ GString *string)
+{
+ g_string_append (string, selector->data);
+}
+
+static gboolean
+gtk_css_selector_region_match (const GtkCssSelector *selector,
+ const GtkCssMatcher *matcher)
+{
+ const GtkCssSelector *previous;
+
+ if (!_gtk_css_matcher_has_region (matcher, selector->data, 0))
+ return FALSE;
+
+ previous = gtk_css_selector_previous (selector);
+ if (previous && previous->class == >K_CSS_SELECTOR_DESCENDANT &&
+ gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
+ return TRUE;
+
+ return gtk_css_selector_match (previous, matcher);
+}
+
+static void
+gtk_css_selector_region_tree_match (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher,
+ GHashTable *res)
+{
+ const GtkCssSelectorTree *prev;
+
+ if (!_gtk_css_matcher_has_region (matcher, tree->selector.data, 0))
+ return;
+
+ gtk_css_selector_tree_found_match (tree, res);
+
+ for (prev = gtk_css_selector_tree_get_previous (tree);
+ prev != NULL;
+ prev = gtk_css_selector_tree_get_sibling (prev))
+ {
+ if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT)
+ gtk_css_selector_tree_match_previous (prev, matcher, res);
+
+ gtk_css_selector_tree_match (prev, matcher, res);
+ }
+}
+
+static GtkCssChange
+gtk_css_selector_region_tree_get_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ const GtkCssSelectorTree *prev;
+ GtkCssChange change, previous_change;
+
+ if (!_gtk_css_matcher_has_region (matcher, tree->selector.data, 0))
+ return 0;
+
+ change = 0;
+
+ if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+ change |= GTK_CSS_CHANGE_REGION | GTK_CSS_CHANGE_GOT_MATCH;
+
+ previous_change = 0;
+ for (prev = gtk_css_selector_tree_get_previous (tree);
+ prev != NULL;
+ prev = gtk_css_selector_tree_get_sibling (prev))
+ {
+ if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT)
+ previous_change |= gtk_css_selector_tree_get_previous_change (prev, matcher);
+
+ previous_change |= gtk_css_selector_tree_get_change (prev, matcher);
+ }
+
+ if (previous_change != 0)
+ {
+ previous_change |= GTK_CSS_CHANGE_REGION;
+ previous_change |= _gtk_css_change_for_child (previous_change);
+ change |= previous_change | GTK_CSS_CHANGE_GOT_MATCH;
+ }
+
+ return change;
+}
+
+static GtkCssChange
+gtk_css_selector_region_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
+{
+ GtkCssChange change;
+
+ change = previous_change;
+ change |= GTK_CSS_CHANGE_REGION;
+ change |= _gtk_css_change_for_child (change);
+
+ return change;
+}
+
+static int
+gtk_css_selector_region_compare_one (const GtkCssSelector *a,
+ const GtkCssSelector *b)
+{
+ return strcmp (a->data, b->data);
+}
+
+static const GtkCssSelectorClass GTK_CSS_SELECTOR_REGION = {
+ "region",
+ gtk_css_selector_region_print,
+ gtk_css_selector_region_match,
+ gtk_css_selector_region_tree_match,
+ gtk_css_selector_region_get_change,
+ gtk_css_selector_region_tree_get_change,
+ gtk_css_selector_region_compare_one,
+ FALSE, FALSE, TRUE, TRUE, TRUE
+};
+
+/* CLASS */
+
+static void
+gtk_css_selector_class_print (const GtkCssSelector *selector,
+ GString *string)
+{
+ g_string_append_c (string, '.');
+ g_string_append (string, g_quark_to_string (GPOINTER_TO_UINT (selector->data)));
+}
+
+static gboolean
+gtk_css_selector_class_match (const GtkCssSelector *selector,
+ const GtkCssMatcher *matcher)
+{
+ if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (selector->data)))
+ return FALSE;
+
+ return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
+}
+
+static void
+gtk_css_selector_class_tree_match (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher,
+ GHashTable *res)
+{
+ if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (tree->selector.data)))
+ return;
+
+ gtk_css_selector_tree_found_match (tree, res);
+
+ gtk_css_selector_tree_match_previous (tree, matcher, res);
+}
+
+static GtkCssChange
+gtk_css_selector_class_tree_get_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ GtkCssChange change, previous_change;
+
+ if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (tree->selector.data)))
+ return 0;
+
+ change = 0;
+
+ if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+ change |= GTK_CSS_CHANGE_CLASS | GTK_CSS_CHANGE_GOT_MATCH;
+
+ previous_change = gtk_css_selector_tree_get_previous_change (tree, matcher);
+
+ if (previous_change != 0)
+ change |= previous_change | GTK_CSS_CHANGE_CLASS | GTK_CSS_CHANGE_GOT_MATCH;
+
+ return change;
+}
+
+static GtkCssChange
+gtk_css_selector_class_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
+{
+ return previous_change | GTK_CSS_CHANGE_CLASS;
+}
+
+static int
+gtk_css_selector_class_compare_one (const GtkCssSelector *a,
+ const GtkCssSelector *b)