[gtk+/wip/otte/tokenizer: 8/78] css: Add skeleton for new parsing infrastructure



commit 674ca8ae1cec4699a2bf5b8de8bc52889eb40e56
Author: Benjamin Otte <otte redhat com>
Date:   Wed Mar 9 19:44:50 2016 +0100

    css: Add skeleton for new parsing infrastructure
    
    This infrastructure will be based around the CSSOM and return a
    GtkCssStyleSheet object as its result.
    
    The initial intended use for this is as a syntax tree while editing in
    the inspector, but we might even want to look into exporting the CSSOM
    parts as public API at some point.

 gtk/Makefile.am                     |   12 +
 gtk/gtkcssrule.c                    |  215 +++++++++++++
 gtk/gtkcssrulelist.c                |  166 ++++++++++
 gtk/gtkcssrulelistprivate.h         |   69 ++++
 gtk/gtkcssruleprivate.h             |   70 ++++
 gtk/gtkcssselector.c                |  597 +++++++++++++++++++++++++++++++++--
 gtk/gtkcssselectorprivate.h         |    2 +
 gtk/gtkcssstyledeclaration.c        |   66 ++++
 gtk/gtkcssstyledeclarationprivate.h |   61 ++++
 gtk/gtkcssstylerule.c               |  124 ++++++++
 gtk/gtkcssstyleruleprivate.h        |   56 ++++
 gtk/gtkcssstylesheet.c              |  114 +++++++
 gtk/gtkcssstylesheetprivate.h       |   67 ++++
 gtk/gtkcsstokensource.c             |  369 +++++++++++++++++++++
 gtk/gtkcsstokensourceprivate.h      |   81 +++++
 15 files changed, 2038 insertions(+), 31 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index a73ffa7..c7aba5c 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -434,6 +434,8 @@ gtk_private_h_sources =             \
        gtkcssrbtreeprivate.h   \
        gtkcssrepeatvalueprivate.h      \
        gtkcssrgbavalueprivate.h        \
+       gtkcssruleprivate.h     \
+       gtkcssrulelistprivate.h \
        gtkcsssectionprivate.h  \
        gtkcssselectorprivate.h \
        gtkcssshadowsvalueprivate.h     \
@@ -445,7 +447,11 @@ gtk_private_h_sources =            \
        gtkcssstylechangeprivate.h      \
        gtkcssstyleprivate.h    \
        gtkcssstylepropertyprivate.h \
+       gtkcssstyledeclarationprivate.h \
+       gtkcssstyleruleprivate.h        \
+       gtkcssstylesheetprivate.h       \
        gtkcsstokenizerprivate.h        \
+       gtkcsstokensourceprivate.h      \
        gtkcsstransformvalueprivate.h   \
        gtkcsstransientnodeprivate.h    \
        gtkcsstransitionprivate.h       \
@@ -704,11 +710,15 @@ gtk_base_c_sources =              \
        gtkcssrbtree.c          \
        gtkcssrepeatvalue.c     \
        gtkcssrgbavalue.c       \
+       gtkcssrule.c            \
+       gtkcssrulelist.c        \
        gtkcsssection.c         \
        gtkcssselector.c        \
        gtkcssstringvalue.c     \
        gtkcssstyle.c           \
        gtkcssstylechange.c     \
+       gtkcssstyledeclaration.c \
+       gtkcssstylerule.c       \
        gtkcssshadowsvalue.c    \
        gtkcssshadowvalue.c     \
        gtkcssshorthandproperty.c \
@@ -717,7 +727,9 @@ gtk_base_c_sources =                \
        gtkcssstylefuncs.c      \
        gtkcssstyleproperty.c   \
        gtkcssstylepropertyimpl.c \
+       gtkcssstylesheet.c      \
        gtkcsstokenizer.c       \
+       gtkcsstokensource.c     \
        gtkcsstransformvalue.c  \
        gtkcsstransientnode.c   \
        gtkcsstransition.c      \
diff --git a/gtk/gtkcssrule.c b/gtk/gtkcssrule.c
new file mode 100644
index 0000000..d074a4a
--- /dev/null
+++ b/gtk/gtkcssrule.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright © 2016 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#include "config.h"
+
+#include "gtkcssruleprivate.h"
+
+#include "gtkcssstylesheetprivate.h"
+
+typedef struct _GtkCssRulePrivate GtkCssRulePrivate;
+struct _GtkCssRulePrivate {
+  GtkCssRule *parent_rule;
+  GtkCssStyleSheet *parent_style_sheet;
+};
+
+typedef struct _GtkCssTokenSourceAt GtkCssTokenSourceAt;
+struct _GtkCssTokenSourceAt {
+  GtkCssTokenSource parent;
+  GtkCssTokenSource *source;
+  guint inside_curly_block :1;
+  guint done :1;
+};
+
+static void
+gtk_css_token_source_at_finalize (GtkCssTokenSource *source)
+{
+  GtkCssTokenSourceAt *at = (GtkCssTokenSourceAt *) source;
+
+  gtk_css_token_source_unref (at->source);
+}
+
+static void
+gtk_css_token_source_at_consume_token (GtkCssTokenSource *source)
+{
+  GtkCssTokenSourceAt *at = (GtkCssTokenSourceAt *) source;
+  const GtkCssToken *token;
+
+  if (at->done)
+    return;
+
+  if (gtk_css_token_get_pending_block (source))
+    {
+      gtk_css_token_source_consume_token (at->source);
+      return;
+    }
+
+  token = gtk_css_token_source_get_token (at->source);
+  if (gtk_css_token_is (token, GTK_CSS_TOKEN_SEMICOLON))
+    at->done = TRUE;
+  else if (at->inside_curly_block && gtk_css_token_is (token, GTK_CSS_TOKEN_CLOSE_CURLY))
+    at->done = TRUE;
+  else if (gtk_css_token_is (token, GTK_CSS_TOKEN_OPEN_CURLY))
+    at->inside_curly_block = TRUE;
+
+  gtk_css_token_source_consume_token (at->source);
+}
+
+const GtkCssToken *
+gtk_css_token_source_at_get_token (GtkCssTokenSource *source)
+{
+  GtkCssTokenSourceAt *at = (GtkCssTokenSourceAt *) source;
+  static GtkCssToken eof_token = { GTK_CSS_TOKEN_EOF };
+
+  if (at->done)
+    return &eof_token;
+
+  return gtk_css_token_source_get_token (at->source);
+}
+
+static void
+gtk_css_token_source_at_error (GtkCssTokenSource *source,
+                               const GError      *error)
+{
+  GtkCssTokenSourceAt *at = (GtkCssTokenSourceAt *) source;
+
+  gtk_css_token_source_emit_error (at->source, error);
+}
+
+static const GtkCssTokenSourceClass GTK_CSS_TOKEN_SOURCE_AT = {
+  gtk_css_token_source_at_finalize,
+  gtk_css_token_source_at_consume_token,
+  gtk_css_token_source_at_get_token,
+  gtk_css_token_source_at_error
+};
+
+static GtkCssTokenSource *
+gtk_css_token_source_new_at (GtkCssTokenSource *source)
+{
+  GtkCssTokenSourceAt *at = gtk_css_token_source_new (GtkCssTokenSourceAt, &GTK_CSS_TOKEN_SOURCE_AT);
+
+  at->source = gtk_css_token_source_ref (source);
+
+  return &at->parent;
+}
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GtkCssRule, gtk_css_rule, G_TYPE_OBJECT)
+
+static void
+gtk_css_rule_class_init (GtkCssRuleClass *klass)
+{
+}
+
+static void
+gtk_css_rule_init (GtkCssRule *rule)
+{
+}
+
+GtkCssRule *
+gtk_css_rule_new_from_at_rule (GtkCssTokenSource *source,
+                               GtkCssRule        *parent_rule,
+                               GtkCssStyleSheet  *parent_style_sheet)
+{
+  GtkCssTokenSource *at_source;
+  const GtkCssToken *token;
+
+  g_return_val_if_fail (source != NULL, NULL);
+  g_return_val_if_fail (parent_rule == NULL || GTK_IS_CSS_RULE (parent_rule), NULL);
+  g_return_val_if_fail (GTK_IS_CSS_STYLE_SHEET (parent_style_sheet), NULL);
+
+  at_source = gtk_css_token_source_new_at (source);
+
+  token = gtk_css_token_source_get_token (at_source);
+  if (token->type != GTK_CSS_TOKEN_AT_KEYWORD)
+    {
+      gtk_css_token_source_error (at_source, "Expected an '@'");
+      gtk_css_token_source_unref (at_source);
+      return NULL;
+    }
+  else
+    {
+      const char *name = token->string.string;
+
+      if (g_ascii_strcasecmp (name, "import") == 0)
+        {
+          gtk_css_token_source_error (source, "Add code to parse @import here");
+        }
+      else
+        {
+          gtk_css_token_source_unknown (source, "Unknown rule @%s", name);
+        }
+    }
+
+  gtk_css_token_source_consume_all (at_source);
+  gtk_css_token_source_unref (at_source);
+
+  return NULL;
+}
+
+void
+gtk_css_rule_print_css_text (GtkCssRule *rule,
+                             GString    *string)
+{
+  GtkCssRuleClass *klass;
+
+  g_return_if_fail (GTK_IS_CSS_RULE (rule));
+  g_return_if_fail (string != NULL);
+
+  klass = GTK_CSS_RULE_GET_CLASS (rule);
+
+  klass->get_css_text (rule, string);
+}
+
+char *
+gtk_css_rule_get_css_text (GtkCssRule *rule)
+{
+  GString *string;
+
+  g_return_val_if_fail (GTK_IS_CSS_RULE (rule), NULL);
+
+  string = g_string_new (NULL);
+
+  gtk_css_rule_print_css_text (rule, string);
+
+  return g_string_free (string, FALSE);
+}
+
+GtkCssRule *
+gtk_css_rule_get_parent_rule (GtkCssRule *rule)
+{
+  GtkCssRulePrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_CSS_RULE (rule), NULL);
+
+  priv = gtk_css_rule_get_instance_private (rule);
+
+  return priv->parent_rule;
+}
+
+GtkCssStyleSheet *
+gtk_css_rule_get_parent_style_sheet (GtkCssRule *rule)
+{
+  GtkCssRulePrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_CSS_RULE (rule), NULL);
+
+  priv = gtk_css_rule_get_instance_private (rule);
+
+  return priv->parent_style_sheet;
+}
diff --git a/gtk/gtkcssrulelist.c b/gtk/gtkcssrulelist.c
new file mode 100644
index 0000000..fa72a63
--- /dev/null
+++ b/gtk/gtkcssrulelist.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright © 2016 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#include "config.h"
+
+#include "gtkcssrulelistprivate.h"
+#include "gtkcssstyleruleprivate.h"
+#include "gtkcssstylesheetprivate.h"
+
+typedef struct _GtkCssRuleListPrivate GtkCssRuleListPrivate;
+struct _GtkCssRuleListPrivate {
+  GPtrArray *items;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GtkCssRuleList, gtk_css_rule_list, G_TYPE_OBJECT)
+
+static void
+gtk_css_rule_list_finalize (GObject *object)
+{
+  GtkCssRuleList *rule_list = GTK_CSS_RULE_LIST (object);
+  GtkCssRuleListPrivate *priv = gtk_css_rule_list_get_instance_private (rule_list);
+
+  g_ptr_array_unref (priv->items);
+
+  G_OBJECT_CLASS (gtk_css_rule_list_parent_class)->finalize (object);
+}
+
+static void
+gtk_css_rule_list_class_init (GtkCssRuleListClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gtk_css_rule_list_finalize;
+}
+
+static void
+gtk_css_rule_list_init (GtkCssRuleList *rule_list)
+{
+  GtkCssRuleListPrivate *priv = gtk_css_rule_list_get_instance_private (rule_list);
+  
+  priv->items = g_ptr_array_new_with_free_func (g_object_unref);
+}
+
+GtkCssRuleList *
+gtk_css_rule_list_new (void)
+{
+  return g_object_new (GTK_TYPE_CSS_RULE_LIST, NULL);
+}
+
+void
+gtk_css_rule_list_parse (GtkCssRuleList    *rule_list,
+                         GtkCssTokenSource *source,
+                         GtkCssRule        *parent_rule,
+                         GtkCssStyleSheet  *parent_style_sheet)
+{
+  GtkCssRule *rule;
+  const GtkCssToken *token;
+
+  g_return_if_fail (GTK_IS_CSS_RULE_LIST (rule_list));
+  g_return_if_fail (source != NULL);
+  g_return_if_fail (parent_rule == NULL || GTK_IS_CSS_RULE (parent_rule));
+  g_return_if_fail (GTK_IS_CSS_STYLE_SHEET (parent_style_sheet));
+
+  for (token = gtk_css_token_source_get_token (source);
+       token->type != GTK_CSS_TOKEN_EOF;
+       token = gtk_css_token_source_get_token (source))
+    {
+      switch (token->type)
+        {
+          case GTK_CSS_TOKEN_WHITESPACE:
+            gtk_css_token_source_consume_token (source);
+            break;
+
+          case GTK_CSS_TOKEN_AT_KEYWORD:
+            rule = gtk_css_rule_new_from_at_rule (source, parent_rule, parent_style_sheet);
+            if (rule)
+              gtk_css_rule_list_append (rule_list, rule);
+            break;
+
+          case GTK_CSS_TOKEN_CDO:
+          case GTK_CSS_TOKEN_CDC:
+            if (parent_rule == NULL)
+              {
+                gtk_css_token_source_consume_token (source);
+                break;
+              }
+            /* else fall through */
+          default:
+            rule = gtk_css_style_rule_new_parse (source, parent_rule, parent_style_sheet);
+            if (rule)
+              gtk_css_rule_list_append (rule_list, rule);
+            break;
+        }
+    }
+}
+
+void
+gtk_css_rule_list_insert (GtkCssRuleList *rule_list,
+                          gsize           id,
+                          GtkCssRule     *rule)
+{
+  GtkCssRuleListPrivate *priv;
+
+  g_return_if_fail (GTK_IS_CSS_RULE_LIST (rule_list));
+  g_return_if_fail (GTK_IS_CSS_RULE (rule));
+
+  priv = gtk_css_rule_list_get_instance_private (rule_list);
+
+  g_ptr_array_insert (priv->items, id, g_object_ref (rule));
+}
+
+void
+gtk_css_rule_list_append (GtkCssRuleList *rule_list,
+                          GtkCssRule     *rule)
+{
+  GtkCssRuleListPrivate *priv;
+
+  g_return_if_fail (GTK_IS_CSS_RULE_LIST (rule_list));
+  g_return_if_fail (GTK_IS_CSS_RULE (rule));
+
+  priv = gtk_css_rule_list_get_instance_private (rule_list);
+
+  g_ptr_array_add (priv->items, g_object_ref (rule));
+}
+
+GtkCssRule *
+gtk_css_rule_list_get_item (GtkCssRuleList *rule_list,
+                            gsize           id)
+{
+  GtkCssRuleListPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_CSS_RULE_LIST (rule_list), NULL);
+
+  priv = gtk_css_rule_list_get_instance_private (rule_list);
+  g_return_val_if_fail (id < priv->items->len, NULL);
+
+  return g_ptr_array_index (priv->items, id);
+}
+
+gsize
+gtk_css_rule_list_get_length (GtkCssRuleList *rule_list)
+{
+  GtkCssRuleListPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_CSS_RULE_LIST (rule_list), 0);
+
+  priv = gtk_css_rule_list_get_instance_private (rule_list);
+
+  return priv->items->len;
+}
diff --git a/gtk/gtkcssrulelistprivate.h b/gtk/gtkcssrulelistprivate.h
new file mode 100644
index 0000000..fb449fd
--- /dev/null
+++ b/gtk/gtkcssrulelistprivate.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright © 2016 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#ifndef __GTK_CSS_RULE_LIST_PRIVATE_H__
+#define __GTK_CSS_RULE_LIST_PRIVATE_H__
+
+#include "gtk/gtkcssruleprivate.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CSS_RULE_LIST           (gtk_css_rule_list_get_type ())
+#define GTK_CSS_RULE_LIST(obj)           (G_TYPE_CHECK_INSTANCE_CAST (obj, GTK_TYPE_CSS_RULE_LIST, 
GtkCssRuleList))
+#define GTK_CSS_RULE_LIST_CLASS(cls)     (G_TYPE_CHECK_CLASS_CAST (cls, GTK_TYPE_CSS_RULE_LIST, 
GtkCssRuleListClass))
+#define GTK_IS_CSS_RULE_LIST(obj)        (G_TYPE_CHECK_INSTANCE_TYPE (obj, GTK_TYPE_CSS_RULE_LIST))
+#define GTK_IS_CSS_RULE_LIST_CLASS(obj)  (G_TYPE_CHECK_CLASS_TYPE (obj, GTK_TYPE_CSS_RULE_LIST))
+#define GTK_CSS_RULE_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CSS_RULE_LIST, 
GtkCssRuleListClass))
+
+typedef struct _GtkCssRuleList           GtkCssRuleList;
+typedef struct _GtkCssRuleListClass      GtkCssRuleListClass;
+
+struct _GtkCssRuleList
+{
+  GObject parent;
+};
+
+struct _GtkCssRuleListClass
+{
+  GObjectClass parent_class;
+};
+
+GType                  gtk_css_rule_list_get_type               (void) G_GNUC_CONST;
+
+GtkCssRuleList *       gtk_css_rule_list_new                    (void);
+
+void                   gtk_css_rule_list_parse                  (GtkCssRuleList         *rule_list,
+                                                                 GtkCssTokenSource      *source,
+                                                                 GtkCssRule             *parent_rule,
+                                                                 GtkCssStyleSheet       *parent_style_sheet);
+void                   gtk_css_rule_list_insert                 (GtkCssRuleList         *rule_list,
+                                                                 gsize                   id,
+                                                                 GtkCssRule             *rule);
+void                   gtk_css_rule_list_append                 (GtkCssRuleList         *rule_list,
+                                                                 GtkCssRule             *rule);
+
+/* GtkCSSRule DOM */
+GtkCssRule *           gtk_css_rule_list_get_item               (GtkCssRuleList         *rule_list,
+                                                                 gsize                   id);
+gsize                  gtk_css_rule_list_get_length             (GtkCssRuleList         *rule_list);
+
+
+G_END_DECLS
+
+#endif /* __GTK_CSS_RULE_LIST_PRIVATE_H__ */
diff --git a/gtk/gtkcssruleprivate.h b/gtk/gtkcssruleprivate.h
new file mode 100644
index 0000000..6a07b60
--- /dev/null
+++ b/gtk/gtkcssruleprivate.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright © 2016 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#ifndef __GTK_CSS_RULE_PRIVATE_H__
+#define __GTK_CSS_RULE_PRIVATE_H__
+
+#include "gtk/gtkcsstokensourceprivate.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CSS_RULE           (gtk_css_rule_get_type ())
+#define GTK_CSS_RULE(obj)           (G_TYPE_CHECK_INSTANCE_CAST (obj, GTK_TYPE_CSS_RULE, GtkCssRule))
+#define GTK_CSS_RULE_CLASS(cls)     (G_TYPE_CHECK_CLASS_CAST (cls, GTK_TYPE_CSS_RULE, GtkCssRuleClass))
+#define GTK_IS_CSS_RULE(obj)        (G_TYPE_CHECK_INSTANCE_TYPE (obj, GTK_TYPE_CSS_RULE))
+#define GTK_IS_CSS_RULE_CLASS(obj)  (G_TYPE_CHECK_CLASS_TYPE (obj, GTK_TYPE_CSS_RULE))
+#define GTK_CSS_RULE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CSS_RULE, GtkCssRuleClass))
+
+/* forward declaration */
+typedef struct _GtkCssStyleSheet     GtkCssStyleSheet;
+
+typedef struct _GtkCssRule           GtkCssRule;
+typedef struct _GtkCssRuleClass      GtkCssRuleClass;
+
+struct _GtkCssRule
+{
+  GObject parent;
+};
+
+struct _GtkCssRuleClass
+{
+  GObjectClass parent_class;
+
+  /* gets the cssText for this rule */
+  void                 (* get_css_text)                    (GtkCssRule                  *rule,
+                                                            GString                     *string);
+};
+
+GType                  gtk_css_rule_get_type               (void) G_GNUC_CONST;
+
+GtkCssRule *           gtk_css_rule_new_from_at_rule       (GtkCssTokenSource           *source,
+                                                            GtkCssRule                  *parent_rule,
+                                                            GtkCssStyleSheet            *parent_style_sheet);
+
+void                   gtk_css_rule_print_css_text         (GtkCssRule                  *rule,
+                                                            GString                     *string);
+char *                 gtk_css_rule_get_css_text           (GtkCssRule                  *rule);
+
+GtkCssRule *           gtk_css_rule_get_parent_rule        (GtkCssRule                  *rule);
+GtkCssStyleSheet *     gtk_css_rule_get_parent_style_sheet (GtkCssRule                  *rule);
+
+
+G_END_DECLS
+
+#endif /* __GTK_CSS_RULE_PRIVATE_H__ */
diff --git a/gtk/gtkcssselector.c b/gtk/gtkcssselector.c
index 9309de5..c0f9ef0 100644
--- a/gtk/gtkcssselector.c
+++ b/gtk/gtkcssselector.c
@@ -25,6 +25,8 @@
 #include "gtkcssprovider.h"
 #include "gtkstylecontextprivate.h"
 
+#include <errno.h>
+#include <stdlib.h>
 #if defined(_MSC_VER) && _MSC_VER >= 1500
 # include <intrin.h>
 #endif
@@ -1102,42 +1104,42 @@ parse_selector_pseudo_class_nth_child (GtkCssParser   *parser,
   return selector;
 }
 
+static const struct {
+  const char    *name;
+  gboolean       deprecated;
+  GtkStateFlags  state_flag;
+  PositionType   position_type;
+  int            position_a;
+  int            position_b;
+} pseudo_classes[] = {
+  { "first-child",   0, 0,                           POSITION_FORWARD,  0, 1 },
+  { "last-child",    0, 0,                           POSITION_BACKWARD, 0, 1 },
+  { "only-child",    0, 0,                           POSITION_ONLY,     0, 0 },
+  { "sorted",        1, 0,                           POSITION_SORTED,   0, 0 },
+  { "active",        0, GTK_STATE_FLAG_ACTIVE, },
+  { "prelight",      1, GTK_STATE_FLAG_PRELIGHT, },
+  { "hover",         0, GTK_STATE_FLAG_PRELIGHT, },
+  { "selected",      0, GTK_STATE_FLAG_SELECTED, },
+  { "insensitive",   1, GTK_STATE_FLAG_INSENSITIVE, },
+  { "disabled",      0, GTK_STATE_FLAG_INSENSITIVE, },
+  { "inconsistent",  1, GTK_STATE_FLAG_INCONSISTENT, },
+  { "indeterminate", 0, GTK_STATE_FLAG_INCONSISTENT, },
+  { "focused",       1, GTK_STATE_FLAG_FOCUSED, },
+  { "focus",         0, GTK_STATE_FLAG_FOCUSED, },
+  { "backdrop",      0, GTK_STATE_FLAG_BACKDROP, },
+  { "dir(ltr)",      0, GTK_STATE_FLAG_DIR_LTR, },
+  { "dir(rtl)",      0, GTK_STATE_FLAG_DIR_RTL, },
+  { "link",          0, GTK_STATE_FLAG_LINK, },
+  { "visited",       0, GTK_STATE_FLAG_VISITED, },
+  { "checked",       0, GTK_STATE_FLAG_CHECKED, },
+  { "drop(active)",  0, GTK_STATE_FLAG_DROP_ACTIVE, }
+};
+
 static GtkCssSelector *
 parse_selector_pseudo_class (GtkCssParser   *parser,
                              GtkCssSelector *selector,
                              gboolean        negate)
 {
-  static const struct {
-    const char    *name;
-    gboolean       deprecated;
-    GtkStateFlags  state_flag;
-    PositionType   position_type;
-    int            position_a;
-    int            position_b;
-  } pseudo_classes[] = {
-    { "first-child",   0, 0,                           POSITION_FORWARD,  0, 1 },
-    { "last-child",    0, 0,                           POSITION_BACKWARD, 0, 1 },
-    { "only-child",    0, 0,                           POSITION_ONLY,     0, 0 },
-    { "sorted",        1, 0,                           POSITION_SORTED,   0, 0 },
-    { "active",        0, GTK_STATE_FLAG_ACTIVE, },
-    { "prelight",      1, GTK_STATE_FLAG_PRELIGHT, },
-    { "hover",         0, GTK_STATE_FLAG_PRELIGHT, },
-    { "selected",      0, GTK_STATE_FLAG_SELECTED, },
-    { "insensitive",   1, GTK_STATE_FLAG_INSENSITIVE, },
-    { "disabled",      0, GTK_STATE_FLAG_INSENSITIVE, },
-    { "inconsistent",  1, GTK_STATE_FLAG_INCONSISTENT, },
-    { "indeterminate", 0, GTK_STATE_FLAG_INCONSISTENT, },
-    { "focused",       1, GTK_STATE_FLAG_FOCUSED, },
-    { "focus",         0, GTK_STATE_FLAG_FOCUSED, },
-    { "backdrop",      0, GTK_STATE_FLAG_BACKDROP, },
-    { "dir(ltr)",      0, GTK_STATE_FLAG_DIR_LTR, },
-    { "dir(rtl)",      0, GTK_STATE_FLAG_DIR_RTL, },
-    { "link",          0, GTK_STATE_FLAG_LINK, },
-    { "visited",       0, GTK_STATE_FLAG_VISITED, },
-    { "checked",       0, GTK_STATE_FLAG_CHECKED, },
-    { "drop(active)",  0, GTK_STATE_FLAG_DROP_ACTIVE, }
-
-  };
   guint i;
 
   if (_gtk_css_parser_try (parser, "nth-child", FALSE))
@@ -1305,6 +1307,539 @@ _gtk_css_selector_parse (GtkCssParser *parser)
   return selector;
 }
 
+GtkCssSelector *
+token_parse_selector_class (GtkCssTokenSource *source,
+                            GtkCssSelector    *selector,
+                            gboolean           negate)
+{
+  const GtkCssToken *token;
+
+  gtk_css_token_source_consume_token (source);
+  token = gtk_css_token_source_get_token (source);
+
+  if (gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT))
+    {
+      selector = gtk_css_selector_new (negate ? &GTK_CSS_SELECTOR_NOT_CLASS
+                                              : &GTK_CSS_SELECTOR_CLASS,
+                                       selector);
+      selector->style_class.style_class = g_quark_from_string (token->string.string);
+      gtk_css_token_source_consume_token (source);
+      return selector;
+    }
+  else
+    {
+      gtk_css_token_source_error (source, "No class name after '.' in selector");
+      if (selector)
+        _gtk_css_selector_free (selector);
+      return NULL;
+    }
+}
+
+static gboolean
+parse_plus_b (GtkCssTokenSource *source,
+              gboolean           negate,
+              gint              *b)
+{
+  const GtkCssToken *token;
+  gboolean has_seen_sign;
+
+  gtk_css_token_source_consume_whitespace (source);
+  token = gtk_css_token_source_get_token (source);
+
+  if (negate)
+    {
+      has_seen_sign = TRUE;
+    }
+  else
+    {
+      if (gtk_css_token_is_delim (token, '+'))
+        {
+          gtk_css_token_source_consume_token (source);
+          gtk_css_token_source_consume_whitespace (source);
+          has_seen_sign = TRUE;
+        }
+      else if (gtk_css_token_is_delim (token, '-'))
+        {
+          gtk_css_token_source_consume_token (source);
+          gtk_css_token_source_consume_whitespace (source);
+          negate = TRUE;
+          has_seen_sign = TRUE;
+        }
+    }
+
+  token = gtk_css_token_source_get_token (source);
+  if (gtk_css_token_is (token, GTK_CSS_TOKEN_INTEGER))
+    {
+      if (has_seen_sign && token->number.number >= 0)
+        {
+          *b = token->number.number;
+          if (negate)
+            *b = - *b;
+          return TRUE;
+        }
+    }
+  
+  gtk_css_token_source_error (source, "Not a valid an+b type");
+  return FALSE;
+}
+
+gboolean
+parse_a_n_plus_b (GtkCssTokenSource *source,
+                  gint              *a,
+                  gint              *b)
+{
+  const GtkCssToken *token;
+
+  token = gtk_css_token_source_get_token (source);
+
+  if (gtk_css_token_is_ident (token, "even"))
+    {
+      *a = 2;
+      *b = 0;
+      gtk_css_token_source_consume_token (source);
+      return TRUE;
+    }
+  else if (gtk_css_token_is_ident (token, "odd"))
+    {
+      *a = 2;
+      *b = 1;
+      gtk_css_token_source_consume_token (source);
+      return TRUE;
+    }
+  else if (gtk_css_token_is (token, GTK_CSS_TOKEN_INTEGER))
+    {
+      *a = 0;
+      *b = token->number.number;
+      gtk_css_token_source_consume_token (source);
+      return TRUE;
+    }
+  else if (gtk_css_token_is (token, GTK_CSS_TOKEN_INTEGER_DIMENSION) &&
+           g_ascii_strcasecmp (token->dimension.dimension, "n") == 0)
+    {
+      *a = token->dimension.value;
+      gtk_css_token_source_consume_token (source);
+      return parse_plus_b (source, FALSE, b);
+    }
+  else if (gtk_css_token_is (token, GTK_CSS_TOKEN_INTEGER_DIMENSION) &&
+           g_ascii_strcasecmp (token->dimension.dimension, "n-") == 0)
+    {
+      *a = token->dimension.value;
+      gtk_css_token_source_consume_token (source);
+      return parse_plus_b (source, TRUE, b);
+    }
+  else if (gtk_css_token_is (token, GTK_CSS_TOKEN_INTEGER_DIMENSION) &&
+           g_ascii_strncasecmp (token->dimension.dimension, "n-", 2) == 0)
+    {
+      char *end;
+      *a = token->dimension.value;
+      errno = 0;
+      *b = strtoul (token->dimension.dimension + 2, &end, 10);
+      if (*end == '\0' && errno == 0)
+        {
+          gtk_css_token_source_consume_token (source);
+          return TRUE;
+        }
+    }
+  else if (gtk_css_token_is_ident (token, "-n"))
+    {
+      *a = -1;
+      gtk_css_token_source_consume_token (source);
+      return parse_plus_b (source, FALSE, b);
+    }
+  else if (gtk_css_token_is_ident (token, "-n-"))
+    {
+      *a = -1;
+      gtk_css_token_source_consume_token (source);
+      return parse_plus_b (source, TRUE, b);
+    }
+  
+  gtk_css_token_source_error (source, "Not a valid an+b type");
+  return FALSE;
+}
+GtkCssSelector *
+token_parse_selector_pseudo_class (GtkCssTokenSource *source,
+                                   GtkCssSelector    *selector,
+                                   gboolean           negate)
+{
+  const GtkCssToken *token;
+
+  gtk_css_token_source_consume_token (source);
+  token = gtk_css_token_source_get_token (source);
+
+  if (gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT))
+    {
+      guint i;
+
+      for (i = 0; i < G_N_ELEMENTS (pseudo_classes); i++)
+        {
+          if (g_ascii_strcasecmp (pseudo_classes[i].name, token->string.string) == 0)
+            {
+              if (pseudo_classes[i].state_flag)
+                {
+                  selector = gtk_css_selector_new (negate ? &GTK_CSS_SELECTOR_NOT_PSEUDOCLASS_STATE
+                                                          : &GTK_CSS_SELECTOR_PSEUDOCLASS_STATE,
+                                                   selector);
+                  selector->state.state = pseudo_classes[i].state_flag;
+                  if (pseudo_classes[i].deprecated)
+                    {
+                      if (pseudo_classes[i + 1].state_flag == pseudo_classes[i].state_flag)
+                        gtk_css_token_source_deprecated (source,
+                                                         "The :%s pseudo-class is deprecated. Use :%s 
instead.",
+                                                         pseudo_classes[i].name,
+                                                         pseudo_classes[i + 1].name);
+                      else
+                        gtk_css_token_source_deprecated (source,
+                                                         "The :%s pseudo-class is deprecated.",
+                                                         pseudo_classes[i].name);
+                    }
+                }
+              else
+                {
+                  selector = gtk_css_selector_new (negate ? &GTK_CSS_SELECTOR_NOT_PSEUDOCLASS_POSITION
+                                                          : &GTK_CSS_SELECTOR_PSEUDOCLASS_POSITION,
+                                                   selector);
+                  selector->position.type = pseudo_classes[i].position_type;
+                  selector->position.a = pseudo_classes[i].position_a;
+                  selector->position.b = pseudo_classes[i].position_b;
+                }
+              gtk_css_token_source_consume_token (source);
+              return selector;
+            }
+        }
+          
+      gtk_css_token_source_unknown (source, "Unknown name of pseudo-class");
+      if (selector)
+        _gtk_css_selector_free (selector);
+      return NULL;
+    }
+  else if (gtk_css_token_is (token, GTK_CSS_TOKEN_FUNCTION))
+    {
+      gint a, b;
+
+      if (gtk_css_token_is_function (token, "nth-child"))
+        {
+          gtk_css_token_source_consume_token (source);
+          if (parse_a_n_plus_b (source, &a, &b))
+            {
+              selector = gtk_css_selector_new (negate ? &GTK_CSS_SELECTOR_NOT_PSEUDOCLASS_POSITION
+                                                      : &GTK_CSS_SELECTOR_PSEUDOCLASS_POSITION,
+                                               selector);
+              selector->position.type = POSITION_FORWARD;
+              selector->position.a = a;
+              selector->position.b = b;
+            }
+          else
+            {
+              if (selector)
+                _gtk_css_selector_free (selector);
+              return NULL;
+            }
+          gtk_css_token_source_consume_whitespace (source);
+          token = gtk_css_token_source_get_token (source);
+          if (!gtk_css_token_is (token, GTK_CSS_TOKEN_CLOSE_PARENS))
+            {
+              gtk_css_token_source_error (source, "Expected ')' at end of :nth-child()");
+              if (selector)
+                _gtk_css_selector_free (selector);
+              return NULL;
+            }
+          gtk_css_token_source_consume_token (source);
+        }
+      else if (gtk_css_token_is_function (token, "nth-last-child"))
+        {
+          gtk_css_token_source_consume_token (source);
+          if (parse_a_n_plus_b (source, &a, &b))
+            {
+              selector = gtk_css_selector_new (negate ? &GTK_CSS_SELECTOR_NOT_PSEUDOCLASS_POSITION
+                                                      : &GTK_CSS_SELECTOR_PSEUDOCLASS_POSITION,
+                                               selector);
+              selector->position.type = POSITION_BACKWARD;
+              selector->position.a = a;
+              selector->position.b = b;
+            }
+          else
+            {
+              if (selector)
+                _gtk_css_selector_free (selector);
+              return NULL;
+            }
+          gtk_css_token_source_consume_whitespace (source);
+          token = gtk_css_token_source_get_token (source);
+          if (!gtk_css_token_is (token, GTK_CSS_TOKEN_CLOSE_PARENS))
+            {
+              gtk_css_token_source_error (source, "Expected ')' at end of :nth-last-child()");
+              if (selector)
+                _gtk_css_selector_free (selector);
+              return NULL;
+            }
+          gtk_css_token_source_consume_token (source);
+        }
+      else if (gtk_css_token_is_function (token, "not"))
+        {
+          if (negate)
+            {
+              gtk_css_token_source_error (source, "Nesting of :not() not allowed");
+              if (selector)
+                _gtk_css_selector_free (selector);
+              return NULL;
+            }
+          else
+            {
+              gtk_css_token_source_consume_token (source);
+              token = gtk_css_token_source_get_token (source);
+
+              if (gtk_css_token_is_delim (token, '*'))
+                {
+                  selector = gtk_css_selector_new (&GTK_CSS_SELECTOR_ANY, selector);
+                  gtk_css_token_source_consume_token (source);
+                }
+              else if (gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT))
+                {
+                  selector = gtk_css_selector_new (&GTK_CSS_SELECTOR_NOT_NAME, selector);
+                  selector->name.name = g_intern_string (token->string.string);
+                  gtk_css_token_source_consume_token (source);
+                }
+              else if (gtk_css_token_is (token, GTK_CSS_TOKEN_HASH_ID))
+                {
+                  selector = gtk_css_selector_new (&GTK_CSS_SELECTOR_NOT_ID, selector);
+                  selector->id.name = g_intern_string (token->string.string);
+                  gtk_css_token_source_consume_token (source);
+                }
+              else if (gtk_css_token_is_delim (token, '.'))
+                {
+                  selector = token_parse_selector_class (source, selector, TRUE);
+                }
+              else if (gtk_css_token_is (token, GTK_CSS_TOKEN_COLON))
+                {
+                  selector = token_parse_selector_pseudo_class (source, selector, TRUE);
+                }
+              else
+                {
+                  gtk_css_token_source_error (source, "Invalid contents of :not() selector");
+                  if (selector)
+                    _gtk_css_selector_free (selector);
+                  selector = NULL;
+                  return NULL;
+                }
+
+              gtk_css_token_source_consume_whitespace (source);
+              token = gtk_css_token_source_get_token (source);
+              if (gtk_css_token_is (token, GTK_CSS_TOKEN_CLOSE_PARENS))
+                {
+                  gtk_css_token_source_consume_token (source);
+                }
+              else
+                {
+                  gtk_css_token_source_error (source, "Invalid contents of :not() selector");
+                  if (selector)
+                    _gtk_css_selector_free (selector);
+                  selector = NULL;
+                  return NULL;
+                }
+            }
+        }
+      else if (gtk_css_token_is_function (token, "dir"))
+        {
+          gtk_css_token_source_consume_token (source);
+          gtk_css_token_source_consume_whitespace (source);
+          token = gtk_css_token_source_get_token (source);
+          if (gtk_css_token_is_ident (token, "ltr"))
+            {
+              selector = gtk_css_selector_new (negate ? &GTK_CSS_SELECTOR_NOT_PSEUDOCLASS_STATE
+                                                      : &GTK_CSS_SELECTOR_PSEUDOCLASS_STATE,
+                                               selector);
+              selector->state.state = GTK_STATE_FLAG_DIR_LTR;
+              gtk_css_token_source_consume_token (source);
+            }
+          else if (gtk_css_token_is_ident (token, "rtl"))
+            {
+              selector = gtk_css_selector_new (negate ? &GTK_CSS_SELECTOR_NOT_PSEUDOCLASS_STATE
+                                                      : &GTK_CSS_SELECTOR_PSEUDOCLASS_STATE,
+                                               selector);
+              selector->state.state = GTK_STATE_FLAG_DIR_LTR;
+              gtk_css_token_source_consume_token (source);
+            }
+          else
+            {
+              gtk_css_token_source_error (source, "Expected :dir(ltr) or :dir(rtl)");
+              if (selector)
+                _gtk_css_selector_free (selector);
+              selector = NULL;
+              return NULL;
+            }
+          gtk_css_token_source_consume_whitespace (source);
+          token = gtk_css_token_source_get_token (source);
+          if (!gtk_css_token_is (token, GTK_CSS_TOKEN_CLOSE_PARENS))
+            {
+              gtk_css_token_source_error (source, "Expected ')' at end of :dir()");
+              if (selector)
+                _gtk_css_selector_free (selector);
+              return NULL;
+            }
+          gtk_css_token_source_consume_token (source);
+        }
+      else if (gtk_css_token_is_function (token, "drop"))
+        {
+          gtk_css_token_source_consume_token (source);
+          gtk_css_token_source_consume_whitespace (source);
+          token = gtk_css_token_source_get_token (source);
+          if (gtk_css_token_is_ident (token, "active"))
+            {
+              selector = gtk_css_selector_new (negate ? &GTK_CSS_SELECTOR_NOT_PSEUDOCLASS_STATE
+                                                      : &GTK_CSS_SELECTOR_PSEUDOCLASS_STATE,
+                                               selector);
+              selector->state.state = GTK_STATE_FLAG_DROP_ACTIVE;
+              gtk_css_token_source_consume_token (source);
+            }
+          else
+            {
+              gtk_css_token_source_error (source, "Expected :drop(active)");
+              if (selector)
+                _gtk_css_selector_free (selector);
+              selector = NULL;
+              return NULL;
+            }
+          gtk_css_token_source_consume_whitespace (source);
+          token = gtk_css_token_source_get_token (source);
+          if (!gtk_css_token_is (token, GTK_CSS_TOKEN_CLOSE_PARENS))
+            {
+              gtk_css_token_source_error (source, "Expected ')' at end of :drop()");
+              if (selector)
+                _gtk_css_selector_free (selector);
+              return NULL;
+            }
+          gtk_css_token_source_consume_token (source);
+        }
+      else
+        {
+          gtk_css_token_source_unknown (source, "Unknown pseudoclass");
+          if (selector)
+            _gtk_css_selector_free (selector);
+          return NULL;
+        }
+    }
+  else
+    {
+      gtk_css_token_source_error (source, "Unknown pseudoclass");
+      if (selector)
+        _gtk_css_selector_free (selector);
+      return NULL;
+    }
+
+  return selector;
+}
+
+GtkCssSelector *
+token_parse_simple_selector (GtkCssTokenSource *source,
+                             GtkCssSelector    *selector)
+{
+  gboolean parsed_something = FALSE;
+  const GtkCssToken *token;
+
+  do {
+      token = gtk_css_token_source_get_token (source);
+
+      if (!parsed_something && gtk_css_token_is_delim (token, '*'))
+        {
+          selector = gtk_css_selector_new (&GTK_CSS_SELECTOR_ANY, selector);
+          gtk_css_token_source_consume_token (source);
+        }
+      else if (!parsed_something && gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT))
+        {
+          selector = gtk_css_selector_new (&GTK_CSS_SELECTOR_NAME, selector);
+          selector->name.name = g_intern_string (token->string.string);
+          gtk_css_token_source_consume_token (source);
+        }
+      else if (gtk_css_token_is (token, GTK_CSS_TOKEN_HASH_ID))
+        {
+          selector = gtk_css_selector_new (&GTK_CSS_SELECTOR_ID, selector);
+          selector->id.name = g_intern_string (token->string.string);
+          gtk_css_token_source_consume_token (source);
+        }
+      else if (gtk_css_token_is_delim (token, '.'))
+        {
+          selector = token_parse_selector_class (source, selector, FALSE);
+        }
+      else if (gtk_css_token_is (token, GTK_CSS_TOKEN_COLON))
+        {
+          selector = token_parse_selector_pseudo_class (source, selector, FALSE);
+        }
+      else
+        {
+          if (!parsed_something)
+            {
+              gtk_css_token_source_error (source, "Expected a valid selector");
+              if (selector)
+                _gtk_css_selector_free (selector);
+              selector = NULL;
+            }
+          break;
+        }
+
+      parsed_something = TRUE;
+    }
+  while (TRUE);
+
+  return selector;
+}
+
+GtkCssSelector *
+gtk_css_selector_token_parse (GtkCssTokenSource *source)
+{
+  GtkCssSelector *selector = NULL;
+  const GtkCssToken *token;
+
+  while (TRUE)
+    {
+      gboolean seen_whitespace = FALSE;
+
+      selector = token_parse_simple_selector (source, selector);
+      if (selector == NULL)
+        {
+          gtk_css_token_source_consume_all (source);
+          return NULL;
+        }
+
+      seen_whitespace = gtk_css_token_source_consume_whitespace (source);
+      token = gtk_css_token_source_get_token (source);
+
+      if (gtk_css_token_is_delim (token, '+'))
+        {
+          selector = gtk_css_selector_new (&GTK_CSS_SELECTOR_ADJACENT, selector);
+          gtk_css_token_source_consume_token (source);
+        }
+      else if (gtk_css_token_is_delim (token, '~'))
+        {
+          selector = gtk_css_selector_new (&GTK_CSS_SELECTOR_SIBLING, selector);
+          gtk_css_token_source_consume_token (source);
+        }
+      else if (gtk_css_token_is_delim (token, '>'))
+        {
+          selector = gtk_css_selector_new (&GTK_CSS_SELECTOR_CHILD, selector);
+          gtk_css_token_source_consume_token (source);
+        }
+      else if (gtk_css_token_is (token, GTK_CSS_TOKEN_EOF))
+        {
+          break;
+        }
+      else if (seen_whitespace)
+        {
+          selector = gtk_css_selector_new (&GTK_CSS_SELECTOR_DESCENDANT, selector);
+        }
+      else
+        {
+           gtk_css_token_source_error (source, "Expected a valid selector");
+           _gtk_css_selector_free (selector);
+           gtk_css_token_source_consume_all (source);
+          return NULL;
+        }
+
+      gtk_css_token_source_consume_whitespace (source);
+    }
+
+  return selector;
+}
+
 void
 _gtk_css_selector_free (GtkCssSelector *selector)
 {
diff --git a/gtk/gtkcssselectorprivate.h b/gtk/gtkcssselectorprivate.h
index 64eb13d..55945ad 100644
--- a/gtk/gtkcssselectorprivate.h
+++ b/gtk/gtkcssselectorprivate.h
@@ -20,6 +20,7 @@
 
 #include "gtk/gtkcssmatcherprivate.h"
 #include "gtk/gtkcssparserprivate.h"
+#include "gtk/gtkcsstokensourceprivate.h"
 
 G_BEGIN_DECLS
 
@@ -28,6 +29,7 @@ typedef struct _GtkCssSelectorTree GtkCssSelectorTree;
 typedef struct _GtkCssSelectorTreeBuilder GtkCssSelectorTreeBuilder;
 
 GtkCssSelector *  _gtk_css_selector_parse           (GtkCssParser           *parser);
+GtkCssSelector *  gtk_css_selector_token_parse      (GtkCssTokenSource      *parser);
 void              _gtk_css_selector_free            (GtkCssSelector         *selector);
 
 char *            _gtk_css_selector_to_string       (const GtkCssSelector   *selector);
diff --git a/gtk/gtkcssstyledeclaration.c b/gtk/gtkcssstyledeclaration.c
new file mode 100644
index 0000000..a543a2c
--- /dev/null
+++ b/gtk/gtkcssstyledeclaration.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright © 2016 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#include "config.h"
+
+#include "gtkcssstyledeclarationprivate.h"
+
+#include "gtkcssselectorprivate.h"
+#include "gtkcssstylesheetprivate.h"
+
+typedef struct _GtkCssStyleDeclarationPrivate GtkCssStyleDeclarationPrivate;
+struct _GtkCssStyleDeclarationPrivate {
+  GPtrArray *declarations;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GtkCssStyleDeclaration, gtk_css_style_declaration, G_TYPE_OBJECT)
+
+static void
+gtk_css_style_declaration_finalize (GObject *object)
+{
+  GtkCssStyleDeclaration *style_declaration = GTK_CSS_STYLE_DECLARATION (object);
+  GtkCssStyleDeclarationPrivate *priv = gtk_css_style_declaration_get_instance_private (style_declaration);
+
+  g_ptr_array_unref (priv->declarations);
+
+  G_OBJECT_CLASS (gtk_css_style_declaration_parent_class)->finalize (object);
+}
+
+static void
+gtk_css_style_declaration_class_init (GtkCssStyleDeclarationClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gtk_css_style_declaration_finalize;
+}
+
+static void
+gtk_css_style_declaration_init (GtkCssStyleDeclaration *style_declaration)
+{
+  GtkCssStyleDeclarationPrivate *priv = gtk_css_style_declaration_get_instance_private (style_declaration);
+
+  priv->declarations = g_ptr_array_new_with_free_func (g_object_unref);
+}
+
+GtkCssStyleDeclaration *
+gtk_css_style_declaration_new (GtkCssRule *parent_rule)
+{
+  return g_object_new (GTK_TYPE_CSS_STYLE_DECLARATION, NULL);
+}
+
diff --git a/gtk/gtkcssstyledeclarationprivate.h b/gtk/gtkcssstyledeclarationprivate.h
new file mode 100644
index 0000000..14ca8e0
--- /dev/null
+++ b/gtk/gtkcssstyledeclarationprivate.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright © 2016 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#ifndef __GTK_CSS_STYLE_DECLARATION_PRIVATE_H__
+#define __GTK_CSS_STYLE_DECLARATION_PRIVATE_H__
+
+#include "gtk/gtkcssruleprivate.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CSS_STYLE_DECLARATION           (gtk_css_style_declaration_get_type ())
+#define GTK_CSS_STYLE_DECLARATION(obj)           (G_TYPE_CHECK_INSTANCE_CAST (obj, 
GTK_TYPE_CSS_STYLE_DECLARATION, GtkCssStyleDeclaration))
+#define GTK_CSS_STYLE_DECLARATION_CLASS(cls)     (G_TYPE_CHECK_CLASS_CAST (cls, 
GTK_TYPE_CSS_STYLE_DECLARATION, GtkCssStyleDeclarationClass))
+#define GTK_IS_CSS_STYLE_DECLARATION(obj)        (G_TYPE_CHECK_INSTANCE_TYPE (obj, 
GTK_TYPE_CSS_STYLE_DECLARATION))
+#define GTK_IS_CSS_STYLE_DECLARATION_CLASS(obj)  (G_TYPE_CHECK_CLASS_TYPE (obj, 
GTK_TYPE_CSS_STYLE_DECLARATION))
+#define GTK_CSS_STYLE_DECLARATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GTK_TYPE_CSS_STYLE_DECLARATION, GtkCssStyleDeclarationClass))
+
+typedef struct _GtkCssStyleDeclaration           GtkCssStyleDeclaration;
+typedef struct _GtkCssStyleDeclarationClass      GtkCssStyleDeclarationClass;
+
+struct _GtkCssStyleDeclaration
+{
+  GObject parent;
+};
+
+struct _GtkCssStyleDeclarationClass
+{
+  GObjectClass parent_class;
+};
+
+GType                   gtk_css_style_declaration_get_type              (void) G_GNUC_CONST;
+
+GtkCssStyleDeclaration *gtk_css_style_declaration_new                   (GtkCssRule             
*parent_rule);
+
+void                    gtk_css_style_declaration_print_css_text        (GtkCssStyleDeclaration *declaration,
+                                                                         GString                *string);
+char *                  gtk_css_style_declaration_get_css_text          (GtkCssStyleDeclaration 
*declaration);
+
+/* GtkCssStyleDeclaration DOM */
+gsize                   gtk_css_style_declaration_get_length            (GtkCssStyleDeclaration 
*declaration);
+
+
+G_END_DECLS
+
+#endif /* __GTK_CSS_STYLE_DECLARATION_PRIVATE_H__ */
diff --git a/gtk/gtkcssstylerule.c b/gtk/gtkcssstylerule.c
new file mode 100644
index 0000000..74df7fa
--- /dev/null
+++ b/gtk/gtkcssstylerule.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright © 2016 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#include "config.h"
+
+#include "gtkcssstyleruleprivate.h"
+
+#include "gtkcssselectorprivate.h"
+#include "gtkcssstylesheetprivate.h"
+
+typedef struct _GtkCssStyleRulePrivate GtkCssStyleRulePrivate;
+struct _GtkCssStyleRulePrivate {
+  GPtrArray *selectors;
+  //GtkCssStyleDeclaration *declaration;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GtkCssStyleRule, gtk_css_style_rule, GTK_TYPE_CSS_RULE)
+
+static void
+gtk_css_style_rule_finalize (GObject *object)
+{
+  GtkCssStyleRule *style_rule = GTK_CSS_STYLE_RULE (object);
+  GtkCssStyleRulePrivate *priv = gtk_css_style_rule_get_instance_private (style_rule);
+
+  g_ptr_array_unref (priv->selectors);
+
+  G_OBJECT_CLASS (gtk_css_style_rule_parent_class)->finalize (object);
+}
+
+static void
+gtk_css_style_rule_class_init (GtkCssStyleRuleClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gtk_css_style_rule_finalize;
+}
+
+static void
+gtk_css_style_rule_init (GtkCssStyleRule *style_rule)
+{
+  GtkCssStyleRulePrivate *priv = gtk_css_style_rule_get_instance_private (style_rule);
+
+  priv->selectors = g_ptr_array_new_with_free_func ((GDestroyNotify) _gtk_css_selector_free);
+}
+
+static GtkCssRule *
+gtk_css_style_rule_new (GtkCssRule       *parent_rule,
+                        GtkCssStyleSheet *parent_style_sheet)
+{
+  return g_object_new (GTK_TYPE_CSS_STYLE_RULE, NULL);
+}
+
+static GtkCssStyleRule *
+gtk_css_style_rule_parse_selectors (GtkCssStyleRule   *rule,
+                                    GtkCssTokenSource *source)
+{
+  GtkCssStyleRulePrivate *priv = gtk_css_style_rule_get_instance_private (rule);
+  GtkCssTokenSource *child_source;
+  GtkCssSelector *selector;
+  const GtkCssToken *token;
+
+  for (token = gtk_css_token_source_get_token (source);
+       !gtk_css_token_is (token, GTK_CSS_TOKEN_EOF);
+       token = gtk_css_token_source_get_token (source))
+    {
+      gtk_css_token_source_consume_whitespace (source);
+      child_source = gtk_css_token_source_new_for_part (source, GTK_CSS_TOKEN_COMMA);
+      selector = gtk_css_selector_token_parse (child_source);
+      gtk_css_token_source_unref (child_source);
+      if (selector == NULL)
+        {
+          g_object_unref (rule);
+          return NULL;
+        }
+      g_ptr_array_add (priv->selectors, selector);
+      gtk_css_token_source_consume_token (source);
+    }
+
+  return rule;
+}
+
+GtkCssRule *
+gtk_css_style_rule_new_parse (GtkCssTokenSource *source,
+                              GtkCssRule        *parent_rule,
+                              GtkCssStyleSheet  *parent_style_sheet)
+{
+  GtkCssTokenSource *child_source;
+  GtkCssStyleRule *rule;
+
+  g_return_val_if_fail (source != NULL, NULL);
+  g_return_val_if_fail (parent_rule == NULL || GTK_IS_CSS_RULE (parent_rule), NULL);
+  g_return_val_if_fail (GTK_IS_CSS_STYLE_SHEET (parent_style_sheet), NULL);
+
+  rule = GTK_CSS_STYLE_RULE (gtk_css_style_rule_new (parent_rule, parent_style_sheet));
+
+  child_source = gtk_css_token_source_new_for_part (source, GTK_CSS_TOKEN_OPEN_CURLY);
+  rule = gtk_css_style_rule_parse_selectors (rule, child_source);
+  gtk_css_token_source_unref (child_source);
+
+  gtk_css_token_source_consume_token (source);
+  child_source = gtk_css_token_source_new_for_part (source, GTK_CSS_TOKEN_CLOSE_CURLY);
+  gtk_css_token_source_consume_all (child_source);
+  gtk_css_token_source_unref (child_source);
+  gtk_css_token_source_consume_token (source);
+
+  return GTK_CSS_RULE (rule);
+}
+
diff --git a/gtk/gtkcssstyleruleprivate.h b/gtk/gtkcssstyleruleprivate.h
new file mode 100644
index 0000000..ef5b936
--- /dev/null
+++ b/gtk/gtkcssstyleruleprivate.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright © 2016 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#ifndef __GTK_CSS_STYLE_RULE_PRIVATE_H__
+#define __GTK_CSS_STYLE_RULE_PRIVATE_H__
+
+#include "gtk/gtkcssruleprivate.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CSS_STYLE_RULE           (gtk_css_style_rule_get_type ())
+#define GTK_CSS_STYLE_RULE(obj)           (G_TYPE_CHECK_INSTANCE_CAST (obj, GTK_TYPE_CSS_STYLE_RULE, 
GtkCssStyleRule))
+#define GTK_CSS_STYLE_RULE_CLASS(cls)     (G_TYPE_CHECK_CLASS_CAST (cls, GTK_TYPE_CSS_STYLE_RULE, 
GtkCssStyleRuleClass))
+#define GTK_IS_CSS_STYLE_RULE(obj)        (G_TYPE_CHECK_INSTANCE_TYPE (obj, GTK_TYPE_CSS_STYLE_RULE))
+#define GTK_IS_CSS_STYLE_RULE_CLASS(obj)  (G_TYPE_CHECK_CLASS_TYPE (obj, GTK_TYPE_CSS_STYLE_RULE))
+#define GTK_CSS_STYLE_RULE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CSS_STYLE_RULE, 
GtkCssStyleRuleClass))
+
+typedef struct _GtkCssStyleRule           GtkCssStyleRule;
+typedef struct _GtkCssStyleRuleClass      GtkCssStyleRuleClass;
+
+struct _GtkCssStyleRule
+{
+  GtkCssRule parent;
+};
+
+struct _GtkCssStyleRuleClass
+{
+  GtkCssRuleClass parent_class;
+};
+
+GType                  gtk_css_style_rule_get_type         (void) G_GNUC_CONST;
+
+GtkCssRule *           gtk_css_style_rule_new_parse        (GtkCssTokenSource           *source,
+                                                            GtkCssRule                  *parent_rule,
+                                                            GtkCssStyleSheet            *parent_style_sheet);
+
+
+G_END_DECLS
+
+#endif /* __GTK_CSS_STYLE_RULE_PRIVATE_H__ */
diff --git a/gtk/gtkcssstylesheet.c b/gtk/gtkcssstylesheet.c
new file mode 100644
index 0000000..03a027b
--- /dev/null
+++ b/gtk/gtkcssstylesheet.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright © 2016 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#include "config.h"
+
+#include "gtkcssstylesheetprivate.h"
+
+typedef struct _GtkCssStyleSheetPrivate GtkCssStyleSheetPrivate;
+struct _GtkCssStyleSheetPrivate {
+  GtkCssRule *parent_rule;
+  GtkCssRuleList *css_rules;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GtkCssStyleSheet, gtk_css_style_sheet, G_TYPE_OBJECT)
+
+static void
+gtk_css_style_sheet_finalize (GObject *object)
+{
+  GtkCssStyleSheet *style_sheet = GTK_CSS_STYLE_SHEET (object);
+  GtkCssStyleSheetPrivate *priv = gtk_css_style_sheet_get_instance_private (style_sheet);
+
+  g_object_unref (priv->css_rules);
+
+  G_OBJECT_CLASS (gtk_css_style_sheet_parent_class)->finalize (object);
+}
+
+static void
+gtk_css_style_sheet_class_init (GtkCssStyleSheetClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gtk_css_style_sheet_finalize;
+}
+
+static void
+gtk_css_style_sheet_init (GtkCssStyleSheet *style_sheet)
+{
+  GtkCssStyleSheetPrivate *priv = gtk_css_style_sheet_get_instance_private (style_sheet);
+
+  priv->css_rules = gtk_css_rule_list_new ();
+}
+
+GtkCssStyleSheet *
+gtk_css_style_sheet_new (void)
+{
+  return g_object_new (GTK_TYPE_CSS_STYLE_SHEET, NULL);
+}
+
+void
+gtk_css_style_sheet_parse (GtkCssStyleSheet  *style_sheet,
+                           GtkCssTokenSource *source)
+{
+  GtkCssStyleSheetPrivate *priv;
+
+  g_return_if_fail (GTK_IS_CSS_STYLE_SHEET (style_sheet));
+  g_return_if_fail (source != NULL);
+
+  priv = gtk_css_style_sheet_get_instance_private (style_sheet);
+
+  gtk_css_rule_list_parse (priv->css_rules, source, NULL, style_sheet);
+}
+
+GtkCssStyleSheet *
+gtk_css_style_sheet_get_parent_style_sheet (GtkCssStyleSheet *style_sheet)
+{
+  GtkCssStyleSheetPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_CSS_STYLE_SHEET (style_sheet), NULL);
+
+  priv = gtk_css_style_sheet_get_instance_private (style_sheet);
+
+  return gtk_css_rule_get_parent_style_sheet (priv->parent_rule);
+}
+
+GtkCssRule *
+gtk_css_style_sheet_get_parent_rule (GtkCssStyleSheet *style_sheet)
+{
+  GtkCssStyleSheetPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_CSS_STYLE_SHEET (style_sheet), NULL);
+
+  priv = gtk_css_style_sheet_get_instance_private (style_sheet);
+
+  return priv->parent_rule;
+}
+
+GtkCssRuleList *
+gtk_css_style_sheet_get_css_rules (GtkCssStyleSheet *style_sheet)
+{
+  GtkCssStyleSheetPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_CSS_STYLE_SHEET (style_sheet), NULL);
+
+  priv = gtk_css_style_sheet_get_instance_private (style_sheet);
+
+  return priv->css_rules;
+}
+
diff --git a/gtk/gtkcssstylesheetprivate.h b/gtk/gtkcssstylesheetprivate.h
new file mode 100644
index 0000000..f2d9132
--- /dev/null
+++ b/gtk/gtkcssstylesheetprivate.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2016 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#ifndef __GTK_CSS_STYLE_SHEET_PRIVATE_H__
+#define __GTK_CSS_STYLE_SHEET_PRIVATE_H__
+
+#include "gtk/gtkcssruleprivate.h"
+#include "gtk/gtkcssrulelistprivate.h"
+#include "gtk/gtkcsstokensourceprivate.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CSS_STYLE_SHEET           (gtk_css_style_sheet_get_type ())
+#define GTK_CSS_STYLE_SHEET(obj)           (G_TYPE_CHECK_INSTANCE_CAST (obj, GTK_TYPE_CSS_STYLE_SHEET, 
GtkCssStyleSheet))
+#define GTK_CSS_STYLE_SHEET_CLASS(cls)     (G_TYPE_CHECK_CLASS_CAST (cls, GTK_TYPE_CSS_STYLE_SHEET, 
GtkCssStyleSheetClass))
+#define GTK_IS_CSS_STYLE_SHEET(obj)        (G_TYPE_CHECK_INSTANCE_TYPE (obj, GTK_TYPE_CSS_STYLE_SHEET))
+#define GTK_IS_CSS_STYLE_SHEET_CLASS(obj)  (G_TYPE_CHECK_CLASS_TYPE (obj, GTK_TYPE_CSS_STYLE_SHEET))
+#define GTK_CSS_STYLE_SHEET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CSS_STYLE_SHEET, 
GtkCssStyleSheetClass))
+
+/* declared with GtkCssRule */
+/* typedef struct _GtkCssStyleSheet           GtkCssStyleSheet; */
+typedef struct _GtkCssStyleSheetClass      GtkCssStyleSheetClass;
+
+struct _GtkCssStyleSheet
+{
+  GObject parent;
+};
+
+struct _GtkCssStyleSheetClass
+{
+  GObjectClass parent_class;
+};
+
+GType                  gtk_css_style_sheet_get_type                     (void) G_GNUC_CONST;
+
+GtkCssStyleSheet *     gtk_css_style_sheet_new                          (void);
+
+void                   gtk_css_style_sheet_parse                        (GtkCssStyleSheet       *style_sheet,
+                                                                         GtkCssTokenSource      *source);
+
+/* StyleSheet interface */
+GtkCssStyleSheet *     gtk_css_style_sheet_get_parent_style_sheet       (GtkCssStyleSheet       
*style_sheet);
+
+/* CSSStyleSheet interface */
+GtkCssRule *           gtk_css_style_sheet_get_parent_rule              (GtkCssStyleSheet       
*style_sheet);
+GtkCssRuleList *       gtk_css_style_sheet_get_css_rules                (GtkCssStyleSheet       
*style_sheet);
+
+
+G_END_DECLS
+
+#endif /* __GTK_CSS_STYLE_SHEET_PRIVATE_H__ */
diff --git a/gtk/gtkcsstokensource.c b/gtk/gtkcsstokensource.c
new file mode 100644
index 0000000..069cf9b
--- /dev/null
+++ b/gtk/gtkcsstokensource.c
@@ -0,0 +1,369 @@
+/*
+ * Copyright © 2016 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#include "config.h"
+
+#include "gtkcsstokensourceprivate.h"
+
+#include "gtkcssprovider.h"
+
+typedef struct _GtkCssTokenSourceTokenizer GtkCssTokenSourceTokenizer;
+struct _GtkCssTokenSourceTokenizer {
+  GtkCssTokenSource parent;
+  GtkCssTokenizer *tokenizer;
+  GtkCssToken current_token;
+};
+
+static void
+gtk_css_token_source_tokenizer_finalize (GtkCssTokenSource *source)
+{
+  GtkCssTokenSourceTokenizer *tok = (GtkCssTokenSourceTokenizer *) source;
+
+  gtk_css_token_clear (&tok->current_token);
+  gtk_css_tokenizer_unref (tok->tokenizer);
+}
+
+static void
+gtk_css_token_source_tokenizer_consume_token (GtkCssTokenSource *source)
+{
+  GtkCssTokenSourceTokenizer *tok = (GtkCssTokenSourceTokenizer *) source;
+
+  gtk_css_token_clear (&tok->current_token);
+}
+
+static const GtkCssToken *
+gtk_css_token_source_tokenizer_get_token (GtkCssTokenSource   *source)
+{
+  GtkCssTokenSourceTokenizer *tok = (GtkCssTokenSourceTokenizer *) source;
+
+  if (gtk_css_token_is (&tok->current_token, GTK_CSS_TOKEN_EOF))
+    {
+      do {
+       gtk_css_tokenizer_read_token (tok->tokenizer, &tok->current_token);
+      } while (gtk_css_token_is (&tok->current_token, GTK_CSS_TOKEN_COMMENT));
+
+#if 0
+      if (!gtk_css_token_is (&tok->current_token, GTK_CSS_TOKEN_EOF)) {
+        char *s = gtk_css_token_to_string (&tok->current_token);
+        g_print ("%3zu:%02zu %2d %s\n",
+                 gtk_css_tokenizer_get_line (tok->tokenizer), gtk_css_tokenizer_get_line_char 
(tok->tokenizer),
+                 tok->current_token.type, s);
+        g_free (s);
+      }
+#endif
+    }
+
+  return &tok->current_token;
+}
+
+static void
+gtk_css_token_source_tokenizer_error (GtkCssTokenSource *source,
+                                      const GError      *error)
+{
+  GtkCssTokenSourceTokenizer *tok = (GtkCssTokenSourceTokenizer *) source;
+
+  /* XXX */
+  g_print ("ERROR: %zu:%zu: %s\n",
+           gtk_css_tokenizer_get_line (tok->tokenizer),
+           gtk_css_tokenizer_get_line_char (tok->tokenizer),
+           error->message);
+}
+
+const GtkCssTokenSourceClass GTK_CSS_TOKEN_SOURCE_TOKENIZER = {
+  gtk_css_token_source_tokenizer_finalize,
+  gtk_css_token_source_tokenizer_consume_token,
+  gtk_css_token_source_tokenizer_get_token,
+  gtk_css_token_source_tokenizer_error,
+};
+
+GtkCssTokenSource *
+gtk_css_token_source_new_for_tokenizer (GtkCssTokenizer *tokenizer)
+{
+  GtkCssTokenSourceTokenizer *source;
+
+  g_return_val_if_fail (tokenizer != NULL, NULL);
+
+  source = gtk_css_token_source_new (GtkCssTokenSourceTokenizer, &GTK_CSS_TOKEN_SOURCE_TOKENIZER);
+  source->tokenizer = gtk_css_tokenizer_ref (tokenizer);
+
+  return &source->parent;
+}
+
+typedef struct _GtkCssTokenSourcePart GtkCssTokenSourcePart;
+struct _GtkCssTokenSourcePart {
+  GtkCssTokenSource parent;
+  GtkCssTokenSource *source;
+  GtkCssTokenType end_type;
+};
+
+static void
+gtk_css_token_source_part_finalize (GtkCssTokenSource *source)
+{
+  GtkCssTokenSourcePart *part = (GtkCssTokenSourcePart *) source;
+
+  gtk_css_token_source_unref (part->source);
+}
+
+static void
+gtk_css_token_source_part_consume_token (GtkCssTokenSource *source)
+{
+  GtkCssTokenSourcePart *part = (GtkCssTokenSourcePart *) source;
+  const GtkCssToken *token;
+
+  if (!gtk_css_token_get_pending_block (source))
+    {
+      token = gtk_css_token_source_get_token (part->source);
+      if (gtk_css_token_is (token, part->end_type))
+        return;
+    }
+
+  gtk_css_token_source_consume_token (part->source);
+}
+
+static const GtkCssToken *
+gtk_css_token_source_part_get_token (GtkCssTokenSource *source)
+{
+  GtkCssTokenSourcePart *part = (GtkCssTokenSourcePart *) source;
+  static const GtkCssToken eof_token = { GTK_CSS_TOKEN_EOF };
+  const GtkCssToken *token;
+
+  token = gtk_css_token_source_get_token (part->source);
+  if (!gtk_css_token_get_pending_block (source) &&
+      gtk_css_token_is (token, part->end_type))
+    return &eof_token;
+
+  return token;
+}
+
+static void
+gtk_css_token_source_part_error (GtkCssTokenSource *source,
+                                 const GError      *error)
+{
+  GtkCssTokenSourcePart *part = (GtkCssTokenSourcePart *) source;
+
+  gtk_css_token_source_emit_error (part->source, error);
+}
+
+const GtkCssTokenSourceClass GTK_CSS_TOKEN_SOURCE_PART = {
+  gtk_css_token_source_part_finalize,
+  gtk_css_token_source_part_consume_token,
+  gtk_css_token_source_part_get_token,
+  gtk_css_token_source_part_error,
+};
+
+GtkCssTokenSource *
+gtk_css_token_source_new_for_part (GtkCssTokenSource *source,
+                                   GtkCssTokenType    end_type)
+{
+  GtkCssTokenSourcePart *part;
+
+  g_return_val_if_fail (source != NULL, NULL);
+  g_return_val_if_fail (end_type != GTK_CSS_TOKEN_EOF, NULL);
+
+  part = gtk_css_token_source_new (GtkCssTokenSourcePart, &GTK_CSS_TOKEN_SOURCE_PART);
+  part->source = gtk_css_token_source_ref (source);
+  part->end_type = end_type;
+
+  return &part->parent;
+}
+
+GtkCssTokenSource *
+gtk_css_token_source_alloc (gsize                         struct_size,
+                            const GtkCssTokenSourceClass *klass)
+{
+  GtkCssTokenSource *source;
+
+  source = g_malloc0 (struct_size);
+  source->klass = klass;
+  source->ref_count = 1;
+
+  return source;
+}
+
+GtkCssTokenSource *
+gtk_css_token_source_ref (GtkCssTokenSource *source)
+{
+  source->ref_count++;
+
+  return source;
+}
+
+void
+gtk_css_token_source_unref (GtkCssTokenSource *source)
+{
+  source->ref_count--;
+  if (source->ref_count > 0)
+    return;
+
+  source->klass->finalize (source);
+
+  g_free (source);
+}
+
+
+void
+gtk_css_token_source_consume_token (GtkCssTokenSource *source)
+{
+  gtk_css_token_source_consume_token_as (source, source->consumer);
+}
+
+void
+gtk_css_token_source_consume_token_as (GtkCssTokenSource *source,
+                                       GObject           *consumer)
+{
+  const GtkCssToken *token;
+  
+  if (source->blocks)
+    {
+      token = gtk_css_token_source_get_token (source);
+      if (gtk_css_token_is (token, GPOINTER_TO_UINT (source->blocks->data)))
+        source->blocks = g_slist_remove (source->blocks, source->blocks->data);
+    }
+
+  source->klass->consume_token (source);
+
+  token = gtk_css_token_source_get_token (source);
+  switch (token->type)
+    {
+    case GTK_CSS_TOKEN_FUNCTION:
+    case GTK_CSS_TOKEN_OPEN_PARENS:
+      source->blocks = g_slist_prepend (source->blocks, GUINT_TO_POINTER (GTK_CSS_TOKEN_CLOSE_PARENS));
+      break;
+    case GTK_CSS_TOKEN_OPEN_SQUARE:
+      source->blocks = g_slist_prepend (source->blocks, GUINT_TO_POINTER (GTK_CSS_TOKEN_CLOSE_SQUARE));
+      break;
+    case GTK_CSS_TOKEN_OPEN_CURLY:
+      source->blocks = g_slist_prepend (source->blocks, GUINT_TO_POINTER (GTK_CSS_TOKEN_CLOSE_CURLY));
+      break;
+    default:
+      break;
+    }
+
+  source->klass->consume_token (source, consumer);
+
+  if (source->blocks)
+    {
+      if (token_type == GPOINTER_TO_UINT (source->blocks->data))
+        source->blocks = g_slist_remove (source->blocks, source->blocks->data);
+    }
+}
+
+const GtkCssToken *
+gtk_css_token_source_get_token (GtkCssTokenSource *source)
+{
+  return source->klass->get_token (source);
+}
+
+void
+gtk_css_token_source_consume_all (GtkCssTokenSource *source)
+{
+  const GtkCssToken *token;
+
+  for (token = gtk_css_token_source_get_token (source);
+       !gtk_css_token_is (token, GTK_CSS_TOKEN_EOF);
+       token = gtk_css_token_source_get_token (source))
+    {
+      gtk_css_token_source_consume_token (source);
+    }
+}
+
+gboolean
+gtk_css_token_source_consume_whitespace (GtkCssTokenSource *source)
+{
+  const GtkCssToken *token;
+  gboolean seen_whitespace = FALSE;
+
+  for (token = gtk_css_token_source_get_token (source);
+       gtk_css_token_is (token, GTK_CSS_TOKEN_WHITESPACE);
+       token = gtk_css_token_source_get_token (source))
+    {
+      seen_whitespace = TRUE;
+      gtk_css_token_source_consume_token (source);
+    }
+
+  return seen_whitespace;
+}
+
+GtkCssTokenType
+gtk_css_token_get_pending_block (GtkCssTokenSource *source)
+{
+  if (!source->blocks)
+    return GTK_CSS_TOKEN_EOF;
+
+  return GPOINTER_TO_UINT(source->blocks->data);
+}
+
+void
+gtk_css_token_source_emit_error (GtkCssTokenSource *source,
+                                 const GError      *error)
+{
+  source->klass->error (source, error);
+}
+
+void
+gtk_css_token_source_error (GtkCssTokenSource *source,
+                            const char        *format,
+                            ...)
+{
+  va_list args;
+  GError *error;
+
+  va_start (args, format);
+  error = g_error_new_valist (GTK_CSS_PROVIDER_ERROR,
+                              GTK_CSS_PROVIDER_ERROR_SYNTAX,
+                              format, args);
+  gtk_css_token_source_emit_error (source, error);
+  g_error_free (error);
+  va_end (args);
+}
+
+void
+gtk_css_token_source_unknown (GtkCssTokenSource *source,
+                              const char        *format,
+                              ...)
+{
+  va_list args;
+  GError *error;
+
+  va_start (args, format);
+  error = g_error_new_valist (GTK_CSS_PROVIDER_ERROR,
+                              GTK_CSS_PROVIDER_ERROR_UNKNOWN_VALUE,
+                              format, args);
+  gtk_css_token_source_emit_error (source, error);
+  g_error_free (error);
+  va_end (args);
+}
+
+void
+gtk_css_token_source_deprecated (GtkCssTokenSource *source,
+                                 const char        *format,
+                                 ...)
+{
+  va_list args;
+  GError *error;
+
+  va_start (args, format);
+  error = g_error_new_valist (GTK_CSS_PROVIDER_ERROR,
+                              GTK_CSS_PROVIDER_ERROR_DEPRECATED,
+                              format, args);
+  gtk_css_token_source_emit_error (source, error);
+  g_error_free (error);
+  va_end (args);
+}
+
diff --git a/gtk/gtkcsstokensourceprivate.h b/gtk/gtkcsstokensourceprivate.h
new file mode 100644
index 0000000..7316c19
--- /dev/null
+++ b/gtk/gtkcsstokensourceprivate.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright © 2016 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#ifndef __GTK_CSS_TOKEN_SOURCE_PRIVATE_H__
+#define __GTK_CSS_TOKEN_SOURCE_PRIVATE_H__
+
+#include <glib-object.h>
+#include "gtk/gtkcsstokenizerprivate.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GtkCssTokenSource GtkCssTokenSource;
+typedef struct _GtkCssTokenSourceClass GtkCssTokenSourceClass;
+
+struct _GtkCssTokenSource
+{
+  const GtkCssTokenSourceClass *klass;
+  gint ref_count;
+  GSList *blocks; /* of GPOINTER_TO_UINT(GtkCssTokenType) */
+};
+
+struct _GtkCssTokenSourceClass
+{
+  void                  (* finalize)                            (GtkCssTokenSource      *source);
+  void                  (* consume_token)                       (GtkCssTokenSource      *source);
+  const GtkCssToken *   (* get_token)                           (GtkCssTokenSource      *source);
+  void                  (* error)                               (GtkCssTokenSource      *source,
+                                                                 const GError           *error);
+};
+
+GtkCssTokenSource *     gtk_css_token_source_new_for_tokenizer  (GtkCssTokenizer        *tokenizer);
+GtkCssTokenSource *     gtk_css_token_source_new_for_part       (GtkCssTokenSource      *source,
+                                                                 GtkCssTokenType         end_type);
+
+GtkCssTokenSource *     gtk_css_token_source_ref                (GtkCssTokenSource      *source);
+void                    gtk_css_token_source_unref              (GtkCssTokenSource      *source);
+
+#define gtk_css_token_source_new(type,klass) ((type *) gtk_css_token_source_alloc (sizeof (type), (klass)))
+GtkCssTokenSource *     gtk_css_token_source_alloc              (gsize                   struct_size,
+                                                                 const GtkCssTokenSourceClass *klass);
+
+void                    gtk_css_token_source_consume_token      (GtkCssTokenSource      *source);
+const GtkCssToken *     gtk_css_token_source_get_token          (GtkCssTokenSource      *source);
+
+GtkCssTokenType         gtk_css_token_get_pending_block         (GtkCssTokenSource      *source);
+
+void                    gtk_css_token_source_consume_all        (GtkCssTokenSource      *source);
+gboolean                gtk_css_token_source_consume_whitespace (GtkCssTokenSource      *source);
+
+void                    gtk_css_token_source_emit_error         (GtkCssTokenSource      *source,
+                                                                 const GError           *error);
+void                    gtk_css_token_source_error              (GtkCssTokenSource      *source,
+                                                                 const char             *format,
+                                                                 ...) G_GNUC_PRINTF(2, 3);
+void                    gtk_css_token_source_unknown            (GtkCssTokenSource      *source,
+                                                                 const char             *format,
+                                                                 ...) G_GNUC_PRINTF(2, 3);
+void                    gtk_css_token_source_deprecated         (GtkCssTokenSource      *source,
+                                                                 const char             *format,
+                                                                 ...) G_GNUC_PRINTF(2, 3);
+
+
+G_END_DECLS
+
+#endif /* __GTK_CSS_TOKEN_SOURCE_PRIVATE_H__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]