[gtk+] css: Rewrite the parser
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+] css: Rewrite the parser
- Date: Wed, 18 May 2011 20:29:55 +0000 (UTC)
commit 7ccb9db79e702e507dedf211ed25787be2f32721
Author: Benjamin Otte <otte redhat com>
Date: Thu Apr 14 04:47:18 2011 +0200
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(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 6ca52ec..f675830 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 0000000..de798e7
--- /dev/null
+++ b/gtk/gtkcssparser.c
@@ -0,0 +1,938 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2011 Benjamin Otte <otte gnome org>
+ *
+ * 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 <errno.h>
+#include <string.h>
+
+/* 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 0000000..609b9f5
--- /dev/null
+++ b/gtk/gtkcssparserprivate.h
@@ -0,0 +1,85 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2011 Benjamin Otte <otte gnome org>
+ *
+ * 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 <gtk/gtksymboliccolor.h>
+
+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 b65af8c..05b0f5a 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 : "<unknown>", 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 : "<unknown>", 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) ||
+ _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 65292df..b49640c 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 f36e942..53a5ccc 100644
--- a/gtk/gtkcssstringfuncs.c
+++ b/gtk/gtkcssstringfuncs.c
@@ -29,6 +29,7 @@
#include <cairo-gobject.h>
#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 900d38c..8a910f9 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 d609f13..93dcca5 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 a002e60..7d17ea0 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 22d7743..512b42f 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 4cb80b3..8cd7044 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
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]