From d2ef71627b5021108086143c0afaefb9ee196a7d Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Wed, 18 May 2011 18:32:22 +0200 Subject: [PATCH] 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 de798e781..c956fd5bb 100644 --- a/gtk/gtkcssparser.c +++ b/gtk/gtkcssparser.c @@ -107,6 +107,15 @@ _gtk_css_parser_get_position (GtkCssParser *parser) return parser->data - parser->line_start; } +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 609b9f56f..976edc00d 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 62a7ba3ba..ab6580451 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 bb70bb97a..b8eb913bf 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 8a910f99d..4911a7a71 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 +#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 -- 2.43.2