[gtk+] css: Make property parsing functions take a css parser



commit d2ef71627b5021108086143c0afaefb9ee196a7d
Author: Benjamin Otte <otte redhat com>
Date:   Wed May 18 18:32:22 2011 +0200

    css: Make property parsing functions take a css parser
    
    Instead of reading a string and then passing that in, let the parse
    functions use the full power of the parser.

 gtk/gtkcssparser.c             |   13 +-
 gtk/gtkcssparserprivate.h      |    2 +
 gtk/gtkcssprovider.c           |  112 ++++--
 gtk/gtkcssstringfuncs.c        |  914 ++++++++++++++--------------------------
 gtk/gtkcssstringfuncsprivate.h |   15 +-
 5 files changed, 410 insertions(+), 646 deletions(-)
---
diff --git a/gtk/gtkcssparser.c b/gtk/gtkcssparser.c
index de798e7..c956fd5 100644
--- a/gtk/gtkcssparser.c
+++ b/gtk/gtkcssparser.c
@@ -108,6 +108,15 @@ _gtk_css_parser_get_position (GtkCssParser *parser)
 }
 
 void
+_gtk_css_parser_take_error (GtkCssParser *parser,
+                            GError       *error)
+{
+  parser->error_func (parser, error, parser->user_data);
+
+  g_error_free (error);
+}
+
+void
 _gtk_css_parser_error (GtkCssParser *parser,
                        const char   *format,
                        ...)
@@ -122,9 +131,7 @@ _gtk_css_parser_error (GtkCssParser *parser,
                               format, args);
   va_end (args);
 
-  parser->error_func (parser, error, parser->user_data);
-
-  g_error_free (error);
+  _gtk_css_parser_take_error (parser, error);
 }
 
 static gboolean
diff --git a/gtk/gtkcssparserprivate.h b/gtk/gtkcssparserprivate.h
index 609b9f5..976edc0 100644
--- a/gtk/gtkcssparserprivate.h
+++ b/gtk/gtkcssparserprivate.h
@@ -35,6 +35,8 @@ GtkCssParser *  _gtk_css_parser_new               (const char            *data,
                                                    gpointer               user_data);
 void            _gtk_css_parser_free              (GtkCssParser          *parser);
 
+void            _gtk_css_parser_take_error        (GtkCssParser          *parser,
+                                                   GError                *error);
 void            _gtk_css_parser_error             (GtkCssParser          *parser,
                                                    const char            *format,
                                                     ...) G_GNUC_PRINTF (2, 3);
diff --git a/gtk/gtkcssprovider.c b/gtk/gtkcssprovider.c
index 62a7ba3..ab65804 100644
--- a/gtk/gtkcssprovider.c
+++ b/gtk/gtkcssprovider.c
@@ -1187,6 +1187,20 @@ gtk_css_provider_get_style (GtkStyleProvider *provider,
   return props;
 }
 
+static void
+gtk_css_provider_parser_error (GtkCssParser *parser,
+                               const GError *error,
+                               gpointer      user_data)
+{
+  GtkCssProvider *provider = user_data;
+
+  gtk_css_provider_take_error_full (provider,
+                                    NULL,
+                                    _gtk_css_parser_get_line (parser),
+                                    _gtk_css_parser_get_position (parser),
+                                    g_error_copy (error));
+}
+
 static gboolean
 gtk_css_provider_get_style_property (GtkStyleProvider *provider,
                                      GtkWidgetPath    *path,
@@ -1227,21 +1241,20 @@ gtk_css_provider_get_style_property (GtkStyleProvider *provider,
            ((selector_state & state) != 0 &&
             (selector_state & ~(state)) == 0)))
         {
-          GError *error = NULL;
+          GtkCssParser *parser;
+
+          parser = _gtk_css_parser_new (g_value_get_string (val),
+                                        gtk_css_provider_parser_error,
+                                        provider);
+
+          found = _gtk_css_value_parse (value,
+                                        parser,
+                                        NULL);
 
-          found = _gtk_css_value_from_string (value,
-                                              NULL,
-                                              g_value_get_string (val),
-                                              &error);
+          _gtk_css_parser_free (parser);
 
           if (found)
             break;
-          
-          /* error location should be _way_ better */
-          gtk_css_provider_take_error_full (GTK_CSS_PROVIDER (provider),
-                                            NULL,
-                                            0, 0,
-                                            error);
         }
     }
 
@@ -1902,7 +1915,7 @@ parse_declaration (GtkCssScanner *scanner,
 {
   GtkStylePropertyParser parse_func = NULL;
   GParamSpec *pspec = NULL;
-  char *name, *value_str;
+  char *name;
 
   name = _gtk_css_parser_try_ident (scanner->parser, TRUE);
   if (name == NULL)
@@ -1930,14 +1943,6 @@ parse_declaration (GtkCssScanner *scanner,
       return;
     }
 
-  value_str = _gtk_css_parser_read_value (scanner->parser);
-  if (value_str == NULL)
-    {
-      _gtk_css_parser_resync (scanner->parser, TRUE, '}');
-      g_free (name);
-      return;
-    }
-  
   if (pspec)
     {
       GValue *val;
@@ -1947,7 +1952,7 @@ parse_declaration (GtkCssScanner *scanner,
       val = g_slice_new0 (GValue);
       g_value_init (val, pspec->value_type);
 
-      if (strcmp (value_str, "none") == 0)
+      if (_gtk_css_parser_try (scanner->parser, "none", TRUE))
         {
           /* Insert the default value, so it has an opportunity
            * to override other style providers when merged
@@ -1958,47 +1963,80 @@ parse_declaration (GtkCssScanner *scanner,
       else if (parse_func)
         {
           GError *error = NULL;
+          char *value_str;
+          
+          value_str = _gtk_css_parser_read_value (scanner->parser);
+          if (value_str == NULL)
+            {
+              _gtk_css_parser_resync (scanner->parser, TRUE, '}');
+              return;
+            }
           
           if ((*parse_func) (value_str, val, &error))
             gtk_css_ruleset_add (ruleset, pspec, val);
           else
             gtk_css_provider_take_error (scanner->provider, scanner, error);
+
+          g_free (value_str);
         }
       else
         {
-          GError *error = NULL;
-          
-          if (_gtk_css_value_from_string (val,
-                                          gtk_css_scanner_get_base_url (scanner),
-                                          value_str,
-                                          &error))
+          if (_gtk_css_value_parse (val,
+                                    scanner->parser,
+                                    gtk_css_scanner_get_base_url (scanner)))
             {
-              gtk_css_ruleset_add (ruleset, pspec, val);
+              if (_gtk_css_parser_begins_with (scanner->parser, ';') ||
+                  _gtk_css_parser_begins_with (scanner->parser, '}') ||
+                  _gtk_css_parser_is_eof (scanner->parser))
+                {
+                  gtk_css_ruleset_add (ruleset, pspec, val);
+                }
+              else
+                {
+                  gtk_css_provider_error_literal (scanner->provider,
+                                                  scanner,
+                                                  GTK_CSS_PROVIDER_ERROR,
+                                                  GTK_CSS_PROVIDER_ERROR_SYNTAX,
+                                                  "Junk at end of value");
+                  _gtk_css_parser_resync (scanner->parser, TRUE, '}');
+                  g_value_unset (val);
+                  g_slice_free (GValue, val);
+                  return;
+                }
             }
           else
             {
               g_value_unset (val);
               g_slice_free (GValue, val);
-
-              gtk_css_provider_take_error (scanner->provider, scanner, error);
+              _gtk_css_parser_resync (scanner->parser, TRUE, '}');
+              return;
             }
         }
     }
   else if (name[0] == '-')
     {
-      GValue *val;
+      char *value_str;
 
-      val = g_slice_new0 (GValue);
-      g_value_init (val, G_TYPE_STRING);
-      g_value_set_string (val, value_str);
+      value_str = _gtk_css_parser_read_value (scanner->parser);
+      if (value_str)
+        {
+          GValue *val;
+
+          val = g_slice_new0 (GValue);
+          g_value_init (val, G_TYPE_STRING);
+          g_value_take_string (val, value_str);
 
-      gtk_css_ruleset_add_style (ruleset, name, val);
+          gtk_css_ruleset_add_style (ruleset, name, val);
+        }
+      else
+        {
+          _gtk_css_parser_resync (scanner->parser, TRUE, '}');
+          return;
+        }
     }
   else
     g_free (name);
 
-  g_free (value_str);
-
 check_for_semicolon:
   if (!_gtk_css_parser_try (scanner->parser, ";", TRUE))
     {
diff --git a/gtk/gtkcssstringfuncs.c b/gtk/gtkcssstringfuncs.c
index bb70bb9..b8eb913 100644
--- a/gtk/gtkcssstringfuncs.c
+++ b/gtk/gtkcssstringfuncs.c
@@ -38,84 +38,36 @@
 #include "gtkgradient.h"
 #include "gtkthemingengine.h"
 
-typedef gboolean (* FromStringFunc)   (const char    *str,
+typedef gboolean (* ParseFunc)        (GtkCssParser  *parser,
                                        GFile         *base,
-                                       GValue        *value,
-                                       GError       **error);
+                                       GValue        *value);
 typedef char *   (* ToStringFunc)     (const GValue  *value);
 
-static GHashTable *from_string_funcs = NULL;
+static GHashTable *parse_funcs = NULL;
 static GHashTable *to_string_funcs = NULL;
 
 static void
 register_conversion_function (GType          type,
-                              FromStringFunc from_string,
+                              ParseFunc      parse,
                               ToStringFunc   to_string)
 {
-  if (from_string)
-    g_hash_table_insert (from_string_funcs, GSIZE_TO_POINTER (type), from_string);
+  if (parse)
+    g_hash_table_insert (parse_funcs, GSIZE_TO_POINTER (type), parse);
   if (to_string)
     g_hash_table_insert (to_string_funcs, GSIZE_TO_POINTER (type), to_string);
 }
 
-static gboolean
-set_default_error (GError **error,
-                   GType    type)
-{
-  g_set_error (error,
-               GTK_CSS_PROVIDER_ERROR,
-               GTK_CSS_PROVIDER_ERROR_SYNTAX,
-               "Could not convert property value to type '%s'",
-               g_type_name (type));
-  return FALSE;
-}
-
 /*** IMPLEMENTATIONS ***/
 
-#define SKIP_SPACES(s) while (g_ascii_isspace (*(s))) (s)++
-#define SKIP_SPACES_BACK(s) while (g_ascii_isspace (*(s))) (s)--
-
-static void
-propagate_parser_error (GtkCssParser *parser,
-                        const GError *error,
-                        gpointer      user_data)
-{
-  GError **propagate_here = user_data;
-
-  if (propagate_here == NULL)
-    return;
-
-  /* only copy the first error */
-  if (*propagate_here == NULL)
-    *propagate_here = g_error_copy (error);
-}
-
-static GtkSymbolicColor *
-_gtk_css_parse_symbolic_color (const char  *str,
-                               GError     **error)
-{
-  GtkSymbolicColor *symbolic;
-  GtkCssParser *parser;
-
-  parser = _gtk_css_parser_new (str,
-                                propagate_parser_error,
-                                error);
-  symbolic = _gtk_css_parser_read_symbolic_color (parser);
-  _gtk_css_parser_free (parser);
-
-  return symbolic;
-}
-
 static gboolean 
-rgba_value_from_string (const char  *str,
-                        GFile       *base,
-                        GValue      *value,
-                        GError     **error)
+rgba_value_parse (GtkCssParser *parser,
+                  GFile        *base,
+                  GValue       *value)
 {
   GtkSymbolicColor *symbolic;
   GdkRGBA rgba;
 
-  symbolic = _gtk_css_parse_symbolic_color (str, error);
+  symbolic = _gtk_css_parser_read_symbolic_color (parser);
   if (symbolic == NULL)
     return FALSE;
 
@@ -145,15 +97,14 @@ rgba_value_to_string (const GValue *value)
 }
 
 static gboolean 
-color_value_from_string (const char  *str,
-                         GFile       *base,
-                         GValue      *value,
-                         GError     **error)
+color_value_parse (GtkCssParser *parser,
+                   GFile        *base,
+                   GValue       *value)
 {
   GtkSymbolicColor *symbolic;
   GdkRGBA rgba;
 
-  symbolic = _gtk_css_parse_symbolic_color (str, error);
+  symbolic = _gtk_css_parser_read_symbolic_color (parser);
   if (symbolic == NULL)
     return FALSE;
 
@@ -188,15 +139,14 @@ color_value_to_string (const GValue *value)
   return gdk_color_to_string (color);
 }
 
-static gboolean 
-symbolic_color_value_from_string (const char  *str,
-                                  GFile       *base,
-                                  GValue      *value,
-                                  GError     **error)
+static gboolean
+symbolic_color_value_parse (GtkCssParser *parser,
+                            GFile        *base,
+                            GValue       *value)
 {
   GtkSymbolicColor *symbolic;
 
-  symbolic = _gtk_css_parse_symbolic_color (str, error);
+  symbolic = _gtk_css_parser_read_symbolic_color (parser);
   if (symbolic == NULL)
     return FALSE;
 
@@ -216,14 +166,19 @@ symbolic_color_value_to_string (const GValue *value)
 }
 
 static gboolean 
-font_description_value_from_string (const char  *str,
-                                    GFile       *base,
-                                    GValue      *value,
-                                    GError     **error)
+font_description_value_parse (GtkCssParser *parser,
+                              GFile        *base,
+                              GValue       *value)
 {
   PangoFontDescription *font_desc;
+  char *str;
+
+  str = _gtk_css_parser_read_value (parser);
+  if (str == NULL)
+    return FALSE;
 
   font_desc = pango_font_description_from_string (str);
+  g_free (str);
   g_value_take_boxed (value, font_desc);
   return TRUE;
 }
@@ -240,25 +195,27 @@ font_description_value_to_string (const GValue *value)
 }
 
 static gboolean 
-boolean_value_from_string (const char  *str,
-                           GFile       *base,
-                           GValue      *value,
-                           GError     **error)
+boolean_value_parse (GtkCssParser *parser,
+                     GFile        *base,
+                     GValue       *value)
 {
-  if (g_ascii_strcasecmp (str, "true") == 0 ||
-      g_ascii_strcasecmp (str, "1") == 0)
+  if (_gtk_css_parser_try (parser, "true", TRUE) ||
+      _gtk_css_parser_try (parser, "1", TRUE))
     {
       g_value_set_boolean (value, TRUE);
       return TRUE;
     }
-  else if (g_ascii_strcasecmp (str, "false") == 0 ||
-           g_ascii_strcasecmp (str, "0") == 0)
+  else if (_gtk_css_parser_try (parser, "false", TRUE) ||
+           _gtk_css_parser_try (parser, "0", TRUE))
     {
       g_value_set_boolean (value, FALSE);
       return TRUE;
     }
-
-  return set_default_error (error, G_VALUE_TYPE (value));
+  else
+    {
+      _gtk_css_parser_error (parser, "Expected a boolean value");
+      return FALSE;
+    }
 }
 
 static char *
@@ -271,28 +228,15 @@ boolean_value_to_string (const GValue *value)
 }
 
 static gboolean 
-int_value_from_string (const char  *str,
-                       GFile       *base,
-                       GValue      *value,
-                       GError     **error)
+int_value_parse (GtkCssParser *parser,
+                 GFile        *base,
+                 GValue       *value)
 {
-  gint64 i;
-  char *end;
+  gint i;
 
-  if (*str == '+')
-    return set_default_error (error, G_VALUE_TYPE (value));
-
-  i = g_ascii_strtoll (str, &end, 10);
-
-  if (*end != '\0')
-    return set_default_error (error, G_VALUE_TYPE (value));
-
-  if (i > G_MAXINT || i < G_MININT)
+  if (!_gtk_css_parser_try_int (parser, &i))
     {
-      g_set_error_literal (error,
-                           GTK_CSS_PROVIDER_ERROR,
-                           GTK_CSS_PROVIDER_ERROR_SYNTAX,
-                           "Number too big");
+      _gtk_css_parser_error (parser, "Expected a valid integer value");
       return FALSE;
     }
 
@@ -307,28 +251,15 @@ int_value_to_string (const GValue *value)
 }
 
 static gboolean 
-uint_value_from_string (const char  *str,
-                        GFile       *base,
-                        GValue      *value,
-                        GError     **error)
+uint_value_parse (GtkCssParser *parser,
+                  GFile        *base,
+                  GValue       *value)
 {
-  guint64 u;
-  char *end;
-
-  if (*str == '+')
-    return set_default_error (error, G_VALUE_TYPE (value));
+  guint u;
 
-  u = g_ascii_strtoull (str, &end, 10);
-
-  if (*end != '\0')
-    return set_default_error (error, G_VALUE_TYPE (value));
-
-  if (u > G_MAXUINT)
+  if (!_gtk_css_parser_try_uint (parser, &u))
     {
-      g_set_error_literal (error,
-                           GTK_CSS_PROVIDER_ERROR,
-                           GTK_CSS_PROVIDER_ERROR_SYNTAX,
-                           "Number too big");
+      _gtk_css_parser_error (parser, "Expected a valid unsigned value");
       return FALSE;
     }
 
@@ -343,25 +274,15 @@ uint_value_to_string (const GValue *value)
 }
 
 static gboolean 
-double_value_from_string (const char  *str,
-                          GFile       *base,
-                          GValue      *value,
-                          GError     **error)
+double_value_parse (GtkCssParser *parser,
+                    GFile        *base,
+                    GValue       *value)
 {
-  double d;
-  char *end;
-
-  d = g_ascii_strtod (str, &end);
-
-  if (*end != '\0')
-    return set_default_error (error, G_VALUE_TYPE (value));
+  gdouble d;
 
-  if (errno == ERANGE)
+  if (!_gtk_css_parser_try_double (parser, &d))
     {
-      g_set_error_literal (error,
-                           GTK_CSS_PROVIDER_ERROR,
-                           GTK_CSS_PROVIDER_ERROR_SYNTAX,
-                           "Number not representable");
+      _gtk_css_parser_error (parser, "Expected a number");
       return FALSE;
     }
 
@@ -380,25 +301,15 @@ double_value_to_string (const GValue *value)
 }
 
 static gboolean 
-float_value_from_string (const char  *str,
-                         GFile       *base,
-                         GValue      *value,
-                         GError     **error)
+float_value_parse (GtkCssParser *parser,
+                   GFile        *base,
+                   GValue       *value)
 {
-  double d;
-  char *end;
+  gdouble d;
 
-  d = g_ascii_strtod (str, &end);
-
-  if (*end != '\0')
-    return set_default_error (error, G_VALUE_TYPE (value));
-
-  if (errno == ERANGE)
+  if (!_gtk_css_parser_try_double (parser, &d))
     {
-      g_set_error_literal (error,
-                           GTK_CSS_PROVIDER_ERROR,
-                           GTK_CSS_PROVIDER_ERROR_SYNTAX,
-                           "Number not representable");
+      _gtk_css_parser_error (parser, "Expected a number");
       return FALSE;
     }
 
@@ -416,113 +327,17 @@ float_value_to_string (const GValue *value)
   return g_strdup (buf);
 }
 
-static char *
-gtk_css_string_unescape (const char  *string,
-                         GError     **error)
-{
-  GString *str;
-  char quote;
-  gsize len;
-
-  quote = string[0];
-  string++;
-  if (quote != '\'' && quote != '"')
-    {
-      g_set_error_literal (error,
-                           GTK_CSS_PROVIDER_ERROR,
-                           GTK_CSS_PROVIDER_ERROR_SYNTAX,
-                           "String value not properly quoted.");
-      return NULL;
-    }
-
-  str = g_string_new (NULL);
-
-  while (TRUE)
-    {
-      len = strcspn (string, "\\'\"\n\r\f");
-
-      g_string_append_len (str, string, len);
-
-      string += len;
-
-      switch (string[0])
-        {
-        case '\\':
-          string++;
-          if (string[0] >= '0' && string[0] <= '9' &&
-              string[0] >= 'a' && string[0] <= 'f' &&
-              string[0] >= 'A' && string[0] <= 'F')
-            {
-              g_set_error_literal (error,
-                                   GTK_CSS_PROVIDER_ERROR,
-                                   GTK_CSS_PROVIDER_ERROR_SYNTAX,
-                                   "FIXME: Implement unicode escape sequences.");
-              g_string_free (str, TRUE);
-              return NULL;
-            }
-          else if (string[0] == '\r' && string[1] == '\n')
-            string++;
-          else if (string[0] != '\r' && string[0] != '\n' && string[0] != '\f')
-            g_string_append_c (str, string[0]);
-          break;
-        case '"':
-        case '\'':
-          if (string[0] != quote)
-            {
-              g_string_append_c (str, string[0]);
-            }
-          else
-            {
-              if (string[1] == 0)
-                {
-                  return g_string_free (str, FALSE);
-                }
-              else
-                {
-                  g_set_error_literal (error,
-                                       GTK_CSS_PROVIDER_ERROR,
-                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
-                                       "Junk after end of string.");
-                  g_string_free (str, TRUE);
-                  return NULL;
-                }
-            }
-          break;
-        case '\0':
-          g_set_error_literal (error,
-                               GTK_CSS_PROVIDER_ERROR,
-                               GTK_CSS_PROVIDER_ERROR_SYNTAX,
-                               "Missing end quote in string.");
-          g_string_free (str, TRUE);
-          return NULL;
-        default:
-          g_set_error_literal (error,
-                               GTK_CSS_PROVIDER_ERROR,
-                               GTK_CSS_PROVIDER_ERROR_SYNTAX,
-                               "Invalid character in string. Must be escaped.");
-          g_string_free (str, TRUE);
-          return NULL;
-        }
-
-      string++;
-    }
-
-  g_assert_not_reached ();
-  return NULL;
-}
-
 static gboolean 
-string_value_from_string (const char  *str,
-                          GFile       *base,
-                          GValue      *value,
-                          GError     **error)
+string_value_parse (GtkCssParser *parser,
+                    GFile        *base,
+                    GValue       *value)
 {
-  char *unescaped = gtk_css_string_unescape (str, error);
+  char *str = _gtk_css_parser_read_string (parser);
 
-  if (unescaped == NULL)
+  if (str == NULL)
     return FALSE;
 
-  g_value_take_string (value, unescaped);
+  g_value_take_string (value, str);
   return TRUE;
 }
 
@@ -567,24 +382,30 @@ string_value_to_string (const GValue *value)
 }
 
 static gboolean 
-theming_engine_value_from_string (const char  *str,
-                                  GFile       *base,
-                                  GValue      *value,
-                                  GError     **error)
+theming_engine_value_parse (GtkCssParser *parser,
+                            GFile        *base,
+                            GValue       *value)
 {
   GtkThemingEngine *engine;
+  char *str;
+
+  str = _gtk_css_parser_try_ident (parser, TRUE);
+  if (str == NULL)
+    {
+      _gtk_css_parser_error (parser, "Expected a valid theme name");
+      return FALSE;
+    }
 
   engine = gtk_theming_engine_load (str);
   if (engine == NULL)
     {
-      g_set_error (error,
-                   GTK_CSS_PROVIDER_ERROR,
-                   GTK_CSS_PROVIDER_ERROR_SYNTAX,
-                   "Themeing engine '%s' not found", str);
+      _gtk_css_parser_error (parser, "Themeing engine '%s' not found", str);
+      g_free (str);
       return FALSE;
     }
 
   g_value_set_object (value, engine);
+  g_free (str);
   return TRUE;
 }
 
@@ -605,17 +426,25 @@ theming_engine_value_to_string (const GValue *value)
 }
 
 static gboolean 
-animation_description_value_from_string (const char  *str,
-                                         GFile       *base,
-                                         GValue      *value,
-                                         GError     **error)
+animation_description_value_parse (GtkCssParser *parser,
+                                   GFile        *base,
+                                   GValue       *value)
 {
   GtkAnimationDescription *desc;
+  char *str;
+
+  str = _gtk_css_parser_read_value (parser);
+  if (str == NULL)
+    return FALSE;
 
   desc = _gtk_animation_description_from_string (str);
+  g_free (str);
 
   if (desc == NULL)
-    return set_default_error (error, G_VALUE_TYPE (value));
+    {
+      _gtk_css_parser_error (parser, "Invalid animation description");
+      return FALSE;
+    }
   
   g_value_take_boxed (value, desc);
   return TRUE;
@@ -632,97 +461,44 @@ animation_description_value_to_string (const GValue *value)
   return _gtk_animation_description_to_string (desc);
 }
 
-static gboolean
-parse_border_value (const char  *str,
-                    gint16      *value,
-                    const char **end,
-                    GError     **error)
+static gboolean 
+border_value_parse (GtkCssParser *parser,
+                    GFile        *base,
+                    GValue       *value)
 {
-  gint64 d;
-
-  d = g_ascii_strtoll (str, (char **) end, 10);
+  GtkBorder border = { 0, };
+  guint i, numbers[4];
 
-  if (d > G_MAXINT16 || d < 0)
+  for (i = 0; i < G_N_ELEMENTS (numbers); i++)
     {
-      g_set_error_literal (error,
-                           GTK_CSS_PROVIDER_ERROR,
-                           GTK_CSS_PROVIDER_ERROR_SYNTAX,
-                           "Number out of range for border");
-      return FALSE;
-    }
+      if (!_gtk_css_parser_try_uint (parser, &numbers[i]))
+        break;
 
-  if (str == *end)
-    {
-      g_set_error_literal (error,
-                           GTK_CSS_PROVIDER_ERROR,
-                           GTK_CSS_PROVIDER_ERROR_SYNTAX,
-                           "No number given for border value");
-      return FALSE;
+      /* XXX: shouldn't allow spaces here? */
+      _gtk_css_parser_try (parser, "px", TRUE);
     }
 
-  /* Skip optional unit type.
-   * We only handle pixels at the moment.
-   */
-  if (strncmp (*end, "px", 2) == 0)
-    *end += 2;
-
-  if (**end != '\0' &&
-      !g_ascii_isspace (**end))
+  if (i == 0)
     {
-      g_set_error_literal (error,
-                           GTK_CSS_PROVIDER_ERROR,
-                           GTK_CSS_PROVIDER_ERROR_SYNTAX,
-                           "Junk at end of border value");
+      _gtk_css_parser_error (parser, "Expected valid border");
       return FALSE;
     }
 
-  SKIP_SPACES (*end);
-
-  *value = d;
-  return TRUE;
-}
-
-static gboolean 
-border_value_from_string (const char  *str,
-                          GFile       *base,
-                          GValue      *value,
-                          GError     **error)
-{
-  GtkBorder *border;
-
-  border = gtk_border_new ();
-
-  if (!parse_border_value (str, &border->top, &str, error))
-    return FALSE;
-
-  if (*str == '\0')
-    border->right = border->top;
+  border.top = numbers[0];
+  if (i > 1)
+    border.right = numbers[1];
   else
-    if (!parse_border_value (str, &border->right, &str, error))
-      return FALSE;
-
-  if (*str == '\0')
-    border->bottom = border->top;
+    border.right = border.top;
+  if (i > 2)
+    border.bottom = numbers[2];
   else
-    if (!parse_border_value (str, &border->bottom, &str, error))
-      return FALSE;
-
-  if (*str == '\0')
-    border->left = border->right;
+    border.bottom = border.top;
+  if (i > 3)
+    border.left = numbers[3];
   else
-    if (!parse_border_value (str, &border->left, &str, error))
-      return FALSE;
-
-  if (*str != '\0')
-    {
-      g_set_error_literal (error,
-                           GTK_CSS_PROVIDER_ERROR,
-                           GTK_CSS_PROVIDER_ERROR_SYNTAX,
-                           "Junk at end of border value");
-      return FALSE;
-    }
+    border.left = border.right;
 
-  g_value_take_boxed (value, border);
+  g_value_set_boxed (value, &border);
   return TRUE;
 }
 
@@ -743,8 +519,10 @@ border_value_to_string (const GValue *value)
     return g_strdup_printf ("%d", border->top);
 }
 
-static GtkGradient *
-_gtk_css_parse_gradient (GtkCssParser  *parser)
+static gboolean 
+gradient_value_parse (GtkCssParser *parser,
+                      GFile        *base,
+                      GValue       *value)
 {
   GtkGradient *gradient;
   cairo_pattern_type_t type;
@@ -755,14 +533,14 @@ _gtk_css_parse_gradient (GtkCssParser  *parser)
     {
       _gtk_css_parser_error (parser,
                              "Expected '-gtk-gradient'");
-      return NULL;
+      return FALSE;
     }
 
   if (!_gtk_css_parser_try (parser, "(", TRUE))
     {
       _gtk_css_parser_error (parser,
                              "Expected '(' after '-gtk-gradient'");
-      return NULL;
+      return FALSE;
     }
 
   /* Parse gradient type */
@@ -774,7 +552,7 @@ _gtk_css_parse_gradient (GtkCssParser  *parser)
     {
       _gtk_css_parser_error (parser,
                              "Gradient type must be 'radial' or 'linear'");
-      return NULL;
+      return FALSE;
     }
 
   /* Parse start/stop position parameters */
@@ -784,7 +562,7 @@ _gtk_css_parse_gradient (GtkCssParser  *parser)
         {
           _gtk_css_parser_error (parser,
                                  "Expected ','");
-          return NULL;
+          return FALSE;
         }
 
       if (_gtk_css_parser_try (parser, "left", TRUE))
@@ -797,7 +575,7 @@ _gtk_css_parse_gradient (GtkCssParser  *parser)
         {
           _gtk_css_parser_error (parser,
                                  "Expected a valid X coordinate");
-          return NULL;
+          return FALSE;
         }
 
       if (_gtk_css_parser_try (parser, "top", TRUE))
@@ -810,7 +588,7 @@ _gtk_css_parse_gradient (GtkCssParser  *parser)
         {
           _gtk_css_parser_error (parser,
                                  "Expected a valid Y coordinate");
-          return NULL;
+          return FALSE;
         }
 
       if (type == CAIRO_PATTERN_TYPE_RADIAL)
@@ -820,14 +598,14 @@ _gtk_css_parse_gradient (GtkCssParser  *parser)
             {
               _gtk_css_parser_error (parser,
                                      "Expected ','");
-              return NULL;
+              return FALSE;
             }
 
           if (! _gtk_css_parser_try_double (parser, &coords[(i * 3) + 2]))
             {
               _gtk_css_parser_error (parser,
                                      "Expected a numer for the radius");
-              return NULL;
+              return FALSE;
             }
         }
     }
@@ -852,7 +630,7 @@ _gtk_css_parse_gradient (GtkCssParser  *parser)
               gtk_gradient_unref (gradient);
               _gtk_css_parser_error (parser,
                                      "Expected '('");
-              return NULL;
+              return FALSE;
             }
 
         }
@@ -865,7 +643,7 @@ _gtk_css_parse_gradient (GtkCssParser  *parser)
               gtk_gradient_unref (gradient);
               _gtk_css_parser_error (parser,
                                      "Expected '('");
-              return NULL;
+              return FALSE;
             }
 
         }
@@ -876,7 +654,7 @@ _gtk_css_parse_gradient (GtkCssParser  *parser)
               gtk_gradient_unref (gradient);
               _gtk_css_parser_error (parser,
                                      "Expected '('");
-              return NULL;
+              return FALSE;
             }
 
           if (!_gtk_css_parser_try_double (parser, &position))
@@ -884,7 +662,7 @@ _gtk_css_parse_gradient (GtkCssParser  *parser)
               gtk_gradient_unref (gradient);
               _gtk_css_parser_error (parser,
                                      "Expected a valid number");
-              return NULL;
+              return FALSE;
             }
 
           if (!_gtk_css_parser_try (parser, ",", TRUE))
@@ -892,7 +670,7 @@ _gtk_css_parse_gradient (GtkCssParser  *parser)
               gtk_gradient_unref (gradient);
               _gtk_css_parser_error (parser,
                                      "Expected a comma");
-              return NULL;
+              return FALSE;
             }
         }
       else
@@ -900,14 +678,14 @@ _gtk_css_parse_gradient (GtkCssParser  *parser)
           gtk_gradient_unref (gradient);
           _gtk_css_parser_error (parser,
                                  "Not a valid color-stop definition");
-          return NULL;
+          return FALSE;
         }
 
       color = _gtk_css_parser_read_symbolic_color (parser);
       if (color == NULL)
         {
           gtk_gradient_unref (gradient);
-          return NULL;
+          return FALSE;
         }
 
       gtk_gradient_add_color_stop (gradient, position, color);
@@ -918,7 +696,7 @@ _gtk_css_parse_gradient (GtkCssParser  *parser)
           gtk_gradient_unref (gradient);
           _gtk_css_parser_error (parser,
                                  "Expected ')'");
-          return NULL;
+          return FALSE;
         }
     }
 
@@ -927,30 +705,9 @@ _gtk_css_parse_gradient (GtkCssParser  *parser)
       gtk_gradient_unref (gradient);
       _gtk_css_parser_error (parser,
                              "Expected ')'");
-      return NULL;
+      return FALSE;
     }
 
-  return gradient;
-}
-
-static gboolean 
-gradient_value_from_string (const char  *str,
-                            GFile       *base,
-                            GValue      *value,
-                            GError     **error)
-{
-  GtkGradient *gradient;
-  GtkCssParser *parser;
-
-  parser = _gtk_css_parser_new (str,
-                                propagate_parser_error,
-                                error);
-  gradient = _gtk_css_parse_gradient (parser);
-  _gtk_css_parser_free (parser);
-
-  if (gradient == NULL)
-    return FALSE;
-
   g_value_set_boxed (value, gradient);
   return TRUE;
 }
@@ -967,39 +724,41 @@ gradient_value_to_string (const GValue *value)
 }
 
 static gboolean 
-pattern_value_from_string (const char  *str,
-                           GFile       *base,
-                           GValue      *value,
-                           GError     **error)
+pattern_value_parse (GtkCssParser *parser,
+                     GFile        *base,
+                     GValue       *value)
 {
-  if (g_str_has_prefix (str, "-gtk-gradient"))
+  if (_gtk_css_parser_begins_with (parser, '-'))
     {
       g_value_unset (value);
       g_value_init (value, GTK_TYPE_GRADIENT);
-      return gradient_value_from_string (str, base, value, error);
+      return gradient_value_parse (parser, base, value);
     }
   else
     {
+      GError *error = NULL;
       gchar *path;
       GdkPixbuf *pixbuf;
       GFile *file;
+      cairo_surface_t *surface;
+      cairo_pattern_t *pattern;
+      cairo_t *cr;
+      cairo_matrix_t matrix;
 
-      file = _gtk_css_parse_url (base, str, NULL, error);
+      file = _gtk_css_parse_url (parser, base);
       if (file == NULL)
         return FALSE;
 
       path = g_file_get_path (file);
       g_object_unref (file);
 
-      pixbuf = gdk_pixbuf_new_from_file (path, error);
+      pixbuf = gdk_pixbuf_new_from_file (path, &error);
       g_free (path);
       if (pixbuf == NULL)
-        return FALSE;
-
-      cairo_surface_t *surface;
-      cairo_pattern_t *pattern;
-      cairo_t *cr;
-      cairo_matrix_t matrix;
+        {
+          _gtk_css_parser_take_error (parser, error);
+          return FALSE;
+        }
 
       surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
                                             gdk_pixbuf_get_width (pixbuf),
@@ -1025,10 +784,9 @@ pattern_value_from_string (const char  *str,
 }
 
 static gboolean 
-slice_value_from_string (const char  *str,
-                         GFile       *base,
-                         GValue      *value,
-                         GError     **error)
+slice_value_parse (GtkCssParser *parser,
+                   GFile        *base,
+                   GValue       *value)
 {
   gdouble distance_top, distance_bottom;
   gdouble distance_left, distance_right;
@@ -1036,74 +794,49 @@ slice_value_from_string (const char  *str,
   GdkPixbuf *pixbuf;
   Gtk9Slice *slice;
   GFile *file;
-  gint i = 0;
+  GError *error = NULL;
+  gint i;
   char *path;
 
-  SKIP_SPACES (str);
-
   /* Parse image url */
-  file = _gtk_css_parse_url (base, str, (char **) &str, error);
+  file = _gtk_css_parse_url (parser, base);
   if (!file)
       return FALSE;
 
-  SKIP_SPACES (str);
-
-  /* Parse top/left/bottom/right distances */
-  distance_top = g_ascii_strtod (str, (char **) &str);
-
-  SKIP_SPACES (str);
-
-  distance_right = g_ascii_strtod (str, (char **) &str);
-
-  SKIP_SPACES (str);
-
-  distance_bottom = g_ascii_strtod (str, (char **) &str);
-
-  SKIP_SPACES (str);
-
-  distance_left = g_ascii_strtod (str, (char **) &str);
-
-  SKIP_SPACES (str);
-
-  while (*str && i < 2)
-    {
-      if (g_str_has_prefix (str, "stretch"))
-        {
-          str += strlen ("stretch");
-          mods[i] = GTK_SLICE_STRETCH;
-        }
-      else if (g_str_has_prefix (str, "repeat"))
-        {
-          str += strlen ("repeat");
-          mods[i] = GTK_SLICE_REPEAT;
-        }
-      else
-        {
-          g_object_unref (file);
-          return set_default_error (error, G_VALUE_TYPE (value));
-        }
-
-      SKIP_SPACES (str);
-      i++;
-    }
-
-  if (*str != '\0')
+  if (!_gtk_css_parser_try_double (parser, &distance_top) ||
+      !_gtk_css_parser_try_double (parser, &distance_right) ||
+      !_gtk_css_parser_try_double (parser, &distance_bottom) ||
+      !_gtk_css_parser_try_double (parser, &distance_left))
     {
+      _gtk_css_parser_error (parser, "Expected a number");
       g_object_unref (file);
-      return set_default_error (error, G_VALUE_TYPE (value));
+      return FALSE;
     }
 
-  if (i != 2)
+  for (i = 0; i < 2; i++)
     {
-      /* Fill in second modifier, same as the first */
-      mods[1] = mods[0];
+      if (_gtk_css_parser_try (parser, "stretch", TRUE))
+        mods[i] = GTK_SLICE_STRETCH;
+      else if (_gtk_css_parser_try (parser, "repeat", TRUE))
+        mods[i] = GTK_SLICE_REPEAT;
+      else if (i == 0)
+        {
+          mods[1] = mods[0] = GTK_SLICE_STRETCH;
+          break;
+        }
+      else
+        mods[i] = mods[0];
     }
 
   path = g_file_get_path (file);
-  pixbuf = gdk_pixbuf_new_from_file (path, error);
+  g_object_unref (file);
+  pixbuf = gdk_pixbuf_new_from_file (path, &error);
   g_free (path);
   if (!pixbuf)
-    return FALSE;
+    {
+      _gtk_css_parser_take_error (parser, error);
+      return FALSE;
+    }
 
   slice = _gtk_9slice_new (pixbuf,
                            distance_top, distance_bottom,
@@ -1116,31 +849,35 @@ slice_value_from_string (const char  *str,
 }
 
 static gboolean 
-enum_value_from_string (const char  *str,
-                        GFile       *base,
-                        GValue      *value,
-                        GError     **error)
+enum_value_parse (GtkCssParser *parser,
+                  GFile        *base,
+                  GValue       *value)
 {
   GEnumClass *enum_class;
   GEnumValue *enum_value;
+  char *str;
 
-  enum_class = g_type_class_ref (G_VALUE_TYPE (value));
-  enum_value = g_enum_get_value_by_nick (enum_class, str);
-
-  if (!enum_value)
+  str = _gtk_css_parser_try_ident (parser, TRUE);
+  if (str == NULL)
     {
-      g_set_error (error,
-                   GTK_CSS_PROVIDER_ERROR,
-                   GTK_CSS_PROVIDER_ERROR_SYNTAX,
-                   "Unknown value '%s' for enum type '%s'",
-                   str, g_type_name (G_VALUE_TYPE (value)));
-      g_type_class_unref (enum_class);
+      _gtk_css_parser_error (parser, "Expected an identifier");
       return FALSE;
     }
+
+  enum_class = g_type_class_ref (G_VALUE_TYPE (value));
+  enum_value = g_enum_get_value_by_nick (enum_class, str);
+
+  if (enum_value)
+    g_value_set_enum (value, enum_value->value);
+  else
+    _gtk_css_parser_error (parser,
+                           "Unknown value '%s' for enum type '%s'",
+                           str, g_type_name (G_VALUE_TYPE (value)));
   
-  g_value_set_enum (value, enum_value->value);
   g_type_class_unref (enum_class);
-  return TRUE;
+  g_free (str);
+
+  return enum_value != NULL;
 }
 
 static char *
@@ -1161,41 +898,44 @@ enum_value_to_string (const GValue *value)
 }
 
 static gboolean 
-flags_value_from_string (const char  *str,
-                         GFile       *base,
-                         GValue      *value,
-                         GError     **error)
+flags_value_parse (GtkCssParser *parser,
+                   GFile        *base,
+                   GValue       *value)
 {
   GFlagsClass *flags_class;
   GFlagsValue *flag_value;
   guint flags = 0;
-  char **strv;
-  guint i;
-
-  strv = g_strsplit (str, ",", -1);
+  char *str;
 
   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
 
-  for (i = 0; strv[i]; i++)
-    {
-      strv[i] = g_strstrip (strv[i]);
+  do {
+    str = _gtk_css_parser_try_ident (parser, TRUE);
+    if (str == NULL)
+      {
+        _gtk_css_parser_error (parser, "Expected an identifier");
+        g_type_class_unref (flags_class);
+        return FALSE;
+      }
 
-      flag_value = g_flags_get_value_by_nick (flags_class, strv[i]);
+      flag_value = g_flags_get_value_by_nick (flags_class, str);
       if (!flag_value)
         {
-          g_set_error (error,
-                       GTK_CSS_PROVIDER_ERROR,
-                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
-                       "Unknown flag value '%s' for type '%s'",
-                       strv[i], g_type_name (G_VALUE_TYPE (value)));
+          _gtk_css_parser_error (parser,
+                                 "Unknown flag value '%s' for type '%s'",
+                                 str, g_type_name (G_VALUE_TYPE (value)));
+          /* XXX Do we want to return FALSE here? We can get
+           * forward-compatibility for new values this way
+           */
+          g_free (str);
           g_type_class_unref (flags_class);
           return FALSE;
         }
-      
-      flags |= flag_value->value;
+
+      g_free (str);
     }
+  while (_gtk_css_parser_try (parser, ",", FALSE));
 
-  g_strfreev (strv);
   g_type_class_unref (flags_class);
 
   g_value_set_enum (value, flags);
@@ -1233,31 +973,40 @@ flags_value_to_string (const GValue *value)
 }
 
 static gboolean 
-bindings_value_from_string (const char  *str,
-                            GFile       *base,
-                            GValue      *value,
-                            GError     **error)
+bindings_value_parse (GtkCssParser *parser,
+                      GFile        *base,
+                      GValue       *value)
 {
   GPtrArray *array;
-  gchar **bindings, **name;
+  GtkBindingSet *binding_set;
+  char *name;
 
-  bindings = g_strsplit (str, ",", -1);
   array = g_ptr_array_new ();
 
-  for (name = bindings; *name; name++)
-    {
-      GtkBindingSet *binding_set;
+  do {
+      name = _gtk_css_parser_try_ident (parser, TRUE);
+      if (name == NULL)
+        {
+          _gtk_css_parser_error (parser, "Not a valid binding name");
+          g_ptr_array_free (array, TRUE);
+          return FALSE;
+        }
 
-      binding_set = gtk_binding_set_find (g_strstrip (*name));
+      binding_set = gtk_binding_set_find (name);
 
       if (!binding_set)
-        continue;
+        {
+          _gtk_css_parser_error (parser, "No binding set named '%s'", name);
+          g_free (name);
+          continue;
+        }
 
       g_ptr_array_add (array, binding_set);
+      g_free (name);
     }
+  while (_gtk_css_parser_try (parser, ",", TRUE));
 
   g_value_take_boxed (value, array);
-  g_strfreev (bindings);
 
   return TRUE;
 }
@@ -1289,101 +1038,98 @@ bindings_value_to_string (const GValue *value)
 static void
 css_string_funcs_init (void)
 {
-  if (G_LIKELY (from_string_funcs != NULL))
+  if (G_LIKELY (parse_funcs != NULL))
     return;
 
-  from_string_funcs = g_hash_table_new (NULL, NULL);
+  parse_funcs = g_hash_table_new (NULL, NULL);
   to_string_funcs = g_hash_table_new (NULL, NULL);
 
   register_conversion_function (GDK_TYPE_RGBA,
-                                rgba_value_from_string,
+                                rgba_value_parse,
                                 rgba_value_to_string);
   register_conversion_function (GDK_TYPE_COLOR,
-                                color_value_from_string,
+                                color_value_parse,
                                 color_value_to_string);
   register_conversion_function (GTK_TYPE_SYMBOLIC_COLOR,
-                                symbolic_color_value_from_string,
+                                symbolic_color_value_parse,
                                 symbolic_color_value_to_string);
   register_conversion_function (PANGO_TYPE_FONT_DESCRIPTION,
-                                font_description_value_from_string,
+                                font_description_value_parse,
                                 font_description_value_to_string);
   register_conversion_function (G_TYPE_BOOLEAN,
-                                boolean_value_from_string,
+                                boolean_value_parse,
                                 boolean_value_to_string);
   register_conversion_function (G_TYPE_INT,
-                                int_value_from_string,
+                                int_value_parse,
                                 int_value_to_string);
   register_conversion_function (G_TYPE_UINT,
-                                uint_value_from_string,
+                                uint_value_parse,
                                 uint_value_to_string);
   register_conversion_function (G_TYPE_DOUBLE,
-                                double_value_from_string,
+                                double_value_parse,
                                 double_value_to_string);
   register_conversion_function (G_TYPE_FLOAT,
-                                float_value_from_string,
+                                float_value_parse,
                                 float_value_to_string);
   register_conversion_function (G_TYPE_STRING,
-                                string_value_from_string,
+                                string_value_parse,
                                 string_value_to_string);
   register_conversion_function (GTK_TYPE_THEMING_ENGINE,
-                                theming_engine_value_from_string,
+                                theming_engine_value_parse,
                                 theming_engine_value_to_string);
   register_conversion_function (GTK_TYPE_ANIMATION_DESCRIPTION,
-                                animation_description_value_from_string,
+                                animation_description_value_parse,
                                 animation_description_value_to_string);
   register_conversion_function (GTK_TYPE_BORDER,
-                                border_value_from_string,
+                                border_value_parse,
                                 border_value_to_string);
   register_conversion_function (GTK_TYPE_GRADIENT,
-                                gradient_value_from_string,
+                                gradient_value_parse,
                                 gradient_value_to_string);
   register_conversion_function (CAIRO_GOBJECT_TYPE_PATTERN,
-                                pattern_value_from_string,
+                                pattern_value_parse,
                                 NULL);
   register_conversion_function (GTK_TYPE_9SLICE,
-                                slice_value_from_string,
+                                slice_value_parse,
                                 NULL);
   register_conversion_function (G_TYPE_ENUM,
-                                enum_value_from_string,
+                                enum_value_parse,
                                 enum_value_to_string);
   register_conversion_function (G_TYPE_FLAGS,
-                                flags_value_from_string,
+                                flags_value_parse,
                                 flags_value_to_string);
   register_conversion_function (G_TYPE_PTR_ARRAY,
-                                bindings_value_from_string,
+                                bindings_value_parse,
                                 bindings_value_to_string);
 }
 
 gboolean
-_gtk_css_value_from_string (GValue        *value,
-                            GFile         *base,
-                            const char    *string,
-                            GError       **error)
+_gtk_css_value_parse (GValue       *value,
+                      GtkCssParser *parser,
+                      GFile        *base)
 {
-  FromStringFunc func;
+  ParseFunc func;
 
-  g_return_val_if_fail (string != NULL, FALSE);
-  g_return_val_if_fail (string[0] != 0, FALSE);
+  g_return_val_if_fail (value != NULL, FALSE);
+  g_return_val_if_fail (parser != NULL, FALSE);
 
   css_string_funcs_init ();
 
-  func = g_hash_table_lookup (from_string_funcs,
+  func = g_hash_table_lookup (parse_funcs,
                               GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
   if (func == NULL)
-    func = g_hash_table_lookup (from_string_funcs,
+    func = g_hash_table_lookup (parse_funcs,
                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
 
   if (func == NULL)
     {
-      g_set_error (error,
-                   GTK_CSS_PROVIDER_ERROR,
-                   GTK_CSS_PROVIDER_ERROR_SYNTAX,
-                   "Cannot convert to type '%s'",
-                   g_type_name (G_VALUE_TYPE (value)));
+      _gtk_css_parser_error (parser,
+                             "Cannot convert to type '%s'",
+                             g_type_name (G_VALUE_TYPE (value)));
       return FALSE;
     }
 
-  return (*func) (string, base, value, error);
+  return (*func) (parser, base, value);
 }
 
 char *
@@ -1406,79 +1152,53 @@ _gtk_css_value_to_string (const GValue *value)
 }
 
 GFile *
-_gtk_css_parse_url (GFile       *base,
-                    const char  *str,
-                    char       **end,
-                    GError     **error)
+_gtk_css_parse_url (GtkCssParser *parser,
+                    GFile        *base)
 {
-  gchar *path, *chr;
+  gchar *path;
   GFile *file;
 
-  if (g_str_has_prefix (str, "url"))
+  if (_gtk_css_parser_try (parser, "url", FALSE))
     {
-      str += strlen ("url");
-      SKIP_SPACES (str);
-
-      if (*str != '(')
-        {
-          g_set_error_literal (error,
-                               GTK_CSS_PROVIDER_ERROR,
-                               GTK_CSS_PROVIDER_ERROR_SYNTAX,
-                               "Expected '(' after 'url'");
-          return NULL;
-        }
-
-      chr = strchr (str, ')');
-      if (!chr)
-        {
-          g_set_error_literal (error,
-                               GTK_CSS_PROVIDER_ERROR,
-                               GTK_CSS_PROVIDER_ERROR_SYNTAX,
-                               "No closing ')' found for 'url'");
-          return NULL;
-        }
-
-      if (end)
-        *end = chr + 1;
-
-      str++;
-      SKIP_SPACES (str);
-
-      if (*str == '"' || *str == '\'')
+      if (!_gtk_css_parser_try (parser, "(", TRUE))
         {
-          const gchar *p;
-          p = str;
-
-          str++;
-          chr--;
-          SKIP_SPACES_BACK (chr);
-
-          if (*chr != *p || chr == p)
+          _gtk_css_parser_skip_whitespace (parser);
+          if (_gtk_css_parser_try (parser, "(", TRUE))
             {
-              g_set_error_literal (error,
-                                   GTK_CSS_PROVIDER_ERROR,
-                                   GTK_CSS_PROVIDER_ERROR_SYNTAX,
-                                   "Did not find closing quote for url");
+              GError *error;
+              
+              error = g_error_new_literal (GTK_CSS_PROVIDER_ERROR,
+                                           GTK_CSS_PROVIDER_ERROR_DEPRECATED,
+                                           "Whitespace between 'url' and '(' is not allowed");
+                             
+              _gtk_css_parser_take_error (parser, error);
+            }
+          else
+            {
+              _gtk_css_parser_error (parser, "Expected '(' after 'url'");
               return NULL;
             }
         }
-      else
+
+      path = _gtk_css_parser_read_string (parser);
+      if (path == NULL)
+        return NULL;
+
+      if (!_gtk_css_parser_try (parser, ")", TRUE))
         {
-          g_set_error_literal (error,
-                               GTK_CSS_PROVIDER_ERROR,
-                               GTK_CSS_PROVIDER_ERROR_SYNTAX,
-                               "url not properly escaped");
+          _gtk_css_parser_error (parser, "No closing ')' found for 'url'");
+          g_free (path);
           return NULL;
         }
-
-      path = g_strndup (str, chr - str);
-      g_strstrip (path);
     }
   else
     {
-      path = g_strdup (str);
-      if (end)
-        *end = (gchar *) str + strlen (str);
+      path = _gtk_css_parser_try_name (parser, TRUE);
+      if (path == NULL)
+        {
+          _gtk_css_parser_error (parser, "Not a valid url");
+          return NULL;
+        }
     }
 
   file = g_file_resolve_relative_path (base, path);
diff --git a/gtk/gtkcssstringfuncsprivate.h b/gtk/gtkcssstringfuncsprivate.h
index 8a910f9..4911a7a 100644
--- a/gtk/gtkcssstringfuncsprivate.h
+++ b/gtk/gtkcssstringfuncsprivate.h
@@ -20,20 +20,17 @@
 #ifndef __GTK_CSS_STRINGFUNCS_PRIVATE_H__
 #define __GTK_CSS_STRINGFUNCS_PRIVATE_H__
 
-#include <gtk/gtksymboliccolor.h>
+#include "gtkcssparserprivate.h"
 
 G_BEGIN_DECLS
 
-gboolean                _gtk_css_value_from_string        (GValue        *value,
-                                                           GFile         *base,
-                                                           const char    *string,
-                                                           GError       **error);
+gboolean                _gtk_css_value_parse              (GValue        *value,
+                                                           GtkCssParser  *parser,
+                                                           GFile         *base);
 char *                  _gtk_css_value_to_string          (const GValue  *value);
 
-GFile *                 _gtk_css_parse_url                (GFile         *base,
-                                                           const char    *str,
-                                                           char         **end,
-                                                           GError       **error);
+GFile *                 _gtk_css_parse_url                (GtkCssParser *parser,
+                                                           GFile        *base);
 
 G_END_DECLS
 



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