From 7ccb9db79e702e507dedf211ed25787be2f32721 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Thu, 14 Apr 2011 04:47:18 +0200 Subject: [PATCH] css: Rewrite the parser Instead of relying on GScanner and its idea of syntax, code up a parser that obeys the CSS spec. This also has the great side effect of reporting correct line numbers and positions. Also included is a reorganization of the returned error values. Instead of error values describing what type of syntax error was returned, the code just returns SYNTAX_ERROR. Other messages exist for when actual values don't work or when errors shouldn't be fatal due to backwards compatibility. --- gtk/Makefile.am | 2 + gtk/gtkcssparser.c | 938 ++++++++++++++++++ gtk/gtkcssparserprivate.h | 85 ++ gtk/gtkcssprovider.c | 1202 +++++++++++++----------- gtk/gtkcssprovider.h | 10 +- gtk/gtkcssstringfuncs.c | 632 ++++--------- gtk/gtkcssstringfuncsprivate.h | 3 - tests/css/parser/boolean.errors | 16 +- tests/css/parser/border.errors | 16 +- tests/css/parser/does-not-exist.errors | 2 +- tests/css/parser/integer.errors | 14 +- 11 files changed, 1902 insertions(+), 1018 deletions(-) create mode 100644 gtk/gtkcssparser.c create mode 100644 gtk/gtkcssparserprivate.h diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 6ca52ec68..f6758309f 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -386,6 +386,7 @@ gtk_private_h_sources = \ gtkbuilderprivate.h \ gtkbuttonprivate.h \ gtkcellareaboxcontextprivate.h \ + gtkcssparserprivate.h \ gtkcssproviderprivate.h \ gtkcssstringfuncsprivate.h \ gtkcustompaperunixdialog.h \ @@ -512,6 +513,7 @@ gtk_base_c_sources = \ gtkcombobox.c \ gtkcomboboxtext.c \ gtkcontainer.c \ + gtkcssparser.c \ gtkcssprovider.c \ gtkcssstringfuncs.c \ gtkdialog.c \ diff --git a/gtk/gtkcssparser.c b/gtk/gtkcssparser.c new file mode 100644 index 000000000..de798e781 --- /dev/null +++ b/gtk/gtkcssparser.c @@ -0,0 +1,938 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2011 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "gtkcssparserprivate.h" + +#include +#include + +/* just for the errors, yay! */ +#include "gtkcssprovider.h" + +#define NEWLINE_CHARS "\r\n" +#define WHITESPACE_CHARS "\f \t" +#define NMSTART "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +#define NMCHAR NMSTART "01234567890-_" +#define URLCHAR NMCHAR "!#$%&*~" + +#define GTK_IS_CSS_PARSER(parser) ((parser) != NULL) + +struct _GtkCssParser +{ + const char *data; + GtkCssParserErrorFunc error_func; + gpointer user_data; + + const char *line_start; + guint line; +}; + +GtkCssParser * +_gtk_css_parser_new (const char *data, + GtkCssParserErrorFunc error_func, + gpointer user_data) +{ + GtkCssParser *parser; + + g_return_val_if_fail (data != NULL, NULL); + + parser = g_slice_new0 (GtkCssParser); + + parser->data = data; + parser->error_func = error_func; + parser->user_data = user_data; + + parser->line_start = data; + parser->line = 1; + + return parser; +} + +void +_gtk_css_parser_free (GtkCssParser *parser) +{ + g_return_if_fail (GTK_IS_CSS_PARSER (parser)); + + g_slice_free (GtkCssParser, parser); +} + +gboolean +_gtk_css_parser_is_eof (GtkCssParser *parser) +{ + g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), TRUE); + + return *parser->data == 0; +} + +gboolean +_gtk_css_parser_begins_with (GtkCssParser *parser, + char c) +{ + g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), TRUE); + + return *parser->data == c; +} + +guint +_gtk_css_parser_get_line (GtkCssParser *parser) +{ + g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), 1); + + return parser->line; +} + +guint +_gtk_css_parser_get_position (GtkCssParser *parser) +{ + g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), 1); + + return parser->data - parser->line_start; +} + +void +_gtk_css_parser_error (GtkCssParser *parser, + const char *format, + ...) +{ + GError *error; + + va_list args; + + va_start (args, format); + error = g_error_new_valist (GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + format, args); + va_end (args); + + parser->error_func (parser, error, parser->user_data); + + g_error_free (error); +} + +static gboolean +gtk_css_parser_new_line (GtkCssParser *parser) +{ + gboolean result = FALSE; + + if (*parser->data == '\r') + { + result = TRUE; + parser->data++; + } + if (*parser->data == '\n') + { + result = TRUE; + parser->data++; + } + + if (result) + { + parser->line++; + parser->line_start = parser->data; + } + + return result; +} + +static gboolean +gtk_css_parser_skip_comment (GtkCssParser *parser) +{ + if (parser->data[0] != '/' || + parser->data[1] != '*') + return FALSE; + + parser->data += 2; + + while (*parser->data) + { + gsize len = strcspn (parser->data, NEWLINE_CHARS "/"); + + parser->data += len; + + if (gtk_css_parser_new_line (parser)) + continue; + + parser->data++; + + if (parser->data[-2] == '*') + return TRUE; + if (parser->data[0] == '*') + _gtk_css_parser_error (parser, "'/*' in comment block"); + } + + /* FIXME: position */ + _gtk_css_parser_error (parser, "Unterminated comment"); + return TRUE; +} + +void +_gtk_css_parser_skip_whitespace (GtkCssParser *parser) +{ + size_t len; + + while (*parser->data) + { + if (gtk_css_parser_new_line (parser)) + continue; + + len = strspn (parser->data, WHITESPACE_CHARS); + if (len) + { + parser->data += len; + continue; + } + + if (!gtk_css_parser_skip_comment (parser)) + break; + } +} + +gboolean +_gtk_css_parser_try (GtkCssParser *parser, + const char *string, + gboolean skip_whitespace) +{ + g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE); + g_return_val_if_fail (string != NULL, FALSE); + + if (g_ascii_strncasecmp (parser->data, string, strlen (string)) != 0) + return FALSE; + + parser->data += strlen (string); + + if (skip_whitespace) + _gtk_css_parser_skip_whitespace (parser); + return TRUE; +} + +static guint +get_xdigit (char c) +{ + if (c >= 'a') + return c - 'a' + 10; + else if (c >= 'A') + return c - 'A' + 10; + else + return c - '0'; +} + +static void +_gtk_css_parser_unescape (GtkCssParser *parser, + GString *str) +{ + guint i; + gunichar result = 0; + + g_assert (*parser->data == '\\'); + + parser->data++; + + for (i = 0; i < 6; i++) + { + if (!g_ascii_isxdigit (parser->data[i])) + break; + + result = (result << 4) + get_xdigit (parser->data[i]); + } + + if (i != 0) + { + g_string_append_unichar (str, result); + parser->data += i; + + /* NB: gtk_css_parser_new_line() forward data pointer itself */ + if (!gtk_css_parser_new_line (parser) && + *parser->data && + strchr (WHITESPACE_CHARS, *parser->data)) + parser->data++; + return; + } + + if (gtk_css_parser_new_line (parser)) + return; + + g_string_append_c (str, *parser->data); + parser->data++; + + return; +} + +static gboolean +_gtk_css_parser_read_char (GtkCssParser *parser, + GString * str, + const char * allowed) +{ + if (*parser->data == 0) + return FALSE; + + if (strchr (allowed, *parser->data)) + { + g_string_append_c (str, *parser->data); + parser->data++; + return TRUE; + } + if (*parser->data >= 127) + { + gsize len = g_utf8_skip[(guint) *(guchar *) parser->data]; + + g_string_append_len (str, parser->data, len); + parser->data += len; + return TRUE; + } + if (*parser->data == '\\') + { + _gtk_css_parser_unescape (parser, str); + return TRUE; + } + + return FALSE; +} + +char * +_gtk_css_parser_try_name (GtkCssParser *parser, + gboolean skip_whitespace) +{ + GString *name; + + g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL); + + name = g_string_new (NULL); + + while (_gtk_css_parser_read_char (parser, name, NMCHAR)) + ; + + if (skip_whitespace) + _gtk_css_parser_skip_whitespace (parser); + + return g_string_free (name, FALSE); +} + +char * +_gtk_css_parser_try_ident (GtkCssParser *parser, + gboolean skip_whitespace) +{ + const char *start; + GString *ident; + + g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL); + + start = parser->data; + + ident = g_string_new (NULL); + + if (*parser->data == '-') + { + g_string_append_c (ident, '-'); + parser->data++; + } + + if (!_gtk_css_parser_read_char (parser, ident, NMSTART)) + { + parser->data = start; + g_string_free (ident, TRUE); + return NULL; + } + + while (_gtk_css_parser_read_char (parser, ident, NMCHAR)) + ; + + if (skip_whitespace) + _gtk_css_parser_skip_whitespace (parser); + + return g_string_free (ident, FALSE); +} + +gboolean +_gtk_css_parser_is_string (GtkCssParser *parser) +{ + g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE); + + return *parser->data == '"' || *parser->data == '\''; +} + +char * +_gtk_css_parser_read_string (GtkCssParser *parser) +{ + GString *str; + char quote; + + g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL); + + quote = *parser->data; + + if (quote != '"' && quote != '\'') + return NULL; + + parser->data++; + str = g_string_new (NULL); + + while (TRUE) + { + gsize len = strcspn (parser->data, "\\'\"\n\r\f"); + + g_string_append_len (str, parser->data, len); + + parser->data += len; + + switch (*parser->data) + { + case '\\': + _gtk_css_parser_unescape (parser, str); + break; + case '"': + case '\'': + if (*parser->data == quote) + { + parser->data++; + _gtk_css_parser_skip_whitespace (parser); + return g_string_free (str, FALSE); + } + + g_string_append_c (str, *parser->data); + parser->data++; + break; + case '\0': + /* FIXME: position */ + _gtk_css_parser_error (parser, "Missing end quote in string."); + g_string_free (str, TRUE); + return NULL; + default: + _gtk_css_parser_error (parser, + "Invalid character in string. Must be escaped."); + g_string_free (str, TRUE); + return NULL; + } + } + + g_assert_not_reached (); + return NULL; +} + +char * +_gtk_css_parser_read_uri (GtkCssParser *parser) +{ + char *result; + + g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL); + + if (!_gtk_css_parser_try (parser, "url(", TRUE)) + { + _gtk_css_parser_error (parser, "expected 'url('"); + return NULL; + } + + _gtk_css_parser_skip_whitespace (parser); + + if (_gtk_css_parser_is_string (parser)) + { + result = _gtk_css_parser_read_string (parser); + } + else + { + GString *str = g_string_new (NULL); + + while (_gtk_css_parser_read_char (parser, str, URLCHAR)) + ; + result = g_string_free (str, FALSE); + if (result == NULL) + _gtk_css_parser_error (parser, "not a url"); + } + + if (result == NULL) + return NULL; + + _gtk_css_parser_skip_whitespace (parser); + + if (*parser->data != ')') + { + _gtk_css_parser_error (parser, "missing ')' for url"); + g_free (result); + return NULL; + } + + parser->data++; + + _gtk_css_parser_skip_whitespace (parser); + + return result; +} + +gboolean +_gtk_css_parser_try_int (GtkCssParser *parser, + int *value) +{ + gint64 result; + char *end; + + g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE); + g_return_val_if_fail (value != NULL, FALSE); + + /* strtoll parses a plus, but we are not allowed to */ + if (*parser->data == '+') + return FALSE; + + errno = 0; + result = g_ascii_strtoll (parser->data, &end, 10); + if (errno) + return FALSE; + if (result > G_MAXINT || result < G_MININT) + return FALSE; + if (parser->data == end) + return FALSE; + + parser->data = end; + *value = result; + + _gtk_css_parser_skip_whitespace (parser); + + return TRUE; +} + +gboolean +_gtk_css_parser_try_uint (GtkCssParser *parser, + uint *value) +{ + guint64 result; + char *end; + + g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE); + g_return_val_if_fail (value != NULL, FALSE); + + errno = 0; + result = g_ascii_strtoull (parser->data, &end, 10); + if (errno) + return FALSE; + if (result > G_MAXUINT) + return FALSE; + if (parser->data == end) + return FALSE; + + parser->data = end; + *value = result; + + _gtk_css_parser_skip_whitespace (parser); + + return TRUE; +} + +gboolean +_gtk_css_parser_try_double (GtkCssParser *parser, + gdouble *value) +{ + gdouble result; + char *end; + + g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE); + g_return_val_if_fail (value != NULL, FALSE); + + errno = 0; + result = g_ascii_strtod (parser->data, &end); + if (errno) + return FALSE; + if (parser->data == end) + return FALSE; + + parser->data = end; + *value = result; + + _gtk_css_parser_skip_whitespace (parser); + + return TRUE; +} + +typedef enum { + COLOR_RGBA, + COLOR_RGB, + COLOR_LIGHTER, + COLOR_DARKER, + COLOR_SHADE, + COLOR_ALPHA, + COLOR_MIX +} ColorType; + +static GtkSymbolicColor * +gtk_css_parser_read_symbolic_color_function (GtkCssParser *parser, + ColorType color) +{ + GtkSymbolicColor *symbolic; + GtkSymbolicColor *child1, *child2; + double value; + + if (!_gtk_css_parser_try (parser, "(", TRUE)) + { + _gtk_css_parser_error (parser, "Missing opening bracket in color definition"); + return NULL; + } + + if (color == COLOR_RGB || color == COLOR_RGBA) + { + GdkRGBA rgba; + double tmp; + guint i; + + for (i = 0; i < 3; i++) + { + if (i > 0 && !_gtk_css_parser_try (parser, ",", TRUE)) + { + _gtk_css_parser_error (parser, "Expected ',' in color definition"); + return NULL; + } + + if (!_gtk_css_parser_try_double (parser, &tmp)) + { + _gtk_css_parser_error (parser, "Invalid number for color value"); + return NULL; + } + if (_gtk_css_parser_try (parser, "%", TRUE)) + tmp /= 100.0; + else + tmp /= 255.0; + if (i == 0) + rgba.red = tmp; + else if (i == 1) + rgba.green = tmp; + else if (i == 2) + rgba.blue = tmp; + else + g_assert_not_reached (); + } + + if (color == COLOR_RGBA) + { + if (i > 0 && !_gtk_css_parser_try (parser, ",", TRUE)) + { + _gtk_css_parser_error (parser, "Expected ',' in color definition"); + return NULL; + } + + if (!_gtk_css_parser_try_double (parser, &rgba.alpha)) + { + _gtk_css_parser_error (parser, "Invalid number for alpha value"); + return NULL; + } + } + else + rgba.alpha = 1.0; + + symbolic = gtk_symbolic_color_new_literal (&rgba); + } + else + { + child1 = _gtk_css_parser_read_symbolic_color (parser); + if (child1 == NULL) + return NULL; + + if (color == COLOR_MIX) + { + if (!_gtk_css_parser_try (parser, ",", TRUE)) + { + _gtk_css_parser_error (parser, "Expected ',' in color definition"); + gtk_symbolic_color_unref (child1); + return NULL; + } + + child2 = _gtk_css_parser_read_symbolic_color (parser); + if (child2 == NULL) + { + g_object_unref (child1); + return NULL; + } + } + else + child2 = NULL; + + if (color == COLOR_LIGHTER) + value = 1.3; + else if (color == COLOR_DARKER) + value = 0.7; + else + { + if (!_gtk_css_parser_try (parser, ",", TRUE)) + { + _gtk_css_parser_error (parser, "Expected ',' in color definition"); + gtk_symbolic_color_unref (child1); + if (child2) + gtk_symbolic_color_unref (child2); + return NULL; + } + + if (!_gtk_css_parser_try_double (parser, &value)) + { + _gtk_css_parser_error (parser, "Expected number in color definition"); + gtk_symbolic_color_unref (child1); + if (child2) + gtk_symbolic_color_unref (child2); + return NULL; + } + } + + switch (color) + { + case COLOR_LIGHTER: + case COLOR_DARKER: + case COLOR_SHADE: + symbolic = gtk_symbolic_color_new_shade (child1, value); + break; + case COLOR_ALPHA: + symbolic = gtk_symbolic_color_new_alpha (child1, value); + break; + case COLOR_MIX: + symbolic = gtk_symbolic_color_new_mix (child1, child2, value); + break; + default: + g_assert_not_reached (); + symbolic = NULL; + } + + gtk_symbolic_color_unref (child1); + if (child2) + gtk_symbolic_color_unref (child2); + } + + if (!_gtk_css_parser_try (parser, ")", TRUE)) + { + gtk_symbolic_color_unref (symbolic); + return NULL; + } + + return symbolic; +} + +static GtkSymbolicColor * +gtk_css_parser_try_hash_color (GtkCssParser *parser) +{ + if (parser->data[0] == '#' && + g_ascii_isxdigit (parser->data[1]) && + g_ascii_isxdigit (parser->data[2]) && + g_ascii_isxdigit (parser->data[3])) + { + GdkRGBA rgba; + + if (g_ascii_isxdigit (parser->data[4]) && + g_ascii_isxdigit (parser->data[5]) && + g_ascii_isxdigit (parser->data[6])) + { + rgba.red = ((get_xdigit (parser->data[1]) << 4) + get_xdigit (parser->data[2])) / 255.0; + rgba.green = ((get_xdigit (parser->data[3]) << 4) + get_xdigit (parser->data[4])) / 255.0; + rgba.blue = ((get_xdigit (parser->data[5]) << 4) + get_xdigit (parser->data[6])) / 255.0; + rgba.alpha = 1.0; + parser->data += 7; + } + else + { + rgba.red = get_xdigit (parser->data[1]) / 15.0; + rgba.green = get_xdigit (parser->data[2]) / 15.0; + rgba.blue = get_xdigit (parser->data[3]) / 15.0; + rgba.alpha = 1.0; + parser->data += 4; + } + + _gtk_css_parser_skip_whitespace (parser); + + return gtk_symbolic_color_new_literal (&rgba); + } + + return NULL; +} + +GtkSymbolicColor * +_gtk_css_parser_read_symbolic_color (GtkCssParser *parser) +{ + GtkSymbolicColor *symbolic; + guint color; + const char *names[] = {"rgba", "rgb", "lighter", "darker", "shade", "alpha", "mix" }; + char *name; + + g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL); + + if (_gtk_css_parser_try (parser, "@", FALSE)) + { + name = _gtk_css_parser_try_name (parser, TRUE); + + if (name) + { + symbolic = gtk_symbolic_color_new_name (name); + } + else + { + _gtk_css_parser_error (parser, "'%s' is not a valid symbolic color name", name); + symbolic = NULL; + } + + g_free (name); + return symbolic; + } + + for (color = 0; color < G_N_ELEMENTS (names); color++) + { + if (_gtk_css_parser_try (parser, names[color], TRUE)) + break; + } + + if (color < G_N_ELEMENTS (names)) + return gtk_css_parser_read_symbolic_color_function (parser, color); + + symbolic = gtk_css_parser_try_hash_color (parser); + if (symbolic) + return symbolic; + + name = _gtk_css_parser_try_name (parser, TRUE); + if (name) + { + GdkRGBA rgba; + + if (gdk_rgba_parse (&rgba, name)) + { + symbolic = gtk_symbolic_color_new_literal (&rgba); + } + else + { + _gtk_css_parser_error (parser, "'%s' is not a valid color name", name); + symbolic = NULL; + } + g_free (name); + return symbolic; + } + + _gtk_css_parser_error (parser, "Not a color definition"); + return NULL; +} + +void +_gtk_css_parser_resync_internal (GtkCssParser *parser, + gboolean sync_at_semicolon, + gboolean read_sync_token, + char terminator) +{ + gsize len; + + do { + len = strcspn (parser->data, "\\\"'/()[]{};" NEWLINE_CHARS); + parser->data += len; + + if (gtk_css_parser_new_line (parser)) + continue; + + if (_gtk_css_parser_is_string (parser)) + { + /* Hrm, this emits errors, and i suspect it shouldn't... */ + char *free_me = _gtk_css_parser_read_string (parser); + g_free (free_me); + continue; + } + + if (gtk_css_parser_skip_comment (parser)) + continue; + + switch (*parser->data) + { + case '/': + { + GString *ignore = g_string_new (NULL); + _gtk_css_parser_unescape (parser, ignore); + g_string_free (ignore, TRUE); + } + break; + case ';': + if (sync_at_semicolon && !read_sync_token) + return; + parser->data++; + if (sync_at_semicolon) + { + _gtk_css_parser_skip_whitespace (parser); + return; + } + break; + case '(': + parser->data++; + _gtk_css_parser_resync (parser, FALSE, ')'); + parser->data++; + break; + case '[': + parser->data++; + _gtk_css_parser_resync (parser, FALSE, ']'); + parser->data++; + break; + case '{': + parser->data++; + _gtk_css_parser_resync (parser, FALSE, '}'); + parser->data++; + if (sync_at_semicolon || !terminator) + { + _gtk_css_parser_skip_whitespace (parser); + return; + } + break; + case '}': + case ')': + case ']': + if (terminator == *parser->data) + { + _gtk_css_parser_skip_whitespace (parser); + return; + } + parser->data++; + continue; + default: + break; + } + } while (*parser->data); +} + +char * +_gtk_css_parser_read_value (GtkCssParser *parser) +{ + const char *start; + char *result; + + g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL); + + start = parser->data; + + /* This needs to be done better */ + _gtk_css_parser_resync_internal (parser, TRUE, FALSE, '}'); + + result = g_strndup (start, parser->data - start); + if (result) + { + g_strchomp (result); + if (result[0] == 0) + { + g_free (result); + result = NULL; + } + } + + if (result == NULL) + _gtk_css_parser_error (parser, "Expected a property value"); + + return result; +} + +void +_gtk_css_parser_resync (GtkCssParser *parser, + gboolean sync_at_semicolon, + char terminator) +{ + g_return_if_fail (GTK_IS_CSS_PARSER (parser)); + + _gtk_css_parser_resync_internal (parser, sync_at_semicolon, TRUE, terminator); +} diff --git a/gtk/gtkcssparserprivate.h b/gtk/gtkcssparserprivate.h new file mode 100644 index 000000000..609b9f56f --- /dev/null +++ b/gtk/gtkcssparserprivate.h @@ -0,0 +1,85 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2011 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GTK_CSS_PARSER_PRIVATE_H__ +#define __GTK_CSS_PARSER_PRIVATE_H__ + +#include + +G_BEGIN_DECLS + +typedef struct _GtkCssParser GtkCssParser; + +typedef void (* GtkCssParserErrorFunc) (GtkCssParser *parser, + const GError *error, + gpointer user_data); + +GtkCssParser * _gtk_css_parser_new (const char *data, + GtkCssParserErrorFunc error_func, + gpointer user_data); +void _gtk_css_parser_free (GtkCssParser *parser); + +void _gtk_css_parser_error (GtkCssParser *parser, + const char *format, + ...) G_GNUC_PRINTF (2, 3); + +guint _gtk_css_parser_get_line (GtkCssParser *parser); +guint _gtk_css_parser_get_position (GtkCssParser *parser); + +gboolean _gtk_css_parser_is_eof (GtkCssParser *parser); +gboolean _gtk_css_parser_begins_with (GtkCssParser *parser, + char c); +gboolean _gtk_css_parser_is_string (GtkCssParser *parser); + +/* IMPORTANT: + * _try_foo() functions do not modify the data pointer if they fail, nor do they + * signal an error. _read_foo() will modify the data pointer and position it at + * the first token that is broken and emit an error about the failure. + * So only call _read_foo() when you know that you are reading a foo. _try_foo() + * however is fine to call if you don't know yet if the token is a foo or a bar, + * you can _try_bar() if try_foo() failed. + */ +gboolean _gtk_css_parser_try (GtkCssParser *parser, + const char *string, + gboolean skip_whitespace); +char * _gtk_css_parser_try_ident (GtkCssParser *parser, + gboolean skip_whitespace); +char * _gtk_css_parser_try_name (GtkCssParser *parser, + gboolean skip_whitespace); +gboolean _gtk_css_parser_try_int (GtkCssParser *parser, + int *value); +gboolean _gtk_css_parser_try_uint (GtkCssParser *parser, + uint *value); +gboolean _gtk_css_parser_try_double (GtkCssParser *parser, + gdouble *value); + +void _gtk_css_parser_skip_whitespace (GtkCssParser *parser); +char * _gtk_css_parser_read_string (GtkCssParser *parser); +char * _gtk_css_parser_read_uri (GtkCssParser *parser); +char * _gtk_css_parser_read_value (GtkCssParser *parser); +GtkSymbolicColor *_gtk_css_parser_read_symbolic_color + (GtkCssParser *parser); + +void _gtk_css_parser_resync (GtkCssParser *parser, + gboolean sync_at_semicolon, + char terminator); + +G_END_DECLS + +#endif /* __GTK_CSS_PARSER_PRIVATE_H__ */ diff --git a/gtk/gtkcssprovider.c b/gtk/gtkcssprovider.c index b65af8cc0..05b0f5a27 100644 --- a/gtk/gtkcssprovider.c +++ b/gtk/gtkcssprovider.c @@ -27,6 +27,7 @@ #include "gtkcssproviderprivate.h" +#include "gtkcssparserprivate.h" #include "gtkcssstringfuncsprivate.h" #include "gtksymboliccolor.h" #include "gtkstyleprovider.h" @@ -735,7 +736,7 @@ typedef struct SelectorElement SelectorElement; typedef struct SelectorPath SelectorPath; typedef struct SelectorStyleInfo SelectorStyleInfo; -typedef struct _GtkCssScannerPrivate GtkCssScannerPrivate; +typedef struct _GtkCssScanner GtkCssScanner; typedef enum SelectorElementType SelectorElementType; typedef enum CombinatorType CombinatorType; typedef enum ParserScope ParserScope; @@ -786,9 +787,11 @@ struct SelectorStyleInfo GHashTable *style; }; -struct _GtkCssScannerPrivate +struct _GtkCssScanner { - GScanner *parent; + GtkCssProvider *provider; + GtkCssParser *parser; + GtkCssScanner *parent; GFile *file; GFile *base; GSList *state; @@ -841,11 +844,9 @@ static guint css_provider_signals[LAST_SIGNAL] = { 0 }; static void gtk_css_provider_finalize (GObject *object); static void gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface); -static void scanner_apply_scope (GScanner *scanner, - ParserScope scope); static gboolean gtk_css_provider_load_internal (GtkCssProvider *css_provider, - GScanner *scanner, + GtkCssScanner *scanner, GFile *file, const char *data, gsize length, @@ -1080,12 +1081,6 @@ selector_path_prepend_combinator (SelectorPath *path, elem->combinator = combinator; } -static gint -selector_path_depth (SelectorPath *path) -{ - return g_slist_length (path->elements); -} - static SelectorStyleInfo * selector_style_info_new (SelectorPath *path) { @@ -1132,124 +1127,110 @@ property_value_free (GValue *value) } static void -gtk_css_scanner_reset (GScanner *scanner) +gtk_css_scanner_reset (GtkCssScanner *scanner) { - GtkCssScannerPrivate *priv = scanner->user_data; + g_slist_free (scanner->state); + scanner->state = NULL; - g_slist_free (priv->state); - priv->state = NULL; + g_slist_foreach (scanner->cur_selectors, (GFunc) selector_path_unref, NULL); + g_slist_free (scanner->cur_selectors); + scanner->cur_selectors = NULL; - g_slist_foreach (priv->cur_selectors, (GFunc) selector_path_unref, NULL); - g_slist_free (priv->cur_selectors); - priv->cur_selectors = NULL; + if (scanner->cur_properties) + g_hash_table_unref (scanner->cur_properties); - if (priv->cur_properties) - g_hash_table_unref (priv->cur_properties); + scanner ->cur_properties = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) property_value_free); +} - priv->cur_properties = g_hash_table_new_full (g_str_hash, - g_str_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) property_value_free); +static void +gtk_css_scanner_destroy (GtkCssScanner *scanner) +{ + gtk_css_scanner_reset (scanner); + + g_object_unref (scanner->provider); + if (scanner->file) + g_object_unref (scanner->file); + g_object_unref (scanner->base); + g_hash_table_destroy (scanner->cur_properties); + _gtk_css_parser_free (scanner->parser); - scanner_apply_scope (scanner, SCOPE_SELECTOR); + g_slice_free (GtkCssScanner, scanner); } static void -gtk_css_scanner_destroy (GScanner *scanner) +gtk_css_scanner_parser_error (GtkCssParser *parser, + const GError *error, + gpointer user_data) { - GtkCssScannerPrivate *priv = scanner->user_data; - - gtk_css_scanner_reset (scanner); + GtkCssScanner *scanner = user_data; - if (priv->file) - g_object_unref (priv->file); - g_hash_table_destroy (priv->cur_properties); - g_slice_free (GtkCssScannerPrivate, priv); - - g_scanner_destroy (scanner); + gtk_css_provider_take_error_full (scanner->provider, + scanner->file, + _gtk_css_parser_get_line (scanner->parser), + _gtk_css_parser_get_position (scanner->parser), + g_error_copy (error)); } -static GScanner * -gtk_css_scanner_new (GScanner *parent, - GFile *file, - const gchar *data, - gsize length) +static GtkCssScanner * +gtk_css_scanner_new (GtkCssProvider *provider, + GtkCssScanner *parent, + GFile *file, + const gchar *data, + gsize length) { - GtkCssScannerPrivate *priv; - GScanner *scanner; + GtkCssScanner *scanner; - scanner = g_scanner_new (NULL); + g_assert (data[length] == 0); - priv = scanner->user_data = g_slice_new0 (GtkCssScannerPrivate); + scanner = g_slice_new0 (GtkCssScanner); - priv->parent = parent; + g_object_ref (provider); + scanner->provider = provider; + scanner->parent = parent; if (file) { - priv->file = g_object_ref (file); - priv->base = g_file_get_parent (file); + scanner->file = g_object_ref (file); + scanner->base = g_file_get_parent (file); } else { char *dir = g_get_current_dir (); - priv->base = g_file_new_for_path (dir); + scanner->base = g_file_new_for_path (dir); g_free (dir); } - priv->cur_properties = g_hash_table_new_full (g_str_hash, - g_str_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) property_value_free); + scanner->cur_properties = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) property_value_free); - g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "active", GUINT_TO_POINTER (GTK_STATE_ACTIVE)); - g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "prelight", GUINT_TO_POINTER (GTK_STATE_PRELIGHT)); - g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "hover", GUINT_TO_POINTER (GTK_STATE_PRELIGHT)); - g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "selected", GUINT_TO_POINTER (GTK_STATE_SELECTED)); - g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "insensitive", GUINT_TO_POINTER (GTK_STATE_INSENSITIVE)); - g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "inconsistent", GUINT_TO_POINTER (GTK_STATE_INCONSISTENT)); - g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "focused", GUINT_TO_POINTER (GTK_STATE_FOCUSED)); - g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "focus", GUINT_TO_POINTER (GTK_STATE_FOCUSED)); - - g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "nth-child", GUINT_TO_POINTER (SYMBOL_NTH_CHILD)); - g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "first-child", GUINT_TO_POINTER (SYMBOL_FIRST_CHILD)); - g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "last-child", GUINT_TO_POINTER (SYMBOL_LAST_CHILD)); - g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "sorted", GUINT_TO_POINTER (SYMBOL_SORTED_CHILD)); - - g_scanner_scope_add_symbol (scanner, SCOPE_NTH_CHILD, "even", GUINT_TO_POINTER (SYMBOL_NTH_CHILD_EVEN)); - g_scanner_scope_add_symbol (scanner, SCOPE_NTH_CHILD, "odd", GUINT_TO_POINTER (SYMBOL_NTH_CHILD_ODD)); - g_scanner_scope_add_symbol (scanner, SCOPE_NTH_CHILD, "first", GUINT_TO_POINTER (SYMBOL_NTH_CHILD_FIRST)); - g_scanner_scope_add_symbol (scanner, SCOPE_NTH_CHILD, "last", GUINT_TO_POINTER (SYMBOL_NTH_CHILD_LAST)); - - scanner_apply_scope (scanner, SCOPE_SELECTOR); - - if (length > G_MAXUINT32) - g_warning ("CSS file too large, truncating"); - - g_scanner_input_text (scanner, data, length); + scanner->parser = _gtk_css_parser_new (data, + gtk_css_scanner_parser_error, + scanner); return scanner; } static GFile * -gtk_css_scanner_get_base_url (GScanner *scanner) +gtk_css_scanner_get_base_url (GtkCssScanner *scanner) { - GtkCssScannerPrivate *priv = scanner->user_data; - - return priv->base; + return scanner->base; } static gboolean -gtk_css_scanner_would_recurse (GScanner *scanner, - GFile *file) +gtk_css_scanner_would_recurse (GtkCssScanner *scanner, + GFile *file) { while (scanner) { - GtkCssScannerPrivate *priv = scanner->user_data; - - if (priv->file && g_file_equal (priv->file, file)) + if (scanner->file && g_file_equal (scanner->file, file)) return TRUE; - scanner = priv->parent; + scanner = scanner->parent; } return FALSE; @@ -1677,21 +1658,19 @@ gtk_css_provider_new (void) static void gtk_css_provider_take_error (GtkCssProvider *provider, - GScanner *scanner, + GtkCssScanner *scanner, GError *error) { - GtkCssScannerPrivate *priv = scanner->user_data; - gtk_css_provider_take_error_full (provider, - priv->file, - scanner->line, - scanner->position, + scanner->file, + _gtk_css_parser_get_line (scanner->parser), + _gtk_css_parser_get_position (scanner->parser), error); } static void gtk_css_provider_error_literal (GtkCssProvider *provider, - GScanner *scanner, + GtkCssScanner *scanner, GQuark domain, gint code, const char *message) @@ -1703,14 +1682,14 @@ gtk_css_provider_error_literal (GtkCssProvider *provider, static void gtk_css_provider_error (GtkCssProvider *provider, - GScanner *scanner, + GtkCssScanner *scanner, GQuark domain, gint code, const char *format, ...) G_GNUC_PRINTF (5, 6); static void gtk_css_provider_error (GtkCssProvider *provider, - GScanner *scanner, + GtkCssScanner *scanner, GQuark domain, gint code, const char *format, @@ -1728,7 +1707,7 @@ gtk_css_provider_error (GtkCssProvider *provider, static void gtk_css_provider_invalid_token (GtkCssProvider *provider, - GScanner *scanner, + GtkCssScanner *scanner, const char *expected) { gtk_css_provider_error (provider, @@ -1738,118 +1717,32 @@ gtk_css_provider_invalid_token (GtkCssProvider *provider, "expected a valid %s", expected); } -static void -scanner_apply_scope (GScanner *scanner, - ParserScope scope) -{ - g_scanner_set_scope (scanner, scope); - - if (scope == SCOPE_VALUE) - { - scanner->config->cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "@#-_\"'"; - scanner->config->cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "@#-_ +(),.%\t\n'/\""; - scanner->config->scan_identifier_1char = TRUE; - scanner->config->scan_string_sq = FALSE; - scanner->config->scan_string_dq = FALSE; - } - else if (scope == SCOPE_BINDING_SET) - { - scanner->config->cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "@#-_"; - scanner->config->cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "@#-_ +(){}<>,.%\t\n'/\""; - scanner->config->scan_identifier_1char = TRUE; - scanner->config->scan_string_sq = TRUE; - scanner->config->scan_string_dq = TRUE; - } - else if (scope == SCOPE_SELECTOR) - { - scanner->config->cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "*@"; - scanner->config->cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "-_#."; - scanner->config->scan_identifier_1char = TRUE; - scanner->config->scan_string_sq = TRUE; - scanner->config->scan_string_dq = TRUE; - } - else if (scope == SCOPE_PSEUDO_CLASS || - scope == SCOPE_NTH_CHILD || - scope == SCOPE_DECLARATION) - { - scanner->config->cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "-_"; - scanner->config->cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "-_"; - scanner->config->scan_identifier_1char = FALSE; - scanner->config->scan_string_sq = TRUE; - scanner->config->scan_string_dq = TRUE; - } - else - g_assert_not_reached (); - - scanner->config->scan_float = FALSE; - scanner->config->cpair_comment_single = NULL; -} - -static void -gtk_css_scanner_push_scope (GScanner *scanner, - ParserScope scope) -{ - GtkCssScannerPrivate *priv; - - priv = scanner->user_data; - priv->state = g_slist_prepend (priv->state, GUINT_TO_POINTER (scope)); - - scanner_apply_scope (scanner, scope); -} - -static void -gtk_css_scanner_pop_scope (GScanner *scanner) -{ - GtkCssScannerPrivate *priv; - ParserScope scope = SCOPE_SELECTOR; - - priv = scanner->user_data; - - if (!priv->state) - { - g_warning ("Push/pop calls to parser scope aren't paired"); - scanner_apply_scope (scanner, SCOPE_SELECTOR); - return; - } - - priv->state = g_slist_delete_link (priv->state, priv->state); - - /* Fetch new scope */ - if (priv->state) - scope = GPOINTER_TO_INT (priv->state->data); - - scanner_apply_scope (scanner, scope); -} - -static void +static void css_provider_commit (GtkCssProvider *css_provider, - GScanner *scanner) + GSList *selectors, + GHashTable *properties) { - GtkCssScannerPrivate *scanner_priv; GtkCssProviderPrivate *priv; GSList *l; priv = css_provider->priv; - scanner_priv = scanner->user_data; - - l = scanner_priv->cur_selectors; - if (g_hash_table_size (scanner_priv->cur_properties) == 0) + if (g_hash_table_size (properties) == 0) return; - while (l) + for (l = selectors; l; l = l->next) { SelectorPath *path = l->data; SelectorStyleInfo *info; info = selector_style_info_new (path); - selector_style_info_set_style (info, scanner_priv->cur_properties); + selector_style_info_set_style (info, properties); g_ptr_array_add (priv->selectors_info, info); - l = l->next; } } +#if 0 static GTokenType parse_nth_child (GtkCssProvider *css_provider, GScanner *scanner, @@ -2084,6 +1977,7 @@ parse_selector (GtkCssProvider *css_provider, if ((pos = strchr (name, '.')) != NULL) *pos = '\0'; + selector_path_prepend_combinator (path, COMBINATOR_CHILD); selector_path_prepend_name (path, name); /* Parse any remaining classes */ @@ -2177,6 +2071,7 @@ parse_selector (GtkCssProvider *css_provider, return G_TOKEN_NONE; } +#endif static void resolve_binding_sets (const gchar *value_str, @@ -2204,467 +2099,714 @@ resolve_binding_sets (const gchar *value_str, g_strfreev (bindings); } -static GTokenType -parse_rule (GtkCssProvider *css_provider, - GScanner *scanner) +static void +gtk_css_provider_reset (GtkCssProvider *css_provider) { - GtkCssScannerPrivate *priv; - GTokenType expected_token; - SelectorPath *selector; - - priv = scanner->user_data; - - gtk_css_scanner_push_scope (scanner, SCOPE_SELECTOR); + GtkCssProviderPrivate *priv; - /* Handle directives */ - if (scanner->token == G_TOKEN_IDENTIFIER && - scanner->value.v_identifier[0] == '@') - { - gchar *directive; + priv = css_provider->priv; - directive = &scanner->value.v_identifier[1]; + if (priv->selectors_info->len > 0) + g_ptr_array_remove_range (priv->selectors_info, 0, priv->selectors_info->len); +} - if (strcmp (directive, "define-color") == 0) - { - GtkSymbolicColor *color; - gchar *color_name, *color_str; - GError *error = NULL; +static void +gtk_css_provider_propagate_error (GtkCssProvider *provider, + const gchar *path, + guint line, + guint position, + const GError *error, + GError **propagate_to) +{ + /* we already set an error. And we'd like to keep the first one */ + if (*propagate_to) + return; - /* Directive is a color mapping */ - g_scanner_get_next_token (scanner); + *propagate_to = g_error_copy (error); + g_prefix_error (propagate_to, "%s:%u:%u: ", path ? path : "", line, position); +} - if (scanner->token != G_TOKEN_IDENTIFIER) - { - gtk_css_provider_invalid_token (css_provider, scanner, "Color name"); - return G_TOKEN_IDENTIFIER; - } +static void +parse_import (GtkCssScanner *scanner) +{ + GFile *file; + char *uri; - color_name = g_strdup (scanner->value.v_identifier); - gtk_css_scanner_push_scope (scanner, SCOPE_VALUE); - g_scanner_get_next_token (scanner); + uri = _gtk_css_parser_read_uri (scanner->parser); + if (uri == NULL) + { + _gtk_css_parser_resync (scanner->parser, TRUE, 0); + return; + } - if (scanner->token != G_TOKEN_IDENTIFIER) - { - gtk_css_provider_invalid_token (css_provider, scanner, "Color definition"); - return G_TOKEN_IDENTIFIER; - } + file = g_file_resolve_relative_path (gtk_css_scanner_get_base_url (scanner), uri); + g_free (uri); - color_str = g_strstrip (scanner->value.v_identifier); - color = _gtk_css_parse_symbolic_color (color_str, &error); - if (!color) - { - gtk_css_provider_take_error (css_provider, scanner, error); - return G_TOKEN_IDENTIFIER; - } + if (gtk_css_scanner_would_recurse (scanner, file)) + { + char *path = g_file_get_path (file); + gtk_css_provider_error (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_IMPORT, + "Loading '%s' would recurse", + path); + g_free (path); + } + else + { + gtk_css_provider_load_internal (scanner->provider, + scanner, + file, + NULL, 0, + NULL); + } - g_hash_table_insert (css_provider->priv->symbolic_colors, color_name, color); + if (!_gtk_css_parser_try (scanner->parser, ";", TRUE)) + { + g_object_unref (file); + gtk_css_provider_invalid_token (scanner->provider, scanner, "semicolon"); + _gtk_css_parser_resync (scanner->parser, TRUE, 0); + return; + } - gtk_css_scanner_pop_scope (scanner); - g_scanner_get_next_token (scanner); + g_object_unref (file); +} - if (scanner->token != ';') - return ';'; +static void +parse_color_definition (GtkCssScanner *scanner) +{ + GtkSymbolicColor *symbolic; + char *name; - return G_TOKEN_NONE; - } - else if (strcmp (directive, "import") == 0) - { - gchar *path = NULL; - GFile *actual; - GError *error = NULL; + name = _gtk_css_parser_try_name (scanner->parser, TRUE); + if (name == NULL) + { + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "Not a valid color name"); + _gtk_css_parser_resync (scanner->parser, TRUE, 0); + return; + } - gtk_css_scanner_push_scope (scanner, SCOPE_VALUE); - g_scanner_get_next_token (scanner); + symbolic = _gtk_css_parser_read_symbolic_color (scanner->parser); + if (symbolic == NULL) + { + g_free (name); + _gtk_css_parser_resync (scanner->parser, TRUE, 0); + return; + } - if (scanner->token == G_TOKEN_IDENTIFIER && - g_str_has_prefix (scanner->value.v_identifier, "url")) - path = g_strstrip (scanner->value.v_identifier); - else if (scanner->token == G_TOKEN_STRING) - path = g_strstrip (scanner->value.v_string); - else - { - gtk_css_provider_invalid_token (css_provider, scanner, "File URL"); - return G_TOKEN_IDENTIFIER; - } + if (!_gtk_css_parser_try (scanner->parser, ";", TRUE)) + { + g_free (name); + gtk_symbolic_color_unref (symbolic); + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "Missing semicolon at end of color definition"); + _gtk_css_parser_resync (scanner->parser, TRUE, 0); + return; + } - actual = _gtk_css_parse_url (gtk_css_scanner_get_base_url (scanner), - path, - NULL, - &error); + g_hash_table_insert (scanner->provider->priv->symbolic_colors, name, symbolic); +} - if (actual == NULL) - { - gtk_css_provider_take_error (css_provider, scanner, error); - return G_TOKEN_IDENTIFIER; - } +static void +parse_binding_set (GtkCssScanner *scanner) +{ + GtkBindingSet *binding_set; + char *name; - gtk_css_scanner_pop_scope (scanner); - g_scanner_get_next_token (scanner); + name = _gtk_css_parser_try_ident (scanner->parser, TRUE); + if (name == NULL) + { + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "Expected name for binding set"); + _gtk_css_parser_resync (scanner->parser, TRUE, 0); + goto skip_semicolon; + } - if (scanner->token != ';') - { - g_object_unref (actual); - return ';'; - } + binding_set = gtk_binding_set_find (name); + if (!binding_set) + { + binding_set = gtk_binding_set_new (name); + binding_set->parsed = TRUE; + } + g_free (name); - if (gtk_css_scanner_would_recurse (scanner, actual)) - { - char *path = g_file_get_path (actual); - gtk_css_provider_error (css_provider, + if (!_gtk_css_parser_try (scanner->parser, "{", TRUE)) + { + gtk_css_provider_error_literal (scanner->provider, scanner, GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_IMPORT, - "Loading '%s' would recurse", - path); - g_free (path); - } - else - { - gtk_css_provider_load_internal (css_provider, - scanner, - actual, - NULL, 0, - NULL); - } - - g_object_unref (actual); + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "Expected '{' for binding set"); + _gtk_css_parser_resync (scanner->parser, TRUE, 0); + goto skip_semicolon; + } - return G_TOKEN_NONE; - } - else if (strcmp (directive, "binding-set") == 0) + while (!_gtk_css_parser_is_eof (scanner->parser) && + !_gtk_css_parser_begins_with (scanner->parser, '}')) + { + name = _gtk_css_parser_read_value (scanner->parser); + if (name == NULL) { - GtkBindingSet *binding_set; - gchar *binding_set_name; + _gtk_css_parser_resync (scanner->parser, TRUE, '}'); + continue; + } - g_scanner_get_next_token (scanner); + gtk_binding_entry_add_signal_from_string (binding_set, name); + g_free (name); - if (scanner->token != G_TOKEN_IDENTIFIER) + if (!_gtk_css_parser_try (scanner->parser, ";", TRUE)) + { + if (!_gtk_css_parser_begins_with (scanner->parser, '}') && + !_gtk_css_parser_is_eof (scanner->parser)) { - gtk_css_provider_invalid_token (css_provider, scanner, "Binding name"); - return G_TOKEN_IDENTIFIER; + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "Expected semicolon"); + _gtk_css_parser_resync (scanner->parser, TRUE, '}'); } + } + } - binding_set_name = scanner->value.v_identifier; - binding_set = gtk_binding_set_find (binding_set_name); + if (!_gtk_css_parser_try (scanner->parser, "}", TRUE)) + { + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "expected '}' after declarations"); + if (!_gtk_css_parser_is_eof (scanner->parser)) + _gtk_css_parser_resync (scanner->parser, FALSE, 0); + } - if (!binding_set) - { - binding_set = gtk_binding_set_new (binding_set_name); - binding_set->parsed = TRUE; - } +skip_semicolon: + if (_gtk_css_parser_try (scanner->parser, ";", TRUE)) + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_DEPRECATED, + "Nonstandard semicolon at end of binding set"); +} - g_scanner_get_next_token (scanner); +static void +parse_at_keyword (GtkCssScanner *scanner) +{ + if (_gtk_css_parser_try (scanner->parser, "@import", TRUE)) + parse_import (scanner); + else if (_gtk_css_parser_try (scanner->parser, "@define-color", TRUE)) + parse_color_definition (scanner); + else if (_gtk_css_parser_try (scanner->parser, "@binding-set", TRUE)) + parse_binding_set (scanner); + else + { + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "unknown @ rule"); + _gtk_css_parser_resync (scanner->parser, TRUE, 0); + } +} - if (scanner->token != G_TOKEN_LEFT_CURLY) - return G_TOKEN_LEFT_CURLY; +static gboolean +parse_selector_pseudo_class (GtkCssScanner *scanner, SelectorPath *path) +{ + struct { + const char *name; + GtkStateFlags flag; + } classes[] = { + { "active", GTK_STATE_FLAG_ACTIVE }, + { "prelight", GTK_STATE_FLAG_PRELIGHT }, + { "hover", GTK_STATE_FLAG_PRELIGHT }, + { "selected", GTK_STATE_FLAG_SELECTED }, + { "insensitive", GTK_STATE_FLAG_INSENSITIVE }, + { "inconsistent", GTK_STATE_FLAG_INCONSISTENT }, + { "focused", GTK_STATE_FLAG_FOCUSED }, + { "focus", GTK_STATE_FLAG_FOCUSED } + }; + guint i; - gtk_css_scanner_push_scope (scanner, SCOPE_BINDING_SET); - g_scanner_get_next_token (scanner); + for (i = 0; i < G_N_ELEMENTS (classes); i++) + { + if (_gtk_css_parser_try (scanner->parser, classes[i].name, FALSE)) + { + path->state |= classes[i].flag; + return TRUE; + } + } - do - { - GTokenType ret; + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "Expected a valid state name"); + return FALSE; +} - if (scanner->token != G_TOKEN_IDENTIFIER) - { - gtk_css_provider_invalid_token (css_provider, scanner, "Binding definition"); - return G_TOKEN_IDENTIFIER; - } +static gboolean +parse_selector_class (GtkCssScanner *scanner, SelectorPath *path) +{ + char *name = _gtk_css_parser_try_name (scanner->parser, FALSE); - ret = gtk_binding_entry_add_signal_from_string (binding_set, - scanner->value.v_identifier); - if (ret != G_TOKEN_NONE) - { - gtk_css_provider_invalid_token (css_provider, scanner, "Binding definition"); - return ret; - } + if (name == NULL) + { + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "Expected a valid name"); + return FALSE; + } - g_scanner_get_next_token (scanner); + selector_path_prepend_combinator (path, COMBINATOR_CHILD); + selector_path_prepend_class (path, name); + return TRUE; +} - if (scanner->token != ';') - return ';'; +static gboolean +parse_selector_name (GtkCssScanner *scanner, SelectorPath *path) +{ + char *name = _gtk_css_parser_try_name (scanner->parser, FALSE); - g_scanner_get_next_token (scanner); - } - while (scanner->token != G_TOKEN_RIGHT_CURLY); + if (name == NULL) + { + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "Expected a valid name"); + return FALSE; + } - gtk_css_scanner_pop_scope (scanner); - g_scanner_get_next_token (scanner); + selector_path_prepend_combinator (path, COMBINATOR_CHILD); + selector_path_prepend_name (path, name); + return TRUE; +} - return G_TOKEN_NONE; +static gboolean +parse_selector_pseudo_class_for_region (GtkCssScanner *scanner, + SelectorPath *path, + GtkRegionFlags *flags_to_modify) +{ + struct { + const char *name; + GtkRegionFlags flag; + } classes[] = { + { "first", GTK_REGION_FIRST }, + { "last", GTK_REGION_LAST }, + { "sorted", GTK_REGION_SORTED } + }, nth_child[] = { + { "first", GTK_REGION_FIRST }, + { "last", GTK_REGION_LAST }, + { "even", GTK_REGION_EVEN }, + { "odd", GTK_REGION_ODD } + }; + guint i; + + for (i = 0; i < G_N_ELEMENTS (classes); i++) + { + if (_gtk_css_parser_try (scanner->parser, classes[i].name, FALSE)) + { + *flags_to_modify |=classes[i].flag; + return TRUE; } - else + } + + if (!_gtk_css_parser_try (scanner->parser, "nth-child(", TRUE)) + return parse_selector_pseudo_class (scanner, path); + + for (i = 0; i < G_N_ELEMENTS (nth_child); i++) + { + if (_gtk_css_parser_try (scanner->parser, nth_child[i].name, TRUE)) { - gtk_css_provider_invalid_token (css_provider, scanner, "Directive"); - return G_TOKEN_IDENTIFIER; + *flags_to_modify |= nth_child[i].flag; + break; } } - expected_token = parse_selector (css_provider, scanner, &selector); + if (i == G_N_ELEMENTS (nth_child)) + { + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "Not a valid value for nth-child"); + return FALSE; + } - if (expected_token != G_TOKEN_NONE) + if (!_gtk_css_parser_try (scanner->parser, ")", FALSE)) { - selector_path_unref (selector); - gtk_css_provider_invalid_token (css_provider, scanner, "Selector"); - return expected_token; + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "Missing closing bracket"); + return FALSE; } - priv->cur_selectors = g_slist_prepend (priv->cur_selectors, selector); + return TRUE; +} - while (scanner->token == ',') +static gboolean +parse_simple_selector (GtkCssScanner *scanner, SelectorPath *path) +{ + char *name; + gboolean parsed_something; + + name = _gtk_css_parser_try_ident (scanner->parser, FALSE); + if (name) { - g_scanner_get_next_token (scanner); + if (_gtk_style_context_check_region_name (name)) + { + GtkRegionFlags flags; + + flags = 0; - expected_token = parse_selector (css_provider, scanner, &selector); + while (_gtk_css_parser_try (scanner->parser, ":", FALSE)) + { + if (!parse_selector_pseudo_class_for_region (scanner, path, &flags)) + { + g_free (name); + return FALSE; + } + } - if (expected_token != G_TOKEN_NONE) + selector_path_prepend_region (path, name, flags); + g_free (name); + _gtk_css_parser_skip_whitespace (scanner->parser); + return TRUE; + } + else { - selector_path_unref (selector); - gtk_css_provider_invalid_token (css_provider, scanner, "Selector"); - return expected_token; + selector_path_prepend_type (path, name); + parsed_something = TRUE; } - - priv->cur_selectors = g_slist_prepend (priv->cur_selectors, selector); } - - gtk_css_scanner_pop_scope (scanner); - - if (scanner->token != G_TOKEN_LEFT_CURLY) - return G_TOKEN_LEFT_CURLY; - - /* Declarations parsing */ - gtk_css_scanner_push_scope (scanner, SCOPE_DECLARATION); - - g_scanner_get_next_token (scanner); - - while (scanner->token != G_TOKEN_RIGHT_CURLY && - !g_scanner_eof (scanner)) + else { - gchar *value_str = NULL; - GtkStylePropertyParser parse_func = NULL; - GParamSpec *pspec = NULL;; - gchar *prop; + parsed_something = _gtk_css_parser_try (scanner->parser, "*", FALSE); + selector_path_prepend_glob (path); + } - if (scanner->token == ';') + do { + if (_gtk_css_parser_try (scanner->parser, "#", FALSE)) { - g_scanner_get_next_token (scanner); - continue; + if (!parse_selector_name (scanner, path)) + return FALSE; } - - if (scanner->token != G_TOKEN_IDENTIFIER) + else if (_gtk_css_parser_try (scanner->parser, ".", FALSE)) + { + if (!parse_selector_class (scanner, path)) + return FALSE; + } + else if (_gtk_css_parser_try (scanner->parser, ":", FALSE)) + { + if (!parse_selector_pseudo_class (scanner, path)) + return FALSE; + } + else if (!parsed_something) { - gtk_css_provider_error_literal (css_provider, + gtk_css_provider_error_literal (scanner->provider, scanner, GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_NAME, - "Expected a valid property name"); - goto find_end_of_declaration; + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "Expected a valid selector"); + return FALSE; } + else + break; - prop = g_strdup (scanner->value.v_identifier); + parsed_something = TRUE; + } + while (!_gtk_css_parser_is_eof (scanner->parser)); - if (!gtk_style_properties_lookup_property (prop, &parse_func, &pspec) && - prop[0] != '-') - { - gtk_css_provider_error (css_provider, - scanner, - GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_NAME, - "'%s' is not a valid property name", - prop); - g_free (prop); - goto find_end_of_declaration; - } + _gtk_css_parser_skip_whitespace (scanner->parser); + return TRUE; +} - g_scanner_get_next_token (scanner); +static SelectorPath * +parse_selector (GtkCssScanner *scanner) +{ + SelectorPath *path; - if (scanner->token != ':') + path = selector_path_new (); + + do { + if (!parse_simple_selector (scanner, path)) { - g_free (prop); - gtk_css_provider_invalid_token (css_provider, scanner, "':'"); - goto find_end_of_declaration; + selector_path_unref (path); + return NULL; } - gtk_css_scanner_push_scope (scanner, SCOPE_VALUE); - g_scanner_get_next_token (scanner); + if (_gtk_css_parser_try (scanner->parser, ">", TRUE)) + selector_path_prepend_combinator (path, COMBINATOR_CHILD); + } + while (path->state == 0 && + !_gtk_css_parser_is_eof (scanner->parser) && + !_gtk_css_parser_begins_with (scanner->parser, ',') && + !_gtk_css_parser_begins_with (scanner->parser, '{')); - if (scanner->token != G_TOKEN_IDENTIFIER) - { - g_free (prop); - /* the error value here is hacky. But strings should be used for - * strings really, so a string is not a syntax error but a broken - * value for everything that we support. */ - gtk_css_provider_error (css_provider, - scanner, - GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_SYNTAX, - "Not a property value"); - gtk_css_scanner_pop_scope (scanner); - goto find_end_of_declaration; - } + return path; +} - value_str = scanner->value.v_identifier; - g_strchomp (value_str); +static GSList * +parse_selector_list (GtkCssScanner *scanner) +{ + GSList *selectors = NULL; - gtk_css_scanner_pop_scope (scanner); - g_scanner_peek_next_token (scanner); + do { + SelectorPath *path = parse_selector (scanner); - if (scanner->next_token != ';' && - scanner->next_token != G_TOKEN_RIGHT_CURLY && - scanner->next_token != G_TOKEN_EOF) + if (path == NULL) { - gtk_css_provider_invalid_token (css_provider, scanner, "';'"); - g_free (prop); - goto find_end_of_declaration; + g_slist_free_full (selectors, (GDestroyNotify) selector_path_unref); + _gtk_css_parser_resync (scanner->parser, FALSE, 0); + return NULL; } - if (pspec) - { - GValue *val; + selectors = g_slist_prepend (selectors, path); + } + while (_gtk_css_parser_try (scanner->parser, ",", TRUE)); - val = g_slice_new0 (GValue); - g_value_init (val, pspec->value_type); + return selectors; +} - if (strcmp (value_str, "none") == 0) - { - /* Insert the default value, so it has an opportunity - * to override other style providers when merged - */ - g_param_value_set_default (pspec, val); - g_hash_table_insert (priv->cur_properties, prop, val); - } - else if (strcmp (prop, "gtk-key-bindings") == 0) - { - /* Private property holding the binding sets */ - resolve_binding_sets (value_str, val); - g_hash_table_insert (priv->cur_properties, prop, val); - } - else if (parse_func) +static void +parse_declaration (GtkCssScanner *scanner, GHashTable *properties) +{ + GtkStylePropertyParser parse_func = NULL; + GParamSpec *pspec = NULL; + char *name, *value_str; + + name = _gtk_css_parser_try_ident (scanner->parser, TRUE); + if (name == NULL) + goto check_for_semicolon; + + if (!gtk_style_properties_lookup_property (name, &parse_func, &pspec) && + name[0] != '-') + { + gtk_css_provider_error (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_NAME, + "'%s' is not a valid property name", + name); + _gtk_css_parser_resync (scanner->parser, TRUE, '}'); + g_free (name); + return; + } + + if (!_gtk_css_parser_try (scanner->parser, ":", TRUE)) + { + gtk_css_provider_invalid_token (scanner->provider, scanner, "':'"); + _gtk_css_parser_resync (scanner->parser, TRUE, '}'); + g_free (name); + 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; + + val = g_slice_new0 (GValue); + g_value_init (val, pspec->value_type); + + if (strcmp (value_str, "none") == 0) + { + /* Insert the default value, so it has an opportunity + * to override other style providers when merged + */ + g_param_value_set_default (pspec, val); + g_hash_table_insert (properties, name, val); + } + else if (strcmp (name, "gtk-key-bindings") == 0) + { + /* Private property holding the binding sets */ + resolve_binding_sets (value_str, val); + g_hash_table_insert (properties, name, val); + } + else if (parse_func) + { + GError *error = NULL; + + if ((*parse_func) (value_str, val, &error)) + g_hash_table_insert (properties, name, val); + else + gtk_css_provider_take_error (scanner->provider, scanner, error); + } + else + { + GError *error = NULL; + + if (_gtk_css_value_from_string (val, + gtk_css_scanner_get_base_url (scanner), + value_str, + &error)) { - GError *error = NULL; - - if ((*parse_func) (value_str, val, &error)) - g_hash_table_insert (priv->cur_properties, prop, val); - else - gtk_css_provider_take_error (css_provider, scanner, error); + g_hash_table_insert (properties, name, val); } else { - GError *error = NULL; - - if (_gtk_css_value_from_string (val, - gtk_css_scanner_get_base_url (scanner), - value_str, - &error)) - { - g_hash_table_insert (priv->cur_properties, prop, val); - } - else - { - g_value_unset (val); - g_slice_free (GValue, val); - g_free (prop); + g_value_unset (val); + g_slice_free (GValue, val); + g_free (name); - gtk_css_provider_take_error (css_provider, scanner, error); - } + gtk_css_provider_take_error (scanner->provider, scanner, error); } } - else if (prop[0] == '-') - { - GValue *val; + } + else if (name[0] == '-') + { + GValue *val; - val = g_slice_new0 (GValue); - g_value_init (val, G_TYPE_STRING); - g_value_set_string (val, value_str); + val = g_slice_new0 (GValue); + g_value_init (val, G_TYPE_STRING); + g_value_set_string (val, value_str); - g_hash_table_insert (priv->cur_properties, prop, val); - } - else - g_free (prop); + g_hash_table_insert (properties, name, val); + } + else + g_free (name); - g_scanner_get_next_token (scanner); + g_free (value_str); - if (g_scanner_eof (scanner)) +check_for_semicolon: + if (!_gtk_css_parser_try (scanner->parser, ";", TRUE)) + { + if (!_gtk_css_parser_begins_with (scanner->parser, '}') && + !_gtk_css_parser_is_eof (scanner->parser)) { - gtk_css_provider_invalid_token (css_provider, scanner, "}"); - break; + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "Expected semicolon"); + _gtk_css_parser_resync (scanner->parser, TRUE, '}'); } - -find_end_of_declaration: - while (scanner->token != ';' && - scanner->token != G_TOKEN_RIGHT_CURLY && - !g_scanner_eof (scanner)) - g_scanner_get_next_token (scanner); } +} - gtk_css_scanner_pop_scope (scanner); +static GHashTable * +parse_declarations (GtkCssScanner *scanner) +{ + GHashTable *properties; - return G_TOKEN_NONE; + properties = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) property_value_free); + + while (!_gtk_css_parser_is_eof (scanner->parser) && + !_gtk_css_parser_begins_with (scanner->parser, '}')) + { + parse_declaration (scanner, properties); + } + + return properties; } static void -gtk_css_provider_reset (GtkCssProvider *css_provider) +parse_ruleset (GtkCssScanner *scanner) { - GtkCssProviderPrivate *priv; + GSList *selectors; + GHashTable *properties; - priv = css_provider->priv; + selectors = parse_selector_list (scanner); + if (selectors == NULL) + return; - if (priv->selectors_info->len > 0) - g_ptr_array_remove_range (priv->selectors_info, 0, priv->selectors_info->len); + if (!_gtk_css_parser_try (scanner->parser, "{", TRUE)) + { + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "expected '{' after selectors"); + _gtk_css_parser_resync (scanner->parser, FALSE, 0); + g_slist_free_full (selectors, (GDestroyNotify) selector_path_unref); + return; + } + + properties = parse_declarations (scanner); + + if (!_gtk_css_parser_try (scanner->parser, "}", TRUE)) + { + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "expected '}' after declarations"); + if (!_gtk_css_parser_is_eof (scanner->parser)) + { + _gtk_css_parser_resync (scanner->parser, FALSE, 0); + g_slist_free_full (selectors, (GDestroyNotify) selector_path_unref); + if (properties) + g_hash_table_unref (properties); + return; + } + } + + if (properties) + { + css_provider_commit (scanner->provider, selectors, properties); + g_hash_table_unref (properties); + } + g_slist_free_full (selectors, (GDestroyNotify) selector_path_unref); } static void -gtk_css_provider_propagate_error (GtkCssProvider *provider, - const gchar *path, - guint line, - guint position, - const GError *error, - GError **propagate_to) +parse_statement (GtkCssScanner *scanner) { - /* we already set an error. And we'd like to keep the first one */ - if (*propagate_to) - return; - - *propagate_to = g_error_copy (error); - g_prefix_error (propagate_to, "%s:%u:%u: ", path ? path : "", line, position); + if (_gtk_css_parser_begins_with (scanner->parser, '@')) + parse_at_keyword (scanner); + else + parse_ruleset (scanner); } static void -parse_stylesheet (GtkCssProvider *css_provider, - GScanner *scanner) +parse_stylesheet (GtkCssScanner *scanner) { - g_scanner_get_next_token (scanner); + _gtk_css_parser_skip_whitespace (scanner->parser); - while (!g_scanner_eof (scanner)) + while (!_gtk_css_parser_is_eof (scanner->parser)) { - GTokenType expected_token; - - expected_token = parse_rule (css_provider, scanner); - - if (expected_token != G_TOKEN_NONE) - { - while (!g_scanner_eof (scanner) && - scanner->token != G_TOKEN_RIGHT_CURLY) - g_scanner_get_next_token (scanner); - } - else - css_provider_commit (css_provider, scanner); + if (_gtk_css_parser_try (scanner->parser, "", TRUE)) + continue; - g_scanner_get_next_token (scanner); - - gtk_css_scanner_reset (scanner); + parse_statement (scanner); } } static gboolean gtk_css_provider_load_internal (GtkCssProvider *css_provider, - GScanner *parent, + GtkCssScanner *parent, GFile *file, const char *data, gsize length, GError **error) { - GScanner *scanner; + GtkCssScanner *scanner; gulong error_handler; char *free_data; @@ -2712,9 +2854,9 @@ gtk_css_provider_load_internal (GtkCssProvider *css_provider, if (data) { - scanner = gtk_css_scanner_new (parent, file, data, length); + scanner = gtk_css_scanner_new (css_provider, parent, file, data, length); - parse_stylesheet (css_provider, scanner); + parse_stylesheet (scanner); gtk_css_scanner_destroy (scanner); } diff --git a/gtk/gtkcssprovider.h b/gtk/gtkcssprovider.h index 65292dfce..b49640c8c 100644 --- a/gtk/gtkcssprovider.h +++ b/gtk/gtkcssprovider.h @@ -37,15 +37,9 @@ typedef enum { GTK_CSS_PROVIDER_ERROR_FAILED, GTK_CSS_PROVIDER_ERROR_SYNTAX, - GTK_CSS_PROVIDER_ERROR_PROPERTY_NAME, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, - GTK_CSS_PROVIDER_ERROR_SELECTOR, - GTK_CSS_PROVIDER_ERROR_COMBINATOR, - GTK_CSS_PROVIDER_ERROR_CLASS, - GTK_CSS_PROVIDER_ERROR_PSEUDO_CLASS, - GTK_CSS_PROVIDER_ERROR_AT_RULE, GTK_CSS_PROVIDER_ERROR_IMPORT, - GTK_CSS_PROVIDER_ERROR_DEFINE_COLOR + GTK_CSS_PROVIDER_ERROR_NAME, + GTK_CSS_PROVIDER_ERROR_DEPRECATED } GtkCssProviderError; GQuark gtk_css_provider_error_quark (void); diff --git a/gtk/gtkcssstringfuncs.c b/gtk/gtkcssstringfuncs.c index f36e94215..53a5cccd6 100644 --- a/gtk/gtkcssstringfuncs.c +++ b/gtk/gtkcssstringfuncs.c @@ -29,6 +29,7 @@ #include #include "gtkcssprovider.h" +#include "gtkcssparserprivate.h" /* the actual parsers we have */ #include "gtkanimationdescription.h" @@ -62,7 +63,7 @@ set_default_error (GError **error, { g_set_error (error, GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, + GTK_CSS_PROVIDER_ERROR_SYNTAX, "Could not convert property value to type '%s'", g_type_name (type)); return FALSE; @@ -73,257 +74,35 @@ set_default_error (GError **error, #define SKIP_SPACES(s) while (g_ascii_isspace (*(s))) (s)++ #define SKIP_SPACES_BACK(s) while (g_ascii_isspace (*(s))) (s)-- -static GtkSymbolicColor * -symbolic_color_parse_str (const gchar *string, - gchar **end_ptr) +static void +propagate_parser_error (GtkCssParser *parser, + const GError *error, + gpointer user_data) { - GtkSymbolicColor *symbolic_color = NULL; - gchar *str; - - str = (gchar *) string; - *end_ptr = str; - - if (str[0] == '@') - { - const gchar *end; - gchar *name; - - str++; - end = str; - - while (*end == '-' || *end == '_' || g_ascii_isalnum (*end)) - end++; - - name = g_strndup (str, end - str); - symbolic_color = gtk_symbolic_color_new_name (name); - g_free (name); - - *end_ptr = (gchar *) end; - } - else if (g_str_has_prefix (str, "lighter") || - g_str_has_prefix (str, "darker")) - { - GtkSymbolicColor *param_color; - gboolean is_lighter = FALSE; - - is_lighter = g_str_has_prefix (str, "lighter"); - - if (is_lighter) - str += strlen ("lighter"); - else - str += strlen ("darker"); - - SKIP_SPACES (str); - - if (*str != '(') - { - *end_ptr = (gchar *) str; - return NULL; - } - - str++; - SKIP_SPACES (str); - param_color = symbolic_color_parse_str (str, end_ptr); - - if (!param_color) - return NULL; - - str = *end_ptr; - SKIP_SPACES (str); - *end_ptr = (gchar *) str; - - if (*str != ')') - { - gtk_symbolic_color_unref (param_color); - return NULL; - } - - if (is_lighter) - symbolic_color = gtk_symbolic_color_new_shade (param_color, 1.3); - else - symbolic_color = gtk_symbolic_color_new_shade (param_color, 0.7); - - gtk_symbolic_color_unref (param_color); - (*end_ptr)++; - } - else if (g_str_has_prefix (str, "shade") || - g_str_has_prefix (str, "alpha")) - { - GtkSymbolicColor *param_color; - gboolean is_shade = FALSE; - gdouble factor; - - is_shade = g_str_has_prefix (str, "shade"); - - if (is_shade) - str += strlen ("shade"); - else - str += strlen ("alpha"); - - SKIP_SPACES (str); - - if (*str != '(') - { - *end_ptr = (gchar *) str; - return NULL; - } - - str++; - SKIP_SPACES (str); - param_color = symbolic_color_parse_str (str, end_ptr); - - if (!param_color) - return NULL; - - str = *end_ptr; - SKIP_SPACES (str); - - if (str[0] != ',') - { - gtk_symbolic_color_unref (param_color); - *end_ptr = (gchar *) str; - return NULL; - } - - str++; - SKIP_SPACES (str); - factor = g_ascii_strtod (str, end_ptr); - - str = *end_ptr; - SKIP_SPACES (str); - *end_ptr = (gchar *) str; - - if (str[0] != ')') - { - gtk_symbolic_color_unref (param_color); - return NULL; - } - - if (is_shade) - symbolic_color = gtk_symbolic_color_new_shade (param_color, factor); - else - symbolic_color = gtk_symbolic_color_new_alpha (param_color, factor); - - gtk_symbolic_color_unref (param_color); - (*end_ptr)++; - } - else if (g_str_has_prefix (str, "mix")) - { - GtkSymbolicColor *color1, *color2; - gdouble factor; - - str += strlen ("mix"); - SKIP_SPACES (str); - - if (*str != '(') - { - *end_ptr = (gchar *) str; - return NULL; - } - - str++; - SKIP_SPACES (str); - color1 = symbolic_color_parse_str (str, end_ptr); - - if (!color1) - return NULL; + GError **propagate_here = user_data; - str = *end_ptr; - SKIP_SPACES (str); - - if (str[0] != ',') - { - gtk_symbolic_color_unref (color1); - *end_ptr = (gchar *) str; - return NULL; - } - - str++; - SKIP_SPACES (str); - color2 = symbolic_color_parse_str (str, end_ptr); - - if (!color2 || *end_ptr[0] != ',') - { - gtk_symbolic_color_unref (color1); - return NULL; - } - - str = *end_ptr; - SKIP_SPACES (str); - - if (str[0] != ',') - { - gtk_symbolic_color_unref (color1); - gtk_symbolic_color_unref (color2); - *end_ptr = (gchar *) str; - return NULL; - } - - str++; - SKIP_SPACES (str); - factor = g_ascii_strtod (str, end_ptr); - - str = *end_ptr; - SKIP_SPACES (str); - *end_ptr = (gchar *) str; - - if (str[0] != ')') - { - gtk_symbolic_color_unref (color1); - gtk_symbolic_color_unref (color2); - return NULL; - } - - symbolic_color = gtk_symbolic_color_new_mix (color1, color2, factor); - gtk_symbolic_color_unref (color1); - gtk_symbolic_color_unref (color2); - (*end_ptr)++; - } - else - { - GdkRGBA color; - gchar *color_str; - const gchar *end; - - end = str + 1; - - if (str[0] == '#') - { - /* Color in hex format */ - while (g_ascii_isxdigit (*end)) - end++; - } - else if (g_str_has_prefix (str, "rgb")) - { - /* color in rgb/rgba format */ - while (*end != ')' && *end != '\0') - end++; - - if (*end == ')') - end++; - } - else - { - /* Color name */ - while (*end != '\0' && - (g_ascii_isalnum (*end) || *end == ' ')) - end++; - } + if (propagate_here == NULL) + return; - color_str = g_strndup (str, end - str); - *end_ptr = (gchar *) end; + /* only copy the first error */ + if (*propagate_here == NULL) + *propagate_here = g_error_copy (error); +} - if (!gdk_rgba_parse (&color, color_str)) - { - g_free (color_str); - return NULL; - } +static GtkSymbolicColor * +_gtk_css_parse_symbolic_color (const char *str, + GError **error) +{ + GtkSymbolicColor *symbolic; + GtkCssParser *parser; - symbolic_color = gtk_symbolic_color_new_literal (&color); - g_free (color_str); - } + 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_color; + return symbolic; } static gboolean @@ -489,6 +268,9 @@ int_value_from_string (const char *str, gint64 i; char *end; + if (*str == '+') + return set_default_error (error, G_VALUE_TYPE (value)); + i = g_ascii_strtoll (str, &end, 10); if (*end != '\0') @@ -498,7 +280,7 @@ int_value_from_string (const char *str, { g_set_error_literal (error, GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, + GTK_CSS_PROVIDER_ERROR_SYNTAX, "Number too big"); return FALSE; } @@ -522,6 +304,9 @@ uint_value_from_string (const char *str, guint64 u; char *end; + if (*str == '+') + return set_default_error (error, G_VALUE_TYPE (value)); + u = g_ascii_strtoull (str, &end, 10); if (*end != '\0') @@ -531,7 +316,7 @@ uint_value_from_string (const char *str, { g_set_error_literal (error, GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, + GTK_CSS_PROVIDER_ERROR_SYNTAX, "Number too big"); return FALSE; } @@ -564,7 +349,7 @@ double_value_from_string (const char *str, { g_set_error_literal (error, GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, + GTK_CSS_PROVIDER_ERROR_SYNTAX, "Number not representable"); return FALSE; } @@ -601,7 +386,7 @@ float_value_from_string (const char *str, { g_set_error_literal (error, GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, + GTK_CSS_PROVIDER_ERROR_SYNTAX, "Number not representable"); return FALSE; } @@ -634,7 +419,7 @@ gtk_css_string_unescape (const char *string, { g_set_error_literal (error, GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, + GTK_CSS_PROVIDER_ERROR_SYNTAX, "String value not properly quoted."); return NULL; } @@ -659,7 +444,7 @@ gtk_css_string_unescape (const char *string, { g_set_error_literal (error, GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, + GTK_CSS_PROVIDER_ERROR_SYNTAX, "FIXME: Implement unicode escape sequences."); g_string_free (str, TRUE); return NULL; @@ -685,7 +470,7 @@ gtk_css_string_unescape (const char *string, { g_set_error_literal (error, GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, + GTK_CSS_PROVIDER_ERROR_SYNTAX, "Junk after end of string."); g_string_free (str, TRUE); return NULL; @@ -695,14 +480,14 @@ gtk_css_string_unescape (const char *string, case '\0': g_set_error_literal (error, GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, + 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_PROPERTY_VALUE, + GTK_CSS_PROVIDER_ERROR_SYNTAX, "Invalid character in string. Must be escaped."); g_string_free (str, TRUE); return NULL; @@ -783,7 +568,7 @@ theming_engine_value_from_string (const char *str, { g_set_error (error, GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, + GTK_CSS_PROVIDER_ERROR_SYNTAX, "Themeing engine '%s' not found", str); return FALSE; } @@ -850,7 +635,7 @@ parse_border_value (const char *str, { g_set_error_literal (error, GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, + GTK_CSS_PROVIDER_ERROR_SYNTAX, "Number out of range for border"); return FALSE; } @@ -859,7 +644,7 @@ parse_border_value (const char *str, { g_set_error_literal (error, GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, + GTK_CSS_PROVIDER_ERROR_SYNTAX, "No number given for border value"); return FALSE; } @@ -875,7 +660,7 @@ parse_border_value (const char *str, { g_set_error_literal (error, GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, + GTK_CSS_PROVIDER_ERROR_SYNTAX, "Junk at end of border value"); return FALSE; } @@ -921,7 +706,7 @@ border_value_from_string (const char *str, { g_set_error_literal (error, GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, + GTK_CSS_PROVIDER_ERROR_SYNTAX, "Junk at end of border value"); return FALSE; } @@ -947,143 +732,92 @@ border_value_to_string (const GValue *value) return g_strdup_printf ("%d", border->top); } -static gboolean -gradient_value_from_string (const char *str, - GFile *base, - GValue *value, - GError **error) +static GtkGradient * +_gtk_css_parse_gradient (GtkCssParser *parser) { GtkGradient *gradient; cairo_pattern_type_t type; gdouble coords[6]; - gchar *end; guint i; - str += strlen ("-gtk-gradient"); - SKIP_SPACES (str); - - if (*str != '(') + if (!_gtk_css_parser_try (parser, "-gtk-gradient", TRUE)) { - g_set_error_literal (error, - GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, - "Expected '(' after '-gtk-gradient'"); - return FALSE; + _gtk_css_parser_error (parser, + "Expected '-gtk-gradient'"); + return NULL; } - str++; - SKIP_SPACES (str); - - /* Parse gradient type */ - if (g_str_has_prefix (str, "linear")) - { - type = CAIRO_PATTERN_TYPE_LINEAR; - str += strlen ("linear"); - } - else if (g_str_has_prefix (str, "radial")) + if (!_gtk_css_parser_try (parser, "(", TRUE)) { - type = CAIRO_PATTERN_TYPE_RADIAL; - str += strlen ("radial"); + _gtk_css_parser_error (parser, + "Expected '(' after '-gtk-gradient'"); + return NULL; } + + /* Parse gradient type */ + if (_gtk_css_parser_try (parser, "linear", TRUE)) + type = CAIRO_PATTERN_TYPE_LINEAR; + else if (_gtk_css_parser_try (parser, "radial", TRUE)) + type = CAIRO_PATTERN_TYPE_RADIAL; else { - g_set_error_literal (error, - GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, - "Gradient type must be 'radial' or 'linear'"); - return FALSE; + _gtk_css_parser_error (parser, + "Gradient type must be 'radial' or 'linear'"); + return NULL; } - SKIP_SPACES (str); - /* Parse start/stop position parameters */ for (i = 0; i < 2; i++) { - if (*str != ',') + if (! _gtk_css_parser_try (parser, ",", TRUE)) { - g_set_error_literal (error, - GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, - "Expected ','"); - return FALSE; + _gtk_css_parser_error (parser, + "Expected ','"); + return NULL; } - str++; - SKIP_SPACES (str); - - if (strncmp (str, "left", 4) == 0) - { - coords[i * 3] = 0; - str += strlen ("left"); - } - else if (strncmp (str, "right", 5) == 0) - { - coords[i * 3] = 1; - str += strlen ("right"); - } - else if (strncmp (str, "center", 6) == 0) - { - coords[i * 3] = 0.5; - str += strlen ("center"); - } - else + if (_gtk_css_parser_try (parser, "left", TRUE)) + coords[i * 3] = 0; + else if (_gtk_css_parser_try (parser, "right", TRUE)) + coords[i * 3] = 1; + else if (_gtk_css_parser_try (parser, "center", TRUE)) + coords[i * 3] = 0.5; + else if (!_gtk_css_parser_try_double (parser, &coords[i * 3])) { - coords[i * 3] = g_ascii_strtod (str, &end); - - if (str == end) - return set_default_error (error, G_VALUE_TYPE (value)); - - str = end; + _gtk_css_parser_error (parser, + "Expected a valid X coordinate"); + return NULL; } - SKIP_SPACES (str); - - if (strncmp (str, "top", 3) == 0) - { - coords[(i * 3) + 1] = 0; - str += strlen ("top"); - } - else if (strncmp (str, "bottom", 6) == 0) - { - coords[(i * 3) + 1] = 1; - str += strlen ("bottom"); - } - else if (strncmp (str, "center", 6) == 0) - { - coords[(i * 3) + 1] = 0.5; - str += strlen ("center"); - } - else + if (_gtk_css_parser_try (parser, "top", TRUE)) + coords[i * 3 + 1] = 0; + else if (_gtk_css_parser_try (parser, "bottom", TRUE)) + coords[i * 3 + 1] = 1; + else if (_gtk_css_parser_try (parser, "center", TRUE)) + coords[i * 3 + 1] = 0.5; + else if (!_gtk_css_parser_try_double (parser, &coords[i * 3 + 1])) { - coords[(i * 3) + 1] = g_ascii_strtod (str, &end); - - if (str == end) - return set_default_error (error, G_VALUE_TYPE (value)); - - str = end; + _gtk_css_parser_error (parser, + "Expected a valid Y coordinate"); + return NULL; } - SKIP_SPACES (str); - if (type == CAIRO_PATTERN_TYPE_RADIAL) { /* Parse radius */ - if (*str != ',') + if (! _gtk_css_parser_try (parser, ",", TRUE)) { - g_set_error_literal (error, - GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, - "Expected ','"); - return FALSE; + _gtk_css_parser_error (parser, + "Expected ','"); + return NULL; } - str++; - SKIP_SPACES (str); - - coords[(i * 3) + 2] = g_ascii_strtod (str, &end); - str = end; - - SKIP_SPACES (str); + if (! _gtk_css_parser_try_double (parser, &coords[(i * 3) + 2])) + { + _gtk_css_parser_error (parser, + "Expected a numer for the radius"); + return NULL; + } } } @@ -1093,102 +827,120 @@ gradient_value_from_string (const char *str, gradient = gtk_gradient_new_radial (coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]); - while (*str == ',') + while (_gtk_css_parser_try (parser, ",", TRUE)) { GtkSymbolicColor *color; gdouble position; - str++; - SKIP_SPACES (str); - - if (g_str_has_prefix (str, "from")) + if (_gtk_css_parser_try (parser, "from", TRUE)) { position = 0; - str += strlen ("from"); - SKIP_SPACES (str); - if (*str != '(') + if (!_gtk_css_parser_try (parser, "(", TRUE)) { - g_object_unref (gradient); - return set_default_error (error, G_VALUE_TYPE (value)); + gtk_gradient_unref (gradient); + _gtk_css_parser_error (parser, + "Expected '('"); + return NULL; } + } - else if (g_str_has_prefix (str, "to")) + else if (_gtk_css_parser_try (parser, "to", TRUE)) { position = 1; - str += strlen ("to"); - SKIP_SPACES (str); - if (*str != '(') + if (!_gtk_css_parser_try (parser, "(", TRUE)) { - g_object_unref (gradient); - return set_default_error (error, G_VALUE_TYPE (value)); + gtk_gradient_unref (gradient); + _gtk_css_parser_error (parser, + "Expected '('"); + return NULL; } + } - else if (g_str_has_prefix (str, "color-stop")) + else if (_gtk_css_parser_try (parser, "color-stop", TRUE)) { - str += strlen ("color-stop"); - SKIP_SPACES (str); - - if (*str != '(') + if (!_gtk_css_parser_try (parser, "(", TRUE)) { - g_object_unref (gradient); - return set_default_error (error, G_VALUE_TYPE (value)); + gtk_gradient_unref (gradient); + _gtk_css_parser_error (parser, + "Expected '('"); + return NULL; } - str++; - SKIP_SPACES (str); - - position = g_ascii_strtod (str, &end); - - str = end; - SKIP_SPACES (str); + if (!_gtk_css_parser_try_double (parser, &position)) + { + gtk_gradient_unref (gradient); + _gtk_css_parser_error (parser, + "Expected a valid number"); + return NULL; + } - if (*str != ',') + if (!_gtk_css_parser_try (parser, ",", TRUE)) { - g_object_unref (gradient); - return set_default_error (error, G_VALUE_TYPE (value)); + gtk_gradient_unref (gradient); + _gtk_css_parser_error (parser, + "Expected a comma"); + return NULL; } } else { - g_object_unref (gradient); - return set_default_error (error, G_VALUE_TYPE (value)); + gtk_gradient_unref (gradient); + _gtk_css_parser_error (parser, + "Not a valid color-stop definition"); + return NULL; } - str++; - SKIP_SPACES (str); - - color = symbolic_color_parse_str (str, &end); - - str = end; - SKIP_SPACES (str); - - if (*str != ')') + color = _gtk_css_parser_read_symbolic_color (parser); + if (color == NULL) { - if (color) - gtk_symbolic_color_unref (color); - g_object_unref (gradient); - return set_default_error (error, G_VALUE_TYPE (value)); + gtk_gradient_unref (gradient); + return NULL; } - str++; - SKIP_SPACES (str); + gtk_gradient_add_color_stop (gradient, position, color); + gtk_symbolic_color_unref (color); - if (color) + if (!_gtk_css_parser_try (parser, ")", TRUE)) { - gtk_gradient_add_color_stop (gradient, position, color); - gtk_symbolic_color_unref (color); + gtk_gradient_unref (gradient); + _gtk_css_parser_error (parser, + "Expected ')'"); + return NULL; } } - if (*str != ')') + if (!_gtk_css_parser_try (parser, ")", TRUE)) { - g_object_unref (gradient); - return set_default_error (error, G_VALUE_TYPE (value)); + gtk_gradient_unref (gradient); + _gtk_css_parser_error (parser, + "Expected ')'"); + return NULL; } - g_value_take_boxed (value, gradient); + 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; } @@ -1422,7 +1174,7 @@ flags_value_from_string (const char *str, { g_set_error (error, GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_NAME, + GTK_CSS_PROVIDER_ERROR_SYNTAX, "Unknown flag value '%s' for type '%s'", strv[i], g_type_name (G_VALUE_TYPE (value))); g_type_class_unref (flags_class); @@ -1559,7 +1311,7 @@ _gtk_css_value_from_string (GValue *value, { g_set_error (error, GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, + GTK_CSS_PROVIDER_ERROR_SYNTAX, "Cannot convert to type '%s'", g_type_name (G_VALUE_TYPE (value))); return FALSE; @@ -1587,32 +1339,6 @@ _gtk_css_value_to_string (const GValue *value) return g_strdup_value_contents (value); } -GtkSymbolicColor * -_gtk_css_parse_symbolic_color (const char *str, - GError **error) -{ - GtkSymbolicColor *color; - gchar *end; - - color = symbolic_color_parse_str (str, &end); - - if (*end != '\0') - { - if (color) - { - gtk_symbolic_color_unref (color); - color = NULL; - } - - g_set_error_literal (error, - GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, - "Failed to parse symbolic color"); - } - - return color; -} - GFile * _gtk_css_parse_url (GFile *base, const char *str, @@ -1631,7 +1357,7 @@ _gtk_css_parse_url (GFile *base, { g_set_error_literal (error, GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, + GTK_CSS_PROVIDER_ERROR_SYNTAX, "Expected '(' after 'url'"); return NULL; } @@ -1641,7 +1367,7 @@ _gtk_css_parse_url (GFile *base, { g_set_error_literal (error, GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, + GTK_CSS_PROVIDER_ERROR_SYNTAX, "No closing ')' found for 'url'"); return NULL; } @@ -1665,7 +1391,7 @@ _gtk_css_parse_url (GFile *base, { g_set_error_literal (error, GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, + GTK_CSS_PROVIDER_ERROR_SYNTAX, "Did not find closing quote for url"); return NULL; } @@ -1674,7 +1400,7 @@ _gtk_css_parse_url (GFile *base, { g_set_error_literal (error, GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE, + GTK_CSS_PROVIDER_ERROR_SYNTAX, "url not properly escaped"); return NULL; } diff --git a/gtk/gtkcssstringfuncsprivate.h b/gtk/gtkcssstringfuncsprivate.h index 900d38c2d..8a910f99d 100644 --- a/gtk/gtkcssstringfuncsprivate.h +++ b/gtk/gtkcssstringfuncsprivate.h @@ -30,9 +30,6 @@ gboolean _gtk_css_value_from_string (GValue *value, GError **error); char * _gtk_css_value_to_string (const GValue *value); -GtkSymbolicColor * _gtk_css_parse_symbolic_color (const char *str, - GError **error); - GFile * _gtk_css_parse_url (GFile *base, const char *str, char **end, diff --git a/tests/css/parser/boolean.errors b/tests/css/parser/boolean.errors index d609f13f7..93dcca5d3 100644 --- a/tests/css/parser/boolean.errors +++ b/tests/css/parser/boolean.errors @@ -1,8 +1,8 @@ -boolean.css:26: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE -boolean.css:29: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE -boolean.css:32: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE -boolean.css:35: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE -boolean.css:38: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE -boolean.css:41: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE -boolean.css:44: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE -boolean.css:47: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE +boolean.css:26: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +boolean.css:29: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +boolean.css:32: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +boolean.css:35: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +boolean.css:38: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +boolean.css:41: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +boolean.css:44: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +boolean.css:47: error: GTK_CSS_PROVIDER_ERROR_SYNTAX diff --git a/tests/css/parser/border.errors b/tests/css/parser/border.errors index a002e6003..7d17ea00c 100644 --- a/tests/css/parser/border.errors +++ b/tests/css/parser/border.errors @@ -1,8 +1,8 @@ -border.css:26: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE -border.css:30: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE -border.css:34: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE -border.css:38: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE -border.css:42: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE -border.css:46: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE -border.css:50: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE -border.css:54: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_NAME +border.css:26: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +border.css:30: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +border.css:34: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +border.css:38: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +border.css:42: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +border.css:46: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +border.css:50: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +border.css:54: error: GTK_CSS_PROVIDER_ERROR_SYNTAX diff --git a/tests/css/parser/does-not-exist.errors b/tests/css/parser/does-not-exist.errors index 22d7743b7..512b42f32 100644 --- a/tests/css/parser/does-not-exist.errors +++ b/tests/css/parser/does-not-exist.errors @@ -1 +1 @@ -does-not-exist.css:2: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_NAME +does-not-exist.css:2: error: GTK_CSS_PROVIDER_ERROR_NAME diff --git a/tests/css/parser/integer.errors b/tests/css/parser/integer.errors index 4cb80b3d7..8cd70449c 100644 --- a/tests/css/parser/integer.errors +++ b/tests/css/parser/integer.errors @@ -1,8 +1,8 @@ -integer.css:17: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE -integer.css:20: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE -integer.css:23: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE -integer.css:29: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE -integer.css:32: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE +integer.css:17: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +integer.css:20: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +integer.css:23: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +integer.css:29: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +integer.css:32: error: GTK_CSS_PROVIDER_ERROR_SYNTAX integer.css:35: error: GTK_CSS_PROVIDER_ERROR_SYNTAX -integer.css:38: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE -integer.css:41: error: GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE +integer.css:38: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +integer.css:41: error: GTK_CSS_PROVIDER_ERROR_SYNTAX -- 2.43.2